\section{HLL Runtime ABI: PROGRAM, MODEL, CRT и MCPU Libc} \label{sec:hll-crt-contract} Этот раздел фиксирует подписанный HLL Runtime ABI. Он описывает границу между объектами, которые в будущем генерирует \code{mcpu-cc}, существующим \code{mcpu-ld}, CRT, MCPU Libc и уже действующим MELF lifecycle. Контракт не зависит от номера выпуска LibMCPU. \begin{principle} \textbf{Разделение уровней.} HLL-функции \code{main()}, \code{init()}, \code{life()} и \code{fini()} являются обычными функциями CALL ABI и завершаются \code{RET}. Машинные точки \code{_start}, \code{_life}, \code{_fini}, а также lifecycle-инструкции \code{END}/\code{EXIT} принадлежат CRT и MELF runtime. Пользовательская HLL-функция не должна исполнять raw \code{END} или \code{EXIT} из открытого CALL frame. \end{principle} \subsection{Целевая integer-модель HLL ABI} HLL ABI использует фиксированные целевые типы \code{int8/int16/int32/int64}, соответствующие unsigned-типы и \code{PTR64}. Исторические C-имена \code{char/short/int/long} не определяют бинарный ABI. \code{PTR64} --- обозначение бинарного pointer object, а не синоним \code{int64*}. Канонические compiler-visible пределы публикуются препроцессором целевой среды. Для жизненного цикла важны определения: \begin{lstlisting}[language={}] #define LIFE_END 0 #define LIFE_CONTINUE (-__INT32_MAX__ - 1) \end{lstlisting} Таким образом минимальное значение signed int32 выражается непосредственно через target predefined macro \code{__INT32_MAX__}. \subsection{Две формы законченной HLL-программы} Исходный compilation unit определяет ровно одну из двух форм: \begin{center} \begin{tabularx}{0.94\textwidth}{@{}L{27mm}L{66mm}Y@{}} \toprule \textbf{Форма} & \textbf{Функции HLL ABI} & \textbf{Назначение}\\ \midrule PROGRAM & \code{int32 main(int64 argc, PTR64 argv)}; optional source-level \code{void fini(void)} & однократная обычная программа\\ MODEL & \code{int32 init(int64 argc, PTR64 argv)}; \code{int32 life(void)}; optional source-level \code{void fini(void)} & модель с INIT и повторяющимися LIFE-фазами\\ \bottomrule \end{tabularx} \end{center} Frontend нормализует optional source-level \code{fini}: если программист её не определил, compiler создаёт пустую strong GLOBAL/FUNC \code{fini}. Поэтому defining ET_REL всегда содержит: \begin{itemize} \item PROGRAM: strong GLOBAL/FUNC \code{main} и \code{fini}; \item MODEL: strong GLOBAL/FUNC \code{init}, \code{life} и \code{fini}. \end{itemize} Все complete-program definitions одной формы принадлежат \textbf{одному} defining ET_REL. Другие translation units могут их объявлять, но не определять. \code{main} несовместим с парой \code{init/life}; неполная или разделённая между несколькими ET_REL форма является ошибкой final link. \subsection{Автоматическая классификация в mcpu-ld} Для final ET_EXEC \code{mcpu-ld} классифицирует обычные входные ET_REL до разрешения библиотек. Никаких ключей \code{--program} или \code{--model} нет: \begin{center} \begin{tabularx}{0.88\textwidth}{@{}L{54mm}Y@{}} \toprule \textbf{Определения defining ET_REL} & \textbf{Режим}\\ \midrule \code{main + fini} & HLL PROGRAM\\ \code{init + life + fini} & HLL MODEL\\ \code{_start/_life[/_fini]} без HLL roots & low-level MELF executable; automatic CRT не добавляется\\ \bottomrule \end{tabularx} \end{center} Смешение raw lifecycle definitions с HLL complete-program roots запрещено. Символы из \code{-l...} не используются для классификации и потому библиотека не может случайно превратить link в PROGRAM или MODEL. После классификации linker автоматически вставляет CRT из активного MCPU system root: \begin{McpuCode} PROGRAM: /lib/mcpu/crt0.o crti.o crtn.o MODEL: /lib/mcpu/crt1.o crti.o crtn.o \end{McpuCode} Отсутствие любого обязательного CRT object является fatal link error. Переход к другому root или деградация в low-level link не выполняются. \subsection{Распределение ответственности CRT} Канонические исходники принадлежат LibMCPU и устанавливаются как target runtime objects: \begin{center} \begin{tabularx}{0.92\textwidth}{@{}L{26mm}Y@{}} \toprule \code{crt0.o} & PROGRAM adapter; формирует \code{_start == _life} в \code{.text} и вызывает \code{main}.\\ \code{crt1.o} & MODEL adapter; формирует \code{_start} в \code{.init} и \code{_life} в \code{.text}, вызывает \code{init}/\code{life}.\\ \code{crti.o} & общая startup state/validation и вызовы начала MCPU Libc.\\ \code{crtn.o} & обязательная HLL \code{_fini}, PRE_RUNTIME/RUNNING branch и graceful finalization.\\ \bottomrule \end{tabularx} \end{center} HLL ET_EXEC всегда получает \code{_fini}, даже если source-level \code{fini()} была опущена и нормализована frontend-ом в пустую функцию. \subsection{McpuStartup, argc и argv} Loader строит \code{McpuStartup} один раз до первой guest instruction. Заголовок, массив \code{argv[argc+1]} и строки аргументов остаются действительными до конца процесса. CRT проверяет version/size/representation, затем проверяет, что \code{argc} не превышает \code{__INT64_MAX__}, и преобразует unsigned startup field в target signed \code{int64}. Существующий \code{PTR64 argv} переиспользуется; MCPU Libc не копирует и не владеет \code{argc/argv}. Для \code{main()} и \code{init()} CRT создаёт обычный 16-байтовый CALL argument block: \begin{center} \begin{tabularx}{0.72\textwidth}{@{}L{24mm}L{30mm}Y@{}} \toprule \textbf{До CALL} & \textbf{Объект} & \textbf{После push return PC callee видит}\\ \midrule SP+0 & int64 \code{argc} & SP+8\\ SP+8 & PTR64 \code{argv} & SP+16\\ \bottomrule \end{tabularx} \end{center} Возврат \code{int32} из \code{main/init/life} находится в канонической Return Value Area \code{r32@0x7fffc}. \subsection{Внутренние hooks MCPU Libc} CRT использует ровно четыре reserved internal hooks: \begin{lstlisting}[language={}] void __mcpu_libc_init(void); void __mcpu_init_array(void); void __mcpu_fini_array(void); void __mcpu_libc_fini(void); \end{lstlisting} Это обычные no-argument CALL ABI functions с \code{RET}. Startup order после успешной validation: \begin{McpuCode} __mcpu_libc_init() __mcpu_init_array() main(...) или init(...) \end{McpuCode} Graceful HLL finalization в состоянии RUNNING: \begin{McpuCode} fini() __mcpu_fini_array() __mcpu_libc_fini() END \end{McpuCode} Physical ABI init/fini arrays и constructor priorities могут развиваться отдельно; наличие hooks уже фиксирует границу CRT и Libc. Обычная ABI-v3 DT_NEEDED библиотека MCPU Libc имеет право предоставить эти четыре symbols. Explicit preload не имеет права их подменять. Complete-program roots \code{main/init/life/fini}, lifecycle symbols и CRT-private names также не являются пользовательским interposition API. \subsection{PRE_RUNTIME и RUNNING} CRT хранит private logical state \code{PRE_RUNTIME} / \code{RUNNING}; его физическое представление не является public ABI. До успешного завершения \code{__mcpu_libc_init()} и \code{__mcpu_init_array()} состояние остаётся PRE_RUNTIME. Если validation startup area или диапазона \code{argc} завершается ошибкой, CRT выполняет graceful \code{EXIT(startup_failure_status)}. Supervisor входит в \code{_fini}, но PRE_RUNTIME branch выполняет только \code{END}: пользовательская \code{fini}, fini-array и libc-fini не вызываются. Namespace startup-failure status не выводится из host errno. После startup hooks CRT устанавливает RUNNING. С этого момента normal graceful EXIT всегда приводит к полному HLL finalization. Поэтому ненулевой возврат \code{init()} уже находится в RUNNING и вызывает пользовательскую \code{fini()}; MODEL fini должна допускать частично инициализированную модель. \subsection{PROGRAM lifecycle} PROGRAM не имеет отдельной \code{.init}. \code{_start == _life} находится в \code{.text}. После validation и startup hooks CRT вызывает \begin{lstlisting}[language={}] int32 main(int64 argc, PTR64 argv); \end{lstlisting} и после \code{RET} выполняет \code{EXIT(main_status)}. Дополнительного LIFE \code{END} после \code{main()} нет. \subsection{MODEL lifecycle} MODEL использует \code{_start} в \code{.init} и \code{_life} в \code{.text}. В INIT CRT вызывает \begin{lstlisting}[language={}] int32 init(int64 argc, PTR64 argv); \end{lstlisting} Возврат 0 создаёт ровно одну INIT boundary через \code{END}. Ненулевой status выполняет \code{EXIT(status)} и переходит к полной HLL finalization, поскольку runtime уже RUNNING. Каждая LIFE-фаза является отдельным обычным вызовом: \begin{lstlisting}[language={}] int32 life(void); \end{lstlisting} Return namespace фиксирован: \begin{center} \begin{tabularx}{0.94\textwidth}{@{}L{44mm}L{34mm}Y@{}} \toprule \textbf{int32 return} & \textbf{Имя} & \textbf{Действие CRT}\\ \midrule \code{-__INT32_MAX__ - 1} & \code{LIFE_CONTINUE} & выполнить ровно один LIFE \code{END}; после resume снова вызвать \code{life()}\\ 0 & \code{LIFE_END} & \code{EXIT(0)} без дополнительного END\\ 1..\code{__INT32_MAX__} & error status & \code{EXIT(status)} без дополнительного END\\ \code{-__INT32_MAX__}..-1 & reserved & не использовать без будущего расширения ABI\\ \bottomrule \end{tabularx} \end{center} Это гарантирует, что естественное завершение модели не создаёт фиктивной последней LIFE boundary и, например, лишней строки TRACE. \subsection{HLL finalization и сохранение status} В RUNNING каноническая \code{_fini} концептуально выполняет: \begin{McpuCode} CALL fini CALL __mcpu_fini_array CALL __mcpu_libc_fini END \end{McpuCode} Original EXIT status сохраняется runtime через всю finalization sequence. Второй \code{EXIT} из \code{_fini} не выполняется. Диагностическая остановка \code{mcpu-run --steps=...} не является graceful exit и не синтезирует HLL finalization. \begin{normative} \textbf{Главный итог.} HLL-программа определяет только обычные functions. \code{mcpu-ld} распознаёт нормализованную форму ET_REL и выбирает CRT автоматически. CRT владеет MELF lifecycle, MCPU Libc владеет библиотечным runtime, а loader владеет persistent startup area. Ни один слой не дублирует ответственность соседнего. \end{normative}