Sprachen: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
Das Back-End besteht aus vier Headern und neunundzwanzig Phasen.
PluginTarget.h beschreibt ein Target und die Routen durch die
Codeerzeugung. PluginMC.h baut und beobachtet Maschinencode. Das Parsen und
Ausgeben von Assembly wohnt im selben Header. PluginObject.h verwandelt eine
relozierbare Datei in einen normalisierten Graphen und wieder zurück.
Zusammen erlauben sie einem Plugin, ein Target hinzuzufügen, einen einzelnen
Lowering-Schritt oder alle zu ersetzen, jede Instruktion beim Emittieren zu
beobachten, einen Assembly-Dialekt zu definieren oder eine Objektdatei
umzuschreiben — über ein reines C-ABI, das niemals ein MCInst, MCSection
oder object::ObjectFile von LLVM offenlegt.
#include "neverc/Plugin/PluginTarget.h"
#include "neverc/Plugin/PluginMC.h"
#include "neverc/Plugin/PluginObject.h" /* includes both of the above */| Schnittstelle | Tabelle | Slots | Zweck |
|---|---|---|---|
NEVERC_INTERFACE_TARGET_* |
NevercTargetAPI |
2 | RegisterTarget, RegisterCodeGenEdge |
NEVERC_INTERFACE_TARGET_ABI_* |
NevercTargetABIAPI |
1 | RegisterABI |
NEVERC_INTERFACE_CALLING_CONVENTION_* |
NevercCallingConventionAPI |
1 | RegisterCallingConvention |
NEVERC_INTERFACE_MC_* |
NevercMCAPI |
53 | Ein MCUnit lesen und ändern; Encoder, Decoder, Back-Ends registrieren |
NEVERC_INTERFACE_MC_EMISSION_* |
NevercMCEmissionAPI |
7 | Emissionsereignisse und Layout-Momentaufnahmen |
NEVERC_INTERFACE_MC_PROVIDER_* |
NevercMCProviderAPI |
4 | MIR → MC ersetzen |
NEVERC_INTERFACE_ASSEMBLY_PROVIDER_* |
NevercAssemblyProviderAPI |
8 | Assembly-Parser oder -Printer ersetzen |
NEVERC_INTERFACE_OBJECT_* |
NevercObjectAPI |
34 | Einen ObjectGraph lesen und ändern |
NEVERC_INTERFACE_OBJECT_FORMAT_* |
NevercObjectFormatAPI |
1 | RegisterFormat |
NEVERC_INTERFACE_OBJECT_PHASE_* |
NevercObjectPhaseAPI |
2 | GetGraph, GetImage |
Das ist die Regel, die alles Weitere hier bestimmt.
STABLE, und sicher fest zu verdrahten: target-unabhängige Deskriptoren, Phasen-IDs, Artefakt-IDs, die MC- und ObjectGraph-Container, Ausgabetransaktionen und jeder Callback-Vertrag.
LOCKSTEP, und ohne Prüfung unsicher: target-spezifische Schemata für Opcodes, Register, Operanden, Fixups, Relokationen und Aufrufkonventionen. Ihre Zahlenwerte sind nur gegenüber genau einer Schema-Revision bedeutsam.
Überall, wo ein LOCKSTEP-Wert auftaucht, steht ein Schema-Digest daneben. Vergleichen Sie ihn, bevor Sie den Wert lesen:
if (!string_equal(Target.SchemaDigest, MY_COMPILED_SCHEMA_DIGEST))
return fail(NEVERC_STATUS_ABI_MISMATCH);NeverC weist ein nicht passendes Schema ebenfalls zurück, bevor es einen Provider aufruft — die Prüfung ist also doppelt abgesichert. Ein Plugin, das sie überspringt und trotzdem einen rohen Opcode liest, wird Instruktionen jedoch stillschweigend falsch deuten.
Neunundzwanzig, in vier Domänen.
| Phase | Policy |
|---|---|
neverc.codegen.ir_to_mir |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE |
neverc.codegen.mir_to_mc |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE |
neverc.codegen.coarse_lower |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE |
neverc.codegen.product_verify |
OBSERVABLE, SEALED |
neverc.mc.encode, neverc.mc.decode und neverc.mc.layout sind
OBSERVABLE, INTERCEPTABLE, REPLACEABLE.
neverc.mc.emission.pre_instruction ist das eine Emissionsereignis, das
zusätzlich REPLACEABLE ist — dort ersetzt man eine Instruktion. Die anderen
neun (unit_begin, unit_end, section_change, post_instruction,
post_encode, fixup, relaxation_round, pre_layout, post_layout)
dienen nur der Beobachtung.
neverc.assembly.parse und neverc.assembly.print sind REPLACEABLE.
neverc.assembly.final_verify und neverc.assembly.commit sind SEALED.
neverc.object.probe, read, write, pre_write und post_layout sind
REPLACEABLE; neverc.object.post_write ist nur INTERCEPTABLE;
neverc.object.final_verify und neverc.object.commit sind SEALED.
NevercTargetDescriptor ist der größte Deskriptor im ABI, weil er alles
transportiert, was Front-End und Back-End wissen müssen:
typedef struct NevercTargetDescriptor {
NevercABITableHeader Header;
NevercTargetID TargetID;
NevercStringView CanonicalName;
NevercStringArrayView Aliases;
NevercStructArrayView TripleMatchers; /* NevercTargetTripleMatcher[] */
NevercTargetABIID DefaultABI;
NevercCallingConventionID DefaultCallingConvention;
NevercInterfaceID MCSchemaID;
NevercInterfaceID DefaultObjectFormatID;
NevercTargetMachineDescriptor Machine;
NevercStructArrayView Macros; /* predefined macros */
NevercStructArrayView Builtins; /* target builtins + lowering */
NevercStructArrayView Registers; /* inline-asm register names */
NevercStructArrayView Constraints; /* inline-asm constraints */
NevercStringView Clobbers;
uint64_t Flags;
NevercTargetValidateCPUFn ValidateCPU;
NevercTargetCanonicalizeCPUFn CanonicalizeCPU;
NevercTargetListCPUsFn ListCPUs;
NevercTargetResolveFeaturesFn ResolveFeatures;
NevercCreateTargetMachineFn CreateTargetMachine;
NevercDestroyTargetMachineFn DestroyTargetMachine;
void *UserData;
NevercDestroyUserDataFn DestroyUserData;
} NevercTargetDescriptor;TripleMatchers entscheidet, wann das Target ausgewählt wird: Jeder Matcher
nennt Architektur, Hersteller, Betriebssystem und Umgebung sowie eine
Priority, die Gleichstände gegenüber den eingebauten Targets auflöst.
Machine ist ein NevercTargetMachineDescriptor — Datenlayout, Standard- und
Tuning-CPUs, die Feature-Tabelle, unterstützte ABIs, Aufrufkonventionen und
Objektformate, Adressräume, Relokations- und Codemodelle (sowohl als Standard
als auch als Unterstützungsmaske), Ausnahmemodell (NONE, DWARF, SJLJ,
SEH, WASM), Unwind-Modell, Endianness, die Breite von
pointer/int/long/long long, Stack-Ausrichtung, maximale Atom- und
Vektorbreiten, va_list-Art, Ausführungsebenen (USER, KERNEL,
HYPERVISOR, FIRMWARE) sowie TLS-Unterstützung.
Target-Builtins tragen ihren eigenen Lowering-Callback, der einen lebendigen IR-Builder erhält:
static NevercStatus NEVERC_CALL
lower_builtin(void *UserData,
const NevercTargetBuiltinLoweringInvocation *In,
NevercIRValueHandle *OutResult) {
/* In->Core, In->Builder, In->Mutation, In->IRBuilder,
In->ResultType, In->Arguments, In->ArgumentCount */
return In->Builder->BuildCall(/* … */);
}Ein ABI klassifiziert Funktionssignaturen:
static NevercStatus NEVERC_CALL
classify(void *UserData, const NevercABIFunctionQuery *Query,
NevercABIArgumentClassification *ReturnValue,
NevercABIArgumentClassificationArray *Arguments) {
ReturnValue->Kind = NEVERC_ABI_ARGUMENT_DIRECT;
for (uint64_t I = 0; I != Arguments->Count; ++I) {
NevercABIArgumentClassification *A = &Arguments->Data[I];
A->Kind = NEVERC_ABI_ARGUMENT_INDIRECT;
A->Flags = NEVERC_ABI_ARGUMENT_BYVAL;
}
return neverc_status_ok();
}Argumentarten sind DIRECT, EXTEND, INDIRECT, IGNORE, EXPAND,
INDIRECT_ALIASED und COERCE_AND_EXPAND; Flags sind BYVAL, REALIGN,
INREG, SRET_AFTER_THIS, CAN_BE_FLATTENED, SIGN_EXTEND und
PADDING_INREG. Die Umwandlung ist NONE, INTEGER, FLOAT oder
POINTER, und COERCE_AND_EXPAND liefert ein Array von
NevercABICoercionElement.
Eine Aufrufkonvention geht eine Ebene tiefer und weist die tatsächlichen Orte zu:
static NevercStatus NEVERC_CALL
plan(void *UserData, const NevercCallingConventionQuery *Query,
NevercCallingConventionPlan *Plan) {
/* Query->TargetID, ->CallingConventionID, ->SchemaDigest, ->Function */
/* Fill Plan->ReturnLocations and Plan->ArgumentLocations with
NevercCallingConventionLocation records: REGISTER or STACK,
ValueIndex, PieceOffset, Size, Alignment, RegisterNumber,
StackOffset, and INDIRECT / BYVAL flags. */
Plan->CalleeSavedRegisters = MySavedRegisters;
Plan->StackAlignment = 16;
return neverc_status_ok();
}Query->SchemaDigest ist ein LOCKSTEP-Wert — RegisterNumber bedeutet nur
etwas gegenüber dem Schema, das er benennt. Das vollständig ausgearbeitete
Beispiel finden Sie unter
Eigene Aufrufkonventionen und in
pluginsdk/examples/CustomCallConvPlugin.c.
Eine Route wird aus dem kanonischen NevercTargetKey ausgewählt: Target-ID,
Triple-Teile, CPU, Tuning-CPU, Features, ABI, Aufrufkonvention, Objektformat,
Relokationsmodell, Codemodell, Ausführungsebene, Zeigerbreite, Endianness und
Schema-Digest. Registrieren Sie die Kanten, die Sie bedienen können:
NevercCodeGenEdgeDescriptor Edge = {0};
Edge.Header = /* … */;
Edge.EdgeID = MyEdgeID;
Edge.CanonicalName = SV("com.example.mir-to-mc");
Edge.TargetID = MyTargetID;
Edge.InputKind = NEVERC_CODEGEN_PRODUCT_MIR;
Edge.OutputKind = NEVERC_CODEGEN_PRODUCT_MC;
Edge.CompatibilityKey = SV("…");
Edge.ProviderID = SV("com.example.backend");
Target->RegisterCodeGenEdge(Target->Context, RegistrarContext, &Edge);Produktarten sind IR, MIR, MC, ASSEMBLY, OBJECT_GRAPH,
OBJECT_IMAGE und CUSTOM. Die feingranulare Route lautet
IR → MIR → MC → ObjectGraph → ObjectImage.
NEVERC_CODEGEN_EDGE_COARSE zu setzen und CoarseLower bereitzustellen,
ersetzt die gesamte Spanne IR → ObjectImage in einem Schritt:
static NevercStatus NEVERC_CALL
coarse_lower(void *UserData, NevercTaskHandle Task,
const NevercCodeGenRequest *Request,
NevercCodeGenProductCandidate *OutCandidate) {
/* Request->Target, ->Input, ->InputKind, ->OutputKind,
->OptimizationLevel, ->HasFinalIRProof */
OutCandidate->Kind = NEVERC_CODEGEN_PRODUCT_OBJECT_IMAGE;
OutCandidate->Artifact = MyImage;
OutCandidate->ProductID = MyProductID;
return neverc_status_ok();
}Auch eine grobe Route durchläuft neverc.codegen.product_verify und das
transaktionale Festschreiben der Ausgabe. VerifyProduct wird mit genau den
Pflichten aufgerufen, deren Erfüllung der Host von Ihnen erwartet —
VERIFY_FINAL_IR, VERIFY_TARGET_KEY, VERIFY_PRODUCT_KIND,
VERIFY_PRODUCT_ID, VERIFY_STRUCTURE —, sodass ein Provider kein Gate
heimlich durch eine Abkürzung umgehen kann.
Ein MCUnit enthält Sections, Symbole, Ausdrücke, Fragmente, Instruktionen,
Operanden und Fixups. Gelesen wird per first/next-Iteration:
NevercMCUnitInfo Unit = {0};
Unit.Header = /* … */;
MC->GetUnitInfo(MC->Context, Task, UnitHandle, &Unit);
NevercMCSectionHandle Section;
MC->GetFirstSection(MC->Context, Task, UnitHandle, &Section);
while (!neverc_handle_is_null(Section)) {
NevercMCFragmentHandle Fragment;
MC->GetFirstFragment(MC->Context, Task, Section, &Fragment);
/* … */
MC->GetNextSection(MC->Context, Task, Section, &Section);
}Mutation ist transaktional, wie überall sonst:
NevercMCMutationHandle Mutation;
MC->BeginMutation(MC->Context, Task, Unit, &Mutation);
MC->CreateSection(MC->Context, Task, Mutation, &SectionDescriptor, &Section);
MC->CreateSymbol(MC->Context, Task, Mutation, &SymbolDescriptor, &Symbol);
MC->AppendInstruction(MC->Context, Task, Mutation, Section, &Instruction);
Status = MC->CommitMutation(MC->Context, Task, Mutation);
if (Status.Code != NEVERC_STATUS_OK)
MC->AbandonMutation(MC->Context, Task, Mutation);Handles sind task-gebunden und generationsgeprüft, sodass ein Handle aus einer abgebrochenen Mutation zurückgewiesen statt wiederverwendet wird.
Section-Flags sind ALLOCATED, EXECUTABLE, WRITABLE, MERGEABLE und
DEBUG. Symbolbindungen sind LOCAL, GLOBAL und WEAK; Typen sind NONE,
FUNCTION, OBJECT, SECTION und TLS; Definitionen sind UNDEFINED,
SECTION, ABSOLUTE und COMMON. Ausdrücke unterstützen die unären
Operatoren PLUS, MINUS, NOT sowie die binären ADD, SUBTRACT,
MULTIPLY, DIVIDE, AND, OR, XOR, SHIFT_LEFT, SHIFT_RIGHT.
Übergeben Sie NEVERC_MC_AUTOMATIC_OFFSET, wo der Host etwas für Sie
platzieren soll.
RegisterSchema veröffentlicht ein Target-MC-Schema, und GetSchemaToken /
GetSchemaTokenInfo übersetzen zwischen Name und LOCKSTEP-Token.
Der Emissionsstrom meldet zehn Ereignisarten der Reihe nach — eine je
neverc.mc.emission.*-Phase. Die ABI reserviert außerdem
NEVERC_MC_EMISSION_PRE_OBJECT_WRITE; das Objekt schreiben selbst ist die
eigene Phase neverc.object.pre_write. Abonnieren Sie als
Beobachter und lesen Sie das Ereignis:
NevercMCEmissionEventInfo Event = {0};
Event.Header = /* … */;
Emission->GetEvent(Emission->Context, Frame, Frame->Input, &Event);
/* Event.Kind, Event.Flags */Flags sagt, welche Teile des Ereignisses gefüllt sind: HAS_SECTION,
HAS_INSTRUCTION, HAS_ENCODING, HAS_FIXUP, HAS_LAYOUT und
CAN_REPLACE_INSTRUCTION. Prüfen Sie das Flag, bevor Sie das zugehörige Feld
lesen — ein Ereignis, das noch keine Kodierung hat, bekommt keine, nur weil Sie
danach fragen.
GetLayoutSection, GetLayoutFragment, GetLayoutSymbol und
GetLayoutFixup liefern Adressen und Größen, sobald HAS_LAYOUT gesetzt ist.
Bei pre_instruction und nur dann, wenn CAN_REPLACE_INSTRUCTION gesetzt
ist, können Sie ersetzen:
const NevercMCAPI *MC;
NevercMCUnitHandle Unit;
NevercMCInstHandle Instruction;
Emission->BeginInstructionReplacement(Emission->Context, Frame, Continuation,
&MC, &Unit, &Instruction);
/* mutate Instruction through MC->BeginMutation / … / CommitMutation */
Emission->PublishInstructionReplacement(Emission->Context, Frame, Continuation,
&OutResult->Output);pluginsdk/examples/MCObserverPlugin.c ist die rein lesende Variante davon.
Drei Registrierungen erweitern das Maschinencode-Back-End, alle über Target und Schema-Digest verschlüsselt:
MC->RegisterEncoder(MC->Context, RegistrarContext, &EncoderDescriptor);
MC->RegisterDecoder(MC->Context, RegistrarContext, &DecoderDescriptor);
MC->RegisterAsmBackend(MC->Context, RegistrarContext, &BackendDescriptor);Ein Encoder schreibt durch eine Senke, statt einen Puffer zurückzugeben — so bleibt die Eigentümerschaft auf der Host-Seite:
Sink->WriteBytes(Sink->Context, Bytes);
Sink->AddFixup(Sink->Context, &Fixup);Ein Decoder meldet eines von NEVERC_MC_DECODE_SUCCESS, _SOFT_FAIL,
_UNKNOWN oder _FAIL. Fixup-Arten beschreiben sich selbst über
NevercMCFixupKindInfo mit den Flags PC_RELATIVE, SIGNED, RELAXABLE und
TARGET.
Das Asm-Back-End besitzt die Relaxation. Das Layout gibt einen Beweis-Digest aus, und jede Mutation nach dem Layout entwertet diesen Beweis und erzwingt ein erneutes Layout, bevor das Objekt geschrieben werden kann — dasselbe Muster der Generationsprüfung, das auch der Link-Graph verwendet.
Ein Parser-Provider verbraucht Quellbytes und veröffentlicht ein MCUnit:
NevercAssemblyParseInputInfo In = {0};
In.Header = /* … */;
Asm->GetParseInput(Asm->Context, Frame, Frame->Input, &In);
NevercAssemblyTokenInfo Token = {0};
Asm->PeekSourceToken(Asm->Context, Frame, In.Source.Cursor, &Token);
Asm->AdvanceSourceToken(Asm->Context, Frame, In.Source.Cursor);
const NevercMCAPI *MC;
NevercMCUnitHandle Unit;
Asm->GetParseMCBuilder(Asm->Context, Frame, &MC, &Unit);
/* … build into Unit … */
Asm->PublishParsedMCUnit(Asm->Context, Frame, &Output);Quellen sind entweder NEVERC_ASSEMBLY_SOURCE_BUFFER oder
NEVERC_ASSEMBLY_SOURCE_RENDERED_TOKENS. Vorverarbeitete Assembly (.S)
durchläuft zuerst den normalen Frontend-Präprozessor und kommt als gerenderte
Token an; einfache Assembly (.s) gelangt direkt als Puffer in den Parser.
Ein Printer geht den umgekehrten Weg — GetPrintInput, dann
WritePrintOutput in die bereitgestellte Ausgabetransaktion, dann
PublishAssemblyOutput. Anderswohin zu schreiben wird nicht unterstützt: Die
Parse-/Print-Verifikation und das Commit-Gate des Hosts laufen, bevor Bytes
sichtbar werden, sodass ein fehlgeschlagenes Ausgeben keine halbe Datei
zurücklässt.
NevercObjectAPI normalisiert eine relozierbare Datei zu Sections, Symbolen,
Relokationen und COMDATs. Eingebaute Adapter decken ELF, COFF und Mach-O ab;
RegisterFormat fügt ein weiteres hinzu.
NevercObjectGraphInfo Info = {0};
Info.Header = /* … */;
Object->GetGraphInfo(Object->Context, Task, Graph, &Info);
/* Info.Target, .ObjectSchemaDigest, .Generation, .SectionCount,
.SymbolCount, .RelocationCount, .ComdatCount, .HasLayoutProof */
NevercObjectSymbolHandle Symbol;
Object->GetFirstSymbol(Object->Context, Task, Graph, &Symbol);
while (!neverc_handle_is_null(Symbol)) {
NevercObjectSymbolInfo SymInfo = {0};
SymInfo.Header = /* … */;
Object->GetSymbolInfo(Object->Context, Task, Symbol, &SymInfo);
Object->GetNextSymbol(Object->Context, Task, Symbol, &Symbol);
}Die Mutation folgt für alle vier Entitätsarten dem Muster
create/replace/move/erase und wird innerhalb von BeginMutation …
CommitMutation / AbandonMutation vorgemerkt.
Section-Flags sind ALLOCATED, EXECUTABLE, WRITABLE, MERGEABLE,
STRINGS, TLS, DEBUG, UNWIND, DISCARDABLE und RETAIN. Ziele von
Relokationen sind SYMBOL, SECTION, ABSOLUTE oder FORMAT_EXTENSION.
Jeder Deskriptor hat ein Tripel aus ExtensionOwner / ExtensionVersion /
Extension. So bewahrt ein Format Daten auf, für die der normalisierte Graph
kein Feld hat — die Bytes reisen mit der Entität mit und kommen beim Schreiben
zurück, statt beim Hin und Her verloren zu gehen.
Der eingebaute ELF-Adapter hält exakte native Fakten in markierten Extensions
fest: NCSE v2 enthält Sektionsindex, Adresse, Typ, Flags, Dateioffset und
Eintragsgröße; NCSY v2 enthält st_info, das vollständige st_other,
st_size und einen expliziten Zustand für leere bzw. nicht leere native Namen;
NCRL v1 enthält den nativen Relokationstyp und seinen offiziellen Namen. Ein
gewöhnliches leeres ELF-Symbol bleibt deshalb leer und wird nie in einen
künstlichen Namen $symbol.N umgeschrieben; ein tatsächlich so benanntes
$symbol.N bleibt ein normaler nicht leerer Name. Unverändertes natives
Image-Passthrough kann anonyme Symbole exakt erhalten. Ein graphmaßgeblicher
eingebauter Schreibvorgang lehnt sie vor dem Öffnen der Ausgabe-Senke ab, weil
die portable MC-Schreibweise denselben anonymen Symboltabelleneintrag nicht
rekonstruieren kann. Kanonische Android-Release-Audits verlangen die exakten
aktuellen markierten Payloads und spielen daraus die stabile Graphprojektion
nach.
NevercObjectFormatDescriptor Format = {0};
Format.Header = /* … */;
Format.FormatID = MyFormatID;
Format.CanonicalName = SV("com.example.myfmt");
Format.SupportedTargets = MyTargets;
Format.DefaultExtension = SV(".mof");
Format.Flags = NEVERC_OBJECT_FORMAT_CAN_PROBE |
NEVERC_OBJECT_FORMAT_CAN_READ |
NEVERC_OBJECT_FORMAT_CAN_WRITE;
Format.Probe = probe;
Format.Reader = read;
Format.Writer = write;
ObjectFormat->RegisterFormat(ObjectFormat->Context, RegistrarContext,
&Format);Probe meldet eine Confidence von 0 bis
NEVERC_OBJECT_PROBE_MAX_CONFIDENCE (1000), die erkannte
NevercObjectArtifactKind (RELOCATABLE, ARCHIVE, EXECUTABLE_IMAGE,
SHARED_IMAGE, UNIVERSAL_BINARY) und ein ConsumedMinimum — wie viele
Bytes es zur Gewissheit brauchte, begrenzt auf
NEVERC_OBJECT_PROBE_MAX_CONSUMED_MINIMUM (65536). Die höchste Zuversicht
gewinnt.
Reader bekommt einen Graphen und eine offene Mutation und füllt beide.
Writer bekommt den Graphen, seinen Layout-Beweis und den begrenzten
Binär-Builder.
NevercObjectFormatDescriptor.Header.Minor beschreibt die Fähigkeit des
Providers; es ist kein hostweiter Modusschalter. Ein 1.0-Descriptor bleibt für
Probe, Read und gewöhnliche Default-Writes vollständig kompatibel. Sein Writer
erhält NevercObjectWriteRequest.Header.Minor == 0 und Header.Flags == 0.
Minor 1 darf nur angekündigt werden, wenn der Writer die 1.1-Request-Flags
versteht; ein gewöhnlicher Write führt weiterhin Flags null und behält das
Ausgabeverhalten vor 1.1 bei.
Object Format 1.1 definiert diese Bits in
NevercObjectWriteRequest.Header.Flags:
NEVERC_OBJECT_WRITE_CANONICAL_ELF_TABLESverlangt getrennte kanonische.strtab- und.shstrtab-Sektionen sowie die Neuzuordnung aller abhängigen Indizes. Dies ist eine Kanonisierung der ELF-Tabellen und kein relokierbarer Link: Die Sektionsreihenfolge, COMDAT-Gruppen, Linker-Metadaten, doppelte Symbole und Alias-Symbole, Relokationseinträge sowie sämtliche Nutzdaten außerhalb der Namenstabellen bleiben unverändert. Zusätzliche gültige, formatspezifischeSHT_STRTAB-Sektionen bleiben erhalten; neu aufgebaut werden nur die vom ausgewähltenSHT_SYMTABverwendete Stringtabelle und die durche_shstrndxbezeichnete Tabelle. MitDROP_DEBUG_INFOwerden ausschließlich Debug-Sektionen und Metadaten entfernt, die auf die entfernten Indizes dieser Sektionen verweisen.NEVERC_OBJECT_WRITE_ANDROID_KERNEL_RELEASEmacht zusätzlich das endgültig serialisierte ELF maßgeblich: vom Writer erzeugte Mapping-Symbole werden entfernt und Release-Namen aus den tatsächlichen serialisierten Sektionskoordinaten erneut erzeugt.NEVERC_OBJECT_WRITE_DROP_DEBUG_INFOfordert das Entfernen von Debug-Sektionen als Teil einer dieser ELF-Richtlinien an.
NEVERC_OBJECT_WRITE_REQUEST_KNOWN_FLAGS ist die vollständige Maske bekannter
Bits. Zulässig sind ausschließlich 0, CANONICAL_ELF_TABLES,
CANONICAL_ELF_TABLES | DROP_DEBUG_INFO,
CANONICAL_ELF_TABLES | ANDROID_KERNEL_RELEASE und alle drei Bits zusammen.
Das Release- oder Debug-Bit ohne das Canonical-Bit ist ungültig.
Der Host lehnt unbekannte oder ungültige Kombinationen und jede spezielle
Anforderung an einen Minor-0-Provider ab, bevor er den Ausgabe-Sink öffnet. Ein
1.1-Writer muss empfangene unbekannte oder ungültige Flags ebenfalls ablehnen,
statt sie zu ignorieren. Nach
dem Writer und allen object.post_write-Interceptors prüfen die semantische
Hostvalidierung und das versiegelte object.final_verify die serialisierten
Bytes erneut und sind maßgeblich. Diese Flags sind kein allgemeines Versprechen
für jedes Drittanbieterformat. Minor 1 bedeutet, dass der Writer das
Flags-Protokoll versteht: Er kann eine anwendbare ELF-Richtlinie implementieren
oder ausdrücklich NEVERC_STATUS_CAPABILITY_UNAVAILABLE zurückgeben, wenn sie
nicht anwendbar oder nicht unterstützt ist; die Anforderung darf nie
stillschweigend ignoriert werden.
Wenn --strip ein Android-.ko finalisiert, wird die oben beschriebene
generische Mutable-Object-API auf den vom Host errichteten vertrauenswürdigen
Schreibpfad eingeschränkt. Diese Grenze besitzt zwei unabhängige
Identitätssiegel:
- vor jeder ersetzbaren
ObjectGraph-Phase bindet das Graph-Siegel diesection ID, denfinal ordinalund den exakten Namen jeder erhaltenen logischen Section sowiesymbol ID, Owner, Klasse, Section, Wert, Größe, Binding, Typ und vollständigesst_otherjedes Symbols mit exaktem Namen; - nachdem der host-eigene Writer die vertrauenswürdige Image-Baseline erzeugt
hat, bindet das Image-Siegel Ordinal und exakten Namen jeder erhaltenen
Section, die Gesamtzahl der
.symtab-Einträge sowie den rohen.symtab-slotund die Attribute jedes Symbols mit exaktem Namen. Der vollständige Release-Verifier berechnet zusätzlich jeden strukturellen Release-Namen neu.
| Binding | Verhalten beim finalisierten Android-Release |
|---|---|
neverc.object.write provider / interceptor |
vor dem Callback REJECTED; der vertrauenswürdige Schreibpfad kann nicht ersetzt werden |
plugin-owned ObjectFormat graph writer |
REJECTED; dieser Pfad erfordert den host-eigenen Graph Writer, der die vertrauenswürdige Baseline errichtet |
observer |
READ_ONLY; darf prüfen, aber die Ausgabe weder ändern noch ersetzen |
neverc.object.post_write interceptor |
VALIDATED; seine begrenzte Mutable API darf nur Payload außerhalb der strukturell verifizierten ABI- und Identitätsoberfläche ändern, und das Ergebnis muss Input-ABI-Prüfungen, beide Siegel und den vollständigen Release-Verifier bestehen |
Auch die Eigentümerschaft des finalisierten Merges wird vom Host versiegelt.
Ein MergedImage oder unabhängige Bytes eines third-party ObjectMergeProvider
werden verworfen; der host-owned graph writer serialisiert dessen verifizierten,
finalisierten Graphen. Umgekehrt umgeht built-in finalized input serialization
die external object phases und übergibt dem Host-Merger exakt die
audited native bytes; dieser interne Eingabeschritt umgeht die obige
Ausgabegrenze nicht.
Finalisierung wird nur mit Android module merge semantics akzeptiert; sie
erfordert außerdem sowohl eine relocatable output request als auch eine
relocatable driver configuration, andernfalls schlägt sie before routing
fehl. Für ein finalisiertes Android-Relocatable-Release müssen
frozen input format,
TargetKey.ObjectFormatID und frozen output format dieselbe
one format identity teilen. Eine Abweichung wird before provider dispatch
abgelehnt, also auch vor Route Planning oder Sink-Erstellung; Capability
Preflight und tatsächlicher Graph-Writer-Dispatch können dadurch keine
unterschiedlichen Formate sehen.
Native-Image-Passthrough lehnt jeden ersetzbaren route-matching provider und
alle Interceptors ab. Ein Provider mit nicht passender
target/CPU/features/object-format/execution-level Route wird weder ausgeführt
noch blockiert er das Release; nur Observer sind zugelassen. Nur eine
Ablehnung oder fehlgeschlagene Validierung
before sealed commit bricht das Staging ab und veröffentlicht keine Datei.
Ein Fehler eines AFTER_COMMIT-Observers wird nach der Veröffentlichung gemeldet
und kann die veröffentlichte Datei nicht zurückrollen.
- sondieren und Bytes in einen ObjectGraph einlesen;
- die Graph-Interceptors
object.pre_writeausführen; - Layout erstellen, dann
object.post_layoutausführen (nach jeder Mutation neu layouten); - ein begrenztes Kandidaten-Image schreiben;
- die Binär-Interceptors
object.post_writeausführen; - das versiegelte
object.final_verifyund das atomareobject.commitausführen.
Der Image-Zustand wandert CANDIDATE → VERIFIED → COMMITTED oder
ABORTED / FAILED_PARTIAL.
Beobachter erhalten rein lesende Brücken; eine aus einem Beobachter versuchte
Mutation wird mit NEVERC_STATUS_POLICY_VIOLATION abgelehnt. Writer und
Post-Write-Interceptors bekommen nur den begrenzten Builder
NevercMutableBinaryAPI — Reserve, Write, WriteAt, Tell, ReadAt,
Insert, Append, Resize. Ein Überlauf, ein fehlgeschlagener Callback oder
eine gescheiterte Verifikation bricht das Vormerken ab, sodass ein Fehlschlag
nie eine halbe Datei auf der Platte hinterlässt.
pluginsdk/examples/ObjectRewritePlugin.c ist eine vollständige
transaktionale Umschreibung.
- Vergleichen Sie den Schema-Digest, bevor Sie irgendeinen LOCKSTEP-Wert für Opcode, Register, Operand, Fixup, Relokation oder Aufrufkonvention verwenden.
- Halten Sie veränderlichen Zustand im vom Host bereitgestellten Process-, Session- und Task-Zustand.
- Cachen Sie keine Task-Handles oder geliehenen Views, nachdem ein Callback zurückgekehrt ist.
- Rufen Sie die Fortsetzung eines Interceptors höchstens einmal auf, und zwar auf dem Callback-Thread.
- Jedes
BeginMutationerreicht genau ein Commit oder ein Abandon. - Layouten Sie neu, nachdem Sie ein bereits layoutetes MCUnit oder einen ObjectGraph verändert haben; der alte Layout-Beweis ist veraltet und der Host wird ihn zurückweisen.
- Prüfen Sie
NevercMCEmissionEventInfo.Flags, bevor Sie ein Ereignisfeld lesen, und ersetzen Sie eine Instruktion nur, wennCAN_REPLACE_INSTRUCTIONgesetzt ist. - Schreiben Sie Ausgaben ausschließlich über die bereitgestellte Transaktion oder Byte-Senke.
- Geben Sie im Fehlerfall den ursprünglichen
NevercStatuszurück und veröffentlichen Sie nichts Teilweises. - Deklarieren Sie die engsten zutreffenden Modelle für Nebenläufigkeit und Reentranz.
codegen.product_verify,assembly.final_verify,assembly.commit,object.final_verifyundobject.commitsind versiegelt. Nur beobachten.
Die normativen Deklarationen finden Sie in PluginTarget.h, PluginMC.h,
PluginObject.h und Schema/PhaseSchema.json; die Entitäts-, Operanden-,
Fixup- und Abschnittsarten, die sie verwenden, stammen aus
Schema/MCSchema.json und Schema/ObjectSchema.json, aus denen
Schema/PluginMCSchema.inc und Schema/PluginObjectSchema.inc erzeugt
werden. coverage.json ordnet jeder dieser stabilen Phasen ihre positiven,
negativen, Ersetzungs-, Nur-Lese-Beobachter- und Sealed-Gate-Tests zu.