Langues : English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
Le back-end tient en quatre en-têtes et vingt-neuf phases. PluginTarget.h
décrit une cible et les routes qui traversent la génération de code.
PluginMC.h construit et observe le code machine. L'analyse et l'impression
d'assembleur vivent dans le même en-tête. PluginObject.h transforme un
fichier relogeable en graphe normalisé, et inversement.
Ensemble, ils permettent à un plugin d'ajouter une cible, de remplacer une
étape d'abaissement ou toutes, de surveiller chaque instruction au moment de
son émission, de définir un dialecte d'assembleur ou de réécrire un fichier
objet — à travers un ABI C pur qui n'expose jamais un MCInst, un
MCSection ni un object::ObjectFile de LLVM.
#include "neverc/Plugin/PluginTarget.h"
#include "neverc/Plugin/PluginMC.h"
#include "neverc/Plugin/PluginObject.h" /* includes both of the above */| Interface | Table | Emplacements | Rôle |
|---|---|---|---|
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 | Lire et modifier un MCUnit ; enregistrer encodeurs, décodeurs, back-ends |
NEVERC_INTERFACE_MC_EMISSION_* |
NevercMCEmissionAPI |
7 | Événements d'émission et instantanés de disposition |
NEVERC_INTERFACE_MC_PROVIDER_* |
NevercMCProviderAPI |
4 | Remplacer MIR → MC |
NEVERC_INTERFACE_ASSEMBLY_PROVIDER_* |
NevercAssemblyProviderAPI |
8 | Remplacer l'analyseur ou l'imprimeur d'assembleur |
NEVERC_INTERFACE_OBJECT_* |
NevercObjectAPI |
34 | Lire et modifier un ObjectGraph |
NEVERC_INTERFACE_OBJECT_FORMAT_* |
NevercObjectFormatAPI |
1 | RegisterFormat |
NEVERC_INTERFACE_OBJECT_PHASE_* |
NevercObjectPhaseAPI |
2 | GetGraph, GetImage |
C'est la règle qui gouverne tout le reste de ce document.
STABLE, et sûr à coder en dur : les descripteurs indépendants de la cible, les identifiants de phase, les identifiants d'artefact, les conteneurs MC et ObjectGraph, les transactions de sortie et tous les contrats de rappel.
LOCKSTEP, et dangereux sans vérification : les schémas d'opcode, de registre, d'opérande, de fixup, de relogement et de convention d'appel propres à une cible. Leurs valeurs numériques n'ont de sens que face à une révision de schéma bien précise.
Partout où apparaît une valeur LOCKSTEP, une empreinte de schéma l'accompagne. Comparez-la avant de lire la valeur :
if (!string_equal(Target.SchemaDigest, MY_COMPILED_SCHEMA_DIGEST))
return fail(NEVERC_STATUS_ABI_MISMATCH);NeverC rejette lui aussi un schéma discordant avant d'invoquer un fournisseur, donc cette vérification tient de la ceinture et des bretelles — mais un plugin qui la saute et lit tout de même un opcode brut interprétera silencieusement les instructions de travers.
Vingt-neuf, réparties en quatre domaines.
| Phase | Politique |
|---|---|
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 et neverc.mc.layout sont OBSERVABLE,
INTERCEPTABLE, REPLACEABLE.
neverc.mc.emission.pre_instruction est le seul événement d'émission qui soit
aussi REPLACEABLE — c'est là que l'on substitue une instruction. Les neuf
autres (unit_begin, unit_end, section_change, post_instruction,
post_encode, fixup, relaxation_round, pre_layout, post_layout) sont
en observation seule.
neverc.assembly.parse et neverc.assembly.print sont REPLACEABLE.
neverc.assembly.final_verify et neverc.assembly.commit sont SEALED.
neverc.object.probe, read, write, pre_write et post_layout sont
REPLACEABLE ; neverc.object.post_write est seulement INTERCEPTABLE ;
neverc.object.final_verify et neverc.object.commit sont SEALED.
NevercTargetDescriptor est le plus grand descripteur de l'ABI, parce qu'il
transporte tout ce que le front-end et le back-end doivent savoir :
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 décide quand la cible est sélectionnée : chaque filtre nomme
une architecture, un fournisseur, un système d'exploitation et un
environnement, plus une Priority qui départage face aux cibles intégrées.
Machine est un NevercTargetMachineDescriptor — disposition des données,
CPU par défaut et de réglage, table des fonctionnalités, ABI, conventions
d'appel et formats objet pris en charge, espaces d'adressage, modèles de
relogement et de code (à la fois par défaut et sous forme de masque de
prise en charge), modèle d'exceptions (NONE, DWARF, SJLJ, SEH,
WASM), modèle de déroulement, boutisme, largeur de pointer/int/long/long
long, alignement de pile, largeurs atomique et vectorielle maximales, type de
va_list, niveaux d'exécution (USER, KERNEL, HYPERVISOR, FIRMWARE) et
prise en charge du TLS.
Les fonctions intégrées de la cible portent leur propre rappel d'abaissement, qui reçoit un constructeur d'IR bien vivant :
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 classe les signatures de fonction :
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();
}Les genres d'argument sont DIRECT, EXTEND, INDIRECT, IGNORE,
EXPAND, INDIRECT_ALIASED et COERCE_AND_EXPAND ; les drapeaux sont
BYVAL, REALIGN, INREG, SRET_AFTER_THIS, CAN_BE_FLATTENED,
SIGN_EXTEND et PADDING_INREG. La coercition vaut NONE, INTEGER,
FLOAT ou POINTER, et COERCE_AND_EXPAND fournit un tableau de
NevercABICoercionElement.
Une convention d'appel descend d'un cran et attribue les emplacements réels :
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 est une valeur LOCKSTEP — RegisterNumber n'a de sens
que face au schéma qu'il nomme. Voir
Conventions d'appel personnalisées et
pluginsdk/examples/CustomCallConvPlugin.c pour l'exemple complet.
Une route est choisie à partir de la NevercTargetKey canonique :
identifiant de cible, parties du triplet, CPU, CPU de réglage,
fonctionnalités, ABI, convention d'appel, format objet, modèle de relogement,
modèle de code, niveau d'exécution, largeur de pointeur, boutisme et empreinte
de schéma. Enregistrez les arêtes que vous savez servir :
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);Les genres de produit sont IR, MIR, MC, ASSEMBLY, OBJECT_GRAPH,
OBJECT_IMAGE et CUSTOM. La route à grain fin est
IR → MIR → MC → ObjectGraph → ObjectImage.
Poser NEVERC_CODEGEN_EDGE_COARSE et fournir CoarseLower remplace d'un seul
coup toute l'étendue 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();
}Une route grossière passe malgré tout par neverc.codegen.product_verify et
par la validation transactionnelle de la sortie. VerifyProduct est appelée
avec les obligations que l'hôte attend de vous — VERIFY_FINAL_IR,
VERIFY_TARGET_KEY, VERIFY_PRODUCT_KIND, VERIFY_PRODUCT_ID,
VERIFY_STRUCTURE — de sorte qu'un fournisseur ne peut pas discrètement
sauter une porte en prenant un raccourci.
Un MCUnit contient des sections, des symboles, des expressions, des
fragments, des instructions, des opérandes et des fixups. La lecture se fait
par itération 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 mutation est transactionnelle, comme partout ailleurs :
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);Les handles ont la portée de la tâche et sont vérifiés par génération : un handle issu d'une mutation abandonnée est donc rejeté plutôt que réutilisé.
Les drapeaux de section sont ALLOCATED, EXECUTABLE, WRITABLE,
MERGEABLE et DEBUG. Les liaisons de symbole sont LOCAL, GLOBAL et
WEAK ; les types sont NONE, FUNCTION, OBJECT, SECTION et TLS ; les
définitions sont UNDEFINED, SECTION, ABSOLUTE et COMMON. Les
expressions acceptent les opérateurs unaires PLUS, MINUS, NOT et
binaires ADD, SUBTRACT, MULTIPLY, DIVIDE, AND, OR, XOR,
SHIFT_LEFT, SHIFT_RIGHT. Passez NEVERC_MC_AUTOMATIC_OFFSET là où vous
voulez que l'hôte place quelque chose à votre place.
RegisterSchema publie un schéma MC de cible, et GetSchemaToken /
GetSchemaTokenInfo convertissent un nom en jeton LOCKSTEP et inversement.
Le flux d'émission signale dix genres d'événements dans l'ordre — un par phase
neverc.mc.emission.*. L'ABI réserve aussi
NEVERC_MC_EMISSION_PRE_OBJECT_WRITE ; l'écriture d'objet elle-même est la
phase séparée neverc.object.pre_write. Abonnez-vous
comme observateur et lisez l'événement :
NevercMCEmissionEventInfo Event = {0};
Event.Header = /* … */;
Emission->GetEvent(Emission->Context, Frame, Frame->Input, &Event);
/* Event.Kind, Event.Flags */Flags indique quelles parties de l'événement sont renseignées :
HAS_SECTION, HAS_INSTRUCTION, HAS_ENCODING, HAS_FIXUP, HAS_LAYOUT et
CAN_REPLACE_INSTRUCTION. Vérifiez le drapeau avant de lire le champ
correspondant — un événement qui n'a pas encore d'encodage n'en aura pas
davantage parce que vous l'avez demandé.
GetLayoutSection, GetLayoutFragment, GetLayoutSymbol et
GetLayoutFixup donnent adresses et tailles une fois HAS_LAYOUT posé.
À pre_instruction, et seulement quand CAN_REPLACE_INSTRUCTION est posé,
vous pouvez substituer :
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 en est la version en lecture seule.
Trois enregistrements étendent le back-end de code machine, tous indexés par cible et par empreinte de schéma :
MC->RegisterEncoder(MC->Context, RegistrarContext, &EncoderDescriptor);
MC->RegisterDecoder(MC->Context, RegistrarContext, &DecoderDescriptor);
MC->RegisterAsmBackend(MC->Context, RegistrarContext, &BackendDescriptor);Un encodeur écrit à travers un puits plutôt que de renvoyer un tampon, ce qui laisse la propriété du côté de l'hôte :
Sink->WriteBytes(Sink->Context, Bytes);
Sink->AddFixup(Sink->Context, &Fixup);Un décodeur signale NEVERC_MC_DECODE_SUCCESS, _SOFT_FAIL, _UNKNOWN ou
_FAIL. Les genres de fixup se décrivent eux-mêmes via
NevercMCFixupKindInfo avec les drapeaux PC_RELATIVE, SIGNED,
RELAXABLE et TARGET.
Le back-end d'assemblage possède la relaxation. La disposition émet une empreinte de preuve, et toute mutation postérieure invalide cette preuve et force une nouvelle disposition avant que l'objet puisse être écrit — le même schéma de vérification par génération que celui du graphe de liaison.
Un fournisseur d'analyseur consomme des octets source et publie 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);Les sources sont soit NEVERC_ASSEMBLY_SOURCE_BUFFER, soit
NEVERC_ASSEMBLY_SOURCE_RENDERED_TOKENS. L'assembleur prétraité (.S) passe
d'abord par le préprocesseur frontal normal et arrive sous forme de jetons
rendus ; l'assembleur simple (.s) entre directement dans l'analyseur comme
tampon.
Un imprimeur fait le chemin inverse — GetPrintInput, puis
WritePrintOutput dans la transaction de sortie fournie, puis
PublishAssemblyOutput. Écrire ailleurs n'est pas pris en charge : la
vérification d'analyse/impression et la porte de validation de l'hôte
s'exécutent avant que les octets ne deviennent visibles, si bien qu'une
impression ratée ne laisse aucun fichier partiel derrière elle.
NevercObjectAPI normalise un fichier relogeable en sections, symboles,
relogements et COMDAT. Les adaptateurs intégrés couvrent ELF, COFF et Mach-O ;
RegisterFormat en ajoute un autre.
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 mutation suit le schéma create/replace/move/erase pour les quatre genres
d'entités, préparée à l'intérieur de BeginMutation … CommitMutation /
AbandonMutation.
Les drapeaux de section sont ALLOCATED, EXECUTABLE, WRITABLE,
MERGEABLE, STRINGS, TLS, DEBUG, UNWIND, DISCARDABLE et RETAIN.
Les cibles de relogement sont SYMBOL, SECTION, ABSOLUTE ou
FORMAT_EXTENSION.
Chaque descripteur possède un triplet ExtensionOwner / ExtensionVersion /
Extension. C'est ainsi qu'un format conserve des données pour lesquelles le
graphe normalisé n'a pas de champ — les octets voyagent avec l'entité et
reviennent à l'écriture, au lieu d'être perdus par l'aller-retour.
L'adaptateur ELF intégré enregistre les faits natifs exacts dans des extensions
balisées : NCSE v2 contient l'index, l'adresse, le type, les flags, l'offset
de fichier et la taille d'entrée de la section ; NCSY v2 contient st_info,
le st_other complet, st_size et un état explicite de nom natif vide/non
vide ; NCRL v1 contient le type de relocalisation natif et son nom officiel.
Un symbole ELF ordinaire sans nom reste donc vide et n'est jamais réécrit sous
un nom synthétique $symbol.N ; un symbole source réellement nommé
$symbol.N reste un nom ordinaire non vide. Le passthrough d'une image native
inchangée peut préserver exactement les symboles anonymes. Une écriture
intégrée où le graphe fait autorité les refuse avant d'ouvrir le sink de sortie,
car l'orthographe MC portable ne peut pas reconstruire la même entrée anonyme
de table des symboles. Les audits canoniques de release Android exigent les
payloads balisés courants exacts et rejouent à partir d'eux la projection
stable du graphe.
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 rapporte une Confidence de 0 à
NEVERC_OBJECT_PROBE_MAX_CONFIDENCE (1000), le NevercObjectArtifactKind
qu'il a reconnu (RELOCATABLE, ARCHIVE, EXECUTABLE_IMAGE,
SHARED_IMAGE, UNIVERSAL_BINARY) et un ConsumedMinimum — le nombre
d'octets qu'il lui a fallu pour en être sûr, plafonné à
NEVERC_OBJECT_PROBE_MAX_CONSUMED_MINIMUM (65536). La plus grande confiance
l'emporte.
Reader reçoit un graphe et une mutation ouverte, et les remplit. Writer
reçoit le graphe, sa preuve de disposition et le constructeur binaire borné.
NevercObjectFormatDescriptor.Header.Minor annonce la capacité du provider ;
ce n'est pas un sélecteur de mode global de l'hôte. Un descriptor 1.0 reste
entièrement compatible pour probe, read et les écritures ordinaires par défaut.
Son writer reçoit NevercObjectWriteRequest.Header.Minor == 0 et
Header.Flags == 0. N'annoncez minor 1 que si le writer comprend les request
flags 1.1 ; une écriture ordinaire garde des flags nuls et le comportement de
sortie antérieur à 1.1.
Object Format 1.1 définit les bits suivants dans
NevercObjectWriteRequest.Header.Flags :
NEVERC_OBJECT_WRITE_CANONICAL_ELF_TABLESexige des sections.strtabet.shstrtabcanoniques et distinctes, ainsi que le remappage de tous les indices qui en dépendent. Il s'agit d'une normalisation canonique des tables ELF, et non d'une édition de liens relogeable : l'ordre des sections, les groupes COMDAT, les métadonnées du linker, les symboles dupliqués et les alias, les enregistrements de relocalisation et tout contenu hors tables de noms restent intacts. Les sectionsSHT_STRTABsupplémentaires valides et propres au format sont conservées ; seules la table de chaînes duSHT_SYMTABsélectionné et la table désignée pare_shstrndxsont reconstruites. AvecDROP_DEBUG_INFO, seules les sections de debug et les métadonnées qui référencent les indices supprimés de ces sections sont filtrées.NEVERC_OBJECT_WRITE_ANDROID_KERNEL_RELEASErend en plus l'ELF sérialisé final faisant autorité : il supprime les mapping symbols synthétisés par le writer et rejoue les noms release depuis les coordonnées réelles des sections sérialisées.NEVERC_OBJECT_WRITE_DROP_DEBUG_INFOdemande la suppression des sections de debug dans le cadre de l'une de ces politiques ELF.
NEVERC_OBJECT_WRITE_REQUEST_KNOWN_FLAGS est le masque complet des bits connus.
Les seules combinaisons valides sont 0, CANONICAL_ELF_TABLES,
CANONICAL_ELF_TABLES | DROP_DEBUG_INFO,
CANONICAL_ELF_TABLES | ANDROID_KERNEL_RELEASE et les trois bits ensemble.
Un bit release ou debug sans le bit canonical est invalide.
L'hôte rejette toute combinaison inconnue ou invalide, ainsi que toute requête
spéciale adressée à un provider minor-0, avant d'ouvrir le sink de sortie. Un
writer 1.1 doit également rejeter, et non ignorer, tout flag inconnu ou invalide
qu'il reçoit. Après le writer et tout intercepteur object.post_write, la
validation sémantique de
l'hôte et le object.final_verify scellé réauditent les octets sérialisés et
font autorité. Ces flags ne constituent pas une promesse générale pour tous les
formats tiers. Minor 1 signifie que le writer comprend le protocole des flags :
il peut implémenter une politique ELF applicable ou retourner explicitement
NEVERC_STATUS_CAPABILITY_UNAVAILABLE si elle ne s'applique pas ou n'est pas
prise en charge ; il ne doit jamais ignorer silencieusement la requête.
Quand --strip finalise un .ko Android, l'API générique d'objet mutable
ci-dessus se restreint au chemin d'écriture fiable établi par l'hôte. Cette
frontière comporte deux sceaux d'identité indépendants :
- avant toute phase
ObjectGraphremplaçable, le sceau du graphe lie lesection ID, lefinal ordinalet le nom exact de chaque section logique conservée, ainsi que lesymbol ID, l'owner, la classe, la section, la valeur, la taille, le binding, le type et lest_othercomplet de chaque symbole à nom exact ; - après que le writer appartenant à l'hôte a créé la baseline fiable de
l'image, le sceau d'image lie l'ordinal et le nom exact de chaque section
conservée, le nombre total d'entrées
.symtab, ainsi que leslot.symtabbrut et les attributs de chaque symbole à nom exact. Le vérificateur complet de release recalcule séparément chaque nom structurel de release.
| Binding | Comportement de la release Android finalisée |
|---|---|
neverc.object.write provider / interceptor |
REJECTED avant le callback ; il ne peut pas remplacer le chemin d'écriture fiable |
plugin-owned ObjectFormat graph writer |
REJECTED ; ce chemin exige le graph writer appartenant à l'hôte qui établit la baseline fiable |
observer |
READ_ONLY ; il peut inspecter, sans modifier ni remplacer la sortie |
neverc.object.post_write interceptor |
VALIDATED ; son API mutable bornée ne peut modifier que le payload hors de la surface ABI et d'identité vérifiée structurellement, et le résultat doit passer les contrôles ABI d'entrée, les deux sceaux et le vérificateur complet de release |
La propriété du merge finalisé est elle aussi scellée par l'hôte. Tout
MergedImage ou octet indépendant d'un third-party ObjectMergeProvider est
écarté ; le host-owned graph writer sérialise le graphe vérifié et finalisé
de ce provider. Inversement, built-in finalized input serialization contourne
les external object phases et fournit au merger de l'hôte les exacts
audited native bytes ; cette étape d'entrée interne ne contourne pas la
frontière de sortie ci-dessus.
La finalisation n'est acceptée qu'avec Android module merge semantics ; elle
exige aussi à la fois une relocatable output request et une
relocatable driver configuration, sinon elle échoue before routing. Pour
une release Android relocatable finalisée, frozen input format,
TargetKey.ObjectFormatID et frozen output format doivent partager
one format identity. Une divergence est refusée before provider dispatch,
donc également avant le route planning ou la création du sink ; le capability
preflight et le graph-writer dispatch réel ne peuvent ainsi observer des
formats différents.
Le native-image passthrough rejette chaque route-matching provider remplaçable
et chaque interceptor. Un provider dont la route
target/CPU/features/object-format/execution-level ne correspond pas ne
s'exécute pas et ne bloque pas la release ; seuls les observers sont admis.
Seul un refus ou un échec de validation
before sealed commit annule le staging et ne publie aucun fichier. L'échec
d'un observer AFTER_COMMIT est signalé après publication et ne peut pas
annuler le fichier déjà publié.
- sonder et lire les octets dans un ObjectGraph ;
- exécuter les intercepteurs de graphe
object.pre_write; - disposer, puis exécuter
object.post_layout(nouvelle disposition après toute mutation) ; - écrire une image candidate bornée ;
- exécuter les intercepteurs binaires
object.post_write; - exécuter le
object.final_verifyscellé et leobject.commitatomique.
L'état de l'image évolue en CANDIDATE → VERIFIED → COMMITTED, ou en
ABORTED / FAILED_PARTIAL.
Les observateurs reçoivent des ponts en lecture seule ; une mutation tentée
depuis un observateur est rejetée avec NEVERC_STATUS_POLICY_VIOLATION. Les
écrivains et les intercepteurs post-écriture n'obtiennent que le constructeur
borné NevercMutableBinaryAPI — Reserve, Write, WriteAt, Tell,
ReadAt, Insert, Append, Resize. Un dépassement, un rappel en échec ou
une vérification ratée annule la préparation, si bien qu'un échec ne laisse
jamais un demi-fichier sur le disque.
pluginsdk/examples/ObjectRewritePlugin.c est une réécriture transactionnelle
complète.
- Comparez l'empreinte de schéma avant de consommer toute valeur LOCKSTEP d'opcode, de registre, d'opérande, de fixup, de relogement ou de convention d'appel.
- Gardez l'état mutable dans l'état process, session et task fourni par l'hôte.
- Ne mettez pas en cache les handles de tâche ni les vues empruntées après le retour d'un rappel.
- Invoquez la continuation d'un intercepteur au plus une fois, sur le thread du rappel.
- Chaque
BeginMutationaboutit à exactement une validation ou un abandon. - Redisposez après avoir muté un MCUnit ou un ObjectGraph déjà disposé ; l'ancienne preuve de disposition est périmée et l'hôte la rejettera.
- Vérifiez
NevercMCEmissionEventInfo.Flagsavant de lire un champ d'événement, et ne remplacez une instruction que siCAN_REPLACE_INSTRUCTIONest posé. - N'écrivez la sortie qu'à travers la transaction ou le puits d'octets fourni.
- Renvoyez le
NevercStatusd'origine en cas d'échec et ne publiez rien de partiel. - Déclarez les modèles de concurrence et de réentrance les plus étroits qui soient vrais.
codegen.product_verify,assembly.final_verify,assembly.commit,object.final_verifyetobject.commitsont scellés. Observez seulement.
Voir PluginTarget.h, PluginMC.h, PluginObject.h et
Schema/PhaseSchema.json pour les déclarations normatives ; les genres
d'entité, d'opérande, de fixup et de section qu'ils emploient viennent de
Schema/MCSchema.json et Schema/ObjectSchema.json, qui engendrent
Schema/PluginMCSchema.inc et Schema/PluginObjectSchema.inc. Voir
aussi coverage.json, qui associe chacune de ces phases stables à ses
tests positifs, négatifs, de remplacement, d'observateur en lecture seule et
de porte scellée.