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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
\documentclass[10pt,a4paper,oneside,openany]{memoir}
\newcommand{\DocShortTitle}{LibMCPU: архитектура машинного кода и ABI}
\input{libmcpu-doc-common.tex}
\hypersetup{pdftitle={LibMCPU: архитектура машинного кода, памяти и ABI вызовов},pdfsubject={Технический справочник по архитектуре LibMCPU}}
\begin{document}
\DocTitlePage{LibMCPU: архитектура машинного кода,\\памяти и ABI вызовов}{Регистровая модель, ISA, PTR64, CALL ABI, lifecycle и runtime}{Техническое справочное руководство}
\ManualIntro{Документ фиксирует текущий архитектурный контракт LibMCPU: register model, ISA, memory spaces, PTR64, CALL ABI, MELF lifecycle, syscall/error ABI, dynamic startup linking и подписанный HLL Runtime ABI между mcpu-ld, CRT и MCPU Libc. Документ намеренно не связан с номером выпуска пакета. Для бинарного формата MELF действует отдельное руководство \code{libmcpu-melf-abi-ru.pdf}.}
\begin{principle}
\textbf{Главный принцип.} Физический регистр LibMCPU нетипизирован. \REG{} задаёт только физическую ширину и байтовый адрес в едином register file. Integer/Real/Complex смысл определяется opcode; запись \code{c<bits>@addr} является semantic view того же физического регистра. Архитектурный \PTR{} также является обычным 64-битным значением, но его декодирование и права доступа задаются ABI.
\end{principle}
\tableofcontents
\clearpage
\part{Базовая архитектура, ISA и CALL ABI}
\input{libmcpu-architecture-core-ru.tex}
\part{Memory backing, представление объектов и assembler}
\input{libmcpu-architecture-runtime-ru.tex}
\input{libmcpu-isa-coverage-ru.tex}
\input{libmcpu-assembler-details-ru.tex}
\part{Program Model ABI, runtime и динамическая загрузка}
\section{Program Model Lifecycle ABI}
Каждый исполняемый MELF-файл имеет обязательные lifecycle symbols \code{_start} и \code{_life}; \code{e_entry} всегда указывает на \code{_start}. Секция \code{.init} и символ \code{_fini} необязательны. Эти точки входа принадлежат MELF/runtime ABI и не должны смешиваться с функциями будущего языка высокого уровня.
\subsection{С отдельным INIT}
\begin{lstlisting}
.init
_start:
... one-time startup ...
END
.text
_life:
... resumable LIFE phase ...
END
\end{lstlisting}
Тогда \code{e_init_entry=_start}, а \code{e_life_entry=_life}. Первый \code{END} завершает INIT; runtime устанавливает PC в \code{_life} и начинает LIFE. Последующие LIFE \code{END} возвращают управление supervisor-у, а следующий resume продолжается с инструкции после соответствующего \code{END}.
\subsection{Без INIT}
\begin{lstlisting}
.text
_start_life:
... startup / ordinary program trampoline ...
\end{lstlisting}
Псевдометка \code{_start_life:} эквивалентна двум последовательным labels \code{_start:} и \code{_life:} по одному адресу. В final executable \code{e_init_entry=0} и \code{e_entry == e_life_entry}. Эта форма особенно естественна для обычной PROGRAM, которая заканчивается через \code{EXIT} и не создаёт LIFE boundaries.
\subsection{FINI, END и EXIT}
\code{END} завершает текущую управляемую фазу и возвращает управление supervisor-у. Graceful \code{EXIT(status)} завершает normal execution; если имеется \code{.fini}/\code{_fini}, runtime сохраняет status, входит в FINI и требует завершить FINI инструкцией \code{END}. Второй \code{EXIT} внутри FINI не является нормальным контрактом. Diagnostic stop по \code{--steps} не симулирует graceful \code{EXIT} и потому сам по себе FINI не запускает.
\input{libmcpu-hll-crt-contract-ru.tex}
\section{McpuStartup и argc/argv}
Перед первой guest-инструкцией загрузчик создаёт постоянную область запуска (startup area) в верхней части STACK. Заголовок имеет размер 32 байта и выравнивание 16 байт:
\par\medskip
\noindent\begin{tabularx}{\textwidth}{@{}L{20mm}L{30mm}L{24mm}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 & \PTR(STACK, массив argv)\\
0x18 & reserved & 8 & ноль\\
\bottomrule
\end{tabularx}
После заголовка расположен массив \code{argv[0..argc]} из PTR64, завершающий NULL, затем NUL-terminated строки аргументов. Эта область остаётся действительной всё время жизни executable. Начальное состояние: \code{PC=e_entry}, \code{SP=startup_base}, \code{FP=stack_size}.
\section{HEAP и program break}
HEAP --- отдельный runtime-сегмент SEG=0x7. Он не расширяет DATA и не имеет assembler payload. Определяемый компоновщиком неподменяемый символ \code{_end} равен \PTR(HEAP,16); это начальный и минимальный program break.
Runtime хранит \code{_heap_brk}, \code{_heap_size}, \code{_heap_capacity}, immutable \code{_heap_limit}. Запрос ниже 16 или выше limit отклоняется и возвращает прежний break. Shrink не обязан уменьшать host mapping/capacity; освобождённый range немедленно zeroes. Regrow в уже существующий backing снова открывает deterministic zero memory. При backing relocation guest pointers остаются valid, потому что содержат segment+offset.
\section{SYSCALL ABI и SYS_brk}
Zero-operand instruction \code{SYSCALL} использует fixed overlapping register area в верхней части register file.
\par\medskip
\noindent\begin{tabularx}{\textwidth}{@{}L{32mm}L{32mm}Y@{}}
\toprule
\textbf{Role} & \textbf{Address/view} & \textbf{Meaning}\\
\midrule
SYSNO & \code{r32@0x7fffc} & syscall number\\
NARGS & \code{r32@0x7fff8} & argument count\\
RET & \code{r64@0x7fff8} & return value; overlaps NARGS\\
ARG0..ARG5 & \code{r64@0x7fff0} ... \code{r64@0x7ffc8} & up to six arguments\\
\bottomrule
\end{tabularx}
Первый syscall --- \code{SYS_brk}, number 10. Он требует один argument: NULL означает query, HEAP PTR64 requests absolute new break. Общее правило syscall ABI использует стабильные host-independent MCPU errno: неуспех возвращает отрицательное \code{-MCPU_errno}, а host errno преобразуется на sysdeps boundary и наружу не выходит. \code{SYS_brk} является специальным исключением: он возвращает фактический/current HEAP PTR64 и при accepted, и при rejected request. Unsupported syscall number возвращает \code{-ENOSYS} из MCPU errno namespace.
\begin{lstlisting}
; query current break
MOV r32@0x7fff8, 1
LDI r32@0x7fffc, 10
CLR r64@0x7fff0
SYSCALL
; result: r64@0x7fff8
\end{lstlisting}
\section{Startup dynamic linking: архитектурный взгляд}
Текущий normal dynamic contract --- frozen MELF ABI v3. \code{mcpu-ld --dynamic} создаёт ABI-v3 ET_EXEC, а \code{mcpu-ld --shared --soname=NAME} --- ABI-v3 ET_DYN. В отличие от исторического ABI v2, normal dependencies записываются в canonical non-ALLOC \code{.dynamic} как \code{DT_NEEDED}, а shared object имеет обязательный \code{DT_SONAME}. ABI v2 остаётся читаемым как historical explicit-preload input format.
\code{mcpu-run} обнаруживает NEEDED graph до исполнения guest-кода, проверяет SONAME identity, загружает dependencies breadth-first/left-to-right и завершает symbol resolution/RELA до первой инструкции \code{_start}. Явный \code{--ldpreload} и \code{MCPU_LD_PRELOAD} остаются override/interposition mechanisms. PLT/GOT, lazy binding, \code{dlopen()}, \code{dlsym()} и runtime lookup после старта отсутствуют.
ET_DYN содержит только загружаемое PROGRAM/DATA содержимое \code{.text/.data/.bss} плюс non-ALLOC metadata; lifecycle, CONTROL/INPUT/OUTPUT/TRACE, STACK и HEAP принадлежат главному executable.
\subsection{GLOBAL/WEAK interposition}
Сильный GLOBAL имеет приоритет над WEAK. Для equal-strength definitions highest scope имеет explicit preload с установленным rightmost priority, затем main executable, затем normal NEEDED graph в breadth-first/left-to-right order. Lifecycle symbols \code{_start/_life/_fini} остаются частными для executable и не участвуют в interposition.
\subsection{LOCAL ET_DYN runtime rebasing}
\begin{normative}
LOCAL symbol остаётся private и non-preemptible, но ссылка на него внутри ET_DYN \textbf{не может быть окончательно превращена в process address при static link}: final PROGRAM/DATA base shared object ещё неизвестна. Поэтому такая ссылка сохраняется как LOCAL runtime RELA. Startup linker разрешает её строго против originating module и добавляет runtime segment base. LOCAL symbols не попадают в GLOBAL/WEAK winner namespace.
\end{normative}
Правило возникло как исправление runtime-address contract ещё в ABI v2 и является нормативной частью frozen ABI v3. Оно не открывает LOCAL symbol для interposition.
\section{mcpu-run как host-side test bench}
Помимо one-shot inspection options, runtime поддерживает четыре lifecycle stages:
\par\medskip
\noindent\begin{tabularx}{\textwidth}{@{}L{36mm}Y@{}}
\toprule
\textbf{Опция} & \textbf{Граница выполнения}\\
\midrule
\code{--before-run=<action>} & after load/preload/relocations/startup, before first guest instruction\\
\code{--after-init=<action>} & after INIT END; при отсутствии .init --- один раз на logical empty INIT boundary\\
\code{--on-step=<action>} & after every LIFE END\\
\code{--after-run=<action>} & after final normal stop; graceful FINI уже завершён\\
\bottomrule
\end{tabularx}
Stage-опции можно повторять; они выполняются слева направо. Фильтры по номеру шага намеренно отсутствуют.
\subsection{Typed actions}
\begin{lstlisting}[language={}]
print-int-regs:r64@0x100,r64@0x108
print-real-regs:r128@0x200,r256@0x300
set-int-regs:r64@0x100=123,r64@0x108=-17
set-real-regs:r128@0x200=1.25,r128@0x220=-2.5
print-int-<memory>:64@16,64@24
print-real-<memory>:128@32,128@48
set-int-<memory>:64@16=123,64@24=456
set-real-<memory>:128@32=1.25,128@48=2.5
\end{lstlisting}
\code{<memory>} --- data, control, heap, input, output, trace. Один scalar print-action печатает comma-list одной строкой через spaces. Set values разбираются через LibMPUIO \code{mpu_sscanf()}; whole action валидируется до первой записи, а guest PSW сохраняется вокруг host-side numeric parsing.
\subsection{Raw actions}
\code{print-raw-<memory>:<bytes>@<addr>} read-only и описывает ровно один dump. Comma-list запрещён; для нескольких dumps повторяется stage option. \code{set-raw-*} не существует. Size принимает raw bytes и K/KiB, M/MiB, G/GiB с binary multipliers. DATA raw view ограничен linked \code{.data} и не перелезает в \code{.bss}; HEAP host view ограничен prepared backing, тогда как guest access ограничен current break.
\subsection{Двойной дефис как разделитель}
Preferred CLI:
\begin{lstlisting}[language={}]
mcpu-run [runtime-options] -- executable [program-args...]
\end{lstlisting}
После separator \lstinline|--| tokens не интерпретируются как runner options. Первый token становится executable spelling и guest \code{argv[0]}. Legacy form без separator сохранена.
\section{Бинарные утилиты toolchain}
Системные инструменты MELF используют единый public format contract и не вводят альтернативного представления машинного кода.
\par\medskip
\noindent\begin{tabularx}{\textwidth}{@{}L{36mm}Y@{}}
\toprule
\textbf{Команда} & \textbf{Назначение}\\
\midrule
\code{mcpu-readelf} & Структурный read-only просмотр MELF headers, sections, symbols, RELA, dynamic и MCPU metadata.\\
\code{mcpu-ar} & Deterministic UNIX static archives из MELF ET\_REL с System-V/GNU-style symbol index.\\
\code{mcpu-nm} & Symbol-table inspection для объектов, executables, shared objects и archives.\\
\code{mcpu-objdump} & Section/relocation inspection и дизассемблирование через тот же decoder, который используется CPU runtime.\\
\code{mcpu-strip} & Консервативное удаление только non-runtime symbol metadata; link/runtime-critical sections сохраняются.\\
\code{mcpu-objcopy} & MELF-to-MELF structural copy и safe editing non-ALLOC metadata; runtime-visible image защищён.\\
\bottomrule
\end{tabularx}
\begin{principle}
MELF уже является native object/executable container виртуальной MCPU. Поэтому \code{mcpu-objcopy} не имеет режимов flat binary, Intel HEX, S-record, VMA/LMA или подготовки firmware image для SPI NOR. Metadata-only edit обязан сохранять guest-visible allocated image и frozen ABI v3 execution semantics.
\end{principle}
Английские и русские manual pages устанавливаются как section 1 для каждой команды; обзор архитектуры и toolchain доступен как \code{libmcpu(7)}.
\section{Инварианты реализации}
\begin{enumerate}
\item PROGRAM machine fields всегда MSB-first; data representation не смешивается с code representation.
\item Register address и PTR64 offset не являются host pointers.
\item Bounds/permission validation precedes guest-visible writes, где instruction contract требует atomicity.
\item DATA имеет один process-owned 16-byte NULL guard; ET_DYN не получает собственного guard.
\item HEAP имеет immutable configured limit и minimum break 16; released bytes zeroed on shrink.
\item ABI-v1 static executable полностью linked; ordinary loader его не relink-ит.
\item ABI-v2/v3 startup linking завершается до \code{_start}; после старта symbol lookup отсутствует.
\item LOCAL ET_DYN references runtime-rebased, но никогда не interpose.
\item Lifecycle boundaries определяются guest \code{END/EXIT}; stage actions лишь наблюдают/модифицируют state на host side.
\end{enumerate}
\appendix
\section{Полный справочник директив mcpu-as}
Этот appendix перечисляет assembler directives и специальные формы, которые являются частью текущего source contract. Детальная grammar выражений и ограничения sections приведены в основной части документа.
\input{libmcpu-assembler-directives-table.tex}
\section{Полная таблица OPCODE16}
Таблица перечисляет все значения public \code{cpu_opcode}. Каждый opcode однозначно задаёт число и тип последующих operands; PROGRAM machine fields сериализуются MSB-first. Operand spellings показаны в терминах assembler ABI.
\input{libmcpu-opcode-table.tex}
\section{PSW selectors и RCONST}
\subsection{PSW bit selectors}
\input{libmcpu-psw-table.tex}
\subsection{RCONST selectors}
\input{libmcpu-rconst-table.tex}
\section{Краткая памятка ABI}
\par\medskip
\noindent\begin{tabularx}{\textwidth}{@{}L{42mm}Y@{}}
\toprule
Register file & 512 KiB, overlapping REG24 views\\
Return Value Area & top 8192 bytes, scalar result at \code{0x80000-size}\\
PTR64 & SEG4:OFF60; DATA=0, STACK=1, PROGRAM=2, CONTROL=3, INPUT=4, OUTPUT=5, TRACE=6, HEAP=7\\
CALL alignment & 16 bytes; CALL pushes 8-byte return PC; caller owns argument block\\
PSW & bits 0..15 volatile across call; TF/IF/MODE preserved if changed\\
DATA guard & process offsets 0..15 zero\\
HEAP & begin/break minimum 16; \code{_end=HEAP:16}\\
Startup & 32-byte McpuStartup + argv PTR64 array + strings; persistent\\
Low-level lifecycle & mandatory \code{_start}, \code{_life}; optional \code{_fini}; \code{e_entry=_start}\\
HLL PROGRAM & defining ET_REL: \code{main+fini}; automatic \code{crt0.o+crti.o+crtn.o}\\
HLL MODEL & defining ET_REL: \code{init+life+fini}; automatic \code{crt1.o+crti.o+crtn.o}\\
Dynamic & frozen normal ABI v3 with SONAME/NEEDED; ABI v2 remains historical explicit-preload input format\\
\bottomrule
\end{tabularx}
\end{document}
|