Skip to content

Latest commit

 

History

History
617 lines (506 loc) · 34.1 KB

File metadata and controls

617 lines (506 loc) · 34.1 KB

言語: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية

← NeverC プラグイン ABI

NeverC プラグイン ターゲット・MC・アセンブリ・オブジェクト API

バックエンドは 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

2 つの互換性ティア

ここから先のすべてを支配するのがこの規則です。

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 つのドメインに分かれています。

codegen — 経路選択(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

mc — 機械語(13)

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)は観測専用です。

assembly(4)

neverc.assembly.parse と neverc.assembly.print は REPLACEABLE です。 neverc.assembly.final_verify と neverc.assembly.commit は SEALED です。

object(8)

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 と呼び出し規約

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——とともに呼ばれるので、プロバイダーが近道 を選んでゲートをこっそり飛ばすことはできません。

MC の構築

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 にはグラフ、そ のレイアウト証明、そして境界付きバイナリビルダーが渡されます。

Object Format 1.1 のライターポリシー

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 を明示的に返すことができますが、要求を 黙って無視してはなりません。

最終 Android release の書き出し権限

--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 の生の .symtab slot と属性を束縛します。完全な 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 の失敗は公開後に報告され、 公開済みファイルをロールバックできません。

書き出しパイプライン

  1. 探査してバイト列を ObjectGraph に読み込む;
  2. object.pre_write のグラフインターセプターを実行する;
  3. レイアウトし、それから object.post_layout を実行する(変更後は再レイアウト);
  4. 境界付きの候補イメージを書く;
  5. object.post_write のバイナリインターセプターを実行する;
  6. 封印された 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 を参照してください。