言語: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
バックエンドは 4 つのヘッダーと 29 のフェーズです。PluginTarget.h はターゲット
とコード生成の経路を記述します。PluginMC.h は機械語を構築し観測します。アセン
ブリの解析と出力も同じヘッダーにあります。PluginObject.h は再配置可能ファイル
を正規化されたグラフに変換し、また元に戻します。
これらを合わせると、プラグインはターゲットを追加し、低位化ステップの 1 つまたは
全部を差し替え、命令が発行される様子をすべて監視し、アセンブリ方言を定義し、オブ
ジェクトファイルを書き換えられます——しかもすべて、LLVM の MCInst、MCSection、
object::ObjectFile を一切露出しない純粋な C ABI を通してです。
#include "neverc/Plugin/PluginTarget.h"
#include "neverc/Plugin/PluginMC.h"
#include "neverc/Plugin/PluginObject.h" /* includes both of the above */| インターフェース | テーブル | スロット | 目的 |
|---|---|---|---|
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 | MCUnit の読み取りと変更、エンコーダー・デコーダー・バックエンドの登録 |
NEVERC_INTERFACE_MC_EMISSION_* |
NevercMCEmissionAPI |
7 | 発行イベントとレイアウトのスナップショット |
NEVERC_INTERFACE_MC_PROVIDER_* |
NevercMCProviderAPI |
4 | MIR → MC の置き換え |
NEVERC_INTERFACE_ASSEMBLY_PROVIDER_* |
NevercAssemblyProviderAPI |
8 | アセンブリパーサーまたはプリンターの置き換え |
NEVERC_INTERFACE_OBJECT_* |
NevercObjectAPI |
34 | ObjectGraph の読み取りと変更 |
NEVERC_INTERFACE_OBJECT_FORMAT_* |
NevercObjectFormatAPI |
1 | RegisterFormat |
NEVERC_INTERFACE_OBJECT_PHASE_* |
NevercObjectPhaseAPI |
2 | GetGraph、GetImage |
ここから先のすべてを支配するのがこの規則です。
STABLE、ハードコードしても安全なもの: ターゲット非依存のディスクリプター、 フェーズ ID、アーティファクト ID、MC と ObjectGraph のコンテナー、出力トランザク ション、そしてすべてのコールバック契約。
LOCKSTEP、確認なしでは危険なもの: ターゲット固有のオペコード、レジスター、オ ペランド、フィックスアップ、再配置、呼び出し規約のスキーマ。これらの数値は、ある 特定のスキーマ改訂に対してのみ意味を持ちます。
LOCKSTEP の値が現れる場所には必ず、その隣にスキーマダイジェストがあります。値を読 む前に比較してください:
if (!string_equal(Target.SchemaDigest, MY_COMPILED_SCHEMA_DIGEST))
return fail(NEVERC_STATUS_ABI_MISMATCH);NeverC もプロバイダーを呼ぶ前に不一致のスキーマを拒否するので、このチェックは二重 の備えです——とはいえ、これを飛ばして生のオペコードを読んでしまうプラグインは、黙 って命令を読み違えます。
29 個、4 つのドメインに分かれています。
| フェーズ | ポリシー |
|---|---|
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、neverc.mc.layout は OBSERVABLE、
INTERCEPTABLE、REPLACEABLE です。
neverc.mc.emission.pre_instruction は REPLACEABLE でもある唯一の発行イベントで
——命令を差し替えるのはそこです。残り 9 つ(unit_begin、unit_end、
section_change、post_instruction、post_encode、fixup、
relaxation_round、pre_layout、post_layout)は観測専用です。
neverc.assembly.parse と neverc.assembly.print は REPLACEABLE です。
neverc.assembly.final_verify と neverc.assembly.commit は SEALED です。
neverc.object.probe、read、write、pre_write、post_layout は
REPLACEABLE、neverc.object.post_write は INTERCEPTABLE のみ、
neverc.object.final_verify と neverc.object.commit は SEALED です。
NevercTargetDescriptor はこの ABI で最大のディスクリプターです。フロントエンド
とバックエンドが知る必要のあるものをすべて運ぶからです:
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 はターゲットがいつ選ばれるかを決めます。各マッチャーはアーキテク
チャー、ベンダー、OS、環境を指定し、さらに組み込みターゲットとの同点を破るための
Priority を持ちます。
Machine は NevercTargetMachineDescriptor です——データレイアウト、既定および
チューニング用 CPU、機能テーブル、対応 ABI・呼び出し規約・オブジェクト形式、アド
レス空間、再配置モデルとコードモデル(既定値と対応マスクの両方)、例外モデル
(NONE、DWARF、SJLJ、SEH、WASM)、巻き戻しモデル、エンディアン、
pointer/int/long/long long の幅、スタックアライメント、アトミックとベクターの最大
幅、va_list の種類、実行レベル(USER、KERNEL、HYPERVISOR、FIRMWARE)、
そして TLS 対応。
ターゲット組み込み関数はそれぞれ独自の低位化コールバックを持ち、そこには生きた IR ビルダーが渡されます:
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(/* … */);
}ABI は関数シグネチャを分類します:
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();
}引数の種別は DIRECT、EXTEND、INDIRECT、IGNORE、EXPAND、
INDIRECT_ALIASED、COERCE_AND_EXPAND。フラグは BYVAL、REALIGN、INREG、
SRET_AFTER_THIS、CAN_BE_FLATTENED、SIGN_EXTEND、PADDING_INREG。強制変換は
NONE、INTEGER、FLOAT、POINTER のいずれかで、COERCE_AND_EXPAND は
NevercABICoercionElement の配列を提供します。
呼び出し規約はもう一段下のレベルで、実際の配置場所を割り当てます:
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 は LOCKSTEP の値です——RegisterNumber は、それが指し示すス
キーマに対してのみ意味を持ちます。完全な実例は
カスタム呼び出し規約 と
pluginsdk/examples/CustomCallConvPlugin.c を参照してください。
経路は正規の NevercTargetKey から選ばれます: ターゲット ID、トリプルの各部分、
CPU、チューニング CPU、機能、ABI、呼び出し規約、オブジェクト形式、再配置モデル、
コードモデル、実行レベル、ポインター幅、エンディアン、スキーマダイジェスト。自分
が担える辺を登録します:
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);成果物の種別は IR、MIR、MC、ASSEMBLY、OBJECT_GRAPH、OBJECT_IMAGE、
CUSTOM です。細粒度の経路は IR → MIR → MC → ObjectGraph → ObjectImage です。
NEVERC_CODEGEN_EDGE_COARSE を設定して CoarseLower を与えると、
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();
}粗粒度の経路でも neverc.codegen.product_verify とトランザクショナルな出力コミッ
トは通ります。VerifyProduct は、ホストがあなたに果たしていることを期待する義務
——VERIFY_FINAL_IR、VERIFY_TARGET_KEY、VERIFY_PRODUCT_KIND、
VERIFY_PRODUCT_ID、VERIFY_STRUCTURE——とともに呼ばれるので、プロバイダーが近道
を選んでゲートをこっそり飛ばすことはできません。
MCUnit はセクション、シンボル、式、フラグメント、命令、オペランド、フィックスア
ップを保持します。読み取りは 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);
}変更はトランザクショナルで、他の場所と同じです:
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);ハンドルはタスクスコープで世代チェックされるため、放棄された変更に由来するハンド ルは再利用されるのではなく拒否されます。
セクションフラグは ALLOCATED、EXECUTABLE、WRITABLE、MERGEABLE、DEBUG。
シンボルの束縛は LOCAL、GLOBAL、WEAK、型は NONE、FUNCTION、OBJECT、
SECTION、TLS、定義は UNDEFINED、SECTION、ABSOLUTE、COMMON です。式は
単項の PLUS、MINUS、NOT と、二項の ADD、SUBTRACT、MULTIPLY、
DIVIDE、AND、OR、XOR、SHIFT_LEFT、SHIFT_RIGHT に対応します。配置をホ
ストに任せたいところでは NEVERC_MC_AUTOMATIC_OFFSET を渡してください。
RegisterSchema はターゲットの MC スキーマを公開し、GetSchemaToken /
GetSchemaTokenInfo は名前と LOCKSTEP トークンを相互に解決します。
発行ストリームは、各 neverc.mc.emission.* フェーズに対応する 10 種類の
イベントを順に報告します。ABI はさらに
NEVERC_MC_EMISSION_PRE_OBJECT_WRITE を予約していますが、オブジェクト書き込み
自体は別フェーズ neverc.object.pre_write です。オブザーバーとして購読し、イ
ベントを読みます:
NevercMCEmissionEventInfo Event = {0};
Event.Header = /* … */;
Emission->GetEvent(Emission->Context, Frame, Frame->Input, &Event);
/* Event.Kind, Event.Flags */Flags はイベントのどの部分が埋まっているかを示します: HAS_SECTION、
HAS_INSTRUCTION、HAS_ENCODING、HAS_FIXUP、HAS_LAYOUT、
CAN_REPLACE_INSTRUCTION。対応するフィールドを読む前にフラグを確認してください
——まだエンコーディングを持たないイベントは、尋ねたからといって持つようにはなりま
せん。
HAS_LAYOUT が立てば、GetLayoutSection、GetLayoutFragment、
GetLayoutSymbol、GetLayoutFixup がアドレスとサイズを返します。
pre_instruction で、かつ CAN_REPLACE_INSTRUCTION が立っているときに限り、差し
替えができます:
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 はこの読み取り専用版です。
3 つの登録が機械語バックエンドを拡張します。いずれもターゲットとスキーマダイジェ ストをキーとします:
MC->RegisterEncoder(MC->Context, RegistrarContext, &EncoderDescriptor);
MC->RegisterDecoder(MC->Context, RegistrarContext, &DecoderDescriptor);
MC->RegisterAsmBackend(MC->Context, RegistrarContext, &BackendDescriptor);エンコーダーはバッファーを返すのではなくシンクを通して書き込むので、所有権はホス ト側に留まります:
Sink->WriteBytes(Sink->Context, Bytes);
Sink->AddFixup(Sink->Context, &Fixup);デコーダーは NEVERC_MC_DECODE_SUCCESS、_SOFT_FAIL、_UNKNOWN、_FAIL のいず
れかを報告します。フィックスアップの種別は NevercMCFixupKindInfo を通じて
PC_RELATIVE、SIGNED、RELAXABLE、TARGET のフラグで自己記述します。
asm バックエンドが緩和(relaxation)を担います。レイアウトは証明ダイジェストを発 行し、レイアウト後のいかなる変更もその証明を無効化して、オブジェクトを書き出 す前に再レイアウトを強制します——リンクグラフが使うのと同じ世代チェックの流儀で す。
パーサープロバイダーはソースバイト列を消費して 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);ソースは NEVERC_ASSEMBLY_SOURCE_BUFFER か
NEVERC_ASSEMBLY_SOURCE_RENDERED_TOKENS のいずれかです。前処理付きアセンブリ
(.S)はまず通常のフロントエンドプリプロセッサーを通り、レンダリング済みトーク
ンとして届きます。素のアセンブリ(.s)はバッファーとしてそのままパーサーに入り
ます。
プリンターは逆方向です——GetPrintInput、続いて与えられた出力トランザクションへの
WritePrintOutput、そして PublishAssemblyOutput。それ以外の場所への書き込みは
サポートされません。解析/出力の検証とホストのコミットゲートはバイトが可視になる
前に走るので、出力に失敗しても中途半端なファイルは残りません。
NevercObjectAPI は再配置可能ファイルをセクション、シンボル、再配置、COMDAT に正
規化します。組み込みアダプターは ELF、COFF、Mach-O を網羅し、RegisterFormat で
さらに追加できます。
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);
}変更は 4 種類のエンティティすべてについて create/replace/move/erase のパターンに
従い、BeginMutation … CommitMutation / AbandonMutation の中でステージされま
す。
セクションフラグは ALLOCATED、EXECUTABLE、WRITABLE、MERGEABLE、
STRINGS、TLS、DEBUG、UNWIND、DISCARDABLE、RETAIN。再配置のターゲット
は SYMBOL、SECTION、ABSOLUTE、FORMAT_EXTENSION のいずれかです。
どのディスクリプターにも ExtensionOwner / ExtensionVersion / Extension の三
点セットがあります。正規化グラフに対応するフィールドがないデータを形式が保持する
のはこの仕組みによってです——そのバイト列はエンティティとともに運ばれ、書き出し時
に戻ってくるので、往復で失われることがありません。
組み込み ELF アダプターは、正確なネイティブ情報をタグ付き extension に記録します。
NCSE v2 はセクションのインデックス、アドレス、型、flags、ファイルオフセット、
entry size を、NCSY v2 は st_info、完全な st_other、st_size とネイティブ名
が空か否かを示す明示的な状態を、NCRL v1 はネイティブのリロケーション型と公式名
を保持します。そのため通常の空名 ELF シンボルは空のままで、合成名 $symbol.N に
書き換えられることはありません。一方、ソースで実際に $symbol.N と名付けた
シンボルは通常の非空名のままです。未変更のネイティブイメージのパススルーなら匿名
シンボルを正確に保持できます。グラフを正とする組み込み書き出しでは、portable MC
表記が同じ匿名シンボルテーブルエントリーを再構築できないため、出力 sink を開く前に
拒否します。Android canonical release の監査は現行バージョンのタグ付き payload
全体を要求し、そこから安定グラフへの投影を再現します。
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 は 0 から NEVERC_OBJECT_PROBE_MAX_CONFIDENCE(1000)までの
Confidence、認識した NevercObjectArtifactKind(RELOCATABLE、ARCHIVE、
EXECUTABLE_IMAGE、SHARED_IMAGE、UNIVERSAL_BINARY)、そして確信を得るために
必要だったバイト数 ConsumedMinimum(上限は
NEVERC_OBJECT_PROBE_MAX_CONSUMED_MINIMUM、65536)を報告します。最も確信度の高い
ものが選ばれます。
Reader にはグラフと開いた変更が渡され、それを埋めます。Writer にはグラフ、そ
のレイアウト証明、そして境界付きバイナリビルダーが渡されます。
NevercObjectFormatDescriptor.Header.Minor は provider の能力を示すものであり、
ホスト全体のモード切り替えではありません。1.0 descriptor は probe、read、通常の
default write について完全な互換性を保ち、その writer は
NevercObjectWriteRequest.Header.Minor == 0 と Header.Flags == 0 を受け取ります。
writer が 1.1 request flags を理解する場合にだけ minor 1 を宣言してください。通常の
write は引き続き flags がゼロで、1.1 より前の出力動作を変えません。
Object Format 1.1 は NevercObjectWriteRequest.Header.Flags に次のビットを定義します。
NEVERC_OBJECT_WRITE_CANONICAL_ELF_TABLESは、独立した正規の.strtabと.shstrtab、およびそれらに依存するインデックスの再マッピングを要求します。これは ELF テーブルの正規化であり、再配置可能リンクではありません。セクション順序、COMDAT グループ、linker メタデータ、重複シンボルとエイリアス、リロケーションレコード、および 名前テーブル以外のすべての payload はそのまま保持されます。追加の正当なフォーマット固有SHT_STRTABセクションも保持され、再構築されるのは選択されたSHT_SYMTABの文字列 テーブルとe_shstrndxが指すテーブルだけです。DROP_DEBUG_INFOを併用した場合、debug セクションと、それらの削除されたインデックスを参照するメタデータだけが除去されます。NEVERC_OBJECT_WRITE_ANDROID_KERNEL_RELEASEはさらに、最終的にシリアライズされた ELF を権威ある境界とし、writer が合成した mapping symbols を削除して、実際の シリアライズ済みセクション座標から release 名を再生成します。NEVERC_OBJECT_WRITE_DROP_DEBUG_INFOは、上記いずれかの ELF ポリシーの一部として debug セクションの削除を要求します。
NEVERC_OBJECT_WRITE_REQUEST_KNOWN_FLAGS が既知ビットの完全なマスクです。正当な
組み合わせは 0、CANONICAL_ELF_TABLES、
CANONICAL_ELF_TABLES | DROP_DEBUG_INFO、
CANONICAL_ELF_TABLES | ANDROID_KERNEL_RELEASE、および 3 ビットすべてだけです。
release または debug ビットを canonical ビットなしで使うことはできません。
ホストは未知または不正な組み合わせ、および minor-0 provider への特別な要求を、
出力 sink を開く前に拒否します。1.1 writer も、受け取った未知または不正な flags を
無視せず拒否しなければなりません。writer と任意の object.post_write インターセプター
の後で、ホストの意味検証と封印された object.final_verify がシリアライズ済みバイト
を再監査し、その結果が権威を持ちます。これらの flags はすべての第三者形式に対する
一般的な保証ではありません。minor 1 は writer が flags プロトコルを理解することを
意味します。適用可能な ELF ポリシーを実装するか、そのポリシーが適用不能または未対応
なら NEVERC_STATUS_CAPABILITY_UNAVAILABLE を明示的に返すことができますが、要求を
黙って無視してはなりません。
--strip が Android .ko を最終化すると、上記の汎用 mutable object API は
host が確立した信頼済み write path に制限されます。この境界には独立した 2 段階の
identity seal があります。
- 置換可能な
ObjectGraphフェーズより前に、graph seal は保持される各 logical section のsection ID、final ordinal、正確な名前と、各 exact-name symbol のsymbol ID、owner、class、section、value、size、binding、type、完全なst_otherを束縛します。 - host-owned writer が信頼済み image baseline を作った後、image seal は保持される
各 section の ordinal と正確な名前、
.symtabの総 entry 数、および各 exact-name symbol の生の.symtabslotと属性を束縛します。完全な release verifier は すべての構造的 release name も独立に再計算します。
| Binding | 最終 Android release での動作 |
|---|---|
neverc.object.write provider / interceptor |
callback 前に REJECTED。信頼済み write path を置換できません |
plugin-owned ObjectFormat graph writer |
REJECTED。この path には信頼済み baseline を確立する host-owned graph writer が必要です |
observer |
READ_ONLY。検査のみ可能で、出力の変更や置換はできません |
neverc.object.post_write interceptor |
VALIDATED。bounded mutable API が変更できるのは構造的に検証される ABI/identity surface 外の payload だけで、結果は input ABI checks、両 seal、完全な release verifier を通過する必要があります |
最終 merge の所有権も host によって封印されます。third-party ObjectMergeProvider
が返した MergedImage または独立した byte 列は破棄され、検証・finalize 済みの
graph を host-owned graph writer が直列化します。一方、
built-in finalized input serialization は external object phases を迂回して、
完全一致する audited native bytes を host merger に渡します。この内部入力処理が
上記の出力境界を迂回することはありません。
Finalization は Android module merge semantics の場合にだけ受け入れられます。
relocatable output request と relocatable driver configuration の両方も必須で、
満たさなければ before routing に失敗します。最終 Android relocatable release
では、frozen input format、
TargetKey.ObjectFormatID、frozen output format が
one format identity を共有しなければなりません。不一致は
before provider dispatch、つまり route planning や sink creation よりも前に
拒否されるため、capability preflight と実際の graph-writer dispatch が異なる
format を観測することはありません。
native-image passthrough は置換可能な route-matching provider とすべての
interceptor を拒否します。target/CPU/features/object-format/execution-level route が
一致しない provider は実行も release の阻止もせず、observer だけを許可します。
before sealed commit の拒否または検証失敗だけが staging を
中止し、ファイルを公開しません。AFTER_COMMIT observer の失敗は公開後に報告され、
公開済みファイルをロールバックできません。
- 探査してバイト列を ObjectGraph に読み込む;
object.pre_writeのグラフインターセプターを実行する;- レイアウトし、それから
object.post_layoutを実行する(変更後は再レイアウト); - 境界付きの候補イメージを書く;
object.post_writeのバイナリインターセプターを実行する;- 封印された
object.final_verifyとアトミックなobject.commitを実行する。
イメージの状態は CANDIDATE → VERIFIED → COMMITTED、あるいは ABORTED /
FAILED_PARTIAL と遷移します。
オブザーバーには読み取り専用のブリッジが渡され、オブザーバーから変更を試みると
NEVERC_STATUS_POLICY_VIOLATION で拒否されます。ライターと post-write インターセ
プターに渡るのは境界付きの NevercMutableBinaryAPI ビルダーだけです——Reserve、
Write、WriteAt、Tell、ReadAt、Insert、Append、Resize。オーバーフロ
ー、コールバックの失敗、検証の失敗はステージングを中止させるので、失敗が中途半端
なファイルをディスクに残すことはありません。
pluginsdk/examples/ObjectRewritePlugin.c は完全なトランザクショナル書き換えの例
です。
- LOCKSTEP のオペコード、レジスター、オペランド、フィックスアップ、再配置、呼び出 し規約の値を使う前に、スキーマダイジェストを比較する。
- 可変状態はホストが提供する process、session、task の状態に置く。
- コールバックから戻った後にタスクハンドルや借用ビューをキャッシュしない。
- インターセプターの継続は、コールバックスレッド上で高々 1 回だけ呼ぶ。
- すべての
BeginMutationはちょうど 1 回のコミットまたは放棄に到達する。 - レイアウト済みの MCUnit や ObjectGraph を変更したら再レイアウトする。古いレイア ウト証明は失効しており、ホストはそれを拒否する。
- イベントのフィールドを読む前に
NevercMCEmissionEventInfo.Flagsを確認し、命令 の差し替えはCAN_REPLACE_INSTRUCTIONが立っているときだけ行う。 - 出力は、与えられたトランザクションかバイトシンクを通してのみ書く。
- 失敗時は元の
NevercStatusを返し、中途半端なものは一切公開しない。 - 真実であるうちで最も狭い並行性モデルと再入モデルを宣言する。
codegen.product_verify、assembly.final_verify、assembly.commit、object.final_verify、object.commitは封印されている。観測のみ。
規範的な宣言は PluginTarget.h、PluginMC.h、PluginObject.h、
Schema/PhaseSchema.json を参照してください。それらが使うエンティティ・オペラ
ンド・fixup・セクションの種別は Schema/MCSchema.json と
Schema/ObjectSchema.json に由来し、そこから Schema/PluginMCSchema.inc と
Schema/PluginObjectSchema.inc が生成されます。これら安定フェーズそれぞれを肯
定・否定・置換・読み取り専用オブザーバー・封印ゲートの各テストへ対応付けたものは
coverage.json を参照してください。