Skip to content

Latest commit

 

History

History
606 lines (495 loc) · 31.4 KB

File metadata and controls

606 lines (495 loc) · 31.4 KB

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

← NeverC 플러그인 ABI

NeverC 플러그인 타깃·MC·어셈블리·오브젝트 API

백엔드는 네 개의 헤더와 스물아홉 개의 페이즈입니다. PluginTarget.h는 타깃과 코 드 생성 경로를 기술합니다. PluginMC.h는 기계어를 만들고 관찰합니다. 어셈블리 파싱과 출력도 같은 헤더에 있습니다. PluginObject.h는 재배치 가능 파일을 정규화 된 그래프로 바꾸고 다시 되돌립니다.

이들을 합치면 플러그인은 타깃을 추가하고, 로워링 단계 하나 또는 전부를 교체하고, 명령어가 방출되는 순간마다 지켜보고, 어셈블리 방언을 정의하고, 오브젝트 파일을 재 작성할 수 있습니다——그것도 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, 확인 없이는 위험한 것: 타깃 고유의 opcode, 레지스터, 오퍼랜드, fixup, 재배치, 호출 규약 스키마. 이들의 수치는 정확히 하나의 스키마 개정판에 대해 서만 의미를 가집니다.

LOCKSTEP 값이 나타나는 곳마다 그 옆에 스키마 다이제스트가 함께 있습니다. 값을 읽 기 전에 비교하세요:

if (!string_equal(Target.SchemaDigest, MY_COMPILED_SCHEMA_DIGEST))
  return fail(NEVERC_STATUS_ABI_MISMATCH);

NeverC도 프로바이더를 호출하기 전에 어긋난 스키마를 거부하므로 이 검사는 이중 안 전장치입니다——하지만 이를 건너뛰고 원시 opcode를 읽는 플러그인은 명령어를 조용히 잘못 해석하게 됩니다.

페이즈들

스물아홉 개, 네 개의 도메인에 걸쳐 있습니다.

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이기도 한 유일한 방출 이벤트로, 명령어를 대체하는 곳이 바로 여기입니다. 나머지 아홉 개(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는 이 타깃이 언제 선택될지를 결정합니다. 각 매처는 아키텍처, 벤 더, 운영체제, 환경을 지정하고, 내장 타깃과 동점일 때 우열을 가르는 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은 섹션, 심볼, 표현식, 프래그먼트, 명령어, 오퍼랜드, fixup을 담습니다. 읽 기는 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.* 단계에 대응하는 열 가지 이벤트 종류를 순서대로 보고합니다. 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가 이것의 읽기 전용 버전입니다.

인코더, 디코더, 레이아웃

세 가지 등록이 기계어 백엔드를 확장하며, 모두 타깃과 스키마 다이제스트를 키로 삼 습니다:

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 중 하나를 보고합니다. fixup 종류는 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);
}

변경은 네 가지 엔티티 종류 모두에 대해 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이라고 이름 붙인 심볼은 일반적인 비어 있지 않은 이름으로 유지됩니다. 변경되지 않은 네이티브 이미지 passthrough는 익명 심볼을 정확히 보존할 수 있습니다. 그래프를 권위 원본으로 삼는 내장 쓰기는 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 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가 0이며 1.1 이전 출력 동작을 유지합니다.

Object Format 1.1은 NevercObjectWriteRequest.Header.Flags에 다음 비트를 정의합니다.

  • NEVERC_OBJECT_WRITE_CANONICAL_ELF_TABLES는 서로 분리된 canonical .strtab과 .shstrtab, 그리고 모든 종속 인덱스의 재매핑을 요구합니다. 이는 ELF 테이블 canonicalization이지 relocatable link가 아닙니다. 섹션 순서, COMDAT 그룹, linker 메타데이터, 중복 심볼과 alias, relocation 레코드, 이름 테이블이 아닌 모든 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, 그리고 세 비트를 모두 설정한 경우뿐입니다. release 또는 debug 비트는 canonical 비트 없이 사용할 수 없습니다.

호스트는 알 수 없거나 잘못된 조합 및 minor-0 provider에 대한 특수 요청을 출력 sink를 열기 전에 거부합니다. 1.1 writer도 받은 알 수 없거나 잘못된 flags를 무시하지 말고 거부해야 합니다. writer와 모든 object.post_write 인터셉터가 실행된 뒤에는 호스트의 의미 검증과 sealed object.final_verify가 직렬화 바이트를 다시 감사하며 그 결과가 권위적입니다. 이 flags는 모든 제3자 형식에 대한 일반 보장이 아닙니다. minor 1은 writer가 flags 프로토콜을 이해한다는 뜻입니다. 적용 가능한 ELF 정책을 구현하거나, 정책이 적용되지 않거나 지원되지 않으면 NEVERC_STATUS_CAPABILITY_UNAVAILABLE을 명시적으로 반환할 수 있지만 요청을 조용히 무시해서는 안 됩니다.

최종 Android release 쓰기 권한

--strip이 Android .ko를 최종화하면 위의 일반 mutable object API는 host가 설정한 신뢰 write path로 제한됩니다. 이 경계에는 서로 독립적인 두 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인 opcode, 레지스터, 오퍼랜드, fixup, 재배치, 호출 규약 값을 사용하기 전에 스키마 다이제스트를 비교한다.
  • 가변 상태는 호스트가 제공하는 process, session, task 상태에 둔다.
  • 콜백이 반환된 뒤에는 태스크 핸들이나 빌린 뷰를 캐시하지 않는다.
  • 인터셉터의 연속은 콜백 스레드에서 많아야 한 번만 호출한다.
  • 모든 BeginMutation은 정확히 한 번의 커밋 또는 포기에 도달한다.
  • 레이아웃이 끝난 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을 보세요.