اللغات: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
← واجهة NeverC الثنائية للإضافات
الواجهة الخلفية أربع ترويسات وتسع وعشرون مرحلة. يصف 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 المخطط غير المتطابق قبل استدعاء المزوِّد، فالفحص إذًا حزام وحمّالة معًا — لكن الإضافة التي تتخطاه وتقرأ كودًا تشغيليًا خامًا على أي حال ستُسيء تفسير التعليمات بصمت.
تسع وعشرون، في أربعة مجالات.
| المرحلة | السياسة |
|---|---|
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 أيضًا — وهناك تستبدل التعليمة. أما التسعة الأخرى (unit_begin
وunit_end وsection_change وpost_instruction وpost_encode وfixup
وrelaxation_round وpre_layout وpost_layout) فللمراقبة فقط.
neverc.assembly.parse وneverc.assembly.print قابلتان للاستبدال.
وneverc.assembly.final_verify وneverc.assembly.commit مختومتان.
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 تواقيع الدوال:
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 — فلا يستطيع مزوِّد أن يتخطّى بوابةً
خِلسةً بسلوك طريق مختصر.
يحتوي 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 الرسم وإثبات
تخطيطه والباني الثنائي المحدود.
يعلن 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 صراحةً إذا كانت السياسة غير منطبقة أو
غير مدعومة؛ ولا يجوز له تجاهل الطلب بصمت.
عندما ينهي --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 فيُبلّغ بعد النشر
ولا يمكنه التراجع عن الملف المنشور.
- الاستكشاف وقراءة البايتات داخل 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. ولا يحصل الكُتّاب ومعترضات ما بعد الكتابة إلا
على الباني المحدود 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 يربط كل مرحلة من هذه
المراحل المستقرة باختباراتها الإيجابية والسلبية والاستبدالية والمراقِبة
للقراءة فقط واختبارات البوابات المختومة.