Source/Packages

Compiler.ELF

packages/compiler/src/Compiler/ELF.alpha

282 lines59 declarations11.1 KiBSHA-256 88f2369f1a73

Complete file · line 97

ELF.alpha

Definition view
1module Compiler.ELF
2
3import Compiler.AST
4import Compiler.Lexer
5import Compiler.Parser
6import Compiler.Elaborator
7import Compiler.Codegen
8import Model.Parameter
9import Model.Word64
10import Std.Natural
11
12family ExecutableResult : Type 0
13constructor ExecutableGenerated
14field unrestricted executableBytes : Bytes
15constructor ExecutableUnboundVariable
16field unrestricted executableUnboundSpelling : Bytes
17constructor ExecutableUnsupportedTerm
18field unrestricted executableUnsupportedCode : Nat
19constructor ExecutableUnsupportedSuccessor
20
21end-family
22
23-- The ELF machines the physical executor is built for: e_machine 62
24-- (EM_X86_64) or 183 (EM_AARCH64).  Everything else in the header -- entry
25-- 0x400078, one PT_LOAD at 0x400000 with a 4 MiB memory extent, 4 KiB
26-- alignment -- is the same for both.
27def elfMachineX86_64 : Byte = (byte 62)
28def elfMachineAArch64 : Byte = (byte 183)
29
30-- the ELF identification and e_type, before e_machine
31def elfHeaderBeforeMachine : Bytes =
32  (bytes 127 69 76 70 2 1 1 0 0 0 0 0 0 0 0 0 2 0)
33
34-- from e_machine's second byte to the program header's p_filesz
35def elfHeaderAfterMachine : Bytes =
36  (bytes 0 1 0 0 0 120 0 64 0 0 0 0 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 64 0 56 0 1 0 0 0 0 0 0 0 1 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 64 0 0 0 0 0 0 0 64 0 0 0 0 0)
37
38def elfHeaderPrefix : Bytes =
39  (bytes-append elfHeaderBeforeMachine (bytes-append (bytes elfMachineX86_64) elfHeaderAfterMachine))
40
41def elfLoadSize64k : Bytes =
42  (bytes 0 0 4 0 0 0 0 0)
43
44def elfHeaderAlign : Bytes =
45  (bytes 0 16 0 0 0 0 0 0)
46
47def fixedZeroELFHeader : Bytes =
48  (bytes-append
49    elfHeaderPrefix
50    (bytes-append elfLoadSize64k (bytes-append elfLoadSize64k elfHeaderAlign)))
51
52def zeroPadding1 : Bytes =
53  (bytes 0)
54
55def zeroPadding2 : Bytes =
56  (bytes-append zeroPadding1 zeroPadding1)
57
58def zeroPadding4 : Bytes =
59  (bytes-append zeroPadding2 zeroPadding2)
60
61def zeroPadding8 : Bytes =
62  (bytes-append zeroPadding4 zeroPadding4)
63
64def zeroPadding16 : Bytes =
65  (bytes-append zeroPadding8 zeroPadding8)
66
67def zeroPadding32 : Bytes =
68  (bytes-append zeroPadding16 zeroPadding16)
69
70def zeroPadding64 : Bytes =
71  (bytes-append zeroPadding32 zeroPadding32)
72
73def zeroPadding128 : Bytes =
74  (bytes-append zeroPadding64 zeroPadding64)
75
76def zeroPadding256 : Bytes =
77  (bytes-append zeroPadding128 zeroPadding128)
78
79def zeroPadding512 : Bytes =
80  (bytes-append zeroPadding256 zeroPadding256)
81
82def zeroPadding1024 : Bytes =
83  (bytes-append zeroPadding512 zeroPadding512)
84
85def zeroPadding2048 : Bytes =
86  (bytes-append zeroPadding1024 zeroPadding1024)
87
88def zeroPadding4096 : Bytes =
89  (bytes-append zeroPadding2048 zeroPadding2048)
90
91def zeroPadding8192 : Bytes =
92  (bytes-append zeroPadding4096 zeroPadding4096)
93
94def zeroPadding16384 : Bytes =
95  (bytes-append zeroPadding8192 zeroPadding8192)
96
97def zeroPadding32768 : Bytes =
98  (bytes-append zeroPadding16384 zeroPadding16384)
99
100def zeroPadding65536 : Bytes =
101  (bytes-append zeroPadding32768 zeroPadding32768)
102
103def zeroPadding131072 : Bytes =
104  (bytes-append zeroPadding65536 zeroPadding65536)
105
106def zeroPadding262144 : Bytes =
107  (bytes-append zeroPadding131072 zeroPadding131072)
108
109def wrapMachineCode =
110  (lambda unrestricted machineCode : Bytes .
111    (bytes-append (bytes-append fixedZeroELFHeader machineCode) zeroPadding262144))
112
113-- Larger LOAD segment (4 MiB) for the PHYSICAL native-program path only (nvctl/GPU probes whose
114-- interpreted image body + embedded kernel + upload payloads exceed the 256 KiB fixedZeroELFHeader
115-- segment). The header LENGTH is byte-identical to fixedZeroELFHeader (120 bytes), so the machine
116-- code still starts at file offset 120 and the interpreter's state/scratch base is unchanged; only
117-- the mapped segment END moves out from 0x440000 to 0x800000. The self-host compiler ELF path
118-- (compileClosedZeroExecutable -> wrapMachineCode) is deliberately NOT changed, so its byte-identity
119-- is preserved. p_filesz = p_memsz = 0x00400000 (4 MiB); the file is padded to cover it.  The old
120-- 1-MiB physical segment was smaller than a valid 4,242-command checkpoint-resume program: Linux
121-- mapped only 0x400000..0x500000 and execution faulted exactly at the unmapped 0x500000 boundary.
122def zeroPadding524288 : Bytes =
123  (bytes-append zeroPadding262144 zeroPadding262144)
124
125def zeroPadding1048576 : Bytes =
126  (bytes-append zeroPadding524288 zeroPadding524288)
127
128def zeroPadding2097152 : Bytes =
129  (bytes-append zeroPadding1048576 zeroPadding1048576)
130
131def zeroPadding4194304 : Bytes =
132  (bytes-append zeroPadding2097152 zeroPadding2097152)
133
134def elfLoadSize4M : Bytes =
135  (bytes 0 0 64 0 0 0 0 0)
136
137def fixedZeroELFHeaderLarge : Bytes =
138  (bytes-append
139    elfHeaderPrefix
140    (bytes-append elfLoadSize4M (bytes-append elfLoadSize4M elfHeaderAlign)))
141
142def wrapMachineCodeLarge =
143  (lambda unrestricted machineCode : Bytes .
144    (bytes-append (bytes-append fixedZeroELFHeaderLarge machineCode) zeroPadding4194304))
145
146-- Encode a model word in the little-endian byte order used by ELF64 fields.
147def elfModelWord64Bytes =
148  (lambda unrestricted value : (family ModelWord64) .
149    (eliminate
150      ModelWord64
151      (lambda unrestricted current : (family ModelWord64) . Bytes)
152      value
153      (branch ModelWord64Value b0 b1 b2 b3 b4 b5 b6 b7 .
154        (bytes b0 b1 b2 b3 b4 b5 b6 b7))))
155
156-- The physical executor still receives the same 4 MiB PT_LOAD memory extent,
157-- but the file carries only its header and machine bytes, padded with zeros
158-- to the next 0x1000-byte page.  Linux zero-fills p_memsz - p_filesz,
159-- avoiding a 4 MiB compile-time Bytes value.  The page padding is not
160-- optional: the segment is not writable, and a kernel before 6.7 zeroes the
161-- rest of the last file-backed page itself (padzero) and fails the exec with
162-- EFAULT when it cannot write it -- measured 2026-09-24 on a RunPod host with
163-- Linux 6.5.0-44: every artifact died in execve with SIGSEGV; the same
164-- bytes padded to a page loaded.  With p_filesz ending on a page boundary
165-- there is no partial page to zero.
166def elfPageBytes : Nat = 4096
167
168-- zeroPadding<bit> when that bit of n is set
169def elfZerosIfBit =
170  (lambda unrestricted n : Nat . (lambda unrestricted bit : Nat . (lambda unrestricted chunk : Bytes .
171    (nat-eliminate (lambda unrestricted current : Nat . Bytes) b""
172      (lambda unrestricted predecessor : Nat . (lambda unrestricted induction : Bytes . chunk))
173      (naturalModuloUnchecked (naturalDivideUnchecked n bit) 2)))))
174
175-- n zero bytes, n below a page
176def elfZerosBelowPage =
177  (lambda unrestricted n : Nat .
178    (bytes-append (elfZerosIfBit n 2048 zeroPadding2048) (bytes-append (elfZerosIfBit n 1024 zeroPadding1024)
179    (bytes-append (elfZerosIfBit n 512 zeroPadding512) (bytes-append (elfZerosIfBit n 256 zeroPadding256)
180    (bytes-append (elfZerosIfBit n 128 zeroPadding128) (bytes-append (elfZerosIfBit n 64 zeroPadding64)
181    (bytes-append (elfZerosIfBit n 32 zeroPadding32) (bytes-append (elfZerosIfBit n 16 zeroPadding16)
182    (bytes-append (elfZerosIfBit n 8 zeroPadding8) (bytes-append (elfZerosIfBit n 4 zeroPadding4)
183    (bytes-append (elfZerosIfBit n 2 zeroPadding2) (elfZerosIfBit n 1 zeroPadding1)))))))))))))
184
185-- the zeros that carry header + machine code to the next page
186def elfPagePadding =
187  (lambda unrestricted machineCode : Bytes .
188    (elfZerosBelowPage
189      (naturalModuloUnchecked
190        (naturalSaturatingSubtract elfPageBytes
191          (naturalModuloUnchecked (naturalAdd (byte-to-nat (byte 120)) (bytes-length machineCode)) elfPageBytes))
192        elfPageBytes)))
193
194-- elfHeaderPrefix for another e_machine
195def elfHeaderPrefixFor =
196  (lambda unrestricted machine : Byte .
197    (bytes-append elfHeaderBeforeMachine (bytes-append (bytes machine) elfHeaderAfterMachine)))
198
199def fixedZeroELFHeaderSparseLargeFor =
200  (lambda unrestricted machine : Byte .
201    (lambda unrestricted machineCode : Bytes .
202      (let unrestricted fileExtent =
203        (modelWord64FromNaturalTruncated
204          (naturalAdd (byte-to-nat (byte 120)) (naturalAdd (bytes-length machineCode) (bytes-length (elfPagePadding machineCode)))))
205      in
206        (bytes-append
207          (elfHeaderPrefixFor machine)
208          (bytes-append
209            (elfModelWord64Bytes fileExtent)
210            (bytes-append elfLoadSize4M elfHeaderAlign))))))
211
212def wrapMachineCodeSparseLargeFor =
213  (lambda unrestricted machine : Byte .
214    (lambda unrestricted machineCode : Bytes .
215      (bytes-append (fixedZeroELFHeaderSparseLargeFor machine machineCode) (bytes-append machineCode (elfPagePadding machineCode)))))
216
217-- The same container with its memory extent exactly its file extent.  The
218-- 4 MiB extent above dates from when the file itself had to cover the
219-- image; the file now does, and nothing is read from memory beyond it (an
220-- embedded artifact's payloads are read with pread).  The AArch64 host uses
221-- this one: a loader that maps no zero-filled tail is also one qemu-user
222-- accepts without the segment being writable, so the executable its tests
223-- run is the executable itself.  x86-64 keeps the 4 MiB extent, which is
224-- what its published executables carry.
225def fixedZeroELFHeaderExactFor =
226  (lambda unrestricted machine : Byte .
227    (lambda unrestricted machineCode : Bytes .
228      (let unrestricted fileExtent =
229        (elfModelWord64Bytes
230          (modelWord64FromNaturalTruncated
231            (naturalAdd (byte-to-nat (byte 120)) (naturalAdd (bytes-length machineCode) (bytes-length (elfPagePadding machineCode))))))
232      in
233        (bytes-append
234          (elfHeaderPrefixFor machine)
235          (bytes-append fileExtent (bytes-append fileExtent elfHeaderAlign))))))
236
237def wrapMachineCodeExactFor =
238  (lambda unrestricted machine : Byte .
239    (lambda unrestricted machineCode : Bytes .
240      (bytes-append (fixedZeroELFHeaderExactFor machine machineCode) (bytes-append machineCode (elfPagePadding machineCode)))))
241
242def fixedZeroELFHeaderSparseLarge = (fixedZeroELFHeaderSparseLargeFor elfMachineX86_64)
243
244def wrapMachineCodeSparseLarge = (wrapMachineCodeSparseLargeFor elfMachineX86_64)
245
246def fixedZeroELF : Bytes =
247  (wrapMachineCode fixedZeroMachineCode)
248
249def compileClosedZeroExecutable =
250  (lambda unrestricted result : (family ClosedNaturalElaboration) .
251    (eliminate
252      CodegenResult
253      (lambda unrestricted value : (family CodegenResult) . (family ExecutableResult))
254      (compileClosedNaturalElaboration result)
255      (branch
256        CodeGenerated
257        machineCode
258        .
259        (constructor ExecutableResult ExecutableGenerated (wrapMachineCode machineCode)))
260      (branch
261        CodegenUnboundVariable
262        codegenUnboundSpelling
263        .
264        (constructor ExecutableResult ExecutableUnboundVariable codegenUnboundSpelling))
265      (branch
266        CodegenUnsupportedTerm
267        codegenUnsupportedCode
268        .
269        (constructor ExecutableResult ExecutableUnsupportedTerm codegenUnsupportedCode))))
270
271def executableSample : (family ExecutableResult) =
272  (compileClosedZeroExecutable elaboratedParserSample)
273
274def executableSize : Nat =
275  (eliminate
276    ExecutableResult
277    (lambda unrestricted result : (family ExecutableResult) . Nat)
278    executableSample
279    (branch ExecutableGenerated executableBytes . (bytes-length executableBytes))
280    (branch ExecutableUnboundVariable executableUnboundSpelling . zero)
281    (branch ExecutableUnsupportedTerm executableUnsupportedCode . zero)
282    (branch ExecutableUnsupportedSuccessor . zero))

The compiler supplied declaration spans and resolved links from this source snapshot. This page does not assert that this file belongs to a checked closure.