Skip to content

Latest commit

 

History

History
616 lines (503 loc) · 35.8 KB

File metadata and controls

616 lines (503 loc) · 35.8 KB

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

← واجهة NeverC الثنائية للإضافات

واجهة إضافات NeverC للهدف وMC والتجميع والكائنات

الواجهة الخلفية أربع ترويسات وتسع وعشرون مرحلة. يصف PluginTarget.h هدفًا والمسارات المارّة عبر توليد الشيفرة. ويبني PluginMC.h شيفرة الآلة ويراقبها. أما تحليل التجميع وطباعته فيسكنان الترويسة نفسها. ويحوّل PluginObject.h ملفًا قابلًا لإعادة التموضع إلى رسم بياني مُطبَّع والعكس.

معًا تتيح هذه الترويسات للإضافة أن تضيف هدفًا، أو تستبدل خطوة خفض واحدة أو كلها، أو تراقب كل تعليمة لحظة إصدارها، أو تعرّف لهجة تجميع، أو تعيد كتابة ملف كائن —عبر واجهة C ثنائية خالصة لا تكشف أبدًا عن MCInst أو MCSection أو object::ObjectFile من LLVM.

الواجهات

#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، ويمكن تثبيتها في الشيفرة بأمان: الواصفات المستقلة عن الهدف، ومعرِّفات المراحل، ومعرِّفات القطع الأثرية، وحاويتا MC وObjectGraph، ومعاملات الإخراج، وكل عقد رد نداء.

LOCKSTEP، وغير آمنة بلا فحص: مخططات الأكواد التشغيلية والسجلات والمعاملات وعمليات الإصلاح وإعادة التموضع واصطلاحات الاستدعاء الخاصة بالهدف. قيمها العددية لا تعني شيئًا إلا مقابل مراجعة مخطط واحدة بعينها.

في كل موضع تظهر فيه قيمة LOCKSTEP تظهر بجوارها بصمة مخطط. قارنها قبل قراءة القيمة:

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

كما ترفض NeverC المخطط غير المتطابق قبل استدعاء المزوِّد، فالفحص إذًا حزام وحمّالة معًا — لكن الإضافة التي تتخطاه وتقرأ كودًا تشغيليًا خامًا على أي حال ستُسيء تفسير التعليمات بصمت.

المراحل

تسع وعشرون، في أربعة مجالات.

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 قابلتان للاستبدال. وneverc.assembly.final_verify وneverc.assembly.commit مختومتان.

object (8)

neverc.object.probe وread وwrite وpre_write وpost_layout قابلة للاستبدال؛ وneverc.object.post_write قابلة للاعتراض فقط؛ و neverc.object.final_verify وneverc.object.commit مختومتان.

تسجيل هدف

NevercTargetDescriptor هو أكبر واصف في هذه الواجهة الثنائية، لأنه يحمل كل ما تحتاج الواجهتان الأمامية والخلفية إلى معرفته:

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 — تخطيط البيانات، ووحدات المعالجة الافتراضية والمخصّصة للضبط، وجدول الميزات، وواجهات 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 القياسي: معرِّف الهدف، وأجزاء الثلاثية، ووحدة المعالجة، ووحدة معالجة الضبط، والميزات، و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.*. كما تحجز الواجهة الثنائية 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. تحقَّق من الراية قبل قراءة الحقل المقابل — فالحدث الذي لا ترميز له بعد لن يصير له ترميز لمجرد أنك سألت.

وتعطي GetLayoutSection وGetLayoutFragment وGetLayoutSymbol و GetLayoutFixup العناوين والأحجام متى ضُبطت HAS_LAYOUT.

وعند 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. وتصف أنواع الإصلاح نفسها عبر NevercMCFixupKindInfo برايات PC_RELATIVE وSIGNED وRELAXABLE وTARGET.

وتملك الواجهة الخلفية للتجميع عملية الإرخاء. ويُصدر التخطيط بصمة إثبات، وأي تعديل بعد التخطيط يُبطل ذلك الإثبات ويفرض إعادة تخطيط قبل أن يمكن كتابة الكائن — وهو نفس نمط الفحص بالأجيال الذي يستعمله رسم الربط.

التجميع

يستهلك مزوِّد التحليل بايتات المصدر وينشر 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);
}

ويتبع التعديل نمط الإنشاء/الاستبدال/النقل/المحو لأنواع الكيانات الأربعة جميعًا، مُجهَّزًا داخل BeginMutation … CommitMutation / AbandonMutation.

رايات الأقسام هي ALLOCATED وEXECUTABLE وWRITABLE وMERGEABLE وSTRINGS وTLS وDEBUG وUNWIND وDISCARDABLE وRETAIN. وأهداف إعادة التموضع هي SYMBOL أو SECTION أو ABSOLUTE أو FORMAT_EXTENSION.

ولكل واصف ثلاثية ExtensionOwner / ExtensionVersion / Extension. وبهذا تحتفظ الصيغة ببيانات لا حقل لها في الرسم المُطبَّع — إذ تسافر تلك البايتات مع الكيان وتعود عند الكتابة، بدل أن تسقط في رحلة الذهاب والإياب.

