authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:37-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:27:17-04:00
log1b95427715b58cf4cd6df63fc9a7a8d200ca5d47
tree97e700f4bc3c22369cfd9cc904d91c4fc39b0f7a
parent72bc140ad16a4a2dbe632129ea9068630e019277

Coff: add support for .is_dll_import

x86_64: emit the correct encodings for loading / calling .is_dll_import externs Coff: fix emitting an empty import directory objdump: add --exports=sort for sorting implibs in snapshots (self-hosted outputs members in a different order than llvm) objdump: fix some missed redactions / filters / elements calls test: add dllimport standalone test

12 files changed, 250 insertions(+), 48 deletions(-)

lib/compiler/objdump.zig+80-22
...@@ -11,6 +11,7 @@ var stdout_buffer: [4000]u8 = undefined;...@@ -11,6 +11,7 @@ var stdout_buffer: [4000]u8 = undefined;
1111
12const Options = struct {12const Options = struct {
13 exports: bool,13 exports: bool,
14 exports_sort: bool,
14 file_headers: bool,15 file_headers: bool,
15 imports: bool,16 imports: bool,
16 input_path: []const u8,17 input_path: []const u8,
...@@ -53,6 +54,7 @@ pub fn main(init: std.process.Init) !void {...@@ -53,6 +54,7 @@ pub fn main(init: std.process.Init) !void {
53 var i: usize = 1;54 var i: usize = 1;
5455
55 var opt_exports: ?bool = null;56 var opt_exports: ?bool = null;
57 var opt_exports_sort: ?bool = null;
56 var opt_file_headers: ?bool = null;58 var opt_file_headers: ?bool = null;
57 var opt_imports: ?bool = null;59 var opt_imports: ?bool = null;
58 var opt_input_path: ?[]const u8 = null;60 var opt_input_path: ?[]const u8 = null;
...@@ -81,9 +83,11 @@ pub fn main(init: std.process.Init) !void {...@@ -81,9 +83,11 @@ pub fn main(init: std.process.Init) !void {
81 opt_section_headers = true;83 opt_section_headers = true;
82 opt_symbols = true;84 opt_symbols = true;
83 opt_relocs = true;85 opt_relocs = true;
84 } else if (mem.eql(u8, arg, "--exports")) {86 } else if (mem.startsWith(u8, arg, "--exports")) {
85 opt_exports = true;87 opt_exports = true;
86 opt_linker_member = .second_linker;88 opt_linker_member = .second_linker;
89 if (mem.eql(u8, arg["--exports".len..], "=sort"))
90 opt_exports_sort = true;
87 } else if (mem.eql(u8, arg, "--file-headers")) {91 } else if (mem.eql(u8, arg, "--file-headers")) {
88 opt_file_headers = true;92 opt_file_headers = true;
89 } else if (mem.eql(u8, arg, "--imports")) {93 } else if (mem.eql(u8, arg, "--imports")) {
...@@ -156,6 +160,7 @@ pub fn main(init: std.process.Init) !void {...@@ -156,6 +160,7 @@ pub fn main(init: std.process.Init) !void {
156 const opts: Options = .{160 const opts: Options = .{
157 .input_path = opt_input_path orelse fatal("missing input file path positional argument", .{}),161 .input_path = opt_input_path orelse fatal("missing input file path positional argument", .{}),
158 .exports = opt_exports orelse false,162 .exports = opt_exports orelse false,
163 .exports_sort = opt_exports_sort orelse false,
159 .file_headers = opt_file_headers orelse false,164 .file_headers = opt_file_headers orelse false,
160 .imports = opt_imports orelse false,165 .imports = opt_imports orelse false,
161 .linker_member = opt_linker_member,166 .linker_member = opt_linker_member,
...@@ -355,9 +360,12 @@ const coff = struct {...@@ -355,9 +360,12 @@ const coff = struct {
355 const r = &fr.interface;360 const r = &fr.interface;
356 r.toss(std.coff.archive_signature.len);361 r.toss(std.coff.archive_signature.len);
357362
358 var members: std.ArrayList(struct {363 const Member = struct {
359 offset: u32,364 offset: u32,
360 }) = .empty;365 order: ?u32,
366 };
367
368 var members: std.ArrayList(Member) = .empty;
361 defer members.deinit(gpa);369 defer members.deinit(gpa);
362 var symbol_member_indices: std.ArrayList(u32) = .empty;370 var symbol_member_indices: std.ArrayList(u32) = .empty;
363 defer symbol_member_indices.deinit(gpa);371 defer symbol_member_indices.deinit(gpa);
...@@ -415,6 +423,10 @@ const coff = struct {...@@ -415,6 +423,10 @@ const coff = struct {
415 for (0..num_symbols) |symbol_i| {423 for (0..num_symbols) |symbol_i| {
416 const symbol = r.takeDelimiter(0) catch |err|424 const symbol = r.takeDelimiter(0) catch |err|
417 return d.failParse("unable to read first linker member string table: {t}", .{err});425 return d.failParse("unable to read first linker member string table: {t}", .{err});
426
427 if (!filterMatches(d.opts.symbol_filters, symbol.?))
428 continue;
429
418 const offset = std.mem.readInt(u32, offsets[symbol_i * 4 ..][0..4], .big);430 const offset = std.mem.readInt(u32, offsets[symbol_i * 4 ..][0..4], .big);
419 try w.print("{f} {s}\n", .{431 try w.print("{f} {s}\n", .{
420 fmtIntField(d, offset, .{ .kind = .va }),432 fmtIntField(d, offset, .{ .kind = .va }),
...@@ -441,6 +453,7 @@ const coff = struct {...@@ -441,6 +453,7 @@ const coff = struct {
441 for (0..num_members) |_|453 for (0..num_members) |_|
442 members.addOneAssumeCapacity().* = .{454 members.addOneAssumeCapacity().* = .{
443 .offset = try r.takeInt(u32, .little),455 .offset = try r.takeInt(u32, .little),
456 .order = null,
444 };457 };
445458
446 const num_symbols = try r.takeInt(u32, .little);459 const num_symbols = try r.takeInt(u32, .little);
...@@ -451,14 +464,42 @@ const coff = struct {...@@ -451,14 +464,42 @@ const coff = struct {
451 if (dump_header)464 if (dump_header)
452 try w.print(465 try w.print(
453 \\{t: >16} type466 \\{t: >16} type
454 \\ | {d} symbols467 \\ | {f} symbols
455 \\ | {d} members468 \\ | {f} members
456 \\469 \\
457 , .{ expected_kind, num_symbols, num_members });470 , .{
471 expected_kind,
472 fmtIntField(d, num_symbols, .{ .kind = .size, .width = .auto }),
473 fmtIntField(d, num_members, .{ .kind = .size, .width = .auto }),
474 });
458475
459 try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols);476 try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols);
460 for (0..num_symbols) |_|477 for (0..num_symbols) |order| {
461 symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1;478 const index = (try r.takeInt(u16, .little)) - 1;
479 if (index >= members.items.len)
480 return d.failParse("invalid member index 0x{x} in seconds linker member indices array", .{index});
481
482 symbol_member_indices.addOneAssumeCapacity().* = index;
483
484 if (members.items[index].order == null)
485 members.items[index].order = @intCast(order);
486 }
487
488 if (d.opts.exports and d.opts.exports_sort) {
489 std.sort.pdq(Member, members.items, {}, struct {
490 fn lessThan(ctx: void, lhs: Member, rhs: Member) bool {
491 _ = ctx;
492 if (lhs.order == null and rhs.order == null)
493 return lhs.offset < rhs.offset
494 else if (lhs.order) |lhs_order|
495 return if (rhs.order) |rhs_order| lhs_order < rhs_order else false
496 else if (rhs.order) |rhs_order|
497 return if (lhs.order) |lhs_order| lhs_order < rhs_order else true
498 else
499 unreachable;
500 }
501 }.lessThan);
502 }
462503
463 if (d.opts.linker_member == .second_linker) {504 if (d.opts.linker_member == .second_linker) {
464 if (d.element(.@"table-header"))505 if (d.element(.@"table-header"))
...@@ -480,6 +521,9 @@ const coff = struct {...@@ -480,6 +521,9 @@ const coff = struct {
480 else => |e| return e,521 else => |e| return e,
481 }) |n| n else return d.failParse("unterminated string found in second linker member", .{});522 }) |n| n else return d.failParse("unterminated string found in second linker member", .{});
482523
524 if (!filterMatches(d.opts.symbol_filters, symbol_name))
525 continue;
526
483 try w.print("{f} {s}\n", .{527 try w.print("{f} {s}\n", .{
484 fmtIntField(528 fmtIntField(
485 d,529 d,
...@@ -844,8 +888,8 @@ const coff = struct {...@@ -844,8 +888,8 @@ const coff = struct {
844 section_i + 1,888 section_i + 1,
845 raw_name,889 raw_name,
846 fmtIntField(d, section.header.virtual_address, .{ .kind = .va }),890 fmtIntField(d, section.header.virtual_address, .{ .kind = .va }),
847 fmtIntField(d, section.header.virtual_size, .{ .kind = .size, .width = 9 }),891 fmtIntField(d, section.header.virtual_size, .{ .kind = .size, .width = .{ .explicit = 9 } }),
848 fmtIntField(d, section.header.size_of_raw_data, .{ .kind = .size, .width = 9 }),892 fmtIntField(d, section.header.size_of_raw_data, .{ .kind = .size, .width = .{ .explicit = 9 } }),
849 fmtIntField(d, section.header.pointer_to_raw_data, .{ .kind = .va }),893 fmtIntField(d, section.header.pointer_to_raw_data, .{ .kind = .va }),
850 fmtIntField(d, section.header.pointer_to_relocations, .{ .kind = .va }),894 fmtIntField(d, section.header.pointer_to_relocations, .{ .kind = .va }),
851 fmtIntField(d, section.header.pointer_to_linenumbers, .{ .kind = .va }),895 fmtIntField(d, section.header.pointer_to_linenumbers, .{ .kind = .va }),
...@@ -1309,14 +1353,16 @@ const coff = struct {...@@ -1309,14 +1353,16 @@ const coff = struct {
13091353
1310 const dll_name = (try r.takeDelimiter(0)).?;1354 const dll_name = (try r.takeDelimiter(0)).?;
13111355
1312 try w.print("Import table entry for {s}:\n", .{dll_name});1356 if (d.element(.@"header-name"))
1357 try w.print("Import table entry for {s}:\n", .{dll_name});
1313 try dumpHeader(d, Entry, &entry, struct {});1358 try dumpHeader(d, Entry, &entry, struct {});
13141359
1315 try w.print(1360 if (d.element(.@"table-header"))
1316 \\1361 try w.print(
1317 \\ Ord Hint Name1362 \\
1318 \\1363 \\ Ord Hint Name
1319 , .{});1364 \\
1365 , .{});
13201366
1321 const ilt_section = sectionContainingRva(1367 const ilt_section = sectionContainingRva(
1322 rva_index,1368 rva_index,
...@@ -1565,7 +1611,7 @@ const coff = struct {...@@ -1565,7 +1611,7 @@ const coff = struct {
15651611
1566 const FormatIntField = struct {1612 const FormatIntField = struct {
1567 val: ?u64,1613 val: ?u64,
1568 width: usize,1614 width: ?usize,
1569 zero_fill: bool,1615 zero_fill: bool,
1570 };1616 };
15711617
...@@ -1574,14 +1620,22 @@ const coff = struct {...@@ -1574,14 +1620,22 @@ const coff = struct {
1574 val: anytype,1620 val: anytype,
1575 params: struct {1621 params: struct {
1576 kind: ?FieldKind = null,1622 kind: ?FieldKind = null,
1577 width: ?usize = null,1623 width: union(enum) {
1624 fit_max,
1625 auto,
1626 explicit: usize,
1627 } = .fit_max,
1578 zero_fill: bool = false,1628 zero_fill: bool = false,
1579 },1629 },
1580 ) std.fmt.Alt(FormatIntField, intFieldString) {1630 ) std.fmt.Alt(FormatIntField, intFieldString) {
1581 return .{1631 return .{
1582 .data = .{1632 .data = .{
1583 .val = if (d.redacted(params.kind)) null else val,1633 .val = if (d.redacted(params.kind)) null else val,
1584 .width = params.width orelse @typeInfo(@TypeOf(val)).int.bits / 4,1634 .width = switch (params.width) {
1635 .fit_max => @typeInfo(@TypeOf(val)).int.bits / 4,
1636 .auto => null,
1637 .explicit => |w| w,
1638 },
1585 .zero_fill = params.zero_fill,1639 .zero_fill = params.zero_fill,
1586 },1640 },
1587 };1641 };
...@@ -1594,7 +1648,7 @@ const coff = struct {...@@ -1594,7 +1648,7 @@ const coff = struct {
1594 .alignment = .right,1648 .alignment = .right,
1595 .fill = if (field.zero_fill) '0' else ' ',1649 .fill = if (field.zero_fill) '0' else ' ',
1596 });1650 });
1597 } else try w.splatByteAll('x', field.width);1651 } else try w.splatByteAll('x', field.width orelse 1);
1598 }1652 }
15991653
1600 fn dumpFlags(w: *Io.Writer, comptime fmt: []const u8, comptime T: type, flags: *const T, cols: u32) !void {1654 fn dumpFlags(w: *Io.Writer, comptime fmt: []const u8, comptime T: type, flags: *const T, cols: u32) !void {
...@@ -1628,6 +1682,8 @@ const coff = struct {...@@ -1628,6 +1682,8 @@ const coff = struct {
1628 if (std.mem.startsWith(u8, name, "number_") or1682 if (std.mem.startsWith(u8, name, "number_") or
1629 std.mem.startsWith(u8, name, "size"))1683 std.mem.startsWith(u8, name, "size"))
1630 return .size;1684 return .size;
1685 if (std.mem.startsWith(u8, name, "hint"))
1686 return .ord;
1631 return null;1687 return null;
1632 }1688 }
16331689
...@@ -1645,7 +1701,7 @@ const coff = struct {...@@ -1645,7 +1701,7 @@ const coff = struct {
1645 switch (@typeInfo(field.type)) {1701 switch (@typeInfo(field.type)) {
1646 .int => try d.w.print("{f} {s}\n", .{ fmtIntField(d, val.*, .{1702 .int => try d.w.print("{f} {s}\n", .{ fmtIntField(d, val.*, .{
1647 .kind = comptime fieldKind(field.name),1703 .kind = comptime fieldKind(field.name),
1648 .width = 16,1704 .width = .{ .explicit = 16 },
1649 }), field.name }),1705 }), field.name }),
1650 .@"enum" => try d.w.print("{x: >16} {s} ({t})\n", .{ val.*, field.name, val.* }),1706 .@"enum" => try d.w.print("{x: >16} {s} ({t})\n", .{ val.*, field.name, val.* }),
1651 .@"struct" => |s| {1707 .@"struct" => |s| {
...@@ -1690,7 +1746,9 @@ const usage =...@@ -1690,7 +1746,9 @@ const usage =
1690 \\Options:1746 \\Options:
1691 \\ -h, --help Print this help and exit1747 \\ -h, --help Print this help and exit
1692 \\ --all-headers Alias for --file-headers --linker-member=2 --member-headers --section-headers --relocs --symbols1748 \\ --all-headers Alias for --file-headers --linker-member=2 --member-headers --section-headers --relocs --symbols
1693 \\ --exports Display exported symbols. In the case of COFF import libraries, display import headers.1749 \\ --exports[=sort] Display exported symbols.
1750 \\ In the case of COFF import libraries, displays the symbol list and import headers.
1751 \\ Specify =sort to optionally sort the import headers by symbol name.
1694 \\ --file-headers Display file-format specific headers1752 \\ --file-headers Display file-format specific headers
1695 \\ --imports Display imported symbols1753 \\ --imports Display imported symbols
1696 \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified archive linker member (default 2)1754 \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified archive linker member (default 2)
src/codegen/x86_64/Emit.zig+28-2
...@@ -113,6 +113,7 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -113,6 +113,7 @@ pub fn emitMir(emit: *Emit) Error!void {
113 .default => true,113 .default => true,
114 .hidden, .protected => false,114 .hidden, .protected => false,
115 },115 },
116 .is_dll_import = @"extern".is_dll_import,
116 .force_pcrel_direct = switch (@"extern".relocation) {117 .force_pcrel_direct = switch (@"extern".relocation) {
117 .any => false,118 .any => false,
118 .pcrel => true,119 .pcrel => true,
...@@ -171,7 +172,13 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -171,7 +172,13 @@ pub fn emitMir(emit: *Emit) Error!void {
171 switch (lowered_inst.encoding.mnemonic) {172 switch (lowered_inst.encoding.mnemonic) {
172 .call => {173 .call => {
173 reloc.target = .{ .branch = target };174 reloc.target = .{ .branch = target };
174 try emit.encodeInst(lowered_inst, reloc_info);175 if (target.is_dll_import and emit.bin_file.cast(.coff2) != null) {
176 try emit.encodeInst(try .new(.none, .call, &.{
177 .{ .mem = .initRip(.ptr, 0) },
178 }, emit.lower.target), reloc_info);
179 } else {
180 try emit.encodeInst(lowered_inst, reloc_info);
181 }
175 continue :lowered_inst;182 continue :lowered_inst;
176 },183 },
177 else => {},184 else => {},
...@@ -247,7 +254,25 @@ pub fn emitMir(emit: *Emit) Error!void {...@@ -247,7 +254,25 @@ pub fn emitMir(emit: *Emit) Error!void {
247 else => unreachable,254 else => unreachable,
248 }255 }
249 } else if (emit.bin_file.cast(.coff2)) |_| {256 } else if (emit.bin_file.cast(.coff2)) |_| {
250 switch (lowered_inst.encoding.mnemonic) {257 if (reloc.target.is_dll_import) switch (lowered_inst.encoding.mnemonic) {
258 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
259 lowered_inst.ops[0],
260 .{ .mem = .initRip(.ptr, 0) },
261 }, emit.lower.target), reloc_info),
262 .mov => {
263 try emit.encodeInst(try .new(.none, .mov, &.{
264 lowered_inst.ops[0],
265 .{ .mem = .initRip(.ptr, 0) },
266 }, emit.lower.target), reloc_info);
267 try emit.encodeInst(try .new(.none, .mov, &.{
268 lowered_inst.ops[0],
269 .{ .mem = .initSib(lowered_inst.ops[reloc.op_index].mem.sib.ptr_size, .{ .base = .{
270 .reg = lowered_inst.ops[0].reg.to64(),
271 } }) },
272 }, emit.lower.target), &.{});
273 },
274 else => unreachable,
275 } else switch (lowered_inst.encoding.mnemonic) {
251 .lea => try emit.encodeInst(try .new(.none, .lea, &.{276 .lea => try emit.encodeInst(try .new(.none, .lea, &.{
252 lowered_inst.ops[0],277 lowered_inst.ops[0],
253 .{ .mem = .initRip(.none, 0) },278 .{ .mem = .initRip(.none, 0) },
...@@ -717,6 +742,7 @@ const RelocInfo = struct {...@@ -717,6 +742,7 @@ const RelocInfo = struct {
717 const Symbol = struct {742 const Symbol = struct {
718 symbol: link.File.SymbolId,743 symbol: link.File.SymbolId,
719 is_extern: bool,744 is_extern: bool,
745 is_dll_import: bool = false,
720 force_pcrel_direct: bool = false,746 force_pcrel_direct: bool = false,
721 };747 };
722 };748 };
src/link/Coff.zig+18-9
...@@ -2103,7 +2103,7 @@ fn initHeaders(...@@ -2103,7 +2103,7 @@ fn initHeaders(
2103 coff.mf.flags.block_size,2103 coff.mf.flags.block_size,
2104 .{ .read = true, .initialized = true },2104 .{ .read = true, .initialized = true },
2105 )).symbol(coff).node(coff),2105 )).symbol(coff).node(coff),
2106 .{ .alignment = .@"4", .moved = true },2106 .{ .alignment = .@"4" },
2107 );2107 );
2108 coff.nodes.appendAssumeCapacity(.import_directory_table);2108 coff.nodes.appendAssumeCapacity(.import_directory_table);
21092109
...@@ -6115,6 +6115,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6115,6 +6115,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6115 const imp_match = std.mem.startsWith(u8, global_name, imp_prefix);6115 const imp_match = std.mem.startsWith(u8, global_name, imp_prefix);
61166116
6117 // Globals may have the __imp_ prefix already if they are undef externals from another input.6117 // Globals may have the __imp_ prefix already if they are undef externals from another input.
6118 assert(sym.flags.dll_storage_class != .dllexport);
6118 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)6119 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)
6119 .{ gn.name, imp_match }6120 .{ gn.name, imp_match }
6120 else name: {6121 else name: {
...@@ -6722,10 +6723,14 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -6722,10 +6723,14 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
6722 try input_symbol.si.flushMoved(coff);6723 try input_symbol.si.flushMoved(coff);
6723 }6724 }
6724 },6725 },
6725 .import_directory_table => coff.targetStore(6726 .import_directory_table => {
6726 &coff.dataDirectoryPtr(.IMPORT).virtual_address,6727 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
6727 coff.computeNodeRva(ni),6728 if (size > 0)
6728 ),6729 coff.targetStore(
6730 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
6731 coff.computeNodeRva(ni),
6732 );
6733 },
6729 .import_lookup_table => |import_index| coff.targetStore(6734 .import_lookup_table => |import_index| coff.targetStore(
6730 &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,6735 &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
6731 coff.computeNodeRva(ni),6736 coff.computeNodeRva(ni),
...@@ -6936,10 +6941,14 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -6936,10 +6941,14 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
6936 }6941 }
6937 },6942 },
6938 .input_section => {},6943 .input_section => {},
6939 .import_directory_table => coff.targetStore(6944 .import_directory_table => {
6940 &coff.dataDirectoryPtr(.IMPORT).size,6945 const prev_size = coff.targetLoad(&coff.dataDirectoryPtr(.IMPORT).size);
6941 @intCast(size),6946 coff.targetStore(
6942 ),6947 &coff.dataDirectoryPtr(.IMPORT).size,
6948 @intCast(size),
6949 );
6950 if (prev_size == 0) try coff.flushMoved(ni);
6951 },
6943 .import_lookup_table,6952 .import_lookup_table,
6944 .import_address_table,6953 .import_address_table,
6945 .import_hint_name_table,6954 .import_hint_name_table,
test/link.zig+21-12
...@@ -72,6 +72,7 @@ pub fn addCases(ctx: *LinkContext) void {...@@ -72,6 +72,7 @@ pub fn addCases(ctx: *LinkContext) void {
72 if (ctx.includeTest("dynamic-lib-code")) |case| {72 if (ctx.includeTest("dynamic-lib-code")) |case| {
73 const lib = case.addLibrary(.dynamic, .{73 const lib = case.addLibrary(.dynamic, .{
74 .name = "lib",74 .name = "lib",
75 .name_target = false,
75 .zig_source_bytes =76 .zig_source_bytes =
76 \\export fn foo1() callconv(.c) u64 {77 \\export fn foo1() callconv(.c) u64 {
77 \\ return 0x1122334411223344;78 \\ return 0x1122334411223344;
...@@ -91,9 +92,9 @@ pub fn addCases(ctx: *LinkContext) void {...@@ -91,9 +92,9 @@ pub fn addCases(ctx: *LinkContext) void {
91 if (ctx.target.result.os.tag == .windows) {92 if (ctx.target.result.os.tag == .windows) {
92 case.verifyObjdump(lib.getEmittedImplib(), &.{93 case.verifyObjdump(lib.getEmittedImplib(), &.{
93 "-s",94 "-s",
94 "--exports",95 "--exports=sort",
95 "--only-symbol=foo",96 "--only-symbol=foo",
96 }, .{ .sub_name = "implib", .os = true });97 }, .{ .sub_name = "implib", .os = true, .arch = true });
97 }98 }
9899
99 const exe = case.addExecutable(.{100 const exe = case.addExecutable(.{
...@@ -118,9 +119,13 @@ pub fn addCases(ctx: *LinkContext) void {...@@ -118,9 +119,13 @@ pub fn addCases(ctx: *LinkContext) void {
118 if (ctx.includeTest("dynamic-lib-data")) |case| {119 if (ctx.includeTest("dynamic-lib-data")) |case| {
119 const lib = case.addLibrary(.dynamic, .{120 const lib = case.addLibrary(.dynamic, .{
120 .name = "lib",121 .name = "lib",
122 .name_target = false,
121 .zig_source_bytes =123 .zig_source_bytes =
122 \\export var array_foo: [2]u16 = .{ 0xffff, 0xabcd };124 \\export var foo_array: [2]u16 = .{ 0xffff, 0xabcd };
123 \\export var strong_foo: usize = 0x1122334411223344;125 \\export var foo_strong: usize = 0x1122334411223344;
126 \\comptime {
127 \\ @export(&foo_strong, .{ .name = "foo_strong_alias", .linkage = .strong });
128 \\}
124 ,129 ,
125 });130 });
126131
...@@ -131,20 +136,24 @@ pub fn addCases(ctx: *LinkContext) void {...@@ -131,20 +136,24 @@ pub fn addCases(ctx: *LinkContext) void {
131 }, .{});136 }, .{});
132137
133 if (ctx.target.result.os.tag == .windows) {138 if (ctx.target.result.os.tag == .windows) {
134 // TODO: objdump implib on windows139 case.verifyObjdump(lib.getEmittedImplib(), &.{
140 "-s",
141 "--exports=sort",
142 "--only-symbol=foo",
143 }, .{ .sub_name = "implib", .os = true, .arch = true });
135 }144 }
136145
137 const exe = case.addExecutable(.{146 const exe = case.addExecutable(.{
138 .name = "test",147 .name = "test",
139 .zig_source_bytes =148 .zig_source_bytes =
140 \\extern var array_foo: [2]u16;
141 \\extern var strong_foo: usize;
142 \\extern var strong_foo_alias: usize;
143 \\pub fn main() !u8 {149 \\pub fn main() !u8 {
144 \\ return @intFromBool(0x2244668822451255 != 150 \\ const foo_array = @extern(*[2]u16, .{ .name = "foo_array", .is_dll_import = true });
145 \\ array_foo[1] +151 \\ const foo_strong = @extern(*usize, .{ .name = "foo_strong", .is_dll_import = true });
146 \\ strong_foo +152 \\ const foo_strong_alias = @extern(*usize, .{ .name = "foo_strong_alias", .is_dll_import = true });
147 \\ strong_foo_alias);153 \\ return @intFromBool(0x2244668822451255 !=
154 \\ foo_array[1] +
155 \\ foo_strong.* +
156 \\ foo_strong_alias.*);
148 \\}157 \\}
149 ,158 ,
150 });159 });
test/link/snapshots/abs-symbol.x86_64.dmp created+1
...@@ -0,0 +1 @@
1xxxxxxxx ADDR32 xxxxxxxx UNDEF | foo
test/link/snapshots/dynamic-lib-code.implib-windows.dmp+2-2
...@@ -19,7 +19,7 @@ xxxxxxxxxxxxxxxx size_of_data...@@ -19,7 +19,7 @@ xxxxxxxxxxxxxxxx size_of_data
19 NAME name_type 19 NAME name_type
20 symbol name | foo120 symbol name | foo1
21 import name | foo121 import name | foo1
22 dll | dynamic-lib-code-lib-x86_64-windows.win10...win11_dt-msvc-Debug-llvm-lld-libc.dll22 dll | dynamic-lib-code-lib.dll
23 0 version23 0 version
24 8664 machine (AMD64)24 8664 machine (AMD64)
25 0 time_date_stamp25 0 time_date_stamp
...@@ -29,4 +29,4 @@ xxxxxxxxxxxxxxxx size_of_data...@@ -29,4 +29,4 @@ xxxxxxxxxxxxxxxx size_of_data
29 NAME name_type 29 NAME name_type
30 symbol name | foo230 symbol name | foo2
31 import name | foo231 import name | foo2
32 dll | dynamic-lib-code-lib-x86_64-windows.win10...win11_dt-msvc-Debug-llvm-lld-libc.dll32 dll | dynamic-lib-code-lib.dll
test/link/snapshots/dynamic-lib-code.implib-x86_64-windows.dmp created+32
...@@ -0,0 +1,32 @@
1 0 date
2 0 user_id
3 0 group_id
4 0 file_mode
5xxxxxxxxxxxxxxxx size
6 second_linker type
7 | x symbols
8 | x members
9xxxxxxxx __imp_foo1
10xxxxxxxx __imp_foo2
11xxxxxxxx foo1
12xxxxxxxx foo2
13 0 version
14 8664 machine (AMD64)
15 0 time_date_stamp
16xxxxxxxxxxxxxxxx size_of_data
17xxxxxxxxxxxxxxxx hint
18 CODE import_type
19 NAME name_type
20 symbol name | foo1
21 import name | foo1
22 dll | dynamic-lib-code-lib.dll
23 0 version
24 8664 machine (AMD64)
25 0 time_date_stamp
26xxxxxxxxxxxxxxxx size_of_data
27xxxxxxxxxxxxxxxx hint
28 CODE import_type
29 NAME name_type
30 symbol name | foo2
31 import name | foo2
32 dll | dynamic-lib-code-lib.dll
test/link/snapshots/dynamic-lib-data.dmp created+14
...@@ -0,0 +1,14 @@
1Export directory:
2 0 flags
3 0 time_date_stamp
4 0.00 version
5xxxxxxxxxxxxxxxx name_rva
6 1 ordinal_base
7xxxxxxxxxxxxxxxx number_of_entries
8xxxxxxxxxxxxxxxx number_of_names
9xxxxxxxxxxxxxxxx export_address_table_rva
10xxxxxxxxxxxxxxxx name_pointer_table_rva
11xxxxxxxxxxxxxxxx ordinal_table_rva
12xxxx xxxx xxxxxxxx | foo_array
13xxxx xxxx xxxxxxxx | foo_strong
14xxxx xxxx xxxxxxxx | foo_strong_alias
test/link/snapshots/dynamic-lib-data.implib-x86_64-windows.dmp created+41
...@@ -0,0 +1,41 @@
1 0 date
2 0 user_id
3 0 group_id
4 0 file_mode
5xxxxxxxxxxxxxxxx size
6 second_linker type
7 | x symbols
8 | x members
9xxxxxxxx __imp_foo_array
10xxxxxxxx __imp_foo_strong
11xxxxxxxx __imp_foo_strong_alias
12 0 version
13 8664 machine (AMD64)
14 0 time_date_stamp
15xxxxxxxxxxxxxxxx size_of_data
16xxxxxxxxxxxxxxxx hint
17 DATA import_type
18 NAME name_type
19 symbol name | foo_array
20 import name | foo_array
21 dll | dynamic-lib-data-lib.dll
22 0 version
23 8664 machine (AMD64)
24 0 time_date_stamp
25xxxxxxxxxxxxxxxx size_of_data
26xxxxxxxxxxxxxxxx hint
27 DATA import_type
28 NAME name_type
29 symbol name | foo_strong
30 import name | foo_strong
31 dll | dynamic-lib-data-lib.dll
32 0 version
33 8664 machine (AMD64)
34 0 time_date_stamp
35xxxxxxxxxxxxxxxx size_of_data
36xxxxxxxxxxxxxxxx hint
37 DATA import_type
38 NAME name_type
39 symbol name | foo_strong_alias
40 import name | foo_strong_alias
41 dll | dynamic-lib-data-lib.dll
test/src/Link.zig+2-1
...@@ -185,7 +185,8 @@ pub const Case = struct {...@@ -185,7 +185,8 @@ pub const Case = struct {
185 if (try snapshotNameInner(w, scope.link_libc, &sep))185 if (try snapshotNameInner(w, scope.link_libc, &sep))
186 try w.writeAll(if (ctx.link_libc) "libc" else "no-libc");186 try w.writeAll(if (ctx.link_libc) "libc" else "no-libc");
187187
188 if (sep == '-') try w.writeByte('.');188 if (sep == '-') sep = '.';
189 try w.writeByte(sep);
189 try w.writeAll("dmp");190 try w.writeAll("dmp");
190191
191 return try snapshot_name.toOwnedSlice();192 return try snapshot_name.toOwnedSlice();
test/standalone/shared_library/mathtest.zig+2
...@@ -1,3 +1,5 @@...@@ -1,3 +1,5 @@
1export var exported_var: i32 = 9999;
2
1export fn add(a: i32, b: i32) i32 {3export fn add(a: i32, b: i32) i32 {
2 return a + b;4 return a + b;
3}5}
test/standalone/shared_library/test.c+9
...@@ -7,7 +7,16 @@...@@ -7,7 +7,16 @@
7#include <stdint.h>7#include <stdint.h>
8int32_t add(int32_t a, int32_t b);8int32_t add(int32_t a, int32_t b);
99
10#if _WIN32
11#define IMPORT __declspec(dllimport)
12#else
13#define IMPORT
14#endif
15
16extern IMPORT int32_t exported_var;
17
10int main(int argc, char **argv) {18int main(int argc, char **argv) {
11 assert(add(42, 1337) == 1379);19 assert(add(42, 1337) == 1379);
20 assert(exported_var == 9999);
12 return 0;21 return 0;
13}22}