Lingue: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
Il back-end sono quattro header e ventinove fasi. PluginTarget.h descrive un
target e le rotte attraverso la generazione di codice. PluginMC.h costruisce
e osserva il codice macchina. L'analisi e la stampa dell'assembly vivono nello
stesso header. PluginObject.h trasforma un file rilocabile in un grafo
normalizzato e viceversa.
Insieme permettono a un plugin di aggiungere un target, sostituire un passo di
abbassamento o tutti quanti, sorvegliare ogni istruzione mentre viene emessa,
definire un dialetto assembly o riscrivere un file oggetto — attraverso un ABI
C puro che non espone mai un MCInst, un MCSection o un
object::ObjectFile di LLVM.
#include "neverc/Plugin/PluginTarget.h"
#include "neverc/Plugin/PluginMC.h"
#include "neverc/Plugin/PluginObject.h" /* includes both of the above */| Interfaccia | Tabella | Slot | Scopo |
|---|---|---|---|
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 | Leggere e modificare un MCUnit; registrare encoder, decoder, back-end |
NEVERC_INTERFACE_MC_EMISSION_* |
NevercMCEmissionAPI |
7 | Eventi di emissione e istantanee del layout |
NEVERC_INTERFACE_MC_PROVIDER_* |
NevercMCProviderAPI |
4 | Sostituire MIR → MC |
NEVERC_INTERFACE_ASSEMBLY_PROVIDER_* |
NevercAssemblyProviderAPI |
8 | Sostituire il parser o lo stampatore assembly |
NEVERC_INTERFACE_OBJECT_* |
NevercObjectAPI |
34 | Leggere e modificare un ObjectGraph |
NEVERC_INTERFACE_OBJECT_FORMAT_* |
NevercObjectFormatAPI |
1 | RegisterFormat |
NEVERC_INTERFACE_OBJECT_PHASE_* |
NevercObjectPhaseAPI |
2 | GetGraph, GetImage |
È la regola che governa tutto il resto di questo documento.
STABLE, e sicuro da fissare nel codice: i descrittori indipendenti dal target, gli identificatori di fase, gli identificatori di artefatto, i contenitori MC e ObjectGraph, le transazioni di output e ogni contratto di callback.
LOCKSTEP, e pericoloso senza un controllo: gli schemi di opcode, registro, operando, fixup, rilocazione e convenzione di chiamata specifici del target. I loro valori numerici hanno senso solo rispetto a una precisa revisione dello schema.
Ovunque compaia un valore LOCKSTEP, accanto compare un digest dello schema. Confrontatelo prima di leggere il valore:
if (!string_equal(Target.SchemaDigest, MY_COMPILED_SCHEMA_DIGEST))
return fail(NEVERC_STATUS_ABI_MISMATCH);Anche NeverC rifiuta uno schema discordante prima di invocare un provider, quindi il controllo è una doppia sicurezza — ma un plugin che lo salta e legge comunque un opcode grezzo interpreterà le istruzioni in modo errato, in silenzio.
Ventinove, in quattro domini.
| Fase | 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 e neverc.mc.layout sono OBSERVABLE,
INTERCEPTABLE, REPLACEABLE.
neverc.mc.emission.pre_instruction è l'unico evento di emissione che è anche
REPLACEABLE: è lì che si sostituisce un'istruzione. Gli altri nove
(unit_begin, unit_end, section_change, post_instruction,
post_encode, fixup, relaxation_round, pre_layout, post_layout) sono
di sola osservazione.
neverc.assembly.parse e neverc.assembly.print sono REPLACEABLE.
neverc.assembly.final_verify e neverc.assembly.commit sono SEALED.
neverc.object.probe, read, write, pre_write e post_layout sono
REPLACEABLE; neverc.object.post_write è solo INTERCEPTABLE;
neverc.object.final_verify e neverc.object.commit sono SEALED.
NevercTargetDescriptor è il descrittore più grande dell'ABI perché porta con
sé tutto ciò che front-end e back-end devono sapere:
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 decide quando il target viene selezionato: ogni matcher
indica un'architettura, un fornitore, un sistema operativo e un ambiente, più
una Priority che scioglie i pareggi rispetto ai target integrati.
Machine è un NevercTargetMachineDescriptor — layout dei dati, CPU
predefinita e di tuning, la tabella delle funzionalità, gli ABI, le convenzioni
di chiamata e i formati oggetto supportati, gli spazi di indirizzamento, i
modelli di rilocazione e di codice (sia come predefinito sia come maschera dei
supportati), il modello delle eccezioni (NONE, DWARF, SJLJ, SEH,
WASM), il modello di srotolamento, l'endianness, l'ampiezza di
pointer/int/long/long long, l'allineamento dello stack, le ampiezze massime
atomica e vettoriale, il tipo di va_list, i livelli di esecuzione (USER,
KERNEL, HYPERVISOR, FIRMWARE) e il supporto TLS.
Le funzioni integrate del target portano la propria callback di abbassamento, che riceve un costruttore IR vivo:
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(/* … */);
}Un ABI classifica le firme delle funzioni:
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();
}I generi di argomento sono DIRECT, EXTEND, INDIRECT, IGNORE,
EXPAND, INDIRECT_ALIASED e COERCE_AND_EXPAND; i flag sono BYVAL,
REALIGN, INREG, SRET_AFTER_THIS, CAN_BE_FLATTENED, SIGN_EXTEND e
PADDING_INREG. La coercizione è NONE, INTEGER, FLOAT o POINTER, e
COERCE_AND_EXPAND fornisce un array di NevercABICoercionElement.
Una convenzione di chiamata scende di un livello e assegna le posizioni effettive:
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 è un valore LOCKSTEP: RegisterNumber significa
qualcosa solo rispetto allo schema che nomina. Per l'esempio completo si vedano
Convenzioni di chiamata personalizzate e
pluginsdk/examples/CustomCallConvPlugin.c.
Una rotta viene scelta a partire dalla NevercTargetKey canonica:
identificatore del target, parti della tripla, CPU, CPU di tuning,
funzionalità, ABI, convenzione di chiamata, formato oggetto, modello di
rilocazione, modello di codice, livello di esecuzione, ampiezza dei puntatori,
endianness e digest dello schema. Registrate gli archi che sapete servire:
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);I generi di prodotto sono IR, MIR, MC, ASSEMBLY, OBJECT_GRAPH,
OBJECT_IMAGE e CUSTOM. La rotta a grana fine è
IR → MIR → MC → ObjectGraph → ObjectImage.
Impostare NEVERC_CODEGEN_EDGE_COARSE e fornire CoarseLower sostituisce in
un colpo solo l'intera tratta IR → ObjectImage:
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();
}Una rotta grossolana passa comunque per neverc.codegen.product_verify e per
il commit transazionale dell'output. VerifyProduct viene chiamata con gli
obblighi che l'host si aspetta siano stati assolti — VERIFY_FINAL_IR,
VERIFY_TARGET_KEY, VERIFY_PRODUCT_KIND, VERIFY_PRODUCT_ID,
VERIFY_STRUCTURE — così un provider non può saltare di soppiatto un gate
prendendo una scorciatoia.
Un MCUnit contiene sezioni, simboli, espressioni, frammenti, istruzioni,
operandi e fixup. La lettura è un'iterazione first/next:
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);
}La mutazione è transazionale, come ovunque:
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);Gli handle hanno ambito di task e sono controllati per generazione, così un handle proveniente da una mutazione abbandonata viene rifiutato anziché riutilizzato.
I flag di sezione sono ALLOCATED, EXECUTABLE, WRITABLE, MERGEABLE e
DEBUG. I binding dei simboli sono LOCAL, GLOBAL e WEAK; i tipi sono
NONE, FUNCTION, OBJECT, SECTION e TLS; le definizioni sono
UNDEFINED, SECTION, ABSOLUTE e COMMON. Le espressioni supportano gli
unari PLUS, MINUS, NOT e i binari ADD, SUBTRACT, MULTIPLY,
DIVIDE, AND, OR, XOR, SHIFT_LEFT, SHIFT_RIGHT. Passate
NEVERC_MC_AUTOMATIC_OFFSET dove volete che sia l'host a collocare qualcosa
per voi.
RegisterSchema pubblica uno schema MC di target, e GetSchemaToken /
GetSchemaTokenInfo risolvono un nome in un token LOCKSTEP e viceversa.
Il flusso di emissione riporta in ordine dieci generi di evento — uno per
ciascuna fase neverc.mc.emission.*. L'ABI riserva anche
NEVERC_MC_EMISSION_PRE_OBJECT_WRITE; la scrittura dell'oggetto in sé è la
fase separata neverc.object.pre_write.
Sottoscrivetevi come osservatori e leggete l'evento:
NevercMCEmissionEventInfo Event = {0};
Event.Header = /* … */;
Emission->GetEvent(Emission->Context, Frame, Frame->Input, &Event);
/* Event.Kind, Event.Flags */Flags dice quali parti dell'evento sono popolate: HAS_SECTION,
HAS_INSTRUCTION, HAS_ENCODING, HAS_FIXUP, HAS_LAYOUT e
CAN_REPLACE_INSTRUCTION. Controllate il flag prima di leggere il campo
corrispondente: un evento che non ha ancora una codifica non ne avrà una solo
perché l'avete chiesta.
GetLayoutSection, GetLayoutFragment, GetLayoutSymbol e
GetLayoutFixup forniscono indirizzi e dimensioni una volta impostato
HAS_LAYOUT.
A pre_instruction, e solo quando CAN_REPLACE_INSTRUCTION è impostato,
potete sostituire:
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 ne è la versione in sola lettura.
Tre registrazioni estendono il back-end del codice macchina, tutte indicizzate per target e digest dello schema:
MC->RegisterEncoder(MC->Context, RegistrarContext, &EncoderDescriptor);
MC->RegisterDecoder(MC->Context, RegistrarContext, &DecoderDescriptor);
MC->RegisterAsmBackend(MC->Context, RegistrarContext, &BackendDescriptor);Un encoder scrive attraverso un sink invece di restituire un buffer, il che lascia la proprietà dalla parte dell'host:
Sink->WriteBytes(Sink->Context, Bytes);
Sink->AddFixup(Sink->Context, &Fixup);Un decoder riporta uno tra NEVERC_MC_DECODE_SUCCESS, _SOFT_FAIL,
_UNKNOWN e _FAIL. I generi di fixup si autodescrivono tramite
NevercMCFixupKindInfo con i flag PC_RELATIVE, SIGNED, RELAXABLE e
TARGET.
Il back-end asm possiede il rilassamento. Il layout emette un digest di prova, e qualsiasi mutazione successiva al layout invalida quella prova e impone un nuovo layout prima che l'oggetto possa essere scritto: lo stesso schema di controllo per generazione usato dal grafo di collegamento.
Un provider di parsing consuma byte sorgente e pubblica un 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);Le sorgenti sono o NEVERC_ASSEMBLY_SOURCE_BUFFER o
NEVERC_ASSEMBLY_SOURCE_RENDERED_TOKENS. L'assembly preprocessato (.S)
attraversa prima il normale preprocessore del front-end e arriva come token
renderizzati; l'assembly puro (.s) entra direttamente nel parser come
buffer.
Uno stampatore fa il percorso inverso — GetPrintInput, poi
WritePrintOutput nella transazione di output fornita, poi
PublishAssemblyOutput. Scrivere altrove non è supportato: la verifica di
parsing/stampa e il gate di commit dell'host girano prima che i byte diventino
visibili, così una stampa fallita non lascia dietro di sé alcun file parziale.
NevercObjectAPI normalizza un file rilocabile in sezioni, simboli,
rilocazioni e COMDAT. Gli adattatori integrati coprono ELF, COFF e Mach-O;
RegisterFormat ne aggiunge un altro.
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);
}La mutazione segue il pattern crea/sostituisci/sposta/cancella per tutti e
quattro i generi di entità, preparata dentro BeginMutation …
CommitMutation / AbandonMutation.
I flag di sezione sono ALLOCATED, EXECUTABLE, WRITABLE, MERGEABLE,
STRINGS, TLS, DEBUG, UNWIND, DISCARDABLE e RETAIN. I bersagli
delle rilocazioni sono SYMBOL, SECTION, ABSOLUTE o FORMAT_EXTENSION.
Ogni descrittore ha una terna ExtensionOwner / ExtensionVersion /
Extension. È così che un formato conserva dati per i quali il grafo
normalizzato non ha alcun campo: i byte viaggiano con l'entità e ritornano in
scrittura, invece di essere persi nell'andata e ritorno.
L'adattatore ELF integrato registra i fatti nativi esatti in extension con tag:
NCSE v2 contiene indice, indirizzo, tipo, flag, offset nel file e dimensione
delle entry della sezione; NCSY v2 contiene st_info, l'intero st_other,
st_size e uno stato esplicito del nome nativo vuoto/non vuoto; NCRL v1
contiene il tipo di rilocazione nativo e il suo nome ufficiale. Un normale
simbolo ELF con nome vuoto rimane quindi vuoto e non viene mai riscritto come
nome sintetico $symbol.N; un simbolo sorgente chiamato davvero $symbol.N
rimane un normale nome non vuoto. Il passthrough di un'immagine nativa invariata
può preservare esattamente i simboli anonimi. Una scrittura integrata in cui il
grafo è autoritativo li rifiuta prima di aprire il sink di output, perché la
sintassi MC portabile non può ricostruire la stessa voce anonima della tabella
dei simboli. Gli audit canonici delle release Android richiedono i payload con
tag correnti esatti e da questi riproducono la proiezione stabile del grafo.
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 riporta una Confidence da 0 a
NEVERC_OBJECT_PROBE_MAX_CONFIDENCE (1000), il NevercObjectArtifactKind
riconosciuto (RELOCATABLE, ARCHIVE, EXECUTABLE_IMAGE, SHARED_IMAGE,
UNIVERSAL_BINARY) e un ConsumedMinimum — quanti byte gli sono serviti per
esserne certo, limitato a NEVERC_OBJECT_PROBE_MAX_CONSUMED_MINIMUM (65536).
Vince la confidenza più alta.
A Reader vengono consegnati un grafo e una mutazione aperta, che riempie. A
Writer vengono consegnati il grafo, la sua prova di layout e il costruttore
binario limitato.
NevercObjectFormatDescriptor.Header.Minor dichiara la capacità del provider;
non è un selettore di modalità globale dell'host. Un descriptor 1.0 resta
pienamente compatibile per probe, read e le normali scritture predefinite. Il
suo writer riceve NevercObjectWriteRequest.Header.Minor == 0 e
Header.Flags == 0. Dichiarate minor 1 solo se il writer comprende i request
flags 1.1; una scrittura ordinaria continua ad avere flags zero e conserva il
comportamento di output precedente alla 1.1.
Object Format 1.1 definisce questi bit in
NevercObjectWriteRequest.Header.Flags:
NEVERC_OBJECT_WRITE_CANONICAL_ELF_TABLESrichiede sezioni.strtabe.shstrtabcanoniche e distinte, e rimappa tutti gli indici dipendenti. Questa è una canonicalizzazione delle tabelle ELF, non un link rilocabile: restano intatti l'ordine delle sezioni, i gruppi COMDAT, i metadati del linker, i simboli duplicati e gli alias, i record di rilocazione e ogni payload esterno alle tabelle dei nomi. Le ulteriori sezioniSHT_STRTABvalide e specifiche del formato vengono conservate; vengono ricostruite solo la tabella delle stringhe delSHT_SYMTABselezionato e la tabella indicata dae_shstrndx. ConDROP_DEBUG_INFOvengono filtrate soltanto le sezioni di debug e i metadati che fanno riferimento agli indici rimossi di tali sezioni.NEVERC_OBJECT_WRITE_ANDROID_KERNEL_RELEASErende inoltre autorevole l'ELF serializzato finale: rimuove i mapping symbols sintetizzati dal writer e rigenera i nomi release dalle coordinate effettive delle sezioni serializzate.NEVERC_OBJECT_WRITE_DROP_DEBUG_INFOrichiede la rimozione delle sezioni di debug come parte di una di queste politiche ELF.
NEVERC_OBJECT_WRITE_REQUEST_KNOWN_FLAGS è la maschera completa dei bit noti.
Le sole combinazioni valide sono 0, CANONICAL_ELF_TABLES,
CANONICAL_ELF_TABLES | DROP_DEBUG_INFO,
CANONICAL_ELF_TABLES | ANDROID_KERNEL_RELEASE e tutti e tre i bit insieme. Un
bit release o debug senza il bit canonical non è valido.
L'host rifiuta una combinazione sconosciuta o non valida, o qualsiasi richiesta
speciale a un provider minor-0, prima di aprire il sink di output. Anche un
writer 1.1 deve rifiutare, anziché ignorare, qualsiasi flag sconosciuto o non
valido che riceve. Dopo il writer e ogni intercettore object.post_write, la
convalida semantica dell'host e il
object.final_verify sigillato riesaminano i byte serializzati e fanno testo.
Questi flags non sono una promessa generale per ogni formato di terze parti.
Minor 1 significa che il writer comprende il protocollo dei flags: può
implementare una politica ELF applicabile oppure restituire esplicitamente
NEVERC_STATUS_CAPABILITY_UNAVAILABLE se non è applicabile o supportata; non
deve mai ignorare silenziosamente la richiesta.
Quando --strip finalizza un .ko Android, la generica API mutable object
descritta sopra viene limitata al percorso di scrittura attendibile stabilito
dall'host. Questo confine ha due sigilli d'identità indipendenti:
- prima di qualsiasi fase
ObjectGraphsostituibile, il sigillo del grafo legasection ID,final ordinale nome esatto di ogni sezione logica conservata, oltre asymbol ID, owner, classe, sezione, valore, dimensione, binding, tipo est_othercompleto di ogni simbolo con nome esatto; - dopo che il writer dell'host ha creato la baseline attendibile dell'immagine,
il sigillo dell'immagine lega ordinal e nome esatto di ogni sezione conservata,
il numero totale di voci
.symtab, nonchéslot.symtabgrezzo e attributi di ogni simbolo con nome esatto. Il verificatore completo della release ricalcola indipendentemente ogni nome strutturale di release.
| Binding | Comportamento della release Android finalizzata |
|---|---|
neverc.object.write provider / interceptor |
REJECTED prima del callback; non può sostituire il percorso di scrittura attendibile |
plugin-owned ObjectFormat graph writer |
REJECTED; questo percorso richiede il graph writer dell'host che stabilisce la baseline attendibile |
observer |
READ_ONLY; può ispezionare ma non mutare né sostituire l'output |
neverc.object.post_write interceptor |
VALIDATED; la sua API mutable limitata può cambiare solo il payload fuori dalla superficie ABI e d'identità verificata strutturalmente, e il risultato deve superare i controlli ABI di input, entrambi i sigilli e il verificatore completo della release |
Anche la proprietà del merge finalizzato è sigillata dall'host. Qualsiasi
MergedImage o byte indipendente di un third-party ObjectMergeProvider viene
scartato; il host-owned graph writer serializza il grafo verificato e
finalizzato di quel provider. Viceversa, built-in finalized input serialization
aggira le external object phases e passa al merger dell'host gli esatti
audited native bytes; questo passo di input interno non aggira il confine di
output precedente.
La finalizzazione è accettata solo con Android module merge semantics; richiede
anche sia una relocatable output request sia una
relocatable driver configuration, altrimenti fallisce before routing. Per
una release Android relocatable finalizzata, frozen input format,
TargetKey.ObjectFormatID e frozen output format devono condividere
one format identity. Una discrepanza viene rifiutata
before provider dispatch, quindi anche prima del route planning o della
creazione del sink; capability preflight e graph-writer dispatch effettivo non
possono così osservare formati diversi.
Il native-image passthrough rifiuta ogni route-matching provider sostituibile
e ogni interceptor. Un provider la cui route
target/CPU/features/object-format/execution-level non corrisponde non viene
eseguito e non blocca la release; ammette solo observer. Solo un rifiuto o una
validazione fallita
before sealed commit interrompe lo staging senza pubblicare file. Un errore
di observer AFTER_COMMIT viene segnalato dopo la pubblicazione e non può
ripristinare il file già pubblicato.
- sondare e leggere i byte in un ObjectGraph;
- eseguire gli intercettori di grafo
object.pre_write; - fare il layout, poi eseguire
object.post_layout(rifare il layout dopo ogni mutazione); - scrivere un'immagine candidata limitata;
- eseguire gli intercettori binari
object.post_write; - eseguire il sigillato
object.final_verifye l'atomicoobject.commit.
Lo stato dell'immagine passa per CANDIDATE → VERIFIED → COMMITTED,
oppure ABORTED / FAILED_PARTIAL.
Gli osservatori ricevono ponti in sola lettura; una mutazione tentata da un
osservatore viene respinta con NEVERC_STATUS_POLICY_VIOLATION. Gli scrittori
e gli intercettori post-scrittura ottengono solo il costruttore limitato
NevercMutableBinaryAPI — Reserve, Write, WriteAt, Tell, ReadAt,
Insert, Append, Resize. Un overflow, una callback fallita o una verifica
fallita interrompono la preparazione, così un fallimento non lascia mai mezzo
file sul disco.
pluginsdk/examples/ObjectRewritePlugin.c è una riscrittura transazionale
completa.
- Confrontate il digest dello schema prima di consumare qualsiasi valore LOCKSTEP di opcode, registro, operando, fixup, rilocazione o convenzione di chiamata.
- Tenete lo stato mutabile nello stato process, session e task fornito dall'host.
- Non mettete in cache handle di task o viste prese in prestito dopo il ritorno di una callback.
- Invocate la continuazione di un intercettore al massimo una volta, sul thread della callback.
- Ogni
BeginMutationarriva a esattamente un commit o un abbandono. - Rifate il layout dopo aver mutato un MCUnit o un ObjectGraph già disposto: la vecchia prova di layout è scaduta e l'host la rifiuterà.
- Controllate
NevercMCEmissionEventInfo.Flagsprima di leggere un campo dell'evento, e sostituite un'istruzione solo quandoCAN_REPLACE_INSTRUCTIONè impostato. - Scrivete l'output solo attraverso la transazione o il sink di byte forniti.
- In caso di fallimento restituite il
NevercStatusoriginale e non pubblicate nulla di parziale. - Dichiarate i modelli di concorrenza e rientranza più stretti che siano veritieri.
codegen.product_verify,assembly.final_verify,assembly.commit,object.final_verifyeobject.commitsono sigillati. Solo osservazione.
Per le dichiarazioni normative si vedano PluginTarget.h, PluginMC.h,
PluginObject.h e Schema/PhaseSchema.json; i generi di entità, operando,
fixup e sezione che usano provengono da Schema/MCSchema.json e
Schema/ObjectSchema.json, che generano Schema/PluginMCSchema.inc e
Schema/PluginObjectSchema.inc. Si veda anche coverage.json, che mappa
ciascuna di queste fasi stabili sui suoi test positivi, negativi, di
sostituzione, di osservatore in sola lettura e di gate sigillato.