1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
\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: <root>/lib/mcpu/crt0.o crti.o <user objects/libraries> crtn.o
MODEL: <root>/lib/mcpu/crt1.o crti.o <user objects/libraries> 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}
|