Языки: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
Бэкенд — это четыре заголовка и двадцать девять фаз. PluginTarget.h
описывает целевую платформу и маршруты через генерацию кода. PluginMC.h
строит и наблюдает машинный код. Разбор и печать ассемблера живут в том же
заголовке. PluginObject.h превращает перемещаемый файл в нормализованный
граф и обратно.
Вместе они позволяют плагину добавить целевую платформу, заменить один шаг
понижения или все сразу, следить за каждой инструкцией в момент её выпуска,
определить диалект ассемблера или переписать объектный файл — и всё это через
чистый C ABI, который никогда не раскрывает 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 — 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 решает, когда выбирается эта платформа: каждый сопоставитель
называет архитектуру, поставщика, операционную систему и окружение, а также
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: идентификатор
платформы, части тройки, 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.*. 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. Проверяйте флаг, прежде чем читать соответствующее
поле: событие, у которого ещё нет кодировки, не обзаведётся ею оттого, что вы
спросили.
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.
Ослаблением (relaxation) владеет asm-бэкенд. Раскладка выпускает дайджест доказательства, и любое изменение после раскладки аннулирует это доказательство и вынуждает переразложить перед тем, как объект можно будет записать, — тот же приём проверки по поколению, что и в графе компоновки.
Провайдер разбора потребляет исходные байты и публикует 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 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 сообщает 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'а, дублирующиеся символы и алиасы, записи релокаций и всё содержимое вне таблиц имён остаются неизменными. Дополнительные допустимые секции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запрашивает удаление 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
API сужается до доверенного пути записи, установленного хостом. Эта граница
имеет две независимые печати идентичности:
- перед любой заменяемой фазой
ObjectGraphпечать графа связываетsection ID,final ordinalи точное имя каждой сохранённой логической секции, а такжеsymbol ID, owner, класс, секцию, значение, размер, binding, тип и полныйst_otherкаждого символа с точным именем; - после того как принадлежащий хосту writer создаёт доверенную baseline образа,
печать образа связывает ordinal и точное имя каждой сохранённой секции, общее
число записей
.symtab, а также исходный.symtabslotи атрибуты каждого символа с точным именем. Полный 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 и identity, а результат должен пройти input ABI checks, обе печати и полный release verifier |
Владение финализированным merge также запечатывается host. Любой MergedImage
или независимые байты от third-party ObjectMergeProvider отбрасываются;
host-owned graph writer сериализует проверенный и финализированный граф этого
provider. В обратном направлении built-in finalized input serialization
обходит external object phases и передаёт host-merger точные
audited native bytes; этот внутренний входной шаг не обходит описанную выше
границу вывода.
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;
поэтому capability preflight и фактический graph-writer dispatch не могут
видеть разные форматы.
Native-image passthrough отклоняет каждый заменяемый route-matching provider
и все interceptor. Provider, у которого route по
target/CPU/features/object-format/execution-level не совпадает, не запускается
и не блокирует release; допускаются только observers. Только отказ или ошибка
проверки
before sealed commit отменяет staging и не публикует файл. Ошибка 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; используемые ими виды
сущностей, операндов, fixup-ов и секций происходят из
Schema/MCSchema.json и Schema/ObjectSchema.json, из которых
порождаются Schema/PluginMCSchema.inc и
Schema/PluginObjectSchema.inc. А coverage.json сопоставляет каждую из
этих стабильных фаз с её положительными, отрицательными, замещающими,
наблюдательными (только чтение) тестами и тестами запечатанных шлюзов.