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;
1111
1212const Options = struct {
1313 exports: bool,
14 exports_sort: bool,
1415 file_headers: bool,
1516 imports: bool,
1617 input_path: []const u8,
......@@ -53,6 +54,7 @@ pub fn main(init: std.process.Init) !void {
5354 var i: usize = 1;
5455
5556 var opt_exports: ?bool = null;
57 var opt_exports_sort: ?bool = null;
5658 var opt_file_headers: ?bool = null;
5759 var opt_imports: ?bool = null;
5860 var opt_input_path: ?[]const u8 = null;
......@@ -81,9 +83,11 @@ pub fn main(init: std.process.Init) !void {
8183 opt_section_headers = true;
8284 opt_symbols = true;
8385 opt_relocs = true;
84 } else if (mem.eql(u8, arg, "--exports")) {
86 } else if (mem.startsWith(u8, arg, "--exports")) {
8587 opt_exports = true;
8688 opt_linker_member = .second_linker;
89 if (mem.eql(u8, arg["--exports".len..], "=sort"))
90 opt_exports_sort = true;
8791 } else if (mem.eql(u8, arg, "--file-headers")) {
8892 opt_file_headers = true;
8993 } else if (mem.eql(u8, arg, "--imports")) {
......@@ -156,6 +160,7 @@ pub fn main(init: std.process.Init) !void {
156160 const opts: Options = .{
157161 .input_path = opt_input_path orelse fatal("missing input file path positional argument", .{}),
158162 .exports = opt_exports orelse false,
163 .exports_sort = opt_exports_sort orelse false,
159164 .file_headers = opt_file_headers orelse false,
160165 .imports = opt_imports orelse false,
161166 .linker_member = opt_linker_member,
......@@ -355,9 +360,12 @@ const coff = struct {
355360 const r = &fr.interface;
356361 r.toss(std.coff.archive_signature.len);
357362
358 var members: std.ArrayList(struct {
363 const Member = struct {
359364 offset: u32,
360 }) = .empty;
365 order: ?u32,
366 };
367
368 var members: std.ArrayList(Member) = .empty;
361369 defer members.deinit(gpa);
362370 var symbol_member_indices: std.ArrayList(u32) = .empty;
363371 defer symbol_member_indices.deinit(gpa);
......@@ -415,6 +423,10 @@ const coff = struct {
415423 for (0..num_symbols) |symbol_i| {
416424 const symbol = r.takeDelimiter(0) catch |err|
417425 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
418430 const offset = std.mem.readInt(u32, offsets[symbol_i * 4 ..][0..4], .big);
419431 try w.print("{f} {s}\n", .{
420432 fmtIntField(d, offset, .{ .kind = .va }),
......@@ -441,6 +453,7 @@ const coff = struct {
441453 for (0..num_members) |_|
442454 members.addOneAssumeCapacity().* = .{
443455 .offset = try r.takeInt(u32, .little),
456 .order = null,
444457 };
445458
446459 const num_symbols = try r.takeInt(u32, .little);
......@@ -451,14 +464,42 @@ const coff = struct {
451464 if (dump_header)
452465 try w.print(
453466 \\{t: >16} type
454 \\ | {d} symbols
455 \\ | {d} members
467 \\ | {f} symbols
468 \\ | {f} members
456469 \\
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
459476 try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols);
460 for (0..num_symbols) |_|
461 symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1;
477 for (0..num_symbols) |order| {
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
463504 if (d.opts.linker_member == .second_linker) {
464505 if (d.element(.@"table-header"))
......@@ -480,6 +521,9 @@ const coff = struct {
480521 else => |e| return e,
481522 }) |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
483527 try w.print("{f} {s}\n", .{
484528 fmtIntField(
485529 d,
......@@ -844,8 +888,8 @@ const coff = struct {
844888 section_i + 1,
845889 raw_name,
846890 fmtIntField(d, section.header.virtual_address, .{ .kind = .va }),
847 fmtIntField(d, section.header.virtual_size, .{ .kind = .size, .width = 9 }),
848 fmtIntField(d, section.header.size_of_raw_data, .{ .kind = .size, .width = 9 }),
891 fmtIntField(d, section.header.virtual_size, .{ .kind = .size, .width = .{ .explicit = 9 } }),
892 fmtIntField(d, section.header.size_of_raw_data, .{ .kind = .size, .width = .{ .explicit = 9 } }),
849893 fmtIntField(d, section.header.pointer_to_raw_data, .{ .kind = .va }),
850894 fmtIntField(d, section.header.pointer_to_relocations, .{ .kind = .va }),
851895 fmtIntField(d, section.header.pointer_to_linenumbers, .{ .kind = .va }),
......@@ -1309,14 +1353,16 @@ const coff = struct {
13091353
13101354 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});
13131358 try dumpHeader(d, Entry, &entry, struct {});
13141359
1315 try w.print(
1316 \\
1317 \\ Ord Hint Name
1318 \\
1319 , .{});
1360 if (d.element(.@"table-header"))
1361 try w.print(
1362 \\
1363 \\ Ord Hint Name
1364 \\
1365 , .{});
13201366
13211367 const ilt_section = sectionContainingRva(
13221368 rva_index,
......@@ -1565,7 +1611,7 @@ const coff = struct {
15651611
15661612 const FormatIntField = struct {
15671613 val: ?u64,
1568 width: usize,
1614 width: ?usize,
15691615 zero_fill: bool,
15701616 };
15711617
......@@ -1574,14 +1620,22 @@ const coff = struct {
15741620 val: anytype,
15751621 params: struct {
15761622 kind: ?FieldKind = null,
1577 width: ?usize = null,
1623 width: union(enum) {
1624 fit_max,
1625 auto,
1626 explicit: usize,
1627 } = .fit_max,
15781628 zero_fill: bool = false,
15791629 },
15801630 ) std.fmt.Alt(FormatIntField, intFieldString) {
15811631 return .{
15821632 .data = .{
15831633 .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 },
15851639 .zero_fill = params.zero_fill,
15861640 },
15871641 };
......@@ -1594,7 +1648,7 @@ const coff = struct {
15941648 .alignment = .right,
15951649 .fill = if (field.zero_fill) '0' else ' ',
15961650 });
1597 } else try w.splatByteAll('x', field.width);
1651 } else try w.splatByteAll('x', field.width orelse 1);
15981652 }
15991653
16001654 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 {
16281682 if (std.mem.startsWith(u8, name, "number_") or
16291683 std.mem.startsWith(u8, name, "size"))
16301684 return .size;
1685 if (std.mem.startsWith(u8, name, "hint"))
1686 return .ord;
16311687 return null;
16321688 }
16331689
......@@ -1645,7 +1701,7 @@ const coff = struct {
16451701 switch (@typeInfo(field.type)) {
16461702 .int => try d.w.print("{f} {s}\n", .{ fmtIntField(d, val.*, .{
16471703 .kind = comptime fieldKind(field.name),
1648 .width = 16,
1704 .width = .{ .explicit = 16 },
16491705 }), field.name }),
16501706 .@"enum" => try d.w.print("{x: >16} {s} ({t})\n", .{ val.*, field.name, val.* }),
16511707 .@"struct" => |s| {
......@@ -1690,7 +1746,9 @@ const usage =
16901746 \\Options:
16911747 \\ -h, --help Print this help and exit
16921748 \\ --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.
16941752 \\ --file-headers Display file-format specific headers
16951753 \\ --imports Display imported symbols
16961754 \\ --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 {
113113 .default => true,
114114 .hidden, .protected => false,
115115 },
116 .is_dll_import = @"extern".is_dll_import,
116117 .force_pcrel_direct = switch (@"extern".relocation) {
117118 .any => false,
118119 .pcrel => true,
......@@ -171,7 +172,13 @@ pub fn emitMir(emit: *Emit) Error!void {
171172 switch (lowered_inst.encoding.mnemonic) {
172173 .call => {
173174 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 }
175182 continue :lowered_inst;
176183 },
177184 else => {},
......@@ -247,7 +254,25 @@ pub fn emitMir(emit: *Emit) Error!void {
247254 else => unreachable,
248255 }
249256 } 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) {
251276 .lea => try emit.encodeInst(try .new(.none, .lea, &.{
252277 lowered_inst.ops[0],
253278 .{ .mem = .initRip(.none, 0) },
......@@ -717,6 +742,7 @@ const RelocInfo = struct {
717742 const Symbol = struct {
718743 symbol: link.File.SymbolId,
719744 is_extern: bool,
745 is_dll_import: bool = false,
720746 force_pcrel_direct: bool = false,
721747 };
722748 };
src/link/Coff.zig+18-9
......@@ -2103,7 +2103,7 @@ fn initHeaders(
21032103 coff.mf.flags.block_size,
21042104 .{ .read = true, .initialized = true },
21052105 )).symbol(coff).node(coff),
2106 .{ .alignment = .@"4", .moved = true },
2106 .{ .alignment = .@"4" },
21072107 );
21082108 coff.nodes.appendAssumeCapacity(.import_directory_table);
21092109
......@@ -6115,6 +6115,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
61156115 const imp_match = std.mem.startsWith(u8, global_name, imp_prefix);
61166116
61176117 // Globals may have the __imp_ prefix already if they are undef externals from another input.
6118 assert(sym.flags.dll_storage_class != .dllexport);
61186119 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)
61196120 .{ gn.name, imp_match }
61206121 else name: {
......@@ -6722,10 +6723,14 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
67226723 try input_symbol.si.flushMoved(coff);
67236724 }
67246725 },
6725 .import_directory_table => coff.targetStore(
6726 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
6727 coff.computeNodeRva(ni),
6728 ),
6726 .import_directory_table => {
6727 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
6728 if (size > 0)
6729 coff.targetStore(
6730 &coff.dataDirectoryPtr(.IMPORT).virtual_address,
6731 coff.computeNodeRva(ni),
6732 );
6733 },
67296734 .import_lookup_table => |import_index| coff.targetStore(
67306735 &coff.importDirectoryEntryPtr(import_index).import_lookup_table_rva,
67316736 coff.computeNodeRva(ni),
......@@ -6936,10 +6941,14 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
69366941 }
69376942 },
69386943 .input_section => {},
6939 .import_directory_table => coff.targetStore(
6940 &coff.dataDirectoryPtr(.IMPORT).size,
6941 @intCast(size),
6942 ),
6944 .import_directory_table => {
6945 const prev_size = coff.targetLoad(&coff.dataDirectoryPtr(.IMPORT).size);
6946 coff.targetStore(
6947 &coff.dataDirectoryPtr(.IMPORT).size,
6948 @intCast(size),
6949 );
6950 if (prev_size == 0) try coff.flushMoved(ni);
6951 },
69436952 .import_lookup_table,
69446953 .import_address_table,
69456954 .import_hint_name_table,
test/link.zig+21-12
......@@ -72,6 +72,7 @@ pub fn addCases(ctx: *LinkContext) void {
7272 if (ctx.includeTest("dynamic-lib-code")) |case| {
7373 const lib = case.addLibrary(.dynamic, .{
7474 .name = "lib",
75 .name_target = false,
7576 .zig_source_bytes =
7677 \\export fn foo1() callconv(.c) u64 {
7778 \\ return 0x1122334411223344;
......@@ -91,9 +92,9 @@ pub fn addCases(ctx: *LinkContext) void {
9192 if (ctx.target.result.os.tag == .windows) {
9293 case.verifyObjdump(lib.getEmittedImplib(), &.{
9394 "-s",
94 "--exports",
95 "--exports=sort",
9596 "--only-symbol=foo",
96 }, .{ .sub_name = "implib", .os = true });
97 }, .{ .sub_name = "implib", .os = true, .arch = true });
9798 }
9899
99100 const exe = case.addExecutable(.{
......@@ -118,9 +119,13 @@ pub fn addCases(ctx: *LinkContext) void {
118119 if (ctx.includeTest("dynamic-lib-data")) |case| {
119120 const lib = case.addLibrary(.dynamic, .{
120121 .name = "lib",
122 .name_target = false,
121123 .zig_source_bytes =
122 \\export var array_foo: [2]u16 = .{ 0xffff, 0xabcd };
123 \\export var strong_foo: usize = 0x1122334411223344;
124 \\export var foo_array: [2]u16 = .{ 0xffff, 0xabcd };
125 \\export var foo_strong: usize = 0x1122334411223344;
126 \\comptime {
127 \\ @export(&foo_strong, .{ .name = "foo_strong_alias", .linkage = .strong });
128 \\}
124129 ,
125130 });
126131
......@@ -131,20 +136,24 @@ pub fn addCases(ctx: *LinkContext) void {
131136 }, .{});
132137
133138 if (ctx.target.result.os.tag == .windows) {
134 // TODO: objdump implib on windows
139 case.verifyObjdump(lib.getEmittedImplib(), &.{
140 "-s",
141 "--exports=sort",
142 "--only-symbol=foo",
143 }, .{ .sub_name = "implib", .os = true, .arch = true });
135144 }
136145
137146 const exe = case.addExecutable(.{
138147 .name = "test",
139148 .zig_source_bytes =
140 \\extern var array_foo: [2]u16;
141 \\extern var strong_foo: usize;
142 \\extern var strong_foo_alias: usize;
143149 \\pub fn main() !u8 {
144 \\ return @intFromBool(0x2244668822451255 !=
145 \\ array_foo[1] +
146 \\ strong_foo +
147 \\ strong_foo_alias);
150 \\ const foo_array = @extern(*[2]u16, .{ .name = "foo_array", .is_dll_import = true });
151 \\ const foo_strong = @extern(*usize, .{ .name = "foo_strong", .is_dll_import = true });
152 \\ const foo_strong_alias = @extern(*usize, .{ .name = "foo_strong_alias", .is_dll_import = true });
153 \\ return @intFromBool(0x2244668822451255 !=
154 \\ foo_array[1] +
155 \\ foo_strong.* +
156 \\ foo_strong_alias.*);
148157 \\}
149158 ,
150159 });
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
1919 NAME name_type
2020 symbol name | foo1
2121 import name | foo1
22 dll | dynamic-lib-code-lib-x86_64-windows.win10...win11_dt-msvc-Debug-llvm-lld-libc.dll
22 dll | dynamic-lib-code-lib.dll
2323 0 version
2424 8664 machine (AMD64)
2525 0 time_date_stamp
......@@ -29,4 +29,4 @@ xxxxxxxxxxxxxxxx size_of_data
2929 NAME name_type
3030 symbol name | foo2
3131 import name | foo2
32 dll | dynamic-lib-code-lib-x86_64-windows.win10...win11_dt-msvc-Debug-llvm-lld-libc.dll
32 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 {
185185 if (try snapshotNameInner(w, scope.link_libc, &sep))
186186 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);
189190 try w.writeAll("dmp");
190191
191192 return try snapshot_name.toOwnedSlice();
test/standalone/shared_library/mathtest.zig+2
......@@ -1,3 +1,5 @@
1export var exported_var: i32 = 9999;
2
13export fn add(a: i32, b: i32) i32 {
24 return a + b;
35}
test/standalone/shared_library/test.c+9
......@@ -7,7 +7,16 @@
77#include <stdint.h>
88int32_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
1018int main(int argc, char **argv) {
1119 assert(add(42, 1337) == 1379);
20 assert(exported_var == 9999);
1221 return 0;
1322}