authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-10-05 06:56:07-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-06 11:27:39-07:00
log969f2cff8258fc91d7037704ddac4e6f4f037029
tree12e10ee8516dde750fddf7c74b6bdf9e40a5adc1
parent2962db333f43c8bb10a1e2ad4cdd19dfab26515b

Elf2: implement virtual allocation

This allows segments to be moved around in the output file without needing to reapply relocations until virtual address space is exhaused.

6 files changed, 2659 insertions(+), 2679 deletions(-)

CMakeLists.txt+1
......@@ -561,6 +561,7 @@ set(ZIG_STAGE2_SOURCES
561561 src/libs/libunwind.zig
562562 src/link.zig
563563 src/link/C.zig
564 src/link/Coff.zig
564565 src/link/Dwarf.zig
565566 src/link/Elf.zig
566567 src/link/Elf/Archive.zig
src/Compilation.zig+11-29
......@@ -2718,35 +2718,17 @@ pub fn destroy(comp: *Compilation) void {
27182718 }
27192719 comp.crt_files.deinit(gpa);
27202720 }
2721
2722 if (comp.libunwind_static_lib) |*crt_file| {
2723 crt_file.deinit(gpa);
2724 }
2725 if (comp.libcxx_static_lib) |*crt_file| {
2726 crt_file.deinit(gpa);
2727 }
2728 if (comp.libcxxabi_static_lib) |*crt_file| {
2729 crt_file.deinit(gpa);
2730 }
2731 if (comp.compiler_rt_lib) |*crt_file| {
2732 crt_file.deinit(gpa);
2733 }
2734 if (comp.compiler_rt_obj) |*crt_file| {
2735 crt_file.deinit(gpa);
2736 }
2737 if (comp.ubsan_rt_lib) |*crt_file| {
2738 crt_file.deinit(gpa);
2739 }
2740 if (comp.ubsan_rt_obj) |*crt_file| {
2741 crt_file.deinit(gpa);
2742 }
2743 if (comp.fuzzer_lib) |*crt_file| {
2744 crt_file.deinit(gpa);
2745 }
2746
2747 if (comp.zigc_static_lib) |*crt_file| {
2748 crt_file.deinit(gpa);
2749 }
2721 if (comp.libcxx_static_lib) |*crt_file| crt_file.deinit(gpa);
2722 if (comp.libcxxabi_static_lib) |*crt_file| crt_file.deinit(gpa);
2723 if (comp.libunwind_static_lib) |*crt_file| crt_file.deinit(gpa);
2724 if (comp.tsan_lib) |*crt_file| crt_file.deinit(gpa);
2725 if (comp.ubsan_rt_lib) |*crt_file| crt_file.deinit(gpa);
2726 if (comp.ubsan_rt_obj) |*crt_file| crt_file.deinit(gpa);
2727 if (comp.zigc_static_lib) |*crt_file| crt_file.deinit(gpa);
2728 if (comp.compiler_rt_lib) |*crt_file| crt_file.deinit(gpa);
2729 if (comp.compiler_rt_obj) |*crt_file| crt_file.deinit(gpa);
2730 if (comp.compiler_rt_dyn_lib) |*crt_file| crt_file.deinit(gpa);
2731 if (comp.fuzzer_lib) |*crt_file| crt_file.deinit(gpa);
27502732
27512733 if (comp.glibc_so_files) |*glibc_file| {
27522734 glibc_file.deinit(gpa);
src/link.zig+1-1
......@@ -1265,7 +1265,7 @@ pub const File = struct {
12651265
12661266 pub const Lld = @import("link/Lld.zig");
12671267 pub const C = @import("link/C.zig");
1268 pub const Coff2 = @import("link/Coff2.zig");
1268 pub const Coff2 = @import("link/Coff.zig");
12691269 pub const Elf = @import("link/Elf.zig");
12701270 pub const Elf2 = @import("link/Elf2.zig");
12711271 pub const MachO = @import("link/MachO.zig");
src/link/Coff.zig created+2206
......@@ -0,0 +1,2206 @@
1base: link.File,
2mf: MappedFile,
3nodes: std.MultiArrayList(Node),
4import_table: ImportTable,
5strings: std.HashMapUnmanaged(
6 u32,
7 void,
8 std.hash_map.StringIndexContext,
9 std.hash_map.default_max_load_percentage,
10),
11string_bytes: std.ArrayList(u8),
12section_table: std.ArrayList(Symbol.Index),
13symbol_table: std.ArrayList(Symbol),
14globals: std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index),
15global_pending_index: u32,
16navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Symbol.Index),
17uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
18lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
19 map: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
20 pending_index: u32,
21}),
22pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct {
23 alignment: InternPool.Alignment,
24 src_loc: Zcu.LazySrcLoc,
25}),
26relocs: std.ArrayList(Reloc),
27/// This is hiding actual bugs with global symbols! Reconsider once they are implemented correctly.
28entry_hack: Symbol.Index,
29
30pub const default_file_alignment: u16 = 0x200;
31pub const default_size_of_stack_reserve: u32 = 0x1000000;
32pub const default_size_of_stack_commit: u32 = 0x1000;
33pub const default_size_of_heap_reserve: u32 = 0x100000;
34pub const default_size_of_heap_commit: u32 = 0x1000;
35
36/// This is the start of a Portable Executable (PE) file.
37/// It starts with a MS-DOS header followed by a MS-DOS stub program.
38/// This data does not change so we include it as follows in all binaries.
39///
40/// In this context,
41/// A "paragraph" is 16 bytes.
42/// A "page" is 512 bytes.
43/// A "long" is 4 bytes.
44/// A "word" is 2 bytes.
45pub const msdos_stub: [120]u8 = .{
46 'M', 'Z', // Magic number. Stands for Mark Zbikowski (designer of the MS-DOS executable format).
47 0x78, 0x00, // Number of bytes in the last page. This matches the size of this entire MS-DOS stub.
48 0x01, 0x00, // Number of pages.
49 0x00, 0x00, // Number of entries in the relocation table.
50 0x04, 0x00, // The number of paragraphs taken up by the header. 4 * 16 = 64, which matches the header size (all bytes before the MS-DOS stub program).
51 0x00, 0x00, // The number of paragraphs required by the program.
52 0x00, 0x00, // The number of paragraphs requested by the program.
53 0x00, 0x00, // Initial value for SS (relocatable segment address).
54 0x00, 0x00, // Initial value for SP.
55 0x00, 0x00, // Checksum.
56 0x00, 0x00, // Initial value for IP.
57 0x00, 0x00, // Initial value for CS (relocatable segment address).
58 0x40, 0x00, // Absolute offset to relocation table. 64 matches the header size (all bytes before the MS-DOS stub program).
59 0x00, 0x00, // Overlay number. Zero means this is the main executable.
60}
61 // Reserved words.
62 ++ .{ 0x00, 0x00 } ** 4
63 // OEM-related fields.
64 ++ .{
65 0x00, 0x00, // OEM identifier.
66 0x00, 0x00, // OEM information.
67 }
68 // Reserved words.
69 ++ .{ 0x00, 0x00 } ** 10
70 // Address of the PE header (a long). This matches the size of this entire MS-DOS stub, so that's the address of what's after this MS-DOS stub.
71 ++ .{ 0x78, 0x00, 0x00, 0x00 }
72 // What follows is a 16-bit x86 MS-DOS program of 7 instructions that prints the bytes after these instructions and then exits.
73 ++ .{
74 // Set the value of the data segment to the same value as the code segment.
75 0x0e, // push cs
76 0x1f, // pop ds
77 // Set the DX register to the address of the message.
78 // If you count all bytes of these 7 instructions you get 14, so that's the address of what's after these instructions.
79 0xba, 14, 0x00, // mov dx, 14
80 // Set AH to the system call code for printing a message.
81 0xb4, 0x09, // mov ah, 0x09
82 // Perform the system call to print the message.
83 0xcd, 0x21, // int 0x21
84 // Set AH to 0x4c which is the system call code for exiting, and set AL to 0x01 which is the exit code.
85 0xb8, 0x01, 0x4c, // mov ax, 0x4c01
86 // Peform the system call to exit the program with exit code 1.
87 0xcd, 0x21, // int 0x21
88 }
89 // Message to print.
90 ++ "This program cannot be run in DOS mode.".*
91 // Message terminators.
92 ++ .{
93 '$', // We do not pass a length to the print system call; the string is terminated by this character.
94 0x00, 0x00, // Terminating zero bytes.
95 };
96
97pub const Node = union(enum) {
98 file,
99 header,
100 signature,
101 coff_header,
102 optional_header,
103 data_directories,
104 section_table,
105 section: Symbol.Index,
106 import_directory_table,
107 import_lookup_table: ImportTable.Index,
108 import_address_table: ImportTable.Index,
109 import_hint_name_table: ImportTable.Index,
110 global: GlobalMapIndex,
111 nav: NavMapIndex,
112 uav: UavMapIndex,
113 lazy_code: LazyMapRef.Index(.code),
114 lazy_const_data: LazyMapRef.Index(.const_data),
115
116 pub const GlobalMapIndex = enum(u32) {
117 _,
118
119 pub fn globalName(gmi: GlobalMapIndex, coff: *const Coff) GlobalName {
120 return coff.globals.keys()[@intFromEnum(gmi)];
121 }
122
123 pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
124 return coff.globals.values()[@intFromEnum(gmi)];
125 }
126 };
127
128 pub const NavMapIndex = enum(u32) {
129 _,
130
131 pub fn navIndex(nmi: NavMapIndex, coff: *const Coff) InternPool.Nav.Index {
132 return coff.navs.keys()[@intFromEnum(nmi)];
133 }
134
135 pub fn symbol(nmi: NavMapIndex, coff: *const Coff) Symbol.Index {
136 return coff.navs.values()[@intFromEnum(nmi)];
137 }
138 };
139
140 pub const UavMapIndex = enum(u32) {
141 _,
142
143 pub fn uavValue(umi: UavMapIndex, coff: *const Coff) InternPool.Index {
144 return coff.uavs.keys()[@intFromEnum(umi)];
145 }
146
147 pub fn symbol(umi: UavMapIndex, coff: *const Coff) Symbol.Index {
148 return coff.uavs.values()[@intFromEnum(umi)];
149 }
150 };
151
152 pub const LazyMapRef = struct {
153 kind: link.File.LazySymbol.Kind,
154 index: u32,
155
156 pub fn Index(comptime kind: link.File.LazySymbol.Kind) type {
157 return enum(u32) {
158 _,
159
160 pub fn ref(lmi: @This()) LazyMapRef {
161 return .{ .kind = kind, .index = @intFromEnum(lmi) };
162 }
163
164 pub fn lazySymbol(lmi: @This(), coff: *const Coff) link.File.LazySymbol {
165 return lmi.ref().lazySymbol(coff);
166 }
167
168 pub fn symbol(lmi: @This(), coff: *const Coff) Symbol.Index {
169 return lmi.ref().symbol(coff);
170 }
171 };
172 }
173
174 pub fn lazySymbol(lmr: LazyMapRef, coff: *const Coff) link.File.LazySymbol {
175 return .{ .kind = lmr.kind, .ty = coff.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
176 }
177
178 pub fn symbol(lmr: LazyMapRef, coff: *const Coff) Symbol.Index {
179 return coff.lazy.getPtrConst(lmr.kind).map.values()[lmr.index];
180 }
181 };
182
183 pub const Tag = @typeInfo(Node).@"union".tag_type.?;
184
185 const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
186 const known = known: {
187 const Known = enum {
188 file,
189 header,
190 signature,
191 coff_header,
192 optional_header,
193 data_directories,
194 section_table,
195 };
196 var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
197 for (@typeInfo(Known).@"enum".fields) |field|
198 @field(mut_known, field.name) = @enumFromInt(field.value);
199 break :known mut_known;
200 };
201
202 comptime {
203 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
204 }
205};
206
207pub const DataDirectory = enum {
208 export_table,
209 import_table,
210 resorce_table,
211 exception_table,
212 certificate_table,
213 base_relocation_table,
214 debug,
215 architecture,
216 global_ptr,
217 tls_table,
218 load_config_table,
219 bound_import,
220 import_address_table,
221 delay_import_descriptor,
222 clr_runtime_header,
223 reserved,
224
225 pub const len = @typeInfo(DataDirectory).@"enum".fields.len;
226};
227
228pub const ImportTable = struct {
229 ni: MappedFile.Node.Index,
230 entries: std.AutoArrayHashMapUnmanaged(void, Entry),
231
232 pub const Entry = struct {
233 import_lookup_table_ni: MappedFile.Node.Index,
234 import_address_table_si: Symbol.Index,
235 import_hint_name_table_ni: MappedFile.Node.Index,
236 len: u32,
237 hint_name_len: u32,
238 };
239
240 const Adapter = struct {
241 coff: *Coff,
242
243 pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
244 const coff = adapter.coff;
245 const dll_name = coff.import_table.entries.values()[rhs_index]
246 .import_hint_name_table_ni.sliceConst(&coff.mf);
247 return std.mem.startsWith(u8, dll_name, lhs_key) and
248 std.mem.startsWith(u8, dll_name[lhs_key.len..], ".dll\x00");
249 }
250
251 pub fn hash(_: Adapter, key: []const u8) u32 {
252 assert(std.mem.indexOfScalar(u8, key, 0) == null);
253 return std.array_hash_map.hashString(key);
254 }
255 };
256
257 pub const Index = enum(u32) {
258 _,
259
260 pub fn get(import_index: ImportTable.Index, coff: *Coff) *Entry {
261 return &coff.import_table.entries.values()[@intFromEnum(import_index)];
262 }
263 };
264};
265
266pub const String = enum(u32) {
267 _,
268
269 pub const Optional = enum(u32) {
270 none = std.math.maxInt(u32),
271 _,
272
273 pub fn unwrap(os: String.Optional) ?String {
274 return switch (os) {
275 else => |s| @enumFromInt(@intFromEnum(s)),
276 .none => null,
277 };
278 }
279
280 pub fn toSlice(os: String.Optional, coff: *Coff) ?[:0]const u8 {
281 return (os.unwrap() orelse return null).toSlice(coff);
282 }
283 };
284
285 pub fn toSlice(s: String, coff: *Coff) [:0]const u8 {
286 const slice = coff.string_bytes.items[@intFromEnum(s)..];
287 return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
288 }
289
290 pub fn toOptional(s: String) String.Optional {
291 return @enumFromInt(@intFromEnum(s));
292 }
293};
294
295pub const GlobalName = struct { name: String, lib_name: String.Optional };
296
297pub const Symbol = struct {
298 ni: MappedFile.Node.Index,
299 rva: u32,
300 size: u32,
301 /// Relocations contained within this symbol
302 loc_relocs: Reloc.Index,
303 /// Relocations targeting this symbol
304 target_relocs: Reloc.Index,
305 section_number: SectionNumber,
306 unused0: u32 = 0,
307 unused1: u32 = 0,
308 unused2: u16 = 0,
309
310 pub const SectionNumber = enum(i16) {
311 UNDEFINED = 0,
312 ABSOLUTE = -1,
313 DEBUG = -2,
314 _,
315
316 fn toIndex(sn: SectionNumber) u15 {
317 return @intCast(@intFromEnum(sn) - 1);
318 }
319
320 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
321 return coff.section_table.items[sn.toIndex()];
322 }
323
324 pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
325 return &coff.sectionTableSlice()[sn.toIndex()];
326 }
327 };
328
329 pub const Index = enum(u32) {
330 null,
331 data,
332 idata,
333 rdata,
334 text,
335 _,
336
337 const known_count = @typeInfo(Index).@"enum".fields.len;
338
339 pub fn get(si: Symbol.Index, coff: *Coff) *Symbol {
340 return &coff.symbol_table.items[@intFromEnum(si)];
341 }
342
343 pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {
344 const ni = si.get(coff).ni;
345 assert(ni != .none);
346 return ni;
347 }
348
349 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
350 const sym = si.get(coff);
351 sym.rva = coff.computeNodeRva(sym.ni);
352 if (si == coff.entry_hack) {
353 @branchHint(.unlikely);
354 coff.targetStore(&coff.optionalHeaderStandardPtr().address_of_entry_point, sym.rva);
355 }
356 si.applyLocationRelocs(coff);
357 si.applyTargetRelocs(coff);
358 }
359
360 pub fn applyLocationRelocs(si: Symbol.Index, coff: *Coff) void {
361 for (coff.relocs.items[@intFromEnum(si.get(coff).loc_relocs)..]) |*reloc| {
362 if (reloc.loc != si) break;
363 reloc.apply(coff);
364 }
365 }
366
367 pub fn applyTargetRelocs(si: Symbol.Index, coff: *Coff) void {
368 var ri = si.get(coff).target_relocs;
369 while (ri != .none) {
370 const reloc = ri.get(coff);
371 assert(reloc.target == si);
372 reloc.apply(coff);
373 ri = reloc.next;
374 }
375 }
376
377 pub fn deleteLocationRelocs(si: Symbol.Index, coff: *Coff) void {
378 const sym = si.get(coff);
379 for (coff.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
380 if (reloc.loc != si) break;
381 reloc.delete(coff);
382 }
383 sym.loc_relocs = .none;
384 }
385 };
386
387 comptime {
388 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
389 }
390};
391
392pub const Reloc = extern struct {
393 type: Reloc.Type,
394 prev: Reloc.Index,
395 next: Reloc.Index,
396 loc: Symbol.Index,
397 target: Symbol.Index,
398 unused: u32,
399 offset: u64,
400 addend: i64,
401
402 pub const Type = extern union {
403 AMD64: std.coff.IMAGE.REL.AMD64,
404 ARM: std.coff.IMAGE.REL.ARM,
405 ARM64: std.coff.IMAGE.REL.ARM64,
406 SH: std.coff.IMAGE.REL.SH,
407 PPC: std.coff.IMAGE.REL.PPC,
408 I386: std.coff.IMAGE.REL.I386,
409 IA64: std.coff.IMAGE.REL.IA64,
410 MIPS: std.coff.IMAGE.REL.MIPS,
411 M32R: std.coff.IMAGE.REL.M32R,
412 };
413
414 pub const Index = enum(u32) {
415 none = std.math.maxInt(u32),
416 _,
417
418 pub fn get(si: Reloc.Index, coff: *Coff) *Reloc {
419 return &coff.relocs.items[@intFromEnum(si)];
420 }
421 };
422
423 pub fn apply(reloc: *const Reloc, coff: *Coff) void {
424 const loc_sym = reloc.loc.get(coff);
425 switch (loc_sym.ni) {
426 .none => return,
427 else => |ni| if (ni.hasMoved(&coff.mf)) return,
428 }
429 const target_sym = reloc.target.get(coff);
430 switch (target_sym.ni) {
431 .none => return,
432 else => |ni| if (ni.hasMoved(&coff.mf)) return,
433 }
434 const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];
435 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
436 const target_endian = coff.targetEndian();
437 switch (coff.targetLoad(&coff.headerPtr().machine)) {
438 else => |machine| @panic(@tagName(machine)),
439 .AMD64 => switch (reloc.type.AMD64) {
440 else => |kind| @panic(@tagName(kind)),
441 .ABSOLUTE => {},
442 .ADDR64 => std.mem.writeInt(
443 u64,
444 loc_slice[0..8],
445 coff.optionalHeaderField(.image_base) + target_rva,
446 target_endian,
447 ),
448 .ADDR32 => std.mem.writeInt(
449 u32,
450 loc_slice[0..4],
451 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
452 target_endian,
453 ),
454 .ADDR32NB => std.mem.writeInt(
455 u32,
456 loc_slice[0..4],
457 @intCast(target_rva),
458 target_endian,
459 ),
460 .REL32 => std.mem.writeInt(
461 i32,
462 loc_slice[0..4],
463 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
464 target_endian,
465 ),
466 .REL32_1 => std.mem.writeInt(
467 i32,
468 loc_slice[0..4],
469 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 5)))),
470 target_endian,
471 ),
472 .REL32_2 => std.mem.writeInt(
473 i32,
474 loc_slice[0..4],
475 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 6)))),
476 target_endian,
477 ),
478 .REL32_3 => std.mem.writeInt(
479 i32,
480 loc_slice[0..4],
481 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 7)))),
482 target_endian,
483 ),
484 .REL32_4 => std.mem.writeInt(
485 i32,
486 loc_slice[0..4],
487 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 8)))),
488 target_endian,
489 ),
490 .REL32_5 => std.mem.writeInt(
491 i32,
492 loc_slice[0..4],
493 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 9)))),
494 target_endian,
495 ),
496 },
497 .I386 => switch (reloc.type.I386) {
498 else => |kind| @panic(@tagName(kind)),
499 .ABSOLUTE => {},
500 .DIR16 => std.mem.writeInt(
501 u16,
502 loc_slice[0..2],
503 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
504 target_endian,
505 ),
506 .REL16 => std.mem.writeInt(
507 i16,
508 loc_slice[0..2],
509 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 2)))),
510 target_endian,
511 ),
512 .DIR32 => std.mem.writeInt(
513 u32,
514 loc_slice[0..4],
515 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
516 target_endian,
517 ),
518 .DIR32NB => std.mem.writeInt(
519 u32,
520 loc_slice[0..4],
521 @intCast(target_rva),
522 target_endian,
523 ),
524 .REL32 => std.mem.writeInt(
525 i32,
526 loc_slice[0..4],
527 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
528 target_endian,
529 ),
530 },
531 }
532 }
533
534 pub fn delete(reloc: *Reloc, coff: *Coff) void {
535 switch (reloc.prev) {
536 .none => {
537 const target = reloc.target.get(coff);
538 assert(target.target_relocs.get(coff) == reloc);
539 target.target_relocs = reloc.next;
540 },
541 else => |prev| prev.get(coff).next = reloc.next,
542 }
543 switch (reloc.next) {
544 .none => {},
545 else => |next| next.get(coff).prev = reloc.prev,
546 }
547 reloc.* = undefined;
548 }
549
550 comptime {
551 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
552 }
553};
554
555pub fn open(
556 arena: std.mem.Allocator,
557 comp: *Compilation,
558 path: std.Build.Cache.Path,
559 options: link.File.OpenOptions,
560) !*Coff {
561 return create(arena, comp, path, options);
562}
563pub fn createEmpty(
564 arena: std.mem.Allocator,
565 comp: *Compilation,
566 path: std.Build.Cache.Path,
567 options: link.File.OpenOptions,
568) !*Coff {
569 return create(arena, comp, path, options);
570}
571fn create(
572 arena: std.mem.Allocator,
573 comp: *Compilation,
574 path: std.Build.Cache.Path,
575 options: link.File.OpenOptions,
576) !*Coff {
577 const target = &comp.root_mod.resolved_target.result;
578 assert(target.ofmt == .coff);
579 if (target.cpu.arch.endian() != comptime targetEndian(undefined))
580 return error.UnsupportedCOFFArchitecture;
581 const is_image = switch (comp.config.output_mode) {
582 .Exe => true,
583 .Lib => switch (comp.config.link_mode) {
584 .static => false,
585 .dynamic => true,
586 },
587 .Obj => false,
588 };
589 const machine = target.toCoffMachine();
590 const timestamp: u32 = if (options.repro) 0 else @truncate(@as(u64, @bitCast(std.time.timestamp())));
591 const major_subsystem_version = options.major_subsystem_version orelse 6;
592 const minor_subsystem_version = options.minor_subsystem_version orelse 0;
593 const magic: std.coff.OptionalHeader.Magic = switch (target.ptrBitWidth()) {
594 0...32 => .PE32,
595 33...64 => .@"PE32+",
596 else => return error.UnsupportedCOFFArchitecture,
597 };
598 const section_align: std.mem.Alignment = switch (machine) {
599 .AMD64, .I386 => @enumFromInt(12),
600 .SH3, .SH3DSP, .SH4, .SH5 => @enumFromInt(12),
601 .MIPS16, .MIPSFPU, .MIPSFPU16, .WCEMIPSV2 => @enumFromInt(12),
602 .POWERPC, .POWERPCFP => @enumFromInt(12),
603 .ALPHA, .ALPHA64 => @enumFromInt(13),
604 .IA64 => @enumFromInt(13),
605 .ARM => @enumFromInt(12),
606 else => return error.UnsupportedCOFFArchitecture,
607 };
608
609 const coff = try arena.create(Coff);
610 const file = try path.root_dir.handle.createFile(path.sub_path, .{
611 .read = true,
612 .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
613 });
614 errdefer file.close();
615 coff.* = .{
616 .base = .{
617 .tag = .coff2,
618
619 .comp = comp,
620 .emit = path,
621
622 .file = file,
623 .gc_sections = false,
624 .print_gc_sections = false,
625 .build_id = .none,
626 .allow_shlib_undefined = false,
627 .stack_size = 0,
628 },
629 .mf = try .init(file, comp.gpa),
630 .nodes = .empty,
631 .import_table = .{
632 .ni = .none,
633 .entries = .empty,
634 },
635 .strings = .empty,
636 .string_bytes = .empty,
637 .section_table = .empty,
638 .symbol_table = .empty,
639 .globals = .empty,
640 .global_pending_index = 0,
641 .navs = .empty,
642 .uavs = .empty,
643 .lazy = .initFill(.{
644 .map = .empty,
645 .pending_index = 0,
646 }),
647 .pending_uavs = .empty,
648 .relocs = .empty,
649 .entry_hack = .null,
650 };
651 errdefer coff.deinit();
652
653 try coff.initHeaders(
654 is_image,
655 machine,
656 timestamp,
657 major_subsystem_version,
658 minor_subsystem_version,
659 magic,
660 section_align,
661 );
662 return coff;
663}
664
665pub fn deinit(coff: *Coff) void {
666 const gpa = coff.base.comp.gpa;
667 coff.mf.deinit(gpa);
668 coff.nodes.deinit(gpa);
669 coff.import_table.entries.deinit(gpa);
670 coff.strings.deinit(gpa);
671 coff.string_bytes.deinit(gpa);
672 coff.section_table.deinit(gpa);
673 coff.symbol_table.deinit(gpa);
674 coff.globals.deinit(gpa);
675 coff.navs.deinit(gpa);
676 coff.uavs.deinit(gpa);
677 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
678 coff.pending_uavs.deinit(gpa);
679 coff.relocs.deinit(gpa);
680 coff.* = undefined;
681}
682
683fn initHeaders(
684 coff: *Coff,
685 is_image: bool,
686 machine: std.coff.IMAGE.FILE.MACHINE,
687 timestamp: u32,
688 major_subsystem_version: u16,
689 minor_subsystem_version: u16,
690 magic: std.coff.OptionalHeader.Magic,
691 section_align: std.mem.Alignment,
692) !void {
693 const comp = coff.base.comp;
694 const gpa = comp.gpa;
695 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
696 const target_endian = coff.targetEndian();
697
698 const optional_header_size: u16 = if (is_image) switch (magic) {
699 _ => unreachable,
700 inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),
701 } else 0;
702 const data_directories_size: u16 = if (is_image)
703 @sizeOf(std.coff.ImageDataDirectory) * DataDirectory.len
704 else
705 0;
706
707 try coff.nodes.ensureTotalCapacity(gpa, Node.known_count);
708 coff.nodes.appendAssumeCapacity(.file);
709
710 const header_ni = Node.known.header;
711 assert(header_ni == try coff.mf.addOnlyChildNode(gpa, .root, .{
712 .alignment = coff.mf.flags.block_size,
713 .fixed = true,
714 }));
715 coff.nodes.appendAssumeCapacity(.header);
716
717 const signature_ni = Node.known.signature;
718 assert(signature_ni == try coff.mf.addOnlyChildNode(gpa, header_ni, .{
719 .size = (if (is_image) msdos_stub.len else 0) + "PE\x00\x00".len,
720 .alignment = .@"4",
721 .fixed = true,
722 }));
723 coff.nodes.appendAssumeCapacity(.signature);
724 {
725 const signature_slice = signature_ni.slice(&coff.mf);
726 if (is_image) @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
727 @memcpy(signature_slice[signature_slice.len - 4 ..], "PE\x00\x00");
728 }
729
730 const coff_header_ni = Node.known.coff_header;
731 assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
732 .size = @sizeOf(std.coff.Header),
733 .alignment = .@"4",
734 .fixed = true,
735 }));
736 coff.nodes.appendAssumeCapacity(.coff_header);
737 {
738 const coff_header = coff.headerPtr();
739 coff_header.* = .{
740 .machine = machine,
741 .number_of_sections = 0,
742 .time_date_stamp = timestamp,
743 .pointer_to_symbol_table = 0,
744 .number_of_symbols = 0,
745 .size_of_optional_header = optional_header_size + data_directories_size,
746 .flags = .{
747 .RELOCS_STRIPPED = is_image,
748 .EXECUTABLE_IMAGE = is_image,
749 .DEBUG_STRIPPED = true,
750 .@"32BIT_MACHINE" = magic == .PE32,
751 .LARGE_ADDRESS_AWARE = magic == .@"PE32+",
752 .DLL = comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic,
753 },
754 };
755 if (target_endian != native_endian) std.mem.byteSwapAllFields(std.coff.Header, coff_header);
756 }
757
758 const optional_header_ni = Node.known.optional_header;
759 assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
760 .size = optional_header_size,
761 .alignment = .@"4",
762 .fixed = true,
763 }));
764 coff.nodes.appendAssumeCapacity(.optional_header);
765 coff.targetStore(&coff.optionalHeaderStandardPtr().magic, magic);
766 if (is_image) switch (coff.optionalHeaderPtr()) {
767 .PE32 => |optional_header| {
768 optional_header.* = .{
769 .standard = .{
770 .magic = .PE32,
771 .major_linker_version = 0,
772 .minor_linker_version = 0,
773 .size_of_code = 0,
774 .size_of_initialized_data = 0,
775 .size_of_uninitialized_data = 0,
776 .address_of_entry_point = 0,
777 .base_of_code = 0,
778 },
779 .base_of_data = 0,
780 .image_base = switch (coff.base.comp.config.output_mode) {
781 .Exe => 0x400000,
782 .Lib => switch (coff.base.comp.config.link_mode) {
783 .static => 0,
784 .dynamic => 0x10000000,
785 },
786 .Obj => 0,
787 },
788 .section_alignment = @intCast(section_align.toByteUnits()),
789 .file_alignment = @intCast(file_align.toByteUnits()),
790 .major_operating_system_version = 6,
791 .minor_operating_system_version = 0,
792 .major_image_version = 0,
793 .minor_image_version = 0,
794 .major_subsystem_version = major_subsystem_version,
795 .minor_subsystem_version = minor_subsystem_version,
796 .win32_version_value = 0,
797 .size_of_image = 0,
798 .size_of_headers = 0,
799 .checksum = 0,
800 .subsystem = .WINDOWS_CUI,
801 .dll_flags = .{
802 .HIGH_ENTROPY_VA = true,
803 .DYNAMIC_BASE = true,
804 .TERMINAL_SERVER_AWARE = true,
805 .NX_COMPAT = true,
806 },
807 .size_of_stack_reserve = default_size_of_stack_reserve,
808 .size_of_stack_commit = default_size_of_stack_commit,
809 .size_of_heap_reserve = default_size_of_heap_reserve,
810 .size_of_heap_commit = default_size_of_heap_commit,
811 .loader_flags = 0,
812 .number_of_rva_and_sizes = DataDirectory.len,
813 };
814 if (target_endian != native_endian)
815 std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header);
816 },
817 .@"PE32+" => |optional_header| {
818 optional_header.* = .{
819 .standard = .{
820 .magic = .@"PE32+",
821 .major_linker_version = 0,
822 .minor_linker_version = 0,
823 .size_of_code = 0,
824 .size_of_initialized_data = 0,
825 .size_of_uninitialized_data = 0,
826 .address_of_entry_point = 0,
827 .base_of_code = 0,
828 },
829 .image_base = switch (coff.base.comp.config.output_mode) {
830 .Exe => 0x140000000,
831 .Lib => switch (coff.base.comp.config.link_mode) {
832 .static => 0,
833 .dynamic => 0x180000000,
834 },
835 .Obj => 0,
836 },
837 .section_alignment = @intCast(section_align.toByteUnits()),
838 .file_alignment = @intCast(file_align.toByteUnits()),
839 .major_operating_system_version = 6,
840 .minor_operating_system_version = 0,
841 .major_image_version = 0,
842 .minor_image_version = 0,
843 .major_subsystem_version = major_subsystem_version,
844 .minor_subsystem_version = minor_subsystem_version,
845 .win32_version_value = 0,
846 .size_of_image = 0,
847 .size_of_headers = 0,
848 .checksum = 0,
849 .subsystem = .WINDOWS_CUI,
850 .dll_flags = .{
851 .HIGH_ENTROPY_VA = true,
852 .DYNAMIC_BASE = true,
853 .TERMINAL_SERVER_AWARE = true,
854 .NX_COMPAT = true,
855 },
856 .size_of_stack_reserve = default_size_of_stack_reserve,
857 .size_of_stack_commit = default_size_of_stack_commit,
858 .size_of_heap_reserve = default_size_of_heap_reserve,
859 .size_of_heap_commit = default_size_of_heap_commit,
860 .loader_flags = 0,
861 .number_of_rva_and_sizes = DataDirectory.len,
862 };
863 if (target_endian != native_endian)
864 std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", optional_header);
865 },
866 };
867
868 const data_directories_ni = Node.known.data_directories;
869 assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
870 .size = data_directories_size,
871 .alignment = .@"4",
872 .fixed = true,
873 }));
874 coff.nodes.appendAssumeCapacity(.data_directories);
875 {
876 const data_directories = coff.dataDirectorySlice();
877 @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
878 if (target_endian != native_endian)
879 std.mem.byteSwapAllFields([DataDirectory.len]std.coff.ImageDataDirectory, data_directories);
880 }
881
882 const section_table_ni = Node.known.section_table;
883 assert(section_table_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
884 .alignment = .@"4",
885 .fixed = true,
886 }));
887 coff.nodes.appendAssumeCapacity(.section_table);
888
889 assert(coff.nodes.len == Node.known_count);
890
891 try coff.symbol_table.ensureTotalCapacity(gpa, Symbol.Index.known_count);
892 coff.symbol_table.addOneAssumeCapacity().* = .{
893 .ni = .none,
894 .rva = 0,
895 .size = 0,
896 .loc_relocs = .none,
897 .target_relocs = .none,
898 .section_number = .UNDEFINED,
899 };
900 assert(try coff.addSection(".data", .{
901 .CNT_INITIALIZED_DATA = true,
902 .MEM_READ = true,
903 .MEM_WRITE = true,
904 }) == .data);
905 assert(try coff.addSection(".idata", .{
906 .CNT_INITIALIZED_DATA = true,
907 .MEM_READ = true,
908 }) == .idata);
909 assert(try coff.addSection(".rdata", .{
910 .CNT_INITIALIZED_DATA = true,
911 .MEM_READ = true,
912 }) == .rdata);
913 assert(try coff.addSection(".text", .{
914 .CNT_CODE = true,
915 .MEM_EXECUTE = true,
916 .MEM_READ = true,
917 }) == .text);
918 coff.import_table.ni = try coff.mf.addLastChildNode(
919 gpa,
920 Symbol.Index.idata.node(coff),
921 .{ .alignment = .@"4" },
922 );
923 coff.nodes.appendAssumeCapacity(.import_directory_table);
924 assert(coff.symbol_table.items.len == Symbol.Index.known_count);
925}
926
927fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
928 return coff.nodes.get(@intFromEnum(ni));
929}
930fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
931 const parent_rva = parent_rva: {
932 const parent_si = switch (coff.getNode(ni.parent(&coff.mf))) {
933 .file,
934 .header,
935 .signature,
936 .coff_header,
937 .optional_header,
938 .data_directories,
939 .section_table,
940 => unreachable,
941 .section => |si| si,
942 .import_directory_table => unreachable,
943 .import_lookup_table => |import_index| break :parent_rva coff.targetLoad(
944 &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
945 ),
946 .import_address_table => |import_index| break :parent_rva coff.targetLoad(
947 &coff.importDirectoryEntryPtr(import_index).import_address_table_rva,
948 ),
949 .import_hint_name_table => |import_index| break :parent_rva coff.targetLoad(
950 &coff.importDirectoryEntryPtr(import_index).name_rva,
951 ),
952 inline .global, .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(coff),
953 };
954 break :parent_rva parent_si.get(coff).rva;
955 };
956 const offset, _ = ni.location(&coff.mf).resolve(&coff.mf);
957 return @intCast(parent_rva + offset);
958}
959
960pub inline fn targetEndian(_: *const Coff) std.builtin.Endian {
961 return .little;
962}
963fn targetLoad(coff: *const Coff, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
964 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
965 return switch (@typeInfo(Child)) {
966 else => @compileError(@typeName(Child)),
967 .int => std.mem.toNative(Child, ptr.*, coff.targetEndian()),
968 .@"enum" => |@"enum"| @enumFromInt(coff.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr)))),
969 .@"struct" => |@"struct"| @bitCast(
970 coff.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))),
971 ),
972 };
973}
974fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
975 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
976 return switch (@typeInfo(Child)) {
977 else => @compileError(@typeName(Child)),
978 .int => ptr.* = std.mem.nativeTo(Child, val, coff.targetEndian()),
979 .@"enum" => |@"enum"| coff.targetStore(
980 @as(*@"enum".tag_type, @ptrCast(ptr)),
981 @intFromEnum(val),
982 ),
983 .@"struct" => |@"struct"| coff.targetStore(
984 @as(*@"struct".backing_integer.?, @ptrCast(ptr)),
985 @bitCast(val),
986 ),
987 };
988}
989
990pub fn headerPtr(coff: *Coff) *std.coff.Header {
991 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
992}
993
994pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {
995 return @ptrCast(@alignCast(
996 Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],
997 ));
998}
999
1000pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {
1001 PE32: *std.coff.OptionalHeader.PE32,
1002 @"PE32+": *std.coff.OptionalHeader.@"PE32+",
1003};
1004pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {
1005 const slice = Node.known.optional_header.slice(&coff.mf);
1006 return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
1007 _ => unreachable,
1008 inline else => |magic| @unionInit(
1009 OptionalHeaderPtr,
1010 @tagName(magic),
1011 @ptrCast(@alignCast(slice)),
1012 ),
1013 };
1014}
1015pub fn optionalHeaderField(
1016 coff: *Coff,
1017 comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),
1018) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {
1019 return switch (coff.optionalHeaderPtr()) {
1020 inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),
1021 };
1022}
1023
1024pub fn dataDirectorySlice(coff: *Coff) *[DataDirectory.len]std.coff.ImageDataDirectory {
1025 return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
1026}
1027pub fn dataDirectoryPtr(coff: *Coff, data_directory: DataDirectory) *std.coff.ImageDataDirectory {
1028 return &coff.dataDirectorySlice()[@intFromEnum(data_directory)];
1029}
1030
1031pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
1032 return @ptrCast(@alignCast(Node.known.section_table.slice(&coff.mf)));
1033}
1034
1035pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry {
1036 return @ptrCast(@alignCast(coff.import_table.ni.slice(&coff.mf)));
1037}
1038pub fn importDirectoryEntryPtr(
1039 coff: *Coff,
1040 import_index: ImportTable.Index,
1041) *std.coff.ImportDirectoryEntry {
1042 return &coff.importDirectoryTableSlice()[@intFromEnum(import_index)];
1043}
1044
1045fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
1046 defer coff.symbol_table.addOneAssumeCapacity().* = .{
1047 .ni = .none,
1048 .rva = 0,
1049 .size = 0,
1050 .loc_relocs = .none,
1051 .target_relocs = .none,
1052 .section_number = .UNDEFINED,
1053 };
1054 return @enumFromInt(coff.symbol_table.items.len);
1055}
1056
1057fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
1058 const si = coff.addSymbolAssumeCapacity();
1059 return si;
1060}
1061
1062fn getOrPutString(coff: *Coff, string: []const u8) !String {
1063 const gpa = coff.base.comp.gpa;
1064 try coff.string_bytes.ensureUnusedCapacity(gpa, string.len + 1);
1065 const gop = try coff.strings.getOrPutContextAdapted(
1066 gpa,
1067 string,
1068 std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
1069 .{ .bytes = &coff.string_bytes },
1070 );
1071 if (!gop.found_existing) {
1072 gop.key_ptr.* = @intCast(coff.string_bytes.items.len);
1073 gop.value_ptr.* = {};
1074 coff.string_bytes.appendSliceAssumeCapacity(string);
1075 coff.string_bytes.appendAssumeCapacity(0);
1076 }
1077 return @enumFromInt(gop.key_ptr.*);
1078}
1079
1080fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
1081 return (try coff.getOrPutString(string orelse return .none)).toOptional();
1082}
1083
1084pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
1085 const gpa = coff.base.comp.gpa;
1086 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1087 const sym_gop = try coff.globals.getOrPut(gpa, .{
1088 .name = try coff.getOrPutString(name),
1089 .lib_name = try coff.getOrPutOptionalString(lib_name),
1090 });
1091 if (!sym_gop.found_existing) {
1092 sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
1093 coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
1094 }
1095 return sym_gop.value_ptr.*;
1096}
1097
1098fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
1099 const gpa = zcu.gpa;
1100 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1101 const sym_gop = try coff.navs.getOrPut(gpa, nav_index);
1102 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
1103 return @enumFromInt(sym_gop.index);
1104}
1105pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
1106 const ip = &zcu.intern_pool;
1107 const nav = ip.getNav(nav_index);
1108 if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(
1109 @"extern".name.toSlice(ip),
1110 @"extern".lib_name.toSlice(ip),
1111 );
1112 const nmi = try coff.navMapIndex(zcu, nav_index);
1113 return nmi.symbol(coff);
1114}
1115
1116fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
1117 const gpa = coff.base.comp.gpa;
1118 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1119 const sym_gop = try coff.uavs.getOrPut(gpa, uav_val);
1120 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
1121 return @enumFromInt(sym_gop.index);
1122}
1123pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index {
1124 const umi = try coff.uavMapIndex(uav_val);
1125 return umi.symbol(coff);
1126}
1127
1128pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
1129 const gpa = coff.base.comp.gpa;
1130 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1131 const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
1132 if (!sym_gop.found_existing) {
1133 sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity();
1134 coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
1135 }
1136 return sym_gop.value_ptr.*;
1137}
1138
1139pub fn getNavVAddr(
1140 coff: *Coff,
1141 pt: Zcu.PerThread,
1142 nav: InternPool.Nav.Index,
1143 reloc_info: link.File.RelocInfo,
1144) !u64 {
1145 return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));
1146}
1147
1148pub fn getUavVAddr(
1149 coff: *Coff,
1150 uav: InternPool.Index,
1151 reloc_info: link.File.RelocInfo,
1152) !u64 {
1153 return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));
1154}
1155
1156pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
1157 try coff.addReloc(
1158 @enumFromInt(reloc_info.parent.atom_index),
1159 reloc_info.offset,
1160 target_si,
1161 reloc_info.addend,
1162 switch (coff.targetLoad(&coff.headerPtr().machine)) {
1163 else => unreachable,
1164 .AMD64 => .{ .AMD64 = .ADDR64 },
1165 .I386 => .{ .I386 = .DIR32 },
1166 },
1167 );
1168 return coff.optionalHeaderField(.image_base) + target_si.get(coff).rva;
1169}
1170
1171fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
1172 const gpa = coff.base.comp.gpa;
1173 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1174 try coff.section_table.ensureUnusedCapacity(gpa, 1);
1175 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1176
1177 const coff_header = coff.headerPtr();
1178 const section_index = coff.targetLoad(&coff_header.number_of_sections);
1179 const section_table_len = section_index + 1;
1180 coff.targetStore(&coff_header.number_of_sections, section_table_len);
1181 try Node.known.section_table.resize(
1182 &coff.mf,
1183 gpa,
1184 @sizeOf(std.coff.SectionHeader) * section_table_len,
1185 );
1186 const ni = try coff.mf.addLastChildNode(gpa, .root, .{
1187 .alignment = coff.mf.flags.block_size,
1188 .moved = true,
1189 .bubbles_moved = false,
1190 });
1191 const si = coff.addSymbolAssumeCapacity();
1192 coff.section_table.appendAssumeCapacity(si);
1193 coff.nodes.appendAssumeCapacity(.{ .section = si });
1194 const section_table = coff.sectionTableSlice();
1195 const virtual_size = coff.optionalHeaderField(.section_alignment);
1196 const rva: u32 = switch (section_index) {
1197 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
1198 else => coff.section_table.items[section_index - 1].get(coff).rva +
1199 coff.targetLoad(&section_table[section_index - 1].virtual_size),
1200 };
1201 {
1202 const sym = si.get(coff);
1203 sym.ni = ni;
1204 sym.rva = rva;
1205 sym.section_number = @enumFromInt(section_table_len);
1206 }
1207 const section = &section_table[section_index];
1208 section.* = .{
1209 .name = undefined,
1210 .virtual_size = virtual_size,
1211 .virtual_address = rva,
1212 .size_of_raw_data = 0,
1213 .pointer_to_raw_data = 0,
1214 .pointer_to_relocations = 0,
1215 .pointer_to_linenumbers = 0,
1216 .number_of_relocations = 0,
1217 .number_of_linenumbers = 0,
1218 .flags = flags,
1219 };
1220 @memcpy(section.name[0..name.len], name);
1221 @memset(section.name[name.len..], 0);
1222 if (coff.targetEndian() != native_endian)
1223 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
1224 switch (coff.optionalHeaderPtr()) {
1225 inline else => |optional_header| coff.targetStore(
1226 &optional_header.size_of_image,
1227 @intCast(rva + virtual_size),
1228 ),
1229 }
1230 return si;
1231}
1232
1233pub fn addReloc(
1234 coff: *Coff,
1235 loc_si: Symbol.Index,
1236 offset: u64,
1237 target_si: Symbol.Index,
1238 addend: i64,
1239 @"type": Reloc.Type,
1240) !void {
1241 const gpa = coff.base.comp.gpa;
1242 const target = target_si.get(coff);
1243 const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
1244 (try coff.relocs.addOne(gpa)).* = .{
1245 .type = @"type",
1246 .prev = .none,
1247 .next = target.target_relocs,
1248 .loc = loc_si,
1249 .target = target_si,
1250 .unused = 0,
1251 .offset = offset,
1252 .addend = addend,
1253 };
1254 switch (target.target_relocs) {
1255 .none => {},
1256 else => |target_ri| target_ri.get(coff).prev = ri,
1257 }
1258 target.target_relocs = ri;
1259}
1260
1261pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void {
1262 _ = coff;
1263 _ = prog_node;
1264}
1265
1266pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
1267 coff.updateNavInner(pt, nav_index) catch |err| switch (err) {
1268 error.OutOfMemory,
1269 error.Overflow,
1270 error.RelocationNotByteAligned,
1271 => |e| return e,
1272 else => |e| return coff.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}),
1273 };
1274}
1275fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
1276 const zcu = pt.zcu;
1277 const gpa = zcu.gpa;
1278 const ip = &zcu.intern_pool;
1279
1280 const nav = ip.getNav(nav_index);
1281 const nav_val = nav.status.fully_resolved.val;
1282 const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) {
1283 else => .{ nav_val, false },
1284 .variable => |variable| .{ variable.init, variable.is_threadlocal },
1285 .@"extern" => return,
1286 .func => .{ .none, false },
1287 };
1288 if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
1289
1290 const nmi = try coff.navMapIndex(zcu, nav_index);
1291 const si = nmi.symbol(coff);
1292 const ni = ni: {
1293 const sym = si.get(coff);
1294 switch (sym.ni) {
1295 .none => {
1296 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1297 _ = is_threadlocal;
1298 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
1299 .alignment = pt.navAlignment(nav_index).toStdMem(),
1300 .moved = true,
1301 });
1302 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
1303 sym.ni = ni;
1304 sym.section_number = Symbol.Index.data.get(coff).section_number;
1305 },
1306 else => si.deleteLocationRelocs(coff),
1307 }
1308 assert(sym.loc_relocs == .none);
1309 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1310 break :ni sym.ni;
1311 };
1312
1313 var nw: MappedFile.Node.Writer = undefined;
1314 ni.writer(&coff.mf, gpa, &nw);
1315 defer nw.deinit();
1316 codegen.generateSymbol(
1317 &coff.base,
1318 pt,
1319 zcu.navSrcLoc(nav_index),
1320 .fromInterned(nav_init),
1321 &nw.interface,
1322 .{ .atom_index = @intFromEnum(si) },
1323 ) catch |err| switch (err) {
1324 error.WriteFailed => return error.OutOfMemory,
1325 else => |e| return e,
1326 };
1327 si.get(coff).size = @intCast(nw.interface.end);
1328 si.applyLocationRelocs(coff);
1329}
1330
1331pub fn lowerUav(
1332 coff: *Coff,
1333 pt: Zcu.PerThread,
1334 uav_val: InternPool.Index,
1335 uav_align: InternPool.Alignment,
1336 src_loc: Zcu.LazySrcLoc,
1337) !codegen.SymbolResult {
1338 const zcu = pt.zcu;
1339 const gpa = zcu.gpa;
1340
1341 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
1342 const umi = try coff.uavMapIndex(uav_val);
1343 const si = umi.symbol(coff);
1344 if (switch (si.get(coff).ni) {
1345 .none => true,
1346 else => |ni| uav_align.toStdMem().order(ni.alignment(&coff.mf)).compare(.gt),
1347 }) {
1348 const gop = coff.pending_uavs.getOrPutAssumeCapacity(umi);
1349 if (gop.found_existing) {
1350 gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
1351 } else {
1352 gop.value_ptr.* = .{
1353 .alignment = uav_align,
1354 .src_loc = src_loc,
1355 };
1356 coff.base.comp.link_const_prog_node.increaseEstimatedTotalItems(1);
1357 }
1358 }
1359 return .{ .sym_index = @intFromEnum(si) };
1360}
1361
1362pub fn updateFunc(
1363 coff: *Coff,
1364 pt: Zcu.PerThread,
1365 func_index: InternPool.Index,
1366 mir: *const codegen.AnyMir,
1367) !void {
1368 coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
1369 error.OutOfMemory,
1370 error.Overflow,
1371 error.RelocationNotByteAligned,
1372 error.CodegenFail,
1373 => |e| return e,
1374 else => |e| return coff.base.cgFail(
1375 pt.zcu.funcInfo(func_index).owner_nav,
1376 "linker failed to update function: {s}",
1377 .{@errorName(e)},
1378 ),
1379 };
1380}
1381fn updateFuncInner(
1382 coff: *Coff,
1383 pt: Zcu.PerThread,
1384 func_index: InternPool.Index,
1385 mir: *const codegen.AnyMir,
1386) !void {
1387 const zcu = pt.zcu;
1388 const gpa = zcu.gpa;
1389 const ip = &zcu.intern_pool;
1390 const func = zcu.funcInfo(func_index);
1391 const nav = ip.getNav(func.owner_nav);
1392
1393 const nmi = try coff.navMapIndex(zcu, func.owner_nav);
1394 const si = nmi.symbol(coff);
1395 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
1396 const ni = ni: {
1397 const sym = si.get(coff);
1398 switch (sym.ni) {
1399 .none => {
1400 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1401 const mod = zcu.navFileScope(func.owner_nav).mod.?;
1402 const target = &mod.resolved_target.result;
1403 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
1404 .alignment = switch (nav.status.fully_resolved.alignment) {
1405 .none => switch (mod.optimize_mode) {
1406 .Debug,
1407 .ReleaseSafe,
1408 .ReleaseFast,
1409 => target_util.defaultFunctionAlignment(target),
1410 .ReleaseSmall => target_util.minFunctionAlignment(target),
1411 },
1412 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
1413 }.toStdMem(),
1414 .moved = true,
1415 });
1416 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
1417 sym.ni = ni;
1418 sym.section_number = Symbol.Index.text.get(coff).section_number;
1419 },
1420 else => si.deleteLocationRelocs(coff),
1421 }
1422 assert(sym.loc_relocs == .none);
1423 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1424 break :ni sym.ni;
1425 };
1426
1427 var nw: MappedFile.Node.Writer = undefined;
1428 ni.writer(&coff.mf, gpa, &nw);
1429 defer nw.deinit();
1430 codegen.emitFunction(
1431 &coff.base,
1432 pt,
1433 zcu.navSrcLoc(func.owner_nav),
1434 func_index,
1435 @intFromEnum(si),
1436 mir,
1437 &nw.interface,
1438 .none,
1439 ) catch |err| switch (err) {
1440 error.WriteFailed => return nw.err.?,
1441 else => |e| return e,
1442 };
1443 si.get(coff).size = @intCast(nw.interface.end);
1444 si.applyLocationRelocs(coff);
1445}
1446
1447pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void {
1448 coff.flushLazy(pt, .{
1449 .kind = .const_data,
1450 .index = @intCast(coff.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return),
1451 }) catch |err| switch (err) {
1452 error.OutOfMemory => return error.OutOfMemory,
1453 error.CodegenFail => return error.LinkFailure,
1454 else => |e| return coff.base.comp.link_diags.fail("updateErrorData failed {t}", .{e}),
1455 };
1456}
1457
1458pub fn flush(
1459 coff: *Coff,
1460 arena: std.mem.Allocator,
1461 tid: Zcu.PerThread.Id,
1462 prog_node: std.Progress.Node,
1463) !void {
1464 _ = arena;
1465 _ = prog_node;
1466 while (try coff.idle(tid)) {}
1467
1468 // hack for stage2_x86_64 + coff
1469 const comp = coff.base.comp;
1470 if (comp.compiler_rt_dyn_lib) |crt_file| {
1471 const gpa = comp.gpa;
1472 const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{
1473 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
1474 std.fs.path.basename(crt_file.full_object_path.sub_path),
1475 });
1476 defer gpa.free(compiler_rt_sub_path);
1477 crt_file.full_object_path.root_dir.handle.copyFile(
1478 crt_file.full_object_path.sub_path,
1479 coff.base.emit.root_dir.handle,
1480 compiler_rt_sub_path,
1481 .{},
1482 ) catch |err| switch (err) {
1483 else => |e| return comp.link_diags.fail("Copy '{s}' failed: {s}", .{
1484 compiler_rt_sub_path,
1485 @errorName(e),
1486 }),
1487 };
1488 }
1489}
1490
1491pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
1492 const comp = coff.base.comp;
1493 task: {
1494 while (coff.pending_uavs.pop()) |pending_uav| {
1495 const sub_prog_node = coff.idleProgNode(
1496 tid,
1497 comp.link_const_prog_node,
1498 .{ .uav = pending_uav.key },
1499 );
1500 defer sub_prog_node.end();
1501 coff.flushUav(
1502 .{ .zcu = coff.base.comp.zcu.?, .tid = tid },
1503 pending_uav.key,
1504 pending_uav.value.alignment,
1505 pending_uav.value.src_loc,
1506 ) catch |err| switch (err) {
1507 error.OutOfMemory => return error.OutOfMemory,
1508 else => |e| return coff.base.comp.link_diags.fail(
1509 "linker failed to lower constant: {t}",
1510 .{e},
1511 ),
1512 };
1513 break :task;
1514 }
1515 if (coff.global_pending_index < coff.globals.count()) {
1516 const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
1517 const gmi: Node.GlobalMapIndex = @enumFromInt(coff.global_pending_index);
1518 coff.global_pending_index += 1;
1519 const sub_prog_node = comp.link_synth_prog_node.start(
1520 gmi.globalName(coff).name.toSlice(coff),
1521 0,
1522 );
1523 defer sub_prog_node.end();
1524 coff.flushGlobal(pt, gmi) catch |err| switch (err) {
1525 error.OutOfMemory => return error.OutOfMemory,
1526 else => |e| return coff.base.comp.link_diags.fail(
1527 "linker failed to lower constant: {t}",
1528 .{e},
1529 ),
1530 };
1531 break :task;
1532 }
1533 var lazy_it = coff.lazy.iterator();
1534 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
1535 const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
1536 const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index };
1537 lazy.value.pending_index += 1;
1538 const kind = switch (lmr.kind) {
1539 .code => "code",
1540 .const_data => "data",
1541 };
1542 var name: [std.Progress.Node.max_name_len]u8 = undefined;
1543 const sub_prog_node = comp.link_synth_prog_node.start(
1544 std.fmt.bufPrint(&name, "lazy {s} for {f}", .{
1545 kind,
1546 Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt),
1547 }) catch &name,
1548 0,
1549 );
1550 defer sub_prog_node.end();
1551 coff.flushLazy(pt, lmr) catch |err| switch (err) {
1552 error.OutOfMemory => return error.OutOfMemory,
1553 else => |e| return coff.base.comp.link_diags.fail(
1554 "linker failed to lower lazy {s}: {t}",
1555 .{ kind, e },
1556 ),
1557 };
1558 break :task;
1559 };
1560 while (coff.mf.updates.pop()) |ni| {
1561 const clean_moved = ni.cleanMoved(&coff.mf);
1562 const clean_resized = ni.cleanResized(&coff.mf);
1563 if (clean_moved or clean_resized) {
1564 const sub_prog_node = coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni));
1565 defer sub_prog_node.end();
1566 if (clean_moved) try coff.flushMoved(ni);
1567 if (clean_resized) try coff.flushResized(ni);
1568 break :task;
1569 } else coff.mf.update_prog_node.completeOne();
1570 }
1571 }
1572 if (coff.pending_uavs.count() > 0) return true;
1573 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
1574 if (coff.mf.updates.items.len > 0) return true;
1575 return false;
1576}
1577
1578fn idleProgNode(
1579 coff: *Coff,
1580 tid: Zcu.PerThread.Id,
1581 prog_node: std.Progress.Node,
1582 node: Node,
1583) std.Progress.Node {
1584 var name: [std.Progress.Node.max_name_len]u8 = undefined;
1585 return prog_node.start(name: switch (node) {
1586 else => |tag| @tagName(tag),
1587 .section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
1588 .nav => |nmi| {
1589 const ip = &coff.base.comp.zcu.?.intern_pool;
1590 break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
1591 },
1592 .uav => |umi| std.fmt.bufPrint(&name, "{f}", .{
1593 Value.fromInterned(umi.uavValue(coff)).fmtValue(.{
1594 .zcu = coff.base.comp.zcu.?,
1595 .tid = tid,
1596 }),
1597 }) catch &name,
1598 }, 0);
1599}
1600
1601fn flushUav(
1602 coff: *Coff,
1603 pt: Zcu.PerThread,
1604 umi: Node.UavMapIndex,
1605 uav_align: InternPool.Alignment,
1606 src_loc: Zcu.LazySrcLoc,
1607) !void {
1608 const zcu = pt.zcu;
1609 const gpa = zcu.gpa;
1610
1611 const uav_val = umi.uavValue(coff);
1612 const si = umi.symbol(coff);
1613 const ni = ni: {
1614 const sym = si.get(coff);
1615 switch (sym.ni) {
1616 .none => {
1617 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1618 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
1619 .alignment = uav_align.toStdMem(),
1620 .moved = true,
1621 });
1622 coff.nodes.appendAssumeCapacity(.{ .uav = umi });
1623 sym.ni = ni;
1624 sym.section_number = Symbol.Index.data.get(coff).section_number;
1625 },
1626 else => {
1627 if (sym.ni.alignment(&coff.mf).order(uav_align.toStdMem()).compare(.gte)) return;
1628 si.deleteLocationRelocs(coff);
1629 },
1630 }
1631 assert(sym.loc_relocs == .none);
1632 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1633 break :ni sym.ni;
1634 };
1635
1636 var nw: MappedFile.Node.Writer = undefined;
1637 ni.writer(&coff.mf, gpa, &nw);
1638 defer nw.deinit();
1639 codegen.generateSymbol(
1640 &coff.base,
1641 pt,
1642 src_loc,
1643 .fromInterned(uav_val),
1644 &nw.interface,
1645 .{ .atom_index = @intFromEnum(si) },
1646 ) catch |err| switch (err) {
1647 error.WriteFailed => return error.OutOfMemory,
1648 else => |e| return e,
1649 };
1650 si.get(coff).size = @intCast(nw.interface.end);
1651 si.applyLocationRelocs(coff);
1652}
1653
1654fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
1655 const zcu = pt.zcu;
1656 const comp = zcu.comp;
1657 const gpa = zcu.gpa;
1658 const gn = gmi.globalName(coff);
1659 if (gn.lib_name.toSlice(coff)) |lib_name| {
1660 const name = gn.name.toSlice(coff);
1661 try coff.nodes.ensureUnusedCapacity(gpa, 4);
1662 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1663
1664 const target_endian = coff.targetEndian();
1665 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
1666 const addr_size: u64, const addr_align: std.mem.Alignment = switch (magic) {
1667 _ => unreachable,
1668 .PE32 => .{ 4, .@"4" },
1669 .@"PE32+" => .{ 8, .@"8" },
1670 };
1671
1672 const gop = try coff.import_table.entries.getOrPutAdapted(
1673 gpa,
1674 lib_name,
1675 ImportTable.Adapter{ .coff = coff },
1676 );
1677 const import_hint_name_align: std.mem.Alignment = .@"2";
1678 if (!gop.found_existing) {
1679 errdefer _ = coff.import_table.entries.pop();
1680 try coff.import_table.ni.resize(
1681 &coff.mf,
1682 gpa,
1683 @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
1684 );
1685 const import_hint_name_table_len =
1686 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
1687 const idata_section_ni = Symbol.Index.idata.node(coff);
1688 const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
1689 .size = addr_size * 2,
1690 .alignment = addr_align,
1691 .moved = true,
1692 });
1693 const import_address_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
1694 .size = addr_size * 2,
1695 .alignment = addr_align,
1696 .moved = true,
1697 });
1698 const import_address_table_si = coff.addSymbolAssumeCapacity();
1699 {
1700 const import_address_table_sym = import_address_table_si.get(coff);
1701 import_address_table_sym.ni = import_address_table_ni;
1702 assert(import_address_table_sym.loc_relocs == .none);
1703 import_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1704 import_address_table_sym.section_number = Symbol.Index.idata.get(coff).section_number;
1705 }
1706 const import_hint_name_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
1707 .size = import_hint_name_table_len,
1708 .alignment = import_hint_name_align,
1709 .moved = true,
1710 });
1711 gop.value_ptr.* = .{
1712 .import_lookup_table_ni = import_lookup_table_ni,
1713 .import_address_table_si = import_address_table_si,
1714 .import_hint_name_table_ni = import_hint_name_table_ni,
1715 .len = 0,
1716 .hint_name_len = @intCast(import_hint_name_table_len),
1717 };
1718 const import_hint_name_slice = import_hint_name_table_ni.slice(&coff.mf);
1719 @memcpy(import_hint_name_slice[0..lib_name.len], lib_name);
1720 @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll");
1721 @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0);
1722 coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @enumFromInt(gop.index) });
1723 coff.nodes.appendAssumeCapacity(.{ .import_address_table = @enumFromInt(gop.index) });
1724 coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @enumFromInt(gop.index) });
1725
1726 const import_directory_entries = coff.importDirectoryTableSlice()[gop.index..][0..2];
1727 import_directory_entries.* = .{ .{
1728 .import_lookup_table_rva = coff.computeNodeRva(import_lookup_table_ni),
1729 .time_date_stamp = 0,
1730 .forwarder_chain = 0,
1731 .name_rva = coff.computeNodeRva(import_hint_name_table_ni),
1732 .import_address_table_rva = coff.computeNodeRva(import_address_table_ni),
1733 }, .{
1734 .import_lookup_table_rva = 0,
1735 .time_date_stamp = 0,
1736 .forwarder_chain = 0,
1737 .name_rva = 0,
1738 .import_address_table_rva = 0,
1739 } };
1740 if (target_endian != native_endian)
1741 std.mem.byteSwapAllFields([2]std.coff.ImportDirectoryEntry, import_directory_entries);
1742 }
1743 const import_symbol_index = gop.value_ptr.len;
1744 gop.value_ptr.len = import_symbol_index + 1;
1745 const new_symbol_table_size = addr_size * (import_symbol_index + 2);
1746 const import_hint_name_index = gop.value_ptr.hint_name_len;
1747 gop.value_ptr.hint_name_len = @intCast(
1748 import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
1749 );
1750 try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
1751 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
1752 try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
1753 try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);
1754 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
1755 const import_address_slice = import_address_table_ni.slice(&coff.mf);
1756 const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
1757 @memset(import_hint_name_slice[import_hint_name_index..][0..2], 0);
1758 @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..name.len], name);
1759 @memset(import_hint_name_slice[import_hint_name_index + 2 + name.len ..], 0);
1760 const import_hint_name_rva =
1761 coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
1762 switch (magic) {
1763 _ => unreachable,
1764 inline .PE32, .@"PE32+" => |ct_magic| {
1765 const Addr = switch (ct_magic) {
1766 _ => comptime unreachable,
1767 .PE32 => u32,
1768 .@"PE32+" => u64,
1769 };
1770 const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
1771 const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
1772 const import_hint_name_rvas: [2]Addr = .{
1773 std.mem.nativeTo(Addr, @intCast(import_hint_name_rva), target_endian),
1774 std.mem.nativeTo(Addr, 0, target_endian),
1775 };
1776 import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
1777 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
1778 },
1779 }
1780 const si = gmi.symbol(coff);
1781 const sym = si.get(coff);
1782 sym.section_number = Symbol.Index.text.get(coff).section_number;
1783 assert(sym.loc_relocs == .none);
1784 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1785 switch (coff.targetLoad(&coff.headerPtr().machine)) {
1786 else => |tag| @panic(@tagName(tag)),
1787 .AMD64 => {
1788 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
1789 const target = &comp.root_mod.resolved_target.result;
1790 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
1791 .alignment = switch (comp.root_mod.optimize_mode) {
1792 .Debug,
1793 .ReleaseSafe,
1794 .ReleaseFast,
1795 => target_util.defaultFunctionAlignment(target),
1796 .ReleaseSmall => target_util.minFunctionAlignment(target),
1797 }.toStdMem(),
1798 .size = init.len,
1799 });
1800 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
1801 sym.ni = ni;
1802 sym.size = init.len;
1803 try coff.addReloc(
1804 si,
1805 init.len - 4,
1806 gop.value_ptr.import_address_table_si,
1807 @intCast(addr_size * import_symbol_index),
1808 .{ .AMD64 = .REL32 },
1809 );
1810 },
1811 }
1812 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
1813 sym.rva = coff.computeNodeRva(sym.ni);
1814 si.applyLocationRelocs(coff);
1815 }
1816}
1817
1818fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
1819 const zcu = pt.zcu;
1820 const gpa = zcu.gpa;
1821
1822 const lazy = lmr.lazySymbol(coff);
1823 const si = lmr.symbol(coff);
1824 const ni = ni: {
1825 const sym = si.get(coff);
1826 switch (sym.ni) {
1827 .none => {
1828 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1829 const sec_si: Symbol.Index = switch (lazy.kind) {
1830 .code => .text,
1831 .const_data => .rdata,
1832 };
1833 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ .moved = true });
1834 coff.nodes.appendAssumeCapacity(switch (lazy.kind) {
1835 .code => .{ .lazy_code = @enumFromInt(lmr.index) },
1836 .const_data => .{ .lazy_const_data = @enumFromInt(lmr.index) },
1837 });
1838 sym.ni = ni;
1839 sym.section_number = sec_si.get(coff).section_number;
1840 },
1841 else => si.deleteLocationRelocs(coff),
1842 }
1843 assert(sym.loc_relocs == .none);
1844 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1845 break :ni sym.ni;
1846 };
1847
1848 var required_alignment: InternPool.Alignment = .none;
1849 var nw: MappedFile.Node.Writer = undefined;
1850 ni.writer(&coff.mf, gpa, &nw);
1851 defer nw.deinit();
1852 try codegen.generateLazySymbol(
1853 &coff.base,
1854 pt,
1855 Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded,
1856 lazy,
1857 &required_alignment,
1858 &nw.interface,
1859 .none,
1860 .{ .atom_index = @intFromEnum(si) },
1861 );
1862 si.get(coff).size = @intCast(nw.interface.end);
1863 si.applyLocationRelocs(coff);
1864}
1865
1866fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
1867 switch (coff.getNode(ni)) {
1868 .file,
1869 .header,
1870 .signature,
1871 .coff_header,
1872 .optional_header,
1873 .data_directories,
1874 .section_table,
1875 => unreachable,
1876 .section => |si| return coff.targetStore(
1877 &si.get(coff).section_number.header(coff).pointer_to_raw_data,
1878 @intCast(ni.fileLocation(&coff.mf, false).offset),
1879 ),
1880 .import_directory_table => coff.targetStore(
1881 &coff.dataDirectoryPtr(.import_table).virtual_address,
1882 coff.computeNodeRva(ni),
1883 ),
1884 .import_lookup_table => |import_index| coff.targetStore(
1885 &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
1886 coff.computeNodeRva(ni),
1887 ),
1888 .import_address_table => |import_index| {
1889 const import_address_table_si = import_index.get(coff).import_address_table_si;
1890 import_address_table_si.flushMoved(coff);
1891 coff.targetStore(
1892 &coff.importDirectoryEntryPtr(import_index).import_address_table_rva,
1893 import_address_table_si.get(coff).rva,
1894 );
1895 },
1896 .import_hint_name_table => |import_index| {
1897 const target_endian = coff.targetEndian();
1898 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
1899 const import_hint_name_rva = coff.computeNodeRva(ni);
1900 coff.targetStore(
1901 &coff.importDirectoryEntryPtr(import_index).name_rva,
1902 import_hint_name_rva,
1903 );
1904 const import_entry = import_index.get(coff);
1905 const import_lookup_slice = import_entry.import_lookup_table_ni.slice(&coff.mf);
1906 const import_address_slice =
1907 import_entry.import_address_table_si.node(coff).slice(&coff.mf);
1908 const import_hint_name_slice = ni.slice(&coff.mf);
1909 const import_hint_name_align = ni.alignment(&coff.mf);
1910 var import_hint_name_index: u32 = 0;
1911 for (0..import_entry.len) |import_symbol_index| {
1912 import_hint_name_index = @intCast(import_hint_name_align.forward(
1913 std.mem.indexOfScalarPos(
1914 u8,
1915 import_hint_name_slice,
1916 import_hint_name_index,
1917 0,
1918 ).? + 1,
1919 ));
1920 switch (magic) {
1921 _ => unreachable,
1922 inline .PE32, .@"PE32+" => |ct_magic| {
1923 const Addr = switch (ct_magic) {
1924 _ => comptime unreachable,
1925 .PE32 => u32,
1926 .@"PE32+" => u64,
1927 };
1928 const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
1929 const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
1930 const rva = std.mem.nativeTo(
1931 Addr,
1932 import_hint_name_rva + import_hint_name_index,
1933 target_endian,
1934 );
1935 import_lookup_table[import_symbol_index] = rva;
1936 import_address_table[import_symbol_index] = rva;
1937 },
1938 }
1939 import_hint_name_index += 2;
1940 }
1941 },
1942 inline .global,
1943 .nav,
1944 .uav,
1945 .lazy_code,
1946 .lazy_const_data,
1947 => |mi| mi.symbol(coff).flushMoved(coff),
1948 }
1949 try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
1950}
1951
1952fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
1953 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
1954 switch (coff.getNode(ni)) {
1955 .file => {},
1956 .header => {
1957 switch (coff.optionalHeaderPtr()) {
1958 inline else => |optional_header| coff.targetStore(
1959 &optional_header.size_of_headers,
1960 @intCast(size),
1961 ),
1962 }
1963 if (size > coff.section_table.items[0].get(coff).rva) try coff.virtualSlide(
1964 0,
1965 std.mem.alignForward(
1966 u32,
1967 @intCast(size * 4),
1968 coff.optionalHeaderField(.section_alignment),
1969 ),
1970 );
1971 },
1972 .signature, .coff_header, .optional_header, .data_directories => unreachable,
1973 .section_table => {},
1974 .section => |si| {
1975 const sym = si.get(coff);
1976 const section_index = sym.section_number.toIndex();
1977 const section = &coff.sectionTableSlice()[section_index];
1978 coff.targetStore(&section.size_of_raw_data, @intCast(size));
1979 if (size > coff.targetLoad(&section.virtual_size)) {
1980 const virtual_size = std.mem.alignForward(
1981 u32,
1982 @intCast(size * 4),
1983 coff.optionalHeaderField(.section_alignment),
1984 );
1985 coff.targetStore(&section.virtual_size, virtual_size);
1986 try coff.virtualSlide(section_index + 1, sym.rva + virtual_size);
1987 }
1988 },
1989 .import_directory_table => coff.targetStore(
1990 &coff.dataDirectoryPtr(.import_table).size,
1991 @intCast(size),
1992 ),
1993 .import_lookup_table,
1994 .import_address_table,
1995 .import_hint_name_table,
1996 .global,
1997 .nav,
1998 .uav,
1999 .lazy_code,
2000 .lazy_const_data,
2001 => {},
2002 }
2003}
2004fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
2005 var rva = start_rva;
2006 for (
2007 coff.section_table.items[start_section_index..],
2008 coff.sectionTableSlice()[start_section_index..],
2009 ) |section_si, *section| {
2010 const section_sym = section_si.get(coff);
2011 section_sym.rva = rva;
2012 coff.targetStore(&section.virtual_address, rva);
2013 try section_sym.ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
2014 rva += coff.targetLoad(&section.virtual_size);
2015 }
2016 switch (coff.optionalHeaderPtr()) {
2017 inline else => |optional_header| coff.targetStore(
2018 &optional_header.size_of_image,
2019 @intCast(rva),
2020 ),
2021 }
2022}
2023
2024pub fn updateExports(
2025 coff: *Coff,
2026 pt: Zcu.PerThread,
2027 exported: Zcu.Exported,
2028 export_indices: []const Zcu.Export.Index,
2029) !void {
2030 return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
2031 error.OutOfMemory => error.OutOfMemory,
2032 error.LinkFailure => error.AnalysisFail,
2033 };
2034}
2035fn updateExportsInner(
2036 coff: *Coff,
2037 pt: Zcu.PerThread,
2038 exported: Zcu.Exported,
2039 export_indices: []const Zcu.Export.Index,
2040) !void {
2041 const zcu = pt.zcu;
2042 const gpa = zcu.gpa;
2043 const ip = &zcu.intern_pool;
2044
2045 switch (exported) {
2046 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
2047 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
2048 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
2049 Value.fromInterned(uav).fmtValue(pt),
2050 }),
2051 }
2052 try coff.symbol_table.ensureUnusedCapacity(gpa, export_indices.len);
2053 const exported_si: Symbol.Index = switch (exported) {
2054 .nav => |nav| try coff.navSymbol(zcu, nav),
2055 .uav => |uav| @enumFromInt(switch (try coff.lowerUav(
2056 pt,
2057 uav,
2058 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
2059 export_indices[0].ptr(zcu).src,
2060 )) {
2061 .sym_index => |si| si,
2062 .fail => |em| {
2063 defer em.destroy(gpa);
2064 return coff.base.comp.link_diags.fail("{s}", .{em.msg});
2065 },
2066 }),
2067 };
2068 while (try coff.idle(pt.tid)) {}
2069 const exported_ni = exported_si.node(coff);
2070 const exported_sym = exported_si.get(coff);
2071 for (export_indices) |export_index| {
2072 const @"export" = export_index.ptr(zcu);
2073 const export_si = try coff.globalSymbol(@"export".opts.name.toSlice(ip), null);
2074 const export_sym = export_si.get(coff);
2075 export_sym.ni = exported_ni;
2076 export_sym.rva = exported_sym.rva;
2077 export_sym.size = exported_sym.size;
2078 export_sym.section_number = exported_sym.section_number;
2079 export_si.applyTargetRelocs(coff);
2080 if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) {
2081 coff.entry_hack = exported_si;
2082 coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
2083 }
2084 }
2085}
2086
2087pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
2088 _ = coff;
2089 _ = exported;
2090 _ = name;
2091}
2092
2093pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) void {
2094 const w = std.debug.lockStderrWriter(&.{});
2095 defer std.debug.unlockStderrWriter();
2096 coff.printNode(tid, w, .root, 0) catch {};
2097}
2098
2099pub fn printNode(
2100 coff: *Coff,
2101 tid: Zcu.PerThread.Id,
2102 w: *std.Io.Writer,
2103 ni: MappedFile.Node.Index,
2104 indent: usize,
2105) !void {
2106 const node = coff.getNode(ni);
2107 try w.splatByteAll(' ', indent);
2108 try w.writeAll(@tagName(node));
2109 switch (node) {
2110 else => {},
2111 .section => |si| try w.print("({s})", .{
2112 std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
2113 }),
2114 .import_lookup_table,
2115 .import_address_table,
2116 .import_hint_name_table,
2117 => |import_index| try w.print("({s})", .{
2118 std.mem.sliceTo(import_index.get(coff).import_hint_name_table_ni.sliceConst(&coff.mf), 0),
2119 }),
2120 .global => |gmi| {
2121 const gn = gmi.globalName(coff);
2122 try w.writeByte('(');
2123 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
2124 try w.print("{s})", .{gn.name.toSlice(coff)});
2125 },
2126 .nav => |nmi| {
2127 const zcu = coff.base.comp.zcu.?;
2128 const ip = &zcu.intern_pool;
2129 const nav = ip.getNav(nmi.navIndex(coff));
2130 try w.print("({f}, {f})", .{
2131 Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
2132 nav.fqn.fmt(ip),
2133 });
2134 },
2135 .uav => |umi| {
2136 const zcu = coff.base.comp.zcu.?;
2137 const val: Value = .fromInterned(umi.uavValue(coff));
2138 try w.print("({f}, {f})", .{
2139 val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
2140 val.fmtValue(.{ .zcu = zcu, .tid = tid }),
2141 });
2142 },
2143 inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
2144 Type.fromInterned(lmi.lazySymbol(coff).ty).fmt(.{
2145 .zcu = coff.base.comp.zcu.?,
2146 .tid = tid,
2147 }),
2148 }),
2149 }
2150 {
2151 const mf_node = &coff.mf.nodes.items[@intFromEnum(ni)];
2152 const off, const size = mf_node.location().resolve(&coff.mf);
2153 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{
2154 @intFromEnum(ni),
2155 off,
2156 size,
2157 mf_node.flags.alignment.toByteUnits(),
2158 if (mf_node.flags.fixed) " fixed" else "",
2159 if (mf_node.flags.moved) " moved" else "",
2160 if (mf_node.flags.resized) " resized" else "",
2161 if (mf_node.flags.has_content) " has_content" else "",
2162 });
2163 }
2164 var leaf = true;
2165 var child_it = ni.children(&coff.mf);
2166 while (child_it.next()) |child_ni| {
2167 leaf = false;
2168 try coff.printNode(tid, w, child_ni, indent + 1);
2169 }
2170 if (leaf) {
2171 const file_loc = ni.fileLocation(&coff.mf, false);
2172 if (file_loc.size == 0) return;
2173 var address = file_loc.offset;
2174 const line_len = 0x10;
2175 var line_it = std.mem.window(
2176 u8,
2177 coff.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
2178 line_len,
2179 line_len,
2180 );
2181 while (line_it.next()) |line_bytes| : (address += line_len) {
2182 try w.splatByteAll(' ', indent + 1);
2183 try w.print("{x:0>8} ", .{address});
2184 for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
2185 try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
2186 for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
2187 try w.writeByte('\n');
2188 }
2189 }
2190}
2191
2192const assert = std.debug.assert;
2193const builtin = @import("builtin");
2194const codegen = @import("../codegen.zig");
2195const Compilation = @import("../Compilation.zig");
2196const Coff = @This();
2197const InternPool = @import("../InternPool.zig");
2198const link = @import("../link.zig");
2199const log = std.log.scoped(.link);
2200const MappedFile = @import("MappedFile.zig");
2201const native_endian = builtin.cpu.arch.endian();
2202const std = @import("std");
2203const target_util = @import("../target.zig");
2204const Type = @import("../Type.zig");
2205const Value = @import("../Value.zig");
2206const Zcu = @import("../Zcu.zig");
src/link/Coff2.zig deleted-2193
......@@ -1,2193 +0,0 @@
1base: link.File,
2endian: std.builtin.Endian,
3mf: MappedFile,
4nodes: std.MultiArrayList(Node),
5import_table: ImportTable,
6strings: std.HashMapUnmanaged(
7 u32,
8 void,
9 std.hash_map.StringIndexContext,
10 std.hash_map.default_max_load_percentage,
11),
12string_bytes: std.ArrayList(u8),
13section_table: std.ArrayList(Symbol.Index),
14symbol_table: std.ArrayList(Symbol),
15globals: std.AutoArrayHashMapUnmanaged(GlobalName, Symbol.Index),
16global_pending_index: u32,
17navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Symbol.Index),
18uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
19lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
20 map: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
21 pending_index: u32,
22}),
23pending_uavs: std.AutoArrayHashMapUnmanaged(Node.UavMapIndex, struct {
24 alignment: InternPool.Alignment,
25 src_loc: Zcu.LazySrcLoc,
26}),
27relocs: std.ArrayList(Reloc),
28/// This is hiding actual bugs with global symbols! Reconsider once they are implemented correctly.
29entry_hack: Symbol.Index,
30
31pub const default_file_alignment: u16 = 0x200;
32pub const default_size_of_stack_reserve: u32 = 0x1000000;
33pub const default_size_of_stack_commit: u32 = 0x1000;
34pub const default_size_of_heap_reserve: u32 = 0x100000;
35pub const default_size_of_heap_commit: u32 = 0x1000;
36
37/// This is the start of a Portable Executable (PE) file.
38/// It starts with a MS-DOS header followed by a MS-DOS stub program.
39/// This data does not change so we include it as follows in all binaries.
40///
41/// In this context,
42/// A "paragraph" is 16 bytes.
43/// A "page" is 512 bytes.
44/// A "long" is 4 bytes.
45/// A "word" is 2 bytes.
46pub const msdos_stub: [120]u8 = .{
47 'M', 'Z', // Magic number. Stands for Mark Zbikowski (designer of the MS-DOS executable format).
48 0x78, 0x00, // Number of bytes in the last page. This matches the size of this entire MS-DOS stub.
49 0x01, 0x00, // Number of pages.
50 0x00, 0x00, // Number of entries in the relocation table.
51 0x04, 0x00, // The number of paragraphs taken up by the header. 4 * 16 = 64, which matches the header size (all bytes before the MS-DOS stub program).
52 0x00, 0x00, // The number of paragraphs required by the program.
53 0x00, 0x00, // The number of paragraphs requested by the program.
54 0x00, 0x00, // Initial value for SS (relocatable segment address).
55 0x00, 0x00, // Initial value for SP.
56 0x00, 0x00, // Checksum.
57 0x00, 0x00, // Initial value for IP.
58 0x00, 0x00, // Initial value for CS (relocatable segment address).
59 0x40, 0x00, // Absolute offset to relocation table. 64 matches the header size (all bytes before the MS-DOS stub program).
60 0x00, 0x00, // Overlay number. Zero means this is the main executable.
61}
62 // Reserved words.
63 ++ .{ 0x00, 0x00 } ** 4
64 // OEM-related fields.
65 ++ .{
66 0x00, 0x00, // OEM identifier.
67 0x00, 0x00, // OEM information.
68 }
69 // Reserved words.
70 ++ .{ 0x00, 0x00 } ** 10
71 // Address of the PE header (a long). This matches the size of this entire MS-DOS stub, so that's the address of what's after this MS-DOS stub.
72 ++ .{ 0x78, 0x00, 0x00, 0x00 }
73 // What follows is a 16-bit x86 MS-DOS program of 7 instructions that prints the bytes after these instructions and then exits.
74 ++ .{
75 // Set the value of the data segment to the same value as the code segment.
76 0x0e, // push cs
77 0x1f, // pop ds
78 // Set the DX register to the address of the message.
79 // If you count all bytes of these 7 instructions you get 14, so that's the address of what's after these instructions.
80 0xba, 14, 0x00, // mov dx, 14
81 // Set AH to the system call code for printing a message.
82 0xb4, 0x09, // mov ah, 0x09
83 // Perform the system call to print the message.
84 0xcd, 0x21, // int 0x21
85 // Set AH to 0x4c which is the system call code for exiting, and set AL to 0x01 which is the exit code.
86 0xb8, 0x01, 0x4c, // mov ax, 0x4c01
87 // Peform the system call to exit the program with exit code 1.
88 0xcd, 0x21, // int 0x21
89 }
90 // Message to print.
91 ++ "This program cannot be run in DOS mode.".*
92 // Message terminators.
93 ++ .{
94 '$', // We do not pass a length to the print system call; the string is terminated by this character.
95 0x00, 0x00, // Terminating zero bytes.
96 };
97
98pub const Node = union(enum) {
99 file,
100 header,
101 signature,
102 coff_header,
103 optional_header,
104 data_directories,
105 section_table,
106 section: Symbol.Index,
107 import_directory_table,
108 import_lookup_table: u32,
109 import_address_table: u32,
110 import_hint_name_table: u32,
111 global: GlobalMapIndex,
112 nav: NavMapIndex,
113 uav: UavMapIndex,
114 lazy_code: LazyMapRef.Index(.code),
115 lazy_const_data: LazyMapRef.Index(.const_data),
116
117 pub const GlobalMapIndex = enum(u32) {
118 _,
119
120 pub fn globalName(gmi: GlobalMapIndex, coff: *const Coff) GlobalName {
121 return coff.globals.keys()[@intFromEnum(gmi)];
122 }
123
124 pub fn symbol(gmi: GlobalMapIndex, coff: *const Coff) Symbol.Index {
125 return coff.globals.values()[@intFromEnum(gmi)];
126 }
127 };
128
129 pub const NavMapIndex = enum(u32) {
130 _,
131
132 pub fn navIndex(nmi: NavMapIndex, coff: *const Coff) InternPool.Nav.Index {
133 return coff.navs.keys()[@intFromEnum(nmi)];
134 }
135
136 pub fn symbol(nmi: NavMapIndex, coff: *const Coff) Symbol.Index {
137 return coff.navs.values()[@intFromEnum(nmi)];
138 }
139 };
140
141 pub const UavMapIndex = enum(u32) {
142 _,
143
144 pub fn uavValue(umi: UavMapIndex, coff: *const Coff) InternPool.Index {
145 return coff.uavs.keys()[@intFromEnum(umi)];
146 }
147
148 pub fn symbol(umi: UavMapIndex, coff: *const Coff) Symbol.Index {
149 return coff.uavs.values()[@intFromEnum(umi)];
150 }
151 };
152
153 pub const LazyMapRef = struct {
154 kind: link.File.LazySymbol.Kind,
155 index: u32,
156
157 pub fn Index(comptime kind: link.File.LazySymbol.Kind) type {
158 return enum(u32) {
159 _,
160
161 pub fn ref(lmi: @This()) LazyMapRef {
162 return .{ .kind = kind, .index = @intFromEnum(lmi) };
163 }
164
165 pub fn lazySymbol(lmi: @This(), coff: *const Coff) link.File.LazySymbol {
166 return lmi.ref().lazySymbol(coff);
167 }
168
169 pub fn symbol(lmi: @This(), coff: *const Coff) Symbol.Index {
170 return lmi.ref().symbol(coff);
171 }
172 };
173 }
174
175 pub fn lazySymbol(lmr: LazyMapRef, coff: *const Coff) link.File.LazySymbol {
176 return .{ .kind = lmr.kind, .ty = coff.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
177 }
178
179 pub fn symbol(lmr: LazyMapRef, coff: *const Coff) Symbol.Index {
180 return coff.lazy.getPtrConst(lmr.kind).map.values()[lmr.index];
181 }
182 };
183
184 pub const Tag = @typeInfo(Node).@"union".tag_type.?;
185
186 const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
187 const known = known: {
188 const Known = enum {
189 file,
190 header,
191 signature,
192 coff_header,
193 optional_header,
194 data_directories,
195 section_table,
196 };
197 var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
198 for (@typeInfo(Known).@"enum".fields) |field|
199 @field(mut_known, field.name) = @enumFromInt(field.value);
200 break :known mut_known;
201 };
202
203 comptime {
204 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
205 }
206};
207
208pub const DataDirectory = enum {
209 export_table,
210 import_table,
211 resorce_table,
212 exception_table,
213 certificate_table,
214 base_relocation_table,
215 debug,
216 architecture,
217 global_ptr,
218 tls_table,
219 load_config_table,
220 bound_import,
221 import_address_table,
222 delay_import_descriptor,
223 clr_runtime_header,
224 reserved,
225};
226
227pub const ImportTable = struct {
228 directory_table_ni: MappedFile.Node.Index,
229 dlls: std.AutoArrayHashMapUnmanaged(void, Dll),
230
231 pub const Dll = struct {
232 import_lookup_table_ni: MappedFile.Node.Index,
233 import_address_table_si: Symbol.Index,
234 import_hint_name_table_ni: MappedFile.Node.Index,
235 len: u32,
236 hint_name_len: u32,
237 };
238
239 const Adapter = struct {
240 coff: *Coff,
241
242 pub fn eql(adapter: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool {
243 const coff = adapter.coff;
244 const dll_name = coff.import_table.dlls.values()[rhs_index]
245 .import_hint_name_table_ni.sliceConst(&coff.mf);
246 return std.mem.startsWith(u8, dll_name, lhs_key) and
247 std.mem.startsWith(u8, dll_name[lhs_key.len..], ".dll\x00");
248 }
249
250 pub fn hash(_: Adapter, key: []const u8) u32 {
251 assert(std.mem.indexOfScalar(u8, key, 0) == null);
252 return std.array_hash_map.hashString(key);
253 }
254 };
255};
256
257pub const String = enum(u32) {
258 _,
259
260 pub const Optional = enum(u32) {
261 none = std.math.maxInt(u32),
262 _,
263
264 pub fn unwrap(os: String.Optional) ?String {
265 return switch (os) {
266 else => |s| @enumFromInt(@intFromEnum(s)),
267 .none => null,
268 };
269 }
270
271 pub fn toSlice(os: String.Optional, coff: *Coff) ?[:0]const u8 {
272 return (os.unwrap() orelse return null).toSlice(coff);
273 }
274 };
275
276 pub fn toSlice(s: String, coff: *Coff) [:0]const u8 {
277 const slice = coff.string_bytes.items[@intFromEnum(s)..];
278 return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
279 }
280
281 pub fn toOptional(s: String) String.Optional {
282 return @enumFromInt(@intFromEnum(s));
283 }
284};
285
286pub const GlobalName = struct { name: String, lib_name: String.Optional };
287
288pub const Symbol = struct {
289 ni: MappedFile.Node.Index,
290 rva: u32,
291 size: u32,
292 /// Relocations contained within this symbol
293 loc_relocs: Reloc.Index,
294 /// Relocations targeting this symbol
295 target_relocs: Reloc.Index,
296 section_number: SectionNumber,
297 data_directory: ?DataDirectory,
298 unused0: u32 = 0,
299 unused1: u32 = 0,
300
301 pub const SectionNumber = enum(i16) {
302 UNDEFINED = 0,
303 ABSOLUTE = -1,
304 DEBUG = -2,
305 _,
306
307 fn toIndex(sn: SectionNumber) u15 {
308 return @intCast(@intFromEnum(sn) - 1);
309 }
310
311 pub fn symbol(sn: SectionNumber, coff: *const Coff) Symbol.Index {
312 return coff.section_table.items[sn.toIndex()];
313 }
314
315 pub fn header(sn: SectionNumber, coff: *Coff) *std.coff.SectionHeader {
316 return &coff.sectionTableSlice()[sn.toIndex()];
317 }
318 };
319
320 pub const Index = enum(u32) {
321 null,
322 data,
323 idata,
324 rdata,
325 text,
326 _,
327
328 const known_count = @typeInfo(Index).@"enum".fields.len;
329
330 pub fn get(si: Symbol.Index, coff: *Coff) *Symbol {
331 return &coff.symbol_table.items[@intFromEnum(si)];
332 }
333
334 pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {
335 const ni = si.get(coff).ni;
336 assert(ni != .none);
337 return ni;
338 }
339
340 pub fn flushMoved(si: Symbol.Index, coff: *Coff) void {
341 const sym = si.get(coff);
342 sym.rva = coff.computeNodeRva(sym.ni);
343 if (si == coff.entry_hack)
344 coff.targetStore(&coff.optionalHeaderStandardPtr().address_of_entry_point, sym.rva);
345 si.applyLocationRelocs(coff);
346 si.applyTargetRelocs(coff);
347 }
348
349 pub fn applyLocationRelocs(si: Symbol.Index, coff: *Coff) void {
350 for (coff.relocs.items[@intFromEnum(si.get(coff).loc_relocs)..]) |*reloc| {
351 if (reloc.loc != si) break;
352 reloc.apply(coff);
353 }
354 }
355
356 pub fn applyTargetRelocs(si: Symbol.Index, coff: *Coff) void {
357 var ri = si.get(coff).target_relocs;
358 while (ri != .none) {
359 const reloc = ri.get(coff);
360 assert(reloc.target == si);
361 reloc.apply(coff);
362 ri = reloc.next;
363 }
364 }
365
366 pub fn deleteLocationRelocs(si: Symbol.Index, coff: *Coff) void {
367 const sym = si.get(coff);
368 for (coff.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
369 if (reloc.loc != si) break;
370 reloc.delete(coff);
371 }
372 sym.loc_relocs = .none;
373 }
374 };
375
376 comptime {
377 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 32);
378 }
379};
380
381pub const Reloc = extern struct {
382 type: Reloc.Type,
383 prev: Reloc.Index,
384 next: Reloc.Index,
385 loc: Symbol.Index,
386 target: Symbol.Index,
387 unused: u32,
388 offset: u64,
389 addend: i64,
390
391 pub const Type = extern union {
392 AMD64: std.coff.IMAGE.REL.AMD64,
393 ARM: std.coff.IMAGE.REL.ARM,
394 ARM64: std.coff.IMAGE.REL.ARM64,
395 SH: std.coff.IMAGE.REL.SH,
396 PPC: std.coff.IMAGE.REL.PPC,
397 I386: std.coff.IMAGE.REL.I386,
398 IA64: std.coff.IMAGE.REL.IA64,
399 MIPS: std.coff.IMAGE.REL.MIPS,
400 M32R: std.coff.IMAGE.REL.M32R,
401 };
402
403 pub const Index = enum(u32) {
404 none = std.math.maxInt(u32),
405 _,
406
407 pub fn get(si: Reloc.Index, coff: *Coff) *Reloc {
408 return &coff.relocs.items[@intFromEnum(si)];
409 }
410 };
411
412 pub fn apply(reloc: *const Reloc, coff: *Coff) void {
413 const loc_sym = reloc.loc.get(coff);
414 switch (loc_sym.ni) {
415 .none => return,
416 else => |ni| if (ni.hasMoved(&coff.mf)) return,
417 }
418 const target_sym = reloc.target.get(coff);
419 switch (target_sym.ni) {
420 .none => return,
421 else => |ni| if (ni.hasMoved(&coff.mf)) return,
422 }
423 const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];
424 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
425 const target_endian = coff.targetEndian();
426 switch (coff.targetLoad(&coff.headerPtr().machine)) {
427 else => |machine| @panic(@tagName(machine)),
428 .AMD64 => switch (reloc.type.AMD64) {
429 else => |kind| @panic(@tagName(kind)),
430 .ABSOLUTE => {},
431 .ADDR64 => std.mem.writeInt(
432 u64,
433 loc_slice[0..8],
434 coff.optionalHeaderField(.image_base) + target_rva,
435 target_endian,
436 ),
437 .ADDR32 => std.mem.writeInt(
438 u32,
439 loc_slice[0..4],
440 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
441 target_endian,
442 ),
443 .ADDR32NB => std.mem.writeInt(
444 u32,
445 loc_slice[0..4],
446 @intCast(target_rva),
447 target_endian,
448 ),
449 .REL32 => std.mem.writeInt(
450 i32,
451 loc_slice[0..4],
452 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
453 target_endian,
454 ),
455 .REL32_1 => std.mem.writeInt(
456 i32,
457 loc_slice[0..4],
458 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 5)))),
459 target_endian,
460 ),
461 .REL32_2 => std.mem.writeInt(
462 i32,
463 loc_slice[0..4],
464 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 6)))),
465 target_endian,
466 ),
467 .REL32_3 => std.mem.writeInt(
468 i32,
469 loc_slice[0..4],
470 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 7)))),
471 target_endian,
472 ),
473 .REL32_4 => std.mem.writeInt(
474 i32,
475 loc_slice[0..4],
476 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 8)))),
477 target_endian,
478 ),
479 .REL32_5 => std.mem.writeInt(
480 i32,
481 loc_slice[0..4],
482 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 9)))),
483 target_endian,
484 ),
485 },
486 .I386 => switch (reloc.type.I386) {
487 else => |kind| @panic(@tagName(kind)),
488 .ABSOLUTE => {},
489 .DIR16 => std.mem.writeInt(
490 u16,
491 loc_slice[0..2],
492 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
493 target_endian,
494 ),
495 .REL16 => std.mem.writeInt(
496 i16,
497 loc_slice[0..2],
498 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 2)))),
499 target_endian,
500 ),
501 .DIR32 => std.mem.writeInt(
502 u32,
503 loc_slice[0..4],
504 @intCast(coff.optionalHeaderField(.image_base) + target_rva),
505 target_endian,
506 ),
507 .DIR32NB => std.mem.writeInt(
508 u32,
509 loc_slice[0..4],
510 @intCast(target_rva),
511 target_endian,
512 ),
513 .REL32 => std.mem.writeInt(
514 i32,
515 loc_slice[0..4],
516 @intCast(@as(i64, @bitCast(target_rva -% (loc_sym.rva + reloc.offset + 4)))),
517 target_endian,
518 ),
519 },
520 }
521 }
522
523 pub fn delete(reloc: *Reloc, coff: *Coff) void {
524 switch (reloc.prev) {
525 .none => {
526 const target = reloc.target.get(coff);
527 assert(target.target_relocs.get(coff) == reloc);
528 target.target_relocs = reloc.next;
529 },
530 else => |prev| prev.get(coff).next = reloc.next,
531 }
532 switch (reloc.next) {
533 .none => {},
534 else => |next| next.get(coff).prev = reloc.prev,
535 }
536 reloc.* = undefined;
537 }
538
539 comptime {
540 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
541 }
542};
543
544pub fn open(
545 arena: std.mem.Allocator,
546 comp: *Compilation,
547 path: std.Build.Cache.Path,
548 options: link.File.OpenOptions,
549) !*Coff {
550 return create(arena, comp, path, options);
551}
552pub fn createEmpty(
553 arena: std.mem.Allocator,
554 comp: *Compilation,
555 path: std.Build.Cache.Path,
556 options: link.File.OpenOptions,
557) !*Coff {
558 return create(arena, comp, path, options);
559}
560fn create(
561 arena: std.mem.Allocator,
562 comp: *Compilation,
563 path: std.Build.Cache.Path,
564 options: link.File.OpenOptions,
565) !*Coff {
566 const target = &comp.root_mod.resolved_target.result;
567 assert(target.ofmt == .coff);
568 const is_image = switch (comp.config.output_mode) {
569 .Exe => true,
570 .Lib => switch (comp.config.link_mode) {
571 .static => false,
572 .dynamic => true,
573 },
574 .Obj => false,
575 };
576 const machine = target.toCoffMachine();
577 const timestamp: u32 = if (options.repro) 0 else @truncate(@as(u64, @bitCast(std.time.timestamp())));
578 const major_subsystem_version = options.major_subsystem_version orelse 6;
579 const minor_subsystem_version = options.minor_subsystem_version orelse 0;
580 const magic: std.coff.OptionalHeader.Magic = switch (target.ptrBitWidth()) {
581 0...32 => .PE32,
582 33...64 => .@"PE32+",
583 else => return error.UnsupportedCOFFArchitecture,
584 };
585 const section_align: std.mem.Alignment = switch (machine) {
586 .AMD64, .I386 => @enumFromInt(12),
587 .SH3, .SH3DSP, .SH4, .SH5 => @enumFromInt(12),
588 .MIPS16, .MIPSFPU, .MIPSFPU16, .WCEMIPSV2 => @enumFromInt(12),
589 .POWERPC, .POWERPCFP => @enumFromInt(12),
590 .ALPHA, .ALPHA64 => @enumFromInt(13),
591 .IA64 => @enumFromInt(13),
592 .ARM => @enumFromInt(12),
593 else => return error.UnsupportedCOFFArchitecture,
594 };
595
596 const coff = try arena.create(Coff);
597 const file = try path.root_dir.handle.createFile(path.sub_path, .{
598 .read = true,
599 .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
600 });
601 errdefer file.close();
602 coff.* = .{
603 .base = .{
604 .tag = .coff2,
605
606 .comp = comp,
607 .emit = path,
608
609 .file = file,
610 .gc_sections = false,
611 .print_gc_sections = false,
612 .build_id = .none,
613 .allow_shlib_undefined = false,
614 .stack_size = 0,
615 },
616 .endian = target.cpu.arch.endian(),
617 .mf = try .init(file, comp.gpa),
618 .nodes = .empty,
619 .import_table = .{
620 .directory_table_ni = .none,
621 .dlls = .empty,
622 },
623 .strings = .empty,
624 .string_bytes = .empty,
625 .section_table = .empty,
626 .symbol_table = .empty,
627 .globals = .empty,
628 .global_pending_index = 0,
629 .navs = .empty,
630 .uavs = .empty,
631 .lazy = .initFill(.{
632 .map = .empty,
633 .pending_index = 0,
634 }),
635 .pending_uavs = .empty,
636 .relocs = .empty,
637 .entry_hack = .null,
638 };
639 errdefer coff.deinit();
640
641 try coff.initHeaders(
642 is_image,
643 machine,
644 timestamp,
645 major_subsystem_version,
646 minor_subsystem_version,
647 magic,
648 section_align,
649 );
650 return coff;
651}
652
653pub fn deinit(coff: *Coff) void {
654 const gpa = coff.base.comp.gpa;
655 coff.mf.deinit(gpa);
656 coff.nodes.deinit(gpa);
657 coff.import_table.dlls.deinit(gpa);
658 coff.strings.deinit(gpa);
659 coff.string_bytes.deinit(gpa);
660 coff.section_table.deinit(gpa);
661 coff.symbol_table.deinit(gpa);
662 coff.globals.deinit(gpa);
663 coff.navs.deinit(gpa);
664 coff.uavs.deinit(gpa);
665 for (&coff.lazy.values) |*lazy| lazy.map.deinit(gpa);
666 coff.pending_uavs.deinit(gpa);
667 coff.relocs.deinit(gpa);
668 coff.* = undefined;
669}
670
671fn initHeaders(
672 coff: *Coff,
673 is_image: bool,
674 machine: std.coff.IMAGE.FILE.MACHINE,
675 timestamp: u32,
676 major_subsystem_version: u16,
677 minor_subsystem_version: u16,
678 magic: std.coff.OptionalHeader.Magic,
679 section_align: std.mem.Alignment,
680) !void {
681 const comp = coff.base.comp;
682 const gpa = comp.gpa;
683 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);
684 const target_endian = coff.targetEndian();
685
686 const optional_header_size: u16 = if (is_image) switch (magic) {
687 _ => unreachable,
688 inline else => |ct_magic| @sizeOf(@field(std.coff.OptionalHeader, @tagName(ct_magic))),
689 } else 0;
690 const data_directories_len = @typeInfo(DataDirectory).@"enum".fields.len;
691 const data_directories_size: u16 = if (is_image)
692 @sizeOf(std.coff.ImageDataDirectory) * data_directories_len
693 else
694 0;
695
696 try coff.nodes.ensureTotalCapacity(gpa, Node.known_count);
697 coff.nodes.appendAssumeCapacity(.file);
698
699 const header_ni = Node.known.header;
700 assert(header_ni == try coff.mf.addOnlyChildNode(gpa, .root, .{
701 .alignment = coff.mf.flags.block_size,
702 .fixed = true,
703 }));
704 coff.nodes.appendAssumeCapacity(.header);
705
706 const signature_ni = Node.known.signature;
707 assert(signature_ni == try coff.mf.addOnlyChildNode(gpa, header_ni, .{
708 .size = (if (is_image) msdos_stub.len else 0) + "PE\x00\x00".len,
709 .alignment = .@"4",
710 .fixed = true,
711 }));
712 coff.nodes.appendAssumeCapacity(.signature);
713 {
714 const signature_slice = signature_ni.slice(&coff.mf);
715 if (is_image) @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
716 @memcpy(signature_slice[signature_slice.len - 4 ..], "PE\x00\x00");
717 }
718
719 const coff_header_ni = Node.known.coff_header;
720 assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
721 .size = @sizeOf(std.coff.Header),
722 .alignment = .@"4",
723 .fixed = true,
724 }));
725 coff.nodes.appendAssumeCapacity(.coff_header);
726 {
727 const coff_header: *std.coff.Header = @ptrCast(@alignCast(coff_header_ni.slice(&coff.mf)));
728 coff_header.* = .{
729 .machine = machine,
730 .number_of_sections = 0,
731 .time_date_stamp = timestamp,
732 .pointer_to_symbol_table = 0,
733 .number_of_symbols = 0,
734 .size_of_optional_header = optional_header_size + data_directories_size,
735 .flags = .{
736 .RELOCS_STRIPPED = is_image,
737 .EXECUTABLE_IMAGE = is_image,
738 .DEBUG_STRIPPED = true,
739 .@"32BIT_MACHINE" = magic == .PE32,
740 .LARGE_ADDRESS_AWARE = magic == .@"PE32+",
741 .DLL = comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic,
742 },
743 };
744 if (target_endian != native_endian) std.mem.byteSwapAllFields(std.coff.Header, coff_header);
745 }
746
747 const optional_header_ni = Node.known.optional_header;
748 assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
749 .size = optional_header_size,
750 .alignment = .@"4",
751 .fixed = true,
752 }));
753 coff.nodes.appendAssumeCapacity(.optional_header);
754 if (is_image) switch (magic) {
755 _ => unreachable,
756 .PE32 => {
757 const optional_header: *std.coff.OptionalHeader.PE32 =
758 @ptrCast(@alignCast(optional_header_ni.slice(&coff.mf)));
759 optional_header.* = .{
760 .standard = .{
761 .magic = .PE32,
762 .major_linker_version = 0,
763 .minor_linker_version = 0,
764 .size_of_code = 0,
765 .size_of_initialized_data = 0,
766 .size_of_uninitialized_data = 0,
767 .address_of_entry_point = 0,
768 .base_of_code = 0,
769 },
770 .base_of_data = 0,
771 .image_base = switch (coff.base.comp.config.output_mode) {
772 .Exe => 0x400000,
773 .Lib => switch (coff.base.comp.config.link_mode) {
774 .static => 0,
775 .dynamic => 0x10000000,
776 },
777 .Obj => 0,
778 },
779 .section_alignment = @intCast(section_align.toByteUnits()),
780 .file_alignment = @intCast(file_align.toByteUnits()),
781 .major_operating_system_version = 6,
782 .minor_operating_system_version = 0,
783 .major_image_version = 0,
784 .minor_image_version = 0,
785 .major_subsystem_version = major_subsystem_version,
786 .minor_subsystem_version = minor_subsystem_version,
787 .win32_version_value = 0,
788 .size_of_image = 0,
789 .size_of_headers = 0,
790 .checksum = 0,
791 .subsystem = .WINDOWS_CUI,
792 .dll_flags = .{
793 .HIGH_ENTROPY_VA = true,
794 .DYNAMIC_BASE = true,
795 .TERMINAL_SERVER_AWARE = true,
796 .NX_COMPAT = true,
797 },
798 .size_of_stack_reserve = default_size_of_stack_reserve,
799 .size_of_stack_commit = default_size_of_stack_commit,
800 .size_of_heap_reserve = default_size_of_heap_reserve,
801 .size_of_heap_commit = default_size_of_heap_commit,
802 .loader_flags = 0,
803 .number_of_rva_and_sizes = data_directories_len,
804 };
805 if (target_endian != native_endian)
806 std.mem.byteSwapAllFields(std.coff.OptionalHeader.PE32, optional_header);
807 },
808 .@"PE32+" => {
809 const header: *std.coff.OptionalHeader.@"PE32+" =
810 @ptrCast(@alignCast(optional_header_ni.slice(&coff.mf)));
811 header.* = .{
812 .standard = .{
813 .magic = .@"PE32+",
814 .major_linker_version = 0,
815 .minor_linker_version = 0,
816 .size_of_code = 0,
817 .size_of_initialized_data = 0,
818 .size_of_uninitialized_data = 0,
819 .address_of_entry_point = 0,
820 .base_of_code = 0,
821 },
822 .image_base = switch (coff.base.comp.config.output_mode) {
823 .Exe => 0x140000000,
824 .Lib => switch (coff.base.comp.config.link_mode) {
825 .static => 0,
826 .dynamic => 0x180000000,
827 },
828 .Obj => 0,
829 },
830 .section_alignment = @intCast(section_align.toByteUnits()),
831 .file_alignment = @intCast(file_align.toByteUnits()),
832 .major_operating_system_version = 6,
833 .minor_operating_system_version = 0,
834 .major_image_version = 0,
835 .minor_image_version = 0,
836 .major_subsystem_version = major_subsystem_version,
837 .minor_subsystem_version = minor_subsystem_version,
838 .win32_version_value = 0,
839 .size_of_image = 0,
840 .size_of_headers = 0,
841 .checksum = 0,
842 .subsystem = .WINDOWS_CUI,
843 .dll_flags = .{
844 .HIGH_ENTROPY_VA = true,
845 .DYNAMIC_BASE = true,
846 .TERMINAL_SERVER_AWARE = true,
847 .NX_COMPAT = true,
848 },
849 .size_of_stack_reserve = default_size_of_stack_reserve,
850 .size_of_stack_commit = default_size_of_stack_commit,
851 .size_of_heap_reserve = default_size_of_heap_reserve,
852 .size_of_heap_commit = default_size_of_heap_commit,
853 .loader_flags = 0,
854 .number_of_rva_and_sizes = data_directories_len,
855 };
856 if (target_endian != native_endian)
857 std.mem.byteSwapAllFields(std.coff.OptionalHeader.@"PE32+", header);
858 },
859 };
860
861 const data_directories_ni = Node.known.data_directories;
862 assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
863 .size = data_directories_size,
864 .alignment = .@"4",
865 .fixed = true,
866 }));
867 coff.nodes.appendAssumeCapacity(.data_directories);
868 {
869 const data_directories: *[data_directories_len]std.coff.ImageDataDirectory =
870 @ptrCast(@alignCast(data_directories_ni.slice(&coff.mf)));
871 @memset(data_directories, .{ .virtual_address = 0, .size = 0 });
872 if (target_endian != native_endian) for (data_directories) |*data_directory|
873 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, data_directory);
874 }
875
876 const section_table_ni = Node.known.section_table;
877 assert(section_table_ni == try coff.mf.addLastChildNode(gpa, header_ni, .{
878 .alignment = .@"4",
879 .fixed = true,
880 }));
881 coff.nodes.appendAssumeCapacity(.section_table);
882
883 assert(coff.nodes.len == Node.known_count);
884
885 try coff.symbol_table.ensureTotalCapacity(gpa, Symbol.Index.known_count);
886 coff.symbol_table.addOneAssumeCapacity().* = .{
887 .ni = .none,
888 .rva = 0,
889 .size = 0,
890 .loc_relocs = .none,
891 .target_relocs = .none,
892 .section_number = .UNDEFINED,
893 .data_directory = null,
894 };
895 assert(try coff.addSection(".data", null, .{
896 .CNT_INITIALIZED_DATA = true,
897 .MEM_READ = true,
898 .MEM_WRITE = true,
899 }) == .data);
900 assert(try coff.addSection(".idata", .import_table, .{
901 .CNT_INITIALIZED_DATA = true,
902 .MEM_READ = true,
903 }) == .idata);
904 assert(try coff.addSection(".rdata", null, .{
905 .CNT_INITIALIZED_DATA = true,
906 .MEM_READ = true,
907 }) == .rdata);
908 assert(try coff.addSection(".text", null, .{
909 .CNT_CODE = true,
910 .MEM_EXECUTE = true,
911 .MEM_READ = true,
912 }) == .text);
913 coff.import_table.directory_table_ni = try coff.mf.addLastChildNode(
914 gpa,
915 Symbol.Index.idata.node(coff),
916 .{
917 .alignment = .@"4",
918 .fixed = true,
919 },
920 );
921 coff.nodes.appendAssumeCapacity(.import_directory_table);
922 assert(coff.symbol_table.items.len == Symbol.Index.known_count);
923}
924
925fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
926 return coff.nodes.get(@intFromEnum(ni));
927}
928fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
929 var section_offset: u32 = 0;
930 var parent_ni = ni;
931 while (true) {
932 assert(parent_ni != .none);
933 switch (coff.getNode(parent_ni)) {
934 else => {},
935 .section => |si| return si.get(coff).rva + section_offset,
936 }
937 const parent_offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
938 section_offset += @intCast(parent_offset);
939 parent_ni = parent_ni.parent(&coff.mf);
940 }
941}
942
943pub inline fn targetEndian(coff: *const Coff) std.builtin.Endian {
944 return coff.endian;
945}
946fn targetLoad(coff: *const Coff, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
947 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
948 return switch (@typeInfo(Child)) {
949 else => @compileError(@typeName(Child)),
950 .int => std.mem.toNative(Child, ptr.*, coff.targetEndian()),
951 .@"enum" => |@"enum"| @enumFromInt(coff.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr)))),
952 .@"struct" => |@"struct"| @bitCast(
953 coff.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))),
954 ),
955 };
956}
957fn targetStore(coff: *const Coff, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
958 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
959 return switch (@typeInfo(Child)) {
960 else => @compileError(@typeName(Child)),
961 .int => ptr.* = std.mem.nativeTo(Child, val, coff.targetEndian()),
962 .@"enum" => |@"enum"| coff.targetStore(
963 @as(*@"enum".tag_type, @ptrCast(ptr)),
964 @intFromEnum(val),
965 ),
966 .@"struct" => |@"struct"| coff.targetStore(
967 @as(*@"struct".backing_integer.?, @ptrCast(ptr)),
968 @bitCast(val),
969 ),
970 };
971}
972
973pub fn headerPtr(coff: *Coff) *std.coff.Header {
974 return @ptrCast(@alignCast(Node.known.coff_header.slice(&coff.mf)));
975}
976
977pub fn optionalHeaderStandardPtr(coff: *Coff) *std.coff.OptionalHeader {
978 return @ptrCast(@alignCast(
979 Node.known.optional_header.slice(&coff.mf)[0..@sizeOf(std.coff.OptionalHeader)],
980 ));
981}
982
983pub const OptionalHeaderPtr = union(std.coff.OptionalHeader.Magic) {
984 PE32: *std.coff.OptionalHeader.PE32,
985 @"PE32+": *std.coff.OptionalHeader.@"PE32+",
986};
987pub fn optionalHeaderPtr(coff: *Coff) OptionalHeaderPtr {
988 const slice = Node.known.optional_header.slice(&coff.mf);
989 return switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
990 _ => unreachable,
991 inline else => |magic| @unionInit(
992 OptionalHeaderPtr,
993 @tagName(magic),
994 @ptrCast(@alignCast(slice)),
995 ),
996 };
997}
998pub fn optionalHeaderField(
999 coff: *Coff,
1000 comptime field: std.meta.FieldEnum(std.coff.OptionalHeader.@"PE32+"),
1001) @FieldType(std.coff.OptionalHeader.@"PE32+", @tagName(field)) {
1002 return switch (coff.optionalHeaderPtr()) {
1003 inline else => |optional_header| coff.targetLoad(&@field(optional_header, @tagName(field))),
1004 };
1005}
1006
1007pub fn dataDirectoriesSlice(coff: *Coff) []std.coff.ImageDataDirectory {
1008 return @ptrCast(@alignCast(Node.known.data_directories.slice(&coff.mf)));
1009}
1010
1011pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
1012 return @ptrCast(@alignCast(Node.known.section_table.slice(&coff.mf)));
1013}
1014
1015fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
1016 defer coff.symbol_table.addOneAssumeCapacity().* = .{
1017 .ni = .none,
1018 .rva = 0,
1019 .size = 0,
1020 .loc_relocs = .none,
1021 .target_relocs = .none,
1022 .section_number = .UNDEFINED,
1023 .data_directory = null,
1024 };
1025 return @enumFromInt(coff.symbol_table.items.len);
1026}
1027
1028fn initSymbolAssumeCapacity(coff: *Coff) !Symbol.Index {
1029 const si = coff.addSymbolAssumeCapacity();
1030 return si;
1031}
1032
1033fn getOrPutString(coff: *Coff, string: []const u8) !String {
1034 const gpa = coff.base.comp.gpa;
1035 try coff.string_bytes.ensureUnusedCapacity(gpa, string.len + 1);
1036 const gop = try coff.strings.getOrPutContextAdapted(
1037 gpa,
1038 string,
1039 std.hash_map.StringIndexAdapter{ .bytes = &coff.string_bytes },
1040 .{ .bytes = &coff.string_bytes },
1041 );
1042 if (!gop.found_existing) {
1043 gop.key_ptr.* = @intCast(coff.string_bytes.items.len);
1044 gop.value_ptr.* = {};
1045 coff.string_bytes.appendSliceAssumeCapacity(string);
1046 coff.string_bytes.appendAssumeCapacity(0);
1047 }
1048 return @enumFromInt(gop.key_ptr.*);
1049}
1050
1051fn getOrPutOptionalString(coff: *Coff, string: ?[]const u8) !String.Optional {
1052 return (try coff.getOrPutString(string orelse return .none)).toOptional();
1053}
1054
1055pub fn globalSymbol(coff: *Coff, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
1056 const gpa = coff.base.comp.gpa;
1057 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1058 const sym_gop = try coff.globals.getOrPut(gpa, .{
1059 .name = try coff.getOrPutString(name),
1060 .lib_name = try coff.getOrPutOptionalString(lib_name),
1061 });
1062 if (!sym_gop.found_existing) {
1063 sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
1064 coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
1065 }
1066 return sym_gop.value_ptr.*;
1067}
1068
1069fn navMapIndex(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
1070 const gpa = zcu.gpa;
1071 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1072 const sym_gop = try coff.navs.getOrPut(gpa, nav_index);
1073 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
1074 return @enumFromInt(sym_gop.index);
1075}
1076pub fn navSymbol(coff: *Coff, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
1077 const ip = &zcu.intern_pool;
1078 const nav = ip.getNav(nav_index);
1079 if (nav.getExtern(ip)) |@"extern"| return coff.globalSymbol(
1080 @"extern".name.toSlice(ip),
1081 @"extern".lib_name.toSlice(ip),
1082 );
1083 const nmi = try coff.navMapIndex(zcu, nav_index);
1084 return nmi.symbol(coff);
1085}
1086
1087fn uavMapIndex(coff: *Coff, uav_val: InternPool.Index) !Node.UavMapIndex {
1088 const gpa = coff.base.comp.gpa;
1089 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1090 const sym_gop = try coff.uavs.getOrPut(gpa, uav_val);
1091 if (!sym_gop.found_existing) sym_gop.value_ptr.* = coff.addSymbolAssumeCapacity();
1092 return @enumFromInt(sym_gop.index);
1093}
1094pub fn uavSymbol(coff: *Coff, uav_val: InternPool.Index) !Symbol.Index {
1095 const umi = try coff.uavMapIndex(uav_val);
1096 return umi.symbol(coff);
1097}
1098
1099pub fn lazySymbol(coff: *Coff, lazy: link.File.LazySymbol) !Symbol.Index {
1100 const gpa = coff.base.comp.gpa;
1101 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1102 const sym_gop = try coff.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
1103 if (!sym_gop.found_existing) {
1104 sym_gop.value_ptr.* = try coff.initSymbolAssumeCapacity();
1105 coff.base.comp.link_synth_prog_node.increaseEstimatedTotalItems(1);
1106 }
1107 return sym_gop.value_ptr.*;
1108}
1109
1110pub fn getNavVAddr(
1111 coff: *Coff,
1112 pt: Zcu.PerThread,
1113 nav: InternPool.Nav.Index,
1114 reloc_info: link.File.RelocInfo,
1115) !u64 {
1116 return coff.getVAddr(reloc_info, try coff.navSymbol(pt.zcu, nav));
1117}
1118
1119pub fn getUavVAddr(
1120 coff: *Coff,
1121 uav: InternPool.Index,
1122 reloc_info: link.File.RelocInfo,
1123) !u64 {
1124 return coff.getVAddr(reloc_info, try coff.uavSymbol(uav));
1125}
1126
1127pub fn getVAddr(coff: *Coff, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
1128 try coff.addReloc(
1129 @enumFromInt(reloc_info.parent.atom_index),
1130 reloc_info.offset,
1131 target_si,
1132 reloc_info.addend,
1133 switch (coff.targetLoad(&coff.headerPtr().machine)) {
1134 else => unreachable,
1135 .AMD64 => .{ .AMD64 = .ADDR64 },
1136 .I386 => .{ .I386 = .DIR32 },
1137 },
1138 );
1139 return coff.optionalHeaderField(.image_base) + target_si.get(coff).rva;
1140}
1141
1142fn addSection(
1143 coff: *Coff,
1144 name: []const u8,
1145 maybe_data_directory: ?DataDirectory,
1146 flags: std.coff.SectionHeader.Flags,
1147) !Symbol.Index {
1148 const gpa = coff.base.comp.gpa;
1149 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1150 try coff.section_table.ensureUnusedCapacity(gpa, 1);
1151 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1152
1153 const coff_header = coff.headerPtr();
1154 const section_index = coff.targetLoad(&coff_header.number_of_sections);
1155 const section_table_len = section_index + 1;
1156 coff.targetStore(&coff_header.number_of_sections, section_table_len);
1157 try Node.known.section_table.resize(
1158 &coff.mf,
1159 gpa,
1160 @sizeOf(std.coff.SectionHeader) * section_table_len,
1161 );
1162 const ni = try coff.mf.addLastChildNode(gpa, .root, .{
1163 .alignment = coff.mf.flags.block_size,
1164 .moved = true,
1165 .bubbles_moved = false,
1166 });
1167 const si = coff.addSymbolAssumeCapacity();
1168 coff.section_table.appendAssumeCapacity(si);
1169 coff.nodes.appendAssumeCapacity(.{ .section = si });
1170 const section_table = coff.sectionTableSlice();
1171 const virtual_size = coff.optionalHeaderField(.section_alignment);
1172 const rva: u32 = switch (section_index) {
1173 0 => @intCast(Node.known.header.location(&coff.mf).resolve(&coff.mf)[1]),
1174 else => coff.section_table.items[section_index - 1].get(coff).rva +
1175 coff.targetLoad(&section_table[section_index - 1].virtual_size),
1176 };
1177 {
1178 const sym = si.get(coff);
1179 sym.ni = ni;
1180 sym.rva = rva;
1181 sym.section_number = @enumFromInt(section_table_len);
1182 sym.data_directory = maybe_data_directory;
1183 }
1184 const section = &section_table[section_index];
1185 section.* = .{
1186 .name = undefined,
1187 .virtual_size = virtual_size,
1188 .virtual_address = rva,
1189 .size_of_raw_data = 0,
1190 .pointer_to_raw_data = 0,
1191 .pointer_to_relocations = 0,
1192 .pointer_to_linenumbers = 0,
1193 .number_of_relocations = 0,
1194 .number_of_linenumbers = 0,
1195 .flags = flags,
1196 };
1197 @memcpy(section.name[0..name.len], name);
1198 @memset(section.name[name.len..], 0);
1199 if (coff.targetEndian() != native_endian)
1200 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
1201 if (maybe_data_directory) |data_directory|
1202 coff.dataDirectoriesSlice()[@intFromEnum(data_directory)] = .{
1203 .virtual_address = section.virtual_address,
1204 .size = section.virtual_size,
1205 };
1206 switch (coff.optionalHeaderPtr()) {
1207 inline else => |optional_header| coff.targetStore(
1208 &optional_header.size_of_image,
1209 @intCast(rva + virtual_size),
1210 ),
1211 }
1212 return si;
1213}
1214
1215pub fn addReloc(
1216 coff: *Coff,
1217 loc_si: Symbol.Index,
1218 offset: u64,
1219 target_si: Symbol.Index,
1220 addend: i64,
1221 @"type": Reloc.Type,
1222) !void {
1223 const gpa = coff.base.comp.gpa;
1224 const target = target_si.get(coff);
1225 const ri: Reloc.Index = @enumFromInt(coff.relocs.items.len);
1226 (try coff.relocs.addOne(gpa)).* = .{
1227 .type = @"type",
1228 .prev = .none,
1229 .next = target.target_relocs,
1230 .loc = loc_si,
1231 .target = target_si,
1232 .unused = 0,
1233 .offset = offset,
1234 .addend = addend,
1235 };
1236 switch (target.target_relocs) {
1237 .none => {},
1238 else => |target_ri| target_ri.get(coff).prev = ri,
1239 }
1240 target.target_relocs = ri;
1241}
1242
1243pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void {
1244 _ = coff;
1245 _ = prog_node;
1246}
1247
1248pub fn updateNav(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
1249 coff.updateNavInner(pt, nav_index) catch |err| switch (err) {
1250 error.OutOfMemory,
1251 error.Overflow,
1252 error.RelocationNotByteAligned,
1253 => |e| return e,
1254 else => |e| return coff.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}),
1255 };
1256}
1257fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
1258 const zcu = pt.zcu;
1259 const gpa = zcu.gpa;
1260 const ip = &zcu.intern_pool;
1261
1262 const nav = ip.getNav(nav_index);
1263 const nav_val = nav.status.fully_resolved.val;
1264 const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) {
1265 else => .{ nav_val, false },
1266 .variable => |variable| .{ variable.init, variable.is_threadlocal },
1267 .@"extern" => return,
1268 .func => .{ .none, false },
1269 };
1270 if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
1271
1272 const nmi = try coff.navMapIndex(zcu, nav_index);
1273 const si = nmi.symbol(coff);
1274 const ni = ni: {
1275 const sym = si.get(coff);
1276 switch (sym.ni) {
1277 .none => {
1278 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1279 _ = is_threadlocal;
1280 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
1281 .alignment = pt.navAlignment(nav_index).toStdMem(),
1282 .moved = true,
1283 });
1284 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
1285 sym.ni = ni;
1286 sym.section_number = Symbol.Index.data.get(coff).section_number;
1287 },
1288 else => si.deleteLocationRelocs(coff),
1289 }
1290 assert(sym.loc_relocs == .none);
1291 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1292 break :ni sym.ni;
1293 };
1294
1295 var nw: MappedFile.Node.Writer = undefined;
1296 ni.writer(&coff.mf, gpa, &nw);
1297 defer nw.deinit();
1298 codegen.generateSymbol(
1299 &coff.base,
1300 pt,
1301 zcu.navSrcLoc(nav_index),
1302 .fromInterned(nav_init),
1303 &nw.interface,
1304 .{ .atom_index = @intFromEnum(si) },
1305 ) catch |err| switch (err) {
1306 error.WriteFailed => return error.OutOfMemory,
1307 else => |e| return e,
1308 };
1309 si.get(coff).size = @intCast(nw.interface.end);
1310 si.applyLocationRelocs(coff);
1311}
1312
1313pub fn lowerUav(
1314 coff: *Coff,
1315 pt: Zcu.PerThread,
1316 uav_val: InternPool.Index,
1317 uav_align: InternPool.Alignment,
1318 src_loc: Zcu.LazySrcLoc,
1319) !codegen.SymbolResult {
1320 const zcu = pt.zcu;
1321 const gpa = zcu.gpa;
1322
1323 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
1324 const umi = try coff.uavMapIndex(uav_val);
1325 const si = umi.symbol(coff);
1326 if (switch (si.get(coff).ni) {
1327 .none => true,
1328 else => |ni| uav_align.toStdMem().order(ni.alignment(&coff.mf)).compare(.gt),
1329 }) {
1330 const gop = coff.pending_uavs.getOrPutAssumeCapacity(umi);
1331 if (gop.found_existing) {
1332 gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
1333 } else {
1334 gop.value_ptr.* = .{
1335 .alignment = uav_align,
1336 .src_loc = src_loc,
1337 };
1338 coff.base.comp.link_const_prog_node.increaseEstimatedTotalItems(1);
1339 }
1340 }
1341 return .{ .sym_index = @intFromEnum(si) };
1342}
1343
1344pub fn updateFunc(
1345 coff: *Coff,
1346 pt: Zcu.PerThread,
1347 func_index: InternPool.Index,
1348 mir: *const codegen.AnyMir,
1349) !void {
1350 coff.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
1351 error.OutOfMemory,
1352 error.Overflow,
1353 error.RelocationNotByteAligned,
1354 error.CodegenFail,
1355 => |e| return e,
1356 else => |e| return coff.base.cgFail(
1357 pt.zcu.funcInfo(func_index).owner_nav,
1358 "linker failed to update function: {s}",
1359 .{@errorName(e)},
1360 ),
1361 };
1362}
1363fn updateFuncInner(
1364 coff: *Coff,
1365 pt: Zcu.PerThread,
1366 func_index: InternPool.Index,
1367 mir: *const codegen.AnyMir,
1368) !void {
1369 const zcu = pt.zcu;
1370 const gpa = zcu.gpa;
1371 const ip = &zcu.intern_pool;
1372 const func = zcu.funcInfo(func_index);
1373 const nav = ip.getNav(func.owner_nav);
1374
1375 const nmi = try coff.navMapIndex(zcu, func.owner_nav);
1376 const si = nmi.symbol(coff);
1377 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
1378 const ni = ni: {
1379 const sym = si.get(coff);
1380 switch (sym.ni) {
1381 .none => {
1382 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1383 const mod = zcu.navFileScope(func.owner_nav).mod.?;
1384 const target = &mod.resolved_target.result;
1385 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
1386 .alignment = switch (nav.status.fully_resolved.alignment) {
1387 .none => switch (mod.optimize_mode) {
1388 .Debug,
1389 .ReleaseSafe,
1390 .ReleaseFast,
1391 => target_util.defaultFunctionAlignment(target),
1392 .ReleaseSmall => target_util.minFunctionAlignment(target),
1393 },
1394 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
1395 }.toStdMem(),
1396 .moved = true,
1397 });
1398 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
1399 sym.ni = ni;
1400 sym.section_number = Symbol.Index.text.get(coff).section_number;
1401 },
1402 else => si.deleteLocationRelocs(coff),
1403 }
1404 assert(sym.loc_relocs == .none);
1405 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1406 break :ni sym.ni;
1407 };
1408
1409 var nw: MappedFile.Node.Writer = undefined;
1410 ni.writer(&coff.mf, gpa, &nw);
1411 defer nw.deinit();
1412 codegen.emitFunction(
1413 &coff.base,
1414 pt,
1415 zcu.navSrcLoc(func.owner_nav),
1416 func_index,
1417 @intFromEnum(si),
1418 mir,
1419 &nw.interface,
1420 .none,
1421 ) catch |err| switch (err) {
1422 error.WriteFailed => return nw.err.?,
1423 else => |e| return e,
1424 };
1425 si.get(coff).size = @intCast(nw.interface.end);
1426 si.applyLocationRelocs(coff);
1427}
1428
1429pub fn updateErrorData(coff: *Coff, pt: Zcu.PerThread) !void {
1430 coff.flushLazy(pt, .{
1431 .kind = .const_data,
1432 .index = @intCast(coff.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return),
1433 }) catch |err| switch (err) {
1434 error.OutOfMemory => return error.OutOfMemory,
1435 error.CodegenFail => return error.LinkFailure,
1436 else => |e| return coff.base.comp.link_diags.fail("updateErrorData failed {t}", .{e}),
1437 };
1438}
1439
1440pub fn flush(
1441 coff: *Coff,
1442 arena: std.mem.Allocator,
1443 tid: Zcu.PerThread.Id,
1444 prog_node: std.Progress.Node,
1445) !void {
1446 _ = arena;
1447 _ = prog_node;
1448 while (try coff.idle(tid)) {}
1449
1450 // hack for stage2_x86_64 + coff
1451 const comp = coff.base.comp;
1452 if (comp.compiler_rt_dyn_lib) |crt_file| {
1453 const gpa = comp.gpa;
1454 const compiler_rt_sub_path = try std.fs.path.join(gpa, &.{
1455 std.fs.path.dirname(coff.base.emit.sub_path) orelse "",
1456 std.fs.path.basename(crt_file.full_object_path.sub_path),
1457 });
1458 defer gpa.free(compiler_rt_sub_path);
1459 crt_file.full_object_path.root_dir.handle.copyFile(
1460 crt_file.full_object_path.sub_path,
1461 coff.base.emit.root_dir.handle,
1462 compiler_rt_sub_path,
1463 .{},
1464 ) catch |err| switch (err) {
1465 else => |e| return comp.link_diags.fail("Copy '{s}' failed: {s}", .{
1466 compiler_rt_sub_path,
1467 @errorName(e),
1468 }),
1469 };
1470 }
1471}
1472
1473pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
1474 const comp = coff.base.comp;
1475 task: {
1476 while (coff.pending_uavs.pop()) |pending_uav| {
1477 const sub_prog_node = coff.idleProgNode(
1478 tid,
1479 comp.link_const_prog_node,
1480 .{ .uav = pending_uav.key },
1481 );
1482 defer sub_prog_node.end();
1483 coff.flushUav(
1484 .{ .zcu = coff.base.comp.zcu.?, .tid = tid },
1485 pending_uav.key,
1486 pending_uav.value.alignment,
1487 pending_uav.value.src_loc,
1488 ) catch |err| switch (err) {
1489 error.OutOfMemory => return error.OutOfMemory,
1490 else => |e| return coff.base.comp.link_diags.fail(
1491 "linker failed to lower constant: {t}",
1492 .{e},
1493 ),
1494 };
1495 break :task;
1496 }
1497 if (coff.global_pending_index < coff.globals.count()) {
1498 const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
1499 const gmi: Node.GlobalMapIndex = @enumFromInt(coff.global_pending_index);
1500 coff.global_pending_index += 1;
1501 const sub_prog_node = comp.link_synth_prog_node.start(
1502 gmi.globalName(coff).name.toSlice(coff),
1503 0,
1504 );
1505 defer sub_prog_node.end();
1506 coff.flushGlobal(pt, gmi) catch |err| switch (err) {
1507 error.OutOfMemory => return error.OutOfMemory,
1508 else => |e| return coff.base.comp.link_diags.fail(
1509 "linker failed to lower constant: {t}",
1510 .{e},
1511 ),
1512 };
1513 break :task;
1514 }
1515 var lazy_it = coff.lazy.iterator();
1516 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
1517 const pt: Zcu.PerThread = .{ .zcu = coff.base.comp.zcu.?, .tid = tid };
1518 const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index };
1519 lazy.value.pending_index += 1;
1520 const kind = switch (lmr.kind) {
1521 .code => "code",
1522 .const_data => "data",
1523 };
1524 var name: [std.Progress.Node.max_name_len]u8 = undefined;
1525 const sub_prog_node = comp.link_synth_prog_node.start(
1526 std.fmt.bufPrint(&name, "lazy {s} for {f}", .{
1527 kind,
1528 Type.fromInterned(lmr.lazySymbol(coff).ty).fmt(pt),
1529 }) catch &name,
1530 0,
1531 );
1532 defer sub_prog_node.end();
1533 coff.flushLazy(pt, lmr) catch |err| switch (err) {
1534 error.OutOfMemory => return error.OutOfMemory,
1535 else => |e| return coff.base.comp.link_diags.fail(
1536 "linker failed to lower lazy {s}: {t}",
1537 .{ kind, e },
1538 ),
1539 };
1540 break :task;
1541 };
1542 while (coff.mf.updates.pop()) |ni| {
1543 const clean_moved = ni.cleanMoved(&coff.mf);
1544 const clean_resized = ni.cleanResized(&coff.mf);
1545 if (clean_moved or clean_resized) {
1546 const sub_prog_node = coff.idleProgNode(tid, coff.mf.update_prog_node, coff.getNode(ni));
1547 defer sub_prog_node.end();
1548 if (clean_moved) try coff.flushMoved(ni);
1549 if (clean_resized) try coff.flushResized(ni);
1550 break :task;
1551 } else coff.mf.update_prog_node.completeOne();
1552 }
1553 }
1554 if (coff.pending_uavs.count() > 0) return true;
1555 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
1556 if (coff.mf.updates.items.len > 0) return true;
1557 return false;
1558}
1559
1560fn idleProgNode(
1561 coff: *Coff,
1562 tid: Zcu.PerThread.Id,
1563 prog_node: std.Progress.Node,
1564 node: Node,
1565) std.Progress.Node {
1566 var name: [std.Progress.Node.max_name_len]u8 = undefined;
1567 return prog_node.start(name: switch (node) {
1568 else => |tag| @tagName(tag),
1569 .section => |si| std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
1570 .nav => |nmi| {
1571 const ip = &coff.base.comp.zcu.?.intern_pool;
1572 break :name ip.getNav(nmi.navIndex(coff)).fqn.toSlice(ip);
1573 },
1574 .uav => |umi| std.fmt.bufPrint(&name, "{f}", .{
1575 Value.fromInterned(umi.uavValue(coff)).fmtValue(.{
1576 .zcu = coff.base.comp.zcu.?,
1577 .tid = tid,
1578 }),
1579 }) catch &name,
1580 }, 0);
1581}
1582
1583fn flushUav(
1584 coff: *Coff,
1585 pt: Zcu.PerThread,
1586 umi: Node.UavMapIndex,
1587 uav_align: InternPool.Alignment,
1588 src_loc: Zcu.LazySrcLoc,
1589) !void {
1590 const zcu = pt.zcu;
1591 const gpa = zcu.gpa;
1592
1593 const uav_val = umi.uavValue(coff);
1594 const si = umi.symbol(coff);
1595 const ni = ni: {
1596 const sym = si.get(coff);
1597 switch (sym.ni) {
1598 .none => {
1599 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1600 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.data.node(coff), .{
1601 .alignment = uav_align.toStdMem(),
1602 .moved = true,
1603 });
1604 coff.nodes.appendAssumeCapacity(.{ .uav = umi });
1605 sym.ni = ni;
1606 sym.section_number = Symbol.Index.data.get(coff).section_number;
1607 },
1608 else => {
1609 if (sym.ni.alignment(&coff.mf).order(uav_align.toStdMem()).compare(.gte)) return;
1610 si.deleteLocationRelocs(coff);
1611 },
1612 }
1613 assert(sym.loc_relocs == .none);
1614 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1615 break :ni sym.ni;
1616 };
1617
1618 var nw: MappedFile.Node.Writer = undefined;
1619 ni.writer(&coff.mf, gpa, &nw);
1620 defer nw.deinit();
1621 codegen.generateSymbol(
1622 &coff.base,
1623 pt,
1624 src_loc,
1625 .fromInterned(uav_val),
1626 &nw.interface,
1627 .{ .atom_index = @intFromEnum(si) },
1628 ) catch |err| switch (err) {
1629 error.WriteFailed => return error.OutOfMemory,
1630 else => |e| return e,
1631 };
1632 si.get(coff).size = @intCast(nw.interface.end);
1633 si.applyLocationRelocs(coff);
1634}
1635
1636fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
1637 const zcu = pt.zcu;
1638 const comp = zcu.comp;
1639 const gpa = zcu.gpa;
1640 const gn = gmi.globalName(coff);
1641 if (gn.lib_name.toSlice(coff)) |lib_name| {
1642 const name = gn.name.toSlice(coff);
1643 try coff.nodes.ensureUnusedCapacity(gpa, 4);
1644 try coff.symbol_table.ensureUnusedCapacity(gpa, 1);
1645
1646 const target_endian = coff.targetEndian();
1647 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
1648 const addr_size: u64, const addr_align: std.mem.Alignment = switch (magic) {
1649 _ => unreachable,
1650 .PE32 => .{ 4, .@"4" },
1651 .@"PE32+" => .{ 8, .@"8" },
1652 };
1653
1654 const gop = try coff.import_table.dlls.getOrPutAdapted(
1655 gpa,
1656 lib_name,
1657 ImportTable.Adapter{ .coff = coff },
1658 );
1659 const import_hint_name_align: std.mem.Alignment = .@"2";
1660 if (!gop.found_existing) {
1661 errdefer _ = coff.import_table.dlls.pop();
1662 try coff.import_table.directory_table_ni.resize(
1663 &coff.mf,
1664 gpa,
1665 @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
1666 );
1667 const import_hint_name_table_len =
1668 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
1669 const idata_section_ni = Symbol.Index.idata.node(coff);
1670 const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
1671 .size = addr_size * 2,
1672 .alignment = addr_align,
1673 .moved = true,
1674 });
1675 const import_address_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
1676 .size = addr_size * 2,
1677 .alignment = addr_align,
1678 .moved = true,
1679 });
1680 const import_address_table_si = coff.addSymbolAssumeCapacity();
1681 {
1682 const import_address_table_sym = import_address_table_si.get(coff);
1683 import_address_table_sym.ni = import_address_table_ni;
1684 assert(import_address_table_sym.loc_relocs == .none);
1685 import_address_table_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1686 import_address_table_sym.section_number = Symbol.Index.idata.get(coff).section_number;
1687 }
1688 const import_hint_name_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{
1689 .size = import_hint_name_table_len,
1690 .alignment = import_hint_name_align,
1691 .moved = true,
1692 });
1693 gop.value_ptr.* = .{
1694 .import_lookup_table_ni = import_lookup_table_ni,
1695 .import_address_table_si = import_address_table_si,
1696 .import_hint_name_table_ni = import_hint_name_table_ni,
1697 .len = 0,
1698 .hint_name_len = @intCast(import_hint_name_table_len),
1699 };
1700 const import_hint_name_slice = import_hint_name_table_ni.slice(&coff.mf);
1701 @memcpy(import_hint_name_slice[0..lib_name.len], lib_name);
1702 @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll");
1703 @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0);
1704 coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @intCast(gop.index) });
1705 coff.nodes.appendAssumeCapacity(.{ .import_address_table = @intCast(gop.index) });
1706 coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @intCast(gop.index) });
1707
1708 const import_directory_table: []std.coff.ImportDirectoryEntry =
1709 @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
1710 import_directory_table[gop.index..][0..2].* = .{ .{
1711 .import_lookup_table_rva = coff.computeNodeRva(import_lookup_table_ni),
1712 .time_date_stamp = 0,
1713 .forwarder_chain = 0,
1714 .name_rva = coff.computeNodeRva(import_hint_name_table_ni),
1715 .import_address_table_rva = coff.computeNodeRva(import_address_table_ni),
1716 }, .{
1717 .import_lookup_table_rva = 0,
1718 .time_date_stamp = 0,
1719 .forwarder_chain = 0,
1720 .name_rva = 0,
1721 .import_address_table_rva = 0,
1722 } };
1723 }
1724 const import_symbol_index = gop.value_ptr.len;
1725 gop.value_ptr.len = import_symbol_index + 1;
1726 const new_symbol_table_size = addr_size * (import_symbol_index + 2);
1727 const import_hint_name_index = gop.value_ptr.hint_name_len;
1728 gop.value_ptr.hint_name_len = @intCast(
1729 import_hint_name_align.forward(import_hint_name_index + 2 + name.len + 1),
1730 );
1731 try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
1732 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
1733 try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);
1734 try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);
1735 const import_lookup_slice = gop.value_ptr.import_lookup_table_ni.slice(&coff.mf);
1736 const import_address_slice = import_address_table_ni.slice(&coff.mf);
1737 const import_hint_name_slice = gop.value_ptr.import_hint_name_table_ni.slice(&coff.mf);
1738 @memset(import_hint_name_slice[import_hint_name_index..][0..2], 0);
1739 @memcpy(import_hint_name_slice[import_hint_name_index + 2 ..][0..name.len], name);
1740 @memset(import_hint_name_slice[import_hint_name_index + 2 + name.len ..], 0);
1741 const import_hint_name_rva =
1742 coff.computeNodeRva(gop.value_ptr.import_hint_name_table_ni) + import_hint_name_index;
1743 switch (magic) {
1744 _ => unreachable,
1745 inline .PE32, .@"PE32+" => |ct_magic| {
1746 const Addr = switch (ct_magic) {
1747 _ => comptime unreachable,
1748 .PE32 => u32,
1749 .@"PE32+" => u64,
1750 };
1751 const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
1752 const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
1753 const import_hint_name_rvas: [2]Addr = .{
1754 std.mem.nativeTo(Addr, @intCast(import_hint_name_rva), target_endian),
1755 std.mem.nativeTo(Addr, 0, target_endian),
1756 };
1757 import_lookup_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
1758 import_address_table[import_symbol_index..][0..2].* = import_hint_name_rvas;
1759 },
1760 }
1761 const si = gmi.symbol(coff);
1762 const sym = si.get(coff);
1763 sym.section_number = Symbol.Index.text.get(coff).section_number;
1764 assert(sym.loc_relocs == .none);
1765 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1766 switch (coff.targetLoad(&coff.headerPtr().machine)) {
1767 else => |tag| @panic(@tagName(tag)),
1768 .AMD64 => {
1769 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
1770 const target = &comp.root_mod.resolved_target.result;
1771 const ni = try coff.mf.addLastChildNode(gpa, Symbol.Index.text.node(coff), .{
1772 .alignment = switch (comp.root_mod.optimize_mode) {
1773 .Debug,
1774 .ReleaseSafe,
1775 .ReleaseFast,
1776 => target_util.defaultFunctionAlignment(target),
1777 .ReleaseSmall => target_util.minFunctionAlignment(target),
1778 }.toStdMem(),
1779 .size = init.len,
1780 });
1781 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
1782 sym.ni = ni;
1783 sym.size = init.len;
1784 try coff.addReloc(
1785 si,
1786 init.len - 4,
1787 gop.value_ptr.import_address_table_si,
1788 @intCast(addr_size * import_symbol_index),
1789 .{ .AMD64 = .REL32 },
1790 );
1791 },
1792 }
1793 coff.nodes.appendAssumeCapacity(.{ .global = gmi });
1794 sym.rva = coff.computeNodeRva(sym.ni);
1795 si.applyLocationRelocs(coff);
1796 }
1797}
1798
1799fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
1800 const zcu = pt.zcu;
1801 const gpa = zcu.gpa;
1802
1803 const lazy = lmr.lazySymbol(coff);
1804 const si = lmr.symbol(coff);
1805 const ni = ni: {
1806 const sym = si.get(coff);
1807 switch (sym.ni) {
1808 .none => {
1809 try coff.nodes.ensureUnusedCapacity(gpa, 1);
1810 const sec_si: Symbol.Index = switch (lazy.kind) {
1811 .code => .text,
1812 .const_data => .rdata,
1813 };
1814 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ .moved = true });
1815 coff.nodes.appendAssumeCapacity(switch (lazy.kind) {
1816 .code => .{ .lazy_code = @enumFromInt(lmr.index) },
1817 .const_data => .{ .lazy_const_data = @enumFromInt(lmr.index) },
1818 });
1819 sym.ni = ni;
1820 sym.section_number = sec_si.get(coff).section_number;
1821 },
1822 else => si.deleteLocationRelocs(coff),
1823 }
1824 assert(sym.loc_relocs == .none);
1825 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
1826 break :ni sym.ni;
1827 };
1828
1829 var required_alignment: InternPool.Alignment = .none;
1830 var nw: MappedFile.Node.Writer = undefined;
1831 ni.writer(&coff.mf, gpa, &nw);
1832 defer nw.deinit();
1833 try codegen.generateLazySymbol(
1834 &coff.base,
1835 pt,
1836 Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded,
1837 lazy,
1838 &required_alignment,
1839 &nw.interface,
1840 .none,
1841 .{ .atom_index = @intFromEnum(si) },
1842 );
1843 si.get(coff).size = @intCast(nw.interface.end);
1844 si.applyLocationRelocs(coff);
1845}
1846
1847fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
1848 const node = coff.getNode(ni);
1849 switch (node) {
1850 else => |tag| @panic(@tagName(tag)),
1851 .section => |si| return coff.targetStore(
1852 &si.get(coff).section_number.header(coff).pointer_to_raw_data,
1853 @intCast(ni.fileLocation(&coff.mf, false).offset),
1854 ),
1855 .import_directory_table => {},
1856 .import_lookup_table => |import_directory_table_index| {
1857 const import_directory_table: []std.coff.ImportDirectoryEntry =
1858 @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
1859 const import_directory_entry = &import_directory_table[import_directory_table_index];
1860 coff.targetStore(&import_directory_entry.import_lookup_table_rva, coff.computeNodeRva(ni));
1861 },
1862 .import_address_table => |import_directory_table_index| {
1863 const import_directory_table: []std.coff.ImportDirectoryEntry =
1864 @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
1865 const import_directory_entry = &import_directory_table[import_directory_table_index];
1866 coff.targetStore(&import_directory_entry.import_lookup_table_rva, coff.computeNodeRva(ni));
1867 const import_address_table_si =
1868 coff.import_table.dlls.values()[import_directory_table_index].import_address_table_si;
1869 import_address_table_si.flushMoved(coff);
1870 coff.targetStore(
1871 &import_directory_entry.import_address_table_rva,
1872 import_address_table_si.get(coff).rva,
1873 );
1874 },
1875 .import_hint_name_table => |import_directory_table_index| {
1876 const target_endian = coff.targetEndian();
1877 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
1878 const import_directory_table: []std.coff.ImportDirectoryEntry =
1879 @ptrCast(@alignCast(coff.import_table.directory_table_ni.slice(&coff.mf)));
1880 const import_directory_entry = &import_directory_table[import_directory_table_index];
1881 const import_hint_name_rva = coff.computeNodeRva(ni);
1882 coff.targetStore(&import_directory_entry.name_rva, import_hint_name_rva);
1883 const import_entry = &coff.import_table.dlls.values()[import_directory_table_index];
1884 const import_lookup_slice = import_entry.import_lookup_table_ni.slice(&coff.mf);
1885 const import_address_slice =
1886 import_entry.import_address_table_si.node(coff).slice(&coff.mf);
1887 const import_hint_name_slice = ni.slice(&coff.mf);
1888 const import_hint_name_align = ni.alignment(&coff.mf);
1889 var import_hint_name_index: u32 = 0;
1890 for (0..import_entry.len) |import_symbol_index| {
1891 import_hint_name_index = @intCast(import_hint_name_align.forward(
1892 std.mem.indexOfScalarPos(
1893 u8,
1894 import_hint_name_slice,
1895 import_hint_name_index,
1896 0,
1897 ).? + 1,
1898 ));
1899 switch (magic) {
1900 _ => unreachable,
1901 inline .PE32, .@"PE32+" => |ct_magic| {
1902 const Addr = switch (ct_magic) {
1903 _ => comptime unreachable,
1904 .PE32 => u32,
1905 .@"PE32+" => u64,
1906 };
1907 const import_lookup_table: []Addr = @ptrCast(@alignCast(import_lookup_slice));
1908 const import_address_table: []Addr = @ptrCast(@alignCast(import_address_slice));
1909 const rva = std.mem.nativeTo(
1910 Addr,
1911 import_hint_name_rva + import_hint_name_index,
1912 target_endian,
1913 );
1914 import_lookup_table[import_symbol_index] = rva;
1915 import_address_table[import_symbol_index] = rva;
1916 },
1917 }
1918 import_hint_name_index += 2;
1919 }
1920 },
1921 inline .global,
1922 .nav,
1923 .uav,
1924 .lazy_code,
1925 .lazy_const_data,
1926 => |mi| mi.symbol(coff).flushMoved(coff),
1927 }
1928 try ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
1929}
1930
1931fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
1932 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
1933 const node = coff.getNode(ni);
1934 switch (node) {
1935 else => |tag| @panic(@tagName(tag)),
1936 .file => {},
1937 .header => {
1938 switch (coff.optionalHeaderPtr()) {
1939 inline else => |optional_header| coff.targetStore(
1940 &optional_header.size_of_headers,
1941 @intCast(size),
1942 ),
1943 }
1944 if (size > coff.section_table.items[0].get(coff).rva) try coff.virtualSlide(
1945 0,
1946 std.mem.alignForward(
1947 u32,
1948 @intCast(size * 4),
1949 coff.optionalHeaderField(.section_alignment),
1950 ),
1951 );
1952 },
1953 .section_table => {},
1954 .section => |si| {
1955 const sym = si.get(coff);
1956 const section_table = coff.sectionTableSlice();
1957 const section_index = sym.section_number.toIndex();
1958 const section = &section_table[section_index];
1959 coff.targetStore(&section.size_of_raw_data, @intCast(size));
1960 if (size > coff.targetLoad(&section.virtual_size)) {
1961 const virtual_size = std.mem.alignForward(
1962 u32,
1963 @intCast(size * 4),
1964 coff.optionalHeaderField(.section_alignment),
1965 );
1966 coff.targetStore(&section.virtual_size, virtual_size);
1967 if (sym.data_directory) |data_directory|
1968 coff.dataDirectoriesSlice()[@intFromEnum(data_directory)].size =
1969 section.virtual_size;
1970 try coff.virtualSlide(section_index + 1, sym.rva + virtual_size);
1971 }
1972 },
1973 .import_directory_table,
1974 .import_lookup_table,
1975 .import_address_table,
1976 .import_hint_name_table,
1977 .global,
1978 .nav,
1979 .uav,
1980 .lazy_code,
1981 .lazy_const_data,
1982 => {},
1983 }
1984}
1985
1986fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
1987 const section_table = coff.sectionTableSlice();
1988 var rva = start_rva;
1989 for (
1990 coff.section_table.items[start_section_index..],
1991 section_table[start_section_index..],
1992 ) |section_si, *section| {
1993 const section_sym = section_si.get(coff);
1994 section_sym.rva = rva;
1995 coff.targetStore(&section.virtual_address, rva);
1996 if (section_sym.data_directory) |data_directory|
1997 coff.dataDirectoriesSlice()[@intFromEnum(data_directory)].virtual_address =
1998 section.virtual_address;
1999 try section_sym.ni.childrenMoved(coff.base.comp.gpa, &coff.mf);
2000 rva += coff.targetLoad(&section.virtual_size);
2001 }
2002 switch (coff.optionalHeaderPtr()) {
2003 inline else => |optional_header| coff.targetStore(
2004 &optional_header.size_of_image,
2005 @intCast(rva),
2006 ),
2007 }
2008}
2009
2010pub fn updateExports(
2011 coff: *Coff,
2012 pt: Zcu.PerThread,
2013 exported: Zcu.Exported,
2014 export_indices: []const Zcu.Export.Index,
2015) !void {
2016 return coff.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
2017 error.OutOfMemory => error.OutOfMemory,
2018 error.LinkFailure => error.AnalysisFail,
2019 };
2020}
2021fn updateExportsInner(
2022 coff: *Coff,
2023 pt: Zcu.PerThread,
2024 exported: Zcu.Exported,
2025 export_indices: []const Zcu.Export.Index,
2026) !void {
2027 const zcu = pt.zcu;
2028 const gpa = zcu.gpa;
2029 const ip = &zcu.intern_pool;
2030
2031 switch (exported) {
2032 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
2033 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
2034 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
2035 Value.fromInterned(uav).fmtValue(pt),
2036 }),
2037 }
2038 try coff.symbol_table.ensureUnusedCapacity(gpa, export_indices.len);
2039 const exported_si: Symbol.Index = switch (exported) {
2040 .nav => |nav| try coff.navSymbol(zcu, nav),
2041 .uav => |uav| @enumFromInt(switch (try coff.lowerUav(
2042 pt,
2043 uav,
2044 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
2045 export_indices[0].ptr(zcu).src,
2046 )) {
2047 .sym_index => |si| si,
2048 .fail => |em| {
2049 defer em.destroy(gpa);
2050 return coff.base.comp.link_diags.fail("{s}", .{em.msg});
2051 },
2052 }),
2053 };
2054 while (try coff.idle(pt.tid)) {}
2055 const exported_ni = exported_si.node(coff);
2056 const exported_sym = exported_si.get(coff);
2057 for (export_indices) |export_index| {
2058 const @"export" = export_index.ptr(zcu);
2059 const export_si = try coff.globalSymbol(@"export".opts.name.toSlice(ip), null);
2060 const export_sym = export_si.get(coff);
2061 export_sym.ni = exported_ni;
2062 export_sym.rva = exported_sym.rva;
2063 export_sym.size = exported_sym.size;
2064 export_sym.section_number = exported_sym.section_number;
2065 export_si.applyTargetRelocs(coff);
2066 if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) {
2067 coff.entry_hack = exported_si;
2068 coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
2069 }
2070 }
2071}
2072
2073pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
2074 _ = coff;
2075 _ = exported;
2076 _ = name;
2077}
2078
2079pub fn dump(coff: *Coff, tid: Zcu.PerThread.Id) void {
2080 const w = std.debug.lockStderrWriter(&.{});
2081 defer std.debug.unlockStderrWriter();
2082 coff.printNode(tid, w, .root, 0) catch {};
2083}
2084
2085pub fn printNode(
2086 coff: *Coff,
2087 tid: Zcu.PerThread.Id,
2088 w: *std.Io.Writer,
2089 ni: MappedFile.Node.Index,
2090 indent: usize,
2091) !void {
2092 const node = coff.getNode(ni);
2093 try w.splatByteAll(' ', indent);
2094 try w.writeAll(@tagName(node));
2095 switch (node) {
2096 else => {},
2097 .section => |si| try w.print("({s})", .{
2098 std.mem.sliceTo(&si.get(coff).section_number.header(coff).name, 0),
2099 }),
2100 .import_lookup_table,
2101 .import_address_table,
2102 .import_hint_name_table,
2103 => |import_directory_table_index| try w.print("({s})", .{
2104 std.mem.sliceTo(coff.import_table.dlls.values()[import_directory_table_index]
2105 .import_hint_name_table_ni.sliceConst(&coff.mf), 0),
2106 }),
2107 .global => |gmi| {
2108 const gn = gmi.globalName(coff);
2109 try w.writeByte('(');
2110 if (gn.lib_name.toSlice(coff)) |lib_name| try w.print("{s}.dll, ", .{lib_name});
2111 try w.print("{s})", .{gn.name.toSlice(coff)});
2112 },
2113 .nav => |nmi| {
2114 const zcu = coff.base.comp.zcu.?;
2115 const ip = &zcu.intern_pool;
2116 const nav = ip.getNav(nmi.navIndex(coff));
2117 try w.print("({f}, {f})", .{
2118 Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
2119 nav.fqn.fmt(ip),
2120 });
2121 },
2122 .uav => |umi| {
2123 const zcu = coff.base.comp.zcu.?;
2124 const val: Value = .fromInterned(umi.uavValue(coff));
2125 try w.print("({f}, {f})", .{
2126 val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
2127 val.fmtValue(.{ .zcu = zcu, .tid = tid }),
2128 });
2129 },
2130 inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
2131 Type.fromInterned(lmi.lazySymbol(coff).ty).fmt(.{
2132 .zcu = coff.base.comp.zcu.?,
2133 .tid = tid,
2134 }),
2135 }),
2136 }
2137 {
2138 const mf_node = &coff.mf.nodes.items[@intFromEnum(ni)];
2139 const off, const size = mf_node.location().resolve(&coff.mf);
2140 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{
2141 @intFromEnum(ni),
2142 off,
2143 size,
2144 mf_node.flags.alignment.toByteUnits(),
2145 if (mf_node.flags.fixed) " fixed" else "",
2146 if (mf_node.flags.moved) " moved" else "",
2147 if (mf_node.flags.resized) " resized" else "",
2148 if (mf_node.flags.has_content) " has_content" else "",
2149 });
2150 }
2151 var leaf = true;
2152 var child_it = ni.children(&coff.mf);
2153 while (child_it.next()) |child_ni| {
2154 leaf = false;
2155 try coff.printNode(tid, w, child_ni, indent + 1);
2156 }
2157 if (leaf) {
2158 const file_loc = ni.fileLocation(&coff.mf, false);
2159 if (file_loc.size == 0) return;
2160 var address = file_loc.offset;
2161 const line_len = 0x10;
2162 var line_it = std.mem.window(
2163 u8,
2164 coff.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
2165 line_len,
2166 line_len,
2167 );
2168 while (line_it.next()) |line_bytes| : (address += line_len) {
2169 try w.splatByteAll(' ', indent + 1);
2170 try w.print("{x:0>8} ", .{address});
2171 for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
2172 try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
2173 for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
2174 try w.writeByte('\n');
2175 }
2176 }
2177}
2178
2179const assert = std.debug.assert;
2180const builtin = @import("builtin");
2181const codegen = @import("../codegen.zig");
2182const Compilation = @import("../Compilation.zig");
2183const Coff = @This();
2184const InternPool = @import("../InternPool.zig");
2185const link = @import("../link.zig");
2186const log = std.log.scoped(.link);
2187const MappedFile = @import("MappedFile.zig");
2188const native_endian = builtin.cpu.arch.endian();
2189const std = @import("std");
2190const target_util = @import("../target.zig");
2191const Type = @import("../Type.zig");
2192const Value = @import("../Value.zig");
2193const Zcu = @import("../Zcu.zig");
src/link/Elf2.zig+440-456
......@@ -1,6 +1,8 @@
11base: link.File,
22mf: MappedFile,
3known: Node.Known,
34nodes: std.MultiArrayList(Node),
5phdrs: std.ArrayList(MappedFile.Node.Index),
46symtab: std.ArrayList(Symbol),
57shstrtab: StringTable,
68strtab: StringTable,
......@@ -85,23 +87,15 @@ pub const Node = union(enum) {
8587 }
8688 };
8789
88 pub const Tag = @typeInfo(Node).@"union".tag_type.?;
89
90 const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
91 const known = known: {
92 const Known = enum {
93 file,
94 seg_rodata,
95 ehdr,
96 phdr,
97 shdr,
98 seg_text,
99 seg_data,
100 };
101 var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
102 for (@typeInfo(Known).@"enum".fields) |field|
103 @field(mut_known, field.name) = @enumFromInt(field.value);
104 break :known mut_known;
90 pub const Known = struct {
91 pub const rodata: MappedFile.Node.Index = @enumFromInt(1);
92 pub const ehdr: MappedFile.Node.Index = @enumFromInt(2);
93 pub const phdr: MappedFile.Node.Index = @enumFromInt(3);
94 pub const shdr: MappedFile.Node.Index = @enumFromInt(4);
95 pub const text: MappedFile.Node.Index = @enumFromInt(5);
96 pub const data: MappedFile.Node.Index = @enumFromInt(6);
97
98 tls: MappedFile.Node.Index,
10599 };
106100
107101 comptime {
......@@ -231,6 +225,21 @@ pub const Symbol = struct {
231225 }
232226 }
233227
228 pub fn flushMoved(si: Symbol.Index, elf: *Elf) void {
229 const value = elf.computeNodeVAddr(si.node(elf));
230 switch (elf.symPtr(si)) {
231 inline else => |sym, class| {
232 elf.targetStore(&sym.value, @intCast(value));
233 if (si == elf.entry_hack) {
234 @branchHint(.unlikely);
235 @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value;
236 }
237 },
238 }
239 si.applyLocationRelocs(elf);
240 si.applyTargetRelocs(elf);
241 }
242
234243 pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void {
235244 for (elf.relocs.items[@intFromEnum(si.get(elf).loc_relocs)..]) |*reloc| {
236245 if (reloc.loc != si) break;
......@@ -290,8 +299,8 @@ pub const Reloc = extern struct {
290299 };
291300
292301 pub fn apply(reloc: *const Reloc, elf: *Elf) void {
293 const target_endian = elf.targetEndian();
294 switch (reloc.loc.get(elf).ni) {
302 const loc_ni = reloc.loc.get(elf).ni;
303 switch (loc_ni) {
295304 .none => return,
296305 else => |ni| if (ni.hasMoved(&elf.mf)) return,
297306 }
......@@ -299,68 +308,47 @@ pub const Reloc = extern struct {
299308 .none => return,
300309 else => |ni| if (ni.hasMoved(&elf.mf)) return,
301310 }
302 switch (elf.shdrSlice()) {
303 inline else => |shdr, class| {
304 const sym = @field(elf.symSlice(), @tagName(class));
305 const loc_sym = &sym[@intFromEnum(reloc.loc)];
306 const loc_shndx =
307 std.mem.toNative(@TypeOf(loc_sym.shndx), loc_sym.shndx, target_endian);
311 const loc_slice = loc_ni.slice(&elf.mf)[@intCast(reloc.offset)..];
312 const target_endian = elf.targetEndian();
313 switch (elf.symtabSlice()) {
314 inline else => |symtab, class| {
315 const loc_sym = &symtab[@intFromEnum(reloc.loc)];
316 const loc_shndx = elf.targetLoad(&loc_sym.shndx);
308317 assert(loc_shndx != std.elf.SHN_UNDEF);
309 const loc_sh = &shdr[loc_shndx];
310 const loc_value = std.mem.toNative(
311 @TypeOf(loc_sym.value),
312 loc_sym.value,
313 target_endian,
314 ) + reloc.offset;
315 const loc_sh_addr =
316 std.mem.toNative(@TypeOf(loc_sh.addr), loc_sh.addr, target_endian);
317 const loc_sh_offset =
318 std.mem.toNative(@TypeOf(loc_sh.offset), loc_sh.offset, target_endian);
319 const loc_file_offset: usize = @intCast(loc_value - loc_sh_addr + loc_sh_offset);
320 const target_sym = &sym[@intFromEnum(reloc.target)];
321 const target_value = std.mem.toNative(
322 @TypeOf(target_sym.value),
323 target_sym.value,
324 target_endian,
325 ) +% @as(u64, @bitCast(reloc.addend));
318 const loc_value = elf.targetLoad(&loc_sym.value) + reloc.offset;
319 const target_sym = &symtab[@intFromEnum(reloc.target)];
320 const target_value =
321 elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
326322 switch (elf.ehdrField(.machine)) {
327323 else => |machine| @panic(@tagName(machine)),
328324 .X86_64 => switch (reloc.type.X86_64) {
329325 else => |kind| @panic(@tagName(kind)),
330326 .@"64" => std.mem.writeInt(
331327 u64,
332 elf.mf.contents[loc_file_offset..][0..8],
328 loc_slice[0..8],
333329 target_value,
334330 target_endian,
335331 ),
336332 .PC32 => std.mem.writeInt(
337333 i32,
338 elf.mf.contents[loc_file_offset..][0..4],
334 loc_slice[0..4],
339335 @intCast(@as(i64, @bitCast(target_value -% loc_value))),
340336 target_endian,
341337 ),
342338 .@"32" => std.mem.writeInt(
343339 u32,
344 elf.mf.contents[loc_file_offset..][0..4],
340 loc_slice[0..4],
345341 @intCast(target_value),
346342 target_endian,
347343 ),
348344 .TPOFF32 => {
349345 const phdr = @field(elf.phdrSlice(), @tagName(class));
350 const ph = &phdr[4];
351 assert(std.mem.toNative(
352 @TypeOf(ph.type),
353 ph.type,
354 target_endian,
355 ) == std.elf.PT_TLS);
346 const ph = &phdr[elf.getNode(elf.known.tls).segment];
347 assert(elf.targetLoad(&ph.type) == std.elf.PT_TLS);
356348 std.mem.writeInt(
357349 i32,
358 elf.mf.contents[loc_file_offset..][0..4],
359 @intCast(@as(i64, @bitCast(target_value -% std.mem.toNative(
360 @TypeOf(ph.memsz),
361 ph.memsz,
362 target_endian,
363 )))),
350 loc_slice[0..4],
351 @intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))),
364352 target_endian,
365353 );
366354 },
......@@ -475,7 +463,11 @@ fn create(
475463 .stack_size = 0,
476464 },
477465 .mf = try .init(file, comp.gpa),
466 .known = .{
467 .tls = .none,
468 },
478469 .nodes = .empty,
470 .phdrs = .empty,
479471 .symtab = .empty,
480472 .shstrtab = .{
481473 .map = .empty,
......@@ -488,7 +480,7 @@ fn create(
488480 .globals = .empty,
489481 .navs = .empty,
490482 .uavs = .empty,
491 .lazy = .initFill(.{
483 .lazy = comptime .initFill(.{
492484 .map = .empty,
493485 .pending_index = 0,
494486 }),
......@@ -498,18 +490,7 @@ fn create(
498490 };
499491 errdefer elf.deinit();
500492
501 switch (class) {
502 .NONE, _ => unreachable,
503 inline else => |ct_class| try elf.initHeaders(
504 ct_class,
505 data,
506 osabi,
507 @"type",
508 machine,
509 maybe_interp,
510 ),
511 }
512
493 try elf.initHeaders(class, data, osabi, @"type", machine, maybe_interp);
513494 return elf;
514495}
515496
......@@ -517,6 +498,7 @@ pub fn deinit(elf: *Elf) void {
517498 const gpa = elf.base.comp.gpa;
518499 elf.mf.deinit(gpa);
519500 elf.nodes.deinit(gpa);
501 elf.phdrs.deinit(gpa);
520502 elf.symtab.deinit(gpa);
521503 elf.shstrtab.map.deinit(gpa);
522504 elf.strtab.map.deinit(gpa);
......@@ -531,7 +513,7 @@ pub fn deinit(elf: *Elf) void {
531513
532514fn initHeaders(
533515 elf: *Elf,
534 comptime class: std.elf.CLASS,
516 class: std.elf.CLASS,
535517 data: std.elf.DATA,
536518 osabi: std.elf.OSABI,
537519 @"type": std.elf.ET,
......@@ -540,16 +522,10 @@ fn initHeaders(
540522) !void {
541523 const comp = elf.base.comp;
542524 const gpa = comp.gpa;
543 const ElfN = switch (class) {
544 .NONE, _ => comptime unreachable,
545 .@"32" => std.elf.Elf32,
546 .@"64" => std.elf.Elf64,
547 };
548 const addr_align: std.mem.Alignment = comptime .fromByteUnits(@sizeOf(ElfN.Addr));
549 const target_endian: std.builtin.Endian = switch (data) {
525 const addr_align: std.mem.Alignment = switch (class) {
550526 .NONE, _ => unreachable,
551 .@"2LSB" => .little,
552 .@"2MSB" => .big,
527 .@"32" => .@"4",
528 .@"64" => .@"8",
553529 };
554530
555531 var phnum: u32 = 0;
......@@ -570,218 +546,257 @@ fn initHeaders(
570546 break :phndx phnum;
571547 } else undefined;
572548
573 try elf.nodes.ensureTotalCapacity(gpa, Node.known_count);
549 const expected_nodes_len = 15;
550 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
551 try elf.phdrs.resize(gpa, phnum);
574552 elf.nodes.appendAssumeCapacity(.file);
575553
576 const seg_rodata_ni = Node.known.seg_rodata;
577 assert(seg_rodata_ni == try elf.mf.addOnlyChildNode(gpa, .root, .{
554 assert(Node.Known.rodata == try elf.mf.addOnlyChildNode(gpa, .root, .{
578555 .alignment = elf.mf.flags.block_size,
579556 .fixed = true,
580557 .moved = true,
558 .bubbles_moved = false,
581559 }));
582560 elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });
561 elf.phdrs.items[rodata_phndx] = Node.Known.rodata;
583562
584 const ehdr_ni = Node.known.ehdr;
585 assert(ehdr_ni == try elf.mf.addOnlyChildNode(gpa, seg_rodata_ni, .{
586 .size = @sizeOf(ElfN.Ehdr),
587 .alignment = addr_align,
588 .fixed = true,
589 }));
590 elf.nodes.appendAssumeCapacity(.ehdr);
591 {
592 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(ehdr_ni.slice(&elf.mf)));
593 const EI = std.elf.EI;
594 @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC);
595 ehdr.ident[EI.CLASS] = @intFromEnum(class);
596 ehdr.ident[EI.DATA] = @intFromEnum(data);
597 ehdr.ident[EI.VERSION] = 1;
598 ehdr.ident[EI.OSABI] = @intFromEnum(osabi);
599 ehdr.ident[EI.ABIVERSION] = 0;
600 @memset(ehdr.ident[EI.PAD..], 0);
601 ehdr.type = @"type";
602 ehdr.machine = machine;
603 ehdr.version = 1;
604 ehdr.entry = 0;
605 ehdr.phoff = 0;
606 ehdr.shoff = 0;
607 ehdr.flags = 0;
608 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
609 ehdr.phentsize = @sizeOf(ElfN.Phdr);
610 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
611 ehdr.shentsize = @sizeOf(ElfN.Shdr);
612 ehdr.shnum = 1;
613 ehdr.shstrndx = 0;
614 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
563 switch (class) {
564 .NONE, _ => unreachable,
565 inline else => |ct_class| {
566 const ElfN = switch (ct_class) {
567 .NONE, _ => comptime unreachable,
568 .@"32" => std.elf.Elf32,
569 .@"64" => std.elf.Elf64,
570 };
571
572 assert(Node.Known.ehdr == try elf.mf.addOnlyChildNode(gpa, Node.Known.rodata, .{
573 .size = @sizeOf(ElfN.Ehdr),
574 .alignment = addr_align,
575 .fixed = true,
576 }));
577 elf.nodes.appendAssumeCapacity(.ehdr);
578
579 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(Node.Known.ehdr.slice(&elf.mf)));
580 const EI = std.elf.EI;
581 @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC);
582 ehdr.ident[EI.CLASS] = @intFromEnum(class);
583 ehdr.ident[EI.DATA] = @intFromEnum(data);
584 ehdr.ident[EI.VERSION] = 1;
585 ehdr.ident[EI.OSABI] = @intFromEnum(osabi);
586 ehdr.ident[EI.ABIVERSION] = 0;
587 @memset(ehdr.ident[EI.PAD..], 0);
588 ehdr.type = @"type";
589 ehdr.machine = machine;
590 ehdr.version = 1;
591 ehdr.entry = 0;
592 ehdr.phoff = 0;
593 ehdr.shoff = 0;
594 ehdr.flags = 0;
595 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
596 ehdr.phentsize = @sizeOf(ElfN.Phdr);
597 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
598 ehdr.shentsize = @sizeOf(ElfN.Shdr);
599 ehdr.shnum = 1;
600 ehdr.shstrndx = std.elf.SHN_UNDEF;
601 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
602 },
615603 }
616604
617 const phdr_ni = Node.known.phdr;
618 assert(phdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
619 .size = @sizeOf(ElfN.Phdr) * phnum,
605 assert(Node.Known.phdr == try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
606 .size = elf.ehdrField(.phentsize) * elf.ehdrField(.phnum),
620607 .alignment = addr_align,
621608 .moved = true,
622609 .resized = true,
610 .bubbles_moved = false,
623611 }));
624612 elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });
613 elf.phdrs.items[phdr_phndx] = Node.Known.phdr;
625614
626 const shdr_ni = Node.known.shdr;
627 assert(shdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
628 .size = @sizeOf(ElfN.Shdr),
615 assert(Node.Known.shdr == try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
616 .size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum),
629617 .alignment = addr_align,
630618 }));
631619 elf.nodes.appendAssumeCapacity(.shdr);
632620
633 const seg_text_ni = Node.known.seg_text;
634 assert(seg_text_ni == try elf.mf.addLastChildNode(gpa, .root, .{
621 assert(Node.Known.text == try elf.mf.addLastChildNode(gpa, .root, .{
635622 .alignment = elf.mf.flags.block_size,
636623 .moved = true,
624 .bubbles_moved = false,
637625 }));
638626 elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });
627 elf.phdrs.items[text_phndx] = Node.Known.text;
639628
640 const seg_data_ni = Node.known.seg_data;
641 assert(seg_data_ni == try elf.mf.addLastChildNode(gpa, .root, .{
629 assert(Node.Known.data == try elf.mf.addLastChildNode(gpa, .root, .{
642630 .alignment = elf.mf.flags.block_size,
643631 .moved = true,
632 .bubbles_moved = false,
644633 }));
645634 elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });
635 elf.phdrs.items[data_phndx] = Node.Known.data;
646636
647 assert(elf.nodes.len == Node.known_count);
648
649 {
650 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(phdr_ni.slice(&elf.mf)));
651 const ph_phdr = &phdr[phdr_phndx];
652 ph_phdr.* = .{
653 .type = std.elf.PT_PHDR,
654 .offset = 0,
655 .vaddr = 0,
656 .paddr = 0,
657 .filesz = 0,
658 .memsz = 0,
659 .flags = .{ .R = true },
660 .@"align" = @intCast(phdr_ni.alignment(&elf.mf).toByteUnits()),
661 };
662 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
637 var ph_vaddr: u32 = switch (elf.ehdrField(.type)) {
638 else => 0,
639 .EXEC => switch (elf.ehdrField(.machine)) {
640 .@"386" => 0x400000,
641 .AARCH64, .X86_64 => 0x200000,
642 .PPC, .PPC64 => 0x10000000,
643 .S390, .S390_OLD => 0x1000000,
644 .OLD_SPARCV9, .SPARCV9 => 0x100000,
645 else => 0x10000,
646 },
647 };
648 switch (class) {
649 .NONE, _ => unreachable,
650 inline else => |ct_class| {
651 const ElfN = switch (ct_class) {
652 .NONE, _ => comptime unreachable,
653 .@"32" => std.elf.Elf32,
654 .@"64" => std.elf.Elf64,
655 };
656 const target_endian = elf.targetEndian();
663657
664 if (maybe_interp) |_| {
665 const ph_interp = &phdr[interp_phndx];
666 ph_interp.* = .{
667 .type = std.elf.PT_INTERP,
658 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(Node.Known.phdr.slice(&elf.mf)));
659 const ph_phdr = &phdr[phdr_phndx];
660 ph_phdr.* = .{
661 .type = std.elf.PT_PHDR,
668662 .offset = 0,
669663 .vaddr = 0,
670664 .paddr = 0,
671665 .filesz = 0,
672666 .memsz = 0,
673667 .flags = .{ .R = true },
674 .@"align" = 1,
668 .@"align" = @intCast(Node.Known.phdr.alignment(&elf.mf).toByteUnits()),
675669 };
676 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
677 }
678
679 const ph_rodata = &phdr[rodata_phndx];
680 ph_rodata.* = .{
681 .type = std.elf.PT_NULL,
682 .offset = 0,
683 .vaddr = 0,
684 .paddr = 0,
685 .filesz = 0,
686 .memsz = 0,
687 .flags = .{ .R = true },
688 .@"align" = @intCast(seg_rodata_ni.alignment(&elf.mf).toByteUnits()),
689 };
690 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
691
692 const ph_text = &phdr[text_phndx];
693 ph_text.* = .{
694 .type = std.elf.PT_NULL,
695 .offset = 0,
696 .vaddr = 0,
697 .paddr = 0,
698 .filesz = 0,
699 .memsz = 0,
700 .flags = .{ .R = true, .X = true },
701 .@"align" = @intCast(seg_text_ni.alignment(&elf.mf).toByteUnits()),
702 };
703 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
704
705 const ph_data = &phdr[data_phndx];
706 ph_data.* = .{
707 .type = std.elf.PT_NULL,
708 .offset = 0,
709 .vaddr = 0,
710 .paddr = 0,
711 .filesz = 0,
712 .memsz = 0,
713 .flags = .{ .R = true, .W = true },
714 .@"align" = @intCast(seg_data_ni.alignment(&elf.mf).toByteUnits()),
715 };
716 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
670 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
671
672 if (maybe_interp) |_| {
673 const ph_interp = &phdr[interp_phndx];
674 ph_interp.* = .{
675 .type = std.elf.PT_INTERP,
676 .offset = 0,
677 .vaddr = 0,
678 .paddr = 0,
679 .filesz = 0,
680 .memsz = 0,
681 .flags = .{ .R = true },
682 .@"align" = 1,
683 };
684 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
685 }
717686
718 if (comp.config.any_non_single_threaded) {
719 const ph_tls = &phdr[tls_phndx];
720 ph_tls.* = .{
721 .type = std.elf.PT_TLS,
687 _, const rodata_size = Node.Known.rodata.location(&elf.mf).resolve(&elf.mf);
688 const ph_rodata = &phdr[rodata_phndx];
689 ph_rodata.* = .{
690 .type = std.elf.PT_NULL,
722691 .offset = 0,
723 .vaddr = 0,
724 .paddr = 0,
725 .filesz = 0,
726 .memsz = 0,
692 .vaddr = ph_vaddr,
693 .paddr = ph_vaddr,
694 .filesz = @intCast(rodata_size),
695 .memsz = @intCast(rodata_size),
727696 .flags = .{ .R = true },
728 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
697 .@"align" = @intCast(Node.Known.rodata.alignment(&elf.mf).toByteUnits()),
729698 };
730 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
731 }
699 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
700 ph_vaddr += @intCast(rodata_size);
732701
733 const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(shdr_ni.slice(&elf.mf)));
734 sh_null.* = .{
735 .name = try elf.string(.shstrtab, ""),
736 .type = std.elf.SHT_NULL,
737 .flags = .{ .shf = .{} },
738 .addr = 0,
739 .offset = 0,
740 .size = 0,
741 .link = 0,
742 .info = if (phnum >= std.elf.PN_XNUM) phnum else 0,
743 .addralign = 0,
744 .entsize = 0,
745 };
746 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null);
747 }
702 _, const text_size = Node.Known.text.location(&elf.mf).resolve(&elf.mf);
703 const ph_text = &phdr[text_phndx];
704 ph_text.* = .{
705 .type = std.elf.PT_NULL,
706 .offset = 0,
707 .vaddr = ph_vaddr,
708 .paddr = ph_vaddr,
709 .filesz = @intCast(text_size),
710 .memsz = @intCast(text_size),
711 .flags = .{ .R = true, .X = true },
712 .@"align" = @intCast(Node.Known.text.alignment(&elf.mf).toByteUnits()),
713 };
714 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
715 ph_vaddr += @intCast(text_size);
748716
749 try elf.symtab.ensureTotalCapacity(gpa, 1);
750 elf.symtab.addOneAssumeCapacity().* = .{
751 .ni = .none,
752 .loc_relocs = .none,
753 .target_relocs = .none,
754 .unused = 0,
755 };
756 assert(try elf.addSection(seg_rodata_ni, .{
757 .type = std.elf.SHT_SYMTAB,
758 .addralign = addr_align,
759 .entsize = @sizeOf(ElfN.Sym),
760 }) == .symtab);
761 const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf)));
762 symtab.* = .{
763 .name = try elf.string(.strtab, ""),
764 .value = 0,
765 .size = 0,
766 .info = .{
767 .type = .NOTYPE,
768 .bind = .LOCAL,
769 },
770 .other = .{
771 .visibility = .DEFAULT,
717 _, const data_size = Node.Known.data.location(&elf.mf).resolve(&elf.mf);
718 const ph_data = &phdr[data_phndx];
719 ph_data.* = .{
720 .type = std.elf.PT_NULL,
721 .offset = 0,
722 .vaddr = ph_vaddr,
723 .paddr = ph_vaddr,
724 .filesz = @intCast(data_size),
725 .memsz = @intCast(data_size),
726 .flags = .{ .R = true, .W = true },
727 .@"align" = @intCast(Node.Known.data.alignment(&elf.mf).toByteUnits()),
728 };
729 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
730 ph_vaddr += @intCast(data_size);
731
732 if (comp.config.any_non_single_threaded) {
733 const ph_tls = &phdr[tls_phndx];
734 ph_tls.* = .{
735 .type = std.elf.PT_TLS,
736 .offset = 0,
737 .vaddr = 0,
738 .paddr = 0,
739 .filesz = 0,
740 .memsz = 0,
741 .flags = .{ .R = true },
742 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
743 };
744 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
745 }
746
747 const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(Node.Known.shdr.slice(&elf.mf)));
748 sh_null.* = .{
749 .name = try elf.string(.shstrtab, ""),
750 .type = std.elf.SHT_NULL,
751 .flags = .{ .shf = .{} },
752 .addr = 0,
753 .offset = 0,
754 .size = 0,
755 .link = 0,
756 .info = if (phnum >= std.elf.PN_XNUM) phnum else 0,
757 .addralign = 0,
758 .entsize = 0,
759 };
760 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null);
761
762 try elf.symtab.ensureTotalCapacity(gpa, 1);
763 elf.symtab.addOneAssumeCapacity().* = .{
764 .ni = .none,
765 .loc_relocs = .none,
766 .target_relocs = .none,
767 .unused = 0,
768 };
769 assert(try elf.addSection(Node.Known.rodata, .{
770 .type = std.elf.SHT_SYMTAB,
771 .addralign = addr_align,
772 .entsize = @sizeOf(ElfN.Sym),
773 }) == .symtab);
774
775 const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf)));
776 symtab.* = .{
777 .name = try elf.string(.strtab, ""),
778 .value = 0,
779 .size = 0,
780 .info = .{
781 .type = .NOTYPE,
782 .bind = .LOCAL,
783 },
784 .other = .{
785 .visibility = .DEFAULT,
786 },
787 .shndx = std.elf.SHN_UNDEF,
788 };
789
790 const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class));
791 ehdr.shstrndx = ehdr.shnum;
772792 },
773 .shndx = std.elf.SHN_UNDEF,
774 };
775 {
776 const ehdr = @field(elf.ehdrPtr(), @tagName(class));
777 ehdr.shstrndx = ehdr.shnum;
778793 }
779 assert(try elf.addSection(seg_rodata_ni, .{
794 assert(try elf.addSection(Node.Known.rodata, .{
780795 .type = std.elf.SHT_STRTAB,
781796 .addralign = elf.mf.flags.block_size,
782797 .entsize = 1,
783798 }) == .shstrtab);
784 assert(try elf.addSection(seg_rodata_ni, .{
799 assert(try elf.addSection(Node.Known.rodata, .{
785800 .type = std.elf.SHT_STRTAB,
786801 .addralign = elf.mf.flags.block_size,
787802 .entsize = 1,
......@@ -793,75 +808,118 @@ fn initHeaders(
793808 Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[0] = 0;
794809 Symbol.Index.strtab.node(elf).slice(&elf.mf)[0] = 0;
795810
796 assert(try elf.addSection(seg_rodata_ni, .{
811 if (maybe_interp) |interp| {
812 try elf.nodes.ensureUnusedCapacity(gpa, 1);
813 const interp_ni = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
814 .size = interp.len + 1,
815 .moved = true,
816 .resized = true,
817 });
818 elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
819 elf.phdrs.items[interp_phndx] = interp_ni;
820
821 const sec_interp_si = try elf.addSection(interp_ni, .{
822 .name = ".interp",
823 .size = @intCast(interp.len + 1),
824 .flags = .{ .ALLOC = true },
825 });
826 const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
827 @memcpy(sec_interp[0..interp.len], interp);
828 sec_interp[interp.len] = 0;
829 }
830 assert(try elf.addSection(Node.Known.rodata, .{
797831 .name = ".rodata",
798832 .flags = .{ .ALLOC = true },
799833 .addralign = elf.mf.flags.block_size,
800834 }) == .rodata);
801 assert(try elf.addSection(seg_text_ni, .{
835 assert(try elf.addSection(Node.Known.text, .{
802836 .name = ".text",
803837 .flags = .{ .ALLOC = true, .EXECINSTR = true },
804838 .addralign = elf.mf.flags.block_size,
805839 }) == .text);
806 assert(try elf.addSection(seg_data_ni, .{
840 assert(try elf.addSection(Node.Known.data, .{
807841 .name = ".data",
808842 .flags = .{ .WRITE = true, .ALLOC = true },
809843 .addralign = elf.mf.flags.block_size,
810844 }) == .data);
811845 if (comp.config.any_non_single_threaded) {
812846 try elf.nodes.ensureUnusedCapacity(gpa, 1);
813 const seg_tls_ni = try elf.mf.addLastChildNode(gpa, seg_data_ni, .{
847 elf.known.tls = try elf.mf.addLastChildNode(gpa, Node.Known.rodata, .{
814848 .alignment = elf.mf.flags.block_size,
815849 .moved = true,
816850 });
817851 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
852 elf.phdrs.items[tls_phndx] = elf.known.tls;
818853
819 assert(try elf.addSection(seg_tls_ni, .{
854 assert(try elf.addSection(elf.known.tls, .{
820855 .name = ".tdata",
821856 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
822857 .addralign = elf.mf.flags.block_size,
823858 }) == .tdata);
824859 }
825 if (maybe_interp) |interp| {
826 try elf.nodes.ensureUnusedCapacity(gpa, 1);
827 const seg_interp_ni = try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
828 .size = interp.len + 1,
829 .moved = true,
830 .resized = true,
831 });
832 elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
833
834 const sec_interp_si = try elf.addSection(seg_interp_ni, .{
835 .name = ".interp",
836 .size = @intCast(interp.len + 1),
837 .flags = .{ .ALLOC = true },
838 });
839 const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
840 @memcpy(sec_interp[0..interp.len], interp);
841 sec_interp[interp.len] = 0;
842 }
860 assert(elf.nodes.len == expected_nodes_len);
843861}
844862
845fn getNode(elf: *Elf, ni: MappedFile.Node.Index) Node {
863fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
846864 return elf.nodes.get(@intFromEnum(ni));
847865}
866fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
867 const parent_vaddr = parent_vaddr: {
868 const parent_si = switch (elf.getNode(ni.parent(&elf.mf))) {
869 .file, .ehdr, .shdr => unreachable,
870 .segment => |phndx| break :parent_vaddr switch (elf.phdrSlice()) {
871 inline else => |ph| elf.targetLoad(&ph[phndx].vaddr),
872 },
873 .section => |si| si,
874 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf),
875 };
876 break :parent_vaddr switch (elf.symPtr(parent_si)) {
877 inline else => |sym| elf.targetLoad(&sym.value),
878 };
879 };
880 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
881 return parent_vaddr + offset;
882}
848883
849pub fn identClass(elf: *Elf) std.elf.CLASS {
884pub fn identClass(elf: *const Elf) std.elf.CLASS {
850885 return @enumFromInt(elf.mf.contents[std.elf.EI.CLASS]);
851886}
852
853pub fn identData(elf: *Elf) std.elf.DATA {
887pub fn identData(elf: *const Elf) std.elf.DATA {
854888 return @enumFromInt(elf.mf.contents[std.elf.EI.DATA]);
855889}
856fn endianForData(data: std.elf.DATA) std.builtin.Endian {
857 return switch (data) {
890
891pub fn targetEndian(elf: *const Elf) std.builtin.Endian {
892 return switch (elf.identData()) {
858893 .NONE, _ => unreachable,
859894 .@"2LSB" => .little,
860895 .@"2MSB" => .big,
861896 };
862897}
863pub fn targetEndian(elf: *Elf) std.builtin.Endian {
864 return endianForData(elf.identData());
898fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
899 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
900 return switch (@typeInfo(Child)) {
901 else => @compileError(@typeName(Child)),
902 .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),
903 .@"enum" => |@"enum"| @enumFromInt(elf.targetLoad(@as(*@"enum".tag_type, @ptrCast(ptr)))),
904 .@"struct" => |@"struct"| @bitCast(
905 elf.targetLoad(@as(*@"struct".backing_integer.?, @ptrCast(ptr))),
906 ),
907 };
908}
909fn targetStore(elf: *const Elf, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
910 const Child = @typeInfo(@TypeOf(ptr)).pointer.child;
911 return switch (@typeInfo(Child)) {
912 else => @compileError(@typeName(Child)),
913 .int => ptr.* = std.mem.nativeTo(Child, val, elf.targetEndian()),
914 .@"enum" => |@"enum"| elf.targetStore(
915 @as(*@"enum".tag_type, @ptrCast(ptr)),
916 @intFromEnum(val),
917 ),
918 .@"struct" => |@"struct"| elf.targetStore(
919 @as(*@"struct".backing_integer.?, @ptrCast(ptr)),
920 @bitCast(val),
921 ),
922 };
865923}
866924
867925pub const EhdrPtr = union(std.elf.CLASS) {
......@@ -870,7 +928,7 @@ pub const EhdrPtr = union(std.elf.CLASS) {
870928 @"64": *std.elf.Elf64.Ehdr,
871929};
872930pub fn ehdrPtr(elf: *Elf) EhdrPtr {
873 const slice = Node.known.ehdr.slice(&elf.mf);
931 const slice = Node.Known.ehdr.slice(&elf.mf);
874932 return switch (elf.identClass()) {
875933 .NONE, _ => unreachable,
876934 inline else => |class| @unionInit(
......@@ -882,26 +940,12 @@ pub fn ehdrPtr(elf: *Elf) EhdrPtr {
882940}
883941pub fn ehdrField(
884942 elf: *Elf,
885 comptime field: enum { type, machine },
886) @FieldType(std.elf.Elf32.Ehdr, @tagName(field)) {
887 return @enumFromInt(std.mem.toNative(
888 @typeInfo(@FieldType(std.elf.Elf32.Ehdr, @tagName(field))).@"enum".tag_type,
889 @intFromEnum(switch (elf.ehdrPtr()) {
890 inline else => |ehdr| @field(ehdr, @tagName(field)),
891 }),
892 elf.targetEndian(),
893 ));
894}
895
896fn baseAddrForType(@"type": std.elf.ET) u64 {
897 return switch (@"type") {
898 else => 0,
899 .EXEC => 0x1000000,
943 comptime field: std.meta.FieldEnum(std.elf.Elf64.Ehdr),
944) @FieldType(std.elf.Elf64.Ehdr, @tagName(field)) {
945 return switch (elf.ehdrPtr()) {
946 inline else => |ehdr| elf.targetLoad(&@field(ehdr, @tagName(field))),
900947 };
901948}
902pub fn baseAddr(elf: *Elf) u64 {
903 return baseAddrForType(elf.ehdrField(.type));
904}
905949
906950pub const PhdrSlice = union(std.elf.CLASS) {
907951 NONE: noreturn,
......@@ -909,7 +953,7 @@ pub const PhdrSlice = union(std.elf.CLASS) {
909953 @"64": []std.elf.Elf64.Phdr,
910954};
911955pub fn phdrSlice(elf: *Elf) PhdrSlice {
912 const slice = Node.known.phdr.slice(&elf.mf);
956 const slice = Node.Known.phdr.slice(&elf.mf);
913957 return switch (elf.identClass()) {
914958 .NONE, _ => unreachable,
915959 inline else => |class| @unionInit(
......@@ -926,7 +970,7 @@ pub const ShdrSlice = union(std.elf.CLASS) {
926970 @"64": []std.elf.Elf64.Shdr,
927971};
928972pub fn shdrSlice(elf: *Elf) ShdrSlice {
929 const slice = Node.known.shdr.slice(&elf.mf);
973 const slice = Node.Known.shdr.slice(&elf.mf);
930974 return switch (elf.identClass()) {
931975 .NONE, _ => unreachable,
932976 inline else => |class| @unionInit(
......@@ -937,17 +981,17 @@ pub fn shdrSlice(elf: *Elf) ShdrSlice {
937981 };
938982}
939983
940pub const SymSlice = union(std.elf.CLASS) {
984pub const SymtabSlice = union(std.elf.CLASS) {
941985 NONE: noreturn,
942986 @"32": []std.elf.Elf32.Sym,
943987 @"64": []std.elf.Elf64.Sym,
944988};
945pub fn symSlice(elf: *Elf) SymSlice {
989pub fn symtabSlice(elf: *Elf) SymtabSlice {
946990 const slice = Symbol.Index.symtab.node(elf).slice(&elf.mf);
947991 return switch (elf.identClass()) {
948992 .NONE, _ => unreachable,
949993 inline else => |class| @unionInit(
950 SymSlice,
994 SymtabSlice,
951995 @tagName(class),
952996 @ptrCast(@alignCast(slice)),
953997 ),
......@@ -960,7 +1004,7 @@ pub const SymPtr = union(std.elf.CLASS) {
9601004 @"64": *std.elf.Elf64.Sym,
9611005};
9621006pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr {
963 return switch (elf.symSlice()) {
1007 return switch (elf.symtabSlice()) {
9641008 inline else => |sym, class| @unionInit(SymPtr, @tagName(class), &sym[@intFromEnum(si)]),
9651009 };
9661010}
......@@ -1123,7 +1167,9 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.In
11231167 } },
11241168 },
11251169 );
1126 return 0;
1170 return switch (elf.symPtr(target_si)) {
1171 inline else => |sym| elf.targetLoad(&sym.value),
1172 };
11271173}
11281174
11291175fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
......@@ -1135,21 +1181,19 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
11351181 entsize: std.elf.Word = 0,
11361182}) !Symbol.Index {
11371183 const gpa = elf.base.comp.gpa;
1138 const target_endian = elf.targetEndian();
11391184 try elf.nodes.ensureUnusedCapacity(gpa, 1);
11401185 try elf.symtab.ensureUnusedCapacity(gpa, 1);
11411186
11421187 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
11431188 const shndx, const shdr_size = shndx: switch (elf.ehdrPtr()) {
11441189 inline else => |ehdr| {
1145 const shentsize = std.mem.toNative(@TypeOf(ehdr.shentsize), ehdr.shentsize, target_endian);
1146 const shndx = std.mem.toNative(@TypeOf(ehdr.shnum), ehdr.shnum, target_endian);
1190 const shndx = elf.targetLoad(&ehdr.shnum);
11471191 const shnum = shndx + 1;
1148 ehdr.shnum = std.mem.nativeTo(@TypeOf(ehdr.shnum), shnum, target_endian);
1149 break :shndx .{ shndx, shentsize * shnum };
1192 elf.targetStore(&ehdr.shnum, shnum);
1193 break :shndx .{ shndx, elf.targetLoad(&ehdr.shentsize) * shnum };
11501194 },
11511195 };
1152 try Node.known.shdr.resize(&elf.mf, gpa, shdr_size);
1196 try Node.Known.shdr.resize(&elf.mf, gpa, shdr_size);
11531197 const ni = try elf.mf.addLastChildNode(gpa, segment_ni, .{
11541198 .alignment = opts.addralign,
11551199 .size = opts.size,
......@@ -1179,7 +1223,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
11791223 .addralign = @intCast(opts.addralign.toByteUnits()),
11801224 .entsize = opts.entsize,
11811225 };
1182 if (target_endian != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh);
1226 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh);
11831227 },
11841228 }
11851229 return si;
......@@ -1188,37 +1232,29 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
11881232fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void {
11891233 const strtab_entry = try elf.string(.strtab, name);
11901234 const shstrtab_entry = try elf.string(.shstrtab, name);
1191 const target_endian = elf.targetEndian();
11921235 switch (elf.shdrSlice()) {
11931236 inline else => |shdr, class| {
11941237 const sym = @field(elf.symPtr(si), @tagName(class));
1195 sym.name = std.mem.nativeTo(@TypeOf(sym.name), strtab_entry, target_endian);
1196 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1197 const sh = &shdr[shndx];
1198 sh.name = std.mem.nativeTo(@TypeOf(sh.name), shstrtab_entry, target_endian);
1238 elf.targetStore(&sym.name, strtab_entry);
1239 const sh = &shdr[elf.targetLoad(&sym.shndx)];
1240 elf.targetStore(&sh.name, shstrtab_entry);
11991241 },
12001242 }
12011243}
12021244
12031245fn linkSections(elf: *Elf, si: Symbol.Index, link_si: Symbol.Index) !void {
1204 const target_endian = elf.targetEndian();
12051246 switch (elf.shdrSlice()) {
1206 inline else => |shdr, class| {
1207 const sym = @field(elf.symPtr(si), @tagName(class));
1208 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1209 shdr[shndx].link = @field(elf.symPtr(link_si), @tagName(class)).shndx;
1210 },
1247 inline else => |shdr, class| shdr[
1248 elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)
1249 ].link = @field(elf.symPtr(link_si), @tagName(class)).shndx,
12111250 }
12121251}
12131252
12141253fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 {
1215 const target_endian = elf.targetEndian();
1216 const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[name: switch (elf.shdrSlice()) {
1217 inline else => |shndx, class| {
1218 const sym = @field(elf.symPtr(si), @tagName(class));
1219 const sh = &shndx[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
1220 break :name std.mem.toNative(@TypeOf(sh.name), sh.name, target_endian);
1221 },
1254 const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrSlice()) {
1255 inline else => |shndx, class| elf.targetLoad(
1256 &shndx[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].name,
1257 ),
12221258 }..];
12231259 return name[0..std.mem.indexOfScalar(u8, name, 0).? :0];
12241260}
......@@ -1243,7 +1279,7 @@ pub fn addReloc(
12431279) !void {
12441280 const gpa = elf.base.comp.gpa;
12451281 const target = target_si.get(elf);
1246 const ri: link.File.Elf2.Reloc.Index = @enumFromInt(elf.relocs.items.len);
1282 const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);
12471283 (try elf.relocs.addOne(gpa)).* = .{
12481284 .type = @"type",
12491285 .prev = .none,
......@@ -1332,10 +1368,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
13321368 error.WriteFailed => return error.OutOfMemory,
13331369 else => |e| return e,
13341370 };
1335 const target_endian = elf.targetEndian();
13361371 switch (elf.symPtr(si)) {
1337 inline else => |sym| sym.size =
1338 std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
1372 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
13391373 }
13401374 si.applyLocationRelocs(elf);
13411375}
......@@ -1463,10 +1497,8 @@ fn updateFuncInner(
14631497 error.WriteFailed => return nw.err.?,
14641498 else => |e| return e,
14651499 };
1466 const target_endian = elf.targetEndian();
14671500 switch (elf.symPtr(si)) {
1468 inline else => |sym| sym.size =
1469 std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
1501 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
14701502 }
14711503 si.applyLocationRelocs(elf);
14721504}
......@@ -1634,10 +1666,8 @@ fn flushUav(
16341666 error.WriteFailed => return error.OutOfMemory,
16351667 else => |e| return e,
16361668 };
1637 const target_endian = elf.targetEndian();
16381669 switch (elf.symPtr(si)) {
1639 inline else => |sym| sym.size =
1640 std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
1670 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
16411671 }
16421672 si.applyLocationRelocs(elf);
16431673}
......@@ -1689,167 +1719,121 @@ fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
16891719 .none,
16901720 .{ .atom_index = @intFromEnum(si) },
16911721 );
1692 const target_endian = elf.targetEndian();
16931722 switch (elf.symPtr(si)) {
1694 inline else => |sym| sym.size =
1695 std.mem.nativeTo(@TypeOf(sym.size), @intCast(nw.interface.end), target_endian),
1723 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
16961724 }
16971725 si.applyLocationRelocs(elf);
16981726}
16991727
17001728fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
1701 const target_endian = elf.targetEndian();
1702 const file_offset = ni.fileLocation(&elf.mf, false).offset;
1703 const node = elf.getNode(ni);
1704 switch (node) {
1705 else => |tag| @panic(@tagName(tag)),
1706 .ehdr => assert(file_offset == 0),
1729 switch (elf.getNode(ni)) {
1730 .file => unreachable,
1731 .ehdr => assert(ni.fileLocation(&elf.mf, false).offset == 0),
17071732 .shdr => switch (elf.ehdrPtr()) {
1708 inline else => |ehdr| ehdr.shoff =
1709 std.mem.nativeTo(@TypeOf(ehdr.shoff), @intCast(file_offset), target_endian),
1733 inline else => |ehdr| elf.targetStore(
1734 &ehdr.shoff,
1735 @intCast(ni.fileLocation(&elf.mf, false).offset),
1736 ),
17101737 },
17111738 .segment => |phndx| switch (elf.phdrSlice()) {
17121739 inline else => |phdr, class| {
17131740 const ph = &phdr[phndx];
1714 switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
1741 elf.targetStore(&ph.offset, @intCast(ni.fileLocation(&elf.mf, false).offset));
1742 switch (elf.targetLoad(&ph.type)) {
17151743 else => unreachable,
1716 std.elf.PT_NULL, std.elf.PT_LOAD, std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {},
1717 std.elf.PT_PHDR => {
1718 const ehdr = @field(elf.ehdrPtr(), @tagName(class));
1719 ehdr.phoff =
1720 std.mem.nativeTo(@TypeOf(ehdr.phoff), @intCast(file_offset), target_endian);
1721 },
1744 std.elf.PT_NULL, std.elf.PT_LOAD => return,
1745 std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {},
1746 std.elf.PT_PHDR => @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset,
17221747 std.elf.PT_TLS => {},
17231748 }
1724 ph.offset = std.mem.nativeTo(@TypeOf(ph.offset), @intCast(file_offset), target_endian);
1725 ph.vaddr = std.mem.nativeTo(
1726 @TypeOf(ph.vaddr),
1727 @intCast(elf.baseAddr() + file_offset),
1728 target_endian,
1729 );
1749 elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni)));
17301750 ph.paddr = ph.vaddr;
17311751 },
17321752 },
17331753 .section => |si| switch (elf.shdrSlice()) {
17341754 inline else => |shdr, class| {
17351755 const sym = @field(elf.symPtr(si), @tagName(class));
1736 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1737 const sh = &shdr[shndx];
1738 const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
1739 @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
1740 @bitCast(sh.flags),
1741 target_endian,
1742 ));
1743 if (flags.shf.ALLOC) {
1744 sym.value = std.mem.nativeTo(
1745 @TypeOf(sym.value),
1746 @intCast(elf.baseAddr() + file_offset),
1747 target_endian,
1748 );
1749 sh.addr = sym.value;
1756 const sh = &shdr[elf.targetLoad(&sym.shndx)];
1757 elf.targetStore(&sh.offset, @intCast(ni.fileLocation(&elf.mf, false).offset));
1758 const flags = elf.targetLoad(&sh.flags).shf;
1759 if (flags.ALLOC) {
1760 elf.targetStore(&sh.addr, @intCast(elf.computeNodeVAddr(ni)));
1761 if (!flags.TLS) sym.value = sh.addr;
17501762 }
1751 sh.offset = std.mem.nativeTo(@TypeOf(sh.offset), @intCast(file_offset), target_endian);
17521763 },
17531764 },
1754 .nav, .uav, .lazy_code, .lazy_const_data => {
1755 const si = switch (node) {
1756 else => unreachable,
1757 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf),
1758 };
1759 switch (elf.shdrSlice()) {
1760 inline else => |shdr, class| {
1761 const sym = @field(elf.symPtr(si), @tagName(class));
1762 const sh = &shdr[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
1763 const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
1764 @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
1765 @bitCast(sh.flags),
1766 target_endian,
1767 ));
1768 const sh_addr = if (flags.shf.TLS)
1769 0
1770 else
1771 std.mem.toNative(@TypeOf(sh.addr), sh.addr, target_endian);
1772 const sh_offset = std.mem.toNative(@TypeOf(sh.offset), sh.offset, target_endian);
1773 sym.value = std.mem.nativeTo(
1774 @TypeOf(sym.value),
1775 @intCast(file_offset - sh_offset + sh_addr),
1776 target_endian,
1777 );
1778 if (si == elf.entry_hack) @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value;
1779 },
1780 }
1781 si.applyLocationRelocs(elf);
1782 si.applyTargetRelocs(elf);
1783 },
1765 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| mi.symbol(elf).flushMoved(elf),
17841766 }
17851767 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
17861768}
17871769
17881770fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
1789 const target_endian = elf.targetEndian();
17901771 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
1791 const node = elf.getNode(ni);
1792 switch (node) {
1793 else => |tag| @panic(@tagName(tag)),
1794 .file, .shdr => {},
1772 switch (elf.getNode(ni)) {
1773 .file => {},
1774 .ehdr => unreachable,
1775 .shdr => {},
17951776 .segment => |phndx| switch (elf.phdrSlice()) {
17961777 inline else => |phdr| {
1778 assert(elf.phdrs.items[phndx] == ni);
17971779 const ph = &phdr[phndx];
1798 ph.filesz = std.mem.nativeTo(@TypeOf(ph.filesz), @intCast(size), target_endian);
1799 ph.memsz = ph.filesz;
1800 switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
1801 else => unreachable,
1802 std.elf.PT_NULL => {
1803 if (size > 0) ph.type = std.mem.nativeTo(
1804 @TypeOf(ph.type),
1805 std.elf.PT_LOAD,
1806 target_endian,
1807 );
1808 },
1809 std.elf.PT_LOAD => {
1810 if (size == 0) ph.type = std.mem.nativeTo(
1811 @TypeOf(ph.type),
1812 std.elf.PT_NULL,
1813 target_endian,
1814 );
1815 },
1816 std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => {},
1817 std.elf.PT_TLS => try ni.childrenMoved(elf.base.comp.gpa, &elf.mf),
1780 elf.targetStore(&ph.filesz, @intCast(size));
1781 if (size > elf.targetLoad(&ph.memsz)) {
1782 const memsz = ni.alignment(&elf.mf).forward(@intCast(size * 4));
1783 elf.targetStore(&ph.memsz, @intCast(memsz));
1784 switch (elf.targetLoad(&ph.type)) {
1785 else => unreachable,
1786 std.elf.PT_NULL => if (size > 0) elf.targetStore(&ph.type, std.elf.PT_LOAD),
1787 std.elf.PT_LOAD => if (size == 0) elf.targetStore(&ph.type, std.elf.PT_NULL),
1788 std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => return,
1789 std.elf.PT_TLS => return ni.childrenMoved(elf.base.comp.gpa, &elf.mf),
1790 }
1791 var vaddr = elf.targetLoad(&ph.vaddr);
1792 var new_phndx = phndx;
1793 for (phdr[phndx + 1 ..], phndx + 1..) |*next_ph, next_phndx| {
1794 switch (elf.targetLoad(&next_ph.type)) {
1795 else => unreachable,
1796 std.elf.PT_NULL, std.elf.PT_LOAD => {},
1797 std.elf.PT_DYNAMIC,
1798 std.elf.PT_INTERP,
1799 std.elf.PT_PHDR,
1800 std.elf.PT_TLS,
1801 => break,
1802 }
1803 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
1804 if (vaddr + memsz <= next_vaddr) break;
1805 vaddr = next_vaddr + elf.targetLoad(&next_ph.memsz);
1806 std.mem.swap(@TypeOf(ph.*), &phdr[new_phndx], next_ph);
1807 const next_ni = elf.phdrs.items[next_phndx];
1808 elf.phdrs.items[new_phndx] = next_ni;
1809 elf.nodes.items(.data)[@intFromEnum(next_ni)] = .{ .segment = new_phndx };
1810 new_phndx = @intCast(next_phndx);
1811 }
1812 if (new_phndx != phndx) {
1813 const new_ph = &phdr[new_phndx];
1814 elf.targetStore(&new_ph.vaddr, vaddr);
1815 new_ph.paddr = new_ph.vaddr;
1816 elf.phdrs.items[new_phndx] = ni;
1817 elf.nodes.items(.data)[@intFromEnum(ni)] = .{ .segment = new_phndx };
1818 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
1819 }
18181820 }
18191821 },
18201822 },
1821 .section => |si| switch (elf.shdrSlice()) {
1822 inline else => |shdr, class| {
1823 const sym = @field(elf.symPtr(si), @tagName(class));
1824 const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
1825 const sh = &shdr[shndx];
1826 switch (std.mem.toNative(@TypeOf(sh.type), sh.type, target_endian)) {
1823 .section => |si| switch (elf.symPtr(si)) {
1824 inline else => |sym, class| {
1825 const sh = &@field(elf.shdrSlice(), @tagName(class))[elf.targetLoad(&sym.shndx)];
1826 elf.targetStore(&sh.size, @intCast(size));
1827 switch (elf.targetLoad(&sh.type)) {
18271828 else => unreachable,
1828 std.elf.SHT_NULL => {
1829 if (size > 0) sh.type = std.mem.nativeTo(
1830 @TypeOf(sh.type),
1831 std.elf.SHT_PROGBITS,
1832 target_endian,
1833 );
1834 },
1835 std.elf.SHT_PROGBITS => {
1836 if (size == 0) sh.type = std.mem.nativeTo(
1837 @TypeOf(sh.type),
1838 std.elf.SHT_NULL,
1839 target_endian,
1840 );
1841 },
1842 std.elf.SHT_SYMTAB => sh.info = std.mem.nativeTo(
1843 @TypeOf(sh.info),
1844 @intCast(@divExact(
1845 size,
1846 std.mem.toNative(@TypeOf(sh.entsize), sh.entsize, target_endian),
1847 )),
1848 target_endian,
1829 std.elf.SHT_NULL => if (size > 0) elf.targetStore(&sh.type, std.elf.SHT_PROGBITS),
1830 std.elf.SHT_PROGBITS => if (size == 0) elf.targetStore(&sh.type, std.elf.SHT_NULL),
1831 std.elf.SHT_SYMTAB => elf.targetStore(
1832 &sh.info,
1833 @intCast(@divExact(size, elf.targetLoad(&sh.entsize))),
18491834 ),
18501835 std.elf.SHT_STRTAB => {},
18511836 }
1852 sh.size = std.mem.nativeTo(@TypeOf(sh.size), @intCast(size), target_endian);
18531837 },
18541838 },
18551839 .nav, .uav, .lazy_code, .lazy_const_data => {},