يسجل محول ELF المدمج الحقائق الأصلية الدقيقة في امتدادات ذات وسوم: يحمل NCSE v2 فهرس القسم وعنوانه ونوعه وflags وإزاحة الملف وحجم المدخل؛ ويحمل NCSY v2 الحقول st_info وst_other كاملة وst_size وحالة صريحة تدل على كون الاسم الأصلي فارغاً أو غير فارغ؛ ويحمل NCRL v1 نوع إعادة التموضع الأصلي واسمه الرسمي. لذلك يبقى رمز ELF العادي ذو الاسم الفارغ فارغاً، ولا يعاد تسميته أبداً باسم اصطناعي مثل $symbol.N، بينما يظل الرمز المسمى حرفياً $symbol.N اسماً عادياً غير فارغ. يستطيع تمرير الصورة الأصلية من دون تعديل أن يحفظ الرموز المجهولة بدقة. أما الكتابة المدمجة التي يكون فيها الرسم هو المصدر المعتمد فترفضها قبل فتح sink الإخراج، لأن صياغة MC المحمولة لا تستطيع إعادة إنشاء مدخل جدول الرموز المجهول نفسه. تتطلب مراجعات Android canonical release الحمولة الموسومة الدقيقة للإصدار الحالي، وتعيد تشغيل إسقاط الرسم المستقر من تلك الحقائق الأصلية.

تسجيل صيغة

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 عن Confidence من 0 إلى NEVERC_OBJECT_PROBE_MAX_CONFIDENCE (1000)، وعن NevercObjectArtifactKind الذي تعرَّف عليه (RELOCATABLE أو ARCHIVE أو EXECUTABLE_IMAGE أو SHARED_IMAGE أو UNIVERSAL_BINARY)، وعن ConsumedMinimum — أي كم بايتًا لزمه ليتيقّن، بحدّ أقصى NEVERC_OBJECT_PROBE_MAX_CONSUMED_MINIMUM (65536). وتفوز أعلى ثقة.

ويُسلَّم Reader رسمًا وتعديلًا مفتوحًا فيملأهما. ويُسلَّم Writer الرسم وإثبات تخطيطه والباني الثنائي المحدود.

سياسات Writer في Object Format 1.1

يعلن NevercObjectFormatDescriptor.Header.Minor قدرة الـprovider، ولا يبدّل وضع المضيف بأكمله. ويظل descriptor بالإصدار 1.0 متوافقًا تمامًا مع probe وread والكتابة الافتراضية العادية؛ إذ يتلقى writer الخاص به NevercObjectWriteRequest.Header.Minor == 0 وHeader.Flags == 0. لا تعلن minor 1 إلا إذا كان writer يفهم request flags الخاصة بـ1.1؛ فالكتابة العادية تظل تحمل flags صفرية وتحافظ على سلوك الإخراج السابق لـ1.1.

يعرّف Object Format 1.1 البتات الآتية في NevercObjectWriteRequest.Header.Flags:

  • يتطلب NEVERC_OBJECT_WRITE_CANONICAL_ELF_TABLES قسمي .strtab و.shstrtab معياريين ومستقلين، مع إعادة تعيين كل الفهارس التابعة. وهذه عملية توحيد لجداول ELF وليست ربطًا قابلاً لإعادة التموضع: يبقى ترتيب الأقسام ومجموعات COMDAT وبيانات linker الوصفية والرموز المكررة والرموز المستعارة وسجلات relocation وكل محتوى (payload) خارج جداول الأسماء دون تغيير. وتُحفظ أقسام SHT_STRTAB الإضافية الصحيحة الخاصة بالتنسيق؛ ولا يُعاد بناء سوى جدول السلاسل الخاص بـ SHT_SYMTAB المحدد والجدول الذي يسميه e_shstrndx. ومع DROP_DEBUG_INFO، لا تُرشَّح إلا أقسام debug والبيانات الوصفية التي تشير إلى الفهارس المحذوفة لتلك الأقسام.
  • يجعل NEVERC_OBJECT_WRITE_ANDROID_KERNEL_RELEASE ملف ELF النهائي المتسلسل هو المرجع أيضًا: يحذف mapping symbols التي أنشأها writer ويعيد توليد أسماء release من إحداثيات الأقسام المتسلسلة الفعلية.
  • يطلب NEVERC_OBJECT_WRITE_DROP_DEBUG_INFO حذف أقسام debug ضمن إحدى سياستي ELF هاتين.

يمثل 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.

يرفض المضيف أي تركيب مجهول أو غير قانوني، وأي طلب خاص إلى provider بالإصدار minor-0، قبل فتح sink الإخراج. ويجب كذلك على writer بالإصدار 1.1 رفض أي flags مجهولة أو غير قانونية يتلقاها بدلًا من تجاهلها. وبعد writer وأي معترض object.post_write، تعيد المصادقة الدلالية للمضيف وobject.final_verify المختومة تدقيق البايتات المتسلسلة وتكونان المرجع. وليست هذه flags وعدًا عامًا لكل صيغة خارجية. ويعني minor 1 أن writer يفهم بروتوكول flags: يمكنه تنفيذ سياسة ELF المناسبة، أو إرجاع NEVERC_STATUS_CAPABILITY_UNAVAILABLE صراحةً إذا كانت السياسة غير منطبقة أو غير مدعومة؛ ولا يجوز له تجاهل الطلب بصمت.

صلاحية الكتابة لإصدار Android النهائي

عندما ينهي --strip وحدة Android بصيغة .ko، تضيق واجهة mutable object العامة أعلاه إلى مسار كتابة موثوق ينشئه المضيف. ولهذه الحدود ختما هوية مستقلان:

  • قبل أي مرحلة ObjectGraph قابلة للاستبدال، يربط ختم الرسم section ID وfinal ordinal والاسم الدقيق لكل مقطع منطقي محتفَظ به، وكذلك symbol ID والمالك والفئة والمقطع والقيمة والحجم والربط والنوع وst_other الكامل لكل رمز ذي اسم دقيق؛
  • بعد أن ينشئ writer المملوك للمضيف baseline الموثوقة للصورة، يربط ختم الصورة ترتيب كل مقطع محتفَظ به واسمه الدقيق، وإجمالي إدخالات .symtab، وslot .symtab الخام وخصائص كل رمز ذي اسم دقيق. ويعيد release verifier الكامل مستقلًا حساب كل اسم release بنيوي.
Binding سلوك إصدار Android النهائي
neverc.object.write provider / interceptor REJECTED قبل callback؛ لا يمكنه استبدال مسار الكتابة الموثوق
plugin-owned ObjectFormat graph writer REJECTED؛ يتطلب هذا المسار graph writer مملوكًا للمضيف ينشئ baseline الموثوقة
observer READ_ONLY؛ يمكنه الفحص فقط ولا يمكنه تعديل الخرج أو استبداله
neverc.object.post_write interceptor VALIDATED؛ لا يمكن لواجهة mutable API المحدودة تغيير إلا payload خارج سطح ABI والهوية المتحقق منه بنيويًا، ويجب أن تجتاز النتيجة فحوص input ABI والختمين وrelease verifier الكامل

تُختم ملكية الدمج النهائي بواسطة host أيضًا. يُطرح أي MergedImage أو بايتات مستقلة من third-party ObjectMergeProvider، ويتولى host-owned graph writer تسلسل الرسم المتحقق منه والنهائي لذلك provider. وفي الاتجاه المقابل، تتجاوز built-in finalized input serialization مراحل external object phases وتمرر إلى merger الخاص بالمضيف بايتات audited native bytes المطابقة تمامًا؛ ولا تتجاوز خطوة الإدخال الداخلية هذه حد الإخراج أعلاه.

لا يُقبل finalization إلا مع Android module merge semantics، كما يتطلب relocatable output request وrelocatable driver configuration معًا، وإلا يفشل before routing. في إصدار Android relocatable نهائي، يجب أن تشترك frozen input format و TargetKey.ObjectFormatID وfrozen output format في one format identity. يُرفض أي اختلاف before provider dispatch، أي قبل route planning أو إنشاء sink أيضًا، وبذلك لا يمكن لـ capability preflight وgraph-writer dispatch الفعلي رؤية تنسيقين مختلفين.

يرفض native-image passthrough كل route-matching provider قابلة للاستبدال وكل interceptor. أما provider الذي لا يطابق مساره target/CPU/features/object-format/execution-level فلا يُنفذ ولا يمنع release، ولا يُسمح إلا بالـ observers. ولا يلغي staging ويمنع نشر الملف إلا رفض أو فشل تحقق before sealed commit. أما فشل observer عند AFTER_COMMIT فيُبلّغ بعد النشر ولا يمكنه التراجع عن الملف المنشور.

خط أنابيب الكتابة

  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. ولا يحصل الكُتّاب ومعترضات ما بعد الكتابة إلا على الباني المحدود NevercMutableBinaryAPI — Reserve وWrite وWriteAt وTell وReadAt وInsert وAppend وResize. والفيضان أو فشل رد النداء أو فشل التدقيق يُجهِض التجهيز، فلا يترك الفشل أبدًا نصف ملف على القرص.

وpluginsdk/examples/ObjectRewritePlugin.c مثال كامل على إعادة كتابة معاملاتية.

القواعد

  • قارن بصمة المخطط قبل استهلاك أي قيمة LOCKSTEP لكود تشغيلي أو سجل أو معامل أو إصلاح أو إعادة تموضع أو اصطلاح استدعاء.
  • احفظ الحالة القابلة للتغيير في حالة 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 للإعلانات المِعيارية؛ وأنواع الكيانات والمعاملات وعمليات الإصلاح والأقسام التي تستخدمها تأتي من Schema/MCSchema.json وSchema/ObjectSchema.json، اللذين يولّدان Schema/PluginMCSchema.inc وSchema/PluginObjectSchema.inc. وcoverage.json يربط كل مرحلة من هذه المراحل المستقرة باختباراتها الإيجابية والسلبية والاستبدالية والمراقِبة للقراءة فقط واختبارات البوابات المختومة.