Языки: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
-fdyncode компилирует единицу трансляции в плоский позиционно-независимый
образ (.bin), код которого не содержит ни одной релокации и не имеет секции
данных. Он нацелен на arm64/x86_64 в macOS, Linux, Android и Windows, на
пользовательском или ядерном уровне исполнения. Плагины наблюдают,
перехватывают или заменяют типизированные фазы, превращающие C в этот образ,
через тот же чистый C-ABI, что и остальные домены: никаких C++-объектов LLVM,
типов STL, исключений или указателей хоста, время жизни которых не задано
таблицей API.
#include "neverc/Plugin/PluginDynCode.h"| Интерфейс | Таблица | Слоты | Назначение |
|---|---|---|---|
NEVERC_INTERFACE_DYNCODE_{HIGH,LOW} |
NevercDynCodeAPI |
16 | Чтение запроса, образа, отчёта и карт секций/символов/релокаций/внешних ссылок |
NEVERC_INTERFACE_DYNCODE_REGISTRAR_{HIGH,LOW} |
NevercDynCodeRegistrarAPI |
5 | RegisterTarget, RegisterImportProvider, RegisterExtractor, RegisterCharsetEncoder, RegisterBinaryVerifier |
NEVERC_INTERFACE_DYNCODE_PHASE_{HIGH,LOW} |
NevercDynCodePhaseAPI |
4 | GetPhaseInfo, GetRequest, GetImage, GetReport |
Все три имеют статус NEVERC_INTERFACE_STABLE в мажорной версии 1. Внутри
обратного вызова фазы точкой входа служит NevercDynCodePhaseAPI — она
превращает кадр в дескрипторы, которые потребляет другая таблица:
NevercDynCodeRequestHandle Request;
Phase->GetRequest(Phase->Context, Frame, Frame->Input, &Request);
NevercDynCodeRequestInfo Info = {0};
Info.Header = (NevercABITableHeader){sizeof(Info), NEVERC_DYNCODE_API_MAJOR,
NEVERC_DYNCODE_API_MINOR, 0};
DynCode->GetRequestInfo(DynCode->Context, Task, Request, &Info);Все четыре семейства карт — карты секций, карты символов, релокации и внешние
ссылки — обходятся одной и той же тройкой first/next/info, например
GetFirstRelocation, GetNextRelocation, GetRelocationInfo. Так плагин
читает решения, принятые извлечением, не разбирая JSON отчёта.
-fdyncode — это обычная Action/Job в графе DAG драйвера. Задание компиляции
публикует проверенный ObjectGraph в памяти; задание -dyncode-extract
потребляет этот граф и записывает образ пользователя, заданный -o. -###,
печать фаз и граф заданий — все показывают задание извлечения, поэтому плагину
никогда не приходится восстанавливать переписанный argv, чтобы узнать режим.
Замороженный запрос разделяется локально для задачи с генерацией кода внутри
процесса; нет ни getCurrentDynCodeOptions(), ни глобального для процесса флага
режима, ни обхода через временный объект.
Ровно одна единица трансляции понижается до одного образа. Множественные входы,
-c/-S/-E и неподдерживаемые триплеты отклоняются заранее со стабильными
диагностиками.
Идентификаторы фаз, идентификаторы артефактов, контейнеры запроса/отчёта/образа и контракты обратных вызовов — это STABLE-ABI первого выпуска. Специфичные для цели виды релокаций и схемы секций/символов формата объекта — это LOCKSTEP: перед их потреблением сравните идентификатор и дайджест схемы цели. NeverC отклоняет несовпадающую схему до вызова поставщика.
В начале задания драйвер нормализует командную строку в неизменяемый
DynCodeRequest и замораживает его. Дочерние задачи заимствуют снимок и никогда
его не изменяют. Запрос несёт ключ цели и формат объекта, уровень исполнения
(пользователь/ядро), политику точки входа (явный символ, список кандидатов по
умолчанию, требование входа по нулевому смещению), политику PIC/секций, политику
внешних ссылок, набор/профиль запрещённых байтов и флаг переписывания,
идентификатор поставщика кодировки, а также максимальную длину, выравнивание и
байт-заполнитель.
DynCode — это фиксированный граф из 34 фаз. Тридцать обычных переходов имеют
статус OBSERVABLE | INTERCEPTABLE | REPLACEABLE; четыре —
OBSERVABLE | SEALED_HOST_GATE. Запечатанные ворота — это финальная проверка
IR, финальная проверка MIR, проверка образа и фиксация (commit). Плагин может
наблюдать любую фазу, обернуть заменяемый переход перехватчиком или заменить его
поставщика целиком; он никогда не может заменить, пропустить или обойти
запечатанные ворота, и он не может выразить отключённое преобразование как
невызванный обратный вызок — отключённое преобразование запускает явного
поставщика-no-op, эквивалентный вывод которого верификатор хоста всё равно
доказывает.
Фазы по порядку таковы:
- заморозка запроса;
- преобразования IR — prepare, понижение косвенных переходов, понижение
интринсик памяти (до и после кучи), понижение строкового рантайма, арена
кучи, три позиции
compiler_rt(pre/post/final), понижение импортов syscall/PEB/ядра, две позицииdata_to_text(pre/post), встраивающая оптимизация, финализация строк, stackify, all-blrи запечатанная финальная проверка IR; - преобразование MIR prepare и запечатанная финальная проверка MIR;
- импорт объекта — привязка проверенного
ObjectGraphк задаче; - извлечение — планирование, компоновка, релокация и построение образа-кандидата;
- ограниченные бинарные фазы — post-extract, переписывание запрещённых байтов, кодирование набора символов, размер/выравнивание/заполнение и pre-verify;
- запечатанная проверка образа;
- запечатанная фиксация.
Нормативным источником идентификаторов, политик, уровней стабильности и ворот
служит Schema/PhaseSchema.json; исполняемый контракт покрытия —
coverage.json.
Каждый встроенный проход IR/MIR обёрнут как типизированный поставщик; объект
прохода LLVM никогда не выставляется через C-ABI. Замена фазы означает, что
встроенный поставщик не выполняется — проходящий тест доказывает поведение или
трассу, а не просто успех регистрации. Фазы mem_intrin, compiler_rt и
data_to_text встречаются более чем в одной позиции; каждая позиция — это
отдельный идентификатор фазы со своим собственным доказательством, поэтому
повторный запуск идемпотентен и никогда не полагается на скрытое состояние
прохода.
Извлечение потребляет ровно один проверенный ObjectGraph, произведённый
маршрутом генерации кода цели. dyncode.object.import привязывает этот граф и
проверяет ключ цели и происхождение; она никогда не перечитывает байты с диска и
не выполняет второй разбор объекта. Пользовательский формат объекта входит в
DynCode, как только его можно прочитать в ObjectGraph и он имеет
соответствующие поставщики релокаций и цели. Множественные объекты и наборы
графов LTO отклоняются на заморозке со стабильным CAPABILITY_UNAVAILABLE.
Набор разрешённых внешних ссылок запроса означает лишь «поставщик может это
обработать»; он никогда не позволяет неразрешённой релокации дожить до плоского
образа. Каждая внешняя ссылка должна завершиться одним из способов: устранена в
IR/MIR, разрешена в символ внутри образа, преобразована в объявленный и принятый
верификатором контракт рантайм-резолвера или жёсткая ошибка. Заглушка syscall,
импорт PEB и импорт ядра — это три встроенных ImportProvider; каждый объявляет
свой сопоставитель цель/уровень/символ и производимый им контракт ABI. Плагин
может добавить ImportProvider, но он должен вернуть происхождение замены,
изменение ABI входа, параметры резолвера и остаточные ссылки.
Извлечение производит DynCodeImage и DynCodeReport. Образ — это ограниченный
построитель байтов плюс смещение/символ входа, выходные карты исходных секций и
исходных символов, распоряжения релокаций и записи внешних/рантайм-контрактов.
Каждая правка байта проходит через проверяемый API построителя
read/write/insert/append/resize; никакого uint8_t ** нет. Правка обновляет
поколение образа и аннулирует любое доказательство релокации/PIC/входа,
пересекающееся с изменённым диапазоном.
Отчёт — это неизменяемый детерминированный продукт аудита: дайджесты
запроса/маршрута/входа/выхода, пофазный журнал поставщиков, выбранные/отклонённые
секции и почему, выбор входа, исправленные/отклонённые/рантайм-контрактные
релокации, оставшиеся внешние ссылки, размер/выравнивание/заполнение,
сканирование запрещённых байтов и контрольный список верификатора.
-fdyncode-report=<path> записывает его канонический JSON; подробные диагностики
рендерятся из того же отчёта, а не из второго набора счётчиков.
Цепочка переписывания запрещённых байтов выполняется в замороженном топологическом порядке, и каждый шаг возвращает запись об изменении. Кодировщик набора символов выбирается по точному стабильному идентификатору и возвращает заглушку декодера, закодированную полезную нагрузку, обновление входа и доказательство цели; неизвестный или неоднозначный идентификатор — жёсткая ошибка. Отключение переписывания выбирает явный шаг-no-op — финальный аудит всё равно выполняется.
Все записываемые фазы завершаются до запечатанного финального верификатора. Верификатор проверяет, что не осталось необработанных внешних релокаций/ссылок, что отсутствует запрещённая секция данных/TLS/раскрутки/отладки/метаданных, что вход существует, корректно выровнен и (когда требуется) находится по нулевому смещению, что каждый сайт релокации лежит в границах с соответствующим PIC-доказательством для текущих байтов образа, что карты секций/символов не перекрываются, что правила длины/выравнивания/заполнения соблюдены и что финальные байты — включая декодер, заголовок и заполнение — не содержат ни одного запрещённого байта. Любой отказ возвращает структурированную диагностику и отбрасывает весь пакет вывода.
После аудита нет записываемого хука. Если преобразование байтов затрагивает исполняемый диапазон, замороженный маршрут должен предоставить соответствующую способность бинарного верификатора, которую хост вызывает, чтобы заново выпустить PIC-доказательство над финальным неизменяемым образом.
-fdyncode включает режим. -fdyncode-entry= выбирает символ входа.
-fdyncode-bad-bytes= / -fdyncode-bad-byte-profile= задают запрещённые байты,
-fdyncode-bad-byte-rewrite (включён по умолчанию) выбирает цепочку
переписывания, а -fdyncode-charset= выбирает зарегистрированный кодировщик.
-fdyncode-max-length=, -fdyncode-align= и -fdyncode-pad= ограничивают
финальный размер. -fdyncode-keep-obj= ответвляет промежуточный перемещаемый
объект, а -fdyncode-report= записывает отчёт аудита.
-mdyncode-context=user|kernel выбирает уровень исполнения.
- Храните изменяемое состояние в предоставленных хостом областях процесса/сессии/задачи; никогда не используйте синглтон текущего плагина или текущих опций.
- Не кэшируйте дескрипторы задач или заимствованные представления после возврата из обратного вызова.
- Вызывайте продолжение перехватчика не более одного раза, в потоке обратного вызова.
- Возвращайте исходный
NevercStatus; объявленныйREPLACE, который потерпел неудачу, не откатывается молча к встроенному поставщику. - Объявляйте самые узкие правдивые модели параллелизма и реентерабельности.
См. PluginDynCode.h для нормативных объявлений,
pluginsdk/examples/DynCodeTracePlugin.c для трассировщика фаз только для
чтения и pluginsdk/examples/DynCodeEncoderPlugin.c для кодировщика набора
символов.