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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
//! v1.3 Phase AOT Stage 7 polish 3 — deploy-side PE/COFF section
//! walker for Windows AOT binaries.
//!
//! # Why this module exists
//!
//! On Unix targets (ELF + lld) and macOS (Mach-O + Apple ld), the
//! static linker auto-synthesizes `__start_<name>` / `__stop_<name>`
//! (ELF) or `section$start$<seg>$<sect>` / `section$end$<seg>$<sect>`
//! (Mach-O) bracket symbols around any user-defined section whose
//! name is a valid C identifier. The deploy walkers in
//! `aot_strkey_resolver` and `aot_trace_registry` rely on those
//! externs to find their bracketed sections at run time.
//!
//! **Windows PE/COFF has no such convention.** Neither `link.exe` nor
//! `lld-link` synthesize bracket symbols. The two paths to enumerate
//! a section at run time on Windows are:
//!
//! 1. **Per-section registry**: emit a per-`.o` "(slot_id, bytes_id)"
//! pair into a known-name registry section, with one bracket
//! written by hand into a shim object. Strip-friendly but requires
//! changes to the lowerer's data-emission shape.
//!
//! 2. **PE header walk**: at run time, call `GetModuleHandleW(NULL)`
//! to get the loaded module's image base, parse the DOS stub /
//! NT headers / section table, and find the named section by
//! walking the `IMAGE_SECTION_HEADER[]` array. Zero compile-time
//! cost, zero changes to the emit path — the walker is purely a
//! deploy-side concern.
//!
//! Option (2) is what we implement here. It's the conventional shape
//! for Windows-side section enumeration (e.g. `libunwind`'s
//! `__unw_init_local`, the Rust stdlib's TLS callback registry).
//!
//! # PE section name 8-byte limit
//!
//! PE/COFF section headers carry the name as a fixed `[u8; 8]` field
//! (`IMAGE_SECTION_HEADER::Name`). Object-file format COFF supports
//! long names via a `"/<decimal-offset>"` reference into the COFF
//! string table — but that mechanism is **not preserved** by either
//! `link.exe` or `lld-link` into the final PE image. Any input
//! section name > 8 bytes lands in the PE with either a truncated
//! name (typical) or a `/<offset>` form pointing at a string table
//! the linker chose not to emit (rare; produces an unfindable
//! section).
//!
//! Consequence: the `luna-aot` Windows path uses **deliberately
//! short** section names (`.lt_meta` for trace meta, `.lt_skix` for
//! strkey idx, both 7 chars) so the post-link PE preserves the name
//! verbatim and our walker can match by exact byte equality.
//!
//! # Why hand-rolled winapi externs
//!
//! `luna-runtime-helpers` already depends on `luna-jit` (Stage 7
//! sub-piece 1) which transitively pulls cranelift + a Windows winapi
//! crate. We could use those types, but the surface this module needs
//! is tiny (one fn import + one struct definition), and hand-rolling
//! keeps the dependency story for non-Windows targets unchanged. The
//! `winapi` / `windows-sys` ecosystem also routinely shifts shape
//! between major versions; a hand-rolled local extern is immune to
//! upstream churn.
//!
//! # Safety contract
//!
//! - Only safe to call **once the binary is fully loaded**. In the
//! AOT-binary deploy shape, `luna_aot_run` is invoked from the C
//! `main`, which runs after the loader has mapped the entire image
//! and applied base relocations — the contract is trivially met.
//! - `GetModuleHandleW(NULL)` returns the image base of the calling
//! process. The PE header layout is stable across Windows versions
//! (PE32+ has been the only 64-bit shape since Vista).
//! - All pointer arithmetic stays within the image bounds verified
//! by [`find_section`]'s `e_lfanew` + `NumberOfSections` checks.
// ────────────────────────────────────────────────────────────────────
// Hand-rolled winapi externs.
//
// Only one fn import: `GetModuleHandleW(NULL)` returns the image base
// of the current process as `HMODULE` (= `*mut u8`). Linked against
// kernel32, which is already in the `luna-aot` link line for the
// MinGW Windows target (see `crates/luna-aot/src/embed.rs::link_aot_
// binary_for` Windows arm: `-lkernel32`).
//
// `#[link(name = "kernel32")]` is redundant on `x86_64-pc-windows-gnu`
// (MinGW's link line carries `-lkernel32` by default and rust's
// `windows-targets` ships an import lib), but stating it explicitly
// avoids "undefined reference to `GetModuleHandleW`" if a future
// MinGW config drops the default lib.
// ────────────────────────────────────────────────────────────────────
unsafe extern "system"
// ────────────────────────────────────────────────────────────────────
// PE/COFF on-disk header layout (PE32+, x86_64 / arm64).
//
// The structs below are subsets — we only define fields up to the
// last one we read. The layout is `#[repr(C, packed)]` because PE
// headers are byte-streams without natural alignment; the
// `read_unaligned` calls in `find_section` are the safe way to
// dereference these.
// ────────────────────────────────────────────────────────────────────
/// DOS stub header at the start of every PE image. The only field
/// we care about is `e_lfanew` — the offset to the NT headers.
/// COFF file header — sits at `image_base + e_lfanew + 4` (after the
/// "PE\0\0" signature). Carries the section count.
/// PE section header — fixed 40 bytes per entry. We only need
/// `name`, `virtual_size`, and `virtual_address` to compute the
/// section's in-memory base + length. `virtual_address` is the RVA
/// (offset from `image_base`); the run-time base is `image_base +
/// virtual_address` after the loader has applied base relocations.
// ────────────────────────────────────────────────────────────────────
// Public API.
// ────────────────────────────────────────────────────────────────────
/// Find a PE section by name in the calling process's loaded image.
///
/// Returns `Some((ptr, len))` where `ptr` is the section's run-time
/// start address and `len` is `virtual_size` (the section's logical
/// length, not the file-padded `size_of_raw_data`). Returns `None`
/// when:
///
/// - The PE header doesn't parse (DOS magic mismatch, `e_lfanew`
/// out of plausible range — defensive checks against a stripped
/// or corrupted binary).
/// - The requested section name isn't present.
/// - The section is present but has `virtual_size == 0` (empty —
/// treated as "not found" so the caller's bracket-walk equivalent
/// returns a clean zero count, not an empty slice that triggers
/// the "from_raw_parts with non-null but zero len" debug assert
/// downstream).
///
/// # Section name argument
///
/// `name` must be ≤ 8 bytes — PE section names are fixed 8-byte
/// fields. Names < 8 bytes are zero-padded for comparison so callers
/// pass `b".lt_meta"` (8 bytes including the leading `.`) or
/// `b".lt_skix"` etc.; the function returns `None` immediately on a
/// > 8 byte name (configuration bug; surfaces as a missing section
/// at runtime).
///
/// # Safety
///
/// Safe to call. All pointer dereferences go through
/// `core::ptr::read_unaligned`; range checks bound every read to
/// within the image's mapped pages. The worst-case failure mode on a
/// pathologically corrupt PE is a segfault inside the loader-mapped
/// image — but a binary corrupt enough to break the section walk is
/// also corrupt enough to break `LoadLibrary`, so we'd never reach
/// `luna_aot_run` in that scenario.