authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-24 19:43:45-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-26 00:15:07+02:00
loge7d74e49b0dd91e679aa9063311a4513339cedd0
tree4eafbdc644d367df003a76b9d0d7678224b38ac2
parent23bcb8148fb12d86b003708b6410d98a986d2d9f

declare linker test bankruptcy

The active contributors and maintainers of Zig's linker code have generally found the current linker test harness to be cumbersome. The tests require a lot of maintenance, but do not provide a lot of coverage, and when they fail it is painful to troubleshoot. Furthermore, as part of working on #31691, I don't want to port over the CheckObject step, because I don't like the code anyway. The plan forward is to start enhancing `zig objdump` to assist in linker development, as well as using it as the basis for snapshot testing. We absolutely need linker test coverage, but we need to try to improve these things about the next attempt: * less effort to create and maintain tests * less CPU overhead - we should be able to add a lot of tests without adding a lot of CI time. * more helpful failures. A failed linker test should provide the next steps a developer can take to understand why the test failed. * a goal of porting over all of LLD's test suite, or at least the good ones. I'm not going to open an issue to track the lost linker test coverage, because there was already so much lack of coverage for linker stuff. However I will open issues to track this lost coverage: * the deleted checks from test/standalone/glibc_compat/build.zig * the deleted checks from test/standalone/compiler_rt_panic/build.zig * the deleted checks from test/standalone/ios/build.zig

60 files changed, 0 insertions(+), 11870 deletions(-)

build.zig-1
...@@ -618,7 +618,6 @@ pub fn build(b: *std.Build) !void {...@@ -618,7 +618,6 @@ pub fn build(b: *std.Build) !void {
618 .skip_llvm = skip_llvm,618 .skip_llvm = skip_llvm,
619 .max_rss = 3_300_000_000,619 .max_rss = 3_300_000_000,
620 }));620 }));
621 test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows));
622 test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native));621 test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native));
623 test_step.dependOn(tests.addErrorTraceTests(b, test_filters, optimize_modes, skip_non_native));622 test_step.dependOn(tests.addErrorTraceTests(b, test_filters, optimize_modes, skip_non_native));
624 test_step.dependOn(tests.addCliTests(b));623 test_step.dependOn(tests.addCliTests(b));
lib/std/Build/Step.zig-3
...@@ -176,7 +176,6 @@ pub const Id = enum {...@@ -176,7 +176,6 @@ pub const Id = enum {
176 .update_source_files => UpdateSourceFiles,176 .update_source_files => UpdateSourceFiles,
177 .run => Run,177 .run => Run,
178 .check_file => CheckFile,178 .check_file => CheckFile,
179 .check_object => CheckObject,
180 .config_header => ConfigHeader,179 .config_header => ConfigHeader,
181 .objcopy => ObjCopy,180 .objcopy => ObjCopy,
182 .options => Options,181 .options => Options,
...@@ -186,7 +185,6 @@ pub const Id = enum {...@@ -186,7 +185,6 @@ pub const Id = enum {
186};185};
187186
188pub const CheckFile = @import("Step/CheckFile.zig");187pub const CheckFile = @import("Step/CheckFile.zig");
189pub const CheckObject = @import("Step/CheckObject.zig");
190pub const ConfigHeader = @import("Step/ConfigHeader.zig");188pub const ConfigHeader = @import("Step/ConfigHeader.zig");
191pub const Fail = @import("Step/Fail.zig");189pub const Fail = @import("Step/Fail.zig");
192pub const Fmt = @import("Step/Fmt.zig");190pub const Fmt = @import("Step/Fmt.zig");
...@@ -1004,7 +1002,6 @@ pub fn invalidateResult(step: *Step, gpa: Allocator) bool {...@@ -1004,7 +1002,6 @@ pub fn invalidateResult(step: *Step, gpa: Allocator) bool {
10041002
1005test {1003test {
1006 _ = CheckFile;1004 _ = CheckFile;
1007 _ = CheckObject;
1008 _ = Fail;1005 _ = Fail;
1009 _ = Fmt;1006 _ = Fmt;
1010 _ = InstallArtifact;1007 _ = InstallArtifact;
lib/std/Build/Step/CheckObject.zig deleted-2764
...@@ -1,2764 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const elf = std.elf;
4const fs = std.fs;
5const macho = std.macho;
6const math = std.math;
7const mem = std.mem;
8const testing = std.testing;
9const Writer = std.Io.Writer;
10
11const CheckObject = @This();
12
13const Allocator = mem.Allocator;
14const Step = std.Build.Step;
15
16pub const base_id: Step.Id = .check_object;
17
18step: Step,
19source: std.Build.LazyPath,
20max_bytes: usize = 20 * 1024 * 1024,
21checks: std.array_list.Managed(Check),
22obj_format: std.Target.ObjectFormat,
23
24pub fn create(
25 owner: *std.Build,
26 source: std.Build.LazyPath,
27 obj_format: std.Target.ObjectFormat,
28) *CheckObject {
29 const gpa = owner.allocator;
30 const check_object = gpa.create(CheckObject) catch @panic("OOM");
31 check_object.* = .{
32 .step = .init(.{
33 .id = base_id,
34 .name = "CheckObject",
35 .owner = owner,
36 .makeFn = make,
37 }),
38 .source = source.dupe(owner),
39 .checks = std.array_list.Managed(Check).init(gpa),
40 .obj_format = obj_format,
41 };
42 check_object.source.addStepDependencies(&check_object.step);
43 return check_object;
44}
45
46const SearchPhrase = struct {
47 string: []const u8,
48 lazy_path: ?std.Build.LazyPath = null,
49
50 fn resolve(phrase: SearchPhrase, b: *std.Build, step: *Step) []const u8 {
51 const lazy_path = phrase.lazy_path orelse return phrase.string;
52 return b.fmt("{s} {s}", .{ phrase.string, lazy_path.getPath2(b, step) });
53 }
54};
55
56/// There five types of actions currently supported:
57/// .exact - will do an exact match against the haystack
58/// .contains - will check for existence within the haystack
59/// .not_present - will check for non-existence within the haystack
60/// .extract - will do an exact match and extract into a variable enclosed within `{name}` braces
61/// .compute_cmp - will perform an operation on the extracted global variables
62/// using the MatchAction. It currently only supports an addition. The operation is required
63/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
64/// to avoid any parsing really).
65/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
66/// they could then be added with this simple program `vmaddr entryoff +`.
67const Action = struct {
68 tag: enum { exact, contains, not_present, extract, compute_cmp },
69 phrase: SearchPhrase,
70 expected: ?ComputeCompareExpected = null,
71
72 /// Returns true if the `phrase` is an exact match with the haystack and variable was successfully extracted.
73 fn extract(
74 act: Action,
75 b: *std.Build,
76 step: *Step,
77 haystack: []const u8,
78 global_vars: anytype,
79 ) !bool {
80 assert(act.tag == .extract);
81 const hay = mem.trim(u8, haystack, " ");
82 const phrase = mem.trim(u8, act.phrase.resolve(b, step), " ");
83
84 var candidate_vars: std.array_list.Managed(struct { name: []const u8, value: u64 }) = .init(b.allocator);
85 var hay_it = mem.tokenizeScalar(u8, hay, ' ');
86 var needle_it = mem.tokenizeScalar(u8, phrase, ' ');
87
88 while (needle_it.next()) |needle_tok| {
89 const hay_tok = hay_it.next() orelse break;
90 if (mem.startsWith(u8, needle_tok, "{")) {
91 const closing_brace = mem.find(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
92 if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
93
94 const name = needle_tok[1..closing_brace];
95 if (name.len == 0) return error.MissingBraceValue;
96 const value = std.fmt.parseInt(u64, hay_tok, 16) catch return false;
97 try candidate_vars.append(.{
98 .name = name,
99 .value = value,
100 });
101 } else {
102 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
103 }
104 }
105
106 if (candidate_vars.items.len == 0) return false;
107
108 for (candidate_vars.items) |cv| try global_vars.putNoClobber(cv.name, cv.value);
109
110 return true;
111 }
112
113 /// Returns true if the `phrase` is an exact match with the haystack.
114 fn exact(
115 act: Action,
116 b: *std.Build,
117 step: *Step,
118 haystack: []const u8,
119 ) bool {
120 assert(act.tag == .exact);
121 const hay = mem.trim(u8, haystack, " ");
122 const phrase = mem.trim(u8, act.phrase.resolve(b, step), " ");
123 return mem.eql(u8, hay, phrase);
124 }
125
126 /// Returns true if the `phrase` exists within the haystack.
127 fn contains(
128 act: Action,
129 b: *std.Build,
130 step: *Step,
131 haystack: []const u8,
132 ) bool {
133 assert(act.tag == .contains);
134 const hay = mem.trim(u8, haystack, " ");
135 const phrase = mem.trim(u8, act.phrase.resolve(b, step), " ");
136 return mem.find(u8, hay, phrase) != null;
137 }
138
139 /// Returns true if the `phrase` does not exist within the haystack.
140 fn notPresent(
141 act: Action,
142 b: *std.Build,
143 step: *Step,
144 haystack: []const u8,
145 ) bool {
146 assert(act.tag == .not_present);
147 return !contains(.{
148 .tag = .contains,
149 .phrase = act.phrase,
150 .expected = act.expected,
151 }, b, step, haystack);
152 }
153
154 /// Will return true if the `phrase` is correctly parsed into an RPN program and
155 /// its reduced, computed value compares using `op` with the expected value, either
156 /// a literal or another extracted variable.
157 fn computeCmp(act: Action, b: *std.Build, step: *Step, global_vars: anytype) !bool {
158 const gpa = step.owner.allocator;
159 const phrase = act.phrase.resolve(b, step);
160 var op_stack = std.array_list.Managed(enum { add, sub, mod, mul }).init(gpa);
161 var values = std.array_list.Managed(u64).init(gpa);
162
163 var it = mem.tokenizeScalar(u8, phrase, ' ');
164 while (it.next()) |next| {
165 if (mem.eql(u8, next, "+")) {
166 try op_stack.append(.add);
167 } else if (mem.eql(u8, next, "-")) {
168 try op_stack.append(.sub);
169 } else if (mem.eql(u8, next, "%")) {
170 try op_stack.append(.mod);
171 } else if (mem.eql(u8, next, "*")) {
172 try op_stack.append(.mul);
173 } else {
174 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
175 break :blk global_vars.get(next) orelse {
176 try step.addError(
177 \\
178 \\========= variable was not extracted: ===========
179 \\{s}
180 \\=================================================
181 , .{next});
182 return error.UnknownVariable;
183 };
184 };
185 try values.append(val);
186 }
187 }
188
189 var op_i: usize = 1;
190 var reduced: u64 = values.items[0];
191 for (op_stack.items) |op| {
192 const other = values.items[op_i];
193 switch (op) {
194 .add => {
195 reduced += other;
196 },
197 .sub => {
198 reduced -= other;
199 },
200 .mod => {
201 reduced %= other;
202 },
203 .mul => {
204 reduced *= other;
205 },
206 }
207 op_i += 1;
208 }
209
210 const exp_value = switch (act.expected.?.value) {
211 .variable => |name| global_vars.get(name) orelse {
212 try step.addError(
213 \\
214 \\========= variable was not extracted: ===========
215 \\{s}
216 \\=================================================
217 , .{name});
218 return error.UnknownVariable;
219 },
220 .literal => |x| x,
221 };
222 return math.compare(reduced, act.expected.?.op, exp_value);
223 }
224};
225
226const ComputeCompareExpected = struct {
227 op: math.CompareOperator,
228 value: union(enum) {
229 variable: []const u8,
230 literal: u64,
231 },
232
233 pub fn format(value: ComputeCompareExpected, w: *Writer) Writer.Error!void {
234 try w.print("{t} ", .{value.op});
235 switch (value.value) {
236 .variable => |name| try w.writeAll(name),
237 .literal => |x| try w.print("{x}", .{x}),
238 }
239 }
240};
241
242const Check = struct {
243 kind: Kind,
244 payload: Payload,
245 data: std.array_list.Managed(u8),
246 actions: std.array_list.Managed(Action),
247
248 fn create(allocator: Allocator, kind: Kind) Check {
249 return .{
250 .kind = kind,
251 .payload = .{ .none = {} },
252 .data = std.array_list.Managed(u8).init(allocator),
253 .actions = std.array_list.Managed(Action).init(allocator),
254 };
255 }
256
257 fn dumpSection(allocator: Allocator, name: [:0]const u8) Check {
258 var check = Check.create(allocator, .dump_section);
259 const off: u32 = @intCast(check.data.items.len);
260 check.data.print("{s}\x00", .{name}) catch @panic("OOM");
261 check.payload = .{ .dump_section = off };
262 return check;
263 }
264
265 fn extract(check: *Check, phrase: SearchPhrase) void {
266 check.actions.append(.{
267 .tag = .extract,
268 .phrase = phrase,
269 }) catch @panic("OOM");
270 }
271
272 fn exact(check: *Check, phrase: SearchPhrase) void {
273 check.actions.append(.{
274 .tag = .exact,
275 .phrase = phrase,
276 }) catch @panic("OOM");
277 }
278
279 fn contains(check: *Check, phrase: SearchPhrase) void {
280 check.actions.append(.{
281 .tag = .contains,
282 .phrase = phrase,
283 }) catch @panic("OOM");
284 }
285
286 fn notPresent(check: *Check, phrase: SearchPhrase) void {
287 check.actions.append(.{
288 .tag = .not_present,
289 .phrase = phrase,
290 }) catch @panic("OOM");
291 }
292
293 fn computeCmp(check: *Check, phrase: SearchPhrase, expected: ComputeCompareExpected) void {
294 check.actions.append(.{
295 .tag = .compute_cmp,
296 .phrase = phrase,
297 .expected = expected,
298 }) catch @panic("OOM");
299 }
300
301 const Kind = enum {
302 headers,
303 symtab,
304 indirect_symtab,
305 dynamic_symtab,
306 archive_symtab,
307 dynamic_section,
308 dyld_rebase,
309 dyld_bind,
310 dyld_weak_bind,
311 dyld_lazy_bind,
312 exports,
313 compute_compare,
314 dump_section,
315 };
316
317 const Payload = union {
318 none: void,
319 /// Null-delimited string in the 'data' buffer.
320 dump_section: u32,
321 };
322};
323
324/// Creates a new empty sequence of actions.
325fn checkStart(check_object: *CheckObject, kind: Check.Kind) void {
326 const check = Check.create(check_object.step.owner.allocator, kind);
327 check_object.checks.append(check) catch @panic("OOM");
328}
329
330/// Adds an exact match phrase to the latest created Check.
331pub fn checkExact(check_object: *CheckObject, phrase: []const u8) void {
332 check_object.checkExactInner(phrase, null);
333}
334
335/// Like `checkExact()` but takes an additional argument `LazyPath` which will be
336/// resolved to a full search query in `make()`.
337pub fn checkExactPath(check_object: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void {
338 check_object.checkExactInner(phrase, lazy_path);
339}
340
341fn checkExactInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
342 assert(check_object.checks.items.len > 0);
343 const last = &check_object.checks.items[check_object.checks.items.len - 1];
344 last.exact(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path });
345}
346
347/// Adds a fuzzy match phrase to the latest created Check.
348pub fn checkContains(check_object: *CheckObject, phrase: []const u8) void {
349 check_object.checkContainsInner(phrase, null);
350}
351
352/// Like `checkContains()` but takes an additional argument `lazy_path` which will be
353/// resolved to a full search query in `make()`.
354pub fn checkContainsPath(
355 check_object: *CheckObject,
356 phrase: []const u8,
357 lazy_path: std.Build.LazyPath,
358) void {
359 check_object.checkContainsInner(phrase, lazy_path);
360}
361
362fn checkContainsInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
363 assert(check_object.checks.items.len > 0);
364 const last = &check_object.checks.items[check_object.checks.items.len - 1];
365 last.contains(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path });
366}
367
368/// Adds an exact match phrase with variable extractor to the latest created Check.
369pub fn checkExtract(check_object: *CheckObject, phrase: []const u8) void {
370 check_object.checkExtractInner(phrase, null);
371}
372
373/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be
374/// resolved to a full search query in `make()`.
375pub fn checkExtractLazyPath(check_object: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void {
376 check_object.checkExtractInner(phrase, lazy_path);
377}
378
379fn checkExtractInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
380 assert(check_object.checks.items.len > 0);
381 const last = &check_object.checks.items[check_object.checks.items.len - 1];
382 last.extract(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path });
383}
384
385/// Adds another searched phrase to the latest created Check
386/// however ensures there is no matching phrase in the output.
387pub fn checkNotPresent(check_object: *CheckObject, phrase: []const u8) void {
388 check_object.checkNotPresentInner(phrase, null);
389}
390
391/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be
392/// resolved to a full search query in `make()`.
393pub fn checkNotPresentLazyPath(check_object: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void {
394 check_object.checkNotPresentInner(phrase, lazy_path);
395}
396
397fn checkNotPresentInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void {
398 assert(check_object.checks.items.len > 0);
399 const last = &check_object.checks.items[check_object.checks.items.len - 1];
400 last.notPresent(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path });
401}
402
403/// Creates a new check checking in the file headers (section, program headers, etc.).
404pub fn checkInHeaders(check_object: *CheckObject) void {
405 check_object.checkStart(.headers);
406}
407
408/// Creates a new check checking specifically symbol table parsed and dumped from the object
409/// file.
410pub fn checkInSymtab(check_object: *CheckObject) void {
411 const label = switch (check_object.obj_format) {
412 .macho => MachODumper.symtab_label,
413 .elf => ElfDumper.symtab_label,
414 .wasm => WasmDumper.symtab_label,
415 .coff => @panic("TODO symtab for coff"),
416 else => @panic("TODO other file formats"),
417 };
418 check_object.checkStart(.symtab);
419 check_object.checkExact(label);
420}
421
422/// Creates a new check checking specifically dyld rebase opcodes contents parsed and dumped
423/// from the object file.
424/// This check is target-dependent and applicable to MachO only.
425pub fn checkInDyldRebase(check_object: *CheckObject) void {
426 const label = switch (check_object.obj_format) {
427 .macho => MachODumper.dyld_rebase_label,
428 else => @panic("Unsupported target platform"),
429 };
430 check_object.checkStart(.dyld_rebase);
431 check_object.checkExact(label);
432}
433
434/// Creates a new check checking specifically dyld bind opcodes contents parsed and dumped
435/// from the object file.
436/// This check is target-dependent and applicable to MachO only.
437pub fn checkInDyldBind(check_object: *CheckObject) void {
438 const label = switch (check_object.obj_format) {
439 .macho => MachODumper.dyld_bind_label,
440 else => @panic("Unsupported target platform"),
441 };
442 check_object.checkStart(.dyld_bind);
443 check_object.checkExact(label);
444}
445
446/// Creates a new check checking specifically dyld weak bind opcodes contents parsed and dumped
447/// from the object file.
448/// This check is target-dependent and applicable to MachO only.
449pub fn checkInDyldWeakBind(check_object: *CheckObject) void {
450 const label = switch (check_object.obj_format) {
451 .macho => MachODumper.dyld_weak_bind_label,
452 else => @panic("Unsupported target platform"),
453 };
454 check_object.checkStart(.dyld_weak_bind);
455 check_object.checkExact(label);
456}
457
458/// Creates a new check checking specifically dyld lazy bind opcodes contents parsed and dumped
459/// from the object file.
460/// This check is target-dependent and applicable to MachO only.
461pub fn checkInDyldLazyBind(check_object: *CheckObject) void {
462 const label = switch (check_object.obj_format) {
463 .macho => MachODumper.dyld_lazy_bind_label,
464 else => @panic("Unsupported target platform"),
465 };
466 check_object.checkStart(.dyld_lazy_bind);
467 check_object.checkExact(label);
468}
469
470/// Creates a new check checking specifically exports info contents parsed and dumped
471/// from the object file.
472/// This check is target-dependent and applicable to MachO only.
473pub fn checkInExports(check_object: *CheckObject) void {
474 const label = switch (check_object.obj_format) {
475 .macho => MachODumper.exports_label,
476 else => @panic("Unsupported target platform"),
477 };
478 check_object.checkStart(.exports);
479 check_object.checkExact(label);
480}
481
482/// Creates a new check checking specifically indirect symbol table parsed and dumped
483/// from the object file.
484/// This check is target-dependent and applicable to MachO only.
485pub fn checkInIndirectSymtab(check_object: *CheckObject) void {
486 const label = switch (check_object.obj_format) {
487 .macho => MachODumper.indirect_symtab_label,
488 else => @panic("Unsupported target platform"),
489 };
490 check_object.checkStart(.indirect_symtab);
491 check_object.checkExact(label);
492}
493
494/// Creates a new check checking specifically dynamic symbol table parsed and dumped from the object
495/// file.
496/// This check is target-dependent and applicable to ELF only.
497pub fn checkInDynamicSymtab(check_object: *CheckObject) void {
498 const label = switch (check_object.obj_format) {
499 .elf => ElfDumper.dynamic_symtab_label,
500 else => @panic("Unsupported target platform"),
501 };
502 check_object.checkStart(.dynamic_symtab);
503 check_object.checkExact(label);
504}
505
506/// Creates a new check checking specifically dynamic section parsed and dumped from the object
507/// file.
508/// This check is target-dependent and applicable to ELF only.
509pub fn checkInDynamicSection(check_object: *CheckObject) void {
510 const label = switch (check_object.obj_format) {
511 .elf => ElfDumper.dynamic_section_label,
512 else => @panic("Unsupported target platform"),
513 };
514 check_object.checkStart(.dynamic_section);
515 check_object.checkExact(label);
516}
517
518/// Creates a new check checking specifically symbol table parsed and dumped from the archive
519/// file.
520pub fn checkInArchiveSymtab(check_object: *CheckObject) void {
521 const label = switch (check_object.obj_format) {
522 .elf => ElfDumper.archive_symtab_label,
523 else => @panic("TODO other file formats"),
524 };
525 check_object.checkStart(.archive_symtab);
526 check_object.checkExact(label);
527}
528
529pub fn dumpSection(check_object: *CheckObject, name: [:0]const u8) void {
530 const check = Check.dumpSection(check_object.step.owner.allocator, name);
531 check_object.checks.append(check) catch @panic("OOM");
532}
533
534/// Creates a new standalone, singular check which allows running simple binary operations
535/// on the extracted variables. It will then compare the reduced program with the value of
536/// the expected variable.
537pub fn checkComputeCompare(
538 check_object: *CheckObject,
539 program: []const u8,
540 expected: ComputeCompareExpected,
541) void {
542 var check = Check.create(check_object.step.owner.allocator, .compute_compare);
543 check.computeCmp(.{ .string = check_object.step.owner.dupe(program) }, expected);
544 check_object.checks.append(check) catch @panic("OOM");
545}
546
547fn make(step: *Step, make_options: Step.MakeOptions) !void {
548 _ = make_options;
549 const b = step.owner;
550 const io = b.graph.io;
551 const gpa = b.allocator;
552 const check_object: *CheckObject = @fieldParentPtr("step", step);
553 try step.singleUnchangingWatchInput(check_object.source);
554
555 const src_path = check_object.source.getPath3(b, step);
556 const contents = src_path.root_dir.handle.readFileAllocOptions(
557 io,
558 src_path.sub_path,
559 gpa,
560 .limited(check_object.max_bytes),
561 .of(u64),
562 null,
563 ) catch |err| return step.fail("unable to read '{f}': {t}", .{
564 std.fmt.alt(src_path, .formatEscapeChar), err,
565 });
566
567 var vars: std.StringHashMap(u64) = .init(gpa);
568 for (check_object.checks.items) |chk| {
569 if (chk.kind == .compute_compare) {
570 assert(chk.actions.items.len == 1);
571 const act = chk.actions.items[0];
572 assert(act.tag == .compute_cmp);
573 const res = act.computeCmp(b, step, vars) catch |err| switch (err) {
574 error.UnknownVariable => return step.fail("Unknown variable", .{}),
575 else => |e| return e,
576 };
577 if (!res) {
578 return step.fail(
579 \\
580 \\========= comparison failed for action: ===========
581 \\{s} {f}
582 \\===================================================
583 , .{ act.phrase.resolve(b, step), act.expected.? });
584 }
585 continue;
586 }
587
588 const output = switch (check_object.obj_format) {
589 .macho => try MachODumper.parseAndDump(step, chk, contents),
590 .elf => try ElfDumper.parseAndDump(step, chk, contents),
591 .coff => return step.fail("TODO coff parser", .{}),
592 .wasm => try WasmDumper.parseAndDump(step, chk, contents),
593 else => unreachable,
594 };
595
596 // Depending on whether we requested dumping section verbatim or not,
597 // we either format message string with escaped codes, or not to aid debugging
598 // the failed test.
599 const fmtMessageString = struct {
600 fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Alt(Ctx, formatMessageString) {
601 return .{ .data = .{
602 .kind = kind,
603 .msg = msg,
604 } };
605 }
606
607 const Ctx = struct {
608 kind: Check.Kind,
609 msg: []const u8,
610 };
611
612 fn formatMessageString(ctx: Ctx, w: *Writer) !void {
613 switch (ctx.kind) {
614 .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}),
615 else => try w.writeAll(ctx.msg),
616 }
617 }
618 }.fmtMessageString;
619
620 var it = mem.tokenizeAny(u8, output, "\r\n");
621 for (chk.actions.items) |act| {
622 switch (act.tag) {
623 .exact => {
624 while (it.next()) |line| {
625 if (act.exact(b, step, line)) break;
626 } else {
627 return step.fail(
628 \\
629 \\========= expected to find: ==========================
630 \\{f}
631 \\========= but parsed file does not contain it: =======
632 \\{f}
633 \\========= file path: =================================
634 \\{f}
635 , .{
636 fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
637 fmtMessageString(chk.kind, output),
638 src_path,
639 });
640 }
641 },
642
643 .contains => {
644 while (it.next()) |line| {
645 if (act.contains(b, step, line)) break;
646 } else {
647 return step.fail(
648 \\
649 \\========= expected to find: ==========================
650 \\*{f}*
651 \\========= but parsed file does not contain it: =======
652 \\{f}
653 \\========= file path: =================================
654 \\{f}
655 , .{
656 fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
657 fmtMessageString(chk.kind, output),
658 src_path,
659 });
660 }
661 },
662
663 .not_present => {
664 while (it.next()) |line| {
665 if (act.notPresent(b, step, line)) continue;
666 return step.fail(
667 \\
668 \\========= expected not to find: ===================
669 \\{f}
670 \\========= but parsed file does contain it: ========
671 \\{f}
672 \\========= file path: ==============================
673 \\{f}
674 , .{
675 fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
676 fmtMessageString(chk.kind, output),
677 src_path,
678 });
679 }
680 },
681
682 .extract => {
683 while (it.next()) |line| {
684 if (try act.extract(b, step, line, &vars)) break;
685 } else {
686 return step.fail(
687 \\
688 \\========= expected to find and extract: ==============
689 \\{f}
690 \\========= but parsed file does not contain it: =======
691 \\{f}
692 \\========= file path: ==============================
693 \\{f}
694 , .{
695 fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
696 fmtMessageString(chk.kind, output),
697 src_path,
698 });
699 }
700 },
701
702 .compute_cmp => unreachable,
703 }
704 }
705 }
706}
707
708const MachODumper = struct {
709 const dyld_rebase_label = "dyld rebase data";
710 const dyld_bind_label = "dyld bind data";
711 const dyld_weak_bind_label = "dyld weak bind data";
712 const dyld_lazy_bind_label = "dyld lazy bind data";
713 const exports_label = "exports data";
714 const symtab_label = "symbol table";
715 const indirect_symtab_label = "indirect symbol table";
716
717 fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
718 // TODO: handle archives and fat files
719 return parseAndDumpObject(step, check, bytes);
720 }
721
722 const ObjectContext = struct {
723 gpa: Allocator,
724 data: []const u8,
725 header: macho.mach_header_64,
726 segments: std.ArrayList(macho.segment_command_64) = .empty,
727 sections: std.ArrayList(macho.section_64) = .empty,
728 symtab: std.ArrayList(macho.nlist_64) = .empty,
729 strtab: std.ArrayList(u8) = .empty,
730 indsymtab: std.ArrayList(u32) = .empty,
731 imports: std.ArrayList([]const u8) = .empty,
732
733 fn parse(ctx: *ObjectContext) !void {
734 var it = try ctx.getLoadCommandIterator();
735 var i: usize = 0;
736 while (try it.next()) |cmd| {
737 switch (cmd.hdr.cmd) {
738 .SEGMENT_64 => {
739 const seg = cmd.cast(macho.segment_command_64).?;
740 try ctx.segments.append(ctx.gpa, seg);
741 try ctx.sections.ensureUnusedCapacity(ctx.gpa, seg.nsects);
742 for (cmd.getSections()) |sect| {
743 ctx.sections.appendAssumeCapacity(sect);
744 }
745 },
746 .SYMTAB => {
747 const lc = cmd.cast(macho.symtab_command).?;
748 const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(ctx.data.ptr + lc.symoff))[0..lc.nsyms];
749 const strtab = ctx.data[lc.stroff..][0..lc.strsize];
750 try ctx.symtab.appendUnalignedSlice(ctx.gpa, symtab);
751 try ctx.strtab.appendSlice(ctx.gpa, strtab);
752 },
753 .DYSYMTAB => {
754 const lc = cmd.cast(macho.dysymtab_command).?;
755 const indexes = @as([*]align(1) const u32, @ptrCast(ctx.data.ptr + lc.indirectsymoff))[0..lc.nindirectsyms];
756 try ctx.indsymtab.appendUnalignedSlice(ctx.gpa, indexes);
757 },
758 .LOAD_DYLIB,
759 .LOAD_WEAK_DYLIB,
760 .REEXPORT_DYLIB,
761 => {
762 try ctx.imports.append(ctx.gpa, cmd.getDylibPathName());
763 },
764 else => {},
765 }
766
767 i += 1;
768 }
769 }
770
771 fn getString(ctx: ObjectContext, off: u32) [:0]const u8 {
772 assert(off < ctx.strtab.items.len);
773 return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.strtab.items.ptr + off)), 0);
774 }
775
776 fn getLoadCommandIterator(ctx: ObjectContext) !macho.LoadCommandIterator {
777 return .init(&ctx.header, ctx.data[@sizeOf(macho.mach_header_64)..]);
778 }
779
780 fn getLoadCommand(ctx: ObjectContext, cmd: macho.LC) !?macho.LoadCommandIterator.LoadCommand {
781 var it = try ctx.getLoadCommandIterator();
782 while (try it.next()) |lc| if (lc.hdr.cmd == cmd) {
783 return lc;
784 };
785 return null;
786 }
787
788 fn getSegmentByName(ctx: ObjectContext, name: []const u8) ?macho.segment_command_64 {
789 for (ctx.segments.items) |seg| {
790 if (mem.eql(u8, seg.segName(), name)) return seg;
791 }
792 return null;
793 }
794
795 fn getSectionByName(ctx: ObjectContext, segname: []const u8, sectname: []const u8) ?macho.section_64 {
796 for (ctx.sections.items) |sect| {
797 if (mem.eql(u8, sect.segName(), segname) and mem.eql(u8, sect.sectName(), sectname)) return sect;
798 }
799 return null;
800 }
801
802 fn dumpHeader(hdr: macho.mach_header_64, writer: anytype) !void {
803 const cputype = switch (hdr.cputype) {
804 macho.CPU_TYPE_ARM64 => "ARM64",
805 macho.CPU_TYPE_X86_64 => "X86_64",
806 else => "Unknown",
807 };
808 const filetype = switch (hdr.filetype) {
809 macho.MH_OBJECT => "MH_OBJECT",
810 macho.MH_EXECUTE => "MH_EXECUTE",
811 macho.MH_FVMLIB => "MH_FVMLIB",
812 macho.MH_CORE => "MH_CORE",
813 macho.MH_PRELOAD => "MH_PRELOAD",
814 macho.MH_DYLIB => "MH_DYLIB",
815 macho.MH_DYLINKER => "MH_DYLINKER",
816 macho.MH_BUNDLE => "MH_BUNDLE",
817 macho.MH_DYLIB_STUB => "MH_DYLIB_STUB",
818 macho.MH_DSYM => "MH_DSYM",
819 macho.MH_KEXT_BUNDLE => "MH_KEXT_BUNDLE",
820 else => "Unknown",
821 };
822
823 try writer.print(
824 \\header
825 \\cputype {s}
826 \\filetype {s}
827 \\ncmds {d}
828 \\sizeofcmds {x}
829 \\flags
830 , .{
831 cputype,
832 filetype,
833 hdr.ncmds,
834 hdr.sizeofcmds,
835 });
836
837 if (hdr.flags > 0) {
838 if (hdr.flags & macho.MH_NOUNDEFS != 0) try writer.writeAll(" NOUNDEFS");
839 if (hdr.flags & macho.MH_INCRLINK != 0) try writer.writeAll(" INCRLINK");
840 if (hdr.flags & macho.MH_DYLDLINK != 0) try writer.writeAll(" DYLDLINK");
841 if (hdr.flags & macho.MH_BINDATLOAD != 0) try writer.writeAll(" BINDATLOAD");
842 if (hdr.flags & macho.MH_PREBOUND != 0) try writer.writeAll(" PREBOUND");
843 if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try writer.writeAll(" SPLIT_SEGS");
844 if (hdr.flags & macho.MH_LAZY_INIT != 0) try writer.writeAll(" LAZY_INIT");
845 if (hdr.flags & macho.MH_TWOLEVEL != 0) try writer.writeAll(" TWOLEVEL");
846 if (hdr.flags & macho.MH_FORCE_FLAT != 0) try writer.writeAll(" FORCE_FLAT");
847 if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try writer.writeAll(" NOMULTIDEFS");
848 if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try writer.writeAll(" NOFIXPREBINDING");
849 if (hdr.flags & macho.MH_PREBINDABLE != 0) try writer.writeAll(" PREBINDABLE");
850 if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try writer.writeAll(" ALLMODSBOUND");
851 if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try writer.writeAll(" SUBSECTIONS_VIA_SYMBOLS");
852 if (hdr.flags & macho.MH_CANONICAL != 0) try writer.writeAll(" CANONICAL");
853 if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try writer.writeAll(" WEAK_DEFINES");
854 if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try writer.writeAll(" BINDS_TO_WEAK");
855 if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try writer.writeAll(" ALLOW_STACK_EXECUTION");
856 if (hdr.flags & macho.MH_ROOT_SAFE != 0) try writer.writeAll(" ROOT_SAFE");
857 if (hdr.flags & macho.MH_SETUID_SAFE != 0) try writer.writeAll(" SETUID_SAFE");
858 if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try writer.writeAll(" NO_REEXPORTED_DYLIBS");
859 if (hdr.flags & macho.MH_PIE != 0) try writer.writeAll(" PIE");
860 if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try writer.writeAll(" DEAD_STRIPPABLE_DYLIB");
861 if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try writer.writeAll(" HAS_TLV_DESCRIPTORS");
862 if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try writer.writeAll(" NO_HEAP_EXECUTION");
863 if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try writer.writeAll(" APP_EXTENSION_SAFE");
864 if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try writer.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO");
865 }
866
867 try writer.writeByte('\n');
868 }
869
870 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
871 // print header first
872 try writer.print(
873 \\LC {d}
874 \\cmd {s}
875 \\cmdsize {d}
876 , .{ index, @tagName(lc.hdr.cmd), lc.hdr.cmdsize });
877
878 switch (lc.hdr.cmd) {
879 .SEGMENT_64 => {
880 const seg = lc.cast(macho.segment_command_64).?;
881 try writer.writeByte('\n');
882 try writer.print(
883 \\segname {s}
884 \\vmaddr {x}
885 \\vmsize {x}
886 \\fileoff {x}
887 \\filesz {x}
888 , .{
889 seg.segName(),
890 seg.vmaddr,
891 seg.vmsize,
892 seg.fileoff,
893 seg.filesize,
894 });
895
896 for (lc.getSections()) |sect| {
897 try writer.writeByte('\n');
898 try writer.print(
899 \\sectname {s}
900 \\addr {x}
901 \\size {x}
902 \\offset {x}
903 \\align {x}
904 , .{
905 sect.sectName(),
906 sect.addr,
907 sect.size,
908 sect.offset,
909 sect.@"align",
910 });
911 }
912 },
913
914 .ID_DYLIB,
915 .LOAD_DYLIB,
916 .LOAD_WEAK_DYLIB,
917 .REEXPORT_DYLIB,
918 => {
919 const dylib = lc.cast(macho.dylib_command).?;
920 try writer.writeByte('\n');
921 try writer.print(
922 \\name {s}
923 \\timestamp {d}
924 \\current version {x}
925 \\compatibility version {x}
926 , .{
927 lc.getDylibPathName(),
928 dylib.dylib.timestamp,
929 dylib.dylib.current_version,
930 dylib.dylib.compatibility_version,
931 });
932 },
933
934 .MAIN => {
935 const main = lc.cast(macho.entry_point_command).?;
936 try writer.writeByte('\n');
937 try writer.print(
938 \\entryoff {x}
939 \\stacksize {x}
940 , .{ main.entryoff, main.stacksize });
941 },
942
943 .RPATH => {
944 try writer.writeByte('\n');
945 try writer.print(
946 \\path {s}
947 , .{
948 lc.getRpathPathName(),
949 });
950 },
951
952 .UUID => {
953 const uuid = lc.cast(macho.uuid_command).?;
954 try writer.writeByte('\n');
955 try writer.print("uuid {x}", .{&uuid.uuid});
956 },
957
958 .DATA_IN_CODE,
959 .FUNCTION_STARTS,
960 .CODE_SIGNATURE,
961 => {
962 const llc = lc.cast(macho.linkedit_data_command).?;
963 try writer.writeByte('\n');
964 try writer.print(
965 \\dataoff {x}
966 \\datasize {x}
967 , .{ llc.dataoff, llc.datasize });
968 },
969
970 .DYLD_INFO_ONLY => {
971 const dlc = lc.cast(macho.dyld_info_command).?;
972 try writer.writeByte('\n');
973 try writer.print(
974 \\rebaseoff {x}
975 \\rebasesize {x}
976 \\bindoff {x}
977 \\bindsize {x}
978 \\weakbindoff {x}
979 \\weakbindsize {x}
980 \\lazybindoff {x}
981 \\lazybindsize {x}
982 \\exportoff {x}
983 \\exportsize {x}
984 , .{
985 dlc.rebase_off,
986 dlc.rebase_size,
987 dlc.bind_off,
988 dlc.bind_size,
989 dlc.weak_bind_off,
990 dlc.weak_bind_size,
991 dlc.lazy_bind_off,
992 dlc.lazy_bind_size,
993 dlc.export_off,
994 dlc.export_size,
995 });
996 },
997
998 .SYMTAB => {
999 const slc = lc.cast(macho.symtab_command).?;
1000 try writer.writeByte('\n');
1001 try writer.print(
1002 \\symoff {x}
1003 \\nsyms {x}
1004 \\stroff {x}
1005 \\strsize {x}
1006 , .{
1007 slc.symoff,
1008 slc.nsyms,
1009 slc.stroff,
1010 slc.strsize,
1011 });
1012 },
1013
1014 .DYSYMTAB => {
1015 const dlc = lc.cast(macho.dysymtab_command).?;
1016 try writer.writeByte('\n');
1017 try writer.print(
1018 \\ilocalsym {x}
1019 \\nlocalsym {x}
1020 \\iextdefsym {x}
1021 \\nextdefsym {x}
1022 \\iundefsym {x}
1023 \\nundefsym {x}
1024 \\indirectsymoff {x}
1025 \\nindirectsyms {x}
1026 , .{
1027 dlc.ilocalsym,
1028 dlc.nlocalsym,
1029 dlc.iextdefsym,
1030 dlc.nextdefsym,
1031 dlc.iundefsym,
1032 dlc.nundefsym,
1033 dlc.indirectsymoff,
1034 dlc.nindirectsyms,
1035 });
1036 },
1037
1038 .BUILD_VERSION => {
1039 const blc = lc.cast(macho.build_version_command).?;
1040 try writer.writeByte('\n');
1041 try writer.print(
1042 \\platform {s}
1043 \\minos {d}.{d}.{d}
1044 \\sdk {d}.{d}.{d}
1045 \\ntools {d}
1046 , .{
1047 @tagName(blc.platform),
1048 blc.minos >> 16,
1049 @as(u8, @truncate(blc.minos >> 8)),
1050 @as(u8, @truncate(blc.minos)),
1051 blc.sdk >> 16,
1052 @as(u8, @truncate(blc.sdk >> 8)),
1053 @as(u8, @truncate(blc.sdk)),
1054 blc.ntools,
1055 });
1056 for (lc.getBuildVersionTools()) |tool| {
1057 try writer.writeByte('\n');
1058 switch (tool.tool) {
1059 .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}),
1060 else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}),
1061 }
1062 try writer.print(
1063 \\version {d}.{d}.{d}
1064 , .{
1065 tool.version >> 16,
1066 @as(u8, @truncate(tool.version >> 8)),
1067 @as(u8, @truncate(tool.version)),
1068 });
1069 }
1070 },
1071
1072 .VERSION_MIN_MACOSX,
1073 .VERSION_MIN_IPHONEOS,
1074 .VERSION_MIN_WATCHOS,
1075 .VERSION_MIN_TVOS,
1076 => {
1077 const vlc = lc.cast(macho.version_min_command).?;
1078 try writer.writeByte('\n');
1079 try writer.print(
1080 \\version {d}.{d}.{d}
1081 \\sdk {d}.{d}.{d}
1082 , .{
1083 vlc.version >> 16,
1084 @as(u8, @truncate(vlc.version >> 8)),
1085 @as(u8, @truncate(vlc.version)),
1086 vlc.sdk >> 16,
1087 @as(u8, @truncate(vlc.sdk >> 8)),
1088 @as(u8, @truncate(vlc.sdk)),
1089 });
1090 },
1091
1092 else => {},
1093 }
1094 }
1095
1096 fn dumpSymtab(ctx: ObjectContext, writer: anytype) !void {
1097 try writer.writeAll(symtab_label ++ "\n");
1098
1099 for (ctx.symtab.items) |sym| {
1100 const sym_name = ctx.getString(sym.n_strx);
1101 if (sym.n_type.bits.is_stab != 0) {
1102 const tt = switch (sym.n_type.stab) {
1103 _ => "UNKNOWN STAB",
1104 else => @tagName(sym.n_type.stab),
1105 };
1106 try writer.print("{x}", .{sym.n_value});
1107 if (sym.n_sect > 0) {
1108 const sect = ctx.sections.items[sym.n_sect - 1];
1109 try writer.print(" ({s},{s})", .{ sect.segName(), sect.sectName() });
1110 }
1111 try writer.print(" {s} (stab) {s}\n", .{ tt, sym_name });
1112 } else if (sym.n_type.bits.type == .sect) {
1113 const sect = ctx.sections.items[sym.n_sect - 1];
1114 try writer.print("{x} ({s},{s})", .{
1115 sym.n_value,
1116 sect.segName(),
1117 sect.sectName(),
1118 });
1119 if (sym.n_desc.referenced_dynamically) try writer.writeAll(" [referenced dynamically]");
1120 if (sym.n_desc.weak_def_or_ref_to_weak) try writer.writeAll(" weak");
1121 if (sym.n_desc.weak_ref) try writer.writeAll(" weakref");
1122 if (sym.n_type.bits.ext) {
1123 if (sym.n_type.bits.pext) try writer.writeAll(" private");
1124 try writer.writeAll(" external");
1125 } else if (sym.n_type.bits.pext) try writer.writeAll(" (was private external)");
1126 try writer.print(" {s}\n", .{sym_name});
1127 } else if (sym.tentative()) {
1128 const alignment = (@as(u16, @bitCast(sym.n_desc)) >> 8) & 0x0F;
1129 try writer.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment });
1130 if (sym.n_type.bits.ext) try writer.writeAll(" external");
1131 try writer.print(" {s}\n", .{sym_name});
1132 } else if (sym.n_type.bits.type == .undf) {
1133 const ordinal = @divFloor(@as(i16, @bitCast(sym.n_desc)), macho.N_SYMBOL_RESOLVER);
1134 const import_name = blk: {
1135 if (ordinal <= 0) {
1136 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
1137 break :blk "self import";
1138 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
1139 break :blk "main executable";
1140 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
1141 break :blk "flat lookup";
1142 unreachable;
1143 }
1144 const full_path = ctx.imports.items[@as(u16, @bitCast(ordinal)) - 1];
1145 const basename = fs.path.basename(full_path);
1146 assert(basename.len > 0);
1147 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
1148 break :blk basename[0..ext];
1149 };
1150 try writer.writeAll("(undefined)");
1151 if (sym.n_desc.weak_ref) try writer.writeAll(" weakref");
1152 if (sym.n_type.bits.ext) try writer.writeAll(" external");
1153 try writer.print(" {s} (from {s})\n", .{
1154 sym_name,
1155 import_name,
1156 });
1157 }
1158 }
1159 }
1160
1161 fn dumpIndirectSymtab(ctx: ObjectContext, writer: anytype) !void {
1162 try writer.writeAll(indirect_symtab_label ++ "\n");
1163
1164 var sects_buffer: [3]macho.section_64 = undefined;
1165 const sects = blk: {
1166 var count: usize = 0;
1167 if (ctx.getSectionByName("__TEXT", "__stubs")) |sect| {
1168 sects_buffer[count] = sect;
1169 count += 1;
1170 }
1171 if (ctx.getSectionByName("__DATA_CONST", "__got")) |sect| {
1172 sects_buffer[count] = sect;
1173 count += 1;
1174 }
1175 if (ctx.getSectionByName("__DATA", "__la_symbol_ptr")) |sect| {
1176 sects_buffer[count] = sect;
1177 count += 1;
1178 }
1179 break :blk sects_buffer[0..count];
1180 };
1181
1182 const sortFn = struct {
1183 fn sortFn(c: void, lhs: macho.section_64, rhs: macho.section_64) bool {
1184 _ = c;
1185 return lhs.reserved1 < rhs.reserved1;
1186 }
1187 }.sortFn;
1188 mem.sort(macho.section_64, sects, {}, sortFn);
1189
1190 var i: usize = 0;
1191 while (i < sects.len) : (i += 1) {
1192 const sect = sects[i];
1193 const start = sect.reserved1;
1194 const end = if (i + 1 >= sects.len) ctx.indsymtab.items.len else sects[i + 1].reserved1;
1195 const entry_size = blk: {
1196 if (mem.eql(u8, sect.sectName(), "__stubs")) break :blk sect.reserved2;
1197 break :blk @sizeOf(u64);
1198 };
1199
1200 try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() });
1201 try writer.print("nentries {d}\n", .{end - start});
1202 for (ctx.indsymtab.items[start..end], 0..) |index, j| {
1203 const sym = ctx.symtab.items[index];
1204 const addr = sect.addr + entry_size * j;
1205 try writer.print("0x{x} {d} {s}\n", .{ addr, index, ctx.getString(sym.n_strx) });
1206 }
1207 }
1208 }
1209
1210 fn dumpRebaseInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void {
1211 var rebases = std.array_list.Managed(u64).init(ctx.gpa);
1212 defer rebases.deinit();
1213 try ctx.parseRebaseInfo(data, &rebases);
1214 mem.sort(u64, rebases.items, {}, std.sort.asc(u64));
1215 for (rebases.items) |addr| {
1216 try writer.print("0x{x}\n", .{addr});
1217 }
1218 }
1219
1220 fn parseRebaseInfo(ctx: ObjectContext, data: []const u8, rebases: *std.array_list.Managed(u64)) !void {
1221 var reader: std.Io.Reader = .fixed(data);
1222
1223 var seg_id: ?u8 = null;
1224 var offset: u64 = 0;
1225 while (true) {
1226 const byte = reader.takeByte() catch break;
1227 const opc = byte & macho.REBASE_OPCODE_MASK;
1228 const imm = byte & macho.REBASE_IMMEDIATE_MASK;
1229 switch (opc) {
1230 macho.REBASE_OPCODE_DONE => break,
1231 macho.REBASE_OPCODE_SET_TYPE_IMM => {},
1232 macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1233 seg_id = imm;
1234 offset = try reader.takeLeb128(u64);
1235 },
1236 macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => {
1237 offset += imm * @sizeOf(u64);
1238 },
1239 macho.REBASE_OPCODE_ADD_ADDR_ULEB => {
1240 const addend = try reader.takeLeb128(u64);
1241 offset += addend;
1242 },
1243 macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => {
1244 const addend = try reader.takeLeb128(u64);
1245 const seg = ctx.segments.items[seg_id.?];
1246 const addr = seg.vmaddr + offset;
1247 try rebases.append(addr);
1248 offset += addend + @sizeOf(u64);
1249 },
1250 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES,
1251 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES,
1252 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB,
1253 => {
1254 var ntimes: u64 = 1;
1255 var skip: u64 = 0;
1256 switch (opc) {
1257 macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES => {
1258 ntimes = imm;
1259 },
1260 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => {
1261 ntimes = try reader.takeLeb128(u64);
1262 },
1263 macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => {
1264 ntimes = try reader.takeLeb128(u64);
1265 skip = try reader.takeLeb128(u64);
1266 },
1267 else => unreachable,
1268 }
1269 const seg = ctx.segments.items[seg_id.?];
1270 const base_addr = seg.vmaddr;
1271 var count: usize = 0;
1272 while (count < ntimes) : (count += 1) {
1273 const addr = base_addr + offset;
1274 try rebases.append(addr);
1275 offset += skip + @sizeOf(u64);
1276 }
1277 },
1278 else => break,
1279 }
1280 }
1281 }
1282
1283 const Binding = struct {
1284 address: u64,
1285 addend: i64,
1286 ordinal: u16,
1287 tag: Tag,
1288 name: []const u8,
1289
1290 fn deinit(binding: *Binding, gpa: Allocator) void {
1291 gpa.free(binding.name);
1292 }
1293
1294 fn lessThan(ctx: void, lhs: Binding, rhs: Binding) bool {
1295 _ = ctx;
1296 return lhs.address < rhs.address;
1297 }
1298
1299 const Tag = enum {
1300 ord,
1301 self,
1302 exe,
1303 flat,
1304 };
1305 };
1306
1307 fn dumpBindInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void {
1308 var bindings = std.array_list.Managed(Binding).init(ctx.gpa);
1309 defer {
1310 for (bindings.items) |*b| {
1311 b.deinit(ctx.gpa);
1312 }
1313 bindings.deinit();
1314 }
1315 var data_reader: std.Io.Reader = .fixed(data);
1316 try ctx.parseBindInfo(&data_reader, &bindings);
1317 mem.sort(Binding, bindings.items, {}, Binding.lessThan);
1318 for (bindings.items) |binding| {
1319 try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend });
1320 try writer.writeAll(" (");
1321 switch (binding.tag) {
1322 .self => try writer.writeAll("self"),
1323 .exe => try writer.writeAll("main executable"),
1324 .flat => try writer.writeAll("flat lookup"),
1325 .ord => try writer.writeAll(std.fs.path.basename(ctx.imports.items[binding.ordinal - 1])),
1326 }
1327 try writer.print(") {s}\n", .{binding.name});
1328 }
1329 }
1330
1331 fn parseBindInfo(ctx: ObjectContext, reader: *std.Io.Reader, bindings: *std.array_list.Managed(Binding)) !void {
1332 var seg_id: ?u8 = null;
1333 var tag: Binding.Tag = .self;
1334 var ordinal: u16 = 0;
1335 var offset: u64 = 0;
1336 var addend: i64 = 0;
1337
1338 var name_buf = std.array_list.Managed(u8).init(ctx.gpa);
1339 defer name_buf.deinit();
1340
1341 while (true) {
1342 const byte = reader.takeByte() catch break;
1343 const opc = byte & macho.BIND_OPCODE_MASK;
1344 const imm = byte & macho.BIND_IMMEDIATE_MASK;
1345 switch (opc) {
1346 macho.BIND_OPCODE_DONE,
1347 macho.BIND_OPCODE_SET_TYPE_IMM,
1348 => {},
1349 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
1350 tag = .ord;
1351 ordinal = imm;
1352 },
1353 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => {
1354 switch (imm) {
1355 0 => tag = .self,
1356 0xf => tag = .exe,
1357 0xe => tag = .flat,
1358 else => unreachable,
1359 }
1360 },
1361 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1362 seg_id = imm;
1363 offset = try reader.takeLeb128(u64);
1364 },
1365 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1366 name_buf.clearRetainingCapacity();
1367 try name_buf.appendSlice(try reader.takeDelimiterInclusive(0));
1368 },
1369 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1370 addend = try reader.takeLeb128(i64);
1371 },
1372 macho.BIND_OPCODE_ADD_ADDR_ULEB => {
1373 const x = try reader.takeLeb128(u64);
1374 offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x)));
1375 },
1376 macho.BIND_OPCODE_DO_BIND,
1377 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB,
1378 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED,
1379 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB,
1380 => {
1381 var add_addr: u64 = 0;
1382 var count: u64 = 1;
1383 var skip: u64 = 0;
1384
1385 switch (opc) {
1386 macho.BIND_OPCODE_DO_BIND => {},
1387 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
1388 add_addr = try reader.takeLeb128(u64);
1389 },
1390 macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => {
1391 add_addr = imm * @sizeOf(u64);
1392 },
1393 macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
1394 count = try reader.takeLeb128(u64);
1395 skip = try reader.takeLeb128(u64);
1396 },
1397 else => unreachable,
1398 }
1399
1400 const seg = ctx.segments.items[seg_id.?];
1401 var i: u64 = 0;
1402 while (i < count) : (i += 1) {
1403 const addr: u64 = @intCast(@as(i64, @intCast(seg.vmaddr + offset)));
1404 try bindings.append(.{
1405 .address = addr,
1406 .addend = addend,
1407 .tag = tag,
1408 .ordinal = ordinal,
1409 .name = try ctx.gpa.dupe(u8, name_buf.items),
1410 });
1411 offset += skip + @sizeOf(u64) + add_addr;
1412 }
1413 },
1414 else => break,
1415 }
1416 }
1417 }
1418
1419 fn dumpExportsTrie(ctx: ObjectContext, data: []const u8, writer: anytype) !void {
1420 const seg = ctx.getSegmentByName("__TEXT") orelse return;
1421
1422 var arena = std.heap.ArenaAllocator.init(ctx.gpa);
1423 defer arena.deinit();
1424
1425 var exports = std.array_list.Managed(Export).init(arena.allocator());
1426 var it: TrieIterator = .{ .stream = .fixed(data) };
1427 try parseTrieNode(arena.allocator(), &it, "", &exports);
1428
1429 mem.sort(Export, exports.items, {}, Export.lessThan);
1430
1431 for (exports.items) |exp| {
1432 switch (exp.tag) {
1433 .@"export" => {
1434 const info = exp.data.@"export";
1435 if (info.kind != .regular or info.weak) {
1436 try writer.writeByte('[');
1437 }
1438 switch (info.kind) {
1439 .regular => {},
1440 .absolute => try writer.writeAll("ABS, "),
1441 .tlv => try writer.writeAll("THREAD_LOCAL, "),
1442 }
1443 if (info.weak) try writer.writeAll("WEAK");
1444 if (info.kind != .regular or info.weak) {
1445 try writer.writeAll("] ");
1446 }
1447 try writer.print("{x} ", .{seg.vmaddr + info.vmoffset});
1448 },
1449 else => {},
1450 }
1451
1452 try writer.print("{s}\n", .{exp.name});
1453 }
1454 }
1455
1456 const TrieIterator = struct {
1457 stream: std.Io.Reader,
1458
1459 fn takeLeb128(it: *TrieIterator) !u64 {
1460 return it.stream.takeLeb128(u64);
1461 }
1462
1463 fn readString(it: *TrieIterator) ![:0]const u8 {
1464 return it.stream.takeSentinel(0);
1465 }
1466
1467 fn takeByte(it: *TrieIterator) !u8 {
1468 return it.stream.takeByte();
1469 }
1470 };
1471
1472 const Export = struct {
1473 name: []const u8,
1474 tag: enum { @"export", reexport, stub_resolver },
1475 data: union {
1476 @"export": struct {
1477 kind: enum { regular, absolute, tlv },
1478 weak: bool = false,
1479 vmoffset: u64,
1480 },
1481 reexport: u64,
1482 stub_resolver: struct {
1483 stub_offset: u64,
1484 resolver_offset: u64,
1485 },
1486 },
1487
1488 inline fn rankByTag(@"export": Export) u3 {
1489 return switch (@"export".tag) {
1490 .@"export" => 1,
1491 .reexport => 2,
1492 .stub_resolver => 3,
1493 };
1494 }
1495
1496 fn lessThan(ctx: void, lhs: Export, rhs: Export) bool {
1497 _ = ctx;
1498 if (lhs.rankByTag() == rhs.rankByTag()) {
1499 return switch (lhs.tag) {
1500 .@"export" => lhs.data.@"export".vmoffset < rhs.data.@"export".vmoffset,
1501 .reexport => lhs.data.reexport < rhs.data.reexport,
1502 .stub_resolver => lhs.data.stub_resolver.stub_offset < rhs.data.stub_resolver.stub_offset,
1503 };
1504 }
1505 return lhs.rankByTag() < rhs.rankByTag();
1506 }
1507 };
1508
1509 fn parseTrieNode(
1510 arena: Allocator,
1511 it: *TrieIterator,
1512 prefix: []const u8,
1513 exports: *std.array_list.Managed(Export),
1514 ) !void {
1515 const size = try it.takeLeb128();
1516 if (size > 0) {
1517 const flags = try it.takeLeb128();
1518 switch (flags) {
1519 macho.EXPORT_SYMBOL_FLAGS_REEXPORT => {
1520 const ord = try it.takeLeb128();
1521 const name = try arena.dupe(u8, try it.readString());
1522 try exports.append(.{
1523 .name = if (name.len > 0) name else prefix,
1524 .tag = .reexport,
1525 .data = .{ .reexport = ord },
1526 });
1527 },
1528 macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
1529 const stub_offset = try it.takeLeb128();
1530 const resolver_offset = try it.takeLeb128();
1531 try exports.append(.{
1532 .name = prefix,
1533 .tag = .stub_resolver,
1534 .data = .{ .stub_resolver = .{
1535 .stub_offset = stub_offset,
1536 .resolver_offset = resolver_offset,
1537 } },
1538 });
1539 },
1540 else => {
1541 const vmoff = try it.takeLeb128();
1542 try exports.append(.{
1543 .name = prefix,
1544 .tag = .@"export",
1545 .data = .{ .@"export" = .{
1546 .kind = switch (flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK) {
1547 macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR => .regular,
1548 macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE => .absolute,
1549 macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL => .tlv,
1550 else => unreachable,
1551 },
1552 .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0,
1553 .vmoffset = vmoff,
1554 } },
1555 });
1556 },
1557 }
1558 }
1559
1560 const nedges = try it.takeByte();
1561 for (0..nedges) |_| {
1562 const label = try it.readString();
1563 const off = try it.takeLeb128();
1564 const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label });
1565 const curr = it.stream.seek;
1566 it.stream.seek = off;
1567 try parseTrieNode(arena, it, prefix_label, exports);
1568 it.stream.seek = curr;
1569 }
1570 }
1571
1572 fn dumpSection(ctx: ObjectContext, sect: macho.section_64, writer: anytype) !void {
1573 const data = ctx.data[sect.offset..][0..sect.size];
1574 try writer.print("{s}", .{data});
1575 }
1576 };
1577
1578 fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
1579 const gpa = step.owner.allocator;
1580 const hdr = @as(*align(1) const macho.mach_header_64, @ptrCast(bytes.ptr)).*;
1581 if (hdr.magic != macho.MH_MAGIC_64) {
1582 return error.InvalidMagicNumber;
1583 }
1584
1585 var ctx = ObjectContext{ .gpa = gpa, .data = bytes, .header = hdr };
1586 try ctx.parse();
1587
1588 var output: std.Io.Writer.Allocating = .init(gpa);
1589 defer output.deinit();
1590 const writer = &output.writer;
1591
1592 switch (check.kind) {
1593 .headers => {
1594 try ObjectContext.dumpHeader(ctx.header, writer);
1595
1596 var it = try ctx.getLoadCommandIterator();
1597 var i: usize = 0;
1598 while (try it.next()) |cmd| {
1599 try ObjectContext.dumpLoadCommand(cmd, i, writer);
1600 try writer.writeByte('\n');
1601
1602 i += 1;
1603 }
1604 },
1605
1606 .symtab => if (ctx.symtab.items.len > 0) {
1607 try ctx.dumpSymtab(writer);
1608 } else return step.fail("no symbol table found", .{}),
1609
1610 .indirect_symtab => if (ctx.symtab.items.len > 0 and ctx.indsymtab.items.len > 0) {
1611 try ctx.dumpIndirectSymtab(writer);
1612 } else return step.fail("no indirect symbol table found", .{}),
1613
1614 .dyld_rebase,
1615 .dyld_bind,
1616 .dyld_weak_bind,
1617 .dyld_lazy_bind,
1618 => {
1619 const cmd = try ctx.getLoadCommand(.DYLD_INFO_ONLY) orelse
1620 return step.fail("no dyld info found", .{});
1621 const lc = cmd.cast(macho.dyld_info_command).?;
1622
1623 switch (check.kind) {
1624 .dyld_rebase => if (lc.rebase_size > 0) {
1625 const data = ctx.data[lc.rebase_off..][0..lc.rebase_size];
1626 try writer.writeAll(dyld_rebase_label ++ "\n");
1627 try ctx.dumpRebaseInfo(data, writer);
1628 } else return step.fail("no rebase data found", .{}),
1629
1630 .dyld_bind => if (lc.bind_size > 0) {
1631 const data = ctx.data[lc.bind_off..][0..lc.bind_size];
1632 try writer.writeAll(dyld_bind_label ++ "\n");
1633 try ctx.dumpBindInfo(data, writer);
1634 } else return step.fail("no bind data found", .{}),
1635
1636 .dyld_weak_bind => if (lc.weak_bind_size > 0) {
1637 const data = ctx.data[lc.weak_bind_off..][0..lc.weak_bind_size];
1638 try writer.writeAll(dyld_weak_bind_label ++ "\n");
1639 try ctx.dumpBindInfo(data, writer);
1640 } else return step.fail("no weak bind data found", .{}),
1641
1642 .dyld_lazy_bind => if (lc.lazy_bind_size > 0) {
1643 const data = ctx.data[lc.lazy_bind_off..][0..lc.lazy_bind_size];
1644 try writer.writeAll(dyld_lazy_bind_label ++ "\n");
1645 try ctx.dumpBindInfo(data, writer);
1646 } else return step.fail("no lazy bind data found", .{}),
1647
1648 else => unreachable,
1649 }
1650 },
1651
1652 .exports => blk: {
1653 if (try ctx.getLoadCommand(.DYLD_INFO_ONLY)) |cmd| {
1654 const lc = cmd.cast(macho.dyld_info_command).?;
1655 if (lc.export_size > 0) {
1656 const data = ctx.data[lc.export_off..][0..lc.export_size];
1657 try writer.writeAll(exports_label ++ "\n");
1658 try ctx.dumpExportsTrie(data, writer);
1659 break :blk;
1660 }
1661 }
1662 return step.fail("no exports data found", .{});
1663 },
1664
1665 .dump_section => {
1666 const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0);
1667 const sep_index = mem.findScalar(u8, name, ',') orelse
1668 return step.fail("invalid section name: {s}", .{name});
1669 const segname = name[0..sep_index];
1670 const sectname = name[sep_index + 1 ..];
1671 const sect = ctx.getSectionByName(segname, sectname) orelse
1672 return step.fail("section '{s}' not found", .{name});
1673 try ctx.dumpSection(sect, writer);
1674 },
1675
1676 else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}),
1677 }
1678
1679 return output.toOwnedSlice();
1680 }
1681};
1682
1683const ElfDumper = struct {
1684 const symtab_label = "symbol table";
1685 const dynamic_symtab_label = "dynamic symbol table";
1686 const dynamic_section_label = "dynamic section";
1687 const archive_symtab_label = "archive symbol table";
1688
1689 fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
1690 return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) {
1691 error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes),
1692 else => |e| return e,
1693 };
1694 }
1695
1696 fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
1697 const gpa = step.owner.allocator;
1698 var reader: std.Io.Reader = .fixed(bytes);
1699
1700 const magic = try reader.takeArray(elf.ARMAG.len);
1701 if (!mem.eql(u8, magic, elf.ARMAG)) {
1702 return error.InvalidArchiveMagicNumber;
1703 }
1704
1705 if (!mem.isAligned(bytes.len, 2)) {
1706 return error.InvalidArchivePadding;
1707 }
1708
1709 var ctx = ArchiveContext{
1710 .gpa = gpa,
1711 .data = bytes,
1712 .strtab = &[0]u8{},
1713 };
1714 defer {
1715 for (ctx.objects.items) |*object| {
1716 gpa.free(object.name);
1717 }
1718 ctx.objects.deinit(gpa);
1719 }
1720
1721 while (true) {
1722 if (!mem.isAligned(reader.seek, 2)) reader.seek += 1;
1723 if (reader.seek >= ctx.data.len) break;
1724
1725 const hdr = try reader.takeStruct(elf.ar_hdr, .little);
1726
1727 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) return error.InvalidArchiveHeaderMagicNumber;
1728
1729 const size = try hdr.size();
1730 defer reader.seek += size;
1731
1732 if (hdr.isSymtab()) {
1733 try ctx.parseSymtab(ctx.data[reader.seek..][0..size], .p32);
1734 continue;
1735 }
1736 if (hdr.isSymtab64()) {
1737 try ctx.parseSymtab(ctx.data[reader.seek..][0..size], .p64);
1738 continue;
1739 }
1740 if (hdr.isStrtab()) {
1741 ctx.strtab = ctx.data[reader.seek..][0..size];
1742 continue;
1743 }
1744 if (hdr.isSymdef() or hdr.isSymdefSorted()) continue;
1745
1746 const name = if (hdr.name()) |name|
1747 try gpa.dupe(u8, name)
1748 else if (try hdr.nameOffset()) |off|
1749 try gpa.dupe(u8, ctx.getString(off))
1750 else
1751 unreachable;
1752
1753 try ctx.objects.append(gpa, .{ .name = name, .off = reader.seek, .len = size });
1754 }
1755
1756 var output: std.Io.Writer.Allocating = .init(gpa);
1757 defer output.deinit();
1758 const writer = &output.writer;
1759
1760 switch (check.kind) {
1761 .archive_symtab => if (ctx.symtab.items.len > 0) {
1762 try ctx.dumpSymtab(writer);
1763 } else return step.fail("no archive symbol table found", .{}),
1764
1765 else => if (ctx.objects.items.len > 0) {
1766 try ctx.dumpObjects(step, check, writer);
1767 } else return step.fail("empty archive", .{}),
1768 }
1769
1770 return output.toOwnedSlice();
1771 }
1772
1773 const ArchiveContext = struct {
1774 gpa: Allocator,
1775 data: []const u8,
1776 symtab: std.ArrayList(ArSymtabEntry) = .empty,
1777 strtab: []const u8,
1778 objects: std.ArrayList(struct { name: []const u8, off: usize, len: usize }) = .empty,
1779
1780 fn parseSymtab(ctx: *ArchiveContext, raw: []const u8, ptr_width: enum { p32, p64 }) !void {
1781 var reader: std.Io.Reader = .fixed(raw);
1782 const num = switch (ptr_width) {
1783 .p32 => try reader.takeInt(u32, .big),
1784 .p64 => try reader.takeInt(u64, .big),
1785 };
1786 const ptr_size: usize = switch (ptr_width) {
1787 .p32 => @sizeOf(u32),
1788 .p64 => @sizeOf(u64),
1789 };
1790 const strtab_off = (num + 1) * ptr_size;
1791 const strtab_len = raw.len - strtab_off;
1792 const strtab = raw[strtab_off..][0..strtab_len];
1793
1794 try ctx.symtab.ensureTotalCapacityPrecise(ctx.gpa, num);
1795
1796 var stroff: usize = 0;
1797 for (0..num) |_| {
1798 const off = switch (ptr_width) {
1799 .p32 => try reader.takeInt(u32, .big),
1800 .p64 => try reader.takeInt(u64, .big),
1801 };
1802 const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + stroff)), 0);
1803 stroff += name.len + 1;
1804 ctx.symtab.appendAssumeCapacity(.{ .off = off, .name = name });
1805 }
1806 }
1807
1808 fn dumpSymtab(ctx: ArchiveContext, writer: anytype) !void {
1809 var files = std.AutoHashMap(usize, []const u8).init(ctx.gpa);
1810 defer files.deinit();
1811 try files.ensureUnusedCapacity(@intCast(ctx.objects.items.len));
1812
1813 for (ctx.objects.items) |object| {
1814 files.putAssumeCapacityNoClobber(object.off - @sizeOf(elf.ar_hdr), object.name);
1815 }
1816
1817 var symbols: std.array_hash_map.Auto(usize, std.array_list.Managed([]const u8)) = .empty;
1818 defer {
1819 for (symbols.values()) |*value| {
1820 value.deinit();
1821 }
1822 symbols.deinit(ctx.gpa);
1823 }
1824
1825 for (ctx.symtab.items) |entry| {
1826 const gop = try symbols.getOrPut(ctx.gpa, @intCast(entry.off));
1827 if (!gop.found_existing) {
1828 gop.value_ptr.* = std.array_list.Managed([]const u8).init(ctx.gpa);
1829 }
1830 try gop.value_ptr.append(entry.name);
1831 }
1832
1833 try writer.print("{s}\n", .{archive_symtab_label});
1834 for (symbols.keys(), symbols.values()) |off, values| {
1835 try writer.print("in object {s}\n", .{files.get(off).?});
1836 for (values.items) |value| {
1837 try writer.print("{s}\n", .{value});
1838 }
1839 }
1840 }
1841
1842 fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void {
1843 for (ctx.objects.items) |object| {
1844 try writer.print("object {s}\n", .{object.name});
1845 const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]);
1846 defer ctx.gpa.free(output);
1847 try writer.print("{s}\n", .{output});
1848 }
1849 }
1850
1851 fn getString(ctx: ArchiveContext, off: u32) []const u8 {
1852 assert(off < ctx.strtab.len);
1853 const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(ctx.strtab.ptr + off)), 0);
1854 return name[0 .. name.len - 1];
1855 }
1856
1857 const ArSymtabEntry = struct {
1858 name: [:0]const u8,
1859 off: u64,
1860 };
1861 };
1862
1863 fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
1864 const gpa = step.owner.allocator;
1865
1866 // `std.elf.Header` takes care of endianness issues for us.
1867 var reader: std.Io.Reader = .fixed(bytes);
1868 const hdr = try elf.Header.read(&reader);
1869
1870 var shdrs = try gpa.alloc(elf.Elf64_Shdr, hdr.shnum);
1871 defer gpa.free(shdrs);
1872 {
1873 var shdr_it = hdr.iterateSectionHeadersBuffer(bytes);
1874 var shdr_i: usize = 0;
1875 while (try shdr_it.next()) |shdr| : (shdr_i += 1) shdrs[shdr_i] = shdr;
1876 }
1877
1878 var phdrs = try gpa.alloc(elf.Elf64_Phdr, hdr.shnum);
1879 defer gpa.free(phdrs);
1880 {
1881 var phdr_it = hdr.iterateProgramHeadersBuffer(bytes);
1882 var phdr_i: usize = 0;
1883 while (try phdr_it.next()) |phdr| : (phdr_i += 1) phdrs[phdr_i] = phdr;
1884 }
1885
1886 var ctx = ObjectContext{
1887 .gpa = gpa,
1888 .data = bytes,
1889 .hdr = hdr,
1890 .shdrs = shdrs,
1891 .phdrs = phdrs,
1892 .shstrtab = undefined,
1893 };
1894 ctx.shstrtab = ctx.getSectionContents(ctx.hdr.shstrndx);
1895
1896 defer gpa.free(ctx.symtab.symbols);
1897 defer gpa.free(ctx.dysymtab.symbols);
1898 defer gpa.free(ctx.dyns);
1899
1900 for (ctx.shdrs, 0..) |shdr, i| switch (shdr.sh_type) {
1901 elf.SHT_SYMTAB, elf.SHT_DYNSYM => {
1902 const raw = ctx.getSectionContents(i);
1903 const nsyms = @divExact(raw.len, @sizeOf(elf.Elf64_Sym));
1904 const symbols = try gpa.alloc(elf.Elf64_Sym, nsyms);
1905
1906 var r: std.Io.Reader = .fixed(raw);
1907 for (0..nsyms) |si| symbols[si] = r.takeStruct(elf.Elf64_Sym, ctx.hdr.endian) catch unreachable;
1908
1909 const strings = ctx.getSectionContents(shdr.sh_link);
1910
1911 switch (shdr.sh_type) {
1912 elf.SHT_SYMTAB => {
1913 ctx.symtab = .{
1914 .symbols = symbols,
1915 .strings = strings,
1916 };
1917 },
1918 elf.SHT_DYNSYM => {
1919 ctx.dysymtab = .{
1920 .symbols = symbols,
1921 .strings = strings,
1922 };
1923 },
1924 else => unreachable,
1925 }
1926 },
1927 elf.SHT_DYNAMIC => {
1928 const raw = ctx.getSectionContents(i);
1929 const ndyns = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn));
1930 const dyns = try gpa.alloc(elf.Elf64_Dyn, ndyns);
1931
1932 var r: std.Io.Reader = .fixed(raw);
1933 for (0..ndyns) |si| dyns[si] = r.takeStruct(elf.Elf64_Dyn, ctx.hdr.endian) catch unreachable;
1934
1935 ctx.dyns = dyns;
1936 ctx.dyns_strings = ctx.getSectionContents(shdr.sh_link);
1937 },
1938
1939 else => {},
1940 };
1941
1942 var output: std.Io.Writer.Allocating = .init(gpa);
1943 defer output.deinit();
1944 const writer = &output.writer;
1945
1946 switch (check.kind) {
1947 .headers => {
1948 try ctx.dumpHeader(writer);
1949 try ctx.dumpShdrs(writer);
1950 try ctx.dumpPhdrs(writer);
1951 },
1952
1953 .symtab => if (ctx.symtab.symbols.len > 0) {
1954 try ctx.dumpSymtab(.symtab, writer);
1955 } else return step.fail("no symbol table found", .{}),
1956
1957 .dynamic_symtab => if (ctx.dysymtab.symbols.len > 0) {
1958 try ctx.dumpSymtab(.dysymtab, writer);
1959 } else return step.fail("no dynamic symbol table found", .{}),
1960
1961 .dynamic_section => if (ctx.dyns.len > 0) {
1962 try ctx.dumpDynamicSection(writer);
1963 } else return step.fail("no dynamic section found", .{}),
1964
1965 .dump_section => {
1966 const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0);
1967 const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name});
1968 try ctx.dumpSection(shndx, writer);
1969 },
1970
1971 else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}),
1972 }
1973
1974 return output.toOwnedSlice();
1975 }
1976
1977 const ObjectContext = struct {
1978 gpa: Allocator,
1979 data: []const u8,
1980 hdr: elf.Header,
1981 shdrs: []const elf.Elf64_Shdr,
1982 phdrs: []const elf.Elf64_Phdr,
1983 shstrtab: []const u8,
1984 symtab: Symtab = .{},
1985 dysymtab: Symtab = .{},
1986 dyns: []const elf.Elf64_Dyn = &.{},
1987 dyns_strings: []const u8 = &.{},
1988
1989 fn dumpHeader(ctx: ObjectContext, writer: anytype) !void {
1990 try writer.writeAll("header\n");
1991 try writer.print("type {s}\n", .{@tagName(ctx.hdr.type)});
1992 try writer.print("entry {x}\n", .{ctx.hdr.entry});
1993 }
1994
1995 fn dumpPhdrs(ctx: ObjectContext, writer: anytype) !void {
1996 if (ctx.phdrs.len == 0) return;
1997
1998 try writer.writeAll("program headers\n");
1999
2000 for (ctx.phdrs, 0..) |phdr, phndx| {
2001 try writer.print("phdr {d}\n", .{phndx});
2002 try writer.print("type {f}\n", .{fmtPhType(phdr.p_type)});
2003 try writer.print("vaddr {x}\n", .{phdr.p_vaddr});
2004 try writer.print("paddr {x}\n", .{phdr.p_paddr});
2005 try writer.print("offset {x}\n", .{phdr.p_offset});
2006 try writer.print("memsz {x}\n", .{phdr.p_memsz});
2007 try writer.print("filesz {x}\n", .{phdr.p_filesz});
2008 try writer.print("align {x}\n", .{phdr.p_align});
2009
2010 {
2011 const flags = phdr.p_flags;
2012 try writer.writeAll("flags");
2013 if (flags > 0) try writer.writeByte(' ');
2014 if (flags & elf.PF_R != 0) {
2015 try writer.writeByte('R');
2016 }
2017 if (flags & elf.PF_W != 0) {
2018 try writer.writeByte('W');
2019 }
2020 if (flags & elf.PF_X != 0) {
2021 try writer.writeByte('E');
2022 }
2023 if (flags & elf.PF_MASKOS != 0) {
2024 try writer.writeAll("OS");
2025 }
2026 if (flags & elf.PF_MASKPROC != 0) {
2027 try writer.writeAll("PROC");
2028 }
2029 try writer.writeByte('\n');
2030 }
2031 }
2032 }
2033
2034 fn dumpShdrs(ctx: ObjectContext, writer: anytype) !void {
2035 if (ctx.shdrs.len == 0) return;
2036
2037 try writer.writeAll("section headers\n");
2038
2039 for (ctx.shdrs, 0..) |shdr, shndx| {
2040 try writer.print("shdr {d}\n", .{shndx});
2041 try writer.print("name {s}\n", .{ctx.getSectionName(shndx)});
2042 try writer.print("type {f}\n", .{fmtShType(shdr.sh_type)});
2043 try writer.print("addr {x}\n", .{shdr.sh_addr});
2044 try writer.print("offset {x}\n", .{shdr.sh_offset});
2045 try writer.print("size {x}\n", .{shdr.sh_size});
2046 try writer.print("addralign {x}\n", .{shdr.sh_addralign});
2047 // TODO dump formatted sh_flags
2048 }
2049 }
2050
2051 fn dumpDynamicSection(ctx: ObjectContext, writer: anytype) !void {
2052 try writer.writeAll(ElfDumper.dynamic_section_label ++ "\n");
2053
2054 for (ctx.dyns) |entry| {
2055 const key = @as(u64, @bitCast(entry.d_tag));
2056 const value = entry.d_val;
2057
2058 const key_str = switch (key) {
2059 elf.DT_NEEDED => "NEEDED",
2060 elf.DT_SONAME => "SONAME",
2061 elf.DT_INIT_ARRAY => "INIT_ARRAY",
2062 elf.DT_INIT_ARRAYSZ => "INIT_ARRAYSZ",
2063 elf.DT_FINI_ARRAY => "FINI_ARRAY",
2064 elf.DT_FINI_ARRAYSZ => "FINI_ARRAYSZ",
2065 elf.DT_HASH => "HASH",
2066 elf.DT_GNU_HASH => "GNU_HASH",
2067 elf.DT_STRTAB => "STRTAB",
2068 elf.DT_SYMTAB => "SYMTAB",
2069 elf.DT_STRSZ => "STRSZ",
2070 elf.DT_SYMENT => "SYMENT",
2071 elf.DT_PLTGOT => "PLTGOT",
2072 elf.DT_PLTRELSZ => "PLTRELSZ",
2073 elf.DT_PLTREL => "PLTREL",
2074 elf.DT_JMPREL => "JMPREL",
2075 elf.DT_RELA => "RELA",
2076 elf.DT_RELASZ => "RELASZ",
2077 elf.DT_RELAENT => "RELAENT",
2078 elf.DT_VERDEF => "VERDEF",
2079 elf.DT_VERDEFNUM => "VERDEFNUM",
2080 elf.DT_FLAGS => "FLAGS",
2081 elf.DT_FLAGS_1 => "FLAGS_1",
2082 elf.DT_VERNEED => "VERNEED",
2083 elf.DT_VERNEEDNUM => "VERNEEDNUM",
2084 elf.DT_VERSYM => "VERSYM",
2085 elf.DT_RELACOUNT => "RELACOUNT",
2086 elf.DT_RPATH => "RPATH",
2087 elf.DT_RUNPATH => "RUNPATH",
2088 elf.DT_INIT => "INIT",
2089 elf.DT_FINI => "FINI",
2090 elf.DT_NULL => "NULL",
2091 else => "UNKNOWN",
2092 };
2093 try writer.print("{s}", .{key_str});
2094
2095 switch (key) {
2096 elf.DT_NEEDED,
2097 elf.DT_SONAME,
2098 elf.DT_RPATH,
2099 elf.DT_RUNPATH,
2100 => {
2101 const name = getString(ctx.dyns_strings, @intCast(value));
2102 try writer.print(" {s}", .{name});
2103 },
2104
2105 elf.DT_INIT_ARRAY,
2106 elf.DT_FINI_ARRAY,
2107 elf.DT_HASH,
2108 elf.DT_GNU_HASH,
2109 elf.DT_STRTAB,
2110 elf.DT_SYMTAB,
2111 elf.DT_PLTGOT,
2112 elf.DT_JMPREL,
2113 elf.DT_RELA,
2114 elf.DT_VERDEF,
2115 elf.DT_VERNEED,
2116 elf.DT_VERSYM,
2117 elf.DT_INIT,
2118 elf.DT_FINI,
2119 elf.DT_NULL,
2120 => try writer.print(" {x}", .{value}),
2121
2122 elf.DT_INIT_ARRAYSZ,
2123 elf.DT_FINI_ARRAYSZ,
2124 elf.DT_STRSZ,
2125 elf.DT_SYMENT,
2126 elf.DT_PLTRELSZ,
2127 elf.DT_RELASZ,
2128 elf.DT_RELAENT,
2129 elf.DT_RELACOUNT,
2130 => try writer.print(" {d}", .{value}),
2131
2132 elf.DT_PLTREL => try writer.writeAll(switch (value) {
2133 elf.DT_REL => " REL",
2134 elf.DT_RELA => " RELA",
2135 else => " UNKNOWN",
2136 }),
2137
2138 elf.DT_FLAGS => if (value > 0) {
2139 if (value & elf.DF_ORIGIN != 0) try writer.writeAll(" ORIGIN");
2140 if (value & elf.DF_SYMBOLIC != 0) try writer.writeAll(" SYMBOLIC");
2141 if (value & elf.DF_TEXTREL != 0) try writer.writeAll(" TEXTREL");
2142 if (value & elf.DF_BIND_NOW != 0) try writer.writeAll(" BIND_NOW");
2143 if (value & elf.DF_STATIC_TLS != 0) try writer.writeAll(" STATIC_TLS");
2144 },
2145
2146 elf.DT_FLAGS_1 => if (value > 0) {
2147 if (value & elf.DF_1_NOW != 0) try writer.writeAll(" NOW");
2148 if (value & elf.DF_1_GLOBAL != 0) try writer.writeAll(" GLOBAL");
2149 if (value & elf.DF_1_GROUP != 0) try writer.writeAll(" GROUP");
2150 if (value & elf.DF_1_NODELETE != 0) try writer.writeAll(" NODELETE");
2151 if (value & elf.DF_1_LOADFLTR != 0) try writer.writeAll(" LOADFLTR");
2152 if (value & elf.DF_1_INITFIRST != 0) try writer.writeAll(" INITFIRST");
2153 if (value & elf.DF_1_NOOPEN != 0) try writer.writeAll(" NOOPEN");
2154 if (value & elf.DF_1_ORIGIN != 0) try writer.writeAll(" ORIGIN");
2155 if (value & elf.DF_1_DIRECT != 0) try writer.writeAll(" DIRECT");
2156 if (value & elf.DF_1_TRANS != 0) try writer.writeAll(" TRANS");
2157 if (value & elf.DF_1_INTERPOSE != 0) try writer.writeAll(" INTERPOSE");
2158 if (value & elf.DF_1_NODEFLIB != 0) try writer.writeAll(" NODEFLIB");
2159 if (value & elf.DF_1_NODUMP != 0) try writer.writeAll(" NODUMP");
2160 if (value & elf.DF_1_CONFALT != 0) try writer.writeAll(" CONFALT");
2161 if (value & elf.DF_1_ENDFILTEE != 0) try writer.writeAll(" ENDFILTEE");
2162 if (value & elf.DF_1_DISPRELDNE != 0) try writer.writeAll(" DISPRELDNE");
2163 if (value & elf.DF_1_DISPRELPND != 0) try writer.writeAll(" DISPRELPND");
2164 if (value & elf.DF_1_NODIRECT != 0) try writer.writeAll(" NODIRECT");
2165 if (value & elf.DF_1_IGNMULDEF != 0) try writer.writeAll(" IGNMULDEF");
2166 if (value & elf.DF_1_NOKSYMS != 0) try writer.writeAll(" NOKSYMS");
2167 if (value & elf.DF_1_NOHDR != 0) try writer.writeAll(" NOHDR");
2168 if (value & elf.DF_1_EDITED != 0) try writer.writeAll(" EDITED");
2169 if (value & elf.DF_1_NORELOC != 0) try writer.writeAll(" NORELOC");
2170 if (value & elf.DF_1_SYMINTPOSE != 0) try writer.writeAll(" SYMINTPOSE");
2171 if (value & elf.DF_1_GLOBAUDIT != 0) try writer.writeAll(" GLOBAUDIT");
2172 if (value & elf.DF_1_SINGLETON != 0) try writer.writeAll(" SINGLETON");
2173 if (value & elf.DF_1_STUB != 0) try writer.writeAll(" STUB");
2174 if (value & elf.DF_1_PIE != 0) try writer.writeAll(" PIE");
2175 },
2176
2177 else => try writer.print(" {x}", .{value}),
2178 }
2179 try writer.writeByte('\n');
2180 }
2181 }
2182
2183 fn dumpSymtab(ctx: ObjectContext, comptime @"type": enum { symtab, dysymtab }, writer: anytype) !void {
2184 const symtab = switch (@"type") {
2185 .symtab => ctx.symtab,
2186 .dysymtab => ctx.dysymtab,
2187 };
2188
2189 try writer.writeAll(switch (@"type") {
2190 .symtab => symtab_label,
2191 .dysymtab => dynamic_symtab_label,
2192 } ++ "\n");
2193
2194 for (symtab.symbols, 0..) |sym, index| {
2195 try writer.print("{x} {x}", .{ sym.st_value, sym.st_size });
2196
2197 {
2198 if (elf.SHN_LORESERVE <= sym.st_shndx and sym.st_shndx < elf.SHN_HIRESERVE) {
2199 if (elf.SHN_LOPROC <= sym.st_shndx and sym.st_shndx < elf.SHN_HIPROC) {
2200 try writer.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC});
2201 } else {
2202 const sym_ndx = switch (sym.st_shndx) {
2203 elf.SHN_ABS => "ABS",
2204 elf.SHN_COMMON => "COM",
2205 elf.SHN_LIVEPATCH => "LIV",
2206 else => "UNK",
2207 };
2208 try writer.print(" {s}", .{sym_ndx});
2209 }
2210 } else if (sym.st_shndx == elf.SHN_UNDEF) {
2211 try writer.writeAll(" UND");
2212 } else {
2213 try writer.print(" {x}", .{sym.st_shndx});
2214 }
2215 }
2216
2217 blk: {
2218 const tt = sym.st_type();
2219 const sym_type = switch (tt) {
2220 elf.STT_NOTYPE => "NOTYPE",
2221 elf.STT_OBJECT => "OBJECT",
2222 elf.STT_FUNC => "FUNC",
2223 elf.STT_SECTION => "SECTION",
2224 elf.STT_FILE => "FILE",
2225 elf.STT_COMMON => "COMMON",
2226 elf.STT_TLS => "TLS",
2227 elf.STT_NUM => "NUM",
2228 elf.STT_GNU_IFUNC => "IFUNC",
2229 else => if (elf.STT_LOPROC <= tt and tt < elf.STT_HIPROC) {
2230 break :blk try writer.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC});
2231 } else if (elf.STT_LOOS <= tt and tt < elf.STT_HIOS) {
2232 break :blk try writer.print(" LOOS+{d}", .{tt - elf.STT_LOOS});
2233 } else "UNK",
2234 };
2235 try writer.print(" {s}", .{sym_type});
2236 }
2237
2238 blk: {
2239 const bind = sym.st_bind();
2240 const sym_bind = switch (bind) {
2241 elf.STB_LOCAL => "LOCAL",
2242 elf.STB_GLOBAL => "GLOBAL",
2243 elf.STB_WEAK => "WEAK",
2244 elf.STB_NUM => "NUM",
2245 else => if (elf.STB_LOPROC <= bind and bind < elf.STB_HIPROC) {
2246 break :blk try writer.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC});
2247 } else if (elf.STB_LOOS <= bind and bind < elf.STB_HIOS) {
2248 break :blk try writer.print(" LOOS+{d}", .{bind - elf.STB_LOOS});
2249 } else "UNKNOWN",
2250 };
2251 try writer.print(" {s}", .{sym_bind});
2252 }
2253
2254 const sym_vis = @as(elf.STV, @enumFromInt(@as(u3, @truncate(sym.st_other))));
2255 try writer.print(" {s}", .{@tagName(sym_vis)});
2256
2257 const sym_name = switch (sym.st_type()) {
2258 elf.STT_SECTION => ctx.getSectionName(sym.st_shndx),
2259 else => symtab.getName(index).?,
2260 };
2261 try writer.print(" {s}\n", .{sym_name});
2262 }
2263 }
2264
2265 fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void {
2266 const data = ctx.getSectionContents(shndx);
2267 try writer.print("{s}", .{data});
2268 }
2269
2270 inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 {
2271 const shdr = ctx.shdrs[shndx];
2272 return getString(ctx.shstrtab, shdr.sh_name);
2273 }
2274
2275 fn getSectionContents(ctx: ObjectContext, shndx: usize) []const u8 {
2276 const shdr = ctx.shdrs[shndx];
2277 assert(shdr.sh_offset < ctx.data.len);
2278 assert(shdr.sh_offset + shdr.sh_size <= ctx.data.len);
2279 return ctx.data[shdr.sh_offset..][0..shdr.sh_size];
2280 }
2281
2282 fn getSectionByName(ctx: ObjectContext, name: []const u8) ?usize {
2283 for (0..ctx.shdrs.len) |shndx| {
2284 if (mem.eql(u8, ctx.getSectionName(shndx), name)) return shndx;
2285 } else return null;
2286 }
2287 };
2288
2289 const Symtab = struct {
2290 symbols: []const elf.Elf64_Sym = &.{},
2291 strings: []const u8 = &.{},
2292
2293 fn get(st: Symtab, index: usize) ?elf.Elf64_Sym {
2294 if (index >= st.symbols.len) return null;
2295 return st.symbols[index];
2296 }
2297
2298 fn getName(st: Symtab, index: usize) ?[]const u8 {
2299 const sym = st.get(index) orelse return null;
2300 return getString(st.strings, sym.st_name);
2301 }
2302 };
2303
2304 fn getString(strtab: []const u8, off: u32) []const u8 {
2305 assert(off < strtab.len);
2306 return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0);
2307 }
2308
2309 fn fmtShType(sh_type: u32) std.fmt.Alt(u32, formatShType) {
2310 return .{ .data = sh_type };
2311 }
2312
2313 fn formatShType(sh_type: u32, writer: *Writer) Writer.Error!void {
2314 const name = switch (sh_type) {
2315 elf.SHT_NULL => "NULL",
2316 elf.SHT_PROGBITS => "PROGBITS",
2317 elf.SHT_SYMTAB => "SYMTAB",
2318 elf.SHT_STRTAB => "STRTAB",
2319 elf.SHT_RELA => "RELA",
2320 elf.SHT_HASH => "HASH",
2321 elf.SHT_DYNAMIC => "DYNAMIC",
2322 elf.SHT_NOTE => "NOTE",
2323 elf.SHT_NOBITS => "NOBITS",
2324 elf.SHT_REL => "REL",
2325 elf.SHT_SHLIB => "SHLIB",
2326 elf.SHT_DYNSYM => "DYNSYM",
2327 elf.SHT_INIT_ARRAY => "INIT_ARRAY",
2328 elf.SHT_FINI_ARRAY => "FINI_ARRAY",
2329 elf.SHT_PREINIT_ARRAY => "PREINIT_ARRAY",
2330 elf.SHT_GROUP => "GROUP",
2331 elf.SHT_SYMTAB_SHNDX => "SYMTAB_SHNDX",
2332 elf.SHT_X86_64_UNWIND => "X86_64_UNWIND",
2333 elf.SHT_LLVM_ADDRSIG => "LLVM_ADDRSIG",
2334 elf.SHT_GNU_HASH => "GNU_HASH",
2335 elf.SHT_GNU_VERDEF => "VERDEF",
2336 elf.SHT_GNU_VERNEED => "VERNEED",
2337 elf.SHT_GNU_VERSYM => "VERSYM",
2338 else => if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) {
2339 return try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS});
2340 } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) {
2341 return try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC});
2342 } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) {
2343 return try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER});
2344 } else "UNKNOWN",
2345 };
2346 try writer.writeAll(name);
2347 }
2348
2349 fn fmtPhType(ph_type: u32) std.fmt.Alt(u32, formatPhType) {
2350 return .{ .data = ph_type };
2351 }
2352
2353 fn formatPhType(ph_type: u32, writer: *Writer) Writer.Error!void {
2354 const p_type = switch (ph_type) {
2355 elf.PT_NULL => "NULL",
2356 elf.PT_LOAD => "LOAD",
2357 elf.PT_DYNAMIC => "DYNAMIC",
2358 elf.PT_INTERP => "INTERP",
2359 elf.PT_NOTE => "NOTE",
2360 elf.PT_SHLIB => "SHLIB",
2361 elf.PT_PHDR => "PHDR",
2362 elf.PT_TLS => "TLS",
2363 elf.PT_NUM => "NUM",
2364 elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME",
2365 elf.PT_GNU_STACK => "GNU_STACK",
2366 elf.PT_GNU_RELRO => "GNU_RELRO",
2367 else => if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) {
2368 return try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS});
2369 } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) {
2370 return try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC});
2371 } else "UNKNOWN",
2372 };
2373 try writer.writeAll(p_type);
2374 }
2375};
2376
2377const WasmDumper = struct {
2378 const symtab_label = "symbols";
2379
2380 fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 {
2381 const gpa = step.owner.allocator;
2382 var reader: std.Io.Reader = .fixed(bytes);
2383
2384 const buf = try reader.takeArray(8);
2385 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
2386 return error.InvalidMagicByte;
2387 }
2388 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
2389 return error.UnsupportedWasmVersion;
2390 }
2391
2392 var output: std.Io.Writer.Allocating = .init(gpa);
2393 defer output.deinit();
2394 parseAndDumpInner(step, check, bytes, &reader, &output.writer) catch |err| switch (err) {
2395 error.EndOfStream => try output.writer.writeAll("\n<UnexpectedEndOfStream>"),
2396 else => |e| return e,
2397 };
2398 return output.toOwnedSlice();
2399 }
2400
2401 fn parseAndDumpInner(
2402 step: *Step,
2403 check: Check,
2404 bytes: []const u8,
2405 reader: *std.Io.Reader,
2406 writer: *std.Io.Writer,
2407 ) !void {
2408 switch (check.kind) {
2409 .headers => {
2410 while (reader.takeByte()) |current_byte| {
2411 const section = std.enums.fromInt(std.wasm.Section, current_byte) orelse {
2412 return step.fail("Found invalid section id '{d}'", .{current_byte});
2413 };
2414
2415 const section_length = try reader.takeLeb128(u32);
2416 try parseAndDumpSection(step, section, bytes[reader.seek..][0..section_length], writer);
2417 reader.seek += section_length;
2418 } else |_| {} // reached end of stream
2419 },
2420
2421 else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}),
2422 }
2423 }
2424
2425 fn parseAndDumpSection(
2426 step: *Step,
2427 section: std.wasm.Section,
2428 data: []const u8,
2429 writer: *std.Io.Writer,
2430 ) !void {
2431 var reader: std.Io.Reader = .fixed(data);
2432
2433 try writer.print(
2434 \\Section {s}
2435 \\size {d}
2436 , .{ @tagName(section), data.len });
2437
2438 switch (section) {
2439 .type,
2440 .import,
2441 .function,
2442 .table,
2443 .memory,
2444 .global,
2445 .@"export",
2446 .element,
2447 .code,
2448 .data,
2449 => {
2450 const entries = try reader.takeLeb128(u32);
2451 try writer.print("\nentries {d}\n", .{entries});
2452 try parseSection(step, section, data[reader.seek..], entries, writer);
2453 },
2454 .custom => {
2455 const name_length = try reader.takeLeb128(u32);
2456 const name = data[reader.seek..][0..name_length];
2457 reader.seek += name_length;
2458 try writer.print("\nname {s}\n", .{name});
2459
2460 if (mem.eql(u8, name, "name")) {
2461 try parseDumpNames(step, &reader, writer, data);
2462 } else if (mem.eql(u8, name, "producers")) {
2463 try parseDumpProducers(&reader, writer, data);
2464 } else if (mem.eql(u8, name, "target_features")) {
2465 try parseDumpFeatures(&reader, writer, data);
2466 }
2467 // TODO: Implement parsing and dumping other custom sections (such as relocations)
2468 },
2469 .start => {
2470 const start = try reader.takeLeb128(u32);
2471 try writer.print("\nstart {d}\n", .{start});
2472 },
2473 .data_count => {
2474 const count = try reader.takeLeb128(u32);
2475 try writer.print("\ncount {d}\n", .{count});
2476 },
2477 else => {}, // skip unknown sections
2478 }
2479 }
2480
2481 fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
2482 var reader: std.Io.Reader = .fixed(data);
2483
2484 switch (section) {
2485 .type => {
2486 var i: u32 = 0;
2487 while (i < entries) : (i += 1) {
2488 const func_type = try reader.takeByte();
2489 if (func_type != std.wasm.function_type) {
2490 return step.fail("expected function type, found byte '{d}'", .{func_type});
2491 }
2492 const params = try reader.takeLeb128(u32);
2493 try writer.print("params {d}\n", .{params});
2494 var index: u32 = 0;
2495 while (index < params) : (index += 1) {
2496 _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer);
2497 } else index = 0;
2498 const returns = try reader.takeLeb128(u32);
2499 try writer.print("returns {d}\n", .{returns});
2500 while (index < returns) : (index += 1) {
2501 _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer);
2502 }
2503 }
2504 },
2505 .import => {
2506 var i: u32 = 0;
2507 while (i < entries) : (i += 1) {
2508 const module_name_len = try reader.takeLeb128(u32);
2509 const module_name = data[reader.seek..][0..module_name_len];
2510 reader.seek += module_name_len;
2511 const name_len = try reader.takeLeb128(u32);
2512 const name = data[reader.seek..][0..name_len];
2513 reader.seek += name_len;
2514
2515 const kind = std.enums.fromInt(std.wasm.ExternalKind, try reader.takeByte()) orelse {
2516 return step.fail("invalid import kind", .{});
2517 };
2518
2519 try writer.print(
2520 \\module {s}
2521 \\name {s}
2522 \\kind {s}
2523 , .{ module_name, name, @tagName(kind) });
2524 try writer.writeByte('\n');
2525 switch (kind) {
2526 .function => {
2527 try writer.print("index {d}\n", .{try reader.takeLeb128(u32)});
2528 },
2529 .memory => {
2530 try parseDumpLimits(&reader, writer);
2531 },
2532 .global => {
2533 _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer);
2534 try writer.print("mutable {}\n", .{0x01 == try reader.takeLeb128(u32)});
2535 },
2536 .table => {
2537 _ = try parseDumpType(step, std.wasm.RefType, &reader, writer);
2538 try parseDumpLimits(&reader, writer);
2539 },
2540 }
2541 }
2542 },
2543 .function => {
2544 var i: u32 = 0;
2545 while (i < entries) : (i += 1) {
2546 try writer.print("index {d}\n", .{try reader.takeLeb128(u32)});
2547 }
2548 },
2549 .table => {
2550 var i: u32 = 0;
2551 while (i < entries) : (i += 1) {
2552 _ = try parseDumpType(step, std.wasm.RefType, &reader, writer);
2553 try parseDumpLimits(&reader, writer);
2554 }
2555 },
2556 .memory => {
2557 var i: u32 = 0;
2558 while (i < entries) : (i += 1) {
2559 try parseDumpLimits(&reader, writer);
2560 }
2561 },
2562 .global => {
2563 var i: u32 = 0;
2564 while (i < entries) : (i += 1) {
2565 _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer);
2566 try writer.print("mutable {}\n", .{0x01 == try reader.takeLeb128(u1)});
2567 try parseDumpInit(step, &reader, writer);
2568 }
2569 },
2570 .@"export" => {
2571 var i: u32 = 0;
2572 while (i < entries) : (i += 1) {
2573 const name_len = try reader.takeLeb128(u32);
2574 const name = data[reader.seek..][0..name_len];
2575 reader.seek += name_len;
2576 const kind_byte = try reader.takeLeb128(u8);
2577 const kind = std.enums.fromInt(std.wasm.ExternalKind, kind_byte) orelse {
2578 return step.fail("invalid export kind value '{d}'", .{kind_byte});
2579 };
2580 const index = try reader.takeLeb128(u32);
2581 try writer.print(
2582 \\name {s}
2583 \\kind {s}
2584 \\index {d}
2585 , .{ name, @tagName(kind), index });
2586 try writer.writeByte('\n');
2587 }
2588 },
2589 .element => {
2590 var i: u32 = 0;
2591 while (i < entries) : (i += 1) {
2592 try writer.print("table index {d}\n", .{try reader.takeLeb128(u32)});
2593 try parseDumpInit(step, &reader, writer);
2594
2595 const function_indexes = try reader.takeLeb128(u32);
2596 var function_index: u32 = 0;
2597 try writer.print("indexes {d}\n", .{function_indexes});
2598 while (function_index < function_indexes) : (function_index += 1) {
2599 try writer.print("index {d}\n", .{try reader.takeLeb128(u32)});
2600 }
2601 }
2602 },
2603 .code => {}, // code section is considered opaque to linker
2604 .data => {
2605 var i: u32 = 0;
2606 while (i < entries) : (i += 1) {
2607 const flags = try reader.takeLeb128(u32);
2608 const index = if (flags & 0x02 != 0)
2609 try reader.takeLeb128(u32)
2610 else
2611 0;
2612 try writer.print("memory index 0x{x}\n", .{index});
2613 if (flags == 0) {
2614 try parseDumpInit(step, &reader, writer);
2615 }
2616
2617 const size = try reader.takeLeb128(u32);
2618 try writer.print("size {d}\n", .{size});
2619 try reader.discardAll(size); // we do not care about the content of the segments
2620 }
2621 },
2622 else => unreachable,
2623 }
2624 }
2625
2626 fn parseDumpType(step: *Step, comptime E: type, reader: *std.Io.Reader, writer: *std.Io.Writer) !E {
2627 const byte = try reader.takeByte();
2628 const tag = std.enums.fromInt(E, byte) orelse {
2629 return step.fail("invalid wasm type value '{d}'", .{byte});
2630 };
2631 try writer.print("type {s}\n", .{@tagName(tag)});
2632 return tag;
2633 }
2634
2635 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
2636 const flags = try reader.takeLeb128(u8);
2637 const min = try reader.takeLeb128(u32);
2638
2639 try writer.print("min {x}\n", .{min});
2640 if (flags != 0) {
2641 try writer.print("max {x}\n", .{try reader.takeLeb128(u32)});
2642 }
2643 }
2644
2645 fn parseDumpInit(step: *Step, reader: *std.Io.Reader, writer: *std.Io.Writer) !void {
2646 const byte = try reader.takeByte();
2647 const opcode = std.enums.fromInt(std.wasm.Opcode, byte) orelse {
2648 return step.fail("invalid wasm opcode '{d}'", .{byte});
2649 };
2650 switch (opcode) {
2651 .i32_const => try writer.print("i32.const {x}\n", .{try reader.takeLeb128(i32)}),
2652 .i64_const => try writer.print("i64.const {x}\n", .{try reader.takeLeb128(i64)}),
2653 .f32_const => try writer.print("f32.const {x}\n", .{@as(f32, @bitCast(try reader.takeInt(u32, .little)))}),
2654 .f64_const => try writer.print("f64.const {x}\n", .{@as(f64, @bitCast(try reader.takeInt(u64, .little)))}),
2655 .global_get => try writer.print("global.get {x}\n", .{try reader.takeLeb128(u32)}),
2656 else => unreachable,
2657 }
2658 const end_opcode = try reader.takeLeb128(u8);
2659 if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) {
2660 return step.fail("expected 'end' opcode in init expression", .{});
2661 }
2662 }
2663
2664 /// https://webassembly.github.io/spec/core/appendix/custom.html
2665 fn parseDumpNames(step: *Step, reader: *std.Io.Reader, writer: *std.Io.Writer, data: []const u8) !void {
2666 while (reader.seek < data.len) {
2667 switch (try parseDumpType(step, std.wasm.NameSubsection, reader, writer)) {
2668 // The module name subsection ... consists of a single name
2669 // that is assigned to the module itself.
2670 .module => {
2671 const size = try reader.takeLeb128(u32);
2672 const name_len = try reader.takeLeb128(u32);
2673 if (size != name_len + 1) return error.BadSubsectionSize;
2674 if (reader.seek + name_len > data.len) return error.UnexpectedEndOfStream;
2675 try writer.print("name {s}\n", .{data[reader.seek..][0..name_len]});
2676 reader.seek += name_len;
2677 },
2678
2679 // The function name subsection ... consists of a name map
2680 // assigning function names to function indices.
2681 .function, .global, .data_segment => {
2682 const size = try reader.takeLeb128(u32);
2683 const entries = try reader.takeLeb128(u32);
2684 try writer.print(
2685 \\size {d}
2686 \\names {d}
2687 \\
2688 , .{ size, entries });
2689 for (0..entries) |_| {
2690 const index = try reader.takeLeb128(u32);
2691 const name_len = try reader.takeLeb128(u32);
2692 if (reader.seek + name_len > data.len) return error.UnexpectedEndOfStream;
2693 const name = data[reader.seek..][0..name_len];
2694 reader.seek += name.len;
2695
2696 try writer.print(
2697 \\index {d}
2698 \\name {s}
2699 \\
2700 , .{ index, name });
2701 }
2702 },
2703
2704 // The local name subsection ... consists of an indirect name
2705 // map assigning local names to local indices grouped by
2706 // function indices.
2707 .local => {
2708 return step.fail("TODO implement parseDumpNames for local subsections", .{});
2709 },
2710
2711 else => |t| return step.fail("invalid subsection type: {s}", .{@tagName(t)}),
2712 }
2713 }
2714 }
2715
2716 fn parseDumpProducers(reader: *std.Io.Reader, writer: *std.Io.Writer, data: []const u8) !void {
2717 const field_count = try reader.takeLeb128(u32);
2718 try writer.print("fields {d}\n", .{field_count});
2719 var current_field: u32 = 0;
2720 while (current_field < field_count) : (current_field += 1) {
2721 const field_name_length = try reader.takeLeb128(u32);
2722 const field_name = data[reader.seek..][0..field_name_length];
2723 reader.seek += field_name_length;
2724
2725 const value_count = try reader.takeLeb128(u32);
2726 try writer.print(
2727 \\field_name {s}
2728 \\values {d}
2729 , .{ field_name, value_count });
2730 try writer.writeByte('\n');
2731 var current_value: u32 = 0;
2732 while (current_value < value_count) : (current_value += 1) {
2733 const value_length = try reader.takeLeb128(u32);
2734 const value = data[reader.seek..][0..value_length];
2735 reader.seek += value_length;
2736
2737 const version_length = try reader.takeLeb128(u32);
2738 const version = data[reader.seek..][0..version_length];
2739 reader.seek += version_length;
2740
2741 try writer.print(
2742 \\value_name {s}
2743 \\version {s}
2744 , .{ value, version });
2745 try writer.writeByte('\n');
2746 }
2747 }
2748 }
2749
2750 fn parseDumpFeatures(reader: *std.Io.Reader, writer: *std.Io.Writer, data: []const u8) !void {
2751 const feature_count = try reader.takeLeb128(u32);
2752 try writer.print("features {d}\n", .{feature_count});
2753
2754 var index: u32 = 0;
2755 while (index < feature_count) : (index += 1) {
2756 const prefix_byte = try reader.takeLeb128(u8);
2757 const name_length = try reader.takeLeb128(u32);
2758 const feature_name = data[reader.seek..][0..name_length];
2759 reader.seek += name_length;
2760
2761 try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
2762 }
2763 }
2764};
lib/std/Build/Step/Compile.zig-4
...@@ -611,10 +611,6 @@ pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {...@@ -611,10 +611,6 @@ pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {
611 return b.addObjCopy(cs.getEmittedBin(), copy);611 return b.addObjCopy(cs.getEmittedBin(), copy);
612}612}
613613
614pub fn checkObject(compile: *Compile) *Step.CheckObject {
615 return Step.CheckObject.create(compile.step.owner, compile.getEmittedBin(), compile.rootModuleTarget().ofmt);
616}
617
618pub fn setLinkerScript(compile: *Compile, source: LazyPath) void {614pub fn setLinkerScript(compile: *Compile, source: LazyPath) void {
619 const b = compile.step.owner;615 const b = compile.step.owner;
620 compile.linker_script = source.dupe(b);616 compile.linker_script = source.dupe(b);
test/link/bss/build.zig deleted-20
...@@ -1,20 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 b.default_step = test_step;
6
7 const exe = b.addExecutable(.{
8 .name = "bss",
9 .root_module = b.createModule(.{
10 .root_source_file = b.path("main.zig"),
11 .target = b.graph.host,
12 .optimize = .Debug,
13 }),
14 });
15
16 const run = b.addRunArtifact(exe);
17 run.expectStdOutEqual("0, 1, 0\n");
18
19 test_step.dependOn(&run.step);
20}
test/link/bss/main.zig deleted-17
...@@ -1,17 +0,0 @@
1const std = @import("std");
2
3// Stress test zerofill layout
4var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000;
5
6pub fn main() anyerror!void {
7 var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{});
8
9 buffer[0x10] = 1;
10
11 try stdout_writer.interface.print("{d}, {d}, {d}\n", .{
12 // workaround the dreaded decl_val
13 (&buffer)[0],
14 (&buffer)[0x10],
15 (&buffer)[0x1000000 - 1],
16 });
17}
test/link/build.zig deleted-54
...@@ -1,54 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const link = @import("link.zig");
4
5pub fn build(b: *std.Build) void {
6 const step = b.step("test", "Run link test cases");
7 b.default_step = step;
8
9 const enable_ios_sdk = b.option(bool, "enable_ios_sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false;
10 const enable_macos_sdk = b.option(bool, "enable_macos_sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk;
11 const enable_symlinks_windows = b.option(bool, "enable_symlinks_windows", "Run tests requiring presence of symlinks on Windows") orelse false;
12 const has_symlinks = builtin.os.tag != .windows or enable_symlinks_windows;
13
14 const build_opts: link.BuildOptions = .{
15 .has_ios_sdk = enable_ios_sdk,
16 .has_macos_sdk = enable_macos_sdk,
17 .has_symlinks = has_symlinks,
18 };
19 step.dependOn(@import("elf.zig").testAll(b, build_opts));
20 step.dependOn(@import("macho.zig").testAll(b, build_opts));
21
22 add_dep_steps: for (b.available_deps) |available_dep| {
23 const dep_name, const dep_hash = available_dep;
24
25 const all_pkgs = @import("root").dependencies.packages;
26 inline for (@typeInfo(all_pkgs).@"struct".decls) |decl| {
27 const pkg_hash = decl.name;
28 if (std.mem.eql(u8, dep_hash, pkg_hash)) {
29 const pkg = @field(all_pkgs, pkg_hash);
30 if (!@hasDecl(pkg, "build_zig")) {
31 std.debug.panic("link test case '{s}' is missing a 'build.zig' file", .{dep_name});
32 }
33 const requires_ios_sdk = @hasDecl(pkg.build_zig, "requires_ios_sdk") and
34 pkg.build_zig.requires_ios_sdk;
35 const requires_macos_sdk = @hasDecl(pkg.build_zig, "requires_macos_sdk") and
36 pkg.build_zig.requires_macos_sdk;
37 const requires_symlinks = @hasDecl(pkg.build_zig, "requires_symlinks") and
38 pkg.build_zig.requires_symlinks;
39 if ((requires_symlinks and !has_symlinks) or
40 (requires_macos_sdk and !enable_macos_sdk) or
41 (requires_ios_sdk and !enable_ios_sdk))
42 {
43 continue :add_dep_steps;
44 }
45 break;
46 }
47 } else unreachable;
48
49 const dep = b.dependency(dep_name, .{});
50 const dep_step = dep.builder.default_step;
51 dep_step.name = b.fmt("link_test_cases.{s}", .{dep_name});
52 step.dependOn(dep_step);
53 }
54}
test/link/build.zig.zon deleted-61
...@@ -1,61 +0,0 @@
1.{
2 .name = .link_test_cases,
3 .fingerprint = 0x404f657576fec9f2,
4 .version = "0.0.0",
5 .dependencies = .{
6 .bss = .{
7 .path = "bss",
8 },
9 .common_symbols_alignment = .{
10 .path = "common_symbols_alignment",
11 },
12 .interdependent_static_c_libs = .{
13 .path = "interdependent_static_c_libs",
14 },
15 .static_libs_from_object_files = .{
16 .path = "static_libs_from_object_files",
17 },
18 // WASM Cases
19 .wasm_archive = .{
20 .path = "wasm/archive",
21 },
22 .wasm_basic_features = .{
23 .path = "wasm/basic-features",
24 },
25 .wasm_export = .{
26 .path = "wasm/export",
27 },
28 .wasm_export_data = .{
29 .path = "wasm/export-data",
30 },
31 .wasm_extern = .{
32 .path = "wasm/extern",
33 },
34 .wasm_extern_mangle = .{
35 .path = "wasm/extern-mangle",
36 },
37 .wasm_function_table = .{
38 .path = "wasm/function-table",
39 },
40 .wasm_infer_features = .{
41 .path = "wasm/infer-features",
42 },
43 .wasm_producers = .{
44 .path = "wasm/producers",
45 },
46 .wasm_shared_memory = .{
47 .path = "wasm/shared-memory",
48 },
49 .wasm_stack_pointer = .{
50 .path = "wasm/stack_pointer",
51 },
52 .wasm_type = .{
53 .path = "wasm/type",
54 },
55 },
56 .paths = .{
57 "build.zig",
58 "build.zig.zon",
59 "link.zig",
60 },
61}
test/link/common_symbols/a.c deleted-6
...@@ -1,6 +0,0 @@
1int i;
2int j;
3
4int add_to_i_and_j(int x) {
5 return x + i + j;
6}
test/link/common_symbols/b.c deleted-7
...@@ -1,7 +0,0 @@
1long i;
2int j = 2;
3int k;
4
5void incr_i() {
6 i++;
7}
test/link/common_symbols/build.zig deleted-37
...@@ -1,37 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 const lib_a = b.addLibrary(.{
15 .linkage = .static,
16 .name = "a",
17 .root_module = b.createModule(.{
18 .root_source_file = null,
19 .optimize = optimize,
20 .target = b.graph.host,
21 }),
22 });
23 lib_a.root_module.addCSourceFiles(.{
24 .files = &.{ "c.c", "a.c", "b.c" },
25 .flags = &.{"-fcommon"},
26 });
27
28 const test_exe = b.addTest(.{
29 .root_module = b.createModule(.{
30 .root_source_file = b.path("main.zig"),
31 .optimize = optimize,
32 }),
33 });
34 test_exe.root_module.linkLibrary(lib_a);
35
36 test_step.dependOn(&b.addRunArtifact(test_exe).step);
37}
test/link/common_symbols/c.c deleted-5
...@@ -1,5 +0,0 @@
1extern int k;
2
3int common_defined_externally() {
4 return k;
5}
test/link/common_symbols/main.zig deleted-16
...@@ -1,16 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4extern fn common_defined_externally() c_int;
5extern fn incr_i() void;
6extern fn add_to_i_and_j(x: c_int) c_int;
7
8test "undef shadows common symbol: issue #9937" {
9 try expect(common_defined_externally() == 0);
10}
11
12test "import C common symbols" {
13 incr_i();
14 const res = add_to_i_and_j(2);
15 try expect(res == 5);
16}
test/link/common_symbols_alignment/a.c deleted-2
...@@ -1,2 +0,0 @@
1int foo;
2__attribute__((aligned(4096))) int bar;
test/link/common_symbols_alignment/build.zig deleted-38
...@@ -1,38 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 const lib_a = b.addLibrary(.{
15 .linkage = .static,
16 .name = "a",
17 .root_module = b.createModule(.{
18 .root_source_file = null,
19 .optimize = optimize,
20 .target = b.graph.host,
21 }),
22 });
23 lib_a.root_module.addCSourceFiles(.{
24 .files = &.{"a.c"},
25 .flags = &.{"-fcommon"},
26 });
27
28 const test_exe = b.addTest(.{
29 .root_module = b.createModule(.{
30 .root_source_file = b.path("main.zig"),
31 .target = b.graph.host,
32 .optimize = optimize,
33 }),
34 });
35 test_exe.root_module.linkLibrary(lib_a);
36
37 test_step.dependOn(&b.addRunArtifact(test_exe).step);
38}
test/link/common_symbols_alignment/main.zig deleted-9
...@@ -1,9 +0,0 @@
1const std = @import("std");
2
3extern var foo: i32;
4extern var bar: i32;
5
6test {
7 try std.testing.expect(@intFromPtr(&foo) % 4 == 0);
8 try std.testing.expect(@intFromPtr(&bar) % 4096 == 0);
9}
test/link/elf.zig deleted-4300
...@@ -1,4300 +0,0 @@
1pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
2 _ = build_opts;
3 const elf_step = b.step("test-elf", "Run ELF tests");
4
5 // https://github.com/ziglang/zig/issues/25323
6 if (builtin.os.tag == .freebsd) return elf_step;
7
8 // https://github.com/ziglang/zig/issues/25961
9 if (comptime builtin.cpu.arch.endian() == .big) return elf_step;
10
11 const default_target = b.resolveTargetQuery(.{
12 .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
13 .os_tag = .linux,
14 });
15 const x86_64_musl = b.resolveTargetQuery(.{
16 .cpu_arch = .x86_64,
17 .os_tag = .linux,
18 .abi = .musl,
19 });
20 const x86_64_gnu = b.resolveTargetQuery(.{
21 .cpu_arch = .x86_64,
22 .os_tag = .linux,
23 .abi = .gnu,
24 });
25 const aarch64_musl = b.resolveTargetQuery(.{
26 .cpu_arch = .aarch64,
27 .os_tag = .linux,
28 .abi = .musl,
29 });
30 const riscv64_musl = b.resolveTargetQuery(.{
31 .cpu_arch = .riscv64,
32 .os_tag = .linux,
33 .abi = .musl,
34 });
35
36 // Common tests
37 for (&[_]std.Target.Cpu.Arch{
38 .x86_64,
39 .aarch64,
40 }) |cpu_arch| {
41 const musl_target = b.resolveTargetQuery(.{
42 .cpu_arch = cpu_arch,
43 .os_tag = .linux,
44 .abi = .musl,
45 });
46 const gnu_target = b.resolveTargetQuery(.{
47 .cpu_arch = cpu_arch,
48 .os_tag = .linux,
49 .abi = .gnu,
50 });
51
52 // Exercise linker in -r mode
53 elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target }));
54 elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target }));
55 elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target }));
56 elf_step.dependOn(testRelocatableEhFrameComdatHeavy(b, .{ .target = musl_target }));
57 elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target }));
58
59 // Exercise linker in ar mode
60 elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));
61 elf_step.dependOn(testEmitStaticLibZig(b, .{ .target = musl_target }));
62
63 // Exercise linker with LLVM backend
64 // musl tests
65 elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target }));
66 elf_step.dependOn(testComdatElimination(b, .{ .target = musl_target }));
67 elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target }));
68 elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target }));
69 elf_step.dependOn(testCommentString(b, .{ .target = musl_target }));
70 elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));
71 elf_step.dependOn(testEntryPoint(b, .{ .target = musl_target }));
72 elf_step.dependOn(testGcSections(b, .{ .target = musl_target }));
73 elf_step.dependOn(testGcSectionsZig(b, .{ .target = musl_target }));
74 elf_step.dependOn(testImageBase(b, .{ .target = musl_target }));
75 elf_step.dependOn(testInitArrayOrder(b, .{ .target = musl_target }));
76 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = musl_target }));
77 // https://codeberg.org/ziglang/zig/issues/31580
78 // elf_step.dependOn(testLargeBss(b, .{ .target = musl_target }));
79 elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
80 elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
81 elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
82 elf_step.dependOn(testLinksection(b, .{ .target = musl_target }));
83 elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target }));
84 elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target }));
85 // https://github.com/ziglang/zig/issues/17451
86 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = musl_target }));
87 elf_step.dependOn(testTlsStatic(b, .{ .target = musl_target }));
88 elf_step.dependOn(testStrip(b, .{ .target = musl_target }));
89
90 // glibc tests
91 elf_step.dependOn(testAsNeeded(b, .{ .target = gnu_target }));
92 // https://github.com/ziglang/zig/issues/17430
93 // elf_step.dependOn(testCanonicalPlt(b, .{ .target = gnu_target }));
94 elf_step.dependOn(testCommentString(b, .{ .target = gnu_target }));
95 elf_step.dependOn(testCopyrel(b, .{ .target = gnu_target }));
96 // https://github.com/ziglang/zig/issues/17430
97 // elf_step.dependOn(testCopyrelAlias(b, .{ .target = gnu_target }));
98 // https://github.com/ziglang/zig/issues/17430
99 // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = gnu_target }));
100 elf_step.dependOn(testDsoPlt(b, .{ .target = gnu_target }));
101 elf_step.dependOn(testDsoUndef(b, .{ .target = gnu_target }));
102 elf_step.dependOn(testExportDynamic(b, .{ .target = gnu_target }));
103 elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = gnu_target }));
104 // https://github.com/ziglang/zig/issues/17430
105 // elf_step.dependOn(testFuncAddress(b, .{ .target = gnu_target }));
106 elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = gnu_target }));
107 elf_step.dependOn(testIFuncAlias(b, .{ .target = gnu_target }));
108 // https://github.com/ziglang/zig/issues/17430
109 // elf_step.dependOn(testIFuncDlopen(b, .{ .target = gnu_target }));
110 elf_step.dependOn(testIFuncDso(b, .{ .target = gnu_target }));
111 elf_step.dependOn(testIFuncDynamic(b, .{ .target = gnu_target }));
112 elf_step.dependOn(testIFuncExport(b, .{ .target = gnu_target }));
113 elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = gnu_target }));
114 elf_step.dependOn(testIFuncNoPlt(b, .{ .target = gnu_target }));
115 // https://github.com/ziglang/zig/issues/17430 ??
116 // elf_step.dependOn(testIFuncStatic(b, .{ .target = gnu_target }));
117 // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = gnu_target }));
118 elf_step.dependOn(testInitArrayOrder(b, .{ .target = gnu_target }));
119 elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = gnu_target }));
120 elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = gnu_target }));
121 elf_step.dependOn(testLargeBss(b, .{ .target = gnu_target }));
122 elf_step.dependOn(testLinkOrder(b, .{ .target = gnu_target }));
123 elf_step.dependOn(testLdScript(b, .{ .target = gnu_target }));
124 // https://github.com/ziglang/zig/issues/23125
125 // elf_step.dependOn(testLdScriptPathError(b, .{ .target = gnu_target }));
126 elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true }));
127 elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true }));
128 // https://github.com/ziglang/zig/issues/17451
129 // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = gnu_target }));
130 elf_step.dependOn(testPie(b, .{ .target = gnu_target }));
131 elf_step.dependOn(testPltGot(b, .{ .target = gnu_target }));
132 elf_step.dependOn(testPreinitArray(b, .{ .target = gnu_target }));
133 elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = gnu_target }));
134 elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = gnu_target }));
135 elf_step.dependOn(testTlsDso(b, .{ .target = gnu_target }));
136 elf_step.dependOn(testTlsGd(b, .{ .target = gnu_target }));
137 elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = gnu_target }));
138 elf_step.dependOn(testTlsGdToIe(b, .{ .target = gnu_target }));
139 elf_step.dependOn(testTlsIe(b, .{ .target = gnu_target }));
140 elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = gnu_target }));
141 elf_step.dependOn(testTlsLargeTbss(b, .{ .target = gnu_target }));
142 elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = gnu_target }));
143 elf_step.dependOn(testTlsLd(b, .{ .target = gnu_target }));
144 elf_step.dependOn(testTlsLdDso(b, .{ .target = gnu_target }));
145 elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = gnu_target }));
146 // https://github.com/ziglang/zig/issues/17430
147 // elf_step.dependOn(testTlsNoPic(b, .{ .target = gnu_target }));
148 elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = gnu_target }));
149 elf_step.dependOn(testTlsPic(b, .{ .target = gnu_target }));
150 elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = gnu_target }));
151 elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = gnu_target }));
152 elf_step.dependOn(testUnresolvedError(b, .{ .target = gnu_target }));
153 elf_step.dependOn(testWeakExports(b, .{ .target = gnu_target }));
154 elf_step.dependOn(testWeakUndefsDso(b, .{ .target = gnu_target }));
155 elf_step.dependOn(testZNow(b, .{ .target = gnu_target }));
156 elf_step.dependOn(testZStackSize(b, .{ .target = gnu_target }));
157 }
158
159 // x86_64 specific tests
160 elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = x86_64_musl }));
161 elf_step.dependOn(testZText(b, .{ .target = x86_64_gnu }));
162
163 // aarch64 specific tests
164 elf_step.dependOn(testThunks(b, .{ .target = aarch64_musl }));
165
166 // x86_64 self-hosted backend
167 elf_step.dependOn(testCommentString(b, .{ .use_llvm = false, .target = default_target }));
168 elf_step.dependOn(testCommentStringStaticLib(b, .{ .use_llvm = false, .target = default_target }));
169 elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = x86_64_musl }));
170 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = x86_64_musl }));
171 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
172 elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target }));
173 elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target }));
174 elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target }));
175 elf_step.dependOn(testLinksection(b, .{ .use_llvm = false, .target = default_target }));
176 elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu }));
177 elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl }));
178
179 // riscv64 linker backend is currently not complete enough to support more
180 elf_step.dependOn(testLinkingC(b, .{ .target = riscv64_musl }));
181
182 return elf_step;
183}
184
185fn testAbsSymbols(b: *Build, opts: Options) *Step {
186 const test_step = addTestStep(b, "abs-symbols", opts);
187
188 const obj = addObject(b, opts, .{
189 .name = "obj",
190 .asm_source_bytes =
191 \\.globl foo
192 \\foo = 0x800008
193 \\
194 ,
195 });
196
197 const exe = addExecutable(b, opts, .{
198 .name = "test",
199 .c_source_bytes =
200 \\#include <signal.h>
201 \\#include <stdio.h>
202 \\#include <stdlib.h>
203 \\#include <ucontext.h>
204 \\#include <assert.h>
205 \\void handler(int signum, siginfo_t *info, void *ptr) {
206 \\ assert((size_t)info->si_addr == 0x800008);
207 \\ exit(0);
208 \\}
209 \\extern int foo;
210 \\int main() {
211 \\ struct sigaction act;
212 \\ act.sa_flags = SA_SIGINFO | SA_RESETHAND;
213 \\ act.sa_sigaction = handler;
214 \\ sigemptyset(&act.sa_mask);
215 \\ sigaction(SIGSEGV, &act, 0);
216 \\ foo = 5;
217 \\ return 0;
218 \\}
219 ,
220 });
221 exe.root_module.addObject(obj);
222 exe.root_module.link_libc = true;
223
224 const run = addRunArtifact(exe);
225 run.expectExitCode(0);
226 test_step.dependOn(&run.step);
227
228 return test_step;
229}
230
231fn testAsNeeded(b: *Build, opts: Options) *Step {
232 const test_step = addTestStep(b, "as-needed", opts);
233
234 const main_o = addObject(b, opts, .{
235 .name = "main",
236 .c_source_bytes =
237 \\#include <stdio.h>
238 \\int baz();
239 \\int main() {
240 \\ printf("%d\n", baz());
241 \\ return 0;
242 \\}
243 \\
244 ,
245 });
246 main_o.root_module.link_libc = true;
247
248 const libfoo = addSharedLibrary(b, opts, .{ .name = "foo" });
249 addCSourceBytes(libfoo, "int foo() { return 42; }", &.{});
250
251 const libbar = addSharedLibrary(b, opts, .{ .name = "bar" });
252 addCSourceBytes(libbar, "int bar() { return 42; }", &.{});
253
254 const libbaz = addSharedLibrary(b, opts, .{ .name = "baz" });
255 addCSourceBytes(libbaz,
256 \\int foo();
257 \\int baz() { return foo(); }
258 , &.{});
259
260 {
261 const exe = addExecutable(b, opts, .{
262 .name = "test",
263 });
264 exe.root_module.addObject(main_o);
265 exe.root_module.linkSystemLibrary("foo", .{ .needed = true });
266 exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory());
267 exe.root_module.addRPath(libfoo.getEmittedBinDirectory());
268 exe.root_module.linkSystemLibrary("bar", .{ .needed = true });
269 exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory());
270 exe.root_module.addRPath(libbar.getEmittedBinDirectory());
271 exe.root_module.linkSystemLibrary("baz", .{ .needed = true });
272 exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory());
273 exe.root_module.addRPath(libbaz.getEmittedBinDirectory());
274 exe.root_module.link_libc = true;
275
276 const run = addRunArtifact(exe);
277 run.expectStdOutEqual("42\n");
278 test_step.dependOn(&run.step);
279
280 const check = exe.checkObject();
281 check.checkInDynamicSection();
282 check.checkExact("NEEDED libfoo.so");
283 check.checkExact("NEEDED libbar.so");
284 check.checkExact("NEEDED libbaz.so");
285 test_step.dependOn(&check.step);
286 }
287
288 {
289 const exe = addExecutable(b, opts, .{
290 .name = "test",
291 });
292 exe.root_module.addObject(main_o);
293 exe.root_module.linkSystemLibrary("foo", .{ .needed = false });
294 exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory());
295 exe.root_module.addRPath(libfoo.getEmittedBinDirectory());
296 exe.root_module.linkSystemLibrary("bar", .{ .needed = false });
297 exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory());
298 exe.root_module.addRPath(libbar.getEmittedBinDirectory());
299 exe.root_module.linkSystemLibrary("baz", .{ .needed = false });
300 exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory());
301 exe.root_module.addRPath(libbaz.getEmittedBinDirectory());
302 exe.root_module.link_libc = true;
303
304 const run = addRunArtifact(exe);
305 run.expectStdOutEqual("42\n");
306 test_step.dependOn(&run.step);
307
308 const check = exe.checkObject();
309 check.checkInDynamicSection();
310 check.checkNotPresent("NEEDED libbar.so");
311 check.checkInDynamicSection();
312 check.checkExact("NEEDED libfoo.so");
313 check.checkExact("NEEDED libbaz.so");
314 test_step.dependOn(&check.step);
315 }
316
317 return test_step;
318}
319
320fn testCanonicalPlt(b: *Build, opts: Options) *Step {
321 const test_step = addTestStep(b, "canonical-plt", opts);
322
323 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
324 addCSourceBytes(dso,
325 \\void *foo() {
326 \\ return foo;
327 \\}
328 \\void *bar() {
329 \\ return bar;
330 \\}
331 , &.{});
332
333 const b_o = addObject(b, opts, .{
334 .name = "obj",
335 .c_source_bytes =
336 \\void *bar();
337 \\void *baz() {
338 \\ return bar;
339 \\}
340 \\
341 ,
342 .pic = true,
343 });
344
345 const main_o = addObject(b, opts, .{
346 .name = "main",
347 .c_source_bytes =
348 \\#include <assert.h>
349 \\void *foo();
350 \\void *bar();
351 \\void *baz();
352 \\int main() {
353 \\ assert(foo == foo());
354 \\ assert(bar == bar());
355 \\ assert(bar == baz());
356 \\ return 0;
357 \\}
358 \\
359 ,
360 .pic = false,
361 });
362 main_o.root_module.link_libc = true;
363
364 const exe = addExecutable(b, opts, .{
365 .name = "main",
366 });
367 exe.root_module.addObject(main_o);
368 exe.root_module.addObject(b_o);
369 exe.root_module.linkLibrary(dso);
370 exe.root_module.link_libc = true;
371 exe.pie = false;
372
373 const run = addRunArtifact(exe);
374 run.expectExitCode(0);
375 test_step.dependOn(&run.step);
376
377 return test_step;
378}
379
380fn testComdatElimination(b: *Build, opts: Options) *Step {
381 const test_step = addTestStep(b, "comdat-elimination", opts);
382
383 const a_o = addObject(b, opts, .{
384 .name = "a",
385 .cpp_source_bytes =
386 \\#include <stdio.h>
387 \\inline void foo() {
388 \\ printf("calling foo in a\n");
389 \\}
390 \\void hello() {
391 \\ foo();
392 \\}
393 ,
394 });
395 a_o.root_module.link_libcpp = true;
396
397 const main_o = addObject(b, opts, .{
398 .name = "main",
399 .cpp_source_bytes =
400 \\#include <stdio.h>
401 \\inline void foo() {
402 \\ printf("calling foo in main\n");
403 \\}
404 \\void hello();
405 \\int main() {
406 \\ foo();
407 \\ hello();
408 \\ return 0;
409 \\}
410 ,
411 });
412 main_o.root_module.link_libcpp = true;
413
414 {
415 const exe = addExecutable(b, opts, .{ .name = "main1" });
416 exe.root_module.addObject(a_o);
417 exe.root_module.addObject(main_o);
418 exe.root_module.link_libcpp = true;
419
420 const run = addRunArtifact(exe);
421 run.expectStdOutEqual(
422 \\calling foo in a
423 \\calling foo in a
424 \\
425 );
426 test_step.dependOn(&run.step);
427 }
428
429 {
430 const exe = addExecutable(b, opts, .{ .name = "main2" });
431 exe.root_module.addObject(main_o);
432 exe.root_module.addObject(a_o);
433 exe.root_module.link_libcpp = true;
434
435 const run = addRunArtifact(exe);
436 run.expectStdOutEqual(
437 \\calling foo in main
438 \\calling foo in main
439 \\
440 );
441 test_step.dependOn(&run.step);
442 }
443
444 {
445 const c_o = addObject(b, opts, .{ .name = "c" });
446 c_o.root_module.addObject(main_o);
447 c_o.root_module.addObject(a_o);
448
449 const exe = addExecutable(b, opts, .{ .name = "main3" });
450 exe.root_module.addObject(c_o);
451 exe.root_module.link_libcpp = true;
452
453 const run = addRunArtifact(exe);
454 run.expectStdOutEqual(
455 \\calling foo in main
456 \\calling foo in main
457 \\
458 );
459 test_step.dependOn(&run.step);
460 }
461
462 {
463 const d_o = addObject(b, opts, .{ .name = "d" });
464 d_o.root_module.addObject(a_o);
465 d_o.root_module.addObject(main_o);
466
467 const exe = addExecutable(b, opts, .{ .name = "main4" });
468 exe.root_module.addObject(d_o);
469 exe.root_module.link_libcpp = true;
470
471 const run = addRunArtifact(exe);
472 run.expectStdOutEqual(
473 \\calling foo in a
474 \\calling foo in a
475 \\
476 );
477 test_step.dependOn(&run.step);
478 }
479
480 return test_step;
481}
482
483fn testCommentString(b: *Build, opts: Options) *Step {
484 const test_step = addTestStep(b, "comment-string", opts);
485
486 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
487 \\pub fn main() void {}
488 });
489
490 const check = exe.checkObject();
491 check.dumpSection(".comment");
492 check.checkContains("zig");
493 test_step.dependOn(&check.step);
494
495 return test_step;
496}
497
498fn testCommentStringStaticLib(b: *Build, opts: Options) *Step {
499 const test_step = addTestStep(b, "comment-string-static-lib", opts);
500
501 const lib = addStaticLibrary(b, opts, .{ .name = "lib", .zig_source_bytes =
502 \\export fn foo() void {}
503 });
504
505 const check = lib.checkObject();
506 check.dumpSection(".comment");
507 check.checkContains("zig");
508 test_step.dependOn(&check.step);
509
510 return test_step;
511}
512
513fn testCommonSymbols(b: *Build, opts: Options) *Step {
514 const test_step = addTestStep(b, "common-symbols", opts);
515
516 const exe = addExecutable(b, opts, .{
517 .name = "test",
518 });
519 addCSourceBytes(exe,
520 \\int foo;
521 \\int bar;
522 \\int baz = 42;
523 , &.{"-fcommon"});
524 addCSourceBytes(exe,
525 \\#include<stdio.h>
526 \\int foo;
527 \\int bar = 5;
528 \\int baz;
529 \\int main() {
530 \\ printf("%d %d %d\n", foo, bar, baz);
531 \\}
532 , &.{"-fcommon"});
533 exe.root_module.link_libc = true;
534
535 const run = addRunArtifact(exe);
536 run.expectStdOutEqual("0 5 42\n");
537 test_step.dependOn(&run.step);
538
539 return test_step;
540}
541
542fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step {
543 const test_step = addTestStep(b, "common-symbols-in-archive", opts);
544
545 const a_o = addObject(b, opts, .{
546 .name = "a",
547 .c_source_bytes =
548 \\#include <stdio.h>
549 \\int foo;
550 \\int bar;
551 \\extern int baz;
552 \\__attribute__((weak)) int two();
553 \\int main() {
554 \\ printf("%d %d %d %d\n", foo, bar, baz, two ? two() : -1);
555 \\}
556 \\
557 ,
558 .c_source_flags = &.{"-fcommon"},
559 });
560 a_o.root_module.link_libc = true;
561
562 const b_o = addObject(b, opts, .{
563 .name = "b",
564 .c_source_bytes = "int foo = 5;",
565 .c_source_flags = &.{"-fcommon"},
566 });
567
568 {
569 const c_o = addObject(b, opts, .{
570 .name = "c",
571 .c_source_bytes =
572 \\int bar;
573 \\int two() { return 2; }
574 \\
575 ,
576 .c_source_flags = &.{"-fcommon"},
577 });
578
579 const d_o = addObject(b, opts, .{
580 .name = "d",
581 .c_source_bytes = "int baz;",
582 .c_source_flags = &.{"-fcommon"},
583 });
584
585 const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
586 lib.root_module.addObject(b_o);
587 lib.root_module.addObject(c_o);
588 lib.root_module.addObject(d_o);
589
590 const exe = addExecutable(b, opts, .{
591 .name = "test",
592 });
593 exe.root_module.addObject(a_o);
594 exe.root_module.linkLibrary(lib);
595 exe.root_module.link_libc = true;
596
597 const run = addRunArtifact(exe);
598 run.expectStdOutEqual("5 0 0 -1\n");
599 test_step.dependOn(&run.step);
600 }
601
602 {
603 const e_o = addObject(b, opts, .{
604 .name = "e",
605 .c_source_bytes =
606 \\int bar = 0;
607 \\int baz = 7;
608 \\int two() { return 2; }
609 ,
610 .c_source_flags = &.{"-fcommon"},
611 });
612
613 const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
614 lib.root_module.addObject(b_o);
615 lib.root_module.addObject(e_o);
616
617 const exe = addExecutable(b, opts, .{
618 .name = "test",
619 });
620 exe.root_module.addObject(a_o);
621 exe.root_module.linkLibrary(lib);
622 exe.root_module.link_libc = true;
623
624 const run = addRunArtifact(exe);
625 run.expectStdOutEqual("5 0 7 2\n");
626 test_step.dependOn(&run.step);
627 }
628
629 return test_step;
630}
631
632fn testCopyrel(b: *Build, opts: Options) *Step {
633 const test_step = addTestStep(b, "copyrel", opts);
634
635 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
636 addCSourceBytes(dso,
637 \\int foo = 3;
638 \\int bar = 5;
639 , &.{});
640
641 const exe = addExecutable(b, opts, .{
642 .name = "main",
643 .c_source_bytes =
644 \\#include<stdio.h>
645 \\extern int foo, bar;
646 \\int main() {
647 \\ printf("%d %d\n", foo, bar);
648 \\ return 0;
649 \\}
650 ,
651 });
652 exe.root_module.linkLibrary(dso);
653 exe.root_module.link_libc = true;
654
655 const run = addRunArtifact(exe);
656 run.expectStdOutEqual("3 5\n");
657 test_step.dependOn(&run.step);
658
659 return test_step;
660}
661
662fn testCopyrelAlias(b: *Build, opts: Options) *Step {
663 const test_step = addTestStep(b, "copyrel-alias", opts);
664
665 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
666 addCSourceBytes(dso,
667 \\int bruh = 31;
668 \\int foo = 42;
669 \\extern int bar __attribute__((alias("foo")));
670 \\extern int baz __attribute__((alias("foo")));
671 , &.{});
672
673 const exe = addExecutable(b, opts, .{
674 .name = "main",
675 .pic = false,
676 });
677 addCSourceBytes(exe,
678 \\#include<stdio.h>
679 \\extern int foo;
680 \\extern int *get_bar();
681 \\int main() {
682 \\ printf("%d %d %d\n", foo, *get_bar(), &foo == get_bar());
683 \\ return 0;
684 \\}
685 , &.{});
686 addCSourceBytes(exe,
687 \\extern int bar;
688 \\int *get_bar() { return &bar; }
689 , &.{});
690 exe.root_module.linkLibrary(dso);
691 exe.root_module.link_libc = true;
692 exe.pie = false;
693
694 const run = addRunArtifact(exe);
695 run.expectStdOutEqual("42 42 1\n");
696 test_step.dependOn(&run.step);
697
698 return test_step;
699}
700
701fn testCopyrelAlignment(b: *Build, opts: Options) *Step {
702 const test_step = addTestStep(b, "copyrel-alignment", opts);
703
704 const a_so = addSharedLibrary(b, opts, .{ .name = "a" });
705 addCSourceBytes(a_so, "__attribute__((aligned(32))) int foo = 5;", &.{});
706
707 const b_so = addSharedLibrary(b, opts, .{ .name = "b" });
708 addCSourceBytes(b_so, "__attribute__((aligned(8))) int foo = 5;", &.{});
709
710 const c_so = addSharedLibrary(b, opts, .{ .name = "c" });
711 addCSourceBytes(c_so, "__attribute__((aligned(256))) int foo = 5;", &.{});
712
713 const obj = addObject(b, opts, .{
714 .name = "main",
715 .c_source_bytes =
716 \\#include <stdio.h>
717 \\extern int foo;
718 \\int main() { printf("%d\n", foo); }
719 \\
720 ,
721 .pic = false,
722 });
723 obj.root_module.link_libc = true;
724
725 const exp_stdout = "5\n";
726
727 {
728 const exe = addExecutable(b, opts, .{ .name = "main" });
729 exe.root_module.addObject(obj);
730 exe.root_module.linkLibrary(a_so);
731 exe.root_module.link_libc = true;
732 exe.pie = false;
733
734 const run = addRunArtifact(exe);
735 run.expectStdOutEqual(exp_stdout);
736 test_step.dependOn(&run.step);
737
738 const check = exe.checkObject();
739 check.checkInHeaders();
740 check.checkExact("section headers");
741 check.checkExact("name .copyrel");
742 check.checkExact("addralign 20");
743 test_step.dependOn(&check.step);
744 }
745
746 {
747 const exe = addExecutable(b, opts, .{ .name = "main" });
748 exe.root_module.addObject(obj);
749 exe.root_module.linkLibrary(b_so);
750 exe.root_module.link_libc = true;
751 exe.pie = false;
752
753 const run = addRunArtifact(exe);
754 run.expectStdOutEqual(exp_stdout);
755 test_step.dependOn(&run.step);
756
757 const check = exe.checkObject();
758 check.checkInHeaders();
759 check.checkExact("section headers");
760 check.checkExact("name .copyrel");
761 check.checkExact("addralign 8");
762 test_step.dependOn(&check.step);
763 }
764
765 {
766 const exe = addExecutable(b, opts, .{ .name = "main" });
767 exe.root_module.addObject(obj);
768 exe.root_module.linkLibrary(c_so);
769 exe.root_module.link_libc = true;
770 exe.pie = false;
771
772 const run = addRunArtifact(exe);
773 run.expectStdOutEqual(exp_stdout);
774 test_step.dependOn(&run.step);
775
776 const check = exe.checkObject();
777 check.checkInHeaders();
778 check.checkExact("section headers");
779 check.checkExact("name .copyrel");
780 check.checkExact("addralign 100");
781 test_step.dependOn(&check.step);
782 }
783
784 return test_step;
785}
786
787fn testDsoPlt(b: *Build, opts: Options) *Step {
788 const test_step = addTestStep(b, "dso-plt", opts);
789
790 const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
791 addCSourceBytes(dso,
792 \\#include<stdio.h>
793 \\void world() {
794 \\ printf("world\n");
795 \\}
796 \\void real_hello() {
797 \\ printf("Hello ");
798 \\ world();
799 \\}
800 \\void hello() {
801 \\ real_hello();
802 \\}
803 , &.{});
804 dso.root_module.link_libc = true;
805
806 const exe = addExecutable(b, opts, .{ .name = "test" });
807 addCSourceBytes(exe,
808 \\#include<stdio.h>
809 \\void world() {
810 \\ printf("WORLD\n");
811 \\}
812 \\void hello();
813 \\int main() {
814 \\ hello();
815 \\}
816 , &.{});
817 exe.root_module.linkLibrary(dso);
818 exe.root_module.link_libc = true;
819
820 const run = addRunArtifact(exe);
821 run.expectStdOutEqual("Hello WORLD\n");
822 test_step.dependOn(&run.step);
823
824 return test_step;
825}
826
827fn testDsoUndef(b: *Build, opts: Options) *Step {
828 const test_step = addTestStep(b, "dso-undef", opts);
829
830 const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
831 addCSourceBytes(dso,
832 \\extern int foo;
833 \\int bar = 5;
834 \\int baz() { return foo; }
835 , &.{});
836 dso.root_module.link_libc = true;
837
838 const obj = addObject(b, opts, .{
839 .name = "obj",
840 .c_source_bytes = "int foo = 3;",
841 });
842
843 const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
844 lib.root_module.addObject(obj);
845
846 const exe = addExecutable(b, opts, .{ .name = "test" });
847 exe.root_module.linkLibrary(dso);
848 exe.root_module.linkLibrary(lib);
849 addCSourceBytes(exe,
850 \\extern int bar;
851 \\int main() {
852 \\ return bar - 5;
853 \\}
854 , &.{});
855 exe.root_module.link_libc = true;
856
857 const run = addRunArtifact(exe);
858 run.expectExitCode(0);
859 test_step.dependOn(&run.step);
860
861 const check = exe.checkObject();
862 check.checkInDynamicSymtab();
863 check.checkContains("foo");
864 test_step.dependOn(&check.step);
865
866 return test_step;
867}
868
869fn testEmitRelocatable(b: *Build, opts: Options) *Step {
870 const test_step = addTestStep(b, "emit-relocatable", opts);
871
872 const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
873 \\const std = @import("std");
874 \\extern var bar: i32;
875 \\export fn foo() i32 {
876 \\ return bar;
877 \\}
878 \\export fn printFoo() void {
879 \\ std.debug.print("foo={d}\n", .{foo()});
880 \\}
881 });
882 a_o.root_module.link_libc = true;
883
884 const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes =
885 \\#include <stdio.h>
886 \\int bar = 42;
887 \\void printBar() {
888 \\ fprintf(stderr, "bar=%d\n", bar);
889 \\}
890 });
891 b_o.root_module.link_libc = true;
892
893 const c_o = addObject(b, opts, .{ .name = "c" });
894 c_o.root_module.addObject(a_o);
895 c_o.root_module.addObject(b_o);
896
897 const exe = addExecutable(b, opts, .{ .name = "test", .zig_source_bytes =
898 \\const std = @import("std");
899 \\extern fn printFoo() void;
900 \\extern fn printBar() void;
901 \\pub fn main() void {
902 \\ printFoo();
903 \\ printBar();
904 \\}
905 });
906 exe.root_module.addObject(c_o);
907 exe.root_module.link_libc = true;
908
909 const run = addRunArtifact(exe);
910 run.expectStdErrEqual(
911 \\foo=42
912 \\bar=42
913 \\
914 );
915 test_step.dependOn(&run.step);
916
917 return test_step;
918}
919
920fn testEmitStaticLib(b: *Build, opts: Options) *Step {
921 const test_step = addTestStep(b, "emit-static-lib", opts);
922
923 const obj1 = addObject(b, opts, .{
924 .name = "obj1",
925 .c_source_bytes =
926 \\int foo = 0;
927 \\int bar = 2;
928 \\int fooBar() {
929 \\ return foo + bar;
930 \\}
931 ,
932 });
933
934 const obj2 = addObject(b, opts, .{
935 .name = "obj2",
936 .c_source_bytes = "int tentative;",
937 .c_source_flags = &.{"-fcommon"},
938 });
939
940 const obj3 = addObject(b, opts, .{
941 .name = "a_very_long_file_name_so_that_it_ends_up_in_strtab",
942 .zig_source_bytes =
943 \\fn weakFoo() callconv(.c) usize {
944 \\ return 42;
945 \\}
946 \\export var strongBar: usize = 100;
947 \\comptime {
948 \\ @export(&weakFoo, .{ .name = "weakFoo", .linkage = .weak });
949 \\ @export(&strongBar, .{ .name = "strongBarAlias", .linkage = .strong });
950 \\}
951 ,
952 });
953
954 const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
955 lib.root_module.addObject(obj1);
956 lib.root_module.addObject(obj2);
957 lib.root_module.addObject(obj3);
958
959 const check = lib.checkObject();
960 check.checkInArchiveSymtab();
961 check.checkExact("in object obj1.o");
962 check.checkExact("foo");
963 check.checkInArchiveSymtab();
964 check.checkExact("in object obj1.o");
965 check.checkExact("bar");
966 check.checkInArchiveSymtab();
967 check.checkExact("in object obj1.o");
968 check.checkExact("fooBar");
969 check.checkInArchiveSymtab();
970 check.checkExact("in object obj2.o");
971 check.checkExact("tentative");
972 check.checkInArchiveSymtab();
973 check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o");
974 check.checkExact("weakFoo");
975 check.checkInArchiveSymtab();
976 check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o");
977 check.checkExact("strongBar");
978 check.checkInArchiveSymtab();
979 check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o");
980 check.checkExact("strongBarAlias");
981 test_step.dependOn(&check.step);
982
983 return test_step;
984}
985
986fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
987 const test_step = addTestStep(b, "emit-static-lib-zig", opts);
988
989 const obj1 = addObject(b, opts, .{
990 .name = "obj1",
991 .zig_source_bytes =
992 \\export var foo: i32 = 42;
993 \\export var bar: i32 = 2;
994 ,
995 });
996
997 const lib = addStaticLibrary(b, opts, .{
998 .name = "lib",
999 .zig_source_bytes =
1000 \\extern var foo: i32;
1001 \\extern var bar: i32;
1002 \\export fn fooBar() i32 {
1003 \\ return foo + bar;
1004 \\}
1005 ,
1006 });
1007 lib.root_module.addObject(obj1);
1008
1009 const exe = addExecutable(b, opts, .{
1010 .name = "test",
1011 .zig_source_bytes =
1012 \\const std = @import("std");
1013 \\extern fn fooBar() i32;
1014 \\pub fn main() void {
1015 \\ std.debug.print("{d}", .{fooBar()});
1016 \\}
1017 ,
1018 });
1019 exe.root_module.linkLibrary(lib);
1020
1021 const run = addRunArtifact(exe);
1022 run.expectStdErrEqual("44");
1023 test_step.dependOn(&run.step);
1024
1025 return test_step;
1026}
1027
1028fn testEmptyObject(b: *Build, opts: Options) *Step {
1029 const test_step = addTestStep(b, "empty-object", opts);
1030
1031 const exe = addExecutable(b, opts, .{ .name = "test" });
1032 addCSourceBytes(exe, "int main() { return 0; }", &.{});
1033 addCSourceBytes(exe, "", &.{});
1034 exe.root_module.link_libc = true;
1035
1036 const run = addRunArtifact(exe);
1037 run.expectExitCode(0);
1038 test_step.dependOn(&run.step);
1039
1040 return test_step;
1041}
1042
1043fn testEntryPoint(b: *Build, opts: Options) *Step {
1044 const test_step = addTestStep(b, "entry-point", opts);
1045
1046 const a_o = addObject(b, opts, .{
1047 .name = "a",
1048 .asm_source_bytes =
1049 \\.globl foo, bar
1050 \\foo = 0x1000
1051 \\bar = 0x2000
1052 \\
1053 ,
1054 });
1055
1056 const b_o = addObject(b, opts, .{
1057 .name = "b",
1058 .c_source_bytes = "int main() { return 0; }",
1059 });
1060
1061 {
1062 const exe = addExecutable(b, opts, .{ .name = "main" });
1063 exe.root_module.addObject(a_o);
1064 exe.root_module.addObject(b_o);
1065 exe.entry = .{ .symbol_name = "foo" };
1066
1067 const check = exe.checkObject();
1068 check.checkInHeaders();
1069 check.checkExact("header");
1070 check.checkExact("entry 1000");
1071 test_step.dependOn(&check.step);
1072 }
1073
1074 {
1075 // TODO looks like not assigning a unique name to this executable will
1076 // cause an artifact collision taking the cached executable from the above
1077 // step instead of generating a new one.
1078 const exe = addExecutable(b, opts, .{ .name = "other" });
1079 exe.root_module.addObject(a_o);
1080 exe.root_module.addObject(b_o);
1081 exe.entry = .{ .symbol_name = "bar" };
1082
1083 const check = exe.checkObject();
1084 check.checkInHeaders();
1085 check.checkExact("header");
1086 check.checkExact("entry 2000");
1087 test_step.dependOn(&check.step);
1088 }
1089
1090 return test_step;
1091}
1092
1093fn testExportDynamic(b: *Build, opts: Options) *Step {
1094 const test_step = addTestStep(b, "export-dynamic", opts);
1095
1096 const obj = addObject(b, opts, .{
1097 .name = "obj",
1098 .asm_source_bytes =
1099 \\.text
1100 \\ .globl foo
1101 \\ .hidden foo
1102 \\foo:
1103 \\ nop
1104 \\ .globl bar
1105 \\bar:
1106 \\ nop
1107 \\ .globl _start
1108 \\_start:
1109 \\ nop
1110 \\
1111 ,
1112 });
1113
1114 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1115 addCSourceBytes(dso, "int baz = 10;", &.{});
1116
1117 const exe = addExecutable(b, opts, .{ .name = "main" });
1118 addCSourceBytes(exe,
1119 \\extern int baz;
1120 \\int callBaz() {
1121 \\ return baz;
1122 \\}
1123 , &.{});
1124 exe.root_module.addObject(obj);
1125 exe.root_module.linkLibrary(dso);
1126 exe.rdynamic = true;
1127
1128 const check = exe.checkObject();
1129 check.checkInDynamicSymtab();
1130 check.checkContains("bar");
1131 check.checkInDynamicSymtab();
1132 check.checkContains("_start");
1133 test_step.dependOn(&check.step);
1134
1135 return test_step;
1136}
1137
1138fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
1139 const test_step = addTestStep(b, "export-symbols-from-exe", opts);
1140
1141 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1142 addCSourceBytes(dso,
1143 \\void expfn1();
1144 \\void expfn2() {}
1145 \\
1146 \\void foo() {
1147 \\ expfn1();
1148 \\}
1149 , &.{});
1150
1151 const exe = addExecutable(b, opts, .{ .name = "main" });
1152 addCSourceBytes(exe,
1153 \\void expfn1() {}
1154 \\void expfn2() {}
1155 \\void foo();
1156 \\
1157 \\int main() {
1158 \\ expfn1();
1159 \\ expfn2();
1160 \\ foo();
1161 \\}
1162 , &.{});
1163 exe.root_module.linkLibrary(dso);
1164 exe.root_module.link_libc = true;
1165
1166 const check = exe.checkObject();
1167 check.checkInDynamicSymtab();
1168 check.checkContains("expfn2");
1169 check.checkInDynamicSymtab();
1170 check.checkContains("expfn1");
1171 test_step.dependOn(&check.step);
1172
1173 return test_step;
1174}
1175
1176fn testFuncAddress(b: *Build, opts: Options) *Step {
1177 const test_step = addTestStep(b, "func-address", opts);
1178
1179 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1180 addCSourceBytes(dso, "void fn() {}", &.{});
1181
1182 const exe = addExecutable(b, opts, .{ .name = "main" });
1183 addCSourceBytes(exe,
1184 \\#include <assert.h>
1185 \\typedef void Func();
1186 \\void fn();
1187 \\Func *const ptr = fn;
1188 \\int main() {
1189 \\ assert(fn == ptr);
1190 \\}
1191 , &.{});
1192 exe.root_module.linkLibrary(dso);
1193 exe.root_module.pic = false;
1194 exe.pie = false;
1195
1196 const run = addRunArtifact(exe);
1197 run.expectExitCode(0);
1198 test_step.dependOn(&run.step);
1199
1200 return test_step;
1201}
1202
1203fn testGcSections(b: *Build, opts: Options) *Step {
1204 const test_step = addTestStep(b, "gc-sections", opts);
1205
1206 const obj = addObject(b, opts, .{
1207 .name = "obj",
1208 .cpp_source_bytes =
1209 \\#include <stdio.h>
1210 \\int two() { return 2; }
1211 \\int live_var1 = 1;
1212 \\int live_var2 = two();
1213 \\int dead_var1 = 3;
1214 \\int dead_var2 = 4;
1215 \\void live_fn1() {}
1216 \\void live_fn2() { live_fn1(); }
1217 \\void dead_fn1() {}
1218 \\void dead_fn2() { dead_fn1(); }
1219 \\int main() {
1220 \\ printf("%d %d\n", live_var1, live_var2);
1221 \\ live_fn2();
1222 \\}
1223 ,
1224 });
1225 obj.link_function_sections = true;
1226 obj.link_data_sections = true;
1227 obj.root_module.link_libc = true;
1228 obj.root_module.link_libcpp = true;
1229
1230 {
1231 const exe = addExecutable(b, opts, .{ .name = "test" });
1232 exe.root_module.addObject(obj);
1233 exe.link_gc_sections = false;
1234 exe.root_module.link_libc = true;
1235 exe.root_module.link_libcpp = true;
1236
1237 const run = addRunArtifact(exe);
1238 run.expectStdOutEqual("1 2\n");
1239 test_step.dependOn(&run.step);
1240
1241 const check = exe.checkObject();
1242 check.checkInSymtab();
1243 check.checkContains("live_var1");
1244 check.checkInSymtab();
1245 check.checkContains("live_var2");
1246 check.checkInSymtab();
1247 check.checkContains("dead_var1");
1248 check.checkInSymtab();
1249 check.checkContains("dead_var2");
1250 check.checkInSymtab();
1251 check.checkContains("live_fn1");
1252 check.checkInSymtab();
1253 check.checkContains("live_fn2");
1254 check.checkInSymtab();
1255 check.checkContains("dead_fn1");
1256 check.checkInSymtab();
1257 check.checkContains("dead_fn2");
1258 test_step.dependOn(&check.step);
1259 }
1260
1261 {
1262 const exe = addExecutable(b, opts, .{ .name = "test" });
1263 exe.root_module.addObject(obj);
1264 exe.link_gc_sections = true;
1265 exe.root_module.link_libc = true;
1266 exe.root_module.link_libcpp = true;
1267
1268 const run = addRunArtifact(exe);
1269 run.expectStdOutEqual("1 2\n");
1270 test_step.dependOn(&run.step);
1271
1272 const check = exe.checkObject();
1273 check.checkInSymtab();
1274 check.checkContains("live_var1");
1275 check.checkInSymtab();
1276 check.checkContains("live_var2");
1277 check.checkInSymtab();
1278 check.checkNotPresent("dead_var1");
1279 check.checkInSymtab();
1280 check.checkNotPresent("dead_var2");
1281 check.checkInSymtab();
1282 check.checkContains("live_fn1");
1283 check.checkInSymtab();
1284 check.checkContains("live_fn2");
1285 check.checkInSymtab();
1286 check.checkNotPresent("dead_fn1");
1287 check.checkInSymtab();
1288 check.checkNotPresent("dead_fn2");
1289 test_step.dependOn(&check.step);
1290 }
1291
1292 return test_step;
1293}
1294
1295fn testGcSectionsZig(b: *Build, opts: Options) *Step {
1296 const test_step = addTestStep(b, "gc-sections-zig", opts);
1297
1298 const obj = addObject(b, .{
1299 .target = opts.target,
1300 .use_llvm = true,
1301 }, .{
1302 .name = "obj",
1303 .c_source_bytes =
1304 \\int live_var1 = 1;
1305 \\int live_var2 = 2;
1306 \\int dead_var1 = 3;
1307 \\int dead_var2 = 4;
1308 \\void live_fn1() {}
1309 \\void live_fn2() { live_fn1(); }
1310 \\void dead_fn1() {}
1311 \\void dead_fn2() { dead_fn1(); }
1312 ,
1313 });
1314 obj.link_function_sections = true;
1315 obj.link_data_sections = true;
1316
1317 {
1318 const exe = addExecutable(b, opts, .{
1319 .name = "test1",
1320 .zig_source_bytes =
1321 \\const std = @import("std");
1322 \\extern var live_var1: i32;
1323 \\extern var live_var2: i32;
1324 \\extern fn live_fn2() void;
1325 \\pub fn main() void {
1326 \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{});
1327 \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
1328 \\ live_fn2();
1329 \\}
1330 ,
1331 });
1332 exe.root_module.addObject(obj);
1333 exe.link_gc_sections = false;
1334
1335 const run = addRunArtifact(exe);
1336 run.expectStdOutEqual("1 2\n");
1337 test_step.dependOn(&run.step);
1338
1339 const check = exe.checkObject();
1340 check.checkInSymtab();
1341 check.checkContains("live_var1");
1342 check.checkInSymtab();
1343 check.checkContains("live_var2");
1344 check.checkInSymtab();
1345 check.checkContains("dead_var1");
1346 check.checkInSymtab();
1347 check.checkContains("dead_var2");
1348 check.checkInSymtab();
1349 check.checkContains("live_fn1");
1350 check.checkInSymtab();
1351 check.checkContains("live_fn2");
1352 check.checkInSymtab();
1353 check.checkContains("dead_fn1");
1354 check.checkInSymtab();
1355 check.checkContains("dead_fn2");
1356 test_step.dependOn(&check.step);
1357 }
1358
1359 {
1360 const exe = addExecutable(b, opts, .{
1361 .name = "test2",
1362 .zig_source_bytes =
1363 \\const std = @import("std");
1364 \\extern var live_var1: i32;
1365 \\extern var live_var2: i32;
1366 \\extern fn live_fn2() void;
1367 \\pub fn main() void {
1368 \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{});
1369 \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail");
1370 \\ live_fn2();
1371 \\}
1372 ,
1373 });
1374 exe.root_module.addObject(obj);
1375 exe.link_gc_sections = true;
1376
1377 const run = addRunArtifact(exe);
1378 run.expectStdOutEqual("1 2\n");
1379 test_step.dependOn(&run.step);
1380
1381 const check = exe.checkObject();
1382 check.checkInSymtab();
1383 check.checkContains("live_var1");
1384 check.checkInSymtab();
1385 check.checkContains("live_var2");
1386 check.checkInSymtab();
1387 check.checkNotPresent("dead_var1");
1388 check.checkInSymtab();
1389 check.checkNotPresent("dead_var2");
1390 check.checkInSymtab();
1391 check.checkContains("live_fn1");
1392 check.checkInSymtab();
1393 check.checkContains("live_fn2");
1394 check.checkInSymtab();
1395 check.checkNotPresent("dead_fn1");
1396 check.checkInSymtab();
1397 check.checkNotPresent("dead_fn2");
1398 test_step.dependOn(&check.step);
1399 }
1400
1401 return test_step;
1402}
1403
1404fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
1405 const test_step = addTestStep(b, "hidden-weak-undef", opts);
1406
1407 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1408 addCSourceBytes(dso,
1409 \\__attribute__((weak, visibility("hidden"))) void foo();
1410 \\void bar() { foo(); }
1411 , &.{});
1412
1413 const check = dso.checkObject();
1414 check.checkInDynamicSymtab();
1415 check.checkNotPresent("foo");
1416 check.checkInDynamicSymtab();
1417 check.checkContains("bar");
1418 test_step.dependOn(&check.step);
1419
1420 return test_step;
1421}
1422
1423fn testIFuncAlias(b: *Build, opts: Options) *Step {
1424 const test_step = addTestStep(b, "ifunc-alias", opts);
1425
1426 const exe = addExecutable(b, opts, .{ .name = "main" });
1427 addCSourceBytes(exe,
1428 \\#include <assert.h>
1429 \\void foo() {}
1430 \\int bar() __attribute__((ifunc("resolve_bar")));
1431 \\void *resolve_bar() { return foo; }
1432 \\void *bar2 = bar;
1433 \\int main() {
1434 \\ assert(bar == bar2);
1435 \\}
1436 , &.{});
1437 exe.root_module.pic = true;
1438 exe.root_module.link_libc = true;
1439
1440 const run = addRunArtifact(exe);
1441 run.expectExitCode(0);
1442 test_step.dependOn(&run.step);
1443
1444 return test_step;
1445}
1446
1447fn testIFuncDlopen(b: *Build, opts: Options) *Step {
1448 const test_step = addTestStep(b, "ifunc-dlopen", opts);
1449
1450 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1451 addCSourceBytes(dso,
1452 \\__attribute__((ifunc("resolve_foo")))
1453 \\void foo(void);
1454 \\static void real_foo(void) {
1455 \\}
1456 \\typedef void Func();
1457 \\static Func *resolve_foo(void) {
1458 \\ return real_foo;
1459 \\}
1460 , &.{});
1461
1462 const exe = addExecutable(b, opts, .{ .name = "main" });
1463 addCSourceBytes(exe,
1464 \\#include <dlfcn.h>
1465 \\#include <assert.h>
1466 \\#include <stdlib.h>
1467 \\typedef void Func();
1468 \\void foo(void);
1469 \\int main() {
1470 \\ void *handle = dlopen(NULL, RTLD_NOW);
1471 \\ Func *p = dlsym(handle, "foo");
1472 \\
1473 \\ foo();
1474 \\ p();
1475 \\ assert(foo == p);
1476 \\}
1477 , &.{});
1478 exe.root_module.linkLibrary(dso);
1479 exe.root_module.link_libc = true;
1480 exe.root_module.linkSystemLibrary("dl", .{});
1481 exe.root_module.pic = false;
1482 exe.pie = false;
1483
1484 const run = addRunArtifact(exe);
1485 run.expectExitCode(0);
1486 test_step.dependOn(&run.step);
1487
1488 return test_step;
1489}
1490
1491fn testIFuncDso(b: *Build, opts: Options) *Step {
1492 const test_step = addTestStep(b, "ifunc-dso", opts);
1493
1494 const dso = addSharedLibrary(b, opts, .{
1495 .name = "a",
1496 .c_source_bytes =
1497 \\#include<stdio.h>
1498 \\__attribute__((ifunc("resolve_foobar")))
1499 \\void foobar(void);
1500 \\static void real_foobar(void) {
1501 \\ printf("Hello world\n");
1502 \\}
1503 \\typedef void Func();
1504 \\static Func *resolve_foobar(void) {
1505 \\ return real_foobar;
1506 \\}
1507 ,
1508 });
1509 dso.root_module.link_libc = true;
1510
1511 const exe = addExecutable(b, opts, .{
1512 .name = "main",
1513 .c_source_bytes =
1514 \\void foobar(void);
1515 \\int main() {
1516 \\ foobar();
1517 \\}
1518 ,
1519 });
1520 exe.root_module.linkLibrary(dso);
1521
1522 const run = addRunArtifact(exe);
1523 run.expectStdOutEqual("Hello world\n");
1524 test_step.dependOn(&run.step);
1525
1526 return test_step;
1527}
1528
1529fn testIFuncDynamic(b: *Build, opts: Options) *Step {
1530 const test_step = addTestStep(b, "ifunc-dynamic", opts);
1531
1532 const main_c =
1533 \\#include <stdio.h>
1534 \\__attribute__((ifunc("resolve_foobar")))
1535 \\static void foobar(void);
1536 \\static void real_foobar(void) {
1537 \\ printf("Hello world\n");
1538 \\}
1539 \\typedef void Func();
1540 \\static Func *resolve_foobar(void) {
1541 \\ return real_foobar;
1542 \\}
1543 \\int main() {
1544 \\ foobar();
1545 \\}
1546 ;
1547
1548 {
1549 const exe = addExecutable(b, opts, .{ .name = "main" });
1550 addCSourceBytes(exe, main_c, &.{});
1551 exe.root_module.link_libc = true;
1552 exe.link_z_lazy = true;
1553
1554 const run = addRunArtifact(exe);
1555 run.expectStdOutEqual("Hello world\n");
1556 test_step.dependOn(&run.step);
1557 }
1558 {
1559 const exe = addExecutable(b, opts, .{ .name = "other" });
1560 addCSourceBytes(exe, main_c, &.{});
1561 exe.root_module.link_libc = true;
1562
1563 const run = addRunArtifact(exe);
1564 run.expectStdOutEqual("Hello world\n");
1565 test_step.dependOn(&run.step);
1566 }
1567
1568 return test_step;
1569}
1570
1571fn testIFuncExport(b: *Build, opts: Options) *Step {
1572 const test_step = addTestStep(b, "ifunc-export", opts);
1573
1574 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
1575 addCSourceBytes(dso,
1576 \\#include <stdio.h>
1577 \\__attribute__((ifunc("resolve_foobar")))
1578 \\void foobar(void);
1579 \\void real_foobar(void) {
1580 \\ printf("Hello world\n");
1581 \\}
1582 \\typedef void Func();
1583 \\Func *resolve_foobar(void) {
1584 \\ return real_foobar;
1585 \\}
1586 , &.{});
1587 dso.root_module.link_libc = true;
1588
1589 const check = dso.checkObject();
1590 check.checkInDynamicSymtab();
1591 check.checkContains("IFUNC GLOBAL DEFAULT foobar");
1592 test_step.dependOn(&check.step);
1593
1594 return test_step;
1595}
1596
1597fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
1598 const test_step = addTestStep(b, "ifunc-func-ptr", opts);
1599
1600 const exe = addExecutable(b, opts, .{ .name = "main" });
1601 addCSourceBytes(exe,
1602 \\typedef int Fn();
1603 \\int foo() __attribute__((ifunc("resolve_foo")));
1604 \\int real_foo() { return 3; }
1605 \\Fn *resolve_foo(void) {
1606 \\ return real_foo;
1607 \\}
1608 , &.{});
1609 addCSourceBytes(exe,
1610 \\typedef int Fn();
1611 \\int foo();
1612 \\Fn *get_foo() { return foo; }
1613 , &.{});
1614 addCSourceBytes(exe,
1615 \\#include <stdio.h>
1616 \\typedef int Fn();
1617 \\Fn *get_foo();
1618 \\int main() {
1619 \\ Fn *f = get_foo();
1620 \\ printf("%d\n", f());
1621 \\}
1622 , &.{});
1623 exe.root_module.pic = true;
1624 exe.root_module.link_libc = true;
1625
1626 const run = addRunArtifact(exe);
1627 run.expectStdOutEqual("3\n");
1628 test_step.dependOn(&run.step);
1629
1630 return test_step;
1631}
1632
1633fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
1634 const test_step = addTestStep(b, "ifunc-noplt", opts);
1635
1636 const exe = addExecutable(b, opts, .{ .name = "main" });
1637 addCSourceBytes(exe,
1638 \\#include <stdio.h>
1639 \\__attribute__((ifunc("resolve_foo")))
1640 \\void foo(void);
1641 \\void hello(void) {
1642 \\ printf("Hello world\n");
1643 \\}
1644 \\typedef void Fn();
1645 \\Fn *resolve_foo(void) {
1646 \\ return hello;
1647 \\}
1648 \\int main() {
1649 \\ foo();
1650 \\}
1651 , &.{"-fno-plt"});
1652 exe.root_module.pic = true;
1653 exe.root_module.link_libc = true;
1654
1655 const run = addRunArtifact(exe);
1656 run.expectStdOutEqual("Hello world\n");
1657 test_step.dependOn(&run.step);
1658
1659 return test_step;
1660}
1661
1662fn testIFuncStatic(b: *Build, opts: Options) *Step {
1663 const test_step = addTestStep(b, "ifunc-static", opts);
1664
1665 const exe = addExecutable(b, opts, .{ .name = "main" });
1666 addCSourceBytes(exe,
1667 \\#include <stdio.h>
1668 \\void foo() __attribute__((ifunc("resolve_foo")));
1669 \\void hello() {
1670 \\ printf("Hello world\n");
1671 \\}
1672 \\void *resolve_foo() {
1673 \\ return hello;
1674 \\}
1675 \\int main() {
1676 \\ foo();
1677 \\ return 0;
1678 \\}
1679 , &.{});
1680 exe.root_module.link_libc = true;
1681 exe.linkage = .static;
1682
1683 const run = addRunArtifact(exe);
1684 run.expectStdOutEqual("Hello world\n");
1685 test_step.dependOn(&run.step);
1686
1687 return test_step;
1688}
1689
1690fn testIFuncStaticPie(b: *Build, opts: Options) *Step {
1691 const test_step = addTestStep(b, "ifunc-static-pie", opts);
1692
1693 const exe = addExecutable(b, opts, .{ .name = "main" });
1694 addCSourceBytes(exe,
1695 \\#include <stdio.h>
1696 \\void foo() __attribute__((ifunc("resolve_foo")));
1697 \\void hello() {
1698 \\ printf("Hello world\n");
1699 \\}
1700 \\void *resolve_foo() {
1701 \\ return hello;
1702 \\}
1703 \\int main() {
1704 \\ foo();
1705 \\ return 0;
1706 \\}
1707 , &.{});
1708 exe.linkage = .static;
1709 exe.root_module.pic = true;
1710 exe.pie = true;
1711 exe.root_module.link_libc = true;
1712
1713 const run = addRunArtifact(exe);
1714 run.expectStdOutEqual("Hello world\n");
1715 test_step.dependOn(&run.step);
1716
1717 const check = exe.checkObject();
1718 check.checkInHeaders();
1719 check.checkExact("header");
1720 check.checkExact("type DYN");
1721 check.checkInHeaders();
1722 check.checkExact("section headers");
1723 check.checkExact("name .dynamic");
1724 check.checkInHeaders();
1725 check.checkExact("section headers");
1726 check.checkNotPresent("name .interp");
1727 test_step.dependOn(&check.step);
1728
1729 return test_step;
1730}
1731
1732fn testImageBase(b: *Build, opts: Options) *Step {
1733 const test_step = addTestStep(b, "image-base", opts);
1734
1735 {
1736 const exe = addExecutable(b, opts, .{ .name = "main1" });
1737 addCSourceBytes(exe,
1738 \\#include <stdio.h>
1739 \\int main() {
1740 \\ printf("Hello World!\n");
1741 \\ return 0;
1742 \\}
1743 , &.{});
1744 exe.root_module.link_libc = true;
1745 exe.image_base = 0x8000000;
1746
1747 const run = addRunArtifact(exe);
1748 run.expectStdOutEqual("Hello World!\n");
1749 test_step.dependOn(&run.step);
1750
1751 const check = exe.checkObject();
1752 check.checkInHeaders();
1753 check.checkExact("header");
1754 check.checkExtract("entry {addr}");
1755 check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0x8000000 } });
1756 test_step.dependOn(&check.step);
1757 }
1758
1759 {
1760 const exe = addExecutable(b, opts, .{ .name = "main2" });
1761 addCSourceBytes(exe, "void _start() {}", &.{});
1762 exe.image_base = 0xffffffff8000000;
1763
1764 const check = exe.checkObject();
1765 check.checkInHeaders();
1766 check.checkExact("header");
1767 check.checkExtract("entry {addr}");
1768 check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0xffffffff8000000 } });
1769 test_step.dependOn(&check.step);
1770 }
1771
1772 return test_step;
1773}
1774
1775fn testImportingDataDynamic(b: *Build, opts: Options) *Step {
1776 const test_step = addTestStep(b, "importing-data-dynamic", opts);
1777
1778 const dso = addSharedLibrary(b, .{
1779 .target = opts.target,
1780 .optimize = opts.optimize,
1781 .use_llvm = true,
1782 }, .{
1783 .name = "a",
1784 .c_source_bytes =
1785 \\#include <stdio.h>
1786 \\int foo = 42;
1787 \\void printFoo() { fprintf(stderr, "lib foo=%d\n", foo); }
1788 ,
1789 });
1790 dso.root_module.link_libc = true;
1791
1792 const main = addExecutable(b, opts, .{
1793 .name = "main",
1794 .zig_source_bytes =
1795 \\const std = @import("std");
1796 \\extern var foo: i32;
1797 \\extern fn printFoo() void;
1798 \\pub fn main() void {
1799 \\ std.debug.print("exe foo={d}\n", .{foo});
1800 \\ printFoo();
1801 \\ foo += 1;
1802 \\ std.debug.print("exe foo={d}\n", .{foo});
1803 \\ printFoo();
1804 \\}
1805 ,
1806 .strip = true, // TODO temp hack
1807 });
1808 main.pie = true;
1809 main.root_module.linkLibrary(dso);
1810
1811 const run = addRunArtifact(main);
1812 run.expectStdErrEqual(
1813 \\exe foo=42
1814 \\lib foo=42
1815 \\exe foo=43
1816 \\lib foo=43
1817 \\
1818 );
1819 test_step.dependOn(&run.step);
1820
1821 return test_step;
1822}
1823
1824fn testImportingDataStatic(b: *Build, opts: Options) *Step {
1825 const test_step = addTestStep(b, "importing-data-static", opts);
1826
1827 const obj = addObject(b, .{
1828 .target = opts.target,
1829 .optimize = opts.optimize,
1830 .use_llvm = true,
1831 }, .{
1832 .name = "a",
1833 .c_source_bytes = "int foo = 42;",
1834 });
1835
1836 const lib = addStaticLibrary(b, .{
1837 .target = opts.target,
1838 .optimize = opts.optimize,
1839 .use_llvm = true,
1840 }, .{
1841 .name = "a",
1842 });
1843 lib.root_module.addObject(obj);
1844
1845 const main = addExecutable(b, opts, .{
1846 .name = "main",
1847 .zig_source_bytes =
1848 \\extern var foo: i32;
1849 \\pub fn main() void {
1850 \\ @import("std").debug.print("{d}\n", .{foo});
1851 \\}
1852 ,
1853 .strip = true, // TODO temp hack
1854 });
1855 main.root_module.linkLibrary(lib);
1856 main.root_module.link_libc = true;
1857
1858 const run = addRunArtifact(main);
1859 run.expectStdErrEqual("42\n");
1860 test_step.dependOn(&run.step);
1861
1862 return test_step;
1863}
1864
1865fn testInitArrayOrder(b: *Build, opts: Options) *Step {
1866 const test_step = addTestStep(b, "init-array-order", opts);
1867
1868 const a_o = addObject(b, opts, .{
1869 .name = "a",
1870 .c_source_bytes =
1871 \\#include <stdio.h>
1872 \\__attribute__((constructor(10000))) void init4() { printf("1"); }
1873 ,
1874 });
1875 a_o.root_module.link_libc = true;
1876
1877 const b_o = addObject(b, opts, .{
1878 .name = "b",
1879 .c_source_bytes =
1880 \\#include <stdio.h>
1881 \\__attribute__((constructor(1000))) void init3() { printf("2"); }
1882 ,
1883 });
1884 b_o.root_module.link_libc = true;
1885
1886 const c_o = addObject(b, opts, .{
1887 .name = "c",
1888 .c_source_bytes =
1889 \\#include <stdio.h>
1890 \\__attribute__((constructor)) void init1() { printf("3"); }
1891 ,
1892 });
1893 c_o.root_module.link_libc = true;
1894
1895 const d_o = addObject(b, opts, .{
1896 .name = "d",
1897 .c_source_bytes =
1898 \\#include <stdio.h>
1899 \\__attribute__((constructor)) void init2() { printf("4"); }
1900 ,
1901 });
1902 d_o.root_module.link_libc = true;
1903
1904 const e_o = addObject(b, opts, .{
1905 .name = "e",
1906 .c_source_bytes =
1907 \\#include <stdio.h>
1908 \\__attribute__((destructor(10000))) void fini4() { printf("5"); }
1909 ,
1910 });
1911 e_o.root_module.link_libc = true;
1912
1913 const f_o = addObject(b, opts, .{
1914 .name = "f",
1915 .c_source_bytes =
1916 \\#include <stdio.h>
1917 \\__attribute__((destructor(1000))) void fini3() { printf("6"); }
1918 ,
1919 });
1920 f_o.root_module.link_libc = true;
1921
1922 const g_o = addObject(b, opts, .{
1923 .name = "g",
1924 .c_source_bytes =
1925 \\#include <stdio.h>
1926 \\__attribute__((destructor)) void fini1() { printf("7"); }
1927 ,
1928 });
1929 g_o.root_module.link_libc = true;
1930
1931 const h_o = addObject(b, opts, .{ .name = "h", .c_source_bytes =
1932 \\#include <stdio.h>
1933 \\__attribute__((destructor)) void fini2() { printf("8"); }
1934 });
1935 h_o.root_module.link_libc = true;
1936
1937 const exe = addExecutable(b, opts, .{ .name = "main" });
1938 addCSourceBytes(exe, "int main() { return 0; }", &.{});
1939 exe.root_module.addObject(a_o);
1940 exe.root_module.addObject(b_o);
1941 exe.root_module.addObject(c_o);
1942 exe.root_module.addObject(d_o);
1943 exe.root_module.addObject(e_o);
1944 exe.root_module.addObject(f_o);
1945 exe.root_module.addObject(g_o);
1946 exe.root_module.addObject(h_o);
1947
1948 if (opts.target.result.isGnuLibC()) {
1949 // TODO I think we need to clarify our use of `-fPIC -fPIE` flags for different targets
1950 exe.pie = true;
1951 }
1952
1953 const run = addRunArtifact(exe);
1954 run.expectStdOutEqual("21348756");
1955 test_step.dependOn(&run.step);
1956
1957 return test_step;
1958}
1959
1960fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
1961 const test_step = addTestStep(b, "large-alignment-dso", opts);
1962
1963 const dso = addSharedLibrary(b, opts, .{ .name = "dso" });
1964 addCSourceBytes(dso,
1965 \\#include <stdio.h>
1966 \\#include <stdint.h>
1967 \\void hello() __attribute__((aligned(32768), section(".hello")));
1968 \\void world() __attribute__((aligned(32768), section(".world")));
1969 \\void hello() {
1970 \\ printf("Hello");
1971 \\}
1972 \\void world() {
1973 \\ printf(" world");
1974 \\}
1975 \\void greet() {
1976 \\ hello();
1977 \\ world();
1978 \\}
1979 , &.{});
1980 dso.link_function_sections = true;
1981 dso.root_module.link_libc = true;
1982
1983 const check = dso.checkObject();
1984 check.checkInSymtab();
1985 check.checkExtract("{addr1} {size1} {shndx1} FUNC GLOBAL DEFAULT hello");
1986 check.checkInSymtab();
1987 check.checkExtract("{addr2} {size2} {shndx2} FUNC GLOBAL DEFAULT world");
1988 check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1989 check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1990 test_step.dependOn(&check.step);
1991
1992 const exe = addExecutable(b, opts, .{ .name = "test" });
1993 addCSourceBytes(exe,
1994 \\void greet();
1995 \\int main() { greet(); }
1996 , &.{});
1997 exe.root_module.linkLibrary(dso);
1998 exe.root_module.link_libc = true;
1999
2000 const run = addRunArtifact(exe);
2001 run.expectStdOutEqual("Hello world");
2002 test_step.dependOn(&run.step);
2003
2004 return test_step;
2005}
2006
2007fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
2008 const test_step = addTestStep(b, "large-alignment-exe", opts);
2009
2010 const exe = addExecutable(b, opts, .{ .name = "test" });
2011 addCSourceBytes(exe,
2012 \\#include <stdio.h>
2013 \\#include <stdint.h>
2014 \\
2015 \\void hello() __attribute__((aligned(32768), section(".hello")));
2016 \\void world() __attribute__((aligned(32768), section(".world")));
2017 \\
2018 \\void hello() {
2019 \\ printf("Hello");
2020 \\}
2021 \\
2022 \\void world() {
2023 \\ printf(" world");
2024 \\}
2025 \\
2026 \\int main() {
2027 \\ hello();
2028 \\ world();
2029 \\}
2030 , &.{});
2031 exe.link_function_sections = true;
2032 exe.root_module.link_libc = true;
2033
2034 const check = exe.checkObject();
2035 check.checkInSymtab();
2036 check.checkExtract("{addr1} {size1} {shndx1} FUNC LOCAL DEFAULT hello");
2037 check.checkInSymtab();
2038 check.checkExtract("{addr2} {size2} {shndx2} FUNC LOCAL DEFAULT world");
2039 check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
2040 check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
2041 test_step.dependOn(&check.step);
2042
2043 const run = addRunArtifact(exe);
2044 run.expectStdOutEqual("Hello world");
2045 test_step.dependOn(&run.step);
2046
2047 return test_step;
2048}
2049
2050fn testLargeBss(b: *Build, opts: Options) *Step {
2051 const test_step = addTestStep(b, "large-bss", opts);
2052
2053 const exe = addExecutable(b, opts, .{ .name = "main" });
2054 addCSourceBytes(exe,
2055 \\char arr[0x100000000];
2056 \\int main() {
2057 \\ return arr[2000];
2058 \\}
2059 , &.{});
2060 exe.root_module.link_libc = true;
2061 // Disabled to work around the ELF linker crashing.
2062 // Can be reproduced on a x86_64-linux host by commenting out the line below.
2063 exe.root_module.sanitize_c = .off;
2064
2065 const run = addRunArtifact(exe);
2066 run.expectExitCode(0);
2067 test_step.dependOn(&run.step);
2068
2069 return test_step;
2070}
2071
2072fn testLinkOrder(b: *Build, opts: Options) *Step {
2073 const test_step = addTestStep(b, "link-order", opts);
2074
2075 const obj = addObject(b, opts, .{
2076 .name = "obj",
2077 .c_source_bytes = "void foo() {}",
2078 .pic = true,
2079 });
2080
2081 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
2082 dso.root_module.addObject(obj);
2083
2084 const lib = addStaticLibrary(b, opts, .{ .name = "b" });
2085 lib.root_module.addObject(obj);
2086
2087 const main_o = addObject(b, opts, .{
2088 .name = "main",
2089 .c_source_bytes =
2090 \\void foo();
2091 \\int main() {
2092 \\ foo();
2093 \\}
2094 ,
2095 });
2096
2097 // https://github.com/ziglang/zig/issues/17450
2098 // {
2099 // const exe = addExecutable(b, opts, .{ .name = "main1"});
2100 // exe.root_module.addObject(main_o);
2101 // exe.root_module.linkSystemLibrary("a", .{});
2102 // exe.root_module.addLibraryPath(dso.getEmittedBinDirectory());
2103 // exe.root_module.addRPath(dso.getEmittedBinDirectory());
2104 // exe.root_module.linkSystemLibrary("b", .{});
2105 // exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
2106 // exe.root_module.addRPath(lib.getEmittedBinDirectory());
2107 // exe.root_module.link_libc = true;
2108
2109 // const check = exe.checkObject();
2110 // check.checkInDynamicSection();
2111 // check.checkContains("libb.so");
2112 // test_step.dependOn(&check.step);
2113 // }
2114
2115 {
2116 const exe = addExecutable(b, opts, .{ .name = "main2" });
2117 exe.root_module.addObject(main_o);
2118 exe.root_module.linkSystemLibrary("b", .{});
2119 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
2120 exe.root_module.addRPath(lib.getEmittedBinDirectory());
2121 exe.root_module.linkSystemLibrary("a", .{});
2122 exe.root_module.addLibraryPath(dso.getEmittedBinDirectory());
2123 exe.root_module.addRPath(dso.getEmittedBinDirectory());
2124 exe.root_module.link_libc = true;
2125
2126 const check = exe.checkObject();
2127 check.checkInDynamicSection();
2128 check.checkNotPresent("libb.so");
2129 test_step.dependOn(&check.step);
2130 }
2131
2132 return test_step;
2133}
2134
2135fn testLdScript(b: *Build, opts: Options) *Step {
2136 const test_step = addTestStep(b, "ld-script", opts);
2137
2138 const bar = addSharedLibrary(b, opts, .{ .name = "bar" });
2139 addCSourceBytes(bar, "int bar() { return 42; }", &.{});
2140
2141 const baz = addSharedLibrary(b, opts, .{ .name = "baz" });
2142 addCSourceBytes(baz, "int baz() { return 42; }", &.{});
2143
2144 const scripts = WriteFile.create(b);
2145 _ = scripts.add("liba.so", "INPUT(libfoo.so libfoo2.so.1)");
2146 _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))");
2147
2148 // Check finding a versioned .so file that is elsewhere in the library search paths.
2149 const scripts2 = WriteFile.create(b);
2150 _ = scripts2.add("libfoo2.so.1", "GROUP(AS_NEEDED(-lbaz))");
2151
2152 const exe = addExecutable(b, opts, .{ .name = "main" });
2153 addCSourceBytes(exe,
2154 \\int bar();
2155 \\int baz();
2156 \\int main() {
2157 \\ return bar() - baz();
2158 \\}
2159 , &.{});
2160 exe.root_module.linkSystemLibrary("a", .{});
2161 exe.root_module.addLibraryPath(scripts.getDirectory());
2162 exe.root_module.addLibraryPath(scripts2.getDirectory());
2163 exe.root_module.addLibraryPath(bar.getEmittedBinDirectory());
2164 exe.root_module.addLibraryPath(baz.getEmittedBinDirectory());
2165 exe.root_module.addRPath(bar.getEmittedBinDirectory());
2166 exe.root_module.addRPath(baz.getEmittedBinDirectory());
2167 exe.root_module.link_libc = true;
2168 exe.allow_so_scripts = true;
2169
2170 const run = addRunArtifact(exe);
2171 run.expectExitCode(0);
2172 test_step.dependOn(&run.step);
2173
2174 return test_step;
2175}
2176
2177fn testLdScriptPathError(b: *Build, opts: Options) *Step {
2178 const test_step = addTestStep(b, "ld-script-path-error", opts);
2179
2180 const scripts = WriteFile.create(b);
2181 _ = scripts.add("liba.so", "INPUT(libfoo.so)");
2182
2183 const exe = addExecutable(b, opts, .{ .name = "main" });
2184 addCSourceBytes(exe, "int main() { return 0; }", &.{});
2185 exe.root_module.linkSystemLibrary("a", .{});
2186 exe.root_module.addLibraryPath(scripts.getDirectory());
2187 exe.root_module.link_libc = true;
2188 exe.allow_so_scripts = true;
2189
2190 // TODO: A future enhancement could make this error message also mention
2191 // the file that references the missing library.
2192 expectLinkErrors(exe, test_step, .{
2193 .stderr_contains = "error: libfoo.so: file listed in linker script not found",
2194 });
2195
2196 return test_step;
2197}
2198
2199fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step {
2200 const test_step = addTestStep(b, "ld-script-allow-undefined-version", opts);
2201
2202 const so = addSharedLibrary(b, opts, .{
2203 .name = "add",
2204 .zig_source_bytes =
2205 \\export fn add(a: i32, b: i32) i32 {
2206 \\ return a + b;
2207 \\}
2208 ,
2209 });
2210 const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
2211 so.setLinkerScript(ld);
2212 so.linker_allow_undefined_version = true;
2213
2214 const exe = addExecutable(b, opts, .{
2215 .name = "main",
2216 .zig_source_bytes =
2217 \\const std = @import("std");
2218 \\extern fn add(a: i32, b: i32) i32;
2219 \\pub fn main() void {
2220 \\ std.debug.print("{d}\n", .{add(1, 2)});
2221 \\}
2222 ,
2223 });
2224 exe.root_module.linkLibrary(so);
2225 exe.root_module.link_libc = true;
2226 exe.allow_so_scripts = true;
2227
2228 const run = addRunArtifact(exe);
2229 run.expectStdErrEqual("3\n");
2230 test_step.dependOn(&run.step);
2231
2232 return test_step;
2233}
2234
2235fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step {
2236 const test_step = addTestStep(b, "ld-script-disallow-undefined-version", opts);
2237
2238 const so = addSharedLibrary(b, opts, .{
2239 .name = "add",
2240 .zig_source_bytes =
2241 \\export fn add(a: i32, b: i32) i32 {
2242 \\ return a + b;
2243 \\}
2244 ,
2245 });
2246 const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }");
2247 so.setLinkerScript(ld);
2248 so.linker_allow_undefined_version = false;
2249 so.allow_so_scripts = true;
2250
2251 expectLinkErrors(
2252 so,
2253 test_step,
2254 .{
2255 .contains = "error: ld.lld: version script assignment of 'ADD_1.0' to symbol 'sub' failed: symbol not defined",
2256 },
2257 );
2258
2259 return test_step;
2260}
2261
2262fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
2263 const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts);
2264
2265 const obj = addObject(b, .{
2266 .target = b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }),
2267 }, .{
2268 .name = "a",
2269 .c_source_bytes = "int foo;",
2270 .strip = true,
2271 });
2272
2273 const exe = addExecutable(b, opts, .{ .name = "main" });
2274 addCSourceBytes(exe,
2275 \\extern int foo;
2276 \\int main() {
2277 \\ return foo;
2278 \\}
2279 , &.{});
2280 exe.root_module.addObject(obj);
2281 exe.root_module.link_libc = true;
2282
2283 expectLinkErrors(exe, test_step, .{ .exact = &.{
2284 "invalid ELF machine type: AARCH64",
2285 "note: while parsing /?/a.o",
2286 } });
2287
2288 return test_step;
2289}
2290
2291fn testLinkingC(b: *Build, opts: Options) *Step {
2292 const test_step = addTestStep(b, "linking-c", opts);
2293
2294 const exe = addExecutable(b, opts, .{ .name = "test" });
2295 addCSourceBytes(exe,
2296 \\#include <stdio.h>
2297 \\int main() {
2298 \\ printf("Hello World!\n");
2299 \\ return 0;
2300 \\}
2301 , &.{});
2302 exe.root_module.link_libc = true;
2303
2304 const run = addRunArtifact(exe);
2305 run.expectStdOutEqual("Hello World!\n");
2306 test_step.dependOn(&run.step);
2307
2308 const check = exe.checkObject();
2309 check.checkInHeaders();
2310 check.checkExact("header");
2311 check.checkExact("type EXEC");
2312 check.checkInHeaders();
2313 check.checkExact("section headers");
2314 check.checkNotPresent("name .dynamic");
2315 test_step.dependOn(&check.step);
2316
2317 return test_step;
2318}
2319
2320fn testLinkingCpp(b: *Build, opts: Options) *Step {
2321 const test_step = addTestStep(b, "linking-cpp", opts);
2322
2323 const exe = addExecutable(b, opts, .{ .name = "test" });
2324 addCppSourceBytes(exe,
2325 \\#include <iostream>
2326 \\int main() {
2327 \\ std::cout << "Hello World!" << std::endl;
2328 \\ return 0;
2329 \\}
2330 , &.{});
2331 exe.root_module.link_libc = true;
2332 exe.root_module.link_libcpp = true;
2333
2334 const run = addRunArtifact(exe);
2335 run.expectStdOutEqual("Hello World!\n");
2336 test_step.dependOn(&run.step);
2337
2338 const check = exe.checkObject();
2339 check.checkInHeaders();
2340 check.checkExact("header");
2341 check.checkExact("type EXEC");
2342 check.checkInHeaders();
2343 check.checkExact("section headers");
2344 check.checkNotPresent("name .dynamic");
2345 test_step.dependOn(&check.step);
2346
2347 return test_step;
2348}
2349
2350fn testLinkingObj(b: *Build, opts: Options) *Step {
2351 const test_step = addTestStep(b, "linking-obj", opts);
2352
2353 const obj = addObject(b, opts, .{
2354 .name = "aobj",
2355 .zig_source_bytes =
2356 \\extern var mod: usize;
2357 \\export fn callMe() usize {
2358 \\ return me * mod;
2359 \\}
2360 \\var me: usize = 42;
2361 ,
2362 });
2363
2364 const exe = addExecutable(b, opts, .{
2365 .name = "testobj",
2366 .zig_source_bytes =
2367 \\const std = @import("std");
2368 \\extern fn callMe() usize;
2369 \\export var mod: usize = 2;
2370 \\pub fn main() void {
2371 \\ std.debug.print("{d}\n", .{callMe()});
2372 \\}
2373 ,
2374 });
2375 exe.root_module.addObject(obj);
2376
2377 const run = addRunArtifact(exe);
2378 run.expectStdErrEqual("84\n");
2379 test_step.dependOn(&run.step);
2380
2381 return test_step;
2382}
2383
2384fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
2385 const test_step = addTestStep(b, "linking-static-lib", opts);
2386
2387 const obj = addObject(b, opts, .{
2388 .name = "bobj",
2389 .zig_source_bytes = "export var bar: i32 = -42;",
2390 });
2391
2392 const lib = addStaticLibrary(b, opts, .{
2393 .name = "alib",
2394 .zig_source_bytes =
2395 \\export fn foo() i32 {
2396 \\ return 42;
2397 \\}
2398 ,
2399 });
2400 lib.root_module.addObject(obj);
2401
2402 const exe = addExecutable(b, opts, .{
2403 .name = "testlib",
2404 .zig_source_bytes =
2405 \\const std = @import("std");
2406 \\extern fn foo() i32;
2407 \\extern var bar: i32;
2408 \\pub fn main() void {
2409 \\ std.debug.print("{d}\n", .{foo() + bar});
2410 \\}
2411 ,
2412 });
2413 exe.root_module.linkLibrary(lib);
2414
2415 const run = addRunArtifact(exe);
2416 run.expectStdErrEqual("0\n");
2417 test_step.dependOn(&run.step);
2418
2419 return test_step;
2420}
2421
2422fn testLinkingZig(b: *Build, opts: Options) *Step {
2423 const test_step = addTestStep(b, "linking-zig-static", opts);
2424
2425 const exe = addExecutable(b, opts, .{
2426 .name = "test",
2427 .zig_source_bytes =
2428 \\pub fn main() void {
2429 \\ @import("std").debug.print("Hello World!\n", .{});
2430 \\}
2431 ,
2432 });
2433
2434 const run = addRunArtifact(exe);
2435 run.expectStdErrEqual("Hello World!\n");
2436 test_step.dependOn(&run.step);
2437
2438 const check = exe.checkObject();
2439 check.checkInHeaders();
2440 check.checkExact("header");
2441 check.checkExact("type EXEC");
2442 check.checkInHeaders();
2443 check.checkExact("section headers");
2444 check.checkNotPresent("name .dynamic");
2445 test_step.dependOn(&check.step);
2446
2447 return test_step;
2448}
2449
2450fn testLinksection(b: *Build, opts: Options) *Step {
2451 const test_step = addTestStep(b, "linksection", opts);
2452
2453 const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
2454 \\export var test_global: u32 linksection(".TestGlobal") = undefined;
2455 \\export fn testFn() linksection(".TestFn") callconv(.c) void {
2456 \\ TestGenericFn("A").f();
2457 \\}
2458 \\fn TestGenericFn(comptime suffix: []const u8) type {
2459 \\ return struct {
2460 \\ fn f() linksection(".TestGenFn" ++ suffix) void {}
2461 \\ };
2462 \\}
2463 });
2464
2465 const check = obj.checkObject();
2466 check.checkInSymtab();
2467 check.checkContains("SECTION LOCAL DEFAULT .TestGlobal");
2468 check.checkInSymtab();
2469 check.checkContains("SECTION LOCAL DEFAULT .TestFn");
2470 check.checkInSymtab();
2471 check.checkContains("SECTION LOCAL DEFAULT .TestGenFnA");
2472 check.checkInSymtab();
2473 check.checkContains("OBJECT GLOBAL DEFAULT test_global");
2474 check.checkInSymtab();
2475 check.checkContains("FUNC GLOBAL DEFAULT testFn");
2476
2477 if (opts.optimize == .Debug) {
2478 check.checkInSymtab();
2479 check.checkContains("FUNC LOCAL DEFAULT main.TestGenericFn(");
2480 }
2481
2482 test_step.dependOn(&check.step);
2483
2484 return test_step;
2485}
2486
2487// Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh
2488fn testMergeStrings(b: *Build, opts: Options) *Step {
2489 const test_step = addTestStep(b, "merge-strings", opts);
2490
2491 const obj1 = addObject(b, opts, .{ .name = "a.o" });
2492 addCSourceBytes(obj1,
2493 \\#include <uchar.h>
2494 \\#include <wchar.h>
2495 \\char *cstr1 = "foo";
2496 \\wchar_t *wide1 = L"foo";
2497 \\char16_t *utf16_1 = u"foo";
2498 \\char32_t *utf32_1 = U"foo";
2499 , &.{"-O2"});
2500 obj1.root_module.link_libc = true;
2501
2502 const obj2 = addObject(b, opts, .{ .name = "b.o" });
2503 addCSourceBytes(obj2,
2504 \\#include <stdio.h>
2505 \\#include <assert.h>
2506 \\#include <uchar.h>
2507 \\#include <wchar.h>
2508 \\extern char *cstr1;
2509 \\extern wchar_t *wide1;
2510 \\extern char16_t *utf16_1;
2511 \\extern char32_t *utf32_1;
2512 \\char *cstr2 = "foo";
2513 \\wchar_t *wide2 = L"foo";
2514 \\char16_t *utf16_2 = u"foo";
2515 \\char32_t *utf32_2 = U"foo";
2516 \\int main() {
2517 \\ printf("%p %p %p %p %p %p %p %p\n",
2518 \\ cstr1, cstr2, wide1, wide2, utf16_1, utf16_2, utf32_1, utf32_2);
2519 \\ assert((void*)cstr1 == (void*)cstr2);
2520 \\ assert((void*)wide1 == (void*)wide2);
2521 \\ assert((void*)utf16_1 == (void*)utf16_2);
2522 \\ assert((void*)utf32_1 == (void*)utf32_2);
2523 \\ assert((void*)wide1 == (void*)utf32_1);
2524 \\ assert((void*)cstr1 != (void*)wide1);
2525 \\ assert((void*)cstr1 != (void*)utf32_1);
2526 \\ assert((void*)wide1 != (void*)utf16_1);
2527 \\}
2528 , &.{"-O2"});
2529 obj2.root_module.link_libc = true;
2530
2531 const exe = addExecutable(b, opts, .{ .name = "main" });
2532 exe.root_module.addObject(obj1);
2533 exe.root_module.addObject(obj2);
2534 exe.root_module.link_libc = true;
2535
2536 const run = addRunArtifact(exe);
2537 run.expectExitCode(0);
2538 test_step.dependOn(&run.step);
2539
2540 return test_step;
2541}
2542
2543fn testMergeStrings2(b: *Build, opts: Options) *Step {
2544 const test_step = addTestStep(b, "merge-strings2", opts);
2545
2546 const obj1 = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
2547 \\const std = @import("std");
2548 \\export fn foo() void {
2549 \\ var arr: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
2550 \\ const slice = std.mem.sliceTo(&arr, 3);
2551 \\ std.testing.expectEqualSlices(u16, arr[0..2], slice) catch unreachable;
2552 \\}
2553 });
2554
2555 const obj2 = addObject(b, opts, .{ .name = "b", .zig_source_bytes =
2556 \\const std = @import("std");
2557 \\extern fn foo() void;
2558 \\pub fn main() void {
2559 \\ foo();
2560 \\ var arr: [5:0]u16 = [_:0]u16{ 5, 4, 3, 2, 1 };
2561 \\ const slice = std.mem.sliceTo(&arr, 3);
2562 \\ std.testing.expectEqualSlices(u16, arr[0..2], slice) catch unreachable;
2563 \\}
2564 });
2565
2566 {
2567 const exe = addExecutable(b, opts, .{ .name = "main1" });
2568 exe.root_module.addObject(obj1);
2569 exe.root_module.addObject(obj2);
2570
2571 const run = addRunArtifact(exe);
2572 run.expectExitCode(0);
2573 test_step.dependOn(&run.step);
2574
2575 const check = exe.checkObject();
2576 check.dumpSection(".rodata.str");
2577 check.checkContains("\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00\x00");
2578 check.dumpSection(".rodata.str");
2579 check.checkContains("\x05\x00\x04\x00\x03\x00\x02\x00\x01\x00\x00\x00");
2580 test_step.dependOn(&check.step);
2581 }
2582
2583 {
2584 const obj3 = addObject(b, opts, .{ .name = "c" });
2585 obj3.root_module.addObject(obj1);
2586 obj3.root_module.addObject(obj2);
2587
2588 const exe = addExecutable(b, opts, .{ .name = "main2" });
2589 exe.root_module.addObject(obj3);
2590
2591 const run = addRunArtifact(exe);
2592 run.expectExitCode(0);
2593 test_step.dependOn(&run.step);
2594
2595 const check = exe.checkObject();
2596 check.dumpSection(".rodata.str");
2597 check.checkContains("\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00\x00");
2598 check.dumpSection(".rodata.str");
2599 check.checkContains("\x05\x00\x04\x00\x03\x00\x02\x00\x01\x00\x00\x00");
2600 test_step.dependOn(&check.step);
2601 }
2602
2603 return test_step;
2604}
2605
2606fn testNoEhFrameHdr(b: *Build, opts: Options) *Step {
2607 const test_step = addTestStep(b, "no-eh-frame-hdr", opts);
2608
2609 const exe = addExecutable(b, opts, .{ .name = "main" });
2610 addCSourceBytes(exe, "int main() { return 0; }", &.{});
2611 exe.link_eh_frame_hdr = false;
2612 exe.root_module.link_libc = true;
2613
2614 const check = exe.checkObject();
2615 check.checkInHeaders();
2616 check.checkExact("section headers");
2617 check.checkNotPresent("name .eh_frame_hdr");
2618 test_step.dependOn(&check.step);
2619
2620 return test_step;
2621}
2622
2623fn testPie(b: *Build, opts: Options) *Step {
2624 const test_step = addTestStep(b, "hello-pie", opts);
2625
2626 const exe = addExecutable(b, opts, .{ .name = "main" });
2627 addCSourceBytes(exe,
2628 \\#include <stdio.h>
2629 \\int main() {
2630 \\ printf("Hello!\n");
2631 \\ return 0;
2632 \\}
2633 , &.{});
2634 exe.root_module.link_libc = true;
2635 exe.root_module.pic = true;
2636 exe.pie = true;
2637
2638 const run = addRunArtifact(exe);
2639 run.expectStdOutEqual("Hello!\n");
2640 test_step.dependOn(&run.step);
2641
2642 const check = exe.checkObject();
2643 check.checkInHeaders();
2644 check.checkExact("header");
2645 check.checkExact("type DYN");
2646 check.checkInHeaders();
2647 check.checkExact("section headers");
2648 check.checkExact("name .dynamic");
2649 test_step.dependOn(&check.step);
2650
2651 return test_step;
2652}
2653
2654fn testPltGot(b: *Build, opts: Options) *Step {
2655 const test_step = addTestStep(b, "plt-got", opts);
2656
2657 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
2658 addCSourceBytes(dso,
2659 \\#include <stdio.h>
2660 \\void ignore(void *foo) {}
2661 \\void hello() {
2662 \\ printf("Hello world\n");
2663 \\}
2664 , &.{});
2665 dso.root_module.link_libc = true;
2666
2667 const exe = addExecutable(b, opts, .{ .name = "main" });
2668 addCSourceBytes(exe,
2669 \\void ignore(void *);
2670 \\int hello();
2671 \\void foo() { ignore(hello); }
2672 \\int main() { hello(); }
2673 , &.{});
2674 exe.root_module.linkLibrary(dso);
2675 exe.root_module.pic = true;
2676 exe.root_module.link_libc = true;
2677
2678 const run = addRunArtifact(exe);
2679 run.expectStdOutEqual("Hello world\n");
2680 test_step.dependOn(&run.step);
2681
2682 return test_step;
2683}
2684
2685fn testPreinitArray(b: *Build, opts: Options) *Step {
2686 const test_step = addTestStep(b, "preinit-array", opts);
2687
2688 {
2689 const obj = addObject(b, opts, .{
2690 .name = "obj",
2691 .c_source_bytes = "void _start() {}",
2692 });
2693
2694 const exe = addExecutable(b, opts, .{ .name = "main1" });
2695 exe.root_module.addObject(obj);
2696
2697 const check = exe.checkObject();
2698 check.checkInDynamicSection();
2699 check.checkNotPresent("PREINIT_ARRAY");
2700 }
2701
2702 {
2703 const exe = addExecutable(b, opts, .{ .name = "main2" });
2704 addCSourceBytes(exe,
2705 \\void preinit_fn() {}
2706 \\int main() {}
2707 \\__attribute__((section(".preinit_array")))
2708 \\void *preinit[] = { preinit_fn };
2709 , &.{});
2710 exe.root_module.link_libc = true;
2711
2712 const check = exe.checkObject();
2713 check.checkInDynamicSection();
2714 check.checkContains("PREINIT_ARRAY");
2715 }
2716
2717 return test_step;
2718}
2719
2720fn testRelocatableArchive(b: *Build, opts: Options) *Step {
2721 const test_step = addTestStep(b, "relocatable-archive", opts);
2722
2723 const obj1 = addObject(b, opts, .{
2724 .name = "obj1",
2725 .c_source_bytes =
2726 \\void bar();
2727 \\void foo() {
2728 \\ bar();
2729 \\}
2730 ,
2731 });
2732
2733 const obj2 = addObject(b, opts, .{
2734 .name = "obj2",
2735 .c_source_bytes =
2736 \\void bar() {}
2737 ,
2738 });
2739
2740 const obj3 = addObject(b, opts, .{
2741 .name = "obj3",
2742 .c_source_bytes =
2743 \\void baz();
2744 ,
2745 });
2746
2747 const obj4 = addObject(b, opts, .{
2748 .name = "obj4",
2749 .c_source_bytes =
2750 \\void foo();
2751 \\int main() {
2752 \\ foo();
2753 \\}
2754 ,
2755 });
2756
2757 const lib = addStaticLibrary(b, opts, .{ .name = "lib" });
2758 lib.root_module.addObject(obj1);
2759 lib.root_module.addObject(obj2);
2760 lib.root_module.addObject(obj3);
2761
2762 const obj5 = addObject(b, opts, .{
2763 .name = "obj5",
2764 });
2765 obj5.root_module.addObject(obj4);
2766 obj5.root_module.linkLibrary(lib);
2767
2768 const check = obj5.checkObject();
2769 check.checkInSymtab();
2770 check.checkContains("foo");
2771 check.checkInSymtab();
2772 check.checkContains("bar");
2773 check.checkInSymtab();
2774 check.checkNotPresent("baz");
2775 test_step.dependOn(&check.step);
2776
2777 return test_step;
2778}
2779
2780fn testRelocatableEhFrame(b: *Build, opts: Options) *Step {
2781 const test_step = addTestStep(b, "relocatable-eh-frame", opts);
2782
2783 const obj1 = addObject(b, opts, .{
2784 .name = "obj1",
2785 .cpp_source_bytes =
2786 \\#include <stdexcept>
2787 \\int try_me() {
2788 \\ throw std::runtime_error("Oh no!");
2789 \\}
2790 ,
2791 });
2792 obj1.root_module.link_libcpp = true;
2793 const obj2 = addObject(b, opts, .{
2794 .name = "obj2",
2795 .cpp_source_bytes =
2796 \\extern int try_me();
2797 \\int try_again() {
2798 \\ return try_me();
2799 \\}
2800 ,
2801 });
2802 obj2.root_module.link_libcpp = true;
2803 const obj3 = addObject(b, opts, .{ .name = "obj3", .cpp_source_bytes =
2804 \\#include <iostream>
2805 \\#include <stdexcept>
2806 \\extern int try_again();
2807 \\int main() {
2808 \\ try {
2809 \\ try_again();
2810 \\ } catch (const std::exception &e) {
2811 \\ std::cout << "exception=" << e.what();
2812 \\ }
2813 \\ return 0;
2814 \\}
2815 });
2816 obj3.root_module.link_libcpp = true;
2817
2818 {
2819 const obj = addObject(b, opts, .{ .name = "obj" });
2820 obj.root_module.addObject(obj1);
2821 obj.root_module.addObject(obj2);
2822 obj.root_module.link_libcpp = true;
2823
2824 const exe = addExecutable(b, opts, .{ .name = "test1" });
2825 exe.root_module.addObject(obj3);
2826 exe.root_module.addObject(obj);
2827 exe.root_module.link_libcpp = true;
2828
2829 const run = addRunArtifact(exe);
2830 run.expectStdOutEqual("exception=Oh no!");
2831 test_step.dependOn(&run.step);
2832 }
2833 {
2834 // Flipping the order should not influence the end result.
2835 const obj = addObject(b, opts, .{ .name = "obj" });
2836 obj.root_module.addObject(obj2);
2837 obj.root_module.addObject(obj1);
2838 obj.root_module.link_libcpp = true;
2839
2840 const exe = addExecutable(b, opts, .{ .name = "test2" });
2841 exe.root_module.addObject(obj3);
2842 exe.root_module.addObject(obj);
2843 exe.root_module.link_libcpp = true;
2844
2845 const run = addRunArtifact(exe);
2846 run.expectStdOutEqual("exception=Oh no!");
2847 test_step.dependOn(&run.step);
2848 }
2849
2850 return test_step;
2851}
2852
2853fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step {
2854 const test_step = addTestStep(b, "relocatable-eh-frame-comdat-heavy", opts);
2855
2856 const obj1 = addObject(b, opts, .{
2857 .name = "obj1",
2858 .cpp_source_bytes =
2859 \\#include <stdexcept>
2860 \\int try_me() {
2861 \\ throw std::runtime_error("Oh no!");
2862 \\}
2863 ,
2864 });
2865 obj1.root_module.link_libcpp = true;
2866 const obj2 = addObject(b, opts, .{
2867 .name = "obj2",
2868 .cpp_source_bytes =
2869 \\extern int try_me();
2870 \\int try_again() {
2871 \\ return try_me();
2872 \\}
2873 ,
2874 });
2875 obj2.root_module.link_libcpp = true;
2876 const obj3 = addObject(b, opts, .{
2877 .name = "obj3",
2878 .cpp_source_bytes =
2879 \\#include <iostream>
2880 \\#include <stdexcept>
2881 \\extern int try_again();
2882 \\int main() {
2883 \\ try {
2884 \\ try_again();
2885 \\ } catch (const std::exception &e) {
2886 \\ std::cout << "exception=" << e.what();
2887 \\ }
2888 \\ return 0;
2889 \\}
2890 ,
2891 });
2892 obj3.root_module.link_libcpp = true;
2893
2894 const obj = addObject(b, opts, .{ .name = "obj" });
2895 obj.root_module.addObject(obj1);
2896 obj.root_module.addObject(obj2);
2897 obj.root_module.addObject(obj3);
2898 obj.root_module.link_libcpp = true;
2899
2900 const exe = addExecutable(b, opts, .{ .name = "test2" });
2901 exe.root_module.addObject(obj);
2902 exe.root_module.link_libcpp = true;
2903
2904 const run = addRunArtifact(exe);
2905 run.expectStdOutEqual("exception=Oh no!");
2906 test_step.dependOn(&run.step);
2907
2908 return test_step;
2909}
2910
2911// Adapted from https://github.com/rui314/mold/blob/main/test/elf/relocatable-mergeable-sections.sh
2912fn testRelocatableMergeStrings(b: *Build, opts: Options) *Step {
2913 const test_step = addTestStep(b, "relocatable-merge-strings", opts);
2914
2915 const obj1 = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
2916 \\.section .rodata.str1.1,"aMS",@progbits,1
2917 \\val1:
2918 \\.ascii "Hello \0"
2919 \\.section .rodata.str1.1,"aMS",@progbits,1
2920 \\val5:
2921 \\.ascii "World \0"
2922 \\.section .rodata.str1.1,"aMS",@progbits,1
2923 \\val7:
2924 \\.ascii "Hello \0"
2925 });
2926
2927 const obj2 = addObject(b, opts, .{ .name = "b" });
2928 obj2.root_module.addObject(obj1);
2929
2930 const check = obj2.checkObject();
2931 check.dumpSection(".rodata.str1.1");
2932 check.checkExact("Hello \x00World \x00");
2933 test_step.dependOn(&check.step);
2934
2935 return test_step;
2936}
2937
2938fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step {
2939 const test_step = addTestStep(b, "relocatable-no-eh-frame", opts);
2940
2941 const obj1 = addObject(b, opts, .{
2942 .name = "obj1",
2943 .c_source_bytes = "int bar() { return 42; }",
2944 .c_source_flags = &.{
2945 "-fno-unwind-tables",
2946 "-fno-asynchronous-unwind-tables",
2947 },
2948 });
2949
2950 const obj2 = addObject(b, opts, .{
2951 .name = "obj2",
2952 });
2953 obj2.root_module.addObject(obj1);
2954
2955 const check1 = obj1.checkObject();
2956 check1.checkInHeaders();
2957 check1.checkExact("section headers");
2958 check1.checkNotPresent(".eh_frame");
2959 test_step.dependOn(&check1.step);
2960
2961 const check2 = obj2.checkObject();
2962 check2.checkInHeaders();
2963 check2.checkExact("section headers");
2964 check2.checkNotPresent(".eh_frame");
2965 test_step.dependOn(&check2.step);
2966
2967 return test_step;
2968}
2969
2970fn testSharedAbsSymbol(b: *Build, opts: Options) *Step {
2971 const test_step = addTestStep(b, "shared-abs-symbol", opts);
2972
2973 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
2974 addAsmSourceBytes(dso,
2975 \\.globl foo
2976 \\foo = 3;
2977 );
2978
2979 const obj = addObject(b, opts, .{
2980 .name = "obj",
2981 .c_source_bytes =
2982 \\#include <stdio.h>
2983 \\extern char foo;
2984 \\int main() { printf("foo=%p\n", &foo); }
2985 ,
2986 .pic = true,
2987 });
2988 obj.root_module.link_libc = true;
2989
2990 {
2991 const exe = addExecutable(b, opts, .{ .name = "main1" });
2992 exe.root_module.addObject(obj);
2993 exe.root_module.linkLibrary(dso);
2994 exe.pie = true;
2995
2996 const run = addRunArtifact(exe);
2997 run.expectStdOutEqual("foo=0x3\n");
2998 test_step.dependOn(&run.step);
2999
3000 const check = exe.checkObject();
3001 check.checkInHeaders();
3002 check.checkExact("header");
3003 check.checkExact("type DYN");
3004 // TODO fix/improve in CheckObject
3005 // check.checkInSymtab();
3006 // check.checkNotPresent("foo");
3007 test_step.dependOn(&check.step);
3008 }
3009
3010 // https://github.com/ziglang/zig/issues/17430
3011 // {
3012 // const exe = addExecutable(b, opts, .{ .name = "main2"});
3013 // exe.root_module.addObject(obj);
3014 // exe.root_module.linkLibrary(dso);
3015 // exe.pie = false;
3016
3017 // const run = addRunArtifact(exe);
3018 // run.expectStdOutEqual("foo=0x3\n");
3019 // test_step.dependOn(&run.step);
3020
3021 // const check = exe.checkObject();
3022 // check.checkInHeaders();
3023 // check.checkExact("header");
3024 // check.checkExact("type EXEC");
3025 // // TODO fix/improve in CheckObject
3026 // // check.checkInSymtab();
3027 // // check.checkNotPresent("foo");
3028 // test_step.dependOn(&check.step);
3029 // }
3030
3031 return test_step;
3032}
3033
3034fn testStrip(b: *Build, opts: Options) *Step {
3035 const test_step = addTestStep(b, "strip", opts);
3036
3037 const obj = addObject(b, opts, .{
3038 .name = "obj",
3039 .c_source_bytes =
3040 \\#include <stdio.h>
3041 \\int main() {
3042 \\ printf("Hello!\n");
3043 \\ return 0;
3044 \\}
3045 ,
3046 });
3047 obj.root_module.link_libc = true;
3048
3049 {
3050 const exe = addExecutable(b, opts, .{ .name = "main1" });
3051 exe.root_module.addObject(obj);
3052 exe.root_module.strip = false;
3053 exe.root_module.link_libc = true;
3054
3055 const check = exe.checkObject();
3056 check.checkInHeaders();
3057 check.checkExact("section headers");
3058 check.checkExact("name .debug_info");
3059 test_step.dependOn(&check.step);
3060 }
3061
3062 {
3063 const exe = addExecutable(b, opts, .{ .name = "main2" });
3064 exe.root_module.addObject(obj);
3065 exe.root_module.strip = true;
3066 exe.root_module.link_libc = true;
3067
3068 const check = exe.checkObject();
3069 check.checkInHeaders();
3070 check.checkExact("section headers");
3071 check.checkNotPresent("name .debug_info");
3072 test_step.dependOn(&check.step);
3073 }
3074
3075 return test_step;
3076}
3077
3078fn testThunks(b: *Build, opts: Options) *Step {
3079 const test_step = addTestStep(b, "thunks", opts);
3080
3081 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
3082 \\void foo();
3083 \\__attribute__((section(".bar"))) void bar() {
3084 \\ return foo();
3085 \\}
3086 \\__attribute__((section(".foo"))) void foo() {
3087 \\ return bar();
3088 \\}
3089 \\int main() {
3090 \\ foo();
3091 \\ bar();
3092 \\ return 0;
3093 \\}
3094 });
3095
3096 const check = exe.checkObject();
3097 check.checkInSymtab();
3098 check.checkContains("foo$thunk");
3099 check.checkInSymtab();
3100 check.checkContains("bar$thunk");
3101 test_step.dependOn(&check.step);
3102
3103 return test_step;
3104}
3105
3106fn testTlsDfStaticTls(b: *Build, opts: Options) *Step {
3107 const test_step = addTestStep(b, "tls-df-static-tls", opts);
3108
3109 const obj = addObject(b, opts, .{
3110 .name = "obj",
3111 .c_source_bytes =
3112 \\static _Thread_local int foo = 5;
3113 \\void mutate() { ++foo; }
3114 \\int bar() { return foo; }
3115 ,
3116 .c_source_flags = &.{"-ftls-model=initial-exec"},
3117 .pic = true,
3118 });
3119
3120 {
3121 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3122 dso.root_module.addObject(obj);
3123 // dso.link_relax = true;
3124
3125 const check = dso.checkObject();
3126 check.checkInDynamicSection();
3127 check.checkContains("STATIC_TLS");
3128 test_step.dependOn(&check.step);
3129 }
3130
3131 // TODO add -Wl,--no-relax
3132 // {
3133 // const dso = addSharedLibrary(b, opts, .{ .name = "a"});
3134 // dso.root_module.addObject(obj);
3135 // dso.link_relax = false;
3136
3137 // const check = dso.checkObject();
3138 // check.checkInDynamicSection();
3139 // check.checkContains("STATIC_TLS");
3140 // test_step.dependOn(&check.step);
3141 // }
3142
3143 return test_step;
3144}
3145
3146fn testTlsDso(b: *Build, opts: Options) *Step {
3147 const test_step = addTestStep(b, "tls-dso", opts);
3148
3149 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3150 addCSourceBytes(dso,
3151 \\extern _Thread_local int foo;
3152 \\_Thread_local int bar;
3153 \\int get_foo1() { return foo; }
3154 \\int get_bar1() { return bar; }
3155 , &.{});
3156
3157 const exe = addExecutable(b, opts, .{ .name = "main" });
3158 addCSourceBytes(exe,
3159 \\#include <stdio.h>
3160 \\_Thread_local int foo;
3161 \\extern _Thread_local int bar;
3162 \\int get_foo1();
3163 \\int get_bar1();
3164 \\int get_foo2() { return foo; }
3165 \\int get_bar2() { return bar; }
3166 \\int main() {
3167 \\ foo = 5;
3168 \\ bar = 3;
3169 \\ printf("%d %d %d %d %d %d\n",
3170 \\ foo, bar,
3171 \\ get_foo1(), get_bar1(),
3172 \\ get_foo2(), get_bar2());
3173 \\ return 0;
3174 \\}
3175 , &.{});
3176 exe.root_module.linkLibrary(dso);
3177 exe.root_module.link_libc = true;
3178
3179 const run = addRunArtifact(exe);
3180 run.expectStdOutEqual("5 3 5 3 5 3\n");
3181 test_step.dependOn(&run.step);
3182
3183 return test_step;
3184}
3185
3186fn testTlsGd(b: *Build, opts: Options) *Step {
3187 const test_step = addTestStep(b, "tls-gd", opts);
3188
3189 const main_o = addObject(b, opts, .{
3190 .name = "main",
3191 .c_source_bytes =
3192 \\#include <stdio.h>
3193 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
3194 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
3195 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3;
3196 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4;
3197 \\int get_x5();
3198 \\int get_x6();
3199 \\int main() {
3200 \\ x2 = 2;
3201 \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
3202 \\ return 0;
3203 \\}
3204 ,
3205 .pic = true,
3206 });
3207 main_o.root_module.link_libc = true;
3208
3209 const a_o = addObject(b, opts, .{
3210 .name = "a",
3211 .c_source_bytes =
3212 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
3213 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
3214 \\int get_x5() { return x5; }
3215 ,
3216 .pic = true,
3217 });
3218
3219 const b_o = addObject(b, opts, .{
3220 .name = "b",
3221 .c_source_bytes =
3222 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
3223 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
3224 \\int get_x6() { return x6; }
3225 ,
3226 .pic = true,
3227 });
3228
3229 const exp_stdout = "1 2 3 4 5 6\n";
3230
3231 const dso1 = addSharedLibrary(b, opts, .{ .name = "a" });
3232 dso1.root_module.addObject(a_o);
3233
3234 const dso2 = addSharedLibrary(b, opts, .{ .name = "b" });
3235 dso2.root_module.addObject(b_o);
3236 // dso2.link_relax = false; // TODO
3237
3238 {
3239 const exe = addExecutable(b, opts, .{ .name = "main1" });
3240 exe.root_module.addObject(main_o);
3241 exe.root_module.linkLibrary(dso1);
3242 exe.root_module.linkLibrary(dso2);
3243
3244 const run = addRunArtifact(exe);
3245 run.expectStdOutEqual(exp_stdout);
3246 test_step.dependOn(&run.step);
3247 }
3248
3249 {
3250 const exe = addExecutable(b, opts, .{ .name = "main2" });
3251 exe.root_module.addObject(main_o);
3252 // exe.link_relax = false; // TODO
3253 exe.root_module.linkLibrary(dso1);
3254 exe.root_module.linkLibrary(dso2);
3255
3256 const run = addRunArtifact(exe);
3257 run.expectStdOutEqual(exp_stdout);
3258 test_step.dependOn(&run.step);
3259 }
3260
3261 // https://github.com/ziglang/zig/issues/17430 ??
3262 // {
3263 // const exe = addExecutable(b, opts, .{ .name = "main3"});
3264 // exe.root_module.addObject(main_o);
3265 // exe.root_module.linkLibrary(dso1);
3266 // exe.root_module.linkLibrary(dso2);
3267 // exe.linkage = .static;
3268
3269 // const run = addRunArtifact(exe);
3270 // run.expectStdOutEqual(exp_stdout);
3271 // test_step.dependOn(&run.step);
3272 // }
3273
3274 // {
3275 // const exe = addExecutable(b, opts, .{ .name = "main4"});
3276 // exe.root_module.addObject(main_o);
3277 // // exe.link_relax = false; // TODO
3278 // exe.root_module.linkLibrary(dso1);
3279 // exe.root_module.linkLibrary(dso2);
3280 // exe.linkage = .static;
3281
3282 // const run = addRunArtifact(exe);
3283 // run.expectStdOutEqual(exp_stdout);
3284 // test_step.dependOn(&run.step);
3285 // }
3286
3287 return test_step;
3288}
3289
3290fn testTlsGdNoPlt(b: *Build, opts: Options) *Step {
3291 const test_step = addTestStep(b, "tls-gd-no-plt", opts);
3292
3293 const obj = addObject(b, opts, .{
3294 .name = "obj",
3295 .c_source_bytes =
3296 \\#include <stdio.h>
3297 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
3298 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2;
3299 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3;
3300 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4;
3301 \\int get_x5();
3302 \\int get_x6();
3303 \\int main() {
3304 \\ x2 = 2;
3305 \\
3306 \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6());
3307 \\ return 0;
3308 \\}
3309 ,
3310 .c_source_flags = &.{"-fno-plt"},
3311 .pic = true,
3312 });
3313 obj.root_module.link_libc = true;
3314
3315 const a_so = addSharedLibrary(b, opts, .{ .name = "a" });
3316 addCSourceBytes(a_so,
3317 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
3318 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
3319 \\int get_x5() { return x5; }
3320 , &.{"-fno-plt"});
3321
3322 const b_so = addSharedLibrary(b, opts, .{ .name = "b" });
3323 addCSourceBytes(b_so,
3324 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
3325 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
3326 \\int get_x6() { return x6; }
3327 , &.{"-fno-plt"});
3328 // b_so.link_relax = false; // TODO
3329
3330 {
3331 const exe = addExecutable(b, opts, .{ .name = "main1" });
3332 exe.root_module.addObject(obj);
3333 exe.root_module.linkLibrary(a_so);
3334 exe.root_module.linkLibrary(b_so);
3335 exe.root_module.link_libc = true;
3336
3337 const run = addRunArtifact(exe);
3338 run.expectStdOutEqual("1 2 3 4 5 6\n");
3339 test_step.dependOn(&run.step);
3340 }
3341
3342 {
3343 const exe = addExecutable(b, opts, .{ .name = "main2" });
3344 exe.root_module.addObject(obj);
3345 exe.root_module.linkLibrary(a_so);
3346 exe.root_module.linkLibrary(b_so);
3347 exe.root_module.link_libc = true;
3348 // exe.link_relax = false; // TODO
3349
3350 const run = addRunArtifact(exe);
3351 run.expectStdOutEqual("1 2 3 4 5 6\n");
3352 test_step.dependOn(&run.step);
3353 }
3354
3355 return test_step;
3356}
3357
3358fn testTlsGdToIe(b: *Build, opts: Options) *Step {
3359 const test_step = addTestStep(b, "tls-gd-to-ie", opts);
3360
3361 const a_o = addObject(b, opts, .{
3362 .name = "a",
3363 .c_source_bytes =
3364 \\#include <stdio.h>
3365 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
3366 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x2 = 2;
3367 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3;
3368 \\int foo() {
3369 \\ x3 = 3;
3370 \\
3371 \\ printf("%d %d %d\n", x1, x2, x3);
3372 \\ return 0;
3373 \\}
3374 ,
3375 .pic = true,
3376 });
3377 a_o.root_module.link_libc = true;
3378
3379 const b_o = addObject(b, opts, .{
3380 .name = "b",
3381 .c_source_bytes =
3382 \\int foo();
3383 \\int main() { foo(); }
3384 ,
3385 .pic = true,
3386 });
3387
3388 {
3389 const dso = addSharedLibrary(b, opts, .{ .name = "a1" });
3390 dso.root_module.addObject(a_o);
3391
3392 const exe = addExecutable(b, opts, .{ .name = "main1" });
3393 exe.root_module.addObject(b_o);
3394 exe.root_module.linkLibrary(dso);
3395 exe.root_module.link_libc = true;
3396
3397 const run = addRunArtifact(exe);
3398 run.expectStdOutEqual("1 2 3\n");
3399 test_step.dependOn(&run.step);
3400 }
3401
3402 {
3403 const dso = addSharedLibrary(b, opts, .{ .name = "a2" });
3404 dso.root_module.addObject(a_o);
3405 // dso.link_relax = false; // TODO
3406
3407 const exe = addExecutable(b, opts, .{ .name = "main2" });
3408 exe.root_module.addObject(b_o);
3409 exe.root_module.linkLibrary(dso);
3410 exe.root_module.link_libc = true;
3411
3412 const run = addRunArtifact(exe);
3413 run.expectStdOutEqual("1 2 3\n");
3414 test_step.dependOn(&run.step);
3415 }
3416
3417 // {
3418 // const dso = addSharedLibrary(b, opts, .{ .name = "a"});
3419 // dso.root_module.addObject(a_o);
3420 // dso.link_z_nodlopen = true;
3421
3422 // const exe = addExecutable(b, opts, .{ .name = "main"});
3423 // exe.root_module.addObject(b_o);
3424 // exe.root_module.linkLibrary(dso);
3425
3426 // const run = addRunArtifact(exe);
3427 // run.expectStdOutEqual("1 2 3\n");
3428 // test_step.dependOn(&run.step);
3429 // }
3430
3431 // {
3432 // const dso = addSharedLibrary(b, opts, .{ .name = "a"});
3433 // dso.root_module.addObject(a_o);
3434 // dso.link_relax = false;
3435 // dso.link_z_nodlopen = true;
3436
3437 // const exe = addExecutable(b, opts, .{ .name = "main"});
3438 // exe.root_module.addObject(b_o);
3439 // exe.root_module.linkLibrary(dso);
3440
3441 // const run = addRunArtifact(exe);
3442 // run.expectStdOutEqual("1 2 3\n");
3443 // test_step.dependOn(&run.step);
3444 // }
3445
3446 return test_step;
3447}
3448
3449fn testTlsIe(b: *Build, opts: Options) *Step {
3450 const test_step = addTestStep(b, "tls-ie", opts);
3451
3452 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3453 addCSourceBytes(dso,
3454 \\#include <stdio.h>
3455 \\__attribute__((tls_model("initial-exec"))) static _Thread_local int foo;
3456 \\__attribute__((tls_model("initial-exec"))) static _Thread_local int bar;
3457 \\void set() {
3458 \\ foo = 3;
3459 \\ bar = 5;
3460 \\}
3461 \\void print() {
3462 \\ printf("%d %d ", foo, bar);
3463 \\}
3464 , &.{});
3465 dso.root_module.link_libc = true;
3466
3467 const main_o = addObject(b, opts, .{
3468 .name = "main",
3469 .c_source_bytes =
3470 \\#include <stdio.h>
3471 \\_Thread_local int baz;
3472 \\void set();
3473 \\void print();
3474 \\int main() {
3475 \\ baz = 7;
3476 \\ print();
3477 \\ set();
3478 \\ print();
3479 \\ printf("%d\n", baz);
3480 \\}
3481 ,
3482 });
3483 main_o.root_module.link_libc = true;
3484
3485 const exp_stdout = "0 0 3 5 7\n";
3486
3487 {
3488 const exe = addExecutable(b, opts, .{ .name = "main1" });
3489 exe.root_module.addObject(main_o);
3490 exe.root_module.linkLibrary(dso);
3491 exe.root_module.link_libc = true;
3492
3493 const run = addRunArtifact(exe);
3494 run.expectStdOutEqual(exp_stdout);
3495 test_step.dependOn(&run.step);
3496 }
3497
3498 {
3499 const exe = addExecutable(b, opts, .{ .name = "main2" });
3500 exe.root_module.addObject(main_o);
3501 exe.root_module.linkLibrary(dso);
3502 exe.root_module.link_libc = true;
3503 // exe.link_relax = false; // TODO
3504
3505 const run = addRunArtifact(exe);
3506 run.expectStdOutEqual(exp_stdout);
3507 test_step.dependOn(&run.step);
3508 }
3509
3510 return test_step;
3511}
3512
3513fn testTlsLargeAlignment(b: *Build, opts: Options) *Step {
3514 const test_step = addTestStep(b, "tls-large-alignment", opts);
3515
3516 const a_o = addObject(b, opts, .{
3517 .name = "a",
3518 .c_source_bytes =
3519 \\__attribute__((section(".tdata1")))
3520 \\_Thread_local int x = 42;
3521 ,
3522 .c_source_flags = &.{"-std=c11"},
3523 .pic = true,
3524 });
3525
3526 const b_o = addObject(b, opts, .{
3527 .name = "b",
3528 .c_source_bytes =
3529 \\__attribute__((section(".tdata2")))
3530 \\_Alignas(256) _Thread_local int y[] = { 1, 2, 3 };
3531 ,
3532 .c_source_flags = &.{"-std=c11"},
3533 .pic = true,
3534 });
3535
3536 const c_o = addObject(b, opts, .{
3537 .name = "c",
3538 .c_source_bytes =
3539 \\#include <stdio.h>
3540 \\extern _Thread_local int x;
3541 \\extern _Thread_local int y[];
3542 \\int main() {
3543 \\ printf("%d %d %d %d\n", x, y[0], y[1], y[2]);
3544 \\}
3545 ,
3546 .pic = true,
3547 });
3548 c_o.root_module.link_libc = true;
3549
3550 {
3551 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3552 dso.root_module.addObject(a_o);
3553 dso.root_module.addObject(b_o);
3554
3555 const exe = addExecutable(b, opts, .{ .name = "main" });
3556 exe.root_module.addObject(c_o);
3557 exe.root_module.linkLibrary(dso);
3558 exe.root_module.link_libc = true;
3559
3560 const run = addRunArtifact(exe);
3561 run.expectStdOutEqual("42 1 2 3\n");
3562 test_step.dependOn(&run.step);
3563 }
3564
3565 {
3566 const exe = addExecutable(b, opts, .{ .name = "main" });
3567 exe.root_module.addObject(a_o);
3568 exe.root_module.addObject(b_o);
3569 exe.root_module.addObject(c_o);
3570 exe.root_module.link_libc = true;
3571
3572 const run = addRunArtifact(exe);
3573 run.expectStdOutEqual("42 1 2 3\n");
3574 test_step.dependOn(&run.step);
3575 }
3576
3577 return test_step;
3578}
3579
3580fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
3581 const test_step = addTestStep(b, "tls-large-tbss", opts);
3582
3583 const exe = addExecutable(b, opts, .{ .name = "main" });
3584 addAsmSourceBytes(exe,
3585 \\.globl x, y
3586 \\.section .tbss,"awT",@nobits
3587 \\x:
3588 \\.zero 1024
3589 \\.section .tcommon,"awT",@nobits
3590 \\y:
3591 \\.zero 1024
3592 );
3593 addCSourceBytes(exe,
3594 \\#include <stdio.h>
3595 \\extern _Thread_local char x[1024000];
3596 \\extern _Thread_local char y[1024000];
3597 \\int main() {
3598 \\ x[0] = 3;
3599 \\ x[1023] = 5;
3600 \\ printf("%d %d %d %d %d %d\n", x[0], x[1], x[1023], y[0], y[1], y[1023]);
3601 \\}
3602 , &.{});
3603 exe.root_module.link_libc = true;
3604 // Disabled to work around the ELF linker crashing.
3605 // Can be reproduced on a x86_64-linux host by commenting out the line below.
3606 exe.root_module.sanitize_c = .off;
3607
3608 const run = addRunArtifact(exe);
3609 run.expectStdOutEqual("3 0 5 0 0 0\n");
3610 test_step.dependOn(&run.step);
3611
3612 return test_step;
3613}
3614
3615fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step {
3616 const test_step = addTestStep(b, "tls-large-static-image", opts);
3617
3618 const exe = addExecutable(b, opts, .{ .name = "main" });
3619 addCSourceBytes(exe, "_Thread_local int x[] = { 1, 2, 3, [10000] = 5 };", &.{});
3620 addCSourceBytes(exe,
3621 \\#include <stdio.h>
3622 \\extern _Thread_local int x[];
3623 \\int main() {
3624 \\ printf("%d %d %d %d %d\n", x[0], x[1], x[2], x[3], x[10000]);
3625 \\}
3626 , &.{});
3627 exe.root_module.pic = true;
3628 exe.root_module.link_libc = true;
3629
3630 const run = addRunArtifact(exe);
3631 run.expectStdOutEqual("1 2 3 0 5\n");
3632 test_step.dependOn(&run.step);
3633
3634 return test_step;
3635}
3636
3637fn testTlsLd(b: *Build, opts: Options) *Step {
3638 const test_step = addTestStep(b, "tls-ld", opts);
3639
3640 const main_o = addObject(b, opts, .{
3641 .name = "main",
3642 .c_source_bytes =
3643 \\#include <stdio.h>
3644 \\extern _Thread_local int foo;
3645 \\static _Thread_local int bar;
3646 \\int *get_foo_addr() { return &foo; }
3647 \\int *get_bar_addr() { return &bar; }
3648 \\int main() {
3649 \\ bar = 5;
3650 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3651 \\ return 0;
3652 \\}
3653 ,
3654 .c_source_flags = &.{"-ftls-model=local-dynamic"},
3655 .pic = true,
3656 });
3657 main_o.root_module.link_libc = true;
3658
3659 const a_o = addObject(b, opts, .{
3660 .name = "a",
3661 .c_source_bytes = "_Thread_local int foo = 3;",
3662 .c_source_flags = &.{"-ftls-model=local-dynamic"},
3663 .pic = true,
3664 });
3665
3666 const exp_stdout = "3 5 3 5\n";
3667
3668 {
3669 const exe = addExecutable(b, opts, .{ .name = "main1" });
3670 exe.root_module.addObject(main_o);
3671 exe.root_module.addObject(a_o);
3672 exe.root_module.link_libc = true;
3673
3674 const run = addRunArtifact(exe);
3675 run.expectStdOutEqual(exp_stdout);
3676 test_step.dependOn(&run.step);
3677 }
3678
3679 {
3680 const exe = addExecutable(b, opts, .{ .name = "main2" });
3681 exe.root_module.addObject(main_o);
3682 exe.root_module.addObject(a_o);
3683 exe.root_module.link_libc = true;
3684 // exe.link_relax = false; // TODO
3685
3686 const run = addRunArtifact(exe);
3687 run.expectStdOutEqual(exp_stdout);
3688 test_step.dependOn(&run.step);
3689 }
3690
3691 return test_step;
3692}
3693
3694fn testTlsLdDso(b: *Build, opts: Options) *Step {
3695 const test_step = addTestStep(b, "tls-ld-dso", opts);
3696
3697 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3698 addCSourceBytes(dso,
3699 \\static _Thread_local int def, def1;
3700 \\int f0() { return ++def; }
3701 \\int f1() { return ++def1 + def; }
3702 , &.{"-ftls-model=local-dynamic"});
3703
3704 const exe = addExecutable(b, opts, .{ .name = "main" });
3705 addCSourceBytes(exe,
3706 \\#include <stdio.h>
3707 \\extern int f0();
3708 \\extern int f1();
3709 \\int main() {
3710 \\ int x = f0();
3711 \\ int y = f1();
3712 \\ printf("%d %d\n", x, y);
3713 \\ return 0;
3714 \\}
3715 , &.{});
3716 exe.root_module.linkLibrary(dso);
3717 exe.root_module.link_libc = true;
3718
3719 const run = addRunArtifact(exe);
3720 run.expectStdOutEqual("1 2\n");
3721 test_step.dependOn(&run.step);
3722
3723 return test_step;
3724}
3725
3726fn testTlsLdNoPlt(b: *Build, opts: Options) *Step {
3727 const test_step = addTestStep(b, "tls-ld-no-plt", opts);
3728
3729 const a_o = addObject(b, opts, .{
3730 .name = "a",
3731 .c_source_bytes =
3732 \\#include <stdio.h>
3733 \\extern _Thread_local int foo;
3734 \\static _Thread_local int bar;
3735 \\int *get_foo_addr() { return &foo; }
3736 \\int *get_bar_addr() { return &bar; }
3737 \\int main() {
3738 \\ bar = 5;
3739 \\
3740 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3741 \\ return 0;
3742 \\}
3743 ,
3744 .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" },
3745 .pic = true,
3746 });
3747 a_o.root_module.link_libc = true;
3748
3749 const b_o = addObject(b, opts, .{
3750 .name = "b",
3751 .c_source_bytes = "_Thread_local int foo = 3;",
3752 .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" },
3753 .pic = true,
3754 });
3755
3756 {
3757 const exe = addExecutable(b, opts, .{ .name = "main1" });
3758 exe.root_module.addObject(a_o);
3759 exe.root_module.addObject(b_o);
3760 exe.root_module.link_libc = true;
3761
3762 const run = addRunArtifact(exe);
3763 run.expectStdOutEqual("3 5 3 5\n");
3764 test_step.dependOn(&run.step);
3765 }
3766
3767 {
3768 const exe = addExecutable(b, opts, .{ .name = "main2" });
3769 exe.root_module.addObject(a_o);
3770 exe.root_module.addObject(b_o);
3771 exe.root_module.link_libc = true;
3772 // exe.link_relax = false; // TODO
3773
3774 const run = addRunArtifact(exe);
3775 run.expectStdOutEqual("3 5 3 5\n");
3776 test_step.dependOn(&run.step);
3777 }
3778
3779 return test_step;
3780}
3781
3782fn testTlsNoPic(b: *Build, opts: Options) *Step {
3783 const test_step = addTestStep(b, "tls-no-pic", opts);
3784
3785 const exe = addExecutable(b, opts, .{ .name = "main" });
3786 addCSourceBytes(exe,
3787 \\#include <stdio.h>
3788 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
3789 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
3790 \\int *get_foo_addr() { return &foo; }
3791 \\int *get_bar_addr() { return &bar; }
3792 \\int main() {
3793 \\ foo = 3;
3794 \\ bar = 5;
3795 \\
3796 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3797 \\ return 0;
3798 \\}
3799 , .{});
3800 addCSourceBytes(exe,
3801 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
3802 , &.{});
3803 exe.root_module.pic = false;
3804 exe.root_module.link_libc = true;
3805
3806 const run = addRunArtifact(exe);
3807 run.expectStdOutEqual("3 5 3 5\n");
3808 test_step.dependOn(&run.step);
3809
3810 return test_step;
3811}
3812
3813fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step {
3814 const test_step = addTestStep(b, "tls-offset-alignment", opts);
3815
3816 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3817 addCSourceBytes(dso,
3818 \\#include <assert.h>
3819 \\#include <stdlib.h>
3820 \\
3821 \\// .tdata
3822 \\_Thread_local int x = 42;
3823 \\// .tbss
3824 \\__attribute__ ((aligned(64)))
3825 \\_Thread_local int y = 0;
3826 \\
3827 \\void *verify(void *unused) {
3828 \\ assert((unsigned long)(&y) % 64 == 0);
3829 \\ return NULL;
3830 \\}
3831 , &.{});
3832 dso.root_module.link_libc = true;
3833
3834 const exe = addExecutable(b, opts, .{ .name = "main" });
3835 addCSourceBytes(exe,
3836 \\#include <pthread.h>
3837 \\#include <dlfcn.h>
3838 \\#include <assert.h>
3839 \\#include <stdio.h>
3840 \\void *(*verify)(void *);
3841 \\
3842 \\int main() {
3843 \\ void *handle = dlopen("liba.so", RTLD_NOW);
3844 \\ if (!handle) {
3845 \\ fprintf(stderr, "dlopen failed: %s\n", dlerror());
3846 \\ return 1;
3847 \\ }
3848 \\ *(void**)(&verify) = dlsym(handle, "verify");
3849 \\ assert(verify);
3850 \\
3851 \\ pthread_t thread;
3852 \\
3853 \\ verify(NULL);
3854 \\
3855 \\ pthread_create(&thread, NULL, verify, NULL);
3856 \\ pthread_join(thread, NULL);
3857 \\}
3858 , &.{});
3859 exe.root_module.addRPath(dso.getEmittedBinDirectory());
3860 exe.root_module.link_libc = true;
3861 exe.root_module.pic = true;
3862
3863 const run = addRunArtifact(exe);
3864 run.expectExitCode(0);
3865 test_step.dependOn(&run.step);
3866
3867 return test_step;
3868}
3869
3870fn testTlsPic(b: *Build, opts: Options) *Step {
3871 const test_step = addTestStep(b, "tls-pic", opts);
3872
3873 const obj = addObject(b, opts, .{
3874 .name = "obj",
3875 .c_source_bytes =
3876 \\#include <stdio.h>
3877 \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
3878 \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar;
3879 \\int *get_foo_addr() { return &foo; }
3880 \\int *get_bar_addr() { return &bar; }
3881 \\int main() {
3882 \\ bar = 5;
3883 \\
3884 \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar);
3885 \\ return 0;
3886 \\}
3887 ,
3888 .pic = true,
3889 });
3890 obj.root_module.link_libc = true;
3891
3892 const exe = addExecutable(b, opts, .{ .name = "main" });
3893 addCSourceBytes(exe,
3894 \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3;
3895 , &.{});
3896 exe.root_module.addObject(obj);
3897 exe.root_module.link_libc = true;
3898
3899 const run = addRunArtifact(exe);
3900 run.expectStdOutEqual("3 5 3 5\n");
3901 test_step.dependOn(&run.step);
3902
3903 return test_step;
3904}
3905
3906fn testTlsSmallAlignment(b: *Build, opts: Options) *Step {
3907 const test_step = addTestStep(b, "tls-small-alignment", opts);
3908
3909 const a_o = addObject(b, opts, .{
3910 .name = "a",
3911 .asm_source_bytes =
3912 \\.text
3913 \\.byte 0
3914 \\
3915 ,
3916 .pic = true,
3917 });
3918
3919 const b_o = addObject(b, opts, .{
3920 .name = "b",
3921 .c_source_bytes = "_Thread_local char x = 42;",
3922 .c_source_flags = &.{"-std=c11"},
3923 .pic = true,
3924 });
3925
3926 const c_o = addObject(b, opts, .{
3927 .name = "c",
3928 .c_source_bytes =
3929 \\#include <stdio.h>
3930 \\extern _Thread_local char x;
3931 \\int main() {
3932 \\ printf("%d\n", x);
3933 \\}
3934 ,
3935 .pic = true,
3936 });
3937 c_o.root_module.link_libc = true;
3938
3939 {
3940 const exe = addExecutable(b, opts, .{ .name = "main" });
3941 exe.root_module.addObject(a_o);
3942 exe.root_module.addObject(b_o);
3943 exe.root_module.addObject(c_o);
3944 exe.root_module.link_libc = true;
3945
3946 const run = addRunArtifact(exe);
3947 run.expectStdOutEqual("42\n");
3948 test_step.dependOn(&run.step);
3949 }
3950
3951 {
3952 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
3953 dso.root_module.addObject(a_o);
3954 dso.root_module.addObject(b_o);
3955
3956 const exe = addExecutable(b, opts, .{ .name = "main" });
3957 exe.root_module.addObject(c_o);
3958 exe.root_module.linkLibrary(dso);
3959 exe.root_module.link_libc = true;
3960
3961 const run = addRunArtifact(exe);
3962 run.expectStdOutEqual("42\n");
3963 test_step.dependOn(&run.step);
3964 }
3965
3966 return test_step;
3967}
3968
3969fn testTlsStatic(b: *Build, opts: Options) *Step {
3970 const test_step = addTestStep(b, "tls-static", opts);
3971
3972 const exe = addExecutable(b, opts, .{ .name = "test" });
3973 addCSourceBytes(exe,
3974 \\#include <stdio.h>
3975 \\_Thread_local int a = 10;
3976 \\_Thread_local int b;
3977 \\_Thread_local char c = 'a';
3978 \\int main(int argc, char* argv[]) {
3979 \\ printf("%d %d %c\n", a, b, c);
3980 \\ a += 1;
3981 \\ b += 1;
3982 \\ c += 1;
3983 \\ printf("%d %d %c\n", a, b, c);
3984 \\ return 0;
3985 \\}
3986 , &.{});
3987 exe.root_module.link_libc = true;
3988
3989 const run = addRunArtifact(exe);
3990 run.expectStdOutEqual(
3991 \\10 0 a
3992 \\11 1 b
3993 \\
3994 );
3995 test_step.dependOn(&run.step);
3996
3997 return test_step;
3998}
3999
4000fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
4001 const test_step = addTestStep(b, "unknown-file-type-error", opts);
4002
4003 const dylib = addSharedLibrary(b, .{
4004 .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos }),
4005 }, .{
4006 .name = "a",
4007 .zig_source_bytes = "export var foo: i32 = 0;",
4008 });
4009
4010 const exe = addExecutable(b, opts, .{ .name = "main" });
4011 addCSourceBytes(exe,
4012 \\extern int foo;
4013 \\int main() {
4014 \\ return foo;
4015 \\}
4016 , &.{});
4017 exe.root_module.linkLibrary(dylib);
4018 exe.root_module.link_libc = true;
4019
4020 expectLinkErrors(exe, test_step, .{
4021 .contains = "error: failed to parse shared library: BadMagic",
4022 });
4023
4024 return test_step;
4025}
4026
4027fn testUnresolvedError(b: *Build, opts: Options) *Step {
4028 const test_step = addTestStep(b, "unresolved-error", opts);
4029
4030 const obj1 = addObject(b, opts, .{
4031 .name = "a",
4032 .c_source_bytes =
4033 \\#include <stdio.h>
4034 \\int foo();
4035 \\int bar() {
4036 \\ return foo() + 1;
4037 \\}
4038 ,
4039 .c_source_flags = &.{"-ffunction-sections"},
4040 });
4041 obj1.root_module.link_libc = true;
4042
4043 const obj2 = addObject(b, opts, .{
4044 .name = "b",
4045 .c_source_bytes =
4046 \\#include <stdio.h>
4047 \\int foo();
4048 \\int bar();
4049 \\int main() {
4050 \\ return foo() + bar();
4051 \\}
4052 ,
4053 .c_source_flags = &.{"-ffunction-sections"},
4054 });
4055 obj2.root_module.link_libc = true;
4056
4057 const exe = addExecutable(b, opts, .{ .name = "main" });
4058 exe.root_module.addObject(obj1);
4059 exe.root_module.addObject(obj2);
4060 exe.root_module.link_libc = true;
4061
4062 expectLinkErrors(exe, test_step, .{ .exact = &.{
4063 "error: undefined symbol: foo",
4064 "note: referenced by /?/a.o:.text.bar",
4065 "note: referenced by /?/b.o:.text.main",
4066 } });
4067
4068 return test_step;
4069}
4070
4071fn testWeakExports(b: *Build, opts: Options) *Step {
4072 const test_step = addTestStep(b, "weak-exports", opts);
4073
4074 const obj = addObject(b, opts, .{
4075 .name = "obj",
4076 .c_source_bytes =
4077 \\#include <stdio.h>
4078 \\__attribute__((weak)) int foo();
4079 \\int main() {
4080 \\ printf("%d\n", foo ? foo() : 3);
4081 \\}
4082 ,
4083 .pic = true,
4084 });
4085 obj.root_module.link_libc = true;
4086
4087 {
4088 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4089 dso.root_module.addObject(obj);
4090 dso.root_module.link_libc = true;
4091
4092 const check = dso.checkObject();
4093 check.checkInDynamicSymtab();
4094 check.checkContains("UND NOTYPE WEAK DEFAULT foo");
4095 test_step.dependOn(&check.step);
4096 }
4097
4098 {
4099 const exe = addExecutable(b, opts, .{ .name = "main" });
4100 exe.root_module.addObject(obj);
4101 exe.root_module.link_libc = true;
4102
4103 const check = exe.checkObject();
4104 check.checkInDynamicSymtab();
4105 check.checkNotPresent("UND NOTYPE WEAK DEFAULT foo");
4106 test_step.dependOn(&check.step);
4107
4108 const run = addRunArtifact(exe);
4109 run.expectStdOutEqual("3\n");
4110 test_step.dependOn(&run.step);
4111 }
4112
4113 return test_step;
4114}
4115
4116fn testWeakUndefsDso(b: *Build, opts: Options) *Step {
4117 const test_step = addTestStep(b, "weak-undef-dso", opts);
4118
4119 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4120 addCSourceBytes(dso,
4121 \\__attribute__((weak)) int foo();
4122 \\int bar() { return foo ? foo() : -1; }
4123 , &.{});
4124
4125 {
4126 const exe = addExecutable(b, opts, .{ .name = "main" });
4127 addCSourceBytes(exe,
4128 \\#include <stdio.h>
4129 \\int bar();
4130 \\int main() { printf("bar=%d\n", bar()); }
4131 , &.{});
4132 exe.root_module.linkLibrary(dso);
4133 exe.root_module.link_libc = true;
4134
4135 const run = addRunArtifact(exe);
4136 run.expectStdOutEqual("bar=-1\n");
4137 test_step.dependOn(&run.step);
4138 }
4139
4140 {
4141 const exe = addExecutable(b, opts, .{ .name = "main" });
4142 addCSourceBytes(exe,
4143 \\#include <stdio.h>
4144 \\int foo() { return 5; }
4145 \\int bar();
4146 \\int main() { printf("bar=%d\n", bar()); }
4147 , &.{});
4148 exe.root_module.linkLibrary(dso);
4149 exe.root_module.link_libc = true;
4150
4151 const run = addRunArtifact(exe);
4152 run.expectStdOutEqual("bar=5\n");
4153 test_step.dependOn(&run.step);
4154 }
4155
4156 return test_step;
4157}
4158
4159fn testZNow(b: *Build, opts: Options) *Step {
4160 const test_step = addTestStep(b, "z-now", opts);
4161
4162 const obj = addObject(b, opts, .{
4163 .name = "obj",
4164 .c_source_bytes = "int main() { return 0; }",
4165 .pic = true,
4166 });
4167
4168 {
4169 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4170 dso.root_module.addObject(obj);
4171
4172 const check = dso.checkObject();
4173 check.checkInDynamicSection();
4174 check.checkContains("NOW");
4175 test_step.dependOn(&check.step);
4176 }
4177
4178 {
4179 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4180 dso.root_module.addObject(obj);
4181 dso.link_z_lazy = true;
4182
4183 const check = dso.checkObject();
4184 check.checkInDynamicSection();
4185 check.checkNotPresent("NOW");
4186 test_step.dependOn(&check.step);
4187 }
4188
4189 return test_step;
4190}
4191
4192fn testZStackSize(b: *Build, opts: Options) *Step {
4193 const test_step = addTestStep(b, "z-stack-size", opts);
4194
4195 const exe = addExecutable(b, opts, .{ .name = "main" });
4196 addCSourceBytes(exe, "int main() { return 0; }", &.{});
4197 exe.stack_size = 0x800000;
4198 exe.root_module.link_libc = true;
4199
4200 const check = exe.checkObject();
4201 check.checkInHeaders();
4202 check.checkExact("program headers");
4203 check.checkExact("type GNU_STACK");
4204 check.checkExact("memsz 800000");
4205 test_step.dependOn(&check.step);
4206
4207 return test_step;
4208}
4209
4210fn testZText(b: *Build, opts: Options) *Step {
4211 const test_step = addTestStep(b, "z-text", opts);
4212
4213 // Previously, following mold, this test tested text relocs present in a PIE executable.
4214 // However, as we want to cover musl AND glibc, it is now modified to test presence of
4215 // text relocs in a DSO which is then linked with an executable.
4216 // According to Rich and this thread https://www.openwall.com/lists/musl/2020/09/25/4
4217 // musl supports only a very limited number of text relocations and only in DSOs (and
4218 // rightly so!).
4219
4220 const a_o = addObject(b, opts, .{
4221 .name = "a",
4222 .asm_source_bytes =
4223 \\.globl fn1
4224 \\fn1:
4225 \\ sub $8, %rsp
4226 \\ movabs ptr, %rax
4227 \\ call *%rax
4228 \\ add $8, %rsp
4229 \\ ret
4230 \\
4231 ,
4232 });
4233
4234 const b_o = addObject(b, opts, .{
4235 .name = "b",
4236 .c_source_bytes =
4237 \\int fn1();
4238 \\int fn2() {
4239 \\ return 3;
4240 \\}
4241 \\void *ptr = fn2;
4242 \\int fnn() {
4243 \\ return fn1();
4244 \\}
4245 ,
4246 .pic = true,
4247 });
4248
4249 const dso = addSharedLibrary(b, opts, .{ .name = "a" });
4250 dso.root_module.addObject(a_o);
4251 dso.root_module.addObject(b_o);
4252 dso.link_z_notext = true;
4253
4254 const exe = addExecutable(b, opts, .{ .name = "main" });
4255 addCSourceBytes(exe,
4256 \\#include <stdio.h>
4257 \\int fnn();
4258 \\int main() {
4259 \\ printf("%d\n", fnn());
4260 \\}
4261 , &.{});
4262 exe.root_module.linkLibrary(dso);
4263 exe.root_module.link_libc = true;
4264
4265 const run = addRunArtifact(exe);
4266 run.expectStdOutEqual("3\n");
4267 test_step.dependOn(&run.step);
4268
4269 // Check for DT_TEXTREL in a DSO
4270 const check = dso.checkObject();
4271 check.checkInDynamicSection();
4272 // check.checkExact("TEXTREL 0"); // TODO fix in CheckObject parser
4273 check.checkContains("FLAGS TEXTREL");
4274 test_step.dependOn(&check.step);
4275
4276 return test_step;
4277}
4278
4279fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
4280 return link.addTestStep(b, "elf-" ++ prefix, opts);
4281}
4282
4283const addAsmSourceBytes = link.addAsmSourceBytes;
4284const addCSourceBytes = link.addCSourceBytes;
4285const addCppSourceBytes = link.addCppSourceBytes;
4286const addExecutable = link.addExecutable;
4287const addObject = link.addObject;
4288const addRunArtifact = link.addRunArtifact;
4289const addSharedLibrary = link.addSharedLibrary;
4290const addStaticLibrary = link.addStaticLibrary;
4291const expectLinkErrors = link.expectLinkErrors;
4292const link = @import("link.zig");
4293const std = @import("std");
4294const builtin = @import("builtin");
4295
4296const Build = std.Build;
4297const BuildOptions = link.BuildOptions;
4298const Options = link.Options;
4299const Step = Build.Step;
4300const WriteFile = Step.WriteFile;
test/link/interdependent_static_c_libs/a.c deleted-4
...@@ -1,4 +0,0 @@
1#include "a.h"
2int32_t add(int32_t a, int32_t b) {
3 return a + b;
4}
test/link/interdependent_static_c_libs/a.h deleted-2
...@@ -1,2 +0,0 @@
1#include <stdint.h>
2int32_t add(int32_t a, int32_t b);
test/link/interdependent_static_c_libs/b.c deleted-6
...@@ -1,6 +0,0 @@
1#include "a.h"
2#include "b.h"
3
4int32_t sub(int32_t a, int32_t b) {
5 return add(a, -1 * b);
6}
test/link/interdependent_static_c_libs/b.h deleted-2
...@@ -1,2 +0,0 @@
1#include <stdint.h>
2int32_t sub(int32_t a, int32_t b);
test/link/interdependent_static_c_libs/build.zig deleted-50
...@@ -1,50 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 const lib_a = b.addLibrary(.{
15 .linkage = .static,
16 .name = "a",
17 .root_module = b.createModule(.{
18 .root_source_file = null,
19 .optimize = optimize,
20 .target = b.graph.host,
21 }),
22 });
23 lib_a.root_module.addCSourceFile(.{ .file = b.path("a.c"), .flags = &[_][]const u8{} });
24 lib_a.root_module.addIncludePath(b.path("."));
25
26 const lib_b = b.addLibrary(.{
27 .linkage = .static,
28 .name = "b",
29 .root_module = b.createModule(.{
30 .root_source_file = null,
31 .optimize = optimize,
32 .target = b.graph.host,
33 }),
34 });
35 lib_b.root_module.addCSourceFile(.{ .file = b.path("b.c"), .flags = &[_][]const u8{} });
36 lib_b.root_module.addIncludePath(b.path("."));
37
38 const test_exe = b.addTest(.{
39 .root_module = b.createModule(.{
40 .root_source_file = b.path("main.zig"),
41 .target = b.graph.host,
42 .optimize = optimize,
43 }),
44 });
45 test_exe.root_module.linkLibrary(lib_a);
46 test_exe.root_module.linkLibrary(lib_b);
47 test_exe.root_module.addIncludePath(b.path("."));
48
49 test_step.dependOn(&b.addRunArtifact(test_exe).step);
50}
test/link/interdependent_static_c_libs/main.zig deleted-9
...@@ -1,9 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4extern fn sub(a: i32, b: i32) i32;
5
6test "import C sub" {
7 const result = sub(2, 1);
8 try expect(result == 1);
9}
test/link/link.zig deleted-171
...@@ -1,171 +0,0 @@
1pub const BuildOptions = struct {
2 has_macos_sdk: bool,
3 has_ios_sdk: bool,
4 has_symlinks: bool,
5};
6
7pub const Options = struct {
8 target: std.Build.ResolvedTarget,
9 optimize: std.builtin.OptimizeMode = .Debug,
10 use_llvm: bool = true,
11 use_lld: bool = false,
12 strip: ?bool = null,
13};
14
15pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step {
16 const target = opts.target.query.zigTriple(b.allocator) catch @panic("OOM");
17 const optimize = @tagName(opts.optimize);
18 const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";
19 const use_lld = if (opts.use_lld) "lld" else "no-lld";
20 if (opts.strip) |strip| {
21 const s = if (strip) "strip" else "no-strip";
22 const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}-{s}-{s}", .{
23 prefix, target, optimize, use_llvm, use_lld, s,
24 }) catch @panic("OOM");
25 return b.step(name, "");
26 }
27 const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}-{s}", .{
28 prefix, target, optimize, use_llvm, use_lld,
29 }) catch @panic("OOM");
30 return b.step(name, "");
31}
32
33const OverlayOptions = struct {
34 name: []const u8,
35 asm_source_bytes: ?[]const u8 = null,
36 c_source_bytes: ?[]const u8 = null,
37 c_source_flags: []const []const u8 = &.{},
38 cpp_source_bytes: ?[]const u8 = null,
39 cpp_source_flags: []const []const u8 = &.{},
40 objc_source_bytes: ?[]const u8 = null,
41 objc_source_flags: []const []const u8 = &.{},
42 objcpp_source_bytes: ?[]const u8 = null,
43 objcpp_source_flags: []const []const u8 = &.{},
44 zig_source_bytes: ?[]const u8 = null,
45 pic: ?bool = null,
46 strip: ?bool = null,
47};
48
49pub fn addExecutable(b: *std.Build, base: Options, overlay: OverlayOptions) *Compile {
50 return b.addExecutable(.{
51 .name = overlay.name,
52 .root_module = createModule(b, base, overlay),
53 .use_llvm = base.use_llvm,
54 .use_lld = base.use_lld,
55 });
56}
57
58pub fn addObject(b: *Build, base: Options, overlay: OverlayOptions) *Compile {
59 return b.addObject(.{
60 .name = overlay.name,
61 .root_module = createModule(b, base, overlay),
62 .use_llvm = base.use_llvm,
63 .use_lld = base.use_lld,
64 });
65}
66
67pub fn addStaticLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile {
68 return b.addLibrary(.{
69 .linkage = .static,
70 .name = overlay.name,
71 .root_module = createModule(b, base, overlay),
72 .use_llvm = base.use_llvm,
73 .use_lld = base.use_lld,
74 });
75}
76
77pub fn addSharedLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile {
78 return b.addLibrary(.{
79 .linkage = .dynamic,
80 .name = overlay.name,
81 .root_module = createModule(b, base, overlay),
82 .use_llvm = base.use_llvm,
83 .use_lld = base.use_lld,
84 });
85}
86
87fn createModule(b: *Build, base: Options, overlay: OverlayOptions) *Build.Module {
88 const write_files = b.addWriteFiles();
89
90 const mod = b.createModule(.{
91 .target = base.target,
92 .optimize = base.optimize,
93 .root_source_file = rsf: {
94 const bytes = overlay.zig_source_bytes orelse break :rsf null;
95 const name = b.fmt("{s}.zig", .{overlay.name});
96 break :rsf write_files.add(name, bytes);
97 },
98 .pic = overlay.pic,
99 .strip = if (base.strip) |s| s else overlay.strip,
100 });
101
102 if (overlay.objcpp_source_bytes) |bytes| {
103 mod.addCSourceFile(.{
104 .file = write_files.add("a.mm", bytes),
105 .flags = overlay.objcpp_source_flags,
106 });
107 }
108 if (overlay.objc_source_bytes) |bytes| {
109 mod.addCSourceFile(.{
110 .file = write_files.add("a.m", bytes),
111 .flags = overlay.objc_source_flags,
112 });
113 }
114 if (overlay.cpp_source_bytes) |bytes| {
115 mod.addCSourceFile(.{
116 .file = write_files.add("a.cpp", bytes),
117 .flags = overlay.cpp_source_flags,
118 });
119 }
120 if (overlay.c_source_bytes) |bytes| {
121 mod.addCSourceFile(.{
122 .file = write_files.add("a.c", bytes),
123 .flags = overlay.c_source_flags,
124 });
125 }
126 if (overlay.asm_source_bytes) |bytes| {
127 mod.addAssemblyFile(write_files.add("a.s", bytes));
128 }
129
130 return mod;
131}
132
133pub fn addRunArtifact(comp: *Compile) *Run {
134 const b = comp.step.owner;
135 const run = b.addRunArtifact(comp);
136 run.skip_foreign_checks = true;
137 return run;
138}
139
140pub fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
141 const b = comp.step.owner;
142 const file = WriteFile.create(b).add("a.c", bytes);
143 comp.root_module.addCSourceFile(.{ .file = file, .flags = flags });
144}
145
146pub fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
147 const b = comp.step.owner;
148 const file = WriteFile.create(b).add("a.cpp", bytes);
149 comp.root_module.addCSourceFile(.{ .file = file, .flags = flags });
150}
151
152pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
153 const b = comp.step.owner;
154 const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
155 const file = WriteFile.create(b).add("a.s", actual_bytes);
156 comp.root_module.addAssemblyFile(file);
157}
158
159pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
160 comp.expect_errors = expected_errors;
161 const bin_file = comp.getEmittedBin();
162 bin_file.addStepDependencies(test_step);
163}
164
165const std = @import("std");
166
167const Build = std.Build;
168const Compile = Step.Compile;
169const Run = Step.Run;
170const Step = Build.Step;
171const WriteFile = Step.WriteFile;
test/link/macho.zig deleted-3291
...@@ -1,3291 +0,0 @@
1//! Here we test our MachO linker for correctness and functionality.
2
3pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
4 const macho_step = b.step("test-macho", "Run MachO tests");
5
6 // https://github.com/ziglang/zig/issues/25323
7 if (builtin.os.tag == .freebsd) return macho_step;
8
9 // https://github.com/ziglang/zig/issues/25961
10 if (comptime builtin.cpu.arch.endian() == .big) return macho_step;
11
12 const x86_64_target = b.resolveTargetQuery(.{
13 .cpu_arch = .x86_64,
14 .os_tag = .macos,
15 });
16 const aarch64_target = b.resolveTargetQuery(.{
17 .cpu_arch = .aarch64,
18 .os_tag = .macos,
19 });
20
21 const default_target = switch (builtin.cpu.arch) {
22 .x86_64, .aarch64 => b.resolveTargetQuery(.{
23 .os_tag = .macos,
24 }),
25 else => aarch64_target,
26 };
27
28 // Exercise linker with self-hosted backend (no LLVM)
29 macho_step.dependOn(testEmptyZig(b, .{ .use_llvm = false, .target = x86_64_target }));
30 macho_step.dependOn(testHelloZig(b, .{ .use_llvm = false, .target = x86_64_target }));
31 macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target }));
32 macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
33 macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target }));
34 macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target }));
35 macho_step.dependOn(testUnresolvedError(b, .{ .use_llvm = false, .target = x86_64_target }));
36
37 // Exercise linker with LLVM backend
38 macho_step.dependOn(testDeadStrip(b, .{ .target = default_target }));
39 macho_step.dependOn(testDuplicateDefinitions(b, .{ .target = default_target }));
40 macho_step.dependOn(testEmptyObject(b, .{ .target = default_target }));
41 macho_step.dependOn(testEmptyZig(b, .{ .target = default_target }));
42 macho_step.dependOn(testEntryPoint(b, .{ .target = default_target }));
43 macho_step.dependOn(testHeaderWeakFlags(b, .{ .target = default_target }));
44 macho_step.dependOn(testHelloC(b, .{ .target = default_target }));
45 macho_step.dependOn(testHelloZig(b, .{ .target = default_target }));
46 macho_step.dependOn(testLargeBss(b, .{ .target = default_target }));
47 macho_step.dependOn(testLayout(b, .{ .target = default_target }));
48 macho_step.dependOn(testLinkingStaticLib(b, .{ .target = default_target }));
49 macho_step.dependOn(testLinksection(b, .{ .target = default_target }));
50 macho_step.dependOn(testMergeLiteralsX64(b, .{ .target = x86_64_target }));
51 macho_step.dependOn(testMergeLiteralsArm64(b, .{ .target = aarch64_target }));
52 macho_step.dependOn(testMergeLiteralsArm642(b, .{ .target = aarch64_target }));
53 macho_step.dependOn(testMergeLiteralsAlignment(b, .{ .target = aarch64_target }));
54 macho_step.dependOn(testMhExecuteHeader(b, .{ .target = default_target }));
55 macho_step.dependOn(testNoDeadStrip(b, .{ .target = default_target }));
56 macho_step.dependOn(testNoExportsDylib(b, .{ .target = default_target }));
57 macho_step.dependOn(testPagezeroSize(b, .{ .target = default_target }));
58 macho_step.dependOn(testReexportsZig(b, .{ .target = default_target }));
59 macho_step.dependOn(testRelocatable(b, .{ .target = default_target }));
60 macho_step.dependOn(testRelocatableZig(b, .{ .target = default_target }));
61 macho_step.dependOn(testSectionBoundarySymbols(b, .{ .target = default_target }));
62 macho_step.dependOn(testSectionBoundarySymbols2(b, .{ .target = default_target }));
63 macho_step.dependOn(testSegmentBoundarySymbols(b, .{ .target = default_target }));
64 macho_step.dependOn(testSymbolStabs(b, .{ .target = default_target }));
65 macho_step.dependOn(testStackSize(b, .{ .target = default_target }));
66 macho_step.dependOn(testTentative(b, .{ .target = default_target }));
67 macho_step.dependOn(testThunks(b, .{ .target = aarch64_target }));
68 macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
69 macho_step.dependOn(testTlsZig(b, .{ .target = default_target }));
70 macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
71 macho_step.dependOn(testUndefinedDynamicLookup(b, .{ .target = default_target }));
72 macho_step.dependOn(testDiscardLocalSymbols(b, .{ .target = default_target }));
73 macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target }));
74 macho_step.dependOn(testUnresolvedError2(b, .{ .target = default_target }));
75 macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target }));
76 macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target }));
77 macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target }));
78 macho_step.dependOn(testEhFramePointerEncodingSdata4(b, .{ .target = aarch64_target }));
79 macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target }));
80 macho_step.dependOn(testWeakRef(b, .{ .target = b.resolveTargetQuery(.{
81 .cpu_arch = .x86_64,
82 .os_tag = .macos,
83 .os_version_min = .{ .semver = .{ .major = 10, .minor = 13, .patch = 0 } },
84 }) }));
85
86 // Tests requiring symlinks
87 if (build_opts.has_symlinks) {
88 macho_step.dependOn(testEntryPointArchive(b, .{ .target = default_target }));
89 macho_step.dependOn(testEntryPointDylib(b, .{ .target = default_target }));
90 macho_step.dependOn(testDylib(b, .{ .target = default_target }));
91 macho_step.dependOn(testDylibVersionTbd(b, .{ .target = default_target }));
92 macho_step.dependOn(testNeededLibrary(b, .{ .target = default_target }));
93 macho_step.dependOn(testSearchStrategy(b, .{ .target = default_target }));
94 macho_step.dependOn(testTbdv3(b, .{ .target = default_target }));
95 macho_step.dependOn(testTls(b, .{ .target = default_target }));
96 macho_step.dependOn(testTlsPointers(b, .{ .target = default_target }));
97 macho_step.dependOn(testTwoLevelNamespace(b, .{ .target = default_target }));
98 macho_step.dependOn(testWeakLibrary(b, .{ .target = default_target }));
99
100 // Tests requiring presence of macOS SDK in system path
101 if (build_opts.has_macos_sdk) {
102 macho_step.dependOn(testDeadStripDylibs(b, .{ .target = b.graph.host }));
103 macho_step.dependOn(testHeaderpad(b, .{ .target = b.graph.host }));
104 macho_step.dependOn(testLinkDirectlyCppTbd(b, .{ .target = b.graph.host }));
105 macho_step.dependOn(testMergeLiteralsObjc(b, .{ .target = b.graph.host }));
106 macho_step.dependOn(testNeededFramework(b, .{ .target = b.graph.host }));
107 macho_step.dependOn(testObjc(b, .{ .target = b.graph.host }));
108 macho_step.dependOn(testObjcpp(b, .{ .target = b.graph.host }));
109 macho_step.dependOn(testWeakFramework(b, .{ .target = b.graph.host }));
110 }
111 }
112
113 return macho_step;
114}
115
116fn testDeadStrip(b: *Build, opts: Options) *Step {
117 const test_step = addTestStep(b, "dead-strip", opts);
118
119 const obj = addObject(b, opts, .{ .name = "a", .cpp_source_bytes =
120 \\#include <stdio.h>
121 \\int two() { return 2; }
122 \\int live_var1 = 1;
123 \\int live_var2 = two();
124 \\int dead_var1 = 3;
125 \\int dead_var2 = 4;
126 \\void live_fn1() {}
127 \\void live_fn2() { live_fn1(); }
128 \\void dead_fn1() {}
129 \\void dead_fn2() { dead_fn1(); }
130 \\int main() {
131 \\ printf("%d %d\n", live_var1, live_var2);
132 \\ live_fn2();
133 \\}
134 });
135
136 {
137 const exe = addExecutable(b, opts, .{ .name = "no_dead_strip" });
138 exe.root_module.addObject(obj);
139 exe.link_gc_sections = false;
140
141 const check = exe.checkObject();
142 check.checkInSymtab();
143 check.checkContains("live_var1");
144 check.checkInSymtab();
145 check.checkContains("live_var2");
146 check.checkInSymtab();
147 check.checkContains("dead_var1");
148 check.checkInSymtab();
149 check.checkContains("dead_var2");
150 check.checkInSymtab();
151 check.checkContains("live_fn1");
152 check.checkInSymtab();
153 check.checkContains("live_fn2");
154 check.checkInSymtab();
155 check.checkContains("dead_fn1");
156 check.checkInSymtab();
157 check.checkContains("dead_fn2");
158 test_step.dependOn(&check.step);
159
160 const run = addRunArtifact(exe);
161 run.expectStdOutEqual("1 2\n");
162 test_step.dependOn(&run.step);
163 }
164
165 {
166 const exe = addExecutable(b, opts, .{ .name = "yes_dead_strip" });
167 exe.root_module.addObject(obj);
168 exe.link_gc_sections = true;
169
170 const check = exe.checkObject();
171 check.checkInSymtab();
172 check.checkContains("live_var1");
173 check.checkInSymtab();
174 check.checkContains("live_var2");
175 check.checkInSymtab();
176 check.checkNotPresent("dead_var1");
177 check.checkInSymtab();
178 check.checkNotPresent("dead_var2");
179 check.checkInSymtab();
180 check.checkContains("live_fn1");
181 check.checkInSymtab();
182 check.checkContains("live_fn2");
183 check.checkInSymtab();
184 check.checkNotPresent("dead_fn1");
185 check.checkInSymtab();
186 check.checkNotPresent("dead_fn2");
187 test_step.dependOn(&check.step);
188
189 const run = addRunArtifact(exe);
190 run.expectStdOutEqual("1 2\n");
191 test_step.dependOn(&run.step);
192 }
193
194 return test_step;
195}
196
197fn testDuplicateDefinitions(b: *Build, opts: Options) *Step {
198 const test_step = addTestStep(b, "duplicate-definitions", opts);
199
200 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
201 \\var x: usize = 1;
202 \\export fn strong() void { x += 1; }
203 \\export fn weak() void { x += 1; }
204 });
205
206 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
207 \\var x: usize = 1;
208 \\export fn strong() void { x += 1; }
209 \\comptime { @export(&weakImpl, .{ .name = "weak", .linkage = .weak }); }
210 \\fn weakImpl() callconv(.c) void { x += 1; }
211 \\extern fn weak() void;
212 \\pub fn main() void {
213 \\ weak();
214 \\ strong();
215 \\}
216 });
217 exe.root_module.addObject(obj);
218
219 expectLinkErrors(exe, test_step, .{ .exact = &.{
220 "error: duplicate symbol definition: _strong",
221 "note: defined by /?/a.o",
222 "note: defined by /?/main_zcu.o",
223 } });
224
225 return test_step;
226}
227
228fn testDeadStripDylibs(b: *Build, opts: Options) *Step {
229 const test_step = addTestStep(b, "dead-strip-dylibs", opts);
230
231 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
232 \\#include <objc/runtime.h>
233 \\int main() {
234 \\ if (objc_getClass("NSObject") == 0) {
235 \\ return -1;
236 \\ }
237 \\ if (objc_getClass("NSApplication") == 0) {
238 \\ return -2;
239 \\ }
240 \\ return 0;
241 \\}
242 });
243
244 {
245 const exe = addExecutable(b, opts, .{ .name = "main1" });
246 exe.root_module.addObject(main_o);
247 exe.root_module.linkFramework("Cocoa", .{});
248
249 const check = exe.checkObject();
250 check.checkInHeaders();
251 check.checkExact("cmd LOAD_DYLIB");
252 check.checkContains("Cocoa");
253 check.checkInHeaders();
254 check.checkExact("cmd LOAD_DYLIB");
255 check.checkContains("libobjc");
256 test_step.dependOn(&check.step);
257
258 const run = addRunArtifact(exe);
259 run.expectExitCode(0);
260 test_step.dependOn(&run.step);
261 }
262
263 {
264 const exe = addExecutable(b, opts, .{ .name = "main2" });
265 exe.root_module.addObject(main_o);
266 exe.root_module.linkFramework("Cocoa", .{});
267 exe.dead_strip_dylibs = true;
268
269 const run = addRunArtifact(exe);
270 run.expectExitCode(@as(u8, @bitCast(@as(i8, -2))));
271 test_step.dependOn(&run.step);
272 }
273
274 return test_step;
275}
276
277fn testDylib(b: *Build, opts: Options) *Step {
278 const test_step = addTestStep(b, "dylib", opts);
279
280 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
281 \\#include<stdio.h>
282 \\char world[] = "world";
283 \\char* hello() {
284 \\ return "Hello";
285 \\}
286 });
287
288 const check = dylib.checkObject();
289 check.checkInHeaders();
290 check.checkExact("header");
291 check.checkNotPresent("PIE");
292 test_step.dependOn(&check.step);
293
294 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
295 \\#include<stdio.h>
296 \\char* hello();
297 \\extern char world[];
298 \\int main() {
299 \\ printf("%s %s", hello(), world);
300 \\ return 0;
301 \\}
302 });
303 exe.root_module.linkSystemLibrary("a", .{});
304 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
305 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
306
307 const run = addRunArtifact(exe);
308 run.expectStdOutEqual("Hello world");
309 test_step.dependOn(&run.step);
310
311 return test_step;
312}
313
314fn testDylibVersionTbd(b: *Build, opts: Options) *Step {
315 const test_step = addTestStep(b, "dylib-version-tbd", opts);
316
317 const tbd = tbd: {
318 const wf = WriteFile.create(b);
319 break :tbd wf.add("liba.tbd",
320 \\--- !tapi-tbd
321 \\tbd-version: 4
322 \\targets: [ x86_64-macos, arm64-macos ]
323 \\uuids:
324 \\ - target: x86_64-macos
325 \\ value: DEADBEEF
326 \\ - target: arm64-macos
327 \\ value: BEEFDEAD
328 \\install-name: '@rpath/liba.dylib'
329 \\current-version: 1.2
330 \\exports:
331 \\ - targets: [ x86_64-macos, arm64-macos ]
332 \\ symbols: [ _foo ]
333 );
334 };
335
336 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() {}" });
337 exe.root_module.linkSystemLibrary("a", .{});
338 exe.root_module.addLibraryPath(tbd.dirname());
339
340 const check = exe.checkObject();
341 check.checkInHeaders();
342 check.checkExact("cmd LOAD_DYLIB");
343 check.checkExact("name @rpath/liba.dylib");
344 check.checkExact("current version 10200");
345 test_step.dependOn(&check.step);
346
347 return test_step;
348}
349
350fn testEmptyObject(b: *Build, opts: Options) *Step {
351 const test_step = addTestStep(b, "empty-object", opts);
352
353 const empty = addObject(b, opts, .{ .name = "empty", .c_source_bytes = "" });
354
355 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
356 \\#include <stdio.h>
357 \\int main() {
358 \\ printf("Hello world!");
359 \\}
360 });
361 exe.root_module.addObject(empty);
362
363 const run = addRunArtifact(exe);
364 run.expectStdOutEqual("Hello world!");
365 test_step.dependOn(&run.step);
366
367 return test_step;
368}
369
370fn testEmptyZig(b: *Build, opts: Options) *Step {
371 const test_step = addTestStep(b, "empty-zig", opts);
372
373 const exe = addExecutable(b, opts, .{ .name = "empty", .zig_source_bytes = "pub fn main() void {}" });
374
375 const run = addRunArtifact(exe);
376 run.expectExitCode(0);
377 test_step.dependOn(&run.step);
378
379 return test_step;
380}
381
382fn testEntryPoint(b: *Build, opts: Options) *Step {
383 const test_step = addTestStep(b, "entry-point", opts);
384
385 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
386 \\#include<stdio.h>
387 \\int non_main() {
388 \\ printf("%d", 42);
389 \\ return 0;
390 \\}
391 });
392 exe.entry = .{ .symbol_name = "_non_main" };
393
394 const run = addRunArtifact(exe);
395 run.expectStdOutEqual("42");
396 test_step.dependOn(&run.step);
397
398 const check = exe.checkObject();
399 check.checkInHeaders();
400 check.checkExact("segname __TEXT");
401 check.checkExtract("vmaddr {vmaddr}");
402 check.checkInHeaders();
403 check.checkExact("cmd MAIN");
404 check.checkExtract("entryoff {entryoff}");
405 check.checkInSymtab();
406 check.checkExtract("{n_value} (__TEXT,__text) external _non_main");
407 check.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } });
408 test_step.dependOn(&check.step);
409
410 return test_step;
411}
412
413fn testEntryPointArchive(b: *Build, opts: Options) *Step {
414 const test_step = addTestStep(b, "entry-point-archive", opts);
415
416 const lib = addStaticLibrary(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
417
418 {
419 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "" });
420 exe.root_module.linkSystemLibrary("main", .{});
421 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
422
423 const run = addRunArtifact(exe);
424 run.expectExitCode(0);
425 test_step.dependOn(&run.step);
426 }
427
428 {
429 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "" });
430 exe.root_module.linkSystemLibrary("main", .{});
431 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
432 exe.link_gc_sections = true;
433
434 const run = addRunArtifact(exe);
435 run.expectExitCode(0);
436 test_step.dependOn(&run.step);
437 }
438
439 return test_step;
440}
441
442fn testEntryPointDylib(b: *Build, opts: Options) *Step {
443 const test_step = addTestStep(b, "entry-point-dylib", opts);
444
445 const dylib = addSharedLibrary(b, opts, .{ .name = "a" });
446 addCSourceBytes(dylib,
447 \\extern int my_main();
448 \\int bootstrap() {
449 \\ return my_main();
450 \\}
451 , &.{});
452 dylib.linker_allow_shlib_undefined = true;
453
454 const exe = addExecutable(b, opts, .{ .name = "main" });
455 addCSourceBytes(dylib,
456 \\#include<stdio.h>
457 \\int my_main() {
458 \\ fprintf(stdout, "Hello!\n");
459 \\ return 0;
460 \\}
461 , &.{});
462 exe.root_module.linkLibrary(dylib);
463 exe.entry = .{ .symbol_name = "_bootstrap" };
464 exe.forceUndefinedSymbol("_my_main");
465
466 const check = exe.checkObject();
467 check.checkInHeaders();
468 check.checkExact("segname __TEXT");
469 check.checkExtract("vmaddr {text_vmaddr}");
470 check.checkInHeaders();
471 check.checkExact("sectname __stubs");
472 check.checkExtract("addr {stubs_vmaddr}");
473 check.checkInHeaders();
474 check.checkExact("sectname __stubs");
475 check.checkExtract("size {stubs_vmsize}");
476 check.checkInHeaders();
477 check.checkExact("cmd MAIN");
478 check.checkExtract("entryoff {entryoff}");
479 check.checkComputeCompare("text_vmaddr entryoff +", .{
480 .op = .gte,
481 .value = .{ .variable = "stubs_vmaddr" }, // The entrypoint should be a synthetic stub
482 });
483 check.checkComputeCompare("text_vmaddr entryoff + stubs_vmaddr -", .{
484 .op = .lt,
485 .value = .{ .variable = "stubs_vmsize" }, // The entrypoint should be a synthetic stub
486 });
487 test_step.dependOn(&check.step);
488
489 const run = addRunArtifact(exe);
490 run.expectStdOutEqual("Hello!\n");
491 test_step.dependOn(&run.step);
492
493 return test_step;
494}
495
496fn testHeaderpad(b: *Build, opts: Options) *Step {
497 const test_step = addTestStep(b, "headerpad", opts);
498
499 const addExe = struct {
500 fn addExe(bb: *Build, o: Options, name: []const u8) *Compile {
501 const exe = addExecutable(bb, o, .{
502 .name = name,
503 .c_source_bytes = "int main() { return 0; }",
504 });
505 exe.root_module.linkFramework("CoreFoundation", .{});
506 exe.root_module.linkFramework("Foundation", .{});
507 exe.root_module.linkFramework("Cocoa", .{});
508 exe.root_module.linkFramework("CoreGraphics", .{});
509 exe.root_module.linkFramework("CoreHaptics", .{});
510 exe.root_module.linkFramework("CoreAudio", .{});
511 exe.root_module.linkFramework("AVFoundation", .{});
512 exe.root_module.linkFramework("CoreImage", .{});
513 exe.root_module.linkFramework("CoreLocation", .{});
514 exe.root_module.linkFramework("CoreML", .{});
515 exe.root_module.linkFramework("CoreVideo", .{});
516 exe.root_module.linkFramework("CoreText", .{});
517 exe.root_module.linkFramework("CryptoKit", .{});
518 exe.root_module.linkFramework("GameKit", .{});
519 exe.root_module.linkFramework("SwiftUI", .{});
520 exe.root_module.linkFramework("StoreKit", .{});
521 exe.root_module.linkFramework("SpriteKit", .{});
522 return exe;
523 }
524 }.addExe;
525
526 {
527 const exe = addExe(b, opts, "main1");
528 exe.headerpad_max_install_names = true;
529
530 const check = exe.checkObject();
531 check.checkInHeaders();
532 check.checkExact("sectname __text");
533 check.checkExtract("offset {offset}");
534 switch (opts.target.result.cpu.arch) {
535 .aarch64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }),
536 .x86_64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }),
537 else => unreachable,
538 }
539 test_step.dependOn(&check.step);
540
541 const run = addRunArtifact(exe);
542 run.expectExitCode(0);
543 test_step.dependOn(&run.step);
544 }
545
546 {
547 const exe = addExe(b, opts, "main2");
548 exe.headerpad_size = 0x10000;
549
550 const check = exe.checkObject();
551 check.checkInHeaders();
552 check.checkExact("sectname __text");
553 check.checkExtract("offset {offset}");
554 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
555 test_step.dependOn(&check.step);
556
557 const run = addRunArtifact(exe);
558 run.expectExitCode(0);
559 test_step.dependOn(&run.step);
560 }
561
562 {
563 const exe = addExe(b, opts, "main3");
564 exe.headerpad_max_install_names = true;
565 exe.headerpad_size = 0x10000;
566
567 const check = exe.checkObject();
568 check.checkInHeaders();
569 check.checkExact("sectname __text");
570 check.checkExtract("offset {offset}");
571 check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } });
572 test_step.dependOn(&check.step);
573
574 const run = addRunArtifact(exe);
575 run.expectExitCode(0);
576 test_step.dependOn(&run.step);
577 }
578
579 {
580 const exe = addExe(b, opts, "main4");
581 exe.headerpad_max_install_names = true;
582 exe.headerpad_size = 0x1000;
583
584 const check = exe.checkObject();
585 check.checkInHeaders();
586 check.checkExact("sectname __text");
587 check.checkExtract("offset {offset}");
588 switch (opts.target.result.cpu.arch) {
589 .aarch64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }),
590 .x86_64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }),
591 else => unreachable,
592 }
593 test_step.dependOn(&check.step);
594
595 const run = addRunArtifact(exe);
596 run.expectExitCode(0);
597 test_step.dependOn(&run.step);
598 }
599
600 return test_step;
601}
602
603// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-header-flags.s
604fn testHeaderWeakFlags(b: *Build, opts: Options) *Step {
605 const test_step = addTestStep(b, "header-weak-flags", opts);
606
607 const obj1 = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
608 \\.globl _x
609 \\.weak_definition _x
610 \\_x:
611 \\ ret
612 });
613
614 const lib = addSharedLibrary(b, opts, .{ .name = "a" });
615 lib.root_module.addObject(obj1);
616
617 {
618 const exe = addExecutable(b, opts, .{ .name = "main1", .c_source_bytes = "int main() { return 0; }" });
619 exe.root_module.addObject(obj1);
620
621 const check = exe.checkObject();
622 check.checkInHeaders();
623 check.checkExact("header");
624 check.checkContains("WEAK_DEFINES");
625 check.checkInHeaders();
626 check.checkExact("header");
627 check.checkContains("BINDS_TO_WEAK");
628 check.checkInExports();
629 check.checkExtract("[WEAK] {vmaddr} _x");
630 test_step.dependOn(&check.step);
631 }
632
633 {
634 const obj = addObject(b, opts, .{ .name = "b" });
635
636 switch (opts.target.result.cpu.arch) {
637 .aarch64 => addAsmSourceBytes(obj,
638 \\.globl _main
639 \\_main:
640 \\ bl _x
641 \\ ret
642 ),
643 .x86_64 => addAsmSourceBytes(obj,
644 \\.globl _main
645 \\_main:
646 \\ callq _x
647 \\ ret
648 ),
649 else => unreachable,
650 }
651
652 const exe = addExecutable(b, opts, .{ .name = "main2" });
653 exe.root_module.linkLibrary(lib);
654 exe.root_module.addObject(obj);
655
656 const check = exe.checkObject();
657 check.checkInHeaders();
658 check.checkExact("header");
659 check.checkNotPresent("WEAK_DEFINES");
660 check.checkInHeaders();
661 check.checkExact("header");
662 check.checkContains("BINDS_TO_WEAK");
663 check.checkInExports();
664 check.checkNotPresent("[WEAK] {vmaddr} _x");
665 test_step.dependOn(&check.step);
666 }
667
668 {
669 const exe = addExecutable(b, opts, .{ .name = "main3", .asm_source_bytes =
670 \\.globl _main, _x
671 \\_x:
672 \\
673 \\_main:
674 \\ ret
675 });
676 exe.root_module.linkLibrary(lib);
677
678 const check = exe.checkObject();
679 check.checkInHeaders();
680 check.checkExact("header");
681 check.checkNotPresent("WEAK_DEFINES");
682 check.checkInHeaders();
683 check.checkExact("header");
684 check.checkNotPresent("BINDS_TO_WEAK");
685 test_step.dependOn(&check.step);
686 }
687
688 return test_step;
689}
690
691fn testHelloC(b: *Build, opts: Options) *Step {
692 const test_step = addTestStep(b, "hello-c", opts);
693
694 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
695 \\#include <stdio.h>
696 \\int main() {
697 \\ printf("Hello world!\n");
698 \\ return 0;
699 \\}
700 });
701
702 const run = addRunArtifact(exe);
703 run.expectStdOutEqual("Hello world!\n");
704 test_step.dependOn(&run.step);
705
706 const check = exe.checkObject();
707 check.checkInHeaders();
708 check.checkExact("header");
709 check.checkContains("PIE");
710 test_step.dependOn(&check.step);
711
712 return test_step;
713}
714
715fn testHelloZig(b: *Build, opts: Options) *Step {
716 const test_step = addTestStep(b, "hello-zig", opts);
717
718 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
719 \\const std = @import("std");
720 \\pub fn main() void {
721 \\ std.Io.File.stdout().writeStreamingAll(std.Options.debug_io, "Hello world!\n") catch @panic("fail");
722 \\}
723 });
724
725 const run = addRunArtifact(exe);
726 run.expectStdOutEqual("Hello world!\n");
727 test_step.dependOn(&run.step);
728
729 return test_step;
730}
731
732fn testLargeBss(b: *Build, opts: Options) *Step {
733 const test_step = addTestStep(b, "large-bss", opts);
734
735 // TODO this test used use a 4GB zerofill section but this actually fails and causes every
736 // linker I tried misbehave in different ways. This only happened on arm64. I thought that
737 // maybe S_GB_ZEROFILL section is an answer to this but it doesn't seem supported by dyld
738 // anymore. When I get some free time I will re-investigate this.
739 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
740 \\char arr[0x1000000];
741 \\int main() {
742 \\ return arr[2000];
743 \\}
744 });
745
746 const run = addRunArtifact(exe);
747 run.expectExitCode(0);
748 test_step.dependOn(&run.step);
749
750 return test_step;
751}
752
753fn testLayout(b: *Build, opts: Options) *Step {
754 const test_step = addTestStep(b, "layout", opts);
755
756 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
757 \\#include <stdio.h>
758 \\int main() {
759 \\ printf("Hello world!");
760 \\ return 0;
761 \\}
762 });
763
764 const check = exe.checkObject();
765 check.checkInHeaders();
766 check.checkExact("cmd SEGMENT_64");
767 check.checkExact("segname __LINKEDIT");
768 check.checkExtract("fileoff {fileoff}");
769 check.checkExtract("filesz {filesz}");
770 check.checkInHeaders();
771 check.checkExact("cmd DYLD_INFO_ONLY");
772 check.checkExtract("rebaseoff {rebaseoff}");
773 check.checkExtract("rebasesize {rebasesize}");
774 check.checkExtract("bindoff {bindoff}");
775 check.checkExtract("bindsize {bindsize}");
776 check.checkExtract("lazybindoff {lazybindoff}");
777 check.checkExtract("lazybindsize {lazybindsize}");
778 check.checkExtract("exportoff {exportoff}");
779 check.checkExtract("exportsize {exportsize}");
780 check.checkInHeaders();
781 check.checkExact("cmd FUNCTION_STARTS");
782 check.checkExtract("dataoff {fstartoff}");
783 check.checkExtract("datasize {fstartsize}");
784 check.checkInHeaders();
785 check.checkExact("cmd DATA_IN_CODE");
786 check.checkExtract("dataoff {diceoff}");
787 check.checkExtract("datasize {dicesize}");
788 check.checkInHeaders();
789 check.checkExact("cmd SYMTAB");
790 check.checkExtract("symoff {symoff}");
791 check.checkExtract("nsyms {symnsyms}");
792 check.checkExtract("stroff {stroff}");
793 check.checkExtract("strsize {strsize}");
794 check.checkInHeaders();
795 check.checkExact("cmd DYSYMTAB");
796 check.checkExtract("indirectsymoff {dysymoff}");
797 check.checkExtract("nindirectsyms {dysymnsyms}");
798
799 switch (opts.target.result.cpu.arch) {
800 .aarch64 => {
801 check.checkInHeaders();
802 check.checkExact("cmd CODE_SIGNATURE");
803 check.checkExtract("dataoff {codesigoff}");
804 check.checkExtract("datasize {codesigsize}");
805 },
806 .x86_64 => {},
807 else => unreachable,
808 }
809
810 // DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export,
811 // and there are no gaps between them
812 check.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .eq, .value = .{ .variable = "bindoff" } });
813 check.checkComputeCompare("bindoff bindsize +", .{ .op = .eq, .value = .{ .variable = "lazybindoff" } });
814 check.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .eq, .value = .{ .variable = "exportoff" } });
815
816 // FUNCTION_STARTS directly follows DYLD_INFO_ONLY (no gap)
817 check.checkComputeCompare("exportoff exportsize +", .{ .op = .eq, .value = .{ .variable = "fstartoff" } });
818
819 // DATA_IN_CODE directly follows FUNCTION_STARTS (no gap)
820 check.checkComputeCompare("fstartoff fstartsize +", .{ .op = .eq, .value = .{ .variable = "diceoff" } });
821
822 // SYMTAB directly follows DATA_IN_CODE (no gap)
823 check.checkComputeCompare("diceoff dicesize +", .{ .op = .eq, .value = .{ .variable = "symoff" } });
824
825 // DYSYMTAB directly follows SYMTAB (no gap)
826 check.checkComputeCompare("symnsyms 16 symoff * +", .{ .op = .eq, .value = .{ .variable = "dysymoff" } });
827
828 // STRTAB follows DYSYMTAB with possible gap
829 check.checkComputeCompare("dysymnsyms 4 dysymoff * +", .{ .op = .lte, .value = .{ .variable = "stroff" } });
830
831 // all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned
832 check.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
833 check.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
834 check.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
835 check.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
836 check.checkComputeCompare("fstartoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
837 check.checkComputeCompare("diceoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
838 check.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
839 check.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
840 check.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } });
841
842 switch (opts.target.result.cpu.arch) {
843 .aarch64 => {
844 // LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data
845 check.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{
846 .op = .eq,
847 .value = .{ .literal = 0 },
848 });
849
850 // CODE_SIGNATURE data offset is 16-bytes aligned
851 check.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
852 },
853 .x86_64 => {
854 // LINKEDIT segment does not extend beyond, or does not include, strtab data
855 check.checkComputeCompare("fileoff filesz stroff strsize + - -", .{
856 .op = .eq,
857 .value = .{ .literal = 0 },
858 });
859 },
860 else => unreachable,
861 }
862
863 test_step.dependOn(&check.step);
864
865 const run = addRunArtifact(exe);
866 run.expectStdOutEqual("Hello world!");
867 test_step.dependOn(&run.step);
868
869 return test_step;
870}
871
872fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step {
873 const io = b.graph.io;
874 const test_step = addTestStep(b, "link-directly-cpp-tbd", opts);
875
876 const sdk = std.zig.system.darwin.getSdk(b.allocator, io, &opts.target.result) orelse
877 @panic("macOS SDK is required to run the test");
878
879 const exe = addExecutable(b, opts, .{
880 .name = "main",
881 .cpp_source_bytes =
882 \\#include <new>
883 \\#include <cstdio>
884 \\int main() {
885 \\ int *x = new int;
886 \\ *x = 5;
887 \\ fprintf(stderr, "x: %d\n", *x);
888 \\ delete x;
889 \\}
890 ,
891 .cpp_source_flags = &.{ "-nostdlib++", "-nostdinc++" },
892 });
893 exe.root_module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) });
894 exe.root_module.addIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) });
895 exe.root_module.addObjectFile(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/lib/libc++.tbd" }) });
896
897 const check = exe.checkObject();
898 check.checkInSymtab();
899 check.checkContains("[referenced dynamically] external __mh_execute_header");
900 test_step.dependOn(&check.step);
901
902 return test_step;
903}
904
905fn testLinkingStaticLib(b: *Build, opts: Options) *Step {
906 const test_step = addTestStep(b, "linking-static-lib", opts);
907
908 const obj = addObject(b, opts, .{
909 .name = "bobj",
910 .zig_source_bytes = "export var bar: i32 = -42;",
911 .strip = true, // TODO for self-hosted, we don't really emit any valid DWARF yet since we only export a global
912 });
913
914 const lib = addStaticLibrary(b, opts, .{
915 .name = "alib",
916 .zig_source_bytes =
917 \\export fn foo() i32 {
918 \\ return 42;
919 \\}
920 ,
921 });
922 lib.root_module.addObject(obj);
923
924 const exe = addExecutable(b, opts, .{
925 .name = "testlib",
926 .zig_source_bytes =
927 \\const std = @import("std");
928 \\extern fn foo() i32;
929 \\extern var bar: i32;
930 \\pub fn main() void {
931 \\ std.debug.print("{d}\n", .{foo() + bar});
932 \\}
933 ,
934 });
935 exe.root_module.linkLibrary(lib);
936
937 const run = addRunArtifact(exe);
938 run.expectStdErrEqual("0\n");
939 test_step.dependOn(&run.step);
940
941 return test_step;
942}
943
944fn testLinksection(b: *Build, opts: Options) *Step {
945 const test_step = addTestStep(b, "linksection", opts);
946
947 const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
948 \\export var test_global: u32 linksection("__DATA,__TestGlobal") = undefined;
949 \\export fn testFn() linksection("__TEXT,__TestFn") callconv(.c) void {
950 \\ TestGenericFn("A").f();
951 \\}
952 \\fn TestGenericFn(comptime suffix: []const u8) type {
953 \\ return struct {
954 \\ fn f() linksection("__TEXT,__TestGenFn" ++ suffix) void {}
955 \\ };
956 \\}
957 });
958
959 const check = obj.checkObject();
960 check.checkInSymtab();
961 check.checkContains("(__DATA,__TestGlobal) external _test_global");
962 check.checkInSymtab();
963 check.checkContains("(__TEXT,__TestFn) external _testFn");
964
965 if (opts.optimize == .Debug) {
966 check.checkInSymtab();
967 check.checkContains("(__TEXT,__TestGenFnA) _main.TestGenericFn(");
968 }
969
970 test_step.dependOn(&check.step);
971
972 return test_step;
973}
974
975fn testMergeLiteralsX64(b: *Build, opts: Options) *Step {
976 const test_step = addTestStep(b, "merge-literals-x64", opts);
977
978 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
979 \\.globl _q1
980 \\.globl _s1
981 \\
982 \\.align 4
983 \\_q1:
984 \\ lea L._q1(%rip), %rax
985 \\ mov (%rax), %xmm0
986 \\ ret
987 \\
988 \\.section __TEXT,__cstring,cstring_literals
989 \\l._s1:
990 \\ .asciz "hello"
991 \\
992 \\.section __TEXT,__literal8,8byte_literals
993 \\.align 8
994 \\L._q1:
995 \\ .double 1.2345
996 \\
997 \\.section __DATA,__data
998 \\.align 8
999 \\_s1:
1000 \\ .quad l._s1
1001 });
1002
1003 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1004 \\.globl _q2
1005 \\.globl _s2
1006 \\.globl _s3
1007 \\
1008 \\.align 4
1009 \\_q2:
1010 \\ lea L._q2(%rip), %rax
1011 \\ mov (%rax), %xmm0
1012 \\ ret
1013 \\
1014 \\.section __TEXT,__cstring,cstring_literals
1015 \\l._s2:
1016 \\ .asciz "hello"
1017 \\l._s3:
1018 \\ .asciz "world"
1019 \\
1020 \\.section __TEXT,__literal8,8byte_literals
1021 \\.align 8
1022 \\L._q2:
1023 \\ .double 1.2345
1024 \\
1025 \\.section __DATA,__data
1026 \\.align 8
1027 \\_s2:
1028 \\ .quad l._s2
1029 \\_s3:
1030 \\ .quad l._s3
1031 });
1032
1033 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1034 \\#include <stdio.h>
1035 \\extern double q1();
1036 \\extern double q2();
1037 \\extern const char* s1;
1038 \\extern const char* s2;
1039 \\extern const char* s3;
1040 \\int main() {
1041 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
1042 \\ return 0;
1043 \\}
1044 });
1045
1046 const runWithChecks = struct {
1047 fn runWithChecks(step: *Step, exe: *Compile) void {
1048 const run = addRunArtifact(exe);
1049 run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500");
1050 step.dependOn(&run.step);
1051
1052 const check = exe.checkObject();
1053 check.dumpSection("__TEXT,__const");
1054 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1055 check.dumpSection("__TEXT,__cstring");
1056 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1057 step.dependOn(&check.step);
1058 }
1059 }.runWithChecks;
1060
1061 {
1062 const exe = addExecutable(b, opts, .{ .name = "main1" });
1063 exe.root_module.addObject(a_o);
1064 exe.root_module.addObject(b_o);
1065 exe.root_module.addObject(main_o);
1066 runWithChecks(test_step, exe);
1067 }
1068
1069 {
1070 const exe = addExecutable(b, opts, .{ .name = "main2" });
1071 exe.root_module.addObject(b_o);
1072 exe.root_module.addObject(a_o);
1073 exe.root_module.addObject(main_o);
1074 runWithChecks(test_step, exe);
1075 }
1076
1077 {
1078 const c_o = addObject(b, opts, .{ .name = "c" });
1079 c_o.root_module.addObject(a_o);
1080 c_o.root_module.addObject(b_o);
1081 c_o.root_module.addObject(main_o);
1082
1083 const exe = addExecutable(b, opts, .{ .name = "main3" });
1084 exe.root_module.addObject(c_o);
1085 runWithChecks(test_step, exe);
1086 }
1087
1088 return test_step;
1089}
1090
1091fn testMergeLiteralsArm64(b: *Build, opts: Options) *Step {
1092 const test_step = addTestStep(b, "merge-literals-arm64", opts);
1093
1094 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1095 \\.globl _q1
1096 \\.globl _s1
1097 \\
1098 \\.align 4
1099 \\_q1:
1100 \\ adrp x8, L._q1@PAGE
1101 \\ ldr d0, [x8, L._q1@PAGEOFF]
1102 \\ ret
1103 \\
1104 \\.section __TEXT,__cstring,cstring_literals
1105 \\l._s1:
1106 \\ .asciz "hello"
1107 \\
1108 \\.section __TEXT,__literal8,8byte_literals
1109 \\.align 8
1110 \\L._q1:
1111 \\ .double 1.2345
1112 \\
1113 \\.section __DATA,__data
1114 \\.align 8
1115 \\_s1:
1116 \\ .quad l._s1
1117 });
1118
1119 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1120 \\.globl _q2
1121 \\.globl _s2
1122 \\.globl _s3
1123 \\
1124 \\.align 4
1125 \\_q2:
1126 \\ adrp x8, L._q2@PAGE
1127 \\ ldr d0, [x8, L._q2@PAGEOFF]
1128 \\ ret
1129 \\
1130 \\.section __TEXT,__cstring,cstring_literals
1131 \\l._s2:
1132 \\ .asciz "hello"
1133 \\l._s3:
1134 \\ .asciz "world"
1135 \\
1136 \\.section __TEXT,__literal8,8byte_literals
1137 \\.align 8
1138 \\L._q2:
1139 \\ .double 1.2345
1140 \\
1141 \\.section __DATA,__data
1142 \\.align 8
1143 \\_s2:
1144 \\ .quad l._s2
1145 \\_s3:
1146 \\ .quad l._s3
1147 });
1148
1149 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1150 \\#include <stdio.h>
1151 \\extern double q1();
1152 \\extern double q2();
1153 \\extern const char* s1;
1154 \\extern const char* s2;
1155 \\extern const char* s3;
1156 \\int main() {
1157 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
1158 \\ return 0;
1159 \\}
1160 });
1161
1162 const runWithChecks = struct {
1163 fn runWithChecks(step: *Step, exe: *Compile) void {
1164 const run = addRunArtifact(exe);
1165 run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500");
1166 step.dependOn(&run.step);
1167
1168 const check = exe.checkObject();
1169 check.dumpSection("__TEXT,__const");
1170 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1171 check.dumpSection("__TEXT,__cstring");
1172 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1173 step.dependOn(&check.step);
1174 }
1175 }.runWithChecks;
1176
1177 {
1178 const exe = addExecutable(b, opts, .{ .name = "main1" });
1179 exe.root_module.addObject(a_o);
1180 exe.root_module.addObject(b_o);
1181 exe.root_module.addObject(main_o);
1182 runWithChecks(test_step, exe);
1183 }
1184
1185 {
1186 const exe = addExecutable(b, opts, .{ .name = "main2" });
1187 exe.root_module.addObject(b_o);
1188 exe.root_module.addObject(a_o);
1189 exe.root_module.addObject(main_o);
1190 runWithChecks(test_step, exe);
1191 }
1192
1193 {
1194 const c_o = addObject(b, opts, .{ .name = "c" });
1195 c_o.root_module.addObject(a_o);
1196 c_o.root_module.addObject(b_o);
1197 c_o.root_module.addObject(main_o);
1198
1199 const exe = addExecutable(b, opts, .{ .name = "main3" });
1200 exe.root_module.addObject(c_o);
1201 runWithChecks(test_step, exe);
1202 }
1203
1204 return test_step;
1205}
1206
1207/// This particular test case will generate invalid machine code that will segfault at runtime.
1208/// However, this is by design as we want to test that the linker does not panic when linking it
1209/// which is also the case for the system linker and lld - linking succeeds, runtime segfaults.
1210/// It should also be mentioned that runtime segfault is not due to the linker but faulty input asm.
1211fn testMergeLiteralsArm642(b: *Build, opts: Options) *Step {
1212 const test_step = addTestStep(b, "merge-literals-arm64-2", opts);
1213
1214 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1215 \\.globl _q1
1216 \\.globl _s1
1217 \\
1218 \\.align 4
1219 \\_q1:
1220 \\ adrp x0, L._q1@PAGE
1221 \\ ldr x0, [x0, L._q1@PAGEOFF]
1222 \\ ret
1223 \\
1224 \\.section __TEXT,__cstring,cstring_literals
1225 \\_s1:
1226 \\ .asciz "hello"
1227 \\
1228 \\.section __TEXT,__literal8,8byte_literals
1229 \\.align 8
1230 \\L._q1:
1231 \\ .double 1.2345
1232 });
1233
1234 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1235 \\.globl _q2
1236 \\.globl _s2
1237 \\.globl _s3
1238 \\
1239 \\.align 4
1240 \\_q2:
1241 \\ adrp x0, L._q2@PAGE
1242 \\ ldr x0, [x0, L._q2@PAGEOFF]
1243 \\ ret
1244 \\
1245 \\.section __TEXT,__cstring,cstring_literals
1246 \\_s2:
1247 \\ .asciz "hello"
1248 \\_s3:
1249 \\ .asciz "world"
1250 \\
1251 \\.section __TEXT,__literal8,8byte_literals
1252 \\.align 8
1253 \\L._q2:
1254 \\ .double 1.2345
1255 });
1256
1257 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1258 \\#include <stdio.h>
1259 \\extern double q1();
1260 \\extern double q2();
1261 \\extern const char* s1;
1262 \\extern const char* s2;
1263 \\extern const char* s3;
1264 \\int main() {
1265 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
1266 \\ return 0;
1267 \\}
1268 });
1269
1270 const exe = addExecutable(b, opts, .{ .name = "main1" });
1271 exe.root_module.addObject(a_o);
1272 exe.root_module.addObject(b_o);
1273 exe.root_module.addObject(main_o);
1274
1275 const check = exe.checkObject();
1276 check.dumpSection("__TEXT,__const");
1277 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1278 check.dumpSection("__TEXT,__cstring");
1279 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1280 test_step.dependOn(&check.step);
1281
1282 return test_step;
1283}
1284
1285fn testMergeLiteralsAlignment(b: *Build, opts: Options) *Step {
1286 const test_step = addTestStep(b, "merge-literals-alignment", opts);
1287
1288 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1289 \\.globl _s1
1290 \\.globl _s2
1291 \\
1292 \\.section __TEXT,__cstring,cstring_literals
1293 \\.align 3
1294 \\_s1:
1295 \\ .asciz "str1"
1296 \\_s2:
1297 \\ .asciz "str2"
1298 });
1299
1300 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1301 \\.globl _s3
1302 \\.globl _s4
1303 \\
1304 \\.section __TEXT,__cstring,cstring_literals
1305 \\.align 2
1306 \\_s3:
1307 \\ .asciz "str1"
1308 \\_s4:
1309 \\ .asciz "str2"
1310 });
1311
1312 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1313 \\#include <assert.h>
1314 \\#include <stdint.h>
1315 \\#include <stdio.h>
1316 \\extern const char* s1;
1317 \\extern const char* s2;
1318 \\extern const char* s3;
1319 \\extern const char* s4;
1320 \\int main() {
1321 \\ assert((uintptr_t)(&s1) % 8 == 0 && s1 == s3);
1322 \\ assert((uintptr_t)(&s2) % 8 == 0 && s2 == s4);
1323 \\ printf("%s%s%s%s", &s1, &s2, &s3, &s4);
1324 \\ return 0;
1325 \\}
1326 , .c_source_flags = &.{"-Wno-format"} });
1327
1328 const runWithChecks = struct {
1329 fn runWithChecks(step: *Step, exe: *Compile) void {
1330 const run = addRunArtifact(exe);
1331 run.expectStdOutEqual("str1str2str1str2");
1332 step.dependOn(&run.step);
1333
1334 const check = exe.checkObject();
1335 check.dumpSection("__TEXT,__cstring");
1336 check.checkContains("str1\x00\x00\x00\x00str2\x00");
1337 check.checkInHeaders();
1338 check.checkExact("segname __TEXT");
1339 check.checkExact("sectname __cstring");
1340 check.checkExact("align 3");
1341 step.dependOn(&check.step);
1342 }
1343 }.runWithChecks;
1344
1345 {
1346 const exe = addExecutable(b, opts, .{ .name = "main1" });
1347 exe.root_module.addObject(a_o);
1348 exe.root_module.addObject(b_o);
1349 exe.root_module.addObject(main_o);
1350 runWithChecks(test_step, exe);
1351 }
1352
1353 {
1354 const exe = addExecutable(b, opts, .{ .name = "main2" });
1355 exe.root_module.addObject(b_o);
1356 exe.root_module.addObject(a_o);
1357 exe.root_module.addObject(main_o);
1358 runWithChecks(test_step, exe);
1359 }
1360
1361 return test_step;
1362}
1363
1364fn testMergeLiteralsObjc(b: *Build, opts: Options) *Step {
1365 const test_step = addTestStep(b, "merge-literals-objc", opts);
1366
1367 const main_o = addObject(b, opts, .{ .name = "main", .objc_source_bytes =
1368 \\#import <Foundation/Foundation.h>;
1369 \\
1370 \\extern void foo();
1371 \\
1372 \\int main() {
1373 \\ NSString *thing = @"aaa";
1374 \\
1375 \\ SEL sel = @selector(lowercaseString);
1376 \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO");
1377 \\ NSLog (@"Responds to lowercaseString: %@", lower);
1378 \\ if ([thing respondsToSelector:sel]) //(lower == @"YES")
1379 \\ NSLog(@"lowercaseString is: %@", [thing lowercaseString]);
1380 \\
1381 \\ foo();
1382 \\}
1383 });
1384
1385 const a_o = addObject(b, opts, .{ .name = "a", .objc_source_bytes =
1386 \\#import <Foundation/Foundation.h>;
1387 \\
1388 \\void foo() {
1389 \\ NSString *thing = @"aaa";
1390 \\ SEL sel = @selector(lowercaseString);
1391 \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO");
1392 \\ NSLog (@"Responds to lowercaseString in foo(): %@", lower);
1393 \\ if ([thing respondsToSelector:sel]) //(lower == @"YES")
1394 \\ NSLog(@"lowercaseString in foo() is: %@", [thing lowercaseString]);
1395 \\ SEL sel2 = @selector(uppercaseString);
1396 \\ NSString *upper = (([thing respondsToSelector:sel2]) ? @"YES" : @"NO");
1397 \\ NSLog (@"Responds to uppercaseString in foo(): %@", upper);
1398 \\ if ([thing respondsToSelector:sel2]) //(upper == @"YES")
1399 \\ NSLog(@"uppercaseString in foo() is: %@", [thing uppercaseString]);
1400 \\}
1401 });
1402
1403 const runWithChecks = struct {
1404 fn runWithChecks(step: *Step, exe: *Compile) void {
1405 const builder = step.owner;
1406 const run = addRunArtifact(exe);
1407 run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString: YES") });
1408 run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString is: aaa") });
1409 run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString in foo(): YES") });
1410 run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString in foo() is: aaa") });
1411 run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to uppercaseString in foo(): YES") });
1412 run.addCheck(.{ .expect_stderr_match = builder.dupe("uppercaseString in foo() is: AAA") });
1413 step.dependOn(&run.step);
1414
1415 const check = exe.checkObject();
1416 check.dumpSection("__TEXT,__objc_methname");
1417 check.checkContains("lowercaseString\x00");
1418 check.dumpSection("__TEXT,__objc_methname");
1419 check.checkContains("uppercaseString\x00");
1420 step.dependOn(&check.step);
1421 }
1422 }.runWithChecks;
1423
1424 {
1425 const exe = addExecutable(b, opts, .{ .name = "main1" });
1426 exe.root_module.addObject(main_o);
1427 exe.root_module.addObject(a_o);
1428 exe.root_module.linkFramework("Foundation", .{});
1429 runWithChecks(test_step, exe);
1430 }
1431
1432 {
1433 const exe = addExecutable(b, opts, .{ .name = "main2" });
1434 exe.root_module.addObject(a_o);
1435 exe.root_module.addObject(main_o);
1436 exe.root_module.linkFramework("Foundation", .{});
1437 runWithChecks(test_step, exe);
1438 }
1439
1440 {
1441 const b_o = addObject(b, opts, .{ .name = "b" });
1442 b_o.root_module.addObject(a_o);
1443 b_o.root_module.addObject(main_o);
1444
1445 const exe = addExecutable(b, opts, .{ .name = "main3" });
1446 exe.root_module.addObject(b_o);
1447 exe.root_module.linkFramework("Foundation", .{});
1448 runWithChecks(test_step, exe);
1449 }
1450
1451 return test_step;
1452}
1453
1454fn testMhExecuteHeader(b: *Build, opts: Options) *Step {
1455 const test_step = addTestStep(b, "mh-execute-header", opts);
1456
1457 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1458
1459 const check = exe.checkObject();
1460 check.checkInSymtab();
1461 check.checkContains("[referenced dynamically] external __mh_execute_header");
1462 test_step.dependOn(&check.step);
1463
1464 return test_step;
1465}
1466
1467fn testNoDeadStrip(b: *Build, opts: Options) *Step {
1468 const test_step = addTestStep(b, "no-dead-strip", opts);
1469
1470 const exe = addExecutable(b, opts, .{ .name = "name", .c_source_bytes =
1471 \\__attribute__((used)) int bogus1 = 0;
1472 \\int bogus2 = 0;
1473 \\int foo = 42;
1474 \\int main() {
1475 \\ return foo - 42;
1476 \\}
1477 });
1478 exe.link_gc_sections = true;
1479
1480 const check = exe.checkObject();
1481 check.checkInSymtab();
1482 check.checkContains("external _bogus1");
1483 check.checkInSymtab();
1484 check.checkNotPresent("external _bogus2");
1485 test_step.dependOn(&check.step);
1486
1487 const run = addRunArtifact(exe);
1488 run.expectExitCode(0);
1489 test_step.dependOn(&run.step);
1490
1491 return test_step;
1492}
1493
1494fn testNoExportsDylib(b: *Build, opts: Options) *Step {
1495 const test_step = addTestStep(b, "no-exports-dylib", opts);
1496
1497 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "static void abc() {}" });
1498
1499 const check = dylib.checkObject();
1500 check.checkInSymtab();
1501 check.checkNotPresent("external _abc");
1502 test_step.dependOn(&check.step);
1503
1504 return test_step;
1505}
1506
1507fn testNeededFramework(b: *Build, opts: Options) *Step {
1508 const test_step = addTestStep(b, "needed-framework", opts);
1509
1510 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1511 exe.root_module.linkFramework("Cocoa", .{ .needed = true });
1512 exe.dead_strip_dylibs = true;
1513
1514 const check = exe.checkObject();
1515 check.checkInHeaders();
1516 check.checkExact("cmd LOAD_DYLIB");
1517 check.checkContains("Cocoa");
1518 test_step.dependOn(&check.step);
1519
1520 const run = addRunArtifact(exe);
1521 run.expectExitCode(0);
1522 test_step.dependOn(&run.step);
1523
1524 return test_step;
1525}
1526
1527fn testNeededLibrary(b: *Build, opts: Options) *Step {
1528 const test_step = addTestStep(b, "needed-library", opts);
1529
1530 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int a = 42;" });
1531
1532 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1533 exe.root_module.linkSystemLibrary("a", .{ .needed = true });
1534 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
1535 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
1536 exe.dead_strip_dylibs = true;
1537
1538 const check = exe.checkObject();
1539 check.checkInHeaders();
1540 check.checkExact("cmd LOAD_DYLIB");
1541 check.checkContains("liba.dylib");
1542 test_step.dependOn(&check.step);
1543
1544 const run = addRunArtifact(exe);
1545 run.expectExitCode(0);
1546 test_step.dependOn(&run.step);
1547
1548 return test_step;
1549}
1550
1551fn testObjc(b: *Build, opts: Options) *Step {
1552 const test_step = addTestStep(b, "objc", opts);
1553
1554 const lib = addStaticLibrary(b, opts, .{ .name = "a", .objc_source_bytes =
1555 \\#import <Foundation/Foundation.h>
1556 \\@interface Foo : NSObject
1557 \\@end
1558 \\@implementation Foo
1559 \\@end
1560 });
1561
1562 {
1563 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
1564 exe.root_module.linkSystemLibrary("a", .{});
1565 exe.root_module.linkFramework("Foundation", .{});
1566 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
1567
1568 const check = exe.checkObject();
1569 check.checkInSymtab();
1570 check.checkNotPresent("_OBJC_");
1571 test_step.dependOn(&check.step);
1572
1573 const run = addRunArtifact(exe);
1574 run.expectExitCode(0);
1575 test_step.dependOn(&run.step);
1576 }
1577
1578 {
1579 const exe = addExecutable(b, opts, .{ .name = "main2", .c_source_bytes = "int main() { return 0; }" });
1580 exe.root_module.linkSystemLibrary("a", .{});
1581 exe.root_module.linkFramework("Foundation", .{});
1582 exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
1583 exe.force_load_objc = true;
1584
1585 const check = exe.checkObject();
1586 check.checkInSymtab();
1587 check.checkContains("_OBJC_");
1588 test_step.dependOn(&check.step);
1589
1590 const run = addRunArtifact(exe);
1591 run.expectExitCode(0);
1592 test_step.dependOn(&run.step);
1593 }
1594
1595 return test_step;
1596}
1597
1598fn testObjcpp(b: *Build, opts: Options) *Step {
1599 const test_step = addTestStep(b, "objcpp", opts);
1600
1601 const foo_h = foo_h: {
1602 const wf = WriteFile.create(b);
1603 break :foo_h wf.add("Foo.h",
1604 \\#import <Foundation/Foundation.h>
1605 \\@interface Foo : NSObject
1606 \\- (NSString *)name;
1607 \\@end
1608 );
1609 };
1610
1611 const foo_o = addObject(b, opts, .{ .name = "foo", .objcpp_source_bytes =
1612 \\#import "Foo.h"
1613 \\@implementation Foo
1614 \\- (NSString *)name
1615 \\{
1616 \\ NSString *str = [[NSString alloc] initWithFormat:@"Zig"];
1617 \\ return str;
1618 \\}
1619 \\@end
1620 });
1621 foo_o.root_module.addIncludePath(foo_h.dirname());
1622 foo_o.root_module.link_libcpp = true;
1623
1624 const exe = addExecutable(b, opts, .{ .name = "main", .objcpp_source_bytes =
1625 \\#import "Foo.h"
1626 \\#import <assert.h>
1627 \\#include <iostream>
1628 \\int main(int argc, char *argv[])
1629 \\{
1630 \\ @autoreleasepool {
1631 \\ Foo *foo = [[Foo alloc] init];
1632 \\ NSString *result = [foo name];
1633 \\ std::cout << "Hello from C++ and " << [result UTF8String];
1634 \\ assert([result isEqualToString:@"Zig"]);
1635 \\ return 0;
1636 \\ }
1637 \\}
1638 });
1639 exe.root_module.addIncludePath(foo_h.dirname());
1640 exe.root_module.addObject(foo_o);
1641 exe.root_module.link_libcpp = true;
1642 exe.root_module.linkFramework("Foundation", .{});
1643
1644 const run = addRunArtifact(exe);
1645 run.expectStdOutEqual("Hello from C++ and Zig");
1646 test_step.dependOn(&run.step);
1647
1648 return test_step;
1649}
1650
1651fn testPagezeroSize(b: *Build, opts: Options) *Step {
1652 const test_step = addTestStep(b, "pagezero-size", opts);
1653
1654 {
1655 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main () { return 0; }" });
1656 exe.pagezero_size = 0x4000;
1657
1658 const check = exe.checkObject();
1659 check.checkInHeaders();
1660 check.checkExact("LC 0");
1661 check.checkExact("segname __PAGEZERO");
1662 check.checkExact("vmaddr 0");
1663 check.checkExact("vmsize 4000");
1664 check.checkInHeaders();
1665 check.checkExact("segname __TEXT");
1666 check.checkExact("vmaddr 4000");
1667 test_step.dependOn(&check.step);
1668 }
1669
1670 {
1671 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main () { return 0; }" });
1672 exe.pagezero_size = 0;
1673
1674 const check = exe.checkObject();
1675 check.checkInHeaders();
1676 check.checkExact("LC 0");
1677 check.checkExact("segname __TEXT");
1678 check.checkExact("vmaddr 0");
1679 test_step.dependOn(&check.step);
1680 }
1681
1682 return test_step;
1683}
1684
1685fn testReexportsZig(b: *Build, opts: Options) *Step {
1686 const test_step = addTestStep(b, "reexports-zig", opts);
1687
1688 const lib = addStaticLibrary(b, opts, .{ .name = "a", .zig_source_bytes =
1689 \\const x: i32 = 42;
1690 \\export fn foo() i32 {
1691 \\ return x;
1692 \\}
1693 \\comptime {
1694 \\ @export(&foo, .{ .name = "bar", .linkage = .strong });
1695 \\}
1696 });
1697
1698 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
1699 \\extern int foo();
1700 \\extern int bar();
1701 \\int main() {
1702 \\ return bar() - foo();
1703 \\}
1704 });
1705 exe.root_module.linkLibrary(lib);
1706
1707 const run = addRunArtifact(exe);
1708 run.expectExitCode(0);
1709 test_step.dependOn(&run.step);
1710
1711 return test_step;
1712}
1713
1714fn testRelocatable(b: *Build, opts: Options) *Step {
1715 const test_step = addTestStep(b, "relocatable", opts);
1716
1717 const a_o = addObject(b, opts, .{ .name = "a", .cpp_source_bytes =
1718 \\#include <stdexcept>
1719 \\int try_me() {
1720 \\ throw std::runtime_error("Oh no!");
1721 \\}
1722 });
1723 a_o.root_module.link_libcpp = true;
1724
1725 const b_o = addObject(b, opts, .{ .name = "b", .cpp_source_bytes =
1726 \\extern int try_me();
1727 \\int try_again() {
1728 \\ return try_me();
1729 \\}
1730 });
1731
1732 const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
1733 \\#include <iostream>
1734 \\#include <stdexcept>
1735 \\extern int try_again();
1736 \\int main() {
1737 \\ try {
1738 \\ try_again();
1739 \\ } catch (const std::exception &e) {
1740 \\ std::cout << "exception=" << e.what();
1741 \\ }
1742 \\ return 0;
1743 \\}
1744 });
1745 main_o.root_module.link_libcpp = true;
1746
1747 const exp_stdout = "exception=Oh no!";
1748
1749 {
1750 const c_o = addObject(b, opts, .{ .name = "c" });
1751 c_o.root_module.addObject(a_o);
1752 c_o.root_module.addObject(b_o);
1753
1754 const exe = addExecutable(b, opts, .{ .name = "main1" });
1755 exe.root_module.addObject(main_o);
1756 exe.root_module.addObject(c_o);
1757 exe.root_module.link_libcpp = true;
1758
1759 const run = addRunArtifact(exe);
1760 run.expectStdOutEqual(exp_stdout);
1761 test_step.dependOn(&run.step);
1762 }
1763
1764 {
1765 const d_o = addObject(b, opts, .{ .name = "d" });
1766 d_o.root_module.addObject(a_o);
1767 d_o.root_module.addObject(b_o);
1768 d_o.root_module.addObject(main_o);
1769
1770 const exe = addExecutable(b, opts, .{ .name = "main2" });
1771 exe.root_module.addObject(d_o);
1772 exe.root_module.link_libcpp = true;
1773
1774 const run = addRunArtifact(exe);
1775 run.expectStdOutEqual(exp_stdout);
1776 test_step.dependOn(&run.step);
1777 }
1778
1779 return test_step;
1780}
1781
1782fn testRelocatableZig(b: *Build, opts: Options) *Step {
1783 const test_step = addTestStep(b, "relocatable-zig", opts);
1784
1785 const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
1786 \\const std = @import("std");
1787 \\export var foo: i32 = 0;
1788 \\export fn incrFoo() void {
1789 \\ foo += 1;
1790 \\ std.debug.print("incrFoo={d}\n", .{foo});
1791 \\}
1792 });
1793
1794 const b_o = addObject(b, opts, .{ .name = "b", .zig_source_bytes =
1795 \\const std = @import("std");
1796 \\extern var foo: i32;
1797 \\export fn decrFoo() void {
1798 \\ foo -= 1;
1799 \\ std.debug.print("decrFoo={d}\n", .{foo});
1800 \\}
1801 });
1802
1803 const main_o = addObject(b, opts, .{ .name = "main", .zig_source_bytes =
1804 \\const std = @import("std");
1805 \\extern var foo: i32;
1806 \\extern fn incrFoo() void;
1807 \\extern fn decrFoo() void;
1808 \\pub fn main() void {
1809 \\ const init = foo;
1810 \\ incrFoo();
1811 \\ decrFoo();
1812 \\ if (init == foo) @panic("Oh no!");
1813 \\}
1814 });
1815
1816 const c_o = addObject(b, opts, .{ .name = "c" });
1817 c_o.root_module.addObject(a_o);
1818 c_o.root_module.addObject(b_o);
1819 c_o.root_module.addObject(main_o);
1820
1821 const exe = addExecutable(b, opts, .{ .name = "main" });
1822 exe.root_module.addObject(c_o);
1823
1824 const run = addRunArtifact(exe);
1825 run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") });
1826 run.addCheck(.{ .expect_stderr_match = b.dupe("decrFoo=0") });
1827 run.addCheck(.{ .expect_stderr_match = b.dupe("panic: Oh no!") });
1828 test_step.dependOn(&run.step);
1829
1830 return test_step;
1831}
1832
1833fn testSearchStrategy(b: *Build, opts: Options) *Step {
1834 const test_step = addTestStep(b, "search-strategy", opts);
1835
1836 const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes =
1837 \\#include<stdio.h>
1838 \\char world[] = "world";
1839 \\char* hello() {
1840 \\ return "Hello";
1841 \\}
1842 });
1843
1844 const liba = addStaticLibrary(b, opts, .{ .name = "a" });
1845 liba.root_module.addObject(obj);
1846
1847 const dylib = addSharedLibrary(b, opts, .{ .name = "a" });
1848 dylib.root_module.addObject(obj);
1849
1850 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1851 \\#include<stdio.h>
1852 \\char* hello();
1853 \\extern char world[];
1854 \\int main() {
1855 \\ printf("%s %s", hello(), world);
1856 \\ return 0;
1857 \\}
1858 });
1859
1860 {
1861 const exe = addExecutable(b, opts, .{ .name = "main" });
1862 exe.root_module.addObject(main_o);
1863 exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .mode_first });
1864 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
1865 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
1866 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
1867
1868 const run = addRunArtifact(exe);
1869 run.expectStdOutEqual("Hello world");
1870 test_step.dependOn(&run.step);
1871
1872 const check = exe.checkObject();
1873 check.checkInHeaders();
1874 check.checkExact("cmd LOAD_DYLIB");
1875 check.checkContains("liba.dylib");
1876 test_step.dependOn(&check.step);
1877 }
1878
1879 {
1880 const exe = addExecutable(b, opts, .{ .name = "main" });
1881 exe.root_module.addObject(main_o);
1882 exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .paths_first });
1883 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
1884 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
1885 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
1886
1887 const run = addRunArtifact(exe);
1888 run.expectStdOutEqual("Hello world");
1889 test_step.dependOn(&run.step);
1890
1891 const check = exe.checkObject();
1892 check.checkInHeaders();
1893 check.checkExact("cmd LOAD_DYLIB");
1894 check.checkNotPresent("liba.dylib");
1895 test_step.dependOn(&check.step);
1896 }
1897
1898 return test_step;
1899}
1900
1901fn testSectionBoundarySymbols(b: *Build, opts: Options) *Step {
1902 const test_step = addTestStep(b, "section-boundary-symbols", opts);
1903
1904 const obj1 = addObject(b, opts, .{
1905 .name = "obj1",
1906 .cpp_source_bytes =
1907 \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase";
1908 ,
1909 });
1910
1911 const main_o = addObject(b, opts, .{
1912 .name = "main",
1913 .zig_source_bytes =
1914 \\const std = @import("std");
1915 \\extern fn interop() ?[*:0]const u8;
1916 \\pub fn main() !void {
1917 \\ std.debug.print("All your {s} are belong to us.\n", .{
1918 \\ if (interop()) |ptr| std.mem.span(ptr) else "(null)",
1919 \\ });
1920 \\}
1921 ,
1922 });
1923
1924 {
1925 const obj2 = addObject(b, opts, .{
1926 .name = "obj2",
1927 .cpp_source_bytes =
1928 \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr");
1929 \\extern "C" const char* interop() {
1930 \\ return message_pointer;
1931 \\}
1932 ,
1933 });
1934
1935 const exe = addExecutable(b, opts, .{ .name = "test" });
1936 exe.root_module.addObject(obj1);
1937 exe.root_module.addObject(obj2);
1938 exe.root_module.addObject(main_o);
1939
1940 const run = b.addRunArtifact(exe);
1941 run.skip_foreign_checks = true;
1942 run.expectStdErrEqual("All your codebase are belong to us.\n");
1943 test_step.dependOn(&run.step);
1944
1945 const check = exe.checkObject();
1946 check.checkInSymtab();
1947 check.checkNotPresent("external section$start$__DATA_CONST$__message_ptr");
1948 test_step.dependOn(&check.step);
1949 }
1950
1951 {
1952 const obj3 = addObject(b, opts, .{
1953 .name = "obj3",
1954 .cpp_source_bytes =
1955 \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__not_present");
1956 \\extern "C" const char* interop() {
1957 \\ return message_pointer;
1958 \\}
1959 ,
1960 });
1961
1962 const exe = addExecutable(b, opts, .{ .name = "test" });
1963 exe.root_module.addObject(obj1);
1964 exe.root_module.addObject(obj3);
1965 exe.root_module.addObject(main_o);
1966
1967 const run = b.addRunArtifact(exe);
1968 run.skip_foreign_checks = true;
1969 run.expectStdErrEqual("All your (null) are belong to us.\n");
1970 test_step.dependOn(&run.step);
1971
1972 const check = exe.checkObject();
1973 check.checkInSymtab();
1974 check.checkNotPresent("external section$start$__DATA_CONST$__not_present");
1975 test_step.dependOn(&check.step);
1976 }
1977
1978 return test_step;
1979}
1980
1981fn testSectionBoundarySymbols2(b: *Build, opts: Options) *Step {
1982 const test_step = addTestStep(b, "section-boundary-symbols-2", opts);
1983
1984 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
1985 \\#include <stdio.h>
1986 \\struct pair { int a; int b; };
1987 \\struct pair first __attribute__((section("__DATA,__pairs"))) = { 1, 2 };
1988 \\struct pair second __attribute__((section("__DATA,__pairs"))) = { 3, 4 };
1989 \\extern struct pair pairs_start __asm("section$start$__DATA$__pairs");
1990 \\extern struct pair pairs_end __asm("section$end$__DATA$__pairs");
1991 \\int main() {
1992 \\ printf("%d,%d\n", first.a, first.b);
1993 \\ printf("%d,%d\n", second.a, second.b);
1994 \\ struct pair* p;
1995 \\ for (p = &pairs_start; p < &pairs_end; p++) {
1996 \\ p->a = 0;
1997 \\ }
1998 \\ printf("%d,%d\n", first.a, first.b);
1999 \\ printf("%d,%d\n", second.a, second.b);
2000 \\ return 0;
2001 \\}
2002 });
2003
2004 const run = b.addRunArtifact(exe);
2005 run.skip_foreign_checks = true;
2006 run.expectStdOutEqual(
2007 \\1,2
2008 \\3,4
2009 \\0,2
2010 \\0,4
2011 \\
2012 );
2013 test_step.dependOn(&run.step);
2014
2015 return test_step;
2016}
2017
2018fn testSegmentBoundarySymbols(b: *Build, opts: Options) *Step {
2019 const test_step = addTestStep(b, "segment-boundary-symbols", opts);
2020
2021 const obj1 = addObject(b, opts, .{ .name = "a", .cpp_source_bytes =
2022 \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST_1,__message_ptr"))) = "codebase";
2023 });
2024
2025 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
2026 \\#include <stdio.h>
2027 \\const char* interop();
2028 \\int main() {
2029 \\ printf("All your %s are belong to us.\n", interop());
2030 \\ return 0;
2031 \\}
2032 });
2033
2034 {
2035 const obj2 = addObject(b, opts, .{ .name = "b", .cpp_source_bytes =
2036 \\extern const char* message_pointer __asm("segment$start$__DATA_CONST_1");
2037 \\extern "C" const char* interop() {
2038 \\ return message_pointer;
2039 \\}
2040 });
2041
2042 const exe = addExecutable(b, opts, .{ .name = "main" });
2043 exe.root_module.addObject(obj1);
2044 exe.root_module.addObject(obj2);
2045 exe.root_module.addObject(main_o);
2046
2047 const run = addRunArtifact(exe);
2048 run.expectStdOutEqual("All your codebase are belong to us.\n");
2049 test_step.dependOn(&run.step);
2050
2051 const check = exe.checkObject();
2052 check.checkInSymtab();
2053 check.checkNotPresent("external segment$start$__DATA_CONST_1");
2054 test_step.dependOn(&check.step);
2055 }
2056
2057 {
2058 const obj2 = addObject(b, opts, .{ .name = "c", .cpp_source_bytes =
2059 \\extern const char* message_pointer __asm("segment$start$__DATA_1");
2060 \\extern "C" const char* interop() {
2061 \\ return message_pointer;
2062 \\}
2063 });
2064
2065 const exe = addExecutable(b, opts, .{ .name = "main2" });
2066 exe.root_module.addObject(obj1);
2067 exe.root_module.addObject(obj2);
2068 exe.root_module.addObject(main_o);
2069
2070 const check = exe.checkObject();
2071 check.checkInHeaders();
2072 check.checkExact("cmd SEGMENT_64");
2073 check.checkExact("segname __DATA_1");
2074 check.checkExtract("vmsize {vmsize}");
2075 check.checkExtract("filesz {filesz}");
2076 check.checkComputeCompare("vmsize", .{ .op = .eq, .value = .{ .literal = 0 } });
2077 check.checkComputeCompare("filesz", .{ .op = .eq, .value = .{ .literal = 0 } });
2078 check.checkInSymtab();
2079 check.checkNotPresent("external segment$start$__DATA_1");
2080 test_step.dependOn(&check.step);
2081 }
2082
2083 return test_step;
2084}
2085
2086fn testSymbolStabs(b: *Build, opts: Options) *Step {
2087 const test_step = addTestStep(b, "symbol-stabs", opts);
2088
2089 const a_o = addObject(b, opts, .{ .name = "a", .c_source_bytes =
2090 \\int foo = 42;
2091 \\int getFoo() {
2092 \\ return foo;
2093 \\}
2094 });
2095
2096 const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes =
2097 \\int bar = 24;
2098 \\int getBar() {
2099 \\ return bar;
2100 \\}
2101 });
2102
2103 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
2104 \\#include <stdio.h>
2105 \\extern int getFoo();
2106 \\extern int getBar();
2107 \\int main() {
2108 \\ printf("foo=%d,bar=%d", getFoo(), getBar());
2109 \\ return 0;
2110 \\}
2111 });
2112
2113 const exe = addExecutable(b, opts, .{ .name = "main" });
2114 exe.root_module.addObject(a_o);
2115 exe.root_module.addObject(b_o);
2116 exe.root_module.addObject(main_o);
2117
2118 const run = addRunArtifact(exe);
2119 run.expectStdOutEqual("foo=42,bar=24");
2120 test_step.dependOn(&run.step);
2121
2122 const check = exe.checkObject();
2123 check.checkInSymtab();
2124 check.checkContains("a.o"); // TODO we really should do a fuzzy search like OSO <ignore>/a.o
2125 check.checkInSymtab();
2126 check.checkContains("b.o");
2127 check.checkInSymtab();
2128 check.checkContains("main.o");
2129 test_step.dependOn(&check.step);
2130
2131 return test_step;
2132}
2133
2134fn testStackSize(b: *Build, opts: Options) *Step {
2135 const test_step = addTestStep(b, "stack-size", opts);
2136
2137 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
2138 exe.stack_size = 0x100000000;
2139
2140 const run = addRunArtifact(exe);
2141 run.expectExitCode(0);
2142 test_step.dependOn(&run.step);
2143
2144 const check = exe.checkObject();
2145 check.checkInHeaders();
2146 check.checkExact("cmd MAIN");
2147 check.checkExact("stacksize 100000000");
2148 test_step.dependOn(&check.step);
2149
2150 return test_step;
2151}
2152
2153fn testTbdv3(b: *Build, opts: Options) *Step {
2154 const test_step = addTestStep(b, "tbdv3", opts);
2155
2156 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int getFoo() { return 42; }" });
2157
2158 const tbd = tbd: {
2159 const wf = WriteFile.create(b);
2160 break :tbd wf.add("liba.tbd",
2161 \\--- !tapi-tbd-v3
2162 \\archs: [ arm64, x86_64 ]
2163 \\uuids: [ 'arm64: DEADBEEF', 'x86_64: BEEFDEAD' ]
2164 \\platform: macos
2165 \\install-name: @rpath/liba.dylib
2166 \\current-version: 0
2167 \\exports:
2168 \\ - archs: [ arm64, x86_64 ]
2169 \\ symbols: [ _getFoo ]
2170 );
2171 };
2172
2173 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2174 \\#include <stdio.h>
2175 \\int getFoo();
2176 \\int main() {
2177 \\ return getFoo() - 42;
2178 \\}
2179 });
2180 exe.root_module.linkSystemLibrary("a", .{});
2181 exe.root_module.addLibraryPath(tbd.dirname());
2182 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
2183
2184 const run = addRunArtifact(exe);
2185 run.expectExitCode(0);
2186 test_step.dependOn(&run.step);
2187
2188 return test_step;
2189}
2190
2191fn testTentative(b: *Build, opts: Options) *Step {
2192 const test_step = addTestStep(b, "tentative", opts);
2193
2194 const exe = addExecutable(b, opts, .{ .name = "main" });
2195 addCSourceBytes(exe,
2196 \\int foo;
2197 \\int bar;
2198 \\int baz = 42;
2199 , &.{"-fcommon"});
2200 addCSourceBytes(exe,
2201 \\#include<stdio.h>
2202 \\int foo;
2203 \\int bar = 5;
2204 \\int baz;
2205 \\int main() {
2206 \\ printf("%d %d %d\n", foo, bar, baz);
2207 \\}
2208 , &.{"-fcommon"});
2209
2210 const run = addRunArtifact(exe);
2211 run.expectStdOutEqual("0 5 42\n");
2212 test_step.dependOn(&run.step);
2213
2214 return test_step;
2215}
2216
2217fn testThunks(b: *Build, opts: Options) *Step {
2218 const test_step = addTestStep(b, "thunks", opts);
2219
2220 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2221 \\#include <stdio.h>
2222 \\void bar() {
2223 \\ printf("bar");
2224 \\}
2225 \\void foo() {
2226 \\ fprintf(stdout, "foo");
2227 \\}
2228 \\int main() {
2229 \\ foo();
2230 \\ bar();
2231 \\ return 0;
2232 \\}
2233 });
2234
2235 const check = exe.checkObject();
2236 check.checkInSymtab();
2237 check.checkContains("_printf__thunk");
2238 check.checkInSymtab();
2239 check.checkContains("_fprintf__thunk");
2240 test_step.dependOn(&check.step);
2241
2242 const run = addRunArtifact(exe);
2243 run.expectStdOutEqual("foobar");
2244 test_step.dependOn(&run.step);
2245
2246 return test_step;
2247}
2248
2249fn testTls(b: *Build, opts: Options) *Step {
2250 const test_step = addTestStep(b, "tls", opts);
2251
2252 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
2253 \\_Thread_local int a;
2254 \\int getA() {
2255 \\ return a;
2256 \\}
2257 });
2258
2259 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2260 \\#include<stdio.h>
2261 \\extern _Thread_local int a;
2262 \\extern int getA();
2263 \\int getA2() {
2264 \\ return a;
2265 \\}
2266 \\int main() {
2267 \\ a = 2;
2268 \\ printf("%d %d %d", a, getA(), getA2());
2269 \\ return 0;
2270 \\}
2271 });
2272 exe.root_module.linkSystemLibrary("a", .{});
2273 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
2274 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
2275
2276 const run = addRunArtifact(exe);
2277 run.expectStdOutEqual("2 2 2");
2278 test_step.dependOn(&run.step);
2279
2280 return test_step;
2281}
2282
2283// https://github.com/ziglang/zig/issues/19221
2284fn testTlsPointers(b: *Build, opts: Options) *Step {
2285 const test_step = addTestStep(b, "tls-pointers", opts);
2286
2287 const foo_h = foo_h: {
2288 const wf = WriteFile.create(b);
2289 break :foo_h wf.add("foo.h",
2290 \\template<typename just4fun>
2291 \\struct Foo {
2292 \\
2293 \\public:
2294 \\ static int getVar() {
2295 \\ static int thread_local var = 0;
2296 \\ ++var;
2297 \\ return var;
2298 \\}
2299 \\};
2300 );
2301 };
2302
2303 const bar_o = addObject(b, opts, .{ .name = "bar", .cpp_source_bytes =
2304 \\#include "foo.h"
2305 \\int bar() {
2306 \\ int v1 = Foo<int>::getVar();
2307 \\ return v1;
2308 \\}
2309 });
2310 bar_o.root_module.addIncludePath(foo_h.dirname());
2311 bar_o.root_module.link_libcpp = true;
2312
2313 const baz_o = addObject(b, opts, .{ .name = "baz", .cpp_source_bytes =
2314 \\#include "foo.h"
2315 \\int baz() {
2316 \\ int v1 = Foo<unsigned>::getVar();
2317 \\ return v1;
2318 \\}
2319 });
2320 baz_o.root_module.addIncludePath(foo_h.dirname());
2321 baz_o.root_module.link_libcpp = true;
2322
2323 const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
2324 \\extern int bar();
2325 \\extern int baz();
2326 \\int main() {
2327 \\ int v1 = bar();
2328 \\ int v2 = baz();
2329 \\ return v1 != v2;
2330 \\}
2331 });
2332 main_o.root_module.addIncludePath(foo_h.dirname());
2333 main_o.root_module.link_libcpp = true;
2334
2335 const exe = addExecutable(b, opts, .{ .name = "main" });
2336 exe.root_module.addObject(bar_o);
2337 exe.root_module.addObject(baz_o);
2338 exe.root_module.addObject(main_o);
2339 exe.root_module.link_libcpp = true;
2340
2341 const run = addRunArtifact(exe);
2342 run.expectExitCode(0);
2343 test_step.dependOn(&run.step);
2344
2345 return test_step;
2346}
2347
2348fn testTlsLargeTbss(b: *Build, opts: Options) *Step {
2349 const test_step = addTestStep(b, "tls-large-tbss", opts);
2350
2351 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2352 \\#include <stdio.h>
2353 \\_Thread_local int x[0x8000];
2354 \\_Thread_local int y[0x8000];
2355 \\int main() {
2356 \\ x[0] = 3;
2357 \\ x[0x7fff] = 5;
2358 \\ printf("%d %d %d %d %d %d\n", x[0], x[1], x[0x7fff], y[0], y[1], y[0x7fff]);
2359 \\}
2360 });
2361
2362 const run = addRunArtifact(exe);
2363 run.expectStdOutEqual("3 0 5 0 0 0\n");
2364 test_step.dependOn(&run.step);
2365
2366 return test_step;
2367}
2368
2369fn testTlsZig(b: *Build, opts: Options) *Step {
2370 const test_step = addTestStep(b, "tls-zig", opts);
2371
2372 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2373 \\const std = @import("std");
2374 \\threadlocal var x: i32 = 0;
2375 \\threadlocal var y: i32 = -1;
2376 \\pub fn main() void {
2377 \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{});
2378 \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable;
2379 \\ x -= 1;
2380 \\ y += 1;
2381 \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable;
2382 \\}
2383 });
2384
2385 const run = addRunArtifact(exe);
2386 run.expectStdOutEqual(
2387 \\0 -1
2388 \\-1 0
2389 \\
2390 );
2391 test_step.dependOn(&run.step);
2392
2393 return test_step;
2394}
2395
2396fn testTwoLevelNamespace(b: *Build, opts: Options) *Step {
2397 const test_step = addTestStep(b, "two-level-namespace", opts);
2398
2399 const liba = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
2400 \\#include <stdio.h>
2401 \\int foo = 1;
2402 \\int* ptr_to_foo = &foo;
2403 \\int getFoo() {
2404 \\ return foo;
2405 \\}
2406 \\void printInA() {
2407 \\ printf("liba: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo);
2408 \\}
2409 });
2410
2411 {
2412 const check = liba.checkObject();
2413 check.checkInDyldLazyBind();
2414 check.checkNotPresent("(flat lookup) _getFoo");
2415 check.checkInIndirectSymtab();
2416 check.checkNotPresent("_getFoo");
2417 test_step.dependOn(&check.step);
2418 }
2419
2420 const libb = addSharedLibrary(b, opts, .{ .name = "b", .c_source_bytes =
2421 \\#include <stdio.h>
2422 \\int foo = 2;
2423 \\int* ptr_to_foo = &foo;
2424 \\int getFoo() {
2425 \\ return foo;
2426 \\}
2427 \\void printInB() {
2428 \\ printf("libb: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo);
2429 \\}
2430 });
2431
2432 {
2433 const check = libb.checkObject();
2434 check.checkInDyldLazyBind();
2435 check.checkNotPresent("(flat lookup) _getFoo");
2436 check.checkInIndirectSymtab();
2437 check.checkNotPresent("_getFoo");
2438 test_step.dependOn(&check.step);
2439 }
2440
2441 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
2442 \\#include <stdio.h>
2443 \\int getFoo();
2444 \\extern int* ptr_to_foo;
2445 \\void printInA();
2446 \\void printInB();
2447 \\int main() {
2448 \\ printf("main: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo);
2449 \\ printInA();
2450 \\ printInB();
2451 \\ return 0;
2452 \\}
2453 });
2454
2455 {
2456 const exe = addExecutable(b, opts, .{ .name = "main1" });
2457 exe.root_module.addObject(main_o);
2458 exe.root_module.linkSystemLibrary("a", .{});
2459 exe.root_module.linkSystemLibrary("b", .{});
2460 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
2461 exe.root_module.addLibraryPath(libb.getEmittedBinDirectory());
2462 exe.root_module.addRPath(liba.getEmittedBinDirectory());
2463 exe.root_module.addRPath(libb.getEmittedBinDirectory());
2464
2465 const check = exe.checkObject();
2466 check.checkInSymtab();
2467 check.checkExact("(undefined) external _getFoo (from liba)");
2468 check.checkInSymtab();
2469 check.checkExact("(undefined) external _printInA (from liba)");
2470 check.checkInSymtab();
2471 check.checkExact("(undefined) external _printInB (from libb)");
2472 test_step.dependOn(&check.step);
2473
2474 const run = addRunArtifact(exe);
2475 run.expectStdOutEqual(
2476 \\main: getFoo()=1, ptr_to_foo=1
2477 \\liba: getFoo()=1, ptr_to_foo=1
2478 \\libb: getFoo()=2, ptr_to_foo=2
2479 \\
2480 );
2481 test_step.dependOn(&run.step);
2482 }
2483
2484 {
2485 const exe = addExecutable(b, opts, .{ .name = "main2" });
2486 exe.root_module.addObject(main_o);
2487 exe.root_module.linkSystemLibrary("b", .{});
2488 exe.root_module.linkSystemLibrary("a", .{});
2489 exe.root_module.addLibraryPath(liba.getEmittedBinDirectory());
2490 exe.root_module.addLibraryPath(libb.getEmittedBinDirectory());
2491 exe.root_module.addRPath(liba.getEmittedBinDirectory());
2492 exe.root_module.addRPath(libb.getEmittedBinDirectory());
2493
2494 const check = exe.checkObject();
2495 check.checkInSymtab();
2496 check.checkExact("(undefined) external _getFoo (from libb)");
2497 check.checkInSymtab();
2498 check.checkExact("(undefined) external _printInA (from liba)");
2499 check.checkInSymtab();
2500 check.checkExact("(undefined) external _printInB (from libb)");
2501 test_step.dependOn(&check.step);
2502
2503 const run = addRunArtifact(exe);
2504 run.expectStdOutEqual(
2505 \\main: getFoo()=2, ptr_to_foo=2
2506 \\liba: getFoo()=1, ptr_to_foo=1
2507 \\libb: getFoo()=2, ptr_to_foo=2
2508 \\
2509 );
2510 test_step.dependOn(&run.step);
2511 }
2512
2513 return test_step;
2514}
2515
2516fn testDiscardLocalSymbols(b: *Build, opts: Options) *Step {
2517 const test_step = addTestStep(b, "discard-local-symbols", opts);
2518
2519 const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "static int foo = 42;" });
2520
2521 const lib = addStaticLibrary(b, opts, .{ .name = "a" });
2522 lib.root_module.addObject(obj);
2523
2524 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
2525
2526 {
2527 const exe = addExecutable(b, opts, .{ .name = "main3" });
2528 exe.root_module.addObject(main_o);
2529 exe.root_module.addObject(obj);
2530 exe.discard_local_symbols = true;
2531
2532 const run = addRunArtifact(exe);
2533 run.expectExitCode(0);
2534 test_step.dependOn(&run.step);
2535
2536 const check = exe.checkObject();
2537 check.checkInSymtab();
2538 check.checkNotPresent("_foo");
2539 test_step.dependOn(&check.step);
2540 }
2541
2542 {
2543 const exe = addExecutable(b, opts, .{ .name = "main4" });
2544 exe.root_module.addObject(main_o);
2545 exe.root_module.linkLibrary(lib);
2546 exe.discard_local_symbols = true;
2547
2548 const run = addRunArtifact(exe);
2549 run.expectExitCode(0);
2550 test_step.dependOn(&run.step);
2551
2552 const check = exe.checkObject();
2553 check.checkInSymtab();
2554 check.checkNotPresent("_foo");
2555 test_step.dependOn(&check.step);
2556 }
2557
2558 return test_step;
2559}
2560
2561fn testUndefinedFlag(b: *Build, opts: Options) *Step {
2562 const test_step = addTestStep(b, "undefined-flag", opts);
2563
2564 const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "int foo = 42;" });
2565
2566 const lib = addStaticLibrary(b, opts, .{ .name = "a" });
2567 lib.root_module.addObject(obj);
2568
2569 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
2570
2571 {
2572 const exe = addExecutable(b, opts, .{ .name = "main1" });
2573 exe.root_module.addObject(main_o);
2574 exe.root_module.linkLibrary(lib);
2575 exe.forceUndefinedSymbol("_foo");
2576
2577 const run = addRunArtifact(exe);
2578 run.expectExitCode(0);
2579 test_step.dependOn(&run.step);
2580
2581 const check = exe.checkObject();
2582 check.checkInSymtab();
2583 check.checkContains("_foo");
2584 test_step.dependOn(&check.step);
2585 }
2586
2587 {
2588 const exe = addExecutable(b, opts, .{ .name = "main2" });
2589 exe.root_module.addObject(main_o);
2590 exe.root_module.linkLibrary(lib);
2591 exe.forceUndefinedSymbol("_foo");
2592 exe.link_gc_sections = true;
2593
2594 const run = addRunArtifact(exe);
2595 run.expectExitCode(0);
2596 test_step.dependOn(&run.step);
2597
2598 const check = exe.checkObject();
2599 check.checkInSymtab();
2600 check.checkContains("_foo");
2601 test_step.dependOn(&check.step);
2602 }
2603
2604 {
2605 const exe = addExecutable(b, opts, .{ .name = "main3" });
2606 exe.root_module.addObject(main_o);
2607 exe.root_module.addObject(obj);
2608
2609 const run = addRunArtifact(exe);
2610 run.expectExitCode(0);
2611 test_step.dependOn(&run.step);
2612
2613 const check = exe.checkObject();
2614 check.checkInSymtab();
2615 check.checkContains("_foo");
2616 test_step.dependOn(&check.step);
2617 }
2618
2619 {
2620 const exe = addExecutable(b, opts, .{ .name = "main4" });
2621 exe.root_module.addObject(main_o);
2622 exe.root_module.addObject(obj);
2623 exe.link_gc_sections = true;
2624
2625 const run = addRunArtifact(exe);
2626 run.expectExitCode(0);
2627 test_step.dependOn(&run.step);
2628
2629 const check = exe.checkObject();
2630 check.checkInSymtab();
2631 check.checkNotPresent("_foo");
2632 test_step.dependOn(&check.step);
2633 }
2634
2635 return test_step;
2636}
2637
2638fn testUndefinedDynamicLookup(b: *Build, opts: Options) *Step {
2639 const test_step = addTestStep(b, "undefined-dynamic-lookup", opts);
2640
2641 // Create a dylib with an undefined external symbol reference
2642 const dylib = addSharedLibrary(b, opts, .{ .name = "a" });
2643 addCSourceBytes(dylib,
2644 \\extern int undefined_symbol(void);
2645 \\int call_undefined(void) {
2646 \\ return undefined_symbol();
2647 \\}
2648 , &.{});
2649 dylib.linker_allow_shlib_undefined = true;
2650
2651 // Verify the Mach-O header does NOT contain NOUNDEFS flag
2652 const check = dylib.checkObject();
2653 check.checkInHeaders();
2654 check.checkExact("header");
2655 check.checkNotPresent("NOUNDEFS");
2656 test_step.dependOn(&check.step);
2657
2658 return test_step;
2659}
2660
2661fn testUnresolvedError(b: *Build, opts: Options) *Step {
2662 const test_step = addTestStep(b, "unresolved-error", opts);
2663
2664 const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
2665 \\extern fn foo() i32;
2666 \\export fn bar() i32 { return foo() + 1; }
2667 });
2668
2669 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2670 \\const std = @import("std");
2671 \\extern fn foo() i32;
2672 \\extern fn bar() i32;
2673 \\pub fn main() void {
2674 \\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()});
2675 \\}
2676 });
2677 exe.root_module.addObject(obj);
2678
2679 // TODO order should match across backends if possible
2680 if (opts.use_llvm) {
2681 expectLinkErrors(exe, test_step, .{ .exact = &.{
2682 "error: undefined symbol: _foo",
2683 "note: referenced by /?/a.o:_bar",
2684 "note: referenced by /?/main_zcu.o:_main.main",
2685 } });
2686 } else {
2687 expectLinkErrors(exe, test_step, .{ .exact = &.{
2688 "error: undefined symbol: _foo",
2689 "note: referenced by /?/main.o:_main.main",
2690 "note: referenced by /?/a.o:__TEXT$__text_zig",
2691 } });
2692 }
2693
2694 return test_step;
2695}
2696
2697fn testUnresolvedError2(b: *Build, opts: Options) *Step {
2698 const test_step = addTestStep(b, "unresolved-error-2", opts);
2699
2700 const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes =
2701 \\pub fn main() !void {
2702 \\ const msg_send_fn = @extern(
2703 \\ *const fn () callconv(.c) usize,
2704 \\ .{ .name = "objc_msgSend$initWithContentRect:styleMask:backing:defer:screen:" },
2705 \\ );
2706 \\ _ = @call(
2707 \\ .auto,
2708 \\ msg_send_fn,
2709 \\ .{},
2710 \\ );
2711 \\}
2712 });
2713
2714 expectLinkErrors(exe, test_step, .{ .exact = &.{
2715 "error: undefined symbol: _objc_msgSend",
2716 "note: referenced implicitly",
2717 } });
2718
2719 return test_step;
2720}
2721
2722fn testUnwindInfo(b: *Build, opts: Options) *Step {
2723 const test_step = addTestStep(b, "unwind-info", opts);
2724
2725 const all_h = all_h: {
2726 const wf = WriteFile.create(b);
2727 break :all_h wf.add("all.h",
2728 \\#ifndef ALL
2729 \\#define ALL
2730 \\
2731 \\#include <cstddef>
2732 \\#include <string>
2733 \\#include <stdexcept>
2734 \\
2735 \\struct SimpleString {
2736 \\ SimpleString(size_t max_size);
2737 \\ ~SimpleString();
2738 \\
2739 \\ void print(const char* tag) const;
2740 \\ bool append_line(const char* x);
2741 \\
2742 \\private:
2743 \\ size_t max_size;
2744 \\ char* buffer;
2745 \\ size_t length;
2746 \\};
2747 \\
2748 \\struct SimpleStringOwner {
2749 \\ SimpleStringOwner(const char* x);
2750 \\ ~SimpleStringOwner();
2751 \\
2752 \\private:
2753 \\ SimpleString string;
2754 \\};
2755 \\
2756 \\class Error: public std::exception {
2757 \\public:
2758 \\ explicit Error(const char* msg) : msg{ msg } {}
2759 \\ virtual ~Error() noexcept {}
2760 \\ virtual const char* what() const noexcept {
2761 \\ return msg.c_str();
2762 \\ }
2763 \\
2764 \\protected:
2765 \\ std::string msg;
2766 \\};
2767 \\
2768 \\#endif
2769 );
2770 };
2771
2772 const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes =
2773 \\#include "all.h"
2774 \\#include <cstdio>
2775 \\
2776 \\void fn_c() {
2777 \\ SimpleStringOwner c{ "cccccccccc" };
2778 \\}
2779 \\
2780 \\void fn_b() {
2781 \\ SimpleStringOwner b{ "b" };
2782 \\ fn_c();
2783 \\}
2784 \\
2785 \\int main() {
2786 \\ try {
2787 \\ SimpleStringOwner a{ "a" };
2788 \\ fn_b();
2789 \\ SimpleStringOwner d{ "d" };
2790 \\ } catch (const Error& e) {
2791 \\ printf("Error: %s\n", e.what());
2792 \\ } catch(const std::exception& e) {
2793 \\ printf("Exception: %s\n", e.what());
2794 \\ }
2795 \\ return 0;
2796 \\}
2797 });
2798 main_o.root_module.addIncludePath(all_h.dirname());
2799 main_o.root_module.link_libcpp = true;
2800
2801 const simple_string_o = addObject(b, opts, .{ .name = "simple_string", .cpp_source_bytes =
2802 \\#include "all.h"
2803 \\#include <cstdio>
2804 \\#include <cstring>
2805 \\
2806 \\SimpleString::SimpleString(size_t max_size)
2807 \\: max_size{ max_size }, length{} {
2808 \\ if (max_size == 0) {
2809 \\ throw Error{ "Max size must be at least 1." };
2810 \\ }
2811 \\ buffer = new char[max_size];
2812 \\ buffer[0] = 0;
2813 \\}
2814 \\
2815 \\SimpleString::~SimpleString() {
2816 \\ delete[] buffer;
2817 \\}
2818 \\
2819 \\void SimpleString::print(const char* tag) const {
2820 \\ printf("%s: %s", tag, buffer);
2821 \\}
2822 \\
2823 \\bool SimpleString::append_line(const char* x) {
2824 \\ const auto x_len = strlen(x);
2825 \\ if (x_len + length + 2 > max_size) return false;
2826 \\ std::strncpy(buffer + length, x, max_size - length);
2827 \\ length += x_len;
2828 \\ buffer[length++] = '\n';
2829 \\ buffer[length] = 0;
2830 \\ return true;
2831 \\}
2832 });
2833 simple_string_o.root_module.addIncludePath(all_h.dirname());
2834 simple_string_o.root_module.link_libcpp = true;
2835
2836 const simple_string_owner_o = addObject(b, opts, .{ .name = "simple_string_owner", .cpp_source_bytes =
2837 \\#include "all.h"
2838 \\
2839 \\SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } {
2840 \\ if (!string.append_line(x)) {
2841 \\ throw Error{ "Not enough memory!" };
2842 \\ }
2843 \\ string.print("Constructed");
2844 \\}
2845 \\
2846 \\SimpleStringOwner::~SimpleStringOwner() {
2847 \\ string.print("About to destroy");
2848 \\}
2849 });
2850 simple_string_owner_o.root_module.addIncludePath(all_h.dirname());
2851 simple_string_owner_o.root_module.link_libcpp = true;
2852
2853 const exp_stdout =
2854 \\Constructed: a
2855 \\Constructed: b
2856 \\About to destroy: b
2857 \\About to destroy: a
2858 \\Error: Not enough memory!
2859 \\
2860 ;
2861
2862 const exe = addExecutable(b, opts, .{ .name = "main" });
2863 exe.root_module.addObject(main_o);
2864 exe.root_module.addObject(simple_string_o);
2865 exe.root_module.addObject(simple_string_owner_o);
2866 exe.root_module.link_libcpp = true;
2867
2868 const run = addRunArtifact(exe);
2869 run.expectStdOutEqual(exp_stdout);
2870 test_step.dependOn(&run.step);
2871
2872 const check = exe.checkObject();
2873 check.checkInSymtab();
2874 check.checkContains("(was private external) ___gxx_personality_v0");
2875 test_step.dependOn(&check.step);
2876
2877 return test_step;
2878}
2879
2880fn testEhFramePointerEncodingSdata4(b: *Build, opts: Options) *Step {
2881 const test_step = addTestStep(b, "eh_frame-pointer-encoding-sdata4", opts);
2882
2883 const a_o = addObject(b, opts, .{ .name = "foo", .asm_source_bytes =
2884 \\.global _foo
2885 \\.align 2
2886 \\_foo:
2887 \\ mov w0, #100
2888 \\ ret
2889 \\LEND_foo:
2890 \\
2891 \\.section __TEXT,__gcc_except_tab
2892 \\LLSDA_foo:
2893 \\ .byte 0xff
2894 \\ .byte 0xff
2895 \\ .byte 0x01
2896 \\ .uleb128 0
2897 \\
2898 \\.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
2899 \\LCIE:
2900 \\ .long LCIE_end - LCIE_start
2901 \\LCIE_start:
2902 \\ .long 0 ; CIE ID
2903 \\ .byte 1 ; Version
2904 \\ .asciz "zLR" ; Augmentation string
2905 \\ .uleb128 1 ; Code alignment factor
2906 \\ .sleb128 -8 ; Data alignment factor
2907 \\ .byte 30 ; Return address register
2908 \\ .uleb128 2 ; Augmentation data length
2909 \\ .byte 0x1b ; LSDA pointer encoding (DW_EH_PE_pcrel | DW_EH_PE_sdata4)
2910 \\ .byte 0x1b ; FDE pointer encoding (DW_EH_PE_pcrel | DW_EH_PE_sdata4)
2911 \\ .byte 0x0c ; DW_CFA_def_cfa
2912 \\ .uleb128 31 ; Reg 31
2913 \\ .uleb128 0 ; Offset 0
2914 \\ .align 3
2915 \\LCIE_end:
2916 \\LFDE:
2917 \\ .long LFDE_end - LFDE_start
2918 \\LFDE_start:
2919 \\ .long LFDE_start - LCIE ; CIE pointer
2920 \\ .long _foo - . ; PC begin
2921 \\ .long LEND_foo - _foo ; PC range
2922 \\ .uleb128 4 ; Augmentation data length
2923 \\ .long LLSDA_foo - . ; LSDA pointer
2924 \\ .align 3
2925 \\LFDE_end:
2926 });
2927
2928 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2929 \\#include <stdio.h>
2930 \\int foo();
2931 \\int main() {
2932 \\ printf("%d\n", foo());
2933 \\ return 0;
2934 \\}
2935 });
2936 exe.root_module.addObject(a_o);
2937
2938 const run = addRunArtifact(exe);
2939 run.expectStdOutEqual("100\n");
2940 test_step.dependOn(&run.step);
2941
2942 return test_step;
2943}
2944
2945fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step {
2946 const test_step = addTestStep(b, "unwind-info-no-subsections-arm64", opts);
2947
2948 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
2949 \\.globl _foo
2950 \\.align 4
2951 \\_foo:
2952 \\ .cfi_startproc
2953 \\ stp x29, x30, [sp, #-32]!
2954 \\ .cfi_def_cfa_offset 32
2955 \\ .cfi_offset w30, -24
2956 \\ .cfi_offset w29, -32
2957 \\ mov x29, sp
2958 \\ .cfi_def_cfa w29, 32
2959 \\ bl _bar
2960 \\ ldp x29, x30, [sp], #32
2961 \\ .cfi_restore w29
2962 \\ .cfi_restore w30
2963 \\ .cfi_def_cfa_offset 0
2964 \\ ret
2965 \\ .cfi_endproc
2966 \\
2967 \\.globl _bar
2968 \\.align 4
2969 \\_bar:
2970 \\ .cfi_startproc
2971 \\ sub sp, sp, #32
2972 \\ .cfi_def_cfa_offset -32
2973 \\ stp x29, x30, [sp, #16]
2974 \\ .cfi_offset w30, -24
2975 \\ .cfi_offset w29, -32
2976 \\ mov x29, sp
2977 \\ .cfi_def_cfa w29, 32
2978 \\ mov w0, #4
2979 \\ ldp x29, x30, [sp, #16]
2980 \\ .cfi_restore w29
2981 \\ .cfi_restore w30
2982 \\ add sp, sp, #32
2983 \\ .cfi_def_cfa_offset 0
2984 \\ ret
2985 \\ .cfi_endproc
2986 });
2987
2988 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
2989 \\#include <stdio.h>
2990 \\int foo();
2991 \\int main() {
2992 \\ printf("%d\n", foo());
2993 \\ return 0;
2994 \\}
2995 });
2996 exe.root_module.addObject(a_o);
2997
2998 const run = addRunArtifact(exe);
2999 run.expectStdOutEqual("4\n");
3000 test_step.dependOn(&run.step);
3001
3002 return test_step;
3003}
3004
3005fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step {
3006 const test_step = addTestStep(b, "unwind-info-no-subsections-x64", opts);
3007
3008 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
3009 \\.globl _foo
3010 \\_foo:
3011 \\ .cfi_startproc
3012 \\ push %rbp
3013 \\ .cfi_def_cfa_offset 8
3014 \\ .cfi_offset %rbp, -8
3015 \\ mov %rsp, %rbp
3016 \\ .cfi_def_cfa_register %rbp
3017 \\ call _bar
3018 \\ pop %rbp
3019 \\ .cfi_restore %rbp
3020 \\ .cfi_def_cfa_offset 0
3021 \\ ret
3022 \\ .cfi_endproc
3023 \\
3024 \\.globl _bar
3025 \\_bar:
3026 \\ .cfi_startproc
3027 \\ push %rbp
3028 \\ .cfi_def_cfa_offset 8
3029 \\ .cfi_offset %rbp, -8
3030 \\ mov %rsp, %rbp
3031 \\ .cfi_def_cfa_register %rbp
3032 \\ mov $4, %rax
3033 \\ pop %rbp
3034 \\ .cfi_restore %rbp
3035 \\ .cfi_def_cfa_offset 0
3036 \\ ret
3037 \\ .cfi_endproc
3038 });
3039
3040 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
3041 \\#include <stdio.h>
3042 \\int foo();
3043 \\int main() {
3044 \\ printf("%d\n", foo());
3045 \\ return 0;
3046 \\}
3047 });
3048 exe.root_module.addObject(a_o);
3049
3050 const run = addRunArtifact(exe);
3051 run.expectStdOutEqual("4\n");
3052 test_step.dependOn(&run.step);
3053
3054 return test_step;
3055}
3056
3057// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s
3058fn testWeakBind(b: *Build, opts: Options) *Step {
3059 const test_step = addTestStep(b, "weak-bind", opts);
3060
3061 const lib = addSharedLibrary(b, opts, .{ .name = "foo", .asm_source_bytes =
3062 \\.globl _weak_dysym
3063 \\.weak_definition _weak_dysym
3064 \\_weak_dysym:
3065 \\ .quad 0x1234
3066 \\
3067 \\.globl _weak_dysym_for_gotpcrel
3068 \\.weak_definition _weak_dysym_for_gotpcrel
3069 \\_weak_dysym_for_gotpcrel:
3070 \\ .quad 0x1234
3071 \\
3072 \\.globl _weak_dysym_fn
3073 \\.weak_definition _weak_dysym_fn
3074 \\_weak_dysym_fn:
3075 \\ ret
3076 \\
3077 \\.section __DATA,__thread_vars,thread_local_variables
3078 \\
3079 \\.globl _weak_dysym_tlv
3080 \\.weak_definition _weak_dysym_tlv
3081 \\_weak_dysym_tlv:
3082 \\ .quad 0x1234
3083 });
3084
3085 {
3086 const check = lib.checkObject();
3087 check.checkInExports();
3088 check.checkExtract("[WEAK] {vmaddr1} _weak_dysym");
3089 check.checkExtract("[WEAK] {vmaddr2} _weak_dysym_for_gotpcrel");
3090 check.checkExtract("[WEAK] {vmaddr3} _weak_dysym_fn");
3091 check.checkExtract("[THREAD_LOCAL, WEAK] {vmaddr4} _weak_dysym_tlv");
3092 test_step.dependOn(&check.step);
3093 }
3094
3095 const exe = addExecutable(b, opts, .{ .name = "main", .asm_source_bytes =
3096 \\.globl _main, _weak_external, _weak_external_for_gotpcrel, _weak_external_fn
3097 \\.weak_definition _weak_external, _weak_external_for_gotpcrel, _weak_external_fn, _weak_internal, _weak_internal_for_gotpcrel, _weak_internal_fn
3098 \\
3099 \\_main:
3100 \\ mov _weak_dysym_for_gotpcrel@GOTPCREL(%rip), %rax
3101 \\ mov _weak_external_for_gotpcrel@GOTPCREL(%rip), %rax
3102 \\ mov _weak_internal_for_gotpcrel@GOTPCREL(%rip), %rax
3103 \\ mov _weak_tlv@TLVP(%rip), %rax
3104 \\ mov _weak_dysym_tlv@TLVP(%rip), %rax
3105 \\ mov _weak_internal_tlv@TLVP(%rip), %rax
3106 \\ callq _weak_dysym_fn
3107 \\ callq _weak_external_fn
3108 \\ callq _weak_internal_fn
3109 \\ mov $0, %rax
3110 \\ ret
3111 \\
3112 \\_weak_external:
3113 \\ .quad 0x1234
3114 \\
3115 \\_weak_external_for_gotpcrel:
3116 \\ .quad 0x1234
3117 \\
3118 \\_weak_external_fn:
3119 \\ ret
3120 \\
3121 \\_weak_internal:
3122 \\ .quad 0x1234
3123 \\
3124 \\_weak_internal_for_gotpcrel:
3125 \\ .quad 0x1234
3126 \\
3127 \\_weak_internal_fn:
3128 \\ ret
3129 \\
3130 \\.data
3131 \\ .quad _weak_dysym
3132 \\ .quad _weak_external + 2
3133 \\ .quad _weak_internal
3134 \\
3135 \\.tbss _weak_tlv$tlv$init, 4, 2
3136 \\.tbss _weak_internal_tlv$tlv$init, 4, 2
3137 \\
3138 \\.section __DATA,__thread_vars,thread_local_variables
3139 \\.globl _weak_tlv
3140 \\.weak_definition _weak_tlv, _weak_internal_tlv
3141 \\
3142 \\_weak_tlv:
3143 \\ .quad __tlv_bootstrap
3144 \\ .quad 0
3145 \\ .quad _weak_tlv$tlv$init
3146 \\
3147 \\_weak_internal_tlv:
3148 \\ .quad __tlv_bootstrap
3149 \\ .quad 0
3150 \\ .quad _weak_internal_tlv$tlv$init
3151 });
3152 exe.root_module.linkLibrary(lib);
3153
3154 {
3155 const check = exe.checkObject();
3156
3157 check.checkInExports();
3158 check.checkExtract("[WEAK] {vmaddr1} _weak_external");
3159 check.checkExtract("[WEAK] {vmaddr2} _weak_external_for_gotpcrel");
3160 check.checkExtract("[WEAK] {vmaddr3} _weak_external_fn");
3161 check.checkExtract("[THREAD_LOCAL, WEAK] {vmaddr4} _weak_tlv");
3162
3163 check.checkInDyldBind();
3164 check.checkContains("(libfoo.dylib) _weak_dysym_for_gotpcrel");
3165 check.checkContains("(libfoo.dylib) _weak_dysym_fn");
3166 check.checkContains("(libfoo.dylib) _weak_dysym");
3167 check.checkContains("(libfoo.dylib) _weak_dysym_tlv");
3168
3169 check.checkInDyldWeakBind();
3170 check.checkContains("_weak_external_for_gotpcrel");
3171 check.checkContains("_weak_dysym_for_gotpcrel");
3172 check.checkContains("_weak_external_fn");
3173 check.checkContains("_weak_dysym_fn");
3174 check.checkContains("_weak_dysym");
3175 check.checkContains("_weak_external");
3176 check.checkContains("_weak_tlv");
3177 check.checkContains("_weak_dysym_tlv");
3178
3179 test_step.dependOn(&check.step);
3180 }
3181
3182 const run = addRunArtifact(exe);
3183 run.expectExitCode(0);
3184 test_step.dependOn(&run.step);
3185
3186 return test_step;
3187}
3188
3189fn testWeakFramework(b: *Build, opts: Options) *Step {
3190 const test_step = addTestStep(b, "weak-framework", opts);
3191
3192 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" });
3193 exe.root_module.linkFramework("Cocoa", .{ .weak = true });
3194
3195 const run = addRunArtifact(exe);
3196 run.expectExitCode(0);
3197 test_step.dependOn(&run.step);
3198
3199 const check = exe.checkObject();
3200 check.checkInHeaders();
3201 check.checkExact("cmd LOAD_WEAK_DYLIB");
3202 check.checkContains("Cocoa");
3203 test_step.dependOn(&check.step);
3204
3205 return test_step;
3206}
3207
3208fn testWeakLibrary(b: *Build, opts: Options) *Step {
3209 const test_step = addTestStep(b, "weak-library", opts);
3210
3211 const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes =
3212 \\#include<stdio.h>
3213 \\int a = 42;
3214 \\const char* asStr() {
3215 \\ static char str[3];
3216 \\ sprintf(str, "%d", 42);
3217 \\ return str;
3218 \\}
3219 });
3220
3221 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
3222 \\#include<stdio.h>
3223 \\extern int a;
3224 \\extern const char* asStr();
3225 \\int main() {
3226 \\ printf("%d %s", a, asStr());
3227 \\ return 0;
3228 \\}
3229 });
3230 exe.root_module.linkSystemLibrary("a", .{ .weak = true });
3231 exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory());
3232 exe.root_module.addRPath(dylib.getEmittedBinDirectory());
3233
3234 const check = exe.checkObject();
3235 check.checkInHeaders();
3236 check.checkExact("cmd LOAD_WEAK_DYLIB");
3237 check.checkContains("liba.dylib");
3238 check.checkInSymtab();
3239 check.checkExact("(undefined) weakref external _a (from liba)");
3240 check.checkInSymtab();
3241 check.checkExact("(undefined) weakref external _asStr (from liba)");
3242 test_step.dependOn(&check.step);
3243
3244 const run = addRunArtifact(exe);
3245 run.expectStdOutEqual("42 42");
3246 test_step.dependOn(&run.step);
3247
3248 return test_step;
3249}
3250
3251fn testWeakRef(b: *Build, opts: Options) *Step {
3252 const test_step = addTestStep(b, "weak-ref", opts);
3253
3254 const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes =
3255 \\#include <stdio.h>
3256 \\#include <sys/_types/_fd_def.h>
3257 \\int main(int argc, char** argv) {
3258 \\ printf("__darwin_check_fd_set_overflow: %p\n", __darwin_check_fd_set_overflow);
3259 \\}
3260 });
3261
3262 const check = exe.checkObject();
3263 check.checkInSymtab();
3264 check.checkExact("(undefined) weakref external ___darwin_check_fd_set_overflow (from libSystem.B)");
3265 test_step.dependOn(&check.step);
3266
3267 return test_step;
3268}
3269
3270fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
3271 return link.addTestStep(b, "" ++ prefix, opts);
3272}
3273
3274const builtin = @import("builtin");
3275const addAsmSourceBytes = link.addAsmSourceBytes;
3276const addCSourceBytes = link.addCSourceBytes;
3277const addRunArtifact = link.addRunArtifact;
3278const addObject = link.addObject;
3279const addExecutable = link.addExecutable;
3280const addStaticLibrary = link.addStaticLibrary;
3281const addSharedLibrary = link.addSharedLibrary;
3282const expectLinkErrors = link.expectLinkErrors;
3283const link = @import("link.zig");
3284const std = @import("std");
3285
3286const Build = std.Build;
3287const BuildOptions = link.BuildOptions;
3288const Compile = Step.Compile;
3289const Options = link.Options;
3290const Step = Build.Step;
3291const WriteFile = Step.WriteFile;
test/link/static_libs_from_object_files/build.zig deleted-163
...@@ -1,163 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4const Build = std.Build;
5const LazyPath = Build.LazyPath;
6const Step = Build.Step;
7const Run = Step.Run;
8const WriteFile = Step.WriteFile;
9
10pub fn build(b: *Build) void {
11 const nb_files = b.option(u32, "nb_files", "Number of c files to generate.") orelse 10;
12
13 const test_step = b.step("test", "Test it");
14 b.default_step = test_step;
15
16 // generate c files
17 const files = b.allocator.alloc(LazyPath, nb_files) catch unreachable;
18 defer b.allocator.free(files);
19 {
20 for (files[0 .. nb_files - 1], 1..nb_files) |*file, i| {
21 const wf = WriteFile.create(b);
22 file.* = wf.add(b.fmt("src_{}.c", .{i}), b.fmt(
23 \\extern int foo_0();
24 \\extern int bar_{}();
25 \\extern int one_{};
26 \\int one_{} = 1;
27 \\int foo_{}() {{ return one_{} + foo_0(); }}
28 \\int bar_{}() {{ return bar_{}(); }}
29 , .{ i - 1, i - 1, i, i, i - 1, i, i - 1 }));
30 }
31
32 {
33 const wf = WriteFile.create(b);
34 files[nb_files - 1] = wf.add("src_last.c", b.fmt(
35 \\extern int foo_0();
36 \\extern int bar_{}();
37 \\extern int one_{};
38 \\int foo_last() {{ return one_{} + foo_0(); }}
39 \\int bar_last() {{ return bar_{}(); }}
40 , .{ nb_files - 1, nb_files - 1, nb_files - 1, nb_files - 1 }));
41 }
42 }
43
44 add(b, test_step, files, .Debug);
45 add(b, test_step, files, .ReleaseSafe);
46 add(b, test_step, files, .ReleaseSmall);
47 add(b, test_step, files, .ReleaseFast);
48}
49
50fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.builtin.OptimizeMode) void {
51 const flags = [_][]const u8{
52 "-Wall",
53 "-std=c11",
54 };
55
56 // all files at once
57 {
58 const exe = b.addExecutable(.{
59 .name = "test1",
60 .root_module = b.createModule(.{
61 .root_source_file = b.path("main.zig"),
62 .optimize = optimize,
63 .target = b.graph.host,
64 }),
65 });
66
67 for (files) |file| {
68 exe.root_module.addCSourceFile(.{ .file = file, .flags = &flags });
69 }
70
71 const run_cmd = b.addRunArtifact(exe);
72 run_cmd.skip_foreign_checks = true;
73 run_cmd.expectExitCode(0);
74
75 test_step.dependOn(&run_cmd.step);
76 }
77
78 // using static librairies
79 {
80 const mod_a = b.createModule(.{ .target = b.graph.host, .optimize = optimize });
81 const mod_b = b.createModule(.{ .target = b.graph.host, .optimize = optimize });
82
83 for (files, 1..) |file, i| {
84 const mod = if (i & 1 == 0) mod_a else mod_b;
85 mod.addCSourceFile(.{ .file = file, .flags = &flags });
86 }
87
88 const lib_a = b.addLibrary(.{
89 .linkage = .static,
90 .name = "test2_a",
91 .root_module = mod_a,
92 });
93 const lib_b = b.addLibrary(.{
94 .linkage = .static,
95 .name = "test2_b",
96 .root_module = mod_b,
97 });
98
99 const exe = b.addExecutable(.{
100 .name = "test2",
101 .root_module = b.createModule(.{
102 .root_source_file = b.path("main.zig"),
103 .target = b.graph.host,
104 .optimize = optimize,
105 }),
106 });
107 exe.root_module.linkLibrary(lib_a);
108 exe.root_module.linkLibrary(lib_b);
109
110 const run_cmd = b.addRunArtifact(exe);
111 run_cmd.skip_foreign_checks = true;
112 run_cmd.expectExitCode(0);
113
114 test_step.dependOn(&run_cmd.step);
115 }
116
117 // using static librairies and object files
118 {
119 const mod_a = b.createModule(.{ .target = b.graph.host, .optimize = optimize });
120 const mod_b = b.createModule(.{ .target = b.graph.host, .optimize = optimize });
121
122 for (files, 1..) |file, i| {
123 const obj_mod = b.createModule(.{ .target = b.graph.host, .optimize = optimize });
124 obj_mod.addCSourceFile(.{ .file = file, .flags = &flags });
125
126 const obj = b.addObject(.{
127 .name = b.fmt("obj_{}", .{i}),
128 .root_module = obj_mod,
129 });
130
131 const lib_mod = if (i & 1 == 0) mod_a else mod_b;
132 lib_mod.addObject(obj);
133 }
134
135 const lib_a = b.addLibrary(.{
136 .linkage = .static,
137 .name = "test3_a",
138 .root_module = mod_a,
139 });
140 const lib_b = b.addLibrary(.{
141 .linkage = .static,
142 .name = "test3_b",
143 .root_module = mod_b,
144 });
145
146 const exe = b.addExecutable(.{
147 .name = "test3",
148 .root_module = b.createModule(.{
149 .root_source_file = b.path("main.zig"),
150 .target = b.graph.host,
151 .optimize = optimize,
152 }),
153 });
154 exe.root_module.linkLibrary(lib_a);
155 exe.root_module.linkLibrary(lib_b);
156
157 const run_cmd = b.addRunArtifact(exe);
158 run_cmd.skip_foreign_checks = true;
159 run_cmd.expectExitCode(0);
160
161 test_step.dependOn(&run_cmd.step);
162 }
163}
test/link/static_libs_from_object_files/main.zig deleted-20
...@@ -1,20 +0,0 @@
1const std = @import("std");
2
3extern fn foo_last() i32;
4extern fn bar_last() i32;
5
6export const one_0: i32 = 1;
7
8export fn foo_0() i32 {
9 return 1234;
10}
11export fn bar_0() i32 {
12 return 5678;
13}
14
15pub fn main() anyerror!void {
16 const foo_expected: i32 = 1 + 1234;
17 const bar_expected: i32 = 5678;
18 try std.testing.expectEqual(foo_expected, foo_last());
19 try std.testing.expectEqual(bar_expected, bar_last());
20}
test/link/wasm/archive/build.zig deleted-36
...@@ -1,36 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 // The code in question will pull-in compiler-rt,
15 // and therefore link with its archive file.
16 const lib = b.addExecutable(.{
17 .name = "main",
18 .root_module = b.createModule(.{
19 .root_source_file = b.path("main.zig"),
20 .optimize = optimize,
21 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
22 .strip = false,
23 }),
24 });
25 lib.entry = .disabled;
26 lib.use_llvm = false;
27 lib.use_lld = false;
28 lib.root_module.export_symbol_names = &.{"foo"};
29
30 const check = lib.checkObject();
31 check.checkInHeaders();
32 check.checkExact("Section custom");
33 check.checkExact("name __trunch"); // Ensure it was imported and resolved
34
35 test_step.dependOn(&check.step);
36}
test/link/wasm/archive/main.zig deleted-7
...@@ -1,7 +0,0 @@
1export fn foo() void {
2 var a: f16 = 2.2;
3 _ = &a;
4 // this will pull-in compiler-rt
5 const b = @trunc(a);
6 _ = b;
7}
test/link/wasm/basic-features/build.zig deleted-32
...@@ -1,32 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 // Library with explicitly set cpu features
5 const lib = b.addExecutable(.{
6 .name = "lib",
7 .root_module = b.createModule(.{
8 .root_source_file = b.path("main.zig"),
9 .optimize = .Debug,
10 .target = b.resolveTargetQuery(.{
11 .cpu_arch = .wasm32,
12 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
13 .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}),
14 .os_tag = .freestanding,
15 }),
16 }),
17 });
18 lib.entry = .disabled;
19 lib.use_llvm = false;
20 lib.use_lld = false;
21
22 // Verify the result contains the features explicitly set on the target for the library.
23 const check = lib.checkObject();
24 check.checkInHeaders();
25 check.checkExact("name target_features");
26 check.checkExact("features 1");
27 check.checkExact("+ atomics");
28
29 const test_step = b.step("test", "Run linker test");
30 test_step.dependOn(&check.step);
31 b.default_step = test_step;
32}
test/link/wasm/basic-features/main.zig deleted-1
...@@ -1 +0,0 @@
1export fn foo() void {}
test/link/wasm/export-data/build.zig deleted-30
...@@ -1,30 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 b.default_step = test_step;
6
7 const lib = b.addExecutable(.{
8 .name = "lib",
9 .root_module = b.createModule(.{
10 .root_source_file = b.path("lib.zig"),
11 .optimize = .Debug,
12 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
13 }),
14 });
15 lib.entry = .disabled;
16 // Disabled to work around the Wasm linker crashing.
17 // Can be reproduced by commenting out the line below.
18 lib.bundle_ubsan_rt = false;
19 lib.use_lld = false;
20 lib.root_module.export_symbol_names = &.{ "foo", "bar" };
21 // Object being linked has neither functions nor globals named "foo" or "bar" and
22 // so these names correctly fail to be exported when creating an executable.
23 lib.expect_errors = .{ .exact = &.{
24 "error: manually specified export name 'foo' undefined",
25 "error: manually specified export name 'bar' undefined",
26 } };
27 _ = lib.getEmittedBin();
28
29 test_step.dependOn(&lib.step);
30}
test/link/wasm/export-data/lib.zig deleted-2
...@@ -1,2 +0,0 @@
1export const foo: u32 = 0xbbbbbbbb;
2export const bar: u32 = 0xbbbbbbbb;
test/link/wasm/export/build.zig deleted-79
...@@ -1,79 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8}
9
10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
11 const no_export = b.addExecutable(.{
12 .name = "no-export",
13 .root_module = b.createModule(.{
14 .root_source_file = b.path("main-hidden.zig"),
15 .optimize = optimize,
16 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
17 }),
18 });
19 no_export.entry = .disabled;
20 no_export.use_llvm = false;
21 no_export.use_lld = false;
22 // Don't pull in ubsan, since we're just expecting a very minimal executable.
23 no_export.bundle_ubsan_rt = false;
24
25 const dynamic_export = b.addExecutable(.{
26 .name = "dynamic",
27 .root_module = b.createModule(.{
28 .root_source_file = b.path("main.zig"),
29 .optimize = optimize,
30 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
31 }),
32 });
33 dynamic_export.entry = .disabled;
34 dynamic_export.rdynamic = true;
35 dynamic_export.use_llvm = false;
36 dynamic_export.use_lld = false;
37 // Don't pull in ubsan, since we're just expecting a very minimal executable.
38 dynamic_export.bundle_ubsan_rt = false;
39
40 const force_export = b.addExecutable(.{
41 .name = "force",
42 .root_module = b.createModule(.{
43 .root_source_file = b.path("main-hidden.zig"),
44 .optimize = optimize,
45 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
46 }),
47 });
48 force_export.entry = .disabled;
49 force_export.root_module.export_symbol_names = &.{"foo"};
50 force_export.use_llvm = false;
51 force_export.use_lld = false;
52 // Don't pull in ubsan, since we're just expecting a very minimal executable.
53 force_export.bundle_ubsan_rt = false;
54
55 const check_no_export = no_export.checkObject();
56 check_no_export.checkInHeaders();
57 check_no_export.checkExact("Section export");
58 check_no_export.checkExact("entries 1");
59 check_no_export.checkExact("name memory");
60 check_no_export.checkExact("kind memory");
61
62 const check_dynamic_export = dynamic_export.checkObject();
63 check_dynamic_export.checkInHeaders();
64 check_dynamic_export.checkExact("Section export");
65 check_dynamic_export.checkExact("entries 2");
66 check_dynamic_export.checkExact("name foo");
67 check_dynamic_export.checkExact("kind function");
68
69 const check_force_export = force_export.checkObject();
70 check_force_export.checkInHeaders();
71 check_force_export.checkExact("Section export");
72 check_force_export.checkExact("entries 2");
73 check_force_export.checkExact("name foo");
74 check_force_export.checkExact("kind function");
75
76 test_step.dependOn(&check_no_export.step);
77 test_step.dependOn(&check_dynamic_export.step);
78 test_step.dependOn(&check_force_export.step);
79}
test/link/wasm/export/main-hidden.zig deleted-4
...@@ -1,4 +0,0 @@
1fn foo() callconv(.c) void {}
2comptime {
3 @export(&foo, .{ .name = "foo", .visibility = .hidden });
4}
test/link/wasm/export/main.zig deleted-1
...@@ -1 +0,0 @@
1export fn foo() void {}
test/link/wasm/extern-mangle/a.zig deleted-1
...@@ -1 +0,0 @@
1pub extern "a" fn hello() i32;
test/link/wasm/extern-mangle/b.zig deleted-1
...@@ -1 +0,0 @@
1pub extern "b" fn hello() i32;
test/link/wasm/extern-mangle/build.zig deleted-36
...@@ -1,36 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 const lib = b.addExecutable(.{
15 .name = "lib",
16 .root_module = b.createModule(.{
17 .root_source_file = b.path("lib.zig"),
18 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
19 .optimize = optimize,
20 }),
21 });
22 lib.entry = .disabled;
23 lib.import_symbols = true; // import `a` and `b`
24 lib.rdynamic = true; // export `foo`
25
26 const check_lib = lib.checkObject();
27 check_lib.checkInHeaders();
28 check_lib.checkExact("Section import");
29 check_lib.checkExact("entries 2"); // a.hello & b.hello
30 check_lib.checkExact("module a");
31 check_lib.checkExact("name hello");
32 check_lib.checkExact("module b");
33 check_lib.checkExact("name hello");
34
35 test_step.dependOn(&check_lib.step);
36}
test/link/wasm/extern-mangle/lib.zig deleted-6
...@@ -1,6 +0,0 @@
1const a = @import("a.zig").hello;
2const b = @import("b.zig").hello;
3export fn foo() void {
4 _ = a();
5 _ = b();
6}
test/link/wasm/extern/build.zig deleted-28
...@@ -1,28 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8}
9
10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
11 const exe = b.addExecutable(.{
12 .name = "extern",
13 .root_module = b.createModule(.{
14 .root_source_file = b.path("main.zig"),
15 .optimize = optimize,
16 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi }),
17 }),
18 });
19 exe.root_module.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} });
20 exe.use_llvm = false;
21 exe.use_lld = false;
22
23 const run = b.addRunArtifact(exe);
24 run.skip_foreign_checks = true;
25 run.expectStdOutEqual("Result: 30");
26
27 test_step.dependOn(&run.step);
28}
test/link/wasm/extern/foo.c deleted-1
...@@ -1 +0,0 @@
1int foo = 30;
test/link/wasm/extern/main.zig deleted-8
...@@ -1,8 +0,0 @@
1const std = @import("std");
2
3extern const foo: u32;
4
5pub fn main() void {
6 var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{});
7 stdout_writer.interface.print("Result: {d}", .{foo}) catch {};
8}
test/link/wasm/function-table/build.zig deleted-67
...@@ -1,67 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8}
9
10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
11 const export_table = b.addExecutable(.{
12 .name = "export_table",
13 .root_module = b.createModule(.{
14 .root_source_file = b.path("lib.zig"),
15 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
16 .optimize = optimize,
17 }),
18 });
19 export_table.entry = .disabled;
20 export_table.use_llvm = false;
21 export_table.use_lld = false;
22 export_table.export_table = true;
23 export_table.link_gc_sections = false;
24 // Don't pull in ubsan, since we're just expecting a very minimal executable.
25 export_table.bundle_ubsan_rt = false;
26
27 const regular_table = b.addExecutable(.{
28 .name = "regular_table",
29 .root_module = b.createModule(.{
30 .root_source_file = b.path("lib.zig"),
31 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
32 .optimize = optimize,
33 }),
34 });
35 regular_table.entry = .disabled;
36 regular_table.use_llvm = false;
37 regular_table.use_lld = false;
38 regular_table.link_gc_sections = false; // Ensure function table is not empty
39 // Don't pull in ubsan, since we're just expecting a very minimal executable.
40 regular_table.bundle_ubsan_rt = false;
41
42 const check_export = export_table.checkObject();
43 const check_regular = regular_table.checkObject();
44
45 check_export.checkInHeaders();
46 check_export.checkExact("Section export");
47 check_export.checkExact("entries 3");
48 check_export.checkExact("name __indirect_function_table"); // as per linker specification
49 check_export.checkExact("kind table");
50
51 check_regular.checkInHeaders();
52 check_regular.checkExact("Section table");
53 check_regular.checkExact("entries 1");
54 check_regular.checkExact("type funcref");
55 check_regular.checkExact("min 2"); // index starts at 1 & 1 function pointer = 2.
56 check_regular.checkExact("max 2");
57
58 check_regular.checkInHeaders();
59 check_regular.checkExact("Section element");
60 check_regular.checkExact("entries 1");
61 check_regular.checkExact("table index 0");
62 check_regular.checkExact("i32.const 1"); // we want to start function indexes at 1
63 check_regular.checkExact("indexes 1"); // 1 function pointer
64
65 test_step.dependOn(&check_export.step);
66 test_step.dependOn(&check_regular.step);
67}
test/link/wasm/function-table/lib.zig deleted-7
...@@ -1,7 +0,0 @@
1var func: *const fn () void = &bar;
2
3export fn foo() void {
4 func();
5}
6
7fn bar() void {}
test/link/wasm/infer-features/build.zig deleted-44
...@@ -1,44 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 // Wasm Object file which we will use to infer the features from
5 const c_obj = b.addObject(.{
6 .name = "c_obj",
7 .root_module = b.createModule(.{
8 .root_source_file = null,
9 .optimize = .Debug,
10 .target = b.resolveTargetQuery(.{
11 .cpu_arch = .wasm32,
12 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },
13 .os_tag = .freestanding,
14 }),
15 }),
16 });
17 c_obj.root_module.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} });
18
19 // Wasm library that doesn't have any features specified. This will
20 // infer its featureset from other linked object files.
21 const lib = b.addExecutable(.{
22 .name = "lib",
23 .root_module = b.createModule(.{
24 .root_source_file = b.path("main.zig"),
25 .optimize = .Debug,
26 .target = b.resolveTargetQuery(.{
27 .cpu_arch = .wasm32,
28 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
29 .os_tag = .freestanding,
30 }),
31 }),
32 });
33 lib.entry = .disabled;
34 lib.use_llvm = false;
35 lib.use_lld = false;
36 lib.root_module.addObject(c_obj);
37
38 lib.expect_errors = .{ .contains = "error: object requires atomics but specified target features exclude atomics" };
39 _ = lib.getEmittedBin();
40
41 const test_step = b.step("test", "Run linker test");
42 test_step.dependOn(&lib.step);
43 b.default_step = test_step;
44}
test/link/wasm/infer-features/foo.c deleted-3
...@@ -1,3 +0,0 @@
1int foo() {
2 return 5;
3}
test/link/wasm/infer-features/main.zig deleted-1
...@@ -1 +0,0 @@
1extern fn foo() c_int;
test/link/wasm/producers/build.zig deleted-45
...@@ -1,45 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4pub fn build(b: *std.Build) void {
5 const test_step = b.step("test", "Test it");
6 b.default_step = test_step;
7
8 add(b, test_step, .Debug);
9 add(b, test_step, .ReleaseFast);
10 add(b, test_step, .ReleaseSmall);
11 add(b, test_step, .ReleaseSafe);
12}
13
14fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
15 const lib = b.addExecutable(.{
16 .name = "lib",
17 .root_module = b.createModule(.{
18 .root_source_file = b.path("lib.zig"),
19 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
20 .optimize = optimize,
21 .strip = false,
22 }),
23 });
24 lib.entry = .disabled;
25 lib.use_llvm = false;
26 lib.use_lld = false;
27 b.installArtifact(lib);
28
29 const version_fmt = "version " ++ builtin.zig_version_string;
30
31 const check_lib = lib.checkObject();
32 check_lib.checkInHeaders();
33 check_lib.checkExact("name producers");
34 check_lib.checkExact("fields 2");
35 check_lib.checkExact("field_name language");
36 check_lib.checkExact("values 1");
37 check_lib.checkExact("value_name Zig");
38 check_lib.checkExact(version_fmt);
39 check_lib.checkExact("field_name processed-by");
40 check_lib.checkExact("values 1");
41 check_lib.checkExact("value_name Zig");
42 check_lib.checkExact(version_fmt);
43
44 test_step.dependOn(&check_lib.step);
45}
test/link/wasm/producers/lib.zig deleted-1
...@@ -1 +0,0 @@
1export fn foo() void {}
test/link/wasm/shared-memory/build.zig deleted-99
...@@ -1,99 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9}
10
11fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void {
12 const exe = b.addExecutable(.{
13 .name = "lib",
14 .root_module = b.createModule(.{
15 .root_source_file = b.path("lib.zig"),
16 .target = b.resolveTargetQuery(.{
17 .cpu_arch = .wasm32,
18 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
19 .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }),
20 .os_tag = .freestanding,
21 }),
22 .optimize = optimize_mode,
23 .strip = false,
24 .single_threaded = false,
25 }),
26 });
27 exe.entry = .disabled;
28 exe.use_lld = false;
29 exe.import_memory = true;
30 exe.export_memory = true;
31 exe.shared_memory = true;
32 exe.max_memory = 67108864;
33 exe.root_module.export_symbol_names = &.{"foo"};
34 // Don't pull in ubsan, since we're just expecting a very minimal executable.
35 exe.bundle_ubsan_rt = false;
36
37 const check_exe = exe.checkObject();
38
39 check_exe.checkInHeaders();
40 check_exe.checkExact("Section import");
41 check_exe.checkExact("entries 1");
42 check_exe.checkExact("module env");
43 check_exe.checkExact("name memory"); // ensure we are importing memory
44
45 check_exe.checkInHeaders();
46 check_exe.checkExact("Section export");
47 check_exe.checkExact("entries 2");
48 check_exe.checkExact("name foo");
49 check_exe.checkExact("name memory"); // ensure we also export memory again
50
51 // This section *must* be emit as the start function is set to the index
52 // of __wasm_init_memory
53 // release modes will have the TLS segment optimized out in our test-case.
54 // This means we won't have __wasm_init_memory in such case, and therefore
55 // should also not have a section "start"
56 if (optimize_mode == .Debug) {
57 check_exe.checkInHeaders();
58 check_exe.checkExact("Section start");
59 }
60
61 // This section is only and *must* be emit when shared-memory is enabled
62 // release modes will have the TLS segment optimized out in our test-case.
63 if (optimize_mode == .Debug) {
64 check_exe.checkInHeaders();
65 check_exe.checkExact("Section data_count");
66 check_exe.checkExact("count 1");
67 }
68
69 check_exe.checkInHeaders();
70 check_exe.checkExact("Section custom");
71 check_exe.checkExact("name name");
72 check_exe.checkExact("type function");
73 if (optimize_mode == .Debug) {
74 check_exe.checkExact("name __wasm_init_memory");
75 check_exe.checkExact("name __wasm_init_tls");
76 }
77 check_exe.checkExact("type global");
78
79 // In debug mode the symbol __tls_base is resolved to an undefined symbol
80 // from the object file, hence its placement differs than in release modes
81 // where the entire tls segment is optimized away, and tls_base will have
82 // its original position.
83 if (optimize_mode == .Debug) {
84 check_exe.checkExact("name __tls_base");
85 check_exe.checkExact("name __tls_size");
86 check_exe.checkExact("name __tls_align");
87
88 check_exe.checkExact("type data_segment");
89 check_exe.checkExact("names 1");
90 check_exe.checkExact("index 0");
91 check_exe.checkExact("name .tdata");
92 } else {
93 check_exe.checkNotPresent("name __tls_base");
94 check_exe.checkNotPresent("name __tls_size");
95 check_exe.checkNotPresent("name __tls_align");
96 }
97
98 test_step.dependOn(&check_exe.step);
99}
test/link/wasm/shared-memory/lib.zig deleted-5
...@@ -1,5 +0,0 @@
1threadlocal var some_tls_global: u32 = 1;
2
3export fn foo() void {
4 some_tls_global = 2;
5}
test/link/wasm/stack_pointer/build.zig deleted-55
...@@ -1,55 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8 add(b, test_step, .ReleaseFast);
9 add(b, test_step, .ReleaseSmall);
10 add(b, test_step, .ReleaseSafe);
11}
12
13fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
14 const lib = b.addExecutable(.{
15 .name = "lib",
16 .root_module = b.createModule(.{
17 .root_source_file = b.path("lib.zig"),
18 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
19 .optimize = optimize,
20 .strip = false,
21 }),
22 });
23 lib.entry = .disabled;
24 lib.use_llvm = false;
25 lib.use_lld = false;
26 lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size
27 lib.link_gc_sections = false;
28 b.installArtifact(lib);
29
30 const check_lib = lib.checkObject();
31
32 // ensure global exists and its initial value is equal to explitic stack size
33 check_lib.checkInHeaders();
34 check_lib.checkExact("Section global");
35 check_lib.checkExact("entries 1");
36 check_lib.checkExact("type i32"); // on wasm32 the stack pointer must be i32
37 check_lib.checkExact("mutable true"); // must be able to mutate the stack pointer
38 check_lib.checkExtract("i32.const {stack_pointer}");
39 check_lib.checkComputeCompare("stack_pointer", .{ .op = .eq, .value = .{ .literal = lib.stack_size.? } });
40
41 // validate memory section starts after virtual stack
42 check_lib.checkInHeaders();
43 check_lib.checkExact("Section data");
44 check_lib.checkExtract("i32.const {data_start}");
45 check_lib.checkComputeCompare("data_start", .{ .op = .eq, .value = .{ .variable = "stack_pointer" } });
46
47 // validate the name of the stack pointer
48 check_lib.checkInHeaders();
49 check_lib.checkExact("Section custom");
50 check_lib.checkExact("type global");
51 check_lib.checkExact("names 1");
52 check_lib.checkExact("index 0");
53 check_lib.checkExact("name __stack_pointer");
54 test_step.dependOn(&check_lib.step);
55}
test/link/wasm/stack_pointer/lib.zig deleted-1
...@@ -1 +0,0 @@
1export fn foo() void {}
test/link/wasm/type/build.zig deleted-43
...@@ -1,43 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 add(b, test_step, .Debug);
8}
9
10fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
11 const exe = b.addExecutable(.{
12 .name = "lib",
13 .root_module = b.createModule(.{
14 .root_source_file = b.path("lib.zig"),
15 .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
16 .optimize = optimize,
17 .strip = false,
18 }),
19 });
20 exe.entry = .disabled;
21 exe.use_llvm = false;
22 exe.use_lld = false;
23 exe.root_module.export_symbol_names = &.{"foo"};
24 // Don't pull in ubsan, since we're just expecting a very minimal executable.
25 exe.bundle_ubsan_rt = false;
26 b.installArtifact(exe);
27
28 const check_exe = exe.checkObject();
29 check_exe.checkInHeaders();
30 check_exe.checkExact("Section type");
31 // only 2 entries, although we have more functions.
32 // This is to test functions with the same function signature
33 // have their types deduplicated.
34 check_exe.checkExact("entries 2");
35 check_exe.checkExact("params 1");
36 check_exe.checkExact("type i32");
37 check_exe.checkExact("returns 1");
38 check_exe.checkExact("type i64");
39 check_exe.checkExact("params 0");
40 check_exe.checkExact("returns 0");
41
42 test_step.dependOn(&check_exe.step);
43}
test/link/wasm/type/lib.zig deleted-10
...@@ -1,10 +0,0 @@
1export fn foo(x: u32) u64 {
2 return bar(x);
3}
4
5fn bar(x: u32) u64 {
6 y();
7 return x;
8}
9
10fn y() void {}
test/standalone/compiler_rt_panic/build.zig-6
...@@ -25,10 +25,4 @@ pub fn build(b: *std.Build) void {...@@ -25,10 +25,4 @@ pub fn build(b: *std.Build) void {
25 });25 });
26 exe.link_gc_sections = false;26 exe.link_gc_sections = false;
27 exe.bundle_compiler_rt = true;27 exe.bundle_compiler_rt = true;
28
29 // Verify compiler_rt hasn't pulled in any debug handlers
30 const check_exe = exe.checkObject();
31 check_exe.checkInSymtab();
32 check_exe.checkNotPresent("debug.readElfDebugInfo");
33 test_step.dependOn(&check_exe.step);
34}28}
test/standalone/glibc_compat/build.zig-121
...@@ -88,69 +88,6 @@ pub fn build(b: *std.Build) void {...@@ -88,69 +88,6 @@ pub fn build(b: *std.Build) void {
88 test_step.dependOn(&run_cmd.step);88 test_step.dependOn(&run_cmd.step);
89 }89 }
90 }90 }
91 const check = exe.checkObject();
92
93 // __errno_location is always a dynamically linked symbol
94 check.checkInDynamicSymtab();
95 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __errno_location");
96
97 // before v2.32 fstat redirects through __fxstat, afterwards its a
98 // normal dynamic symbol
99 check.checkInDynamicSymtab();
100 if (glibc_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) == .lt) {
101 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __fxstat");
102
103 check.checkInSymtab();
104 check.checkContains("FUNC LOCAL HIDDEN fstat");
105 } else {
106 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT fstat");
107
108 check.checkInSymtab();
109 check.checkNotPresent("__fxstat");
110 }
111
112 // before v2.26 reallocarray is not supported
113 check.checkInDynamicSymtab();
114 if (glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) {
115 check.checkNotPresent("reallocarray");
116 } else {
117 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT reallocarray");
118 }
119
120 // before v2.38 strlcpy is not supported
121 check.checkInDynamicSymtab();
122 if (glibc_ver.order(.{ .major = 2, .minor = 38, .patch = 0 }) == .lt) {
123 check.checkNotPresent("strlcpy");
124 } else {
125 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT strlcpy");
126 }
127
128 // v2.16 introduced getauxval()
129 check.checkInDynamicSymtab();
130 if (glibc_ver.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) {
131 check.checkNotPresent("getauxval");
132 } else {
133 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT getauxval");
134 }
135
136 // Always have dynamic "exit", "pow", and "powf" references
137 check.checkInDynamicSymtab();
138 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT exit");
139 check.checkInDynamicSymtab();
140 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT pow");
141 check.checkInDynamicSymtab();
142 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT powf");
143
144 if (target.result.cpu.arch != .s390x) {
145 // An atexit local symbol is defined, and depends on undefined dynamic
146 // __cxa_atexit.
147 check.checkInSymtab();
148 check.checkContains("FUNC LOCAL HIDDEN atexit");
149 check.checkInDynamicSymtab();
150 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __cxa_atexit");
151 }
152
153 test_step.dependOn(&check.step);
154 }91 }
15592
156 // Build & run a Zig test case against a sampling of supported glibc versions93 // Build & run a Zig test case against a sampling of supported glibc versions
...@@ -236,63 +173,5 @@ pub fn build(b: *std.Build) void {...@@ -236,63 +173,5 @@ pub fn build(b: *std.Build) void {
236 test_step.dependOn(&run_cmd.step);173 test_step.dependOn(&run_cmd.step);
237 }174 }
238 }175 }
239 const check = exe.checkObject();
240
241 // __errno_location is always a dynamically linked symbol
242 check.checkInDynamicSymtab();
243 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __errno_location");
244
245 // before v2.32 fstatat redirects through __fxstatat, afterwards its a
246 // normal dynamic symbol
247 if (glibc_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) == .lt) {
248 check.checkInDynamicSymtab();
249 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __fxstatat");
250
251 check.checkInSymtab();
252 check.checkContains("FUNC LOCAL HIDDEN fstatat");
253 } else {
254 check.checkInDynamicSymtab();
255 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT fstatat");
256
257 check.checkInSymtab();
258 check.checkNotPresent("FUNC LOCAL HIDDEN fstatat");
259 }
260
261 // before v2.26 reallocarray is not supported
262 if (glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) {
263 check.checkInDynamicSymtab();
264 check.checkNotPresent("reallocarray");
265 } else {
266 check.checkInDynamicSymtab();
267 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT reallocarray");
268 }
269
270 // before v2.38 strlcpy is not supported
271 if (glibc_ver.order(.{ .major = 2, .minor = 38, .patch = 0 }) == .lt) {
272 check.checkInDynamicSymtab();
273 check.checkNotPresent("strlcpy");
274 } else {
275 check.checkInDynamicSymtab();
276 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT strlcpy");
277 }
278
279 // v2.16 introduced getauxval(), so always present
280 check.checkInDynamicSymtab();
281 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT getauxval");
282
283 // Always have a dynamic "exit" reference
284 check.checkInDynamicSymtab();
285 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT exit");
286
287 if (target.result.cpu.arch != .s390x) {
288 // An atexit local symbol is defined, and depends on undefined dynamic
289 // __cxa_atexit.
290 check.checkInSymtab();
291 check.checkContains("FUNC LOCAL HIDDEN atexit");
292 check.checkInDynamicSymtab();
293 check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __cxa_atexit");
294 }
295
296 test_step.dependOn(&check.step);
297 }176 }
298}177}
test/standalone/ios/build.zig-6
...@@ -37,10 +37,4 @@ pub fn build(b: *std.Build) void {...@@ -37,10 +37,4 @@ pub fn build(b: *std.Build) void {
37 exe.root_module.addCSourceFile(.{ .file = b.path("main.m"), .flags = &.{} });37 exe.root_module.addCSourceFile(.{ .file = b.path("main.m"), .flags = &.{} });
38 exe.root_module.linkFramework("Foundation", .{});38 exe.root_module.linkFramework("Foundation", .{});
39 exe.root_module.linkFramework("UIKit", .{});39 exe.root_module.linkFramework("UIKit", .{});
40
41 const check = exe.checkObject();
42 check.checkInHeaders();
43 check.checkExact("cmd BUILD_VERSION");
44 check.checkExact("platform IOS");
45 test_step.dependOn(&check.step);
46}40}
test/tests.zig-21
...@@ -2204,27 +2204,6 @@ pub fn addStandaloneTests(...@@ -2204,27 +2204,6 @@ pub fn addStandaloneTests(
2204 return step;2204 return step;
2205}2205}
22062206
2207pub fn addLinkTests(
2208 b: *std.Build,
2209 enable_macos_sdk: bool,
2210 enable_ios_sdk: bool,
2211 enable_symlinks_windows: bool,
2212) *Step {
2213 const step = b.step("test-link", "Run the linker tests");
2214 if (compilerHasPackageManager(b)) {
2215 const test_cases_dep_name = "link_test_cases";
2216 const test_cases_dep = b.dependency(test_cases_dep_name, .{
2217 .enable_ios_sdk = enable_ios_sdk,
2218 .enable_macos_sdk = enable_macos_sdk,
2219 .enable_symlinks_windows = enable_symlinks_windows,
2220 });
2221 const test_cases_dep_step = test_cases_dep.builder.default_step;
2222 test_cases_dep_step.name = b.dupe(test_cases_dep_name);
2223 step.dependOn(test_cases_dep.builder.default_step);
2224 }
2225 return step;
2226}
2227
2228pub fn addCliTests(b: *std.Build) *Step {2207pub fn addCliTests(b: *std.Build) *Step {
2229 const step = b.step("test-cli", "Test the command line interface");2208 const step = b.step("test-cli", "Test the command line interface");
2230 const s = std.fs.path.sep_str;2209 const s = std.fs.path.sep_str;