summaryrefslogtreecommitdiff
path: root/doc/src/libmcpu-melf-abi-ru.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src/libmcpu-melf-abi-ru.tex')
-rw-r--r--doc/src/libmcpu-melf-abi-ru.tex661
1 files changed, 661 insertions, 0 deletions
diff --git a/doc/src/libmcpu-melf-abi-ru.tex b/doc/src/libmcpu-melf-abi-ru.tex
new file mode 100644
index 0000000..e7a73b3
--- /dev/null
+++ b/doc/src/libmcpu-melf-abi-ru.tex
@@ -0,0 +1,661 @@
+\documentclass[10pt,a4paper,oneside,openany]{memoir}
+\newcommand{\DocShortTitle}{LibMCPU MELF ABI}
+\input{libmcpu-doc-common.tex}
+\hypersetup{
+ pdftitle={LibMCPU MELF ABI},
+ pdfsubject={Формат объектов и исполняемых файлов MCPU, контракт компоновщика и загрузчика}
+}
+\begin{document}
+
+\DocTitlePage
+ {LibMCPU MELF ABI}
+ {Формат объектов и исполняемых файлов MCPU, контракт компоновщика (linker) и загрузчика (loader)}
+ {Самостоятельное справочное руководство; нумерация разделов начинается с 1}
+
+\ManualIntro{Документ является самостоятельным описанием текущего MELF-контракта LibMCPU и намеренно не связан с номером выпуска пакета. Он фиксирует статический ABI v1, исторический explicit-preload ABI v2, frozen normal dynamic ABI v3 с SONAME/NEEDED, lifecycle, STACK/HEAP, MCPU static archives и partial ET_REL linking. HLL PROGRAM/MODEL classification и automatic CRT insertion описаны как linker layer над MELF container ABI.}
+
+\begin{principle}
+\textbf{MELF --- не системный ELF.} Формат намеренно использует сигнатуру \code{_ELF}, частное значение \code{e_machine=MCPU} и специфические для MCPU секции и заголовков сегментов (Program Headers). Ядро host-системы и системный \code{ld.so} не участвуют в запуске. \code{mcpu-ld} и \code{mcpu-run} являются частями одного целевого ABI.
+\end{principle}
+
+\tableofcontents
+\clearpage
+
+\section{Назначение и границы формата}
+
+MELF хранит три вида файлов MCPU:
+\begin{itemize}
+\item перемещаемый объект (\term{relocatable object}{перемещаемый объект}) \code{ET_REL};
+\item исполняемый образ (\term{executable}{исполняемый файл}) \code{ET_EXEC};
+\item разделяемый объект (\term{shared object}{разделяемый объект}) \code{ET_DYN}, явно загружаемый при запуске программы.
+\end{itemize}
+
+От ELF заимствованы общие идеи заголовков Ehdr, Shdr и Phdr, таблиц символов и строк, а также relocation-записей RELA. Архитектурные пространства памяти, точки входа lifecycle и метаданные профиля целевой архитектуры определяются самим MCPU ABI.
+
+Текущая реализация использует только MELF64. Имена и структуры MELF32 зарезервированы, но ABI v1, ABI v2 и ABI v3 их не принимают. C-структуры из \code{mcpu-elf.h} описывают логические поля, но не являются готовой сериализацией на диске (on-disk serialization): код записи и чтения явно кодирует и декодирует многобайтовые поля с учётом \code{EI_DATA}.
+
+PROGRAM образует отдельную область с собственным порядком байтов: машинные поля MCPU внутри PROGRAM всегда записываются MSB-first независимо от \code{EI_DATA} контейнера.
+
+\section{Идентификация, класс и e\_machine}
+
+\subsection{Поле e\_ident}
+
+\begin{center}
+\begin{tabularx}{0.95\textwidth}{@{}L{28mm}L{28mm}Y@{}}
+\toprule
+\textbf{Поле} & \textbf{Значение} & \textbf{Смысл}\\
+\midrule
+MAG0..3 & \code{'_','E','L','F'} & сигнатура MELF; это не обычный \code{0x7fELF}\\
+EI\_CLASS & 2 & \code{MELF_CLASS_64}\\
+EI\_DATA & 1 или 2 & порядок байтов LSB/MSB для метаданных и объектов данных целевой архитектуры\\
+EI\_VERSION & 1 & текущая версия формата\\
+EI\_OSABI & 64 & \code{MELF_OSABI_MCPU}\\
+EI\_ABIVERSION & 1, 2 или 3 & статический ABI v1 / исторический dynamic ABI v2 / frozen dynamic ABI v3\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+\subsection{e\_machine и e\_flags}
+
+\code{e_machine = 0x4d43} (ASCII ``MC''). Это частное значение MELF, а не официальное значение системного ELF. В текущем ABI \code{e_flags=0}. Неизвестные биты загрузчик (loader) обязан отвергать, а не молча игнорировать.
+
+\section{Типы файлов и версии ABI}
+
+В MELF номер ABI относится не к версии инструмента, а к конкретному контракту файла. Текущий toolchain продолжает читать исторические ABI v1/v2 и выпускает ABI v1 для ET_REL/статического ET_EXEC и frozen ABI v3 для новых dynamic ET_EXEC/ET_DYN.
+
+\begin{center}
+\begin{tabularx}{0.96\textwidth}{@{}L{43mm}L{24mm}L{18mm}Y@{}}
+\toprule
+\textbf{Режим формирования} & \textbf{e\_type} & \textbf{ABI} & \textbf{Назначение}\\
+\midrule
+\code{mcpu-as} & ET\_REL & 1 & перемещаемый входной объект\\
+\code{mcpu-ld -r} & ET\_REL & 1 & частично скомпонованный перемещаемый объект\\
+\code{mcpu-ld} & ET\_EXEC & 1 & полностью статический исполняемый файл\\
+исторический dynamic output & ET\_EXEC / ET\_DYN & 2 & explicit-preload startup ABI, сохраняемый загрузчиком для совместимости\\
+\code{mcpu-ld --dynamic} & ET\_EXEC & 3 & главный startup-dynamic исполняемый файл с \code{.dynamic}\\
+\code{mcpu-ld --shared} & ET\_DYN & 3 & shared object с обязательным SONAME и возможными NEEDED\\
+ET\_CORE & зарезервировано & --- & пока не поддерживается\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+ABI v3 является frozen normal dynamic ABI. Его несовместимое изменение требует нового номера ABI. ABI v3 не меняет CPU ISA, CALL ABI, PTR64 или базовые типы RELA; он расширяет startup dynamic-link contract метаданными \code{.dynamic}, SONAME/NEEDED, поиском библиотек и детерминированным runtime symbol scope.
+
+\section{Заголовок MELF64}
+
+Сериализованный \code{MElf64_Ehdr} имеет размер 88 байт. Поля точек входа lifecycle содержат полные архитектурные \code{PTR64(PROGRAM,offset)}, а не смещения внутри файла.
+
+\begin{longtable}{@{}L{32mm}L{20mm}L{102mm}@{}}
+\toprule
+\textbf{Поле} & \textbf{Размер} & \textbf{Смысл}\\
+\midrule
+\endfirsthead
+\toprule
+\textbf{Поле} & \textbf{Размер} & \textbf{Смысл}\\
+\midrule
+\endhead
+\code{e_ident} & 16 & сигнатура, класс, порядок данных, версия, OSABI, версия ABI\\
+\code{e_type} & 2 & ET\_REL / ET\_EXEC / ET\_DYN\\
+\code{e_machine} & 2 & MCPU\\
+\code{e_version} & 4 & \code{MELF_EV_CURRENT}\\
+\code{e_entry} & 8 & обязательная точка входа исполняемого файла; всегда \code{_start}\\
+\code{e_init_entry} & 8 & \code{_start}, если существует \code{.init}; иначе 0\\
+\code{e_life_entry} & 8 & обязательный \code{_life}\\
+\code{e_fini_entry} & 8 & необязательный \code{_fini}; иначе 0\\
+\code{e_phoff} & 8 & смещение в файле до таблицы заголовков сегментов (Program Headers)\\
+\code{e_shoff} & 8 & смещение в файле до таблицы заголовков секций (Section Headers)\\
+\code{e_flags} & 4 & в текущем ABI равен 0\\
+\code{e_ehsize} & 2 & 88\\
+\code{e_phentsize} & 2 & 64 для MELF64\\
+\code{e_phnum} & 2 & число записей заголовков сегментов\\
+\code{e_shentsize} & 2 & 64 для MELF64\\
+\code{e_shnum} & 2 & число записей заголовков секций\\
+\code{e_shstrndx} & 2 & индекс секции \code{.shstrtab}\\
+\bottomrule
+\end{longtable}
+
+У \code{ET_REL} окончательных адресов lifecycle ещё нет, поэтому четыре поля точек входа равны нулю. \code{ET_DYN} не является самостоятельной программой и также не имеет исполняемых точек входа lifecycle.
+
+\section{Архитектурные адреса внутри MELF}
+
+Для окончательно размещённых загружаемых секций (allocated sections) поле \code{sh_addr} содержит полный \code{PTR64(segment,final_offset)}. Используется следующая таблица селекторов (selectors):
+
+\begin{center}
+\begin{tabularx}{0.84\textwidth}{@{}L{26mm}L{18mm}Y@{}}
+\toprule
+\textbf{Пространство} & \textbf{SEG} & \textbf{Секции / роль}\\
+\midrule
+DATA & 0x0 & \code{.data}, \code{.bss}\\
+STACK & 0x1 & только дескриптор времени выполнения; полезные данные объекта отсутствуют\\
+PROGRAM & 0x2 & \code{.init}, \code{.text}, \code{.fini}\\
+CONTROL & 0x3 & \code{.control}\\
+INPUT & 0x4 & \code{.input}\\
+OUTPUT & 0x5 & \code{.output}\\
+TRACE & 0x6 & \code{.trace}\\
+HEAP & 0x7 & только дескриптор времени выполнения; данных ассемблерного объекта нет\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+В \code{ET_REL} символы и секции используют смещения относительно своей секции и ещё не имеют окончательной базы процесса. В \code{ET_DYN} адреса также остаются относительными к модулю; окончательные базы PROGRAM и DATA назначает компоновщик запуска (startup linker).
+
+\section{Секции и флаги секций}
+
+MELF использует типы секций, близкие к ELF: \code{SHT_NULL}, \code{SHT_PROGBITS}, \code{SHT_SYMTAB}, \code{SHT_STRTAB}, \code{SHT_RELA}, \code{SHT_NOBITS}, \code{SHT_DYNSYM} и \code{SHT_DYNAMIC}.\par
+Кроме того, определены два специфических для MCPU типа:
+\begin{center}
+\begin{tabularx}{0.82\textwidth}{@{}L{54mm}Y@{}}
+\toprule
+\textbf{Тип секции} & \textbf{Числовое значение}\\
+\midrule
+\code{SHT_MCPU_TARGET} & 0x70000000\\
+\code{SHT_MCPU_LIFECYCLE} & 0x70000001\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+Загружаемые секции получают обычные признаки R/W/X через \code{SHF_WRITE/ALLOC/EXECINSTR}; тег архитектурного сегмента MCPU кодируется в процессорных битах флагов.
+
+\begin{center}
+\begin{tabularx}{0.94\textwidth}{@{}L{24mm}L{24mm}L{26mm}Y@{}}
+\toprule
+\textbf{Секция} & \textbf{Тип} & \textbf{Пространство} & \textbf{Атрибуты}\\
+\midrule
+.init & PROGBITS & PROGRAM & ALLOC + EXEC\\
+.text & PROGBITS & PROGRAM & ALLOC + EXEC\\
+.fini & PROGBITS & PROGRAM & ALLOC + EXEC\\
+.data & PROGBITS & DATA & ALLOC + WRITE\\
+.bss & NOBITS & DATA & ALLOC + WRITE\\
+.control & PROGBITS & CONTROL & ALLOC, только чтение для guest-программы\\
+.input & PROGBITS & INPUT & ALLOC, только чтение для guest-программы\\
+.output & PROGBITS & OUTPUT & ALLOC + WRITE\\
+.trace & PROGBITS & TRACE & ALLOC + WRITE\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+\code{sh_addralign} задаёт требование выравнивания guest-адреса. Значения 0/1 означают отсутствие специального требования, остальные допустимые значения являются степенями двойки. \code{SHT_NOBITS} занимает архитектурную память, но не содержит данных в файле.
+
+\section{Объектная модель ET\_REL}
+
+Ассемблер формирует символы со смещениями относительно секций и явные записи RELA. Типовой путь выглядит так:
+
+\begin{center}
+\begin{tikzpicture}[font=\sffamily\small,>=Stealth,node distance=4mm]
+\node[draw=McpuBlue,rounded corners,fill=McpuGrayBg,minimum width=24mm] (src) {исходник .s};
+\node[draw=McpuBlue,rounded corners,fill=McpuGreenBg,right=of src,minimum width=25mm] (as) {mcpu-as};
+\node[draw=McpuBlue,rounded corners,fill=McpuGrayBg,right=of as,minimum width=31mm] (obj) {object.o / ET\_REL};
+\node[draw=McpuBlue,rounded corners,fill=McpuGreenBg,right=of obj,minimum width=24mm] (ld) {mcpu-ld};
+\node[draw=McpuBlue,rounded corners,fill=McpuGrayBg,right=of ld,minimum width=31mm] (exe) {ET\_EXEC};
+\draw[->,thick] (src)--(as);
+\draw[->,thick] (as)--(obj);
+\draw[->,thick] (obj)--(ld);
+\draw[->,thick] (ld)--(exe);
+\end{tikzpicture}
+\end{center}
+
+В каждом \code{ET_REL}, включая результат \code{mcpu-ld -r}, имеется ровно одна \code{.mcpu.target}. Окончательная \code{.mcpu.lifecycle} ещё отсутствует: формирование lifecycle принадлежит компоновщику (linker), а в перемещаемом объекте представлено каноническими символами \code{_start}, \code{_life} и необязательным \code{_fini}.
+
+\section{Таблицы символов ABI}
+
+Запись MELF64 в таблице символов имеет размер 24 байта. Символ 0 --- полностью нулевая запись неопределённого символа. В \code{st_info} привязка (binding) хранится в старшей тетраде, тип --- в младшей.
+
+Значения binding: LOCAL=0, GLOBAL=1, WEAK=2. Типы символов: NOTYPE, OBJECT, FUNC, SECTION, FILE. Для обычных символов времени запуска используется visibility DEFAULT; в подстановке символов при запуске (interposition) участвуют только имена DEFAULT GLOBAL/WEAK.
+
+\subsection{st\_value в ET\_REL}
+
+Для определённого символа \code{st_shndx} содержит индекс секции, а \code{st_value} --- байтовое смещение от её начала. Для неопределённого символа используется \code{SHN_UNDEF} и нулевое значение. Директива \code{.equ} остаётся механизмом абсолютных выражений времени ассемблирования и не обязана создавать запись MELF symbol table.
+
+\subsection{Статическое разрешение символов}
+
+LOCAL никогда не разрешается через границу object file. Сильный GLOBAL имеет приоритет над WEAK. Несколько сильных GLOBAL-определений являются ошибкой. Если сильное определение отсутствует, несколько WEAK-определений выбираются детерминированно по порядку входных файлов компоновщика. Неопределённый сильный GLOBAL после окончательной компоновки является ошибкой. При \code{mcpu-ld -r} такой символ может оставаться неопределённым и переносится вместе с RELA в новый ET_REL.
+
+\subsection{Lifecycle-символы}
+
+\code{_start} и \code{_life} обязательны как GLOBAL/FUNC для каждого исполняемого файла; \code{_fini} --- необязательный GLOBAL/FUNC. Lifecycle-символы не могут быть WEAK. Псевдометка исходного текста \code{_start_life} существует только в синтаксисе ассемблера и эквивалентна двум символам \code{_start} и \code{_life} с одним смещением в секции \code{.text}, когда \code{.init} отсутствует.
+
+\section{RELA ABI}
+
+Запись MELF64 RELA имеет размер 24 байта: \code{r_offset}, \code{r_info} и знаковый 64-битный \code{r_addend}. Базовое выражение relocation: \code{S + A}. Все текущие MCPU relocation исправляют ровно 8 байт.
+
+\begin{longtable}{@{}L{38mm}L{28mm}L{30mm}L{58mm}@{}}
+\toprule
+\textbf{Relocation} & \textbf{Где находится поле} & \textbf{Цель} & \textbf{Записываемое значение}\\
+\midrule
+\endfirsthead
+\toprule
+\textbf{Relocation} & \textbf{Где находится поле} & \textbf{Цель} & \textbf{Записываемое значение}\\
+\midrule
+\endhead
+R\_MCPU\_CODE64 & PROGRAM & PROGRAM & окончательное необработанное байтовое смещение PROGRAM\\
+R\_MCPU\_DATA64 & PROGRAM & DATA/.bss & окончательное необработанное байтовое смещение DATA\\
+R\_MCPU\_PTR64\_CODE & PROGRAM & загружаемый сегмент & PTR64(segment,offset) в порядке байтов машинного кода\\
+R\_MCPU\_PTR64\_DATA & секция данных & загружаемый сегмент & PTR64 в представлении данных целевой архитектуры\\
+R\_MCPU\_CPTR64\_DATA & секция данных & только PROGRAM & PTR64(PROGRAM,offset)\\
+\bottomrule
+\end{longtable}
+
+В \code{ET_REL} поле \code{r_offset} является байтовым смещением внутри целевой секции, указанной через \code{sh_info}; \code{sh_link} указывает на таблицу символов. Все 8 байт исправляемого поля обязаны помещаться в сохранённой секции. Relocation непосредственно в область NOBITS запрещена: в файле нет байтов, которые можно исправить.
+
+Relocation неопределённого WEAK-указателя с нулевым addend может дать канонический полностью нулевой NULL. Для необработанных CODE64/DATA64 такое молчаливое преобразование в zero запрещено, потому что offset 0 является допустимым архитектурным адресом.
+
+
+\section{Частичная компоновка mcpu-ld -r}
+
+Режим \code{mcpu-ld -r} объединяет несколько MELF \code{ET_REL} в один новый \code{ET_REL}. Это именно \term{partial link}{частичная компоновка}, а не построение исполняемого образа.
+
+Для каждой одноимённой секции вклады входных объектов конкатенируются в порядке входа с соблюдением их \code{sh_addralign}. Значения определённых символов и \code{r_offset} пересчитываются в координаты объединённой секции. LOCAL-символы разных объектов остаются раздельными даже при одинаковом имени. Для GLOBAL/WEAK действует обычное правило силы: сильный GLOBAL заменяет WEAK; два сильных GLOBAL одного имени являются ошибкой.
+
+RELA при \code{-r} не превращаются в окончательные архитектурные адреса. Они переносятся в выходной объект с новым section-relative offset и ссылкой на канонический символ результирующего ET_REL. Неопределённые GLOBAL/WEAK допускаются и могут быть разрешены последующей окончательной компоновкой.
+
+\begin{McpuCommand}
+mcpu-ld -r -o combined.o a.o b.o c.o
+mcpu-ld --stack-size=64K -o program main.o combined.o
+\end{McpuCommand}
+
+STACK/HEAP, lifecycle-финализация, Program Headers, SONAME/NEEDED и runtime image в режиме \code{-r} не создаются. Поэтому \code{-r} взаимоисключён с \code{--dynamic}/\code{--shared}; опции размеров STACK/HEAP и \code{--soname} для него недопустимы.
+
+\section{Статические библиотеки lib*.a}
+
+Статическая библиотека MCPU не является новым типом MELF-файла. Это обычный UNIX \code{ar}-контейнер, чьи ordinary members являются независимыми текущими MELF \code{ET_REL}. Утилита \code{mcpu-ar} создаёт deterministic archives и при модификаторе \code{s} записывает настоящий System-V/GNU-style symbol index.
+
+\begin{McpuCommand}
+mcpu-ar rcs libmodel.a model.o solver.o helpers.o
+mcpu-ar t libmodel.a
+\end{McpuCommand}
+
+При линковке из архива извлекается только member, определяющий текущий неразрешённый сильный GLOBAL. Неопределённый WEAK сам по себе member не вытягивает. После каждого извлечения текущий архив повторно сканируется до устойчивого состояния, поэтому новый member может породить ссылку на member, расположенный раньше в том же архиве. Уже пройденный отдельный \code{-l} archive автоматически после следующей библиотеки не пересматривается; повторное \code{-la} имеет реальный смысл.
+
+Для ABI-v1 и для \code{-r} \code{-lfoo} ищет только \code{libfoo.a}. Для ABI-v3 \code{--dynamic}/\code{--shared} поиск является directory-major: в каждом каталоге сначала проверяется \code{libfoo.so}, затем \code{libfoo.a}. Статически извлечённые members становятся частью результирующего MELF и не создают \code{DT_NEEDED}.
+
+\section{Метаданные профиля целевой архитектуры}
+
+\code{.mcpu.target} обязательна в \code{ET_REL} и \code{ET_EXEC}; модули ABI v2/v3 несут тот же профиль совместимости. Запись имеет размер 64 байта, не является ALLOC и выровнена на 8.
+
+\begin{longtable}{@{}L{38mm}L{18mm}L{94mm}@{}}
+\toprule
+\textbf{Поле} & \textbf{Смещение} & \textbf{Смысл}\\
+\midrule
+\endfirsthead
+\toprule
+\textbf{Поле} & \textbf{Смещение} & \textbf{Смысл}\\
+\midrule
+\endhead
+mt\_version & 0 & версия структуры профиля, сейчас 1\\
+mt\_size & 4 & размер записи, сейчас 64\\
+mt\_isa\_revision & 8 & ревизия ISA, сейчас 1\\
+mt\_abi\_revision & 12 & ревизия программного ABI, сейчас 1\\
+mt\_program\_model & 16 & профиль ILP32/LP64/LLP64\\
+mt\_data\_byte\_order & 20 & порядок байтов объектов данных; согласован с EI\_DATA\\
+mt\_data\_word\_order & 24 & порядок слов LibMPU\\
+mt\_integer\_model & 28 & дополнительный код (дополнительный код)\\
+mt\_address\_model & 32 & PTR64 SEG4:OFF60\\
+mt\_register\_model & 36 & REG24 / регистровый пул 512 KiB\\
+mt\_real\_model & 40 & LibMPU\\
+mt\_complex\_model & 44 & LibMPU\\
+mt\_max\_align & 48 & максимальное обычное выравнивание ABI = 16\\
+mt\_flags & 52 & пока 0\\
+mt\_required\_features & 56 & пока 0\\
+\bottomrule
+\end{longtable}
+
+Компоновщик требует совместимости профилей целевой архитектуры. Загрузчик повторно проверяет окончательный профиль до создания guest-областей памяти. Среда выполнения не исправляет несовместимый профиль и не конвертирует порядок байтов данных автоматически.
+
+\section{Метаданные lifecycle}
+
+Окончательный \code{ET_EXEC} содержит \code{.mcpu.lifecycle} фиксированного размера 32 байта. Запись описывает \textbf{семантику жизненного цикла}, но не дублирует адреса точек входа: единственным нормативным источником адресов остаётся Ehdr.
+
+Флаги ABI v1 фиксируют: START обязателен, LIFE обязателен, INIT и FINI необязательны; \code{e_entry} указывает на START; без INIT START совпадает с LIFE; LIFE завершается END и может быть продолжен; штатный EXIT переходит в FINI; область запуска (startup area) остаётся действительной всё время жизни программы.
+
+\begin{normative}
+Метаданные lifecycle намеренно не содержат копий указателей на \code{_start/_life/_fini}. Благодаря этому в образе нет двух независимо перемещаемых источников истины. Загрузчик проверяет согласованность Ehdr и записи lifecycle, после чего использует числовые поля точек входа из Ehdr.
+\end{normative}
+
+\section{Таблица заголовков сегментов (Program Headers)}
+
+Запись MELF64 Phdr имеет размер 64 байта и расширяет привычную ELF-подобную форму полем \code{p_maxsz}. \code{p_paddr=0}: отдельного пространства физических адресов у текущего MCPU ABI нет. \code{p_align} относится к выравниванию guest-адреса и файла, а не к размеру страницы host-системы.
+
+\begin{center}
+\begin{tabularx}{0.96\textwidth}{@{}L{28mm}L{34mm}L{24mm}Y@{}}
+\toprule
+\textbf{Пространство} & \textbf{p\_type} & \textbf{Флаги} & \textbf{Правило размеров}\\
+\midrule
+PROGRAM & PT\_LOAD & R|X & filesz=memsz=maxsz\\
+DATA & PT\_LOAD & R|W & filesz <= memsz; maxsz=memsz\\
+CONTROL & PT\_LOAD & R & сохранённый и runtime-размеры равны\\
+INPUT & PT\_LOAD & R & сохранённый и runtime-размеры равны\\
+OUTPUT & PT\_LOAD & R|W & сохранённый и runtime-размеры равны\\
+TRACE & PT\_LOAD & R|W & сохранённый и runtime-размеры равны\\
+STACK & PT\_MCPU\_STACK & R|W & filesz=0; memsz=maxsz\\
+HEAP & PT\_MCPU\_HEAP & R|W & filesz=0; memsz <= maxsz\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+Для пространств, загружаемых из образа, \code{p_vaddr=PTR64(segment,0)} в координатах окончательного образа исполняемого файла или модуля. Дескриптор STACK обязателен в \code{ET_EXEC}; дескриптор HEAP необязателен. Округление host-mmap к размеру страницы не является частью ABI.
+
+\section{Статический ET\_EXEC ABI v1}
+
+Обычный \code{mcpu-ld} завершает всю работу времени компоновки до запуска:
+\begin{enumerate}
+\item проверяет профиль целевой архитектуры каждого \code{ET_REL};
+\item размещает вклады секций с учётом \code{sh_addralign};
+\item создаёт единственную 16-байтовую защитную нулевую область DATA (NULL guard) по смещениям 0..15;
+\item разрешает символы GLOBAL/WEAK/UNDEF;
+\item применяет все RELA с проверкой диапазона и сегмента;
+\item проверяет обязательные определения lifecycle;
+\item назначает окончательные PTR64-адреса секций и символов;
+\item формирует окончательные заголовков сегментов (Program Headers) и метаданные.
+\end{enumerate}
+
+Исполняемый файл ABI v1 не нуждается в поиске символов при запуске. Любая попытка использовать \code{--ldpreload} или \code{MCPU_LD_PRELOAD} с ABI v1 является ошибкой, а не поводом неявно превратить программу в dynamic ABI.
+
+\section{Точки входа lifecycle исполняемого файла}
+
+\subsection{С секцией .init}
+\code{_start} принадлежит \code{.init}; поля \code{e_entry} и \code{e_init_entry} указывают на \code{_start}. \code{_life} принадлежит \code{.text}, а \code{e_life_entry} указывает на \code{_life}. После \code{END} в INIT супервизор начинает повторяющиеся фазы LIFE.
+
+\subsection{Без секции .init}
+\code{_start} и \code{_life} обязаны находиться по одному адресу в \code{.text}. Псевдометка \code{_start_life:} создаёт именно эту пару символов. В окончательном исполняемом файле \code{e_init_entry=0} и \code{e_entry == e_life_entry}.
+
+\subsection{FINI}
+Если существует \code{.fini}/\code{_fini}, поле \code{e_fini_entry} указывает на него; иначе оно равно 0. Штатный \code{EXIT} после LIFE входит в FINI. Диагностическая остановка по внешнему лимиту шагов не имитирует \code{EXIT} и не обязана запускать FINI.
+
+\subsection{Связь MELF lifecycle с HLL Runtime ABI}
+Сам MELF container определяет машинные entry points \code{_start}, \code{_life} и optional \code{_fini}. Поверх этого container contract существующий \code{mcpu-ld} реализует HLL complete-program layer: strong GLOBAL/FUNC \code{main+fini} в одном defining ET_REL классифицирует PROGRAM, а \code{init+life+fini} --- MODEL. Linker автоматически добавляет соответствующий CRT из \code{<MCPU-root>/lib/mcpu/}; отсутствие обязательного CRT является fatal error. Raw low-level objects с собственными \code{_start/_life[/_fini]} продолжают линковаться без automatic CRT, но смешивать оба режима запрещено.
+
+\begin{normative}
+Это не меняет wire format MELF: HLL names являются linker policy над обычной symbol table, а окончательный ET_EXEC по-прежнему исполняется через нормативные lifecycle entry points. Loader не классифицирует HLL source form.
+\end{normative}
+
+Полный контракт HLL + CRT + MCPU Libc, включая \code{McpuStartup}, PRE_RUNTIME/RUNNING, internal Libc hooks и MODEL return namespace, приведён в \code{libmcpu-bytecode-architecture-ru.pdf}.
+
+\section{Постоянная область запуска McpuStartup}
+
+До первой guest-инструкции загрузчик строит в верхней части STACK структуру \code{McpuStartup}. Она имеет поле версии, поэтому имя C-типа намеренно не содержит суффикс V1.
+
+\begin{center}
+\begin{tabularx}{0.88\textwidth}{@{}L{20mm}L{30mm}L{18mm}Y@{}}
+\toprule
+\textbf{Смещение} & \textbf{Поле} & \textbf{Размер} & \textbf{Смысл}\\
+\midrule
+0x00 & version & 4 & \code{MCPU_STARTUP_VERSION_1}\\
+0x04 & size & 4 & 32\\
+0x08 & argc & 8 & число аргументов guest-программы\\
+0x10 & argv & 8 & PTR64(STACK, массив argv)\\
+0x18 & зарезервировано & 8 & 0\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+После заголовка располагается массив \code{argv[argc+1]} из PTR64, завершающий NULL, затем строки аргументов, завершённые NUL, и нулевое дополнение. \code{startup_base} выровнен на 16 байт. Начальное состояние: \code{PC=e_entry}, \code{SP=startup_base}, \code{FP=stack_size}. Искусственного CALL-frame для \code{_start} нет.
+
+\section{Исторический MELF ABI v2}
+
+ABI v2 ввёл \code{ET_DYN}, \code{.dynsym/.dynstr} и сохранённые runtime RELA, но не имел SONAME/NEEDED. Полный набор shared objects задавался явным \code{mcpu-run --ldpreload=a.so:b.so}; вся startup-компоновка завершалась до первой инструкции \code{_start}. ABI v2 остаётся читаемым для совместимости, тогда как текущие \code{mcpu-ld --dynamic}/\code{--shared} выпускают ABI v3.
+
+\section{Frozen MELF ABI v3}
+
+ABI v3 сохраняет image/symbol/RELA-модель ABI v2 и добавляет нормальную модель зависимостей. Каждый dynamic ABI-v3 \code{ET_EXEC}/\code{ET_DYN} содержит ровно одну не-ALLOC секцию \code{.dynamic} типа \code{SHT_DYNAMIC}, связанную через \code{sh_link} с \code{.dynstr}.
+
+Запись \code{MElf64_Dyn} имеет размер 16 байт: знаковый 64-битный \code{d_tag} и беззнаковый 64-битный \code{d_val}. Текущие теги:
+
+\begin{center}
+\begin{tabularx}{0.82\textwidth}{@{}L{42mm}L{22mm}Y@{}}
+\toprule
+\textbf{Тег} & \textbf{Значение} & \textbf{Смысл}\\
+\midrule
+\code{DT_NULL} & 0 & обязательный единственный терминатор canonical stream\\
+\code{DT_NEEDED} & 1 & offset имени требуемого SONAME в \code{.dynstr}\\
+\code{DT_SONAME} & 14 & offset собственного SONAME shared object в \code{.dynstr}\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+После \code{DT_NULL} записей быть не должно. ABI-v3 \code{ET_DYN}, используемый как normal dependency, имеет ровно один SONAME. \code{ET_EXEC} не имеет собственного SONAME. Оба типа могут иметь ноль или более NEEDED в записанном порядке.
+
+\section{SONAME, NEEDED и link-time -L/-l}
+
+Имена development link, runtime identity и physical file разделены. Типичная установка:
+
+\begin{McpuCode}
+libfoo.so -> libfoo.so.1
+libfoo.so.1 -> libfoo.so.1.2.3
+libfoo.so.1.2.3 # DT_SONAME = libfoo.so.1
+\end{McpuCode}
+
+При \code{-lfoo} linker находит development name \code{libfoo.so}, полностью валидирует выбранный ABI-v3 ET_DYN, читает его SONAME и записывает в потребитель именно \code{DT_NEEDED [libfoo.so.1]}. Абсолютный путь, имя symlink и physical filename в NEEDED не попадают.
+
+ABI-v3 dynamic output в каждом search directory предпочитает \code{libfoo.so}, затем \code{libfoo.a}. Если выбран archive, нужные ET_REL members встраиваются статически и никакого NEEDED для них нет. ABI-v1 и \code{-r} shared candidate не рассматривают.
+
+\section{Поиск библиотек и runtime root}
+
+\code{mcpu-ld} и \code{mcpu-run} используют общую relocatable root/config policy. Базовый system directory равен \code{<runtime-root>/lib}; конфигурационная цепочка в порядке роста приоритета:
+
+\begin{McpuCode}
+<runtime-root>/etc/mcpu-ld.conf
+/etc/mcpu/mcpu-ld.conf
+$HOME/.mcpu/etc/mcpu-ld.conf
+\end{McpuCode}
+
+Переменная конфигурации \code{MCPU_LD_SYSTEM_LIBRARY_PATH} заменяет system path; пустое значение отключает его. \code{--no-config} не читает config layers, но сохраняет executable-derived default. \code{--sys-root=PATH} подразумевает \code{--no-config} и использует \code{PATH/lib}. Для linker явные \code{-L} идут раньше system path; для runtime явный \code{--library-path} также имеет более высокий search priority.
+
+\section{Рекурсивная загрузка NEEDED}
+
+\code{mcpu-run} сначала читает прямые \code{DT_NEEDED} главного ET_EXEC, ищет каждый SONAME по runtime search policy, валидирует ABI-v3 ET_DYN и требует точного совпадения внутреннего \code{DT_SONAME} с запрошенным именем. Затем так же обрабатываются транзитивные NEEDED.
+
+Граф зависимостей строится breadth-first и дедуплицируется по SONAME. Direct dependencies предшествуют transitive; siblings сохраняют порядок NEEDED слева направо. Цикл вида \code{A -> B -> A} допустим и завершается через предварительную регистрацию SONAME.
+
+\section{.dynsym/.dynstr и runtime RELA}
+
+\code{.dynsym/.dynstr} являются частью startup-link ABI, в отличие от developer/debug \code{.symtab/.strtab}. DEFAULT GLOBAL/WEAK участвуют в выборе определения; LOCAL никогда не входит в interposition namespace. Lifecycle-имена \code{_start/_life/_fini} принадлежат главному executable и не являются runtime-preemptible symbols.
+
+Runtime \code{SHT_RELA} использует тот же \code{MElf64_Rela}. \code{sh_link} указывает на \code{.dynsym}, \code{sh_info} --- на allocated section с исправляемым полем, а \code{r_offset} остаётся offset относительно output section. Все runtime RELA выполняются до \code{_start}; lazy binding отсутствует.
+
+\section{Детерминированный runtime symbol scope}
+
+Для одного имени сначала сравнивается binding strength: GLOBAL всегда сильнее WEAK. Среди определений одинаковой силы действует scope:
+
+\begin{enumerate}
+\item explicit preload modules --- наивысший scope, внутри списка правый элемент имеет больший приоритет;
+\item главный ET_EXEC;
+\item normal DT_NEEDED graph в breadth-first, left-to-right порядке.
+\end{enumerate}
+
+Таким образом direct dependency выигрывает у конфликтующей transitive dependency той же силы, а первый sibling в NEEDED order выигрывает у следующего. Если GLOBAL нет вообще, тот же scope применяется к WEAK.
+
+\code{MCPU_LD_PRELOAD} является environment counterpart для \code{--ldpreload}. Environment entries добавляются первыми, command-line entries --- после них; существующее rightmost-wins правило поэтому естественно делает command line сильнее environment. Эта переменная не является частью \code{mcpu-ld.conf} и не подавляется \code{--no-config}.
+
+\section{Контракт ET_DYN}
+
+ABI-v2/v3 \code{ET_DYN} содержит только library \code{.text/.data/.bss} плюс non-ALLOC metadata. Он не владеет \code{.init}, \code{.fini}, \code{.control}, \code{.input}, \code{.output}, \code{.trace}, lifecycle definitions, \code{PT_MCPU_STACK} или \code{PT_MCPU_HEAP}. STACK, HEAP и model spaces принадлежат главному \code{ET_EXEC}. Для ABI v3 normal shared object SONAME обязателен.
+
+\section{LOCAL relocation внутри ET_DYN}
+
+LOCAL-символ остаётся частным и неподменяемым, но окончательная process base модуля заранее неизвестна. Поэтому relocation внутри ET_DYN, которой нужен окончательный PROGRAM/DATA address, сохраняется как LOCAL runtime RELA. Startup linker разрешает её строго внутри исходного модуля и добавляет runtime base соответствующего сегмента. Эта коррекция является нормативной частью runtime relocation contract и полностью сохраняется в ABI v3.
+
+\begin{center}
+\begin{tikzpicture}[font=\sffamily\small,>=Stealth,node distance=7mm]
+\node[draw=McpuBlue,rounded corners,fill=McpuBlueBg,minimum width=46mm] (so) {ET\_DYN после mcpu-ld};
+\node[draw=McpuBlue,rounded corners,fill=McpuGrayBg,below=of so,minimum width=62mm] (rel) {LOCAL runtime RELA + module-relative symbol};
+\node[draw=McpuGreen,rounded corners,fill=McpuGreenBg,below=of rel,minimum width=56mm] (run) {startup linker в mcpu-run};
+\node[draw=McpuBlue,rounded corners,fill=McpuGrayBg,below=of run,minimum width=62mm] (final) {process PTR64 с PROGRAM/DATA base};
+\draw[->,thick] (so)--(rel);
+\draw[->,thick] (rel)--(run);
+\draw[->,thick] (run)--(final);
+\end{tikzpicture}
+\end{center}
+
+\section{Размещение образа процесса и единственный DATA guard}
+
+У MCPU нет единого ELF-подобного смещения загрузки (load bias): PTR64 имеет независимые селекторы сегментов. Компоновщик запуска назначает отдельную базу для каждого архитектурного сегмента.
+
+\begin{center}
+\begin{tabularx}{0.9\textwidth}{@{}L{48mm}Y@{}}
+\toprule
+\textbf{Вклад} & \textbf{База}\\
+\midrule
+main PROGRAM & 0\\
+lib1 PROGRAM & \code{align_up(main_program_end, lib1_align)}\\
+lib2 PROGRAM & \code{align_up(lib1_program_end, lib2_align)}\\
+main DATA & 0\\
+первая DATA-библиотека & после окончательного DATA main; минимум 16, если DATA main отсутствует\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+В \code{ET_DYN} нет собственного DATA NULL guard. Во всём образе процесса существует ровно одна нулевая область offsets 0..15. Поэтому локальный символ библиотеки с относительным к модулю DATA offset 0 после размещения при запуске закономерно получает process offset 16 или больше.
+
+\section{BSS и соотношение filesz/memsz}
+
+\code{.bss} имеет тип \code{SHT_NOBITS}. Она участвует в расчёте адресов и увеличивает DATA \code{p_memsz}, но не увеличивает объём данных в файле \code{p_filesz}. Статический и startup-загрузчики сначала получают изначально обнулённую память, затем копируют сохранённые байты, поэтому NOBITS детерминированно начинается с нулей.
+
+Для PROGRAM хранится только машинный код, поэтому \code{filesz=memsz}. Для DATA обычно \code{filesz < memsz}. LOCAL-ссылки из \code{ET_DYN} в DATA/.bss всё равно требуют перебазирования по базе модуля, даже если целевые байты относятся к NOBITS.
+
+\section{Дескриптор STACK и командная строка}
+
+Размер STACK является частью ABI исполняемого файла. Он задаётся компоновщиком через \code{--stack-size=N} и записывается в \code{PT_MCPU_STACK}; \code{mcpu-run} не может его переопределить. Разделяемые объекты ABI v2 не задают политику STACK.
+
+Предпочтительный синтаксис запуска использует явный разделитель:
+\begin{lstlisting}[language={}]
+mcpu-run [runtime-options] -- executable [program-args...]
+\end{lstlisting}
+
+Все параметры после \code{--} передаются в guest \code{argv} без повторного разбора программой \code{mcpu-run}; текстовое имя исполняемого файла становится \code{argv[0]}. Старая форма без разделителя сохранена для совместимости.
+
+\section{Дескриптор HEAP, _end и SYS\_brk}
+
+Необязательный \code{PT_MCPU_HEAP} хранит начальный подготовленный размер в \code{p_memsz} и неизменяемый предел в \code{p_maxsz}; оба значения не меньше 16, причём max >= initial. Определяемый компоновщиком символ \code{_end=PTR64(HEAP,16)} является частным для исполняемого файла, неподменяемым и задаёт начальный и минимальный program break.
+
+\code{SYS_brk} имеет номер 10. NULL-аргумент запрашивает текущее значение; HEAP PTR64 задаёт абсолютный новый break. В отличие от общего stable syscall rule \code{-MCPU_errno}, \code{SYS_brk} возвращает фактический текущий HEAP PTR64. При отклонённом запросе break не меняется. Shrink зануляет освобождённый диапазон, но не обязан уменьшать host-отображение памяти; последующий рост остаётся детерминированным. Guest PTR64 остаётся корректным при перемещении host backing, поскольку содержит segment+offset, а не host-адрес.
+
+\section{Дисциплина проверки загрузчика}
+
+Загрузчик сначала полностью проверяет структуру файла и только затем создаёт guest-области памяти. В частности проверяются:
+\begin{itemize}
+\item сигнатура, класс, порядок данных, версия, OSABI, ABI/machine, размеры заголовков и границы таблиц;
+\item обязательные метаданные target/lifecycle и зарезервированные поля;
+\item уникальность заголовков сегментов (Program Headers), права доступа, соотношения размеров, выравнивание и границы OFF60;
+\item соответствие каждой ALLOC-секции её архитектурному сегменту;
+\item selector/range точек входа lifecycle;
+\item нулевые байты DATA guard в статическом исполняемом образе;
+\item для ABI v2/v3 --- структура dynsym/RELA, диапазоны символов, границы исправляемых полей и семантика целевого сегмента; для ABI v3 дополнительно canonical .dynamic, SONAME/NEEDED и соответствие identity загруженного ET_DYN запрошенному SONAME.
+\end{itemize}
+
+\begin{principle}
+\textbf{Загрузчик проверяет, но не «чинит».} Он не выводит lifecycle из названий секций, не создаёт недостающий guard, не меняет порядок байтов PROGRAM, не увеличивает STACK, не ослабляет HEAP limit и не переинтерпретирует несовместимый профиль целевой архитектуры. Компоновщик запуска ABI v2/v3 выполняет только явно определённые сохранённые relocation, dependency discovery и разрешение символов.
+\end{principle}
+
+\section{Режимы команды mcpu-ld}
+
+\begin{lstlisting}[language={}]
+# partial ET_REL
+mcpu-ld -r -o combined.o a.o b.o
+
+# статический ABI v1
+mcpu-ld --stack-size=4096 --heap-size=64K --heap-limit=1M \
+ -L$HOME/.mcpu/lib -lmodel -o app main.o
+
+# главный исполняемый файл frozen ABI v3
+mcpu-ld --dynamic --stack-size=64K --no-heap \
+ -L$HOME/.mcpu/lib -lrk4 -o model pendulum.o
+
+# shared object ABI v3
+mcpu-ld --shared --soname=librk4.so.1 \
+ -o librk4.so.1.0.0 rk4.o
+\end{lstlisting}
+
+STACK/HEAP принадлежат только \code{ET_EXEC}. \code{--soname} допустим только для \code{--shared}. \code{-r} не создаёт executable/runtime metadata и использует для \code{-l} только static archives. Суффиксы размера K/KiB, M/MiB, G/GiB используют двоичные множители.
+
+
+\section{Инструменты анализа и безопасного редактирования MELF}
+Binary utilities работают поверх того же MELF contract и не создают отдельного container ABI.
+
+\subsection{Read-only inspection}
+\code{mcpu-readelf} показывает file/program/section headers, symbol tables, RELA, \code{.dynamic} и MCPU-specific metadata. \code{mcpu-nm} выводит \code{.symtab} или \code{.dynsym} и умеет читать static archives. \code{mcpu-objdump} объединяет section/relocation views с instruction disassembly; decoder общий с CPU runtime через \code{__cpu_decode_at()}, поэтому ISA не дублируется отдельным parser-ом.
+
+\subsection{mcpu-strip}
+Текущая policy намеренно консервативна. В ET\_REL ordinary \code{.symtab/.strtab} сохраняются, потому что они являются частью последующей link semantics. В ET\_EXEC/ET\_DYN ordinary non-ALLOC \code{SHT_SYMTAB} может быть удалена только если retained sections от неё не зависят; \code{.dynsym/.dynstr/.dynamic}, RELA, MCPU metadata и allocated image всегда сохраняются. Современные final-image writers уже компактны, поэтому для обычного ET\_EXEC/ET\_DYN strip часто является byte-for-byte no-op.
+
+\subsection{mcpu-objcopy}
+\code{mcpu-objcopy} выполняет только MELF-to-MELF structural copy/edit. Safe interface умеет выбирать/удалять sections и dump/add/update/rename для metadata, но запрещает разрушать allocated image, frozen dynamic metadata, runtime relocations и ET\_REL link-critical dependencies. Новая section через \code{--add-section} является non-ALLOC \code{SHT_PROGBITS}. Metadata tail может быть repacked, но program headers и runtime-visible PT\_LOAD bytes должны оставаться неизменными.
+
+Flat binary, Intel HEX, S-record, VMA/LMA и firmware-image conversion намеренно отсутствуют: виртуальная MCPU исполняет MELF напрямую.
+
+\subsection{Non-ALLOC debug metadata и frozen ABI v3}
+\begin{normative}
+Будущий debug format не обязан менять MELF ABI v3, если его sections остаются non-ALLOC, не входят в PT\_LOAD, не требуются loader/runtime для исполнения и могут быть полностью удалены без изменения guest-visible поведения. Версия такого debug format должна быть самостоятельной и не кодироваться повышением \code{EI_ABIVERSION} MELF.
+\end{normative}
+
+В ET\_REL debug sections при необходимости могут иметь link-time RELA к code/data symbols; такие relocations являются metadata link-editor contract и не должны становиться обязательными runtime relocations final ET\_EXEC/ET\_DYN.
+
+
+\section{Правила совместимости}
+
+\begin{enumerate}
+\item Статический \code{ET_EXEC} ABI v1 и \code{ET_REL} ABI v1 остаются основой статического/разработческого пути; ABI-v1 executable отвергает preload.
+\item Исторический ABI v2 остаётся читаемым и использует explicit-preload startup model без SONAME/NEEDED.
+\item Frozen ABI v3 является текущим normal dynamic ABI. Несовместимое изменение \code{.dynamic}, NEEDED/SONAME, runtime scope или retained-relocation contract требует нового номера ABI.
+\item Static \code{lib*.a} и \code{mcpu-ld -r} являются link-time механизмами над ABI-v1 ET_REL и не требуют ABI v4.
+\item Ревизия ISA и CALL ABI остаются равны 1 независимо от версии контейнерного dynamic ABI.
+\item Будущие расширения должны использовать явные version/feature поля, а не менять смысл зарезервированных битов или записей молча. Non-ALLOC debug metadata с самостоятельной версией формата не требует нового MELF ABI, пока loader/runtime semantics не зависят от неё.
+\end{enumerate}
+
+
+\appendix
+
+\section{Размеры сериализованных записей}
+
+\begin{center}
+\begin{tabularx}{0.9\textwidth}{@{}L{48mm}L{22mm}Y@{}}
+\toprule
+\textbf{Запись} & \textbf{Байт} & \textbf{Назначение}\\
+\midrule
+MElf64\_Ehdr & 88 & заголовок файла\\
+MElf64\_Shdr & 64 & заголовок секции\\
+MElf64\_Sym & 24 & запись таблицы символов\\
+MElf64\_Rela & 24 & запись RELA\\
+MElf64\_Dyn & 16 & запись \code{.dynamic} ABI v3\\
+MElf64\_Phdr & 64 & заголовок сегмента (Program Header) с \code{p_maxsz}\\
+MElf\_McpuTarget & 64 & метаданные профиля целевой архитектуры\\
+MElf\_McpuLifecycle & 32 & метаданные lifecycle\\
+McpuStartup & 32 & заголовок области запуска (startup) в STACK guest-программы; это не сериализованные метаданные MELF\\
+\bottomrule
+\end{tabularx}
+\end{center}
+
+\section{Канонические имена секций}
+
+\begin{center}
+\begin{tabularx}{\textwidth}{@{}L{62mm}Y@{}}
+\code{.init}, \code{.text}, \code{.fini} & Секции машинного кода PROGRAM.\\
+\code{.data}, \code{.bss} & Объекты DATA; \code{.bss} имеет тип NOBITS.\\
+\code{.control}, \code{.input}, \code{.output}, \code{.trace} & Модельные пространства, принадлежащие ET\_EXEC.\\
+\code{.symtab}, \code{.strtab} & Информация о символах для разработки и статической компоновки.\\
+\code{.dynsym}, \code{.dynstr} & Runtime symbol/string tables ABI v2/v3.\\
+\code{.dynamic} & Non-ALLOC dependency metadata ABI v3: SONAME/NEEDED/NULL.\\
+\code{.rela.init}, \code{.rela.text}, \code{.rela.fini} & Секции relocation для PROGRAM.\\
+RELA секций данных & \code{.rela.data}, \code{.rela.control}, \code{.rela.input}, \code{.rela.output}, \code{.rela.trace}.\\
+\code{.mcpu.target} & Обязательный профиль целевой архитектуры.\\
+\code{.mcpu.lifecycle} & Окончательный lifecycle-контракт исполняемого файла.\\
+\code{.shstrtab} & Таблица строк с именами секций.\\
+\end{tabularx}
+\end{center}
+
+\section{Нормативная памятка frozen dynamic ABI v3}
+
+\begin{normative}
+\begin{itemize}
+\item New dynamic output использует ABI v3; ABI v2 поддерживается как исторический explicit-preload input format.
+\item ABI-v3 ET_DYN имеет обязательный SONAME и не владеет lifecycle, model spaces, STACK или HEAP.
+\item ABI-v3 ET_EXEC/ET_DYN имеют canonical non-ALLOC \code{.dynamic}; NEEDED содержит SONAME, а не pathname или development symlink name.
+\item Normal dependencies обнаруживаются рекурсивно по SONAME, deduplicate/cycle handling выполняются до guest execution.
+\item GLOBAL сильнее WEAK; explicit preload имеет наивысший equal-strength scope и rightmost priority; затем main executable; затем breadth-first/left-to-right NEEDED graph.
+\item LOCAL никогда не участвует в name interposition; нуждающиеся в process base LOCAL ET_DYN relocations сохраняются и rebased внутри исходного модуля.
+\item Все runtime relocation и symbol resolution заканчиваются до \code{_start}; PLT/GOT, lazy binding, \code{dlopen()}, \code{dlsym()} и \code{dlclose()} отсутствуют.
+\item Во всём process DATA существует один NULL guard; отдельный ET_DYN guard не создаётся.
+\item Static archives и \code{mcpu-ld -r} работают на link-time поверх ET_REL и не меняют frozen ABI v3.
+\end{itemize}
+\end{normative}
+
+\end{document}