diff --git a/build.zig b/build.zig index 726ef37d8c..3ac840c309 100644 --- a/build.zig +++ b/build.zig @@ -618,7 +618,6 @@ pub fn build(b: *std.Build) !void { .skip_llvm = skip_llvm, .max_rss = 3_300_000_000, })); - test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows)); test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native)); test_step.dependOn(tests.addErrorTraceTests(b, test_filters, optimize_modes, skip_non_native)); test_step.dependOn(tests.addCliTests(b)); diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 484c101c95..773706d39a 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -176,7 +176,6 @@ pub const Id = enum { .update_source_files => UpdateSourceFiles, .run => Run, .check_file => CheckFile, - .check_object => CheckObject, .config_header => ConfigHeader, .objcopy => ObjCopy, .options => Options, @@ -186,7 +185,6 @@ pub const Id = enum { }; pub const CheckFile = @import("Step/CheckFile.zig"); -pub const CheckObject = @import("Step/CheckObject.zig"); pub const ConfigHeader = @import("Step/ConfigHeader.zig"); pub const Fail = @import("Step/Fail.zig"); pub const Fmt = @import("Step/Fmt.zig"); @@ -1004,7 +1002,6 @@ pub fn invalidateResult(step: *Step, gpa: Allocator) bool { test { _ = CheckFile; - _ = CheckObject; _ = Fail; _ = Fmt; _ = InstallArtifact; diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig deleted file mode 100644 index baaf9cb992..0000000000 --- a/lib/std/Build/Step/CheckObject.zig +++ /dev/null @@ -1,2764 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const elf = std.elf; -const fs = std.fs; -const macho = std.macho; -const math = std.math; -const mem = std.mem; -const testing = std.testing; -const Writer = std.Io.Writer; - -const CheckObject = @This(); - -const Allocator = mem.Allocator; -const Step = std.Build.Step; - -pub const base_id: Step.Id = .check_object; - -step: Step, -source: std.Build.LazyPath, -max_bytes: usize = 20 * 1024 * 1024, -checks: std.array_list.Managed(Check), -obj_format: std.Target.ObjectFormat, - -pub fn create( - owner: *std.Build, - source: std.Build.LazyPath, - obj_format: std.Target.ObjectFormat, -) *CheckObject { - const gpa = owner.allocator; - const check_object = gpa.create(CheckObject) catch @panic("OOM"); - check_object.* = .{ - .step = .init(.{ - .id = base_id, - .name = "CheckObject", - .owner = owner, - .makeFn = make, - }), - .source = source.dupe(owner), - .checks = std.array_list.Managed(Check).init(gpa), - .obj_format = obj_format, - }; - check_object.source.addStepDependencies(&check_object.step); - return check_object; -} - -const SearchPhrase = struct { - string: []const u8, - lazy_path: ?std.Build.LazyPath = null, - - fn resolve(phrase: SearchPhrase, b: *std.Build, step: *Step) []const u8 { - const lazy_path = phrase.lazy_path orelse return phrase.string; - return b.fmt("{s} {s}", .{ phrase.string, lazy_path.getPath2(b, step) }); - } -}; - -/// There five types of actions currently supported: -/// .exact - will do an exact match against the haystack -/// .contains - will check for existence within the haystack -/// .not_present - will check for non-existence within the haystack -/// .extract - will do an exact match and extract into a variable enclosed within `{name}` braces -/// .compute_cmp - will perform an operation on the extracted global variables -/// using the MatchAction. It currently only supports an addition. The operation is required -/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, -/// to avoid any parsing really). -/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively -/// they could then be added with this simple program `vmaddr entryoff +`. -const Action = struct { - tag: enum { exact, contains, not_present, extract, compute_cmp }, - phrase: SearchPhrase, - expected: ?ComputeCompareExpected = null, - - /// Returns true if the `phrase` is an exact match with the haystack and variable was successfully extracted. - fn extract( - act: Action, - b: *std.Build, - step: *Step, - haystack: []const u8, - global_vars: anytype, - ) !bool { - assert(act.tag == .extract); - const hay = mem.trim(u8, haystack, " "); - const phrase = mem.trim(u8, act.phrase.resolve(b, step), " "); - - var candidate_vars: std.array_list.Managed(struct { name: []const u8, value: u64 }) = .init(b.allocator); - var hay_it = mem.tokenizeScalar(u8, hay, ' '); - var needle_it = mem.tokenizeScalar(u8, phrase, ' '); - - while (needle_it.next()) |needle_tok| { - const hay_tok = hay_it.next() orelse break; - if (mem.startsWith(u8, needle_tok, "{")) { - const closing_brace = mem.find(u8, needle_tok, "}") orelse return error.MissingClosingBrace; - if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; - - const name = needle_tok[1..closing_brace]; - if (name.len == 0) return error.MissingBraceValue; - const value = std.fmt.parseInt(u64, hay_tok, 16) catch return false; - try candidate_vars.append(.{ - .name = name, - .value = value, - }); - } else { - if (!mem.eql(u8, hay_tok, needle_tok)) return false; - } - } - - if (candidate_vars.items.len == 0) return false; - - for (candidate_vars.items) |cv| try global_vars.putNoClobber(cv.name, cv.value); - - return true; - } - - /// Returns true if the `phrase` is an exact match with the haystack. - fn exact( - act: Action, - b: *std.Build, - step: *Step, - haystack: []const u8, - ) bool { - assert(act.tag == .exact); - const hay = mem.trim(u8, haystack, " "); - const phrase = mem.trim(u8, act.phrase.resolve(b, step), " "); - return mem.eql(u8, hay, phrase); - } - - /// Returns true if the `phrase` exists within the haystack. - fn contains( - act: Action, - b: *std.Build, - step: *Step, - haystack: []const u8, - ) bool { - assert(act.tag == .contains); - const hay = mem.trim(u8, haystack, " "); - const phrase = mem.trim(u8, act.phrase.resolve(b, step), " "); - return mem.find(u8, hay, phrase) != null; - } - - /// Returns true if the `phrase` does not exist within the haystack. - fn notPresent( - act: Action, - b: *std.Build, - step: *Step, - haystack: []const u8, - ) bool { - assert(act.tag == .not_present); - return !contains(.{ - .tag = .contains, - .phrase = act.phrase, - .expected = act.expected, - }, b, step, haystack); - } - - /// Will return true if the `phrase` is correctly parsed into an RPN program and - /// its reduced, computed value compares using `op` with the expected value, either - /// a literal or another extracted variable. - fn computeCmp(act: Action, b: *std.Build, step: *Step, global_vars: anytype) !bool { - const gpa = step.owner.allocator; - const phrase = act.phrase.resolve(b, step); - var op_stack = std.array_list.Managed(enum { add, sub, mod, mul }).init(gpa); - var values = std.array_list.Managed(u64).init(gpa); - - var it = mem.tokenizeScalar(u8, phrase, ' '); - while (it.next()) |next| { - if (mem.eql(u8, next, "+")) { - try op_stack.append(.add); - } else if (mem.eql(u8, next, "-")) { - try op_stack.append(.sub); - } else if (mem.eql(u8, next, "%")) { - try op_stack.append(.mod); - } else if (mem.eql(u8, next, "*")) { - try op_stack.append(.mul); - } else { - const val = std.fmt.parseInt(u64, next, 0) catch blk: { - break :blk global_vars.get(next) orelse { - try step.addError( - \\ - \\========= variable was not extracted: =========== - \\{s} - \\================================================= - , .{next}); - return error.UnknownVariable; - }; - }; - try values.append(val); - } - } - - var op_i: usize = 1; - var reduced: u64 = values.items[0]; - for (op_stack.items) |op| { - const other = values.items[op_i]; - switch (op) { - .add => { - reduced += other; - }, - .sub => { - reduced -= other; - }, - .mod => { - reduced %= other; - }, - .mul => { - reduced *= other; - }, - } - op_i += 1; - } - - const exp_value = switch (act.expected.?.value) { - .variable => |name| global_vars.get(name) orelse { - try step.addError( - \\ - \\========= variable was not extracted: =========== - \\{s} - \\================================================= - , .{name}); - return error.UnknownVariable; - }, - .literal => |x| x, - }; - return math.compare(reduced, act.expected.?.op, exp_value); - } -}; - -const ComputeCompareExpected = struct { - op: math.CompareOperator, - value: union(enum) { - variable: []const u8, - literal: u64, - }, - - pub fn format(value: ComputeCompareExpected, w: *Writer) Writer.Error!void { - try w.print("{t} ", .{value.op}); - switch (value.value) { - .variable => |name| try w.writeAll(name), - .literal => |x| try w.print("{x}", .{x}), - } - } -}; - -const Check = struct { - kind: Kind, - payload: Payload, - data: std.array_list.Managed(u8), - actions: std.array_list.Managed(Action), - - fn create(allocator: Allocator, kind: Kind) Check { - return .{ - .kind = kind, - .payload = .{ .none = {} }, - .data = std.array_list.Managed(u8).init(allocator), - .actions = std.array_list.Managed(Action).init(allocator), - }; - } - - fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { - var check = Check.create(allocator, .dump_section); - const off: u32 = @intCast(check.data.items.len); - check.data.print("{s}\x00", .{name}) catch @panic("OOM"); - check.payload = .{ .dump_section = off }; - return check; - } - - fn extract(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ - .tag = .extract, - .phrase = phrase, - }) catch @panic("OOM"); - } - - fn exact(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ - .tag = .exact, - .phrase = phrase, - }) catch @panic("OOM"); - } - - fn contains(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ - .tag = .contains, - .phrase = phrase, - }) catch @panic("OOM"); - } - - fn notPresent(check: *Check, phrase: SearchPhrase) void { - check.actions.append(.{ - .tag = .not_present, - .phrase = phrase, - }) catch @panic("OOM"); - } - - fn computeCmp(check: *Check, phrase: SearchPhrase, expected: ComputeCompareExpected) void { - check.actions.append(.{ - .tag = .compute_cmp, - .phrase = phrase, - .expected = expected, - }) catch @panic("OOM"); - } - - const Kind = enum { - headers, - symtab, - indirect_symtab, - dynamic_symtab, - archive_symtab, - dynamic_section, - dyld_rebase, - dyld_bind, - dyld_weak_bind, - dyld_lazy_bind, - exports, - compute_compare, - dump_section, - }; - - const Payload = union { - none: void, - /// Null-delimited string in the 'data' buffer. - dump_section: u32, - }; -}; - -/// Creates a new empty sequence of actions. -fn checkStart(check_object: *CheckObject, kind: Check.Kind) void { - const check = Check.create(check_object.step.owner.allocator, kind); - check_object.checks.append(check) catch @panic("OOM"); -} - -/// Adds an exact match phrase to the latest created Check. -pub fn checkExact(check_object: *CheckObject, phrase: []const u8) void { - check_object.checkExactInner(phrase, null); -} - -/// Like `checkExact()` but takes an additional argument `LazyPath` which will be -/// resolved to a full search query in `make()`. -pub fn checkExactPath(check_object: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void { - check_object.checkExactInner(phrase, lazy_path); -} - -fn checkExactInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { - assert(check_object.checks.items.len > 0); - const last = &check_object.checks.items[check_object.checks.items.len - 1]; - last.exact(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path }); -} - -/// Adds a fuzzy match phrase to the latest created Check. -pub fn checkContains(check_object: *CheckObject, phrase: []const u8) void { - check_object.checkContainsInner(phrase, null); -} - -/// Like `checkContains()` but takes an additional argument `lazy_path` which will be -/// resolved to a full search query in `make()`. -pub fn checkContainsPath( - check_object: *CheckObject, - phrase: []const u8, - lazy_path: std.Build.LazyPath, -) void { - check_object.checkContainsInner(phrase, lazy_path); -} - -fn checkContainsInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { - assert(check_object.checks.items.len > 0); - const last = &check_object.checks.items[check_object.checks.items.len - 1]; - last.contains(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path }); -} - -/// Adds an exact match phrase with variable extractor to the latest created Check. -pub fn checkExtract(check_object: *CheckObject, phrase: []const u8) void { - check_object.checkExtractInner(phrase, null); -} - -/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be -/// resolved to a full search query in `make()`. -pub fn checkExtractLazyPath(check_object: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void { - check_object.checkExtractInner(phrase, lazy_path); -} - -fn checkExtractInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { - assert(check_object.checks.items.len > 0); - const last = &check_object.checks.items[check_object.checks.items.len - 1]; - last.extract(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path }); -} - -/// Adds another searched phrase to the latest created Check -/// however ensures there is no matching phrase in the output. -pub fn checkNotPresent(check_object: *CheckObject, phrase: []const u8) void { - check_object.checkNotPresentInner(phrase, null); -} - -/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be -/// resolved to a full search query in `make()`. -pub fn checkNotPresentLazyPath(check_object: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void { - check_object.checkNotPresentInner(phrase, lazy_path); -} - -fn checkNotPresentInner(check_object: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { - assert(check_object.checks.items.len > 0); - const last = &check_object.checks.items[check_object.checks.items.len - 1]; - last.notPresent(.{ .string = check_object.step.owner.dupe(phrase), .lazy_path = lazy_path }); -} - -/// Creates a new check checking in the file headers (section, program headers, etc.). -pub fn checkInHeaders(check_object: *CheckObject) void { - check_object.checkStart(.headers); -} - -/// Creates a new check checking specifically symbol table parsed and dumped from the object -/// file. -pub fn checkInSymtab(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .macho => MachODumper.symtab_label, - .elf => ElfDumper.symtab_label, - .wasm => WasmDumper.symtab_label, - .coff => @panic("TODO symtab for coff"), - else => @panic("TODO other file formats"), - }; - check_object.checkStart(.symtab); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically dyld rebase opcodes contents parsed and dumped -/// from the object file. -/// This check is target-dependent and applicable to MachO only. -pub fn checkInDyldRebase(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .macho => MachODumper.dyld_rebase_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.dyld_rebase); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically dyld bind opcodes contents parsed and dumped -/// from the object file. -/// This check is target-dependent and applicable to MachO only. -pub fn checkInDyldBind(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .macho => MachODumper.dyld_bind_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.dyld_bind); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically dyld weak bind opcodes contents parsed and dumped -/// from the object file. -/// This check is target-dependent and applicable to MachO only. -pub fn checkInDyldWeakBind(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .macho => MachODumper.dyld_weak_bind_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.dyld_weak_bind); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically dyld lazy bind opcodes contents parsed and dumped -/// from the object file. -/// This check is target-dependent and applicable to MachO only. -pub fn checkInDyldLazyBind(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .macho => MachODumper.dyld_lazy_bind_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.dyld_lazy_bind); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically exports info contents parsed and dumped -/// from the object file. -/// This check is target-dependent and applicable to MachO only. -pub fn checkInExports(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .macho => MachODumper.exports_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.exports); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically indirect symbol table parsed and dumped -/// from the object file. -/// This check is target-dependent and applicable to MachO only. -pub fn checkInIndirectSymtab(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .macho => MachODumper.indirect_symtab_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.indirect_symtab); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically dynamic symbol table parsed and dumped from the object -/// file. -/// This check is target-dependent and applicable to ELF only. -pub fn checkInDynamicSymtab(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .elf => ElfDumper.dynamic_symtab_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.dynamic_symtab); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically dynamic section parsed and dumped from the object -/// file. -/// This check is target-dependent and applicable to ELF only. -pub fn checkInDynamicSection(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .elf => ElfDumper.dynamic_section_label, - else => @panic("Unsupported target platform"), - }; - check_object.checkStart(.dynamic_section); - check_object.checkExact(label); -} - -/// Creates a new check checking specifically symbol table parsed and dumped from the archive -/// file. -pub fn checkInArchiveSymtab(check_object: *CheckObject) void { - const label = switch (check_object.obj_format) { - .elf => ElfDumper.archive_symtab_label, - else => @panic("TODO other file formats"), - }; - check_object.checkStart(.archive_symtab); - check_object.checkExact(label); -} - -pub fn dumpSection(check_object: *CheckObject, name: [:0]const u8) void { - const check = Check.dumpSection(check_object.step.owner.allocator, name); - check_object.checks.append(check) catch @panic("OOM"); -} - -/// Creates a new standalone, singular check which allows running simple binary operations -/// on the extracted variables. It will then compare the reduced program with the value of -/// the expected variable. -pub fn checkComputeCompare( - check_object: *CheckObject, - program: []const u8, - expected: ComputeCompareExpected, -) void { - var check = Check.create(check_object.step.owner.allocator, .compute_compare); - check.computeCmp(.{ .string = check_object.step.owner.dupe(program) }, expected); - check_object.checks.append(check) catch @panic("OOM"); -} - -fn make(step: *Step, make_options: Step.MakeOptions) !void { - _ = make_options; - const b = step.owner; - const io = b.graph.io; - const gpa = b.allocator; - const check_object: *CheckObject = @fieldParentPtr("step", step); - try step.singleUnchangingWatchInput(check_object.source); - - const src_path = check_object.source.getPath3(b, step); - const contents = src_path.root_dir.handle.readFileAllocOptions( - io, - src_path.sub_path, - gpa, - .limited(check_object.max_bytes), - .of(u64), - null, - ) catch |err| return step.fail("unable to read '{f}': {t}", .{ - std.fmt.alt(src_path, .formatEscapeChar), err, - }); - - var vars: std.StringHashMap(u64) = .init(gpa); - for (check_object.checks.items) |chk| { - if (chk.kind == .compute_compare) { - assert(chk.actions.items.len == 1); - const act = chk.actions.items[0]; - assert(act.tag == .compute_cmp); - const res = act.computeCmp(b, step, vars) catch |err| switch (err) { - error.UnknownVariable => return step.fail("Unknown variable", .{}), - else => |e| return e, - }; - if (!res) { - return step.fail( - \\ - \\========= comparison failed for action: =========== - \\{s} {f} - \\=================================================== - , .{ act.phrase.resolve(b, step), act.expected.? }); - } - continue; - } - - const output = switch (check_object.obj_format) { - .macho => try MachODumper.parseAndDump(step, chk, contents), - .elf => try ElfDumper.parseAndDump(step, chk, contents), - .coff => return step.fail("TODO coff parser", .{}), - .wasm => try WasmDumper.parseAndDump(step, chk, contents), - else => unreachable, - }; - - // Depending on whether we requested dumping section verbatim or not, - // we either format message string with escaped codes, or not to aid debugging - // the failed test. - const fmtMessageString = struct { - fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Alt(Ctx, formatMessageString) { - return .{ .data = .{ - .kind = kind, - .msg = msg, - } }; - } - - const Ctx = struct { - kind: Check.Kind, - msg: []const u8, - }; - - fn formatMessageString(ctx: Ctx, w: *Writer) !void { - switch (ctx.kind) { - .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}), - else => try w.writeAll(ctx.msg), - } - } - }.fmtMessageString; - - var it = mem.tokenizeAny(u8, output, "\r\n"); - for (chk.actions.items) |act| { - switch (act.tag) { - .exact => { - while (it.next()) |line| { - if (act.exact(b, step, line)) break; - } else { - return step.fail( - \\ - \\========= expected to find: ========================== - \\{f} - \\========= but parsed file does not contain it: ======= - \\{f} - \\========= file path: ================================= - \\{f} - , .{ - fmtMessageString(chk.kind, act.phrase.resolve(b, step)), - fmtMessageString(chk.kind, output), - src_path, - }); - } - }, - - .contains => { - while (it.next()) |line| { - if (act.contains(b, step, line)) break; - } else { - return step.fail( - \\ - \\========= expected to find: ========================== - \\*{f}* - \\========= but parsed file does not contain it: ======= - \\{f} - \\========= file path: ================================= - \\{f} - , .{ - fmtMessageString(chk.kind, act.phrase.resolve(b, step)), - fmtMessageString(chk.kind, output), - src_path, - }); - } - }, - - .not_present => { - while (it.next()) |line| { - if (act.notPresent(b, step, line)) continue; - return step.fail( - \\ - \\========= expected not to find: =================== - \\{f} - \\========= but parsed file does contain it: ======== - \\{f} - \\========= file path: ============================== - \\{f} - , .{ - fmtMessageString(chk.kind, act.phrase.resolve(b, step)), - fmtMessageString(chk.kind, output), - src_path, - }); - } - }, - - .extract => { - while (it.next()) |line| { - if (try act.extract(b, step, line, &vars)) break; - } else { - return step.fail( - \\ - \\========= expected to find and extract: ============== - \\{f} - \\========= but parsed file does not contain it: ======= - \\{f} - \\========= file path: ============================== - \\{f} - , .{ - fmtMessageString(chk.kind, act.phrase.resolve(b, step)), - fmtMessageString(chk.kind, output), - src_path, - }); - } - }, - - .compute_cmp => unreachable, - } - } - } -} - -const MachODumper = struct { - const dyld_rebase_label = "dyld rebase data"; - const dyld_bind_label = "dyld bind data"; - const dyld_weak_bind_label = "dyld weak bind data"; - const dyld_lazy_bind_label = "dyld lazy bind data"; - const exports_label = "exports data"; - const symtab_label = "symbol table"; - const indirect_symtab_label = "indirect symbol table"; - - fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { - // TODO: handle archives and fat files - return parseAndDumpObject(step, check, bytes); - } - - const ObjectContext = struct { - gpa: Allocator, - data: []const u8, - header: macho.mach_header_64, - segments: std.ArrayList(macho.segment_command_64) = .empty, - sections: std.ArrayList(macho.section_64) = .empty, - symtab: std.ArrayList(macho.nlist_64) = .empty, - strtab: std.ArrayList(u8) = .empty, - indsymtab: std.ArrayList(u32) = .empty, - imports: std.ArrayList([]const u8) = .empty, - - fn parse(ctx: *ObjectContext) !void { - var it = try ctx.getLoadCommandIterator(); - var i: usize = 0; - while (try it.next()) |cmd| { - switch (cmd.hdr.cmd) { - .SEGMENT_64 => { - const seg = cmd.cast(macho.segment_command_64).?; - try ctx.segments.append(ctx.gpa, seg); - try ctx.sections.ensureUnusedCapacity(ctx.gpa, seg.nsects); - for (cmd.getSections()) |sect| { - ctx.sections.appendAssumeCapacity(sect); - } - }, - .SYMTAB => { - const lc = cmd.cast(macho.symtab_command).?; - const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(ctx.data.ptr + lc.symoff))[0..lc.nsyms]; - const strtab = ctx.data[lc.stroff..][0..lc.strsize]; - try ctx.symtab.appendUnalignedSlice(ctx.gpa, symtab); - try ctx.strtab.appendSlice(ctx.gpa, strtab); - }, - .DYSYMTAB => { - const lc = cmd.cast(macho.dysymtab_command).?; - const indexes = @as([*]align(1) const u32, @ptrCast(ctx.data.ptr + lc.indirectsymoff))[0..lc.nindirectsyms]; - try ctx.indsymtab.appendUnalignedSlice(ctx.gpa, indexes); - }, - .LOAD_DYLIB, - .LOAD_WEAK_DYLIB, - .REEXPORT_DYLIB, - => { - try ctx.imports.append(ctx.gpa, cmd.getDylibPathName()); - }, - else => {}, - } - - i += 1; - } - } - - fn getString(ctx: ObjectContext, off: u32) [:0]const u8 { - assert(off < ctx.strtab.items.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(ctx.strtab.items.ptr + off)), 0); - } - - fn getLoadCommandIterator(ctx: ObjectContext) !macho.LoadCommandIterator { - return .init(&ctx.header, ctx.data[@sizeOf(macho.mach_header_64)..]); - } - - fn getLoadCommand(ctx: ObjectContext, cmd: macho.LC) !?macho.LoadCommandIterator.LoadCommand { - var it = try ctx.getLoadCommandIterator(); - while (try it.next()) |lc| if (lc.hdr.cmd == cmd) { - return lc; - }; - return null; - } - - fn getSegmentByName(ctx: ObjectContext, name: []const u8) ?macho.segment_command_64 { - for (ctx.segments.items) |seg| { - if (mem.eql(u8, seg.segName(), name)) return seg; - } - return null; - } - - fn getSectionByName(ctx: ObjectContext, segname: []const u8, sectname: []const u8) ?macho.section_64 { - for (ctx.sections.items) |sect| { - if (mem.eql(u8, sect.segName(), segname) and mem.eql(u8, sect.sectName(), sectname)) return sect; - } - return null; - } - - fn dumpHeader(hdr: macho.mach_header_64, writer: anytype) !void { - const cputype = switch (hdr.cputype) { - macho.CPU_TYPE_ARM64 => "ARM64", - macho.CPU_TYPE_X86_64 => "X86_64", - else => "Unknown", - }; - const filetype = switch (hdr.filetype) { - macho.MH_OBJECT => "MH_OBJECT", - macho.MH_EXECUTE => "MH_EXECUTE", - macho.MH_FVMLIB => "MH_FVMLIB", - macho.MH_CORE => "MH_CORE", - macho.MH_PRELOAD => "MH_PRELOAD", - macho.MH_DYLIB => "MH_DYLIB", - macho.MH_DYLINKER => "MH_DYLINKER", - macho.MH_BUNDLE => "MH_BUNDLE", - macho.MH_DYLIB_STUB => "MH_DYLIB_STUB", - macho.MH_DSYM => "MH_DSYM", - macho.MH_KEXT_BUNDLE => "MH_KEXT_BUNDLE", - else => "Unknown", - }; - - try writer.print( - \\header - \\cputype {s} - \\filetype {s} - \\ncmds {d} - \\sizeofcmds {x} - \\flags - , .{ - cputype, - filetype, - hdr.ncmds, - hdr.sizeofcmds, - }); - - if (hdr.flags > 0) { - if (hdr.flags & macho.MH_NOUNDEFS != 0) try writer.writeAll(" NOUNDEFS"); - if (hdr.flags & macho.MH_INCRLINK != 0) try writer.writeAll(" INCRLINK"); - if (hdr.flags & macho.MH_DYLDLINK != 0) try writer.writeAll(" DYLDLINK"); - if (hdr.flags & macho.MH_BINDATLOAD != 0) try writer.writeAll(" BINDATLOAD"); - if (hdr.flags & macho.MH_PREBOUND != 0) try writer.writeAll(" PREBOUND"); - if (hdr.flags & macho.MH_SPLIT_SEGS != 0) try writer.writeAll(" SPLIT_SEGS"); - if (hdr.flags & macho.MH_LAZY_INIT != 0) try writer.writeAll(" LAZY_INIT"); - if (hdr.flags & macho.MH_TWOLEVEL != 0) try writer.writeAll(" TWOLEVEL"); - if (hdr.flags & macho.MH_FORCE_FLAT != 0) try writer.writeAll(" FORCE_FLAT"); - if (hdr.flags & macho.MH_NOMULTIDEFS != 0) try writer.writeAll(" NOMULTIDEFS"); - if (hdr.flags & macho.MH_NOFIXPREBINDING != 0) try writer.writeAll(" NOFIXPREBINDING"); - if (hdr.flags & macho.MH_PREBINDABLE != 0) try writer.writeAll(" PREBINDABLE"); - if (hdr.flags & macho.MH_ALLMODSBOUND != 0) try writer.writeAll(" ALLMODSBOUND"); - if (hdr.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) try writer.writeAll(" SUBSECTIONS_VIA_SYMBOLS"); - if (hdr.flags & macho.MH_CANONICAL != 0) try writer.writeAll(" CANONICAL"); - if (hdr.flags & macho.MH_WEAK_DEFINES != 0) try writer.writeAll(" WEAK_DEFINES"); - if (hdr.flags & macho.MH_BINDS_TO_WEAK != 0) try writer.writeAll(" BINDS_TO_WEAK"); - if (hdr.flags & macho.MH_ALLOW_STACK_EXECUTION != 0) try writer.writeAll(" ALLOW_STACK_EXECUTION"); - if (hdr.flags & macho.MH_ROOT_SAFE != 0) try writer.writeAll(" ROOT_SAFE"); - if (hdr.flags & macho.MH_SETUID_SAFE != 0) try writer.writeAll(" SETUID_SAFE"); - if (hdr.flags & macho.MH_NO_REEXPORTED_DYLIBS != 0) try writer.writeAll(" NO_REEXPORTED_DYLIBS"); - if (hdr.flags & macho.MH_PIE != 0) try writer.writeAll(" PIE"); - if (hdr.flags & macho.MH_DEAD_STRIPPABLE_DYLIB != 0) try writer.writeAll(" DEAD_STRIPPABLE_DYLIB"); - if (hdr.flags & macho.MH_HAS_TLV_DESCRIPTORS != 0) try writer.writeAll(" HAS_TLV_DESCRIPTORS"); - if (hdr.flags & macho.MH_NO_HEAP_EXECUTION != 0) try writer.writeAll(" NO_HEAP_EXECUTION"); - if (hdr.flags & macho.MH_APP_EXTENSION_SAFE != 0) try writer.writeAll(" APP_EXTENSION_SAFE"); - if (hdr.flags & macho.MH_NLIST_OUTOFSYNC_WITH_DYLDINFO != 0) try writer.writeAll(" NLIST_OUTOFSYNC_WITH_DYLDINFO"); - } - - try writer.writeByte('\n'); - } - - fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void { - // print header first - try writer.print( - \\LC {d} - \\cmd {s} - \\cmdsize {d} - , .{ index, @tagName(lc.hdr.cmd), lc.hdr.cmdsize }); - - switch (lc.hdr.cmd) { - .SEGMENT_64 => { - const seg = lc.cast(macho.segment_command_64).?; - try writer.writeByte('\n'); - try writer.print( - \\segname {s} - \\vmaddr {x} - \\vmsize {x} - \\fileoff {x} - \\filesz {x} - , .{ - seg.segName(), - seg.vmaddr, - seg.vmsize, - seg.fileoff, - seg.filesize, - }); - - for (lc.getSections()) |sect| { - try writer.writeByte('\n'); - try writer.print( - \\sectname {s} - \\addr {x} - \\size {x} - \\offset {x} - \\align {x} - , .{ - sect.sectName(), - sect.addr, - sect.size, - sect.offset, - sect.@"align", - }); - } - }, - - .ID_DYLIB, - .LOAD_DYLIB, - .LOAD_WEAK_DYLIB, - .REEXPORT_DYLIB, - => { - const dylib = lc.cast(macho.dylib_command).?; - try writer.writeByte('\n'); - try writer.print( - \\name {s} - \\timestamp {d} - \\current version {x} - \\compatibility version {x} - , .{ - lc.getDylibPathName(), - dylib.dylib.timestamp, - dylib.dylib.current_version, - dylib.dylib.compatibility_version, - }); - }, - - .MAIN => { - const main = lc.cast(macho.entry_point_command).?; - try writer.writeByte('\n'); - try writer.print( - \\entryoff {x} - \\stacksize {x} - , .{ main.entryoff, main.stacksize }); - }, - - .RPATH => { - try writer.writeByte('\n'); - try writer.print( - \\path {s} - , .{ - lc.getRpathPathName(), - }); - }, - - .UUID => { - const uuid = lc.cast(macho.uuid_command).?; - try writer.writeByte('\n'); - try writer.print("uuid {x}", .{&uuid.uuid}); - }, - - .DATA_IN_CODE, - .FUNCTION_STARTS, - .CODE_SIGNATURE, - => { - const llc = lc.cast(macho.linkedit_data_command).?; - try writer.writeByte('\n'); - try writer.print( - \\dataoff {x} - \\datasize {x} - , .{ llc.dataoff, llc.datasize }); - }, - - .DYLD_INFO_ONLY => { - const dlc = lc.cast(macho.dyld_info_command).?; - try writer.writeByte('\n'); - try writer.print( - \\rebaseoff {x} - \\rebasesize {x} - \\bindoff {x} - \\bindsize {x} - \\weakbindoff {x} - \\weakbindsize {x} - \\lazybindoff {x} - \\lazybindsize {x} - \\exportoff {x} - \\exportsize {x} - , .{ - dlc.rebase_off, - dlc.rebase_size, - dlc.bind_off, - dlc.bind_size, - dlc.weak_bind_off, - dlc.weak_bind_size, - dlc.lazy_bind_off, - dlc.lazy_bind_size, - dlc.export_off, - dlc.export_size, - }); - }, - - .SYMTAB => { - const slc = lc.cast(macho.symtab_command).?; - try writer.writeByte('\n'); - try writer.print( - \\symoff {x} - \\nsyms {x} - \\stroff {x} - \\strsize {x} - , .{ - slc.symoff, - slc.nsyms, - slc.stroff, - slc.strsize, - }); - }, - - .DYSYMTAB => { - const dlc = lc.cast(macho.dysymtab_command).?; - try writer.writeByte('\n'); - try writer.print( - \\ilocalsym {x} - \\nlocalsym {x} - \\iextdefsym {x} - \\nextdefsym {x} - \\iundefsym {x} - \\nundefsym {x} - \\indirectsymoff {x} - \\nindirectsyms {x} - , .{ - dlc.ilocalsym, - dlc.nlocalsym, - dlc.iextdefsym, - dlc.nextdefsym, - dlc.iundefsym, - dlc.nundefsym, - dlc.indirectsymoff, - dlc.nindirectsyms, - }); - }, - - .BUILD_VERSION => { - const blc = lc.cast(macho.build_version_command).?; - try writer.writeByte('\n'); - try writer.print( - \\platform {s} - \\minos {d}.{d}.{d} - \\sdk {d}.{d}.{d} - \\ntools {d} - , .{ - @tagName(blc.platform), - blc.minos >> 16, - @as(u8, @truncate(blc.minos >> 8)), - @as(u8, @truncate(blc.minos)), - blc.sdk >> 16, - @as(u8, @truncate(blc.sdk >> 8)), - @as(u8, @truncate(blc.sdk)), - blc.ntools, - }); - for (lc.getBuildVersionTools()) |tool| { - try writer.writeByte('\n'); - switch (tool.tool) { - .CLANG, .SWIFT, .LD, .LLD, .ZIG => try writer.print("tool {s}\n", .{@tagName(tool.tool)}), - else => |x| try writer.print("tool {d}\n", .{@intFromEnum(x)}), - } - try writer.print( - \\version {d}.{d}.{d} - , .{ - tool.version >> 16, - @as(u8, @truncate(tool.version >> 8)), - @as(u8, @truncate(tool.version)), - }); - } - }, - - .VERSION_MIN_MACOSX, - .VERSION_MIN_IPHONEOS, - .VERSION_MIN_WATCHOS, - .VERSION_MIN_TVOS, - => { - const vlc = lc.cast(macho.version_min_command).?; - try writer.writeByte('\n'); - try writer.print( - \\version {d}.{d}.{d} - \\sdk {d}.{d}.{d} - , .{ - vlc.version >> 16, - @as(u8, @truncate(vlc.version >> 8)), - @as(u8, @truncate(vlc.version)), - vlc.sdk >> 16, - @as(u8, @truncate(vlc.sdk >> 8)), - @as(u8, @truncate(vlc.sdk)), - }); - }, - - else => {}, - } - } - - fn dumpSymtab(ctx: ObjectContext, writer: anytype) !void { - try writer.writeAll(symtab_label ++ "\n"); - - for (ctx.symtab.items) |sym| { - const sym_name = ctx.getString(sym.n_strx); - if (sym.n_type.bits.is_stab != 0) { - const tt = switch (sym.n_type.stab) { - _ => "UNKNOWN STAB", - else => @tagName(sym.n_type.stab), - }; - try writer.print("{x}", .{sym.n_value}); - if (sym.n_sect > 0) { - const sect = ctx.sections.items[sym.n_sect - 1]; - try writer.print(" ({s},{s})", .{ sect.segName(), sect.sectName() }); - } - try writer.print(" {s} (stab) {s}\n", .{ tt, sym_name }); - } else if (sym.n_type.bits.type == .sect) { - const sect = ctx.sections.items[sym.n_sect - 1]; - try writer.print("{x} ({s},{s})", .{ - sym.n_value, - sect.segName(), - sect.sectName(), - }); - if (sym.n_desc.referenced_dynamically) try writer.writeAll(" [referenced dynamically]"); - if (sym.n_desc.weak_def_or_ref_to_weak) try writer.writeAll(" weak"); - if (sym.n_desc.weak_ref) try writer.writeAll(" weakref"); - if (sym.n_type.bits.ext) { - if (sym.n_type.bits.pext) try writer.writeAll(" private"); - try writer.writeAll(" external"); - } else if (sym.n_type.bits.pext) try writer.writeAll(" (was private external)"); - try writer.print(" {s}\n", .{sym_name}); - } else if (sym.tentative()) { - const alignment = (@as(u16, @bitCast(sym.n_desc)) >> 8) & 0x0F; - try writer.print(" 0x{x:0>16} (common) (alignment 2^{d})", .{ sym.n_value, alignment }); - if (sym.n_type.bits.ext) try writer.writeAll(" external"); - try writer.print(" {s}\n", .{sym_name}); - } else if (sym.n_type.bits.type == .undf) { - const ordinal = @divFloor(@as(i16, @bitCast(sym.n_desc)), macho.N_SYMBOL_RESOLVER); - const import_name = blk: { - if (ordinal <= 0) { - if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF) - break :blk "self import"; - if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE) - break :blk "main executable"; - if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP) - break :blk "flat lookup"; - unreachable; - } - const full_path = ctx.imports.items[@as(u16, @bitCast(ordinal)) - 1]; - const basename = fs.path.basename(full_path); - assert(basename.len > 0); - const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len; - break :blk basename[0..ext]; - }; - try writer.writeAll("(undefined)"); - if (sym.n_desc.weak_ref) try writer.writeAll(" weakref"); - if (sym.n_type.bits.ext) try writer.writeAll(" external"); - try writer.print(" {s} (from {s})\n", .{ - sym_name, - import_name, - }); - } - } - } - - fn dumpIndirectSymtab(ctx: ObjectContext, writer: anytype) !void { - try writer.writeAll(indirect_symtab_label ++ "\n"); - - var sects_buffer: [3]macho.section_64 = undefined; - const sects = blk: { - var count: usize = 0; - if (ctx.getSectionByName("__TEXT", "__stubs")) |sect| { - sects_buffer[count] = sect; - count += 1; - } - if (ctx.getSectionByName("__DATA_CONST", "__got")) |sect| { - sects_buffer[count] = sect; - count += 1; - } - if (ctx.getSectionByName("__DATA", "__la_symbol_ptr")) |sect| { - sects_buffer[count] = sect; - count += 1; - } - break :blk sects_buffer[0..count]; - }; - - const sortFn = struct { - fn sortFn(c: void, lhs: macho.section_64, rhs: macho.section_64) bool { - _ = c; - return lhs.reserved1 < rhs.reserved1; - } - }.sortFn; - mem.sort(macho.section_64, sects, {}, sortFn); - - var i: usize = 0; - while (i < sects.len) : (i += 1) { - const sect = sects[i]; - const start = sect.reserved1; - const end = if (i + 1 >= sects.len) ctx.indsymtab.items.len else sects[i + 1].reserved1; - const entry_size = blk: { - if (mem.eql(u8, sect.sectName(), "__stubs")) break :blk sect.reserved2; - break :blk @sizeOf(u64); - }; - - try writer.print("{s},{s}\n", .{ sect.segName(), sect.sectName() }); - try writer.print("nentries {d}\n", .{end - start}); - for (ctx.indsymtab.items[start..end], 0..) |index, j| { - const sym = ctx.symtab.items[index]; - const addr = sect.addr + entry_size * j; - try writer.print("0x{x} {d} {s}\n", .{ addr, index, ctx.getString(sym.n_strx) }); - } - } - } - - fn dumpRebaseInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void { - var rebases = std.array_list.Managed(u64).init(ctx.gpa); - defer rebases.deinit(); - try ctx.parseRebaseInfo(data, &rebases); - mem.sort(u64, rebases.items, {}, std.sort.asc(u64)); - for (rebases.items) |addr| { - try writer.print("0x{x}\n", .{addr}); - } - } - - fn parseRebaseInfo(ctx: ObjectContext, data: []const u8, rebases: *std.array_list.Managed(u64)) !void { - var reader: std.Io.Reader = .fixed(data); - - var seg_id: ?u8 = null; - var offset: u64 = 0; - while (true) { - const byte = reader.takeByte() catch break; - const opc = byte & macho.REBASE_OPCODE_MASK; - const imm = byte & macho.REBASE_IMMEDIATE_MASK; - switch (opc) { - macho.REBASE_OPCODE_DONE => break, - macho.REBASE_OPCODE_SET_TYPE_IMM => {}, - macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { - seg_id = imm; - offset = try reader.takeLeb128(u64); - }, - macho.REBASE_OPCODE_ADD_ADDR_IMM_SCALED => { - offset += imm * @sizeOf(u64); - }, - macho.REBASE_OPCODE_ADD_ADDR_ULEB => { - const addend = try reader.takeLeb128(u64); - offset += addend; - }, - macho.REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB => { - const addend = try reader.takeLeb128(u64); - const seg = ctx.segments.items[seg_id.?]; - const addr = seg.vmaddr + offset; - try rebases.append(addr); - offset += addend + @sizeOf(u64); - }, - macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES, - macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES, - macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB, - => { - var ntimes: u64 = 1; - var skip: u64 = 0; - switch (opc) { - macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES => { - ntimes = imm; - }, - macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES => { - ntimes = try reader.takeLeb128(u64); - }, - macho.REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB => { - ntimes = try reader.takeLeb128(u64); - skip = try reader.takeLeb128(u64); - }, - else => unreachable, - } - const seg = ctx.segments.items[seg_id.?]; - const base_addr = seg.vmaddr; - var count: usize = 0; - while (count < ntimes) : (count += 1) { - const addr = base_addr + offset; - try rebases.append(addr); - offset += skip + @sizeOf(u64); - } - }, - else => break, - } - } - } - - const Binding = struct { - address: u64, - addend: i64, - ordinal: u16, - tag: Tag, - name: []const u8, - - fn deinit(binding: *Binding, gpa: Allocator) void { - gpa.free(binding.name); - } - - fn lessThan(ctx: void, lhs: Binding, rhs: Binding) bool { - _ = ctx; - return lhs.address < rhs.address; - } - - const Tag = enum { - ord, - self, - exe, - flat, - }; - }; - - fn dumpBindInfo(ctx: ObjectContext, data: []const u8, writer: anytype) !void { - var bindings = std.array_list.Managed(Binding).init(ctx.gpa); - defer { - for (bindings.items) |*b| { - b.deinit(ctx.gpa); - } - bindings.deinit(); - } - var data_reader: std.Io.Reader = .fixed(data); - try ctx.parseBindInfo(&data_reader, &bindings); - mem.sort(Binding, bindings.items, {}, Binding.lessThan); - for (bindings.items) |binding| { - try writer.print("0x{x} [addend: {d}]", .{ binding.address, binding.addend }); - try writer.writeAll(" ("); - switch (binding.tag) { - .self => try writer.writeAll("self"), - .exe => try writer.writeAll("main executable"), - .flat => try writer.writeAll("flat lookup"), - .ord => try writer.writeAll(std.fs.path.basename(ctx.imports.items[binding.ordinal - 1])), - } - try writer.print(") {s}\n", .{binding.name}); - } - } - - fn parseBindInfo(ctx: ObjectContext, reader: *std.Io.Reader, bindings: *std.array_list.Managed(Binding)) !void { - var seg_id: ?u8 = null; - var tag: Binding.Tag = .self; - var ordinal: u16 = 0; - var offset: u64 = 0; - var addend: i64 = 0; - - var name_buf = std.array_list.Managed(u8).init(ctx.gpa); - defer name_buf.deinit(); - - while (true) { - const byte = reader.takeByte() catch break; - const opc = byte & macho.BIND_OPCODE_MASK; - const imm = byte & macho.BIND_IMMEDIATE_MASK; - switch (opc) { - macho.BIND_OPCODE_DONE, - macho.BIND_OPCODE_SET_TYPE_IMM, - => {}, - macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => { - tag = .ord; - ordinal = imm; - }, - macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => { - switch (imm) { - 0 => tag = .self, - 0xf => tag = .exe, - 0xe => tag = .flat, - else => unreachable, - } - }, - macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => { - seg_id = imm; - offset = try reader.takeLeb128(u64); - }, - macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => { - name_buf.clearRetainingCapacity(); - try name_buf.appendSlice(try reader.takeDelimiterInclusive(0)); - }, - macho.BIND_OPCODE_SET_ADDEND_SLEB => { - addend = try reader.takeLeb128(i64); - }, - macho.BIND_OPCODE_ADD_ADDR_ULEB => { - const x = try reader.takeLeb128(u64); - offset = @intCast(@as(i64, @intCast(offset)) + @as(i64, @bitCast(x))); - }, - macho.BIND_OPCODE_DO_BIND, - macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB, - macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED, - macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB, - => { - var add_addr: u64 = 0; - var count: u64 = 1; - var skip: u64 = 0; - - switch (opc) { - macho.BIND_OPCODE_DO_BIND => {}, - macho.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => { - add_addr = try reader.takeLeb128(u64); - }, - macho.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => { - add_addr = imm * @sizeOf(u64); - }, - macho.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => { - count = try reader.takeLeb128(u64); - skip = try reader.takeLeb128(u64); - }, - else => unreachable, - } - - const seg = ctx.segments.items[seg_id.?]; - var i: u64 = 0; - while (i < count) : (i += 1) { - const addr: u64 = @intCast(@as(i64, @intCast(seg.vmaddr + offset))); - try bindings.append(.{ - .address = addr, - .addend = addend, - .tag = tag, - .ordinal = ordinal, - .name = try ctx.gpa.dupe(u8, name_buf.items), - }); - offset += skip + @sizeOf(u64) + add_addr; - } - }, - else => break, - } - } - } - - fn dumpExportsTrie(ctx: ObjectContext, data: []const u8, writer: anytype) !void { - const seg = ctx.getSegmentByName("__TEXT") orelse return; - - var arena = std.heap.ArenaAllocator.init(ctx.gpa); - defer arena.deinit(); - - var exports = std.array_list.Managed(Export).init(arena.allocator()); - var it: TrieIterator = .{ .stream = .fixed(data) }; - try parseTrieNode(arena.allocator(), &it, "", &exports); - - mem.sort(Export, exports.items, {}, Export.lessThan); - - for (exports.items) |exp| { - switch (exp.tag) { - .@"export" => { - const info = exp.data.@"export"; - if (info.kind != .regular or info.weak) { - try writer.writeByte('['); - } - switch (info.kind) { - .regular => {}, - .absolute => try writer.writeAll("ABS, "), - .tlv => try writer.writeAll("THREAD_LOCAL, "), - } - if (info.weak) try writer.writeAll("WEAK"); - if (info.kind != .regular or info.weak) { - try writer.writeAll("] "); - } - try writer.print("{x} ", .{seg.vmaddr + info.vmoffset}); - }, - else => {}, - } - - try writer.print("{s}\n", .{exp.name}); - } - } - - const TrieIterator = struct { - stream: std.Io.Reader, - - fn takeLeb128(it: *TrieIterator) !u64 { - return it.stream.takeLeb128(u64); - } - - fn readString(it: *TrieIterator) ![:0]const u8 { - return it.stream.takeSentinel(0); - } - - fn takeByte(it: *TrieIterator) !u8 { - return it.stream.takeByte(); - } - }; - - const Export = struct { - name: []const u8, - tag: enum { @"export", reexport, stub_resolver }, - data: union { - @"export": struct { - kind: enum { regular, absolute, tlv }, - weak: bool = false, - vmoffset: u64, - }, - reexport: u64, - stub_resolver: struct { - stub_offset: u64, - resolver_offset: u64, - }, - }, - - inline fn rankByTag(@"export": Export) u3 { - return switch (@"export".tag) { - .@"export" => 1, - .reexport => 2, - .stub_resolver => 3, - }; - } - - fn lessThan(ctx: void, lhs: Export, rhs: Export) bool { - _ = ctx; - if (lhs.rankByTag() == rhs.rankByTag()) { - return switch (lhs.tag) { - .@"export" => lhs.data.@"export".vmoffset < rhs.data.@"export".vmoffset, - .reexport => lhs.data.reexport < rhs.data.reexport, - .stub_resolver => lhs.data.stub_resolver.stub_offset < rhs.data.stub_resolver.stub_offset, - }; - } - return lhs.rankByTag() < rhs.rankByTag(); - } - }; - - fn parseTrieNode( - arena: Allocator, - it: *TrieIterator, - prefix: []const u8, - exports: *std.array_list.Managed(Export), - ) !void { - const size = try it.takeLeb128(); - if (size > 0) { - const flags = try it.takeLeb128(); - switch (flags) { - macho.EXPORT_SYMBOL_FLAGS_REEXPORT => { - const ord = try it.takeLeb128(); - const name = try arena.dupe(u8, try it.readString()); - try exports.append(.{ - .name = if (name.len > 0) name else prefix, - .tag = .reexport, - .data = .{ .reexport = ord }, - }); - }, - macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { - const stub_offset = try it.takeLeb128(); - const resolver_offset = try it.takeLeb128(); - try exports.append(.{ - .name = prefix, - .tag = .stub_resolver, - .data = .{ .stub_resolver = .{ - .stub_offset = stub_offset, - .resolver_offset = resolver_offset, - } }, - }); - }, - else => { - const vmoff = try it.takeLeb128(); - try exports.append(.{ - .name = prefix, - .tag = .@"export", - .data = .{ .@"export" = .{ - .kind = switch (flags & macho.EXPORT_SYMBOL_FLAGS_KIND_MASK) { - macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR => .regular, - macho.EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE => .absolute, - macho.EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL => .tlv, - else => unreachable, - }, - .weak = flags & macho.EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION != 0, - .vmoffset = vmoff, - } }, - }); - }, - } - } - - const nedges = try it.takeByte(); - for (0..nedges) |_| { - const label = try it.readString(); - const off = try it.takeLeb128(); - const prefix_label = try std.fmt.allocPrint(arena, "{s}{s}", .{ prefix, label }); - const curr = it.stream.seek; - it.stream.seek = off; - try parseTrieNode(arena, it, prefix_label, exports); - it.stream.seek = curr; - } - } - - fn dumpSection(ctx: ObjectContext, sect: macho.section_64, writer: anytype) !void { - const data = ctx.data[sect.offset..][0..sect.size]; - try writer.print("{s}", .{data}); - } - }; - - fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { - const gpa = step.owner.allocator; - const hdr = @as(*align(1) const macho.mach_header_64, @ptrCast(bytes.ptr)).*; - if (hdr.magic != macho.MH_MAGIC_64) { - return error.InvalidMagicNumber; - } - - var ctx = ObjectContext{ .gpa = gpa, .data = bytes, .header = hdr }; - try ctx.parse(); - - var output: std.Io.Writer.Allocating = .init(gpa); - defer output.deinit(); - const writer = &output.writer; - - switch (check.kind) { - .headers => { - try ObjectContext.dumpHeader(ctx.header, writer); - - var it = try ctx.getLoadCommandIterator(); - var i: usize = 0; - while (try it.next()) |cmd| { - try ObjectContext.dumpLoadCommand(cmd, i, writer); - try writer.writeByte('\n'); - - i += 1; - } - }, - - .symtab => if (ctx.symtab.items.len > 0) { - try ctx.dumpSymtab(writer); - } else return step.fail("no symbol table found", .{}), - - .indirect_symtab => if (ctx.symtab.items.len > 0 and ctx.indsymtab.items.len > 0) { - try ctx.dumpIndirectSymtab(writer); - } else return step.fail("no indirect symbol table found", .{}), - - .dyld_rebase, - .dyld_bind, - .dyld_weak_bind, - .dyld_lazy_bind, - => { - const cmd = try ctx.getLoadCommand(.DYLD_INFO_ONLY) orelse - return step.fail("no dyld info found", .{}); - const lc = cmd.cast(macho.dyld_info_command).?; - - switch (check.kind) { - .dyld_rebase => if (lc.rebase_size > 0) { - const data = ctx.data[lc.rebase_off..][0..lc.rebase_size]; - try writer.writeAll(dyld_rebase_label ++ "\n"); - try ctx.dumpRebaseInfo(data, writer); - } else return step.fail("no rebase data found", .{}), - - .dyld_bind => if (lc.bind_size > 0) { - const data = ctx.data[lc.bind_off..][0..lc.bind_size]; - try writer.writeAll(dyld_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, writer); - } else return step.fail("no bind data found", .{}), - - .dyld_weak_bind => if (lc.weak_bind_size > 0) { - const data = ctx.data[lc.weak_bind_off..][0..lc.weak_bind_size]; - try writer.writeAll(dyld_weak_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, writer); - } else return step.fail("no weak bind data found", .{}), - - .dyld_lazy_bind => if (lc.lazy_bind_size > 0) { - const data = ctx.data[lc.lazy_bind_off..][0..lc.lazy_bind_size]; - try writer.writeAll(dyld_lazy_bind_label ++ "\n"); - try ctx.dumpBindInfo(data, writer); - } else return step.fail("no lazy bind data found", .{}), - - else => unreachable, - } - }, - - .exports => blk: { - if (try ctx.getLoadCommand(.DYLD_INFO_ONLY)) |cmd| { - const lc = cmd.cast(macho.dyld_info_command).?; - if (lc.export_size > 0) { - const data = ctx.data[lc.export_off..][0..lc.export_size]; - try writer.writeAll(exports_label ++ "\n"); - try ctx.dumpExportsTrie(data, writer); - break :blk; - } - } - return step.fail("no exports data found", .{}); - }, - - .dump_section => { - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); - const sep_index = mem.findScalar(u8, name, ',') orelse - return step.fail("invalid section name: {s}", .{name}); - const segname = name[0..sep_index]; - const sectname = name[sep_index + 1 ..]; - const sect = ctx.getSectionByName(segname, sectname) orelse - return step.fail("section '{s}' not found", .{name}); - try ctx.dumpSection(sect, writer); - }, - - else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), - } - - return output.toOwnedSlice(); - } -}; - -const ElfDumper = struct { - const symtab_label = "symbol table"; - const dynamic_symtab_label = "dynamic symbol table"; - const dynamic_section_label = "dynamic section"; - const archive_symtab_label = "archive symbol table"; - - fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { - return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) { - error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes), - else => |e| return e, - }; - } - - fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { - const gpa = step.owner.allocator; - var reader: std.Io.Reader = .fixed(bytes); - - const magic = try reader.takeArray(elf.ARMAG.len); - if (!mem.eql(u8, magic, elf.ARMAG)) { - return error.InvalidArchiveMagicNumber; - } - - if (!mem.isAligned(bytes.len, 2)) { - return error.InvalidArchivePadding; - } - - var ctx = ArchiveContext{ - .gpa = gpa, - .data = bytes, - .strtab = &[0]u8{}, - }; - defer { - for (ctx.objects.items) |*object| { - gpa.free(object.name); - } - ctx.objects.deinit(gpa); - } - - while (true) { - if (!mem.isAligned(reader.seek, 2)) reader.seek += 1; - if (reader.seek >= ctx.data.len) break; - - const hdr = try reader.takeStruct(elf.ar_hdr, .little); - - if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) return error.InvalidArchiveHeaderMagicNumber; - - const size = try hdr.size(); - defer reader.seek += size; - - if (hdr.isSymtab()) { - try ctx.parseSymtab(ctx.data[reader.seek..][0..size], .p32); - continue; - } - if (hdr.isSymtab64()) { - try ctx.parseSymtab(ctx.data[reader.seek..][0..size], .p64); - continue; - } - if (hdr.isStrtab()) { - ctx.strtab = ctx.data[reader.seek..][0..size]; - continue; - } - if (hdr.isSymdef() or hdr.isSymdefSorted()) continue; - - const name = if (hdr.name()) |name| - try gpa.dupe(u8, name) - else if (try hdr.nameOffset()) |off| - try gpa.dupe(u8, ctx.getString(off)) - else - unreachable; - - try ctx.objects.append(gpa, .{ .name = name, .off = reader.seek, .len = size }); - } - - var output: std.Io.Writer.Allocating = .init(gpa); - defer output.deinit(); - const writer = &output.writer; - - switch (check.kind) { - .archive_symtab => if (ctx.symtab.items.len > 0) { - try ctx.dumpSymtab(writer); - } else return step.fail("no archive symbol table found", .{}), - - else => if (ctx.objects.items.len > 0) { - try ctx.dumpObjects(step, check, writer); - } else return step.fail("empty archive", .{}), - } - - return output.toOwnedSlice(); - } - - const ArchiveContext = struct { - gpa: Allocator, - data: []const u8, - symtab: std.ArrayList(ArSymtabEntry) = .empty, - strtab: []const u8, - objects: std.ArrayList(struct { name: []const u8, off: usize, len: usize }) = .empty, - - fn parseSymtab(ctx: *ArchiveContext, raw: []const u8, ptr_width: enum { p32, p64 }) !void { - var reader: std.Io.Reader = .fixed(raw); - const num = switch (ptr_width) { - .p32 => try reader.takeInt(u32, .big), - .p64 => try reader.takeInt(u64, .big), - }; - const ptr_size: usize = switch (ptr_width) { - .p32 => @sizeOf(u32), - .p64 => @sizeOf(u64), - }; - const strtab_off = (num + 1) * ptr_size; - const strtab_len = raw.len - strtab_off; - const strtab = raw[strtab_off..][0..strtab_len]; - - try ctx.symtab.ensureTotalCapacityPrecise(ctx.gpa, num); - - var stroff: usize = 0; - for (0..num) |_| { - const off = switch (ptr_width) { - .p32 => try reader.takeInt(u32, .big), - .p64 => try reader.takeInt(u64, .big), - }; - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + stroff)), 0); - stroff += name.len + 1; - ctx.symtab.appendAssumeCapacity(.{ .off = off, .name = name }); - } - } - - fn dumpSymtab(ctx: ArchiveContext, writer: anytype) !void { - var files = std.AutoHashMap(usize, []const u8).init(ctx.gpa); - defer files.deinit(); - try files.ensureUnusedCapacity(@intCast(ctx.objects.items.len)); - - for (ctx.objects.items) |object| { - files.putAssumeCapacityNoClobber(object.off - @sizeOf(elf.ar_hdr), object.name); - } - - var symbols: std.array_hash_map.Auto(usize, std.array_list.Managed([]const u8)) = .empty; - defer { - for (symbols.values()) |*value| { - value.deinit(); - } - symbols.deinit(ctx.gpa); - } - - for (ctx.symtab.items) |entry| { - const gop = try symbols.getOrPut(ctx.gpa, @intCast(entry.off)); - if (!gop.found_existing) { - gop.value_ptr.* = std.array_list.Managed([]const u8).init(ctx.gpa); - } - try gop.value_ptr.append(entry.name); - } - - try writer.print("{s}\n", .{archive_symtab_label}); - for (symbols.keys(), symbols.values()) |off, values| { - try writer.print("in object {s}\n", .{files.get(off).?}); - for (values.items) |value| { - try writer.print("{s}\n", .{value}); - } - } - } - - fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { - for (ctx.objects.items) |object| { - try writer.print("object {s}\n", .{object.name}); - const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); - defer ctx.gpa.free(output); - try writer.print("{s}\n", .{output}); - } - } - - fn getString(ctx: ArchiveContext, off: u32) []const u8 { - assert(off < ctx.strtab.len); - const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(ctx.strtab.ptr + off)), 0); - return name[0 .. name.len - 1]; - } - - const ArSymtabEntry = struct { - name: [:0]const u8, - off: u64, - }; - }; - - fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { - const gpa = step.owner.allocator; - - // `std.elf.Header` takes care of endianness issues for us. - var reader: std.Io.Reader = .fixed(bytes); - const hdr = try elf.Header.read(&reader); - - var shdrs = try gpa.alloc(elf.Elf64_Shdr, hdr.shnum); - defer gpa.free(shdrs); - { - var shdr_it = hdr.iterateSectionHeadersBuffer(bytes); - var shdr_i: usize = 0; - while (try shdr_it.next()) |shdr| : (shdr_i += 1) shdrs[shdr_i] = shdr; - } - - var phdrs = try gpa.alloc(elf.Elf64_Phdr, hdr.shnum); - defer gpa.free(phdrs); - { - var phdr_it = hdr.iterateProgramHeadersBuffer(bytes); - var phdr_i: usize = 0; - while (try phdr_it.next()) |phdr| : (phdr_i += 1) phdrs[phdr_i] = phdr; - } - - var ctx = ObjectContext{ - .gpa = gpa, - .data = bytes, - .hdr = hdr, - .shdrs = shdrs, - .phdrs = phdrs, - .shstrtab = undefined, - }; - ctx.shstrtab = ctx.getSectionContents(ctx.hdr.shstrndx); - - defer gpa.free(ctx.symtab.symbols); - defer gpa.free(ctx.dysymtab.symbols); - defer gpa.free(ctx.dyns); - - for (ctx.shdrs, 0..) |shdr, i| switch (shdr.sh_type) { - elf.SHT_SYMTAB, elf.SHT_DYNSYM => { - const raw = ctx.getSectionContents(i); - const nsyms = @divExact(raw.len, @sizeOf(elf.Elf64_Sym)); - const symbols = try gpa.alloc(elf.Elf64_Sym, nsyms); - - var r: std.Io.Reader = .fixed(raw); - for (0..nsyms) |si| symbols[si] = r.takeStruct(elf.Elf64_Sym, ctx.hdr.endian) catch unreachable; - - const strings = ctx.getSectionContents(shdr.sh_link); - - switch (shdr.sh_type) { - elf.SHT_SYMTAB => { - ctx.symtab = .{ - .symbols = symbols, - .strings = strings, - }; - }, - elf.SHT_DYNSYM => { - ctx.dysymtab = .{ - .symbols = symbols, - .strings = strings, - }; - }, - else => unreachable, - } - }, - elf.SHT_DYNAMIC => { - const raw = ctx.getSectionContents(i); - const ndyns = @divExact(raw.len, @sizeOf(elf.Elf64_Dyn)); - const dyns = try gpa.alloc(elf.Elf64_Dyn, ndyns); - - var r: std.Io.Reader = .fixed(raw); - for (0..ndyns) |si| dyns[si] = r.takeStruct(elf.Elf64_Dyn, ctx.hdr.endian) catch unreachable; - - ctx.dyns = dyns; - ctx.dyns_strings = ctx.getSectionContents(shdr.sh_link); - }, - - else => {}, - }; - - var output: std.Io.Writer.Allocating = .init(gpa); - defer output.deinit(); - const writer = &output.writer; - - switch (check.kind) { - .headers => { - try ctx.dumpHeader(writer); - try ctx.dumpShdrs(writer); - try ctx.dumpPhdrs(writer); - }, - - .symtab => if (ctx.symtab.symbols.len > 0) { - try ctx.dumpSymtab(.symtab, writer); - } else return step.fail("no symbol table found", .{}), - - .dynamic_symtab => if (ctx.dysymtab.symbols.len > 0) { - try ctx.dumpSymtab(.dysymtab, writer); - } else return step.fail("no dynamic symbol table found", .{}), - - .dynamic_section => if (ctx.dyns.len > 0) { - try ctx.dumpDynamicSection(writer); - } else return step.fail("no dynamic section found", .{}), - - .dump_section => { - const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); - const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); - try ctx.dumpSection(shndx, writer); - }, - - else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), - } - - return output.toOwnedSlice(); - } - - const ObjectContext = struct { - gpa: Allocator, - data: []const u8, - hdr: elf.Header, - shdrs: []const elf.Elf64_Shdr, - phdrs: []const elf.Elf64_Phdr, - shstrtab: []const u8, - symtab: Symtab = .{}, - dysymtab: Symtab = .{}, - dyns: []const elf.Elf64_Dyn = &.{}, - dyns_strings: []const u8 = &.{}, - - fn dumpHeader(ctx: ObjectContext, writer: anytype) !void { - try writer.writeAll("header\n"); - try writer.print("type {s}\n", .{@tagName(ctx.hdr.type)}); - try writer.print("entry {x}\n", .{ctx.hdr.entry}); - } - - fn dumpPhdrs(ctx: ObjectContext, writer: anytype) !void { - if (ctx.phdrs.len == 0) return; - - try writer.writeAll("program headers\n"); - - for (ctx.phdrs, 0..) |phdr, phndx| { - try writer.print("phdr {d}\n", .{phndx}); - try writer.print("type {f}\n", .{fmtPhType(phdr.p_type)}); - try writer.print("vaddr {x}\n", .{phdr.p_vaddr}); - try writer.print("paddr {x}\n", .{phdr.p_paddr}); - try writer.print("offset {x}\n", .{phdr.p_offset}); - try writer.print("memsz {x}\n", .{phdr.p_memsz}); - try writer.print("filesz {x}\n", .{phdr.p_filesz}); - try writer.print("align {x}\n", .{phdr.p_align}); - - { - const flags = phdr.p_flags; - try writer.writeAll("flags"); - if (flags > 0) try writer.writeByte(' '); - if (flags & elf.PF_R != 0) { - try writer.writeByte('R'); - } - if (flags & elf.PF_W != 0) { - try writer.writeByte('W'); - } - if (flags & elf.PF_X != 0) { - try writer.writeByte('E'); - } - if (flags & elf.PF_MASKOS != 0) { - try writer.writeAll("OS"); - } - if (flags & elf.PF_MASKPROC != 0) { - try writer.writeAll("PROC"); - } - try writer.writeByte('\n'); - } - } - } - - fn dumpShdrs(ctx: ObjectContext, writer: anytype) !void { - if (ctx.shdrs.len == 0) return; - - try writer.writeAll("section headers\n"); - - for (ctx.shdrs, 0..) |shdr, shndx| { - try writer.print("shdr {d}\n", .{shndx}); - try writer.print("name {s}\n", .{ctx.getSectionName(shndx)}); - try writer.print("type {f}\n", .{fmtShType(shdr.sh_type)}); - try writer.print("addr {x}\n", .{shdr.sh_addr}); - try writer.print("offset {x}\n", .{shdr.sh_offset}); - try writer.print("size {x}\n", .{shdr.sh_size}); - try writer.print("addralign {x}\n", .{shdr.sh_addralign}); - // TODO dump formatted sh_flags - } - } - - fn dumpDynamicSection(ctx: ObjectContext, writer: anytype) !void { - try writer.writeAll(ElfDumper.dynamic_section_label ++ "\n"); - - for (ctx.dyns) |entry| { - const key = @as(u64, @bitCast(entry.d_tag)); - const value = entry.d_val; - - const key_str = switch (key) { - elf.DT_NEEDED => "NEEDED", - elf.DT_SONAME => "SONAME", - elf.DT_INIT_ARRAY => "INIT_ARRAY", - elf.DT_INIT_ARRAYSZ => "INIT_ARRAYSZ", - elf.DT_FINI_ARRAY => "FINI_ARRAY", - elf.DT_FINI_ARRAYSZ => "FINI_ARRAYSZ", - elf.DT_HASH => "HASH", - elf.DT_GNU_HASH => "GNU_HASH", - elf.DT_STRTAB => "STRTAB", - elf.DT_SYMTAB => "SYMTAB", - elf.DT_STRSZ => "STRSZ", - elf.DT_SYMENT => "SYMENT", - elf.DT_PLTGOT => "PLTGOT", - elf.DT_PLTRELSZ => "PLTRELSZ", - elf.DT_PLTREL => "PLTREL", - elf.DT_JMPREL => "JMPREL", - elf.DT_RELA => "RELA", - elf.DT_RELASZ => "RELASZ", - elf.DT_RELAENT => "RELAENT", - elf.DT_VERDEF => "VERDEF", - elf.DT_VERDEFNUM => "VERDEFNUM", - elf.DT_FLAGS => "FLAGS", - elf.DT_FLAGS_1 => "FLAGS_1", - elf.DT_VERNEED => "VERNEED", - elf.DT_VERNEEDNUM => "VERNEEDNUM", - elf.DT_VERSYM => "VERSYM", - elf.DT_RELACOUNT => "RELACOUNT", - elf.DT_RPATH => "RPATH", - elf.DT_RUNPATH => "RUNPATH", - elf.DT_INIT => "INIT", - elf.DT_FINI => "FINI", - elf.DT_NULL => "NULL", - else => "UNKNOWN", - }; - try writer.print("{s}", .{key_str}); - - switch (key) { - elf.DT_NEEDED, - elf.DT_SONAME, - elf.DT_RPATH, - elf.DT_RUNPATH, - => { - const name = getString(ctx.dyns_strings, @intCast(value)); - try writer.print(" {s}", .{name}); - }, - - elf.DT_INIT_ARRAY, - elf.DT_FINI_ARRAY, - elf.DT_HASH, - elf.DT_GNU_HASH, - elf.DT_STRTAB, - elf.DT_SYMTAB, - elf.DT_PLTGOT, - elf.DT_JMPREL, - elf.DT_RELA, - elf.DT_VERDEF, - elf.DT_VERNEED, - elf.DT_VERSYM, - elf.DT_INIT, - elf.DT_FINI, - elf.DT_NULL, - => try writer.print(" {x}", .{value}), - - elf.DT_INIT_ARRAYSZ, - elf.DT_FINI_ARRAYSZ, - elf.DT_STRSZ, - elf.DT_SYMENT, - elf.DT_PLTRELSZ, - elf.DT_RELASZ, - elf.DT_RELAENT, - elf.DT_RELACOUNT, - => try writer.print(" {d}", .{value}), - - elf.DT_PLTREL => try writer.writeAll(switch (value) { - elf.DT_REL => " REL", - elf.DT_RELA => " RELA", - else => " UNKNOWN", - }), - - elf.DT_FLAGS => if (value > 0) { - if (value & elf.DF_ORIGIN != 0) try writer.writeAll(" ORIGIN"); - if (value & elf.DF_SYMBOLIC != 0) try writer.writeAll(" SYMBOLIC"); - if (value & elf.DF_TEXTREL != 0) try writer.writeAll(" TEXTREL"); - if (value & elf.DF_BIND_NOW != 0) try writer.writeAll(" BIND_NOW"); - if (value & elf.DF_STATIC_TLS != 0) try writer.writeAll(" STATIC_TLS"); - }, - - elf.DT_FLAGS_1 => if (value > 0) { - if (value & elf.DF_1_NOW != 0) try writer.writeAll(" NOW"); - if (value & elf.DF_1_GLOBAL != 0) try writer.writeAll(" GLOBAL"); - if (value & elf.DF_1_GROUP != 0) try writer.writeAll(" GROUP"); - if (value & elf.DF_1_NODELETE != 0) try writer.writeAll(" NODELETE"); - if (value & elf.DF_1_LOADFLTR != 0) try writer.writeAll(" LOADFLTR"); - if (value & elf.DF_1_INITFIRST != 0) try writer.writeAll(" INITFIRST"); - if (value & elf.DF_1_NOOPEN != 0) try writer.writeAll(" NOOPEN"); - if (value & elf.DF_1_ORIGIN != 0) try writer.writeAll(" ORIGIN"); - if (value & elf.DF_1_DIRECT != 0) try writer.writeAll(" DIRECT"); - if (value & elf.DF_1_TRANS != 0) try writer.writeAll(" TRANS"); - if (value & elf.DF_1_INTERPOSE != 0) try writer.writeAll(" INTERPOSE"); - if (value & elf.DF_1_NODEFLIB != 0) try writer.writeAll(" NODEFLIB"); - if (value & elf.DF_1_NODUMP != 0) try writer.writeAll(" NODUMP"); - if (value & elf.DF_1_CONFALT != 0) try writer.writeAll(" CONFALT"); - if (value & elf.DF_1_ENDFILTEE != 0) try writer.writeAll(" ENDFILTEE"); - if (value & elf.DF_1_DISPRELDNE != 0) try writer.writeAll(" DISPRELDNE"); - if (value & elf.DF_1_DISPRELPND != 0) try writer.writeAll(" DISPRELPND"); - if (value & elf.DF_1_NODIRECT != 0) try writer.writeAll(" NODIRECT"); - if (value & elf.DF_1_IGNMULDEF != 0) try writer.writeAll(" IGNMULDEF"); - if (value & elf.DF_1_NOKSYMS != 0) try writer.writeAll(" NOKSYMS"); - if (value & elf.DF_1_NOHDR != 0) try writer.writeAll(" NOHDR"); - if (value & elf.DF_1_EDITED != 0) try writer.writeAll(" EDITED"); - if (value & elf.DF_1_NORELOC != 0) try writer.writeAll(" NORELOC"); - if (value & elf.DF_1_SYMINTPOSE != 0) try writer.writeAll(" SYMINTPOSE"); - if (value & elf.DF_1_GLOBAUDIT != 0) try writer.writeAll(" GLOBAUDIT"); - if (value & elf.DF_1_SINGLETON != 0) try writer.writeAll(" SINGLETON"); - if (value & elf.DF_1_STUB != 0) try writer.writeAll(" STUB"); - if (value & elf.DF_1_PIE != 0) try writer.writeAll(" PIE"); - }, - - else => try writer.print(" {x}", .{value}), - } - try writer.writeByte('\n'); - } - } - - fn dumpSymtab(ctx: ObjectContext, comptime @"type": enum { symtab, dysymtab }, writer: anytype) !void { - const symtab = switch (@"type") { - .symtab => ctx.symtab, - .dysymtab => ctx.dysymtab, - }; - - try writer.writeAll(switch (@"type") { - .symtab => symtab_label, - .dysymtab => dynamic_symtab_label, - } ++ "\n"); - - for (symtab.symbols, 0..) |sym, index| { - try writer.print("{x} {x}", .{ sym.st_value, sym.st_size }); - - { - if (elf.SHN_LORESERVE <= sym.st_shndx and sym.st_shndx < elf.SHN_HIRESERVE) { - if (elf.SHN_LOPROC <= sym.st_shndx and sym.st_shndx < elf.SHN_HIPROC) { - try writer.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); - } else { - const sym_ndx = switch (sym.st_shndx) { - elf.SHN_ABS => "ABS", - elf.SHN_COMMON => "COM", - elf.SHN_LIVEPATCH => "LIV", - else => "UNK", - }; - try writer.print(" {s}", .{sym_ndx}); - } - } else if (sym.st_shndx == elf.SHN_UNDEF) { - try writer.writeAll(" UND"); - } else { - try writer.print(" {x}", .{sym.st_shndx}); - } - } - - blk: { - const tt = sym.st_type(); - const sym_type = switch (tt) { - elf.STT_NOTYPE => "NOTYPE", - elf.STT_OBJECT => "OBJECT", - elf.STT_FUNC => "FUNC", - elf.STT_SECTION => "SECTION", - elf.STT_FILE => "FILE", - elf.STT_COMMON => "COMMON", - elf.STT_TLS => "TLS", - elf.STT_NUM => "NUM", - elf.STT_GNU_IFUNC => "IFUNC", - else => if (elf.STT_LOPROC <= tt and tt < elf.STT_HIPROC) { - break :blk try writer.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC}); - } else if (elf.STT_LOOS <= tt and tt < elf.STT_HIOS) { - break :blk try writer.print(" LOOS+{d}", .{tt - elf.STT_LOOS}); - } else "UNK", - }; - try writer.print(" {s}", .{sym_type}); - } - - blk: { - const bind = sym.st_bind(); - const sym_bind = switch (bind) { - elf.STB_LOCAL => "LOCAL", - elf.STB_GLOBAL => "GLOBAL", - elf.STB_WEAK => "WEAK", - elf.STB_NUM => "NUM", - else => if (elf.STB_LOPROC <= bind and bind < elf.STB_HIPROC) { - break :blk try writer.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC}); - } else if (elf.STB_LOOS <= bind and bind < elf.STB_HIOS) { - break :blk try writer.print(" LOOS+{d}", .{bind - elf.STB_LOOS}); - } else "UNKNOWN", - }; - try writer.print(" {s}", .{sym_bind}); - } - - const sym_vis = @as(elf.STV, @enumFromInt(@as(u3, @truncate(sym.st_other)))); - try writer.print(" {s}", .{@tagName(sym_vis)}); - - const sym_name = switch (sym.st_type()) { - elf.STT_SECTION => ctx.getSectionName(sym.st_shndx), - else => symtab.getName(index).?, - }; - try writer.print(" {s}\n", .{sym_name}); - } - } - - fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { - const data = ctx.getSectionContents(shndx); - try writer.print("{s}", .{data}); - } - - inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { - const shdr = ctx.shdrs[shndx]; - return getString(ctx.shstrtab, shdr.sh_name); - } - - fn getSectionContents(ctx: ObjectContext, shndx: usize) []const u8 { - const shdr = ctx.shdrs[shndx]; - assert(shdr.sh_offset < ctx.data.len); - assert(shdr.sh_offset + shdr.sh_size <= ctx.data.len); - return ctx.data[shdr.sh_offset..][0..shdr.sh_size]; - } - - fn getSectionByName(ctx: ObjectContext, name: []const u8) ?usize { - for (0..ctx.shdrs.len) |shndx| { - if (mem.eql(u8, ctx.getSectionName(shndx), name)) return shndx; - } else return null; - } - }; - - const Symtab = struct { - symbols: []const elf.Elf64_Sym = &.{}, - strings: []const u8 = &.{}, - - fn get(st: Symtab, index: usize) ?elf.Elf64_Sym { - if (index >= st.symbols.len) return null; - return st.symbols[index]; - } - - fn getName(st: Symtab, index: usize) ?[]const u8 { - const sym = st.get(index) orelse return null; - return getString(st.strings, sym.st_name); - } - }; - - fn getString(strtab: []const u8, off: u32) []const u8 { - assert(off < strtab.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); - } - - fn fmtShType(sh_type: u32) std.fmt.Alt(u32, formatShType) { - return .{ .data = sh_type }; - } - - fn formatShType(sh_type: u32, writer: *Writer) Writer.Error!void { - const name = switch (sh_type) { - elf.SHT_NULL => "NULL", - elf.SHT_PROGBITS => "PROGBITS", - elf.SHT_SYMTAB => "SYMTAB", - elf.SHT_STRTAB => "STRTAB", - elf.SHT_RELA => "RELA", - elf.SHT_HASH => "HASH", - elf.SHT_DYNAMIC => "DYNAMIC", - elf.SHT_NOTE => "NOTE", - elf.SHT_NOBITS => "NOBITS", - elf.SHT_REL => "REL", - elf.SHT_SHLIB => "SHLIB", - elf.SHT_DYNSYM => "DYNSYM", - elf.SHT_INIT_ARRAY => "INIT_ARRAY", - elf.SHT_FINI_ARRAY => "FINI_ARRAY", - elf.SHT_PREINIT_ARRAY => "PREINIT_ARRAY", - elf.SHT_GROUP => "GROUP", - elf.SHT_SYMTAB_SHNDX => "SYMTAB_SHNDX", - elf.SHT_X86_64_UNWIND => "X86_64_UNWIND", - elf.SHT_LLVM_ADDRSIG => "LLVM_ADDRSIG", - elf.SHT_GNU_HASH => "GNU_HASH", - elf.SHT_GNU_VERDEF => "VERDEF", - elf.SHT_GNU_VERNEED => "VERNEED", - elf.SHT_GNU_VERSYM => "VERSYM", - else => if (elf.SHT_LOOS <= sh_type and sh_type < elf.SHT_HIOS) { - return try writer.print("LOOS+0x{x}", .{sh_type - elf.SHT_LOOS}); - } else if (elf.SHT_LOPROC <= sh_type and sh_type < elf.SHT_HIPROC) { - return try writer.print("LOPROC+0x{x}", .{sh_type - elf.SHT_LOPROC}); - } else if (elf.SHT_LOUSER <= sh_type and sh_type < elf.SHT_HIUSER) { - return try writer.print("LOUSER+0x{x}", .{sh_type - elf.SHT_LOUSER}); - } else "UNKNOWN", - }; - try writer.writeAll(name); - } - - fn fmtPhType(ph_type: u32) std.fmt.Alt(u32, formatPhType) { - return .{ .data = ph_type }; - } - - fn formatPhType(ph_type: u32, writer: *Writer) Writer.Error!void { - const p_type = switch (ph_type) { - elf.PT_NULL => "NULL", - elf.PT_LOAD => "LOAD", - elf.PT_DYNAMIC => "DYNAMIC", - elf.PT_INTERP => "INTERP", - elf.PT_NOTE => "NOTE", - elf.PT_SHLIB => "SHLIB", - elf.PT_PHDR => "PHDR", - elf.PT_TLS => "TLS", - elf.PT_NUM => "NUM", - elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME", - elf.PT_GNU_STACK => "GNU_STACK", - elf.PT_GNU_RELRO => "GNU_RELRO", - else => if (elf.PT_LOOS <= ph_type and ph_type < elf.PT_HIOS) { - return try writer.print("LOOS+0x{x}", .{ph_type - elf.PT_LOOS}); - } else if (elf.PT_LOPROC <= ph_type and ph_type < elf.PT_HIPROC) { - return try writer.print("LOPROC+0x{x}", .{ph_type - elf.PT_LOPROC}); - } else "UNKNOWN", - }; - try writer.writeAll(p_type); - } -}; - -const WasmDumper = struct { - const symtab_label = "symbols"; - - fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { - const gpa = step.owner.allocator; - var reader: std.Io.Reader = .fixed(bytes); - - const buf = try reader.takeArray(8); - if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) { - return error.InvalidMagicByte; - } - if (!mem.eql(u8, buf[4..], &std.wasm.version)) { - return error.UnsupportedWasmVersion; - } - - var output: std.Io.Writer.Allocating = .init(gpa); - defer output.deinit(); - parseAndDumpInner(step, check, bytes, &reader, &output.writer) catch |err| switch (err) { - error.EndOfStream => try output.writer.writeAll("\n"), - else => |e| return e, - }; - return output.toOwnedSlice(); - } - - fn parseAndDumpInner( - step: *Step, - check: Check, - bytes: []const u8, - reader: *std.Io.Reader, - writer: *std.Io.Writer, - ) !void { - switch (check.kind) { - .headers => { - while (reader.takeByte()) |current_byte| { - const section = std.enums.fromInt(std.wasm.Section, current_byte) orelse { - return step.fail("Found invalid section id '{d}'", .{current_byte}); - }; - - const section_length = try reader.takeLeb128(u32); - try parseAndDumpSection(step, section, bytes[reader.seek..][0..section_length], writer); - reader.seek += section_length; - } else |_| {} // reached end of stream - }, - - else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), - } - } - - fn parseAndDumpSection( - step: *Step, - section: std.wasm.Section, - data: []const u8, - writer: *std.Io.Writer, - ) !void { - var reader: std.Io.Reader = .fixed(data); - - try writer.print( - \\Section {s} - \\size {d} - , .{ @tagName(section), data.len }); - - switch (section) { - .type, - .import, - .function, - .table, - .memory, - .global, - .@"export", - .element, - .code, - .data, - => { - const entries = try reader.takeLeb128(u32); - try writer.print("\nentries {d}\n", .{entries}); - try parseSection(step, section, data[reader.seek..], entries, writer); - }, - .custom => { - const name_length = try reader.takeLeb128(u32); - const name = data[reader.seek..][0..name_length]; - reader.seek += name_length; - try writer.print("\nname {s}\n", .{name}); - - if (mem.eql(u8, name, "name")) { - try parseDumpNames(step, &reader, writer, data); - } else if (mem.eql(u8, name, "producers")) { - try parseDumpProducers(&reader, writer, data); - } else if (mem.eql(u8, name, "target_features")) { - try parseDumpFeatures(&reader, writer, data); - } - // TODO: Implement parsing and dumping other custom sections (such as relocations) - }, - .start => { - const start = try reader.takeLeb128(u32); - try writer.print("\nstart {d}\n", .{start}); - }, - .data_count => { - const count = try reader.takeLeb128(u32); - try writer.print("\ncount {d}\n", .{count}); - }, - else => {}, // skip unknown sections - } - } - - fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { - var reader: std.Io.Reader = .fixed(data); - - switch (section) { - .type => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - const func_type = try reader.takeByte(); - if (func_type != std.wasm.function_type) { - return step.fail("expected function type, found byte '{d}'", .{func_type}); - } - const params = try reader.takeLeb128(u32); - try writer.print("params {d}\n", .{params}); - var index: u32 = 0; - while (index < params) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer); - } else index = 0; - const returns = try reader.takeLeb128(u32); - try writer.print("returns {d}\n", .{returns}); - while (index < returns) : (index += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer); - } - } - }, - .import => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - const module_name_len = try reader.takeLeb128(u32); - const module_name = data[reader.seek..][0..module_name_len]; - reader.seek += module_name_len; - const name_len = try reader.takeLeb128(u32); - const name = data[reader.seek..][0..name_len]; - reader.seek += name_len; - - const kind = std.enums.fromInt(std.wasm.ExternalKind, try reader.takeByte()) orelse { - return step.fail("invalid import kind", .{}); - }; - - try writer.print( - \\module {s} - \\name {s} - \\kind {s} - , .{ module_name, name, @tagName(kind) }); - try writer.writeByte('\n'); - switch (kind) { - .function => { - try writer.print("index {d}\n", .{try reader.takeLeb128(u32)}); - }, - .memory => { - try parseDumpLimits(&reader, writer); - }, - .global => { - _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer); - try writer.print("mutable {}\n", .{0x01 == try reader.takeLeb128(u32)}); - }, - .table => { - _ = try parseDumpType(step, std.wasm.RefType, &reader, writer); - try parseDumpLimits(&reader, writer); - }, - } - } - }, - .function => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - try writer.print("index {d}\n", .{try reader.takeLeb128(u32)}); - } - }, - .table => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.RefType, &reader, writer); - try parseDumpLimits(&reader, writer); - } - }, - .memory => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - try parseDumpLimits(&reader, writer); - } - }, - .global => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - _ = try parseDumpType(step, std.wasm.Valtype, &reader, writer); - try writer.print("mutable {}\n", .{0x01 == try reader.takeLeb128(u1)}); - try parseDumpInit(step, &reader, writer); - } - }, - .@"export" => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - const name_len = try reader.takeLeb128(u32); - const name = data[reader.seek..][0..name_len]; - reader.seek += name_len; - const kind_byte = try reader.takeLeb128(u8); - const kind = std.enums.fromInt(std.wasm.ExternalKind, kind_byte) orelse { - return step.fail("invalid export kind value '{d}'", .{kind_byte}); - }; - const index = try reader.takeLeb128(u32); - try writer.print( - \\name {s} - \\kind {s} - \\index {d} - , .{ name, @tagName(kind), index }); - try writer.writeByte('\n'); - } - }, - .element => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - try writer.print("table index {d}\n", .{try reader.takeLeb128(u32)}); - try parseDumpInit(step, &reader, writer); - - const function_indexes = try reader.takeLeb128(u32); - var function_index: u32 = 0; - try writer.print("indexes {d}\n", .{function_indexes}); - while (function_index < function_indexes) : (function_index += 1) { - try writer.print("index {d}\n", .{try reader.takeLeb128(u32)}); - } - } - }, - .code => {}, // code section is considered opaque to linker - .data => { - var i: u32 = 0; - while (i < entries) : (i += 1) { - const flags = try reader.takeLeb128(u32); - const index = if (flags & 0x02 != 0) - try reader.takeLeb128(u32) - else - 0; - try writer.print("memory index 0x{x}\n", .{index}); - if (flags == 0) { - try parseDumpInit(step, &reader, writer); - } - - const size = try reader.takeLeb128(u32); - try writer.print("size {d}\n", .{size}); - try reader.discardAll(size); // we do not care about the content of the segments - } - }, - else => unreachable, - } - } - - fn parseDumpType(step: *Step, comptime E: type, reader: *std.Io.Reader, writer: *std.Io.Writer) !E { - const byte = try reader.takeByte(); - const tag = std.enums.fromInt(E, byte) orelse { - return step.fail("invalid wasm type value '{d}'", .{byte}); - }; - try writer.print("type {s}\n", .{@tagName(tag)}); - return tag; - } - - fn parseDumpLimits(reader: anytype, writer: anytype) !void { - const flags = try reader.takeLeb128(u8); - const min = try reader.takeLeb128(u32); - - try writer.print("min {x}\n", .{min}); - if (flags != 0) { - try writer.print("max {x}\n", .{try reader.takeLeb128(u32)}); - } - } - - fn parseDumpInit(step: *Step, reader: *std.Io.Reader, writer: *std.Io.Writer) !void { - const byte = try reader.takeByte(); - const opcode = std.enums.fromInt(std.wasm.Opcode, byte) orelse { - return step.fail("invalid wasm opcode '{d}'", .{byte}); - }; - switch (opcode) { - .i32_const => try writer.print("i32.const {x}\n", .{try reader.takeLeb128(i32)}), - .i64_const => try writer.print("i64.const {x}\n", .{try reader.takeLeb128(i64)}), - .f32_const => try writer.print("f32.const {x}\n", .{@as(f32, @bitCast(try reader.takeInt(u32, .little)))}), - .f64_const => try writer.print("f64.const {x}\n", .{@as(f64, @bitCast(try reader.takeInt(u64, .little)))}), - .global_get => try writer.print("global.get {x}\n", .{try reader.takeLeb128(u32)}), - else => unreachable, - } - const end_opcode = try reader.takeLeb128(u8); - if (end_opcode != @intFromEnum(std.wasm.Opcode.end)) { - return step.fail("expected 'end' opcode in init expression", .{}); - } - } - - /// https://webassembly.github.io/spec/core/appendix/custom.html - fn parseDumpNames(step: *Step, reader: *std.Io.Reader, writer: *std.Io.Writer, data: []const u8) !void { - while (reader.seek < data.len) { - switch (try parseDumpType(step, std.wasm.NameSubsection, reader, writer)) { - // The module name subsection ... consists of a single name - // that is assigned to the module itself. - .module => { - const size = try reader.takeLeb128(u32); - const name_len = try reader.takeLeb128(u32); - if (size != name_len + 1) return error.BadSubsectionSize; - if (reader.seek + name_len > data.len) return error.UnexpectedEndOfStream; - try writer.print("name {s}\n", .{data[reader.seek..][0..name_len]}); - reader.seek += name_len; - }, - - // The function name subsection ... consists of a name map - // assigning function names to function indices. - .function, .global, .data_segment => { - const size = try reader.takeLeb128(u32); - const entries = try reader.takeLeb128(u32); - try writer.print( - \\size {d} - \\names {d} - \\ - , .{ size, entries }); - for (0..entries) |_| { - const index = try reader.takeLeb128(u32); - const name_len = try reader.takeLeb128(u32); - if (reader.seek + name_len > data.len) return error.UnexpectedEndOfStream; - const name = data[reader.seek..][0..name_len]; - reader.seek += name.len; - - try writer.print( - \\index {d} - \\name {s} - \\ - , .{ index, name }); - } - }, - - // The local name subsection ... consists of an indirect name - // map assigning local names to local indices grouped by - // function indices. - .local => { - return step.fail("TODO implement parseDumpNames for local subsections", .{}); - }, - - else => |t| return step.fail("invalid subsection type: {s}", .{@tagName(t)}), - } - } - } - - fn parseDumpProducers(reader: *std.Io.Reader, writer: *std.Io.Writer, data: []const u8) !void { - const field_count = try reader.takeLeb128(u32); - try writer.print("fields {d}\n", .{field_count}); - var current_field: u32 = 0; - while (current_field < field_count) : (current_field += 1) { - const field_name_length = try reader.takeLeb128(u32); - const field_name = data[reader.seek..][0..field_name_length]; - reader.seek += field_name_length; - - const value_count = try reader.takeLeb128(u32); - try writer.print( - \\field_name {s} - \\values {d} - , .{ field_name, value_count }); - try writer.writeByte('\n'); - var current_value: u32 = 0; - while (current_value < value_count) : (current_value += 1) { - const value_length = try reader.takeLeb128(u32); - const value = data[reader.seek..][0..value_length]; - reader.seek += value_length; - - const version_length = try reader.takeLeb128(u32); - const version = data[reader.seek..][0..version_length]; - reader.seek += version_length; - - try writer.print( - \\value_name {s} - \\version {s} - , .{ value, version }); - try writer.writeByte('\n'); - } - } - } - - fn parseDumpFeatures(reader: *std.Io.Reader, writer: *std.Io.Writer, data: []const u8) !void { - const feature_count = try reader.takeLeb128(u32); - try writer.print("features {d}\n", .{feature_count}); - - var index: u32 = 0; - while (index < feature_count) : (index += 1) { - const prefix_byte = try reader.takeLeb128(u8); - const name_length = try reader.takeLeb128(u32); - const feature_name = data[reader.seek..][0..name_length]; - reader.seek += name_length; - - try writer.print("{c} {s}\n", .{ prefix_byte, feature_name }); - } - } -}; diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index b589833883..5ce7bcb9d6 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -611,10 +611,6 @@ pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy { return b.addObjCopy(cs.getEmittedBin(), copy); } -pub fn checkObject(compile: *Compile) *Step.CheckObject { - return Step.CheckObject.create(compile.step.owner, compile.getEmittedBin(), compile.rootModuleTarget().ofmt); -} - pub fn setLinkerScript(compile: *Compile, source: LazyPath) void { const b = compile.step.owner; compile.linker_script = source.dupe(b); diff --git a/test/link/bss/build.zig b/test/link/bss/build.zig deleted file mode 100644 index 128ec04caa..0000000000 --- a/test/link/bss/build.zig +++ /dev/null @@ -1,20 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - b.default_step = test_step; - - const exe = b.addExecutable(.{ - .name = "bss", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .target = b.graph.host, - .optimize = .Debug, - }), - }); - - const run = b.addRunArtifact(exe); - run.expectStdOutEqual("0, 1, 0\n"); - - test_step.dependOn(&run.step); -} diff --git a/test/link/bss/main.zig b/test/link/bss/main.zig deleted file mode 100644 index 0d69f97450..0000000000 --- a/test/link/bss/main.zig +++ /dev/null @@ -1,17 +0,0 @@ -const std = @import("std"); - -// Stress test zerofill layout -var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; - -pub fn main() anyerror!void { - var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); - - buffer[0x10] = 1; - - try stdout_writer.interface.print("{d}, {d}, {d}\n", .{ - // workaround the dreaded decl_val - (&buffer)[0], - (&buffer)[0x10], - (&buffer)[0x1000000 - 1], - }); -} diff --git a/test/link/build.zig b/test/link/build.zig deleted file mode 100644 index b7535b7348..0000000000 --- a/test/link/build.zig +++ /dev/null @@ -1,54 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const link = @import("link.zig"); - -pub fn build(b: *std.Build) void { - const step = b.step("test", "Run link test cases"); - b.default_step = step; - - const enable_ios_sdk = b.option(bool, "enable_ios_sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false; - const enable_macos_sdk = b.option(bool, "enable_macos_sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk; - const enable_symlinks_windows = b.option(bool, "enable_symlinks_windows", "Run tests requiring presence of symlinks on Windows") orelse false; - const has_symlinks = builtin.os.tag != .windows or enable_symlinks_windows; - - const build_opts: link.BuildOptions = .{ - .has_ios_sdk = enable_ios_sdk, - .has_macos_sdk = enable_macos_sdk, - .has_symlinks = has_symlinks, - }; - step.dependOn(@import("elf.zig").testAll(b, build_opts)); - step.dependOn(@import("macho.zig").testAll(b, build_opts)); - - add_dep_steps: for (b.available_deps) |available_dep| { - const dep_name, const dep_hash = available_dep; - - const all_pkgs = @import("root").dependencies.packages; - inline for (@typeInfo(all_pkgs).@"struct".decls) |decl| { - const pkg_hash = decl.name; - if (std.mem.eql(u8, dep_hash, pkg_hash)) { - const pkg = @field(all_pkgs, pkg_hash); - if (!@hasDecl(pkg, "build_zig")) { - std.debug.panic("link test case '{s}' is missing a 'build.zig' file", .{dep_name}); - } - const requires_ios_sdk = @hasDecl(pkg.build_zig, "requires_ios_sdk") and - pkg.build_zig.requires_ios_sdk; - const requires_macos_sdk = @hasDecl(pkg.build_zig, "requires_macos_sdk") and - pkg.build_zig.requires_macos_sdk; - const requires_symlinks = @hasDecl(pkg.build_zig, "requires_symlinks") and - pkg.build_zig.requires_symlinks; - if ((requires_symlinks and !has_symlinks) or - (requires_macos_sdk and !enable_macos_sdk) or - (requires_ios_sdk and !enable_ios_sdk)) - { - continue :add_dep_steps; - } - break; - } - } else unreachable; - - const dep = b.dependency(dep_name, .{}); - const dep_step = dep.builder.default_step; - dep_step.name = b.fmt("link_test_cases.{s}", .{dep_name}); - step.dependOn(dep_step); - } -} diff --git a/test/link/build.zig.zon b/test/link/build.zig.zon deleted file mode 100644 index 4005a7d55b..0000000000 --- a/test/link/build.zig.zon +++ /dev/null @@ -1,61 +0,0 @@ -.{ - .name = .link_test_cases, - .fingerprint = 0x404f657576fec9f2, - .version = "0.0.0", - .dependencies = .{ - .bss = .{ - .path = "bss", - }, - .common_symbols_alignment = .{ - .path = "common_symbols_alignment", - }, - .interdependent_static_c_libs = .{ - .path = "interdependent_static_c_libs", - }, - .static_libs_from_object_files = .{ - .path = "static_libs_from_object_files", - }, - // WASM Cases - .wasm_archive = .{ - .path = "wasm/archive", - }, - .wasm_basic_features = .{ - .path = "wasm/basic-features", - }, - .wasm_export = .{ - .path = "wasm/export", - }, - .wasm_export_data = .{ - .path = "wasm/export-data", - }, - .wasm_extern = .{ - .path = "wasm/extern", - }, - .wasm_extern_mangle = .{ - .path = "wasm/extern-mangle", - }, - .wasm_function_table = .{ - .path = "wasm/function-table", - }, - .wasm_infer_features = .{ - .path = "wasm/infer-features", - }, - .wasm_producers = .{ - .path = "wasm/producers", - }, - .wasm_shared_memory = .{ - .path = "wasm/shared-memory", - }, - .wasm_stack_pointer = .{ - .path = "wasm/stack_pointer", - }, - .wasm_type = .{ - .path = "wasm/type", - }, - }, - .paths = .{ - "build.zig", - "build.zig.zon", - "link.zig", - }, -} diff --git a/test/link/common_symbols/a.c b/test/link/common_symbols/a.c deleted file mode 100644 index 829a96e507..0000000000 --- a/test/link/common_symbols/a.c +++ /dev/null @@ -1,6 +0,0 @@ -int i; -int j; - -int add_to_i_and_j(int x) { - return x + i + j; -} diff --git a/test/link/common_symbols/b.c b/test/link/common_symbols/b.c deleted file mode 100644 index 18e8a8c23b..0000000000 --- a/test/link/common_symbols/b.c +++ /dev/null @@ -1,7 +0,0 @@ -long i; -int j = 2; -int k; - -void incr_i() { - i++; -} diff --git a/test/link/common_symbols/build.zig b/test/link/common_symbols/build.zig deleted file mode 100644 index 39684bbead..0000000000 --- a/test/link/common_symbols/build.zig +++ /dev/null @@ -1,37 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib_a = b.addLibrary(.{ - .linkage = .static, - .name = "a", - .root_module = b.createModule(.{ - .root_source_file = null, - .optimize = optimize, - .target = b.graph.host, - }), - }); - lib_a.root_module.addCSourceFiles(.{ - .files = &.{ "c.c", "a.c", "b.c" }, - .flags = &.{"-fcommon"}, - }); - - const test_exe = b.addTest(.{ - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = optimize, - }), - }); - test_exe.root_module.linkLibrary(lib_a); - - test_step.dependOn(&b.addRunArtifact(test_exe).step); -} diff --git a/test/link/common_symbols/c.c b/test/link/common_symbols/c.c deleted file mode 100644 index fdf60b9ca8..0000000000 --- a/test/link/common_symbols/c.c +++ /dev/null @@ -1,5 +0,0 @@ -extern int k; - -int common_defined_externally() { - return k; -} diff --git a/test/link/common_symbols/main.zig b/test/link/common_symbols/main.zig deleted file mode 100644 index 255b5aa621..0000000000 --- a/test/link/common_symbols/main.zig +++ /dev/null @@ -1,16 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; - -extern fn common_defined_externally() c_int; -extern fn incr_i() void; -extern fn add_to_i_and_j(x: c_int) c_int; - -test "undef shadows common symbol: issue #9937" { - try expect(common_defined_externally() == 0); -} - -test "import C common symbols" { - incr_i(); - const res = add_to_i_and_j(2); - try expect(res == 5); -} diff --git a/test/link/common_symbols_alignment/a.c b/test/link/common_symbols_alignment/a.c deleted file mode 100644 index adff9d15f3..0000000000 --- a/test/link/common_symbols_alignment/a.c +++ /dev/null @@ -1,2 +0,0 @@ -int foo; -__attribute__((aligned(4096))) int bar; diff --git a/test/link/common_symbols_alignment/build.zig b/test/link/common_symbols_alignment/build.zig deleted file mode 100644 index 682b8a9b8f..0000000000 --- a/test/link/common_symbols_alignment/build.zig +++ /dev/null @@ -1,38 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib_a = b.addLibrary(.{ - .linkage = .static, - .name = "a", - .root_module = b.createModule(.{ - .root_source_file = null, - .optimize = optimize, - .target = b.graph.host, - }), - }); - lib_a.root_module.addCSourceFiles(.{ - .files = &.{"a.c"}, - .flags = &.{"-fcommon"}, - }); - - const test_exe = b.addTest(.{ - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .target = b.graph.host, - .optimize = optimize, - }), - }); - test_exe.root_module.linkLibrary(lib_a); - - test_step.dependOn(&b.addRunArtifact(test_exe).step); -} diff --git a/test/link/common_symbols_alignment/main.zig b/test/link/common_symbols_alignment/main.zig deleted file mode 100644 index 06316b40fa..0000000000 --- a/test/link/common_symbols_alignment/main.zig +++ /dev/null @@ -1,9 +0,0 @@ -const std = @import("std"); - -extern var foo: i32; -extern var bar: i32; - -test { - try std.testing.expect(@intFromPtr(&foo) % 4 == 0); - try std.testing.expect(@intFromPtr(&bar) % 4096 == 0); -} diff --git a/test/link/elf.zig b/test/link/elf.zig deleted file mode 100644 index f5bf07eea3..0000000000 --- a/test/link/elf.zig +++ /dev/null @@ -1,4300 +0,0 @@ -pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { - _ = build_opts; - const elf_step = b.step("test-elf", "Run ELF tests"); - - // https://github.com/ziglang/zig/issues/25323 - if (builtin.os.tag == .freebsd) return elf_step; - - // https://github.com/ziglang/zig/issues/25961 - if (comptime builtin.cpu.arch.endian() == .big) return elf_step; - - const default_target = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs - .os_tag = .linux, - }); - const x86_64_musl = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .musl, - }); - const x86_64_gnu = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .gnu, - }); - const aarch64_musl = b.resolveTargetQuery(.{ - .cpu_arch = .aarch64, - .os_tag = .linux, - .abi = .musl, - }); - const riscv64_musl = b.resolveTargetQuery(.{ - .cpu_arch = .riscv64, - .os_tag = .linux, - .abi = .musl, - }); - - // Common tests - for (&[_]std.Target.Cpu.Arch{ - .x86_64, - .aarch64, - }) |cpu_arch| { - const musl_target = b.resolveTargetQuery(.{ - .cpu_arch = cpu_arch, - .os_tag = .linux, - .abi = .musl, - }); - const gnu_target = b.resolveTargetQuery(.{ - .cpu_arch = cpu_arch, - .os_tag = .linux, - .abi = .gnu, - }); - - // Exercise linker in -r mode - elf_step.dependOn(testEmitRelocatable(b, .{ .target = musl_target })); - elf_step.dependOn(testRelocatableArchive(b, .{ .target = musl_target })); - elf_step.dependOn(testRelocatableEhFrame(b, .{ .target = musl_target })); - elf_step.dependOn(testRelocatableEhFrameComdatHeavy(b, .{ .target = musl_target })); - elf_step.dependOn(testRelocatableNoEhFrame(b, .{ .target = musl_target })); - - // Exercise linker in ar mode - elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target })); - elf_step.dependOn(testEmitStaticLibZig(b, .{ .target = musl_target })); - - // Exercise linker with LLVM backend - // musl tests - elf_step.dependOn(testAbsSymbols(b, .{ .target = musl_target })); - elf_step.dependOn(testComdatElimination(b, .{ .target = musl_target })); - elf_step.dependOn(testCommonSymbols(b, .{ .target = musl_target })); - elf_step.dependOn(testCommonSymbolsInArchive(b, .{ .target = musl_target })); - elf_step.dependOn(testCommentString(b, .{ .target = musl_target })); - elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target })); - elf_step.dependOn(testEntryPoint(b, .{ .target = musl_target })); - elf_step.dependOn(testGcSections(b, .{ .target = musl_target })); - elf_step.dependOn(testGcSectionsZig(b, .{ .target = musl_target })); - elf_step.dependOn(testImageBase(b, .{ .target = musl_target })); - elf_step.dependOn(testInitArrayOrder(b, .{ .target = musl_target })); - elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = musl_target })); - // https://codeberg.org/ziglang/zig/issues/31580 - // elf_step.dependOn(testLargeBss(b, .{ .target = musl_target })); - elf_step.dependOn(testLinkingC(b, .{ .target = musl_target })); - elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target })); - elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target })); - elf_step.dependOn(testLinksection(b, .{ .target = musl_target })); - elf_step.dependOn(testMergeStrings(b, .{ .target = musl_target })); - elf_step.dependOn(testMergeStrings2(b, .{ .target = musl_target })); - // https://github.com/ziglang/zig/issues/17451 - // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = musl_target })); - elf_step.dependOn(testTlsStatic(b, .{ .target = musl_target })); - elf_step.dependOn(testStrip(b, .{ .target = musl_target })); - - // glibc tests - elf_step.dependOn(testAsNeeded(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/17430 - // elf_step.dependOn(testCanonicalPlt(b, .{ .target = gnu_target })); - elf_step.dependOn(testCommentString(b, .{ .target = gnu_target })); - elf_step.dependOn(testCopyrel(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/17430 - // elf_step.dependOn(testCopyrelAlias(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/17430 - // elf_step.dependOn(testCopyrelAlignment(b, .{ .target = gnu_target })); - elf_step.dependOn(testDsoPlt(b, .{ .target = gnu_target })); - elf_step.dependOn(testDsoUndef(b, .{ .target = gnu_target })); - elf_step.dependOn(testExportDynamic(b, .{ .target = gnu_target })); - elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/17430 - // elf_step.dependOn(testFuncAddress(b, .{ .target = gnu_target })); - elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = gnu_target })); - elf_step.dependOn(testIFuncAlias(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/17430 - // elf_step.dependOn(testIFuncDlopen(b, .{ .target = gnu_target })); - elf_step.dependOn(testIFuncDso(b, .{ .target = gnu_target })); - elf_step.dependOn(testIFuncDynamic(b, .{ .target = gnu_target })); - elf_step.dependOn(testIFuncExport(b, .{ .target = gnu_target })); - elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = gnu_target })); - elf_step.dependOn(testIFuncNoPlt(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/17430 ?? - // elf_step.dependOn(testIFuncStatic(b, .{ .target = gnu_target })); - // elf_step.dependOn(testIFuncStaticPie(b, .{ .target = gnu_target })); - elf_step.dependOn(testInitArrayOrder(b, .{ .target = gnu_target })); - elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = gnu_target })); - elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = gnu_target })); - elf_step.dependOn(testLargeBss(b, .{ .target = gnu_target })); - elf_step.dependOn(testLinkOrder(b, .{ .target = gnu_target })); - elf_step.dependOn(testLdScript(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/23125 - // elf_step.dependOn(testLdScriptPathError(b, .{ .target = gnu_target })); - elf_step.dependOn(testLdScriptAllowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true })); - elf_step.dependOn(testLdScriptDisallowUndefinedVersion(b, .{ .target = gnu_target, .use_lld = true })); - // https://github.com/ziglang/zig/issues/17451 - // elf_step.dependOn(testNoEhFrameHdr(b, .{ .target = gnu_target })); - elf_step.dependOn(testPie(b, .{ .target = gnu_target })); - elf_step.dependOn(testPltGot(b, .{ .target = gnu_target })); - elf_step.dependOn(testPreinitArray(b, .{ .target = gnu_target })); - elf_step.dependOn(testSharedAbsSymbol(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsDfStaticTls(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsDso(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsGd(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsGdNoPlt(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsGdToIe(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsIe(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsLargeAlignment(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsLargeTbss(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsLargeStaticImage(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsLd(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsLdDso(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsLdNoPlt(b, .{ .target = gnu_target })); - // https://github.com/ziglang/zig/issues/17430 - // elf_step.dependOn(testTlsNoPic(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsOffsetAlignment(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsPic(b, .{ .target = gnu_target })); - elf_step.dependOn(testTlsSmallAlignment(b, .{ .target = gnu_target })); - elf_step.dependOn(testUnknownFileTypeError(b, .{ .target = gnu_target })); - elf_step.dependOn(testUnresolvedError(b, .{ .target = gnu_target })); - elf_step.dependOn(testWeakExports(b, .{ .target = gnu_target })); - elf_step.dependOn(testWeakUndefsDso(b, .{ .target = gnu_target })); - elf_step.dependOn(testZNow(b, .{ .target = gnu_target })); - elf_step.dependOn(testZStackSize(b, .{ .target = gnu_target })); - } - - // x86_64 specific tests - elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = x86_64_musl })); - elf_step.dependOn(testZText(b, .{ .target = x86_64_gnu })); - - // aarch64 specific tests - elf_step.dependOn(testThunks(b, .{ .target = aarch64_musl })); - - // x86_64 self-hosted backend - elf_step.dependOn(testCommentString(b, .{ .use_llvm = false, .target = default_target })); - elf_step.dependOn(testCommentStringStaticLib(b, .{ .use_llvm = false, .target = default_target })); - elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = x86_64_musl })); - elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = x86_64_musl })); - elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target })); - elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target })); - elf_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = default_target })); - elf_step.dependOn(testLinkingZig(b, .{ .use_llvm = false, .target = default_target })); - elf_step.dependOn(testLinksection(b, .{ .use_llvm = false, .target = default_target })); - elf_step.dependOn(testImportingDataDynamic(b, .{ .use_llvm = false, .target = x86_64_gnu })); - elf_step.dependOn(testImportingDataStatic(b, .{ .use_llvm = false, .target = x86_64_musl })); - - // riscv64 linker backend is currently not complete enough to support more - elf_step.dependOn(testLinkingC(b, .{ .target = riscv64_musl })); - - return elf_step; -} - -fn testAbsSymbols(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "abs-symbols", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .asm_source_bytes = - \\.globl foo - \\foo = 0x800008 - \\ - , - }); - - const exe = addExecutable(b, opts, .{ - .name = "test", - .c_source_bytes = - \\#include - \\#include - \\#include - \\#include - \\#include - \\void handler(int signum, siginfo_t *info, void *ptr) { - \\ assert((size_t)info->si_addr == 0x800008); - \\ exit(0); - \\} - \\extern int foo; - \\int main() { - \\ struct sigaction act; - \\ act.sa_flags = SA_SIGINFO | SA_RESETHAND; - \\ act.sa_sigaction = handler; - \\ sigemptyset(&act.sa_mask); - \\ sigaction(SIGSEGV, &act, 0); - \\ foo = 5; - \\ return 0; - \\} - , - }); - exe.root_module.addObject(obj); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testAsNeeded(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "as-needed", opts); - - const main_o = addObject(b, opts, .{ - .name = "main", - .c_source_bytes = - \\#include - \\int baz(); - \\int main() { - \\ printf("%d\n", baz()); - \\ return 0; - \\} - \\ - , - }); - main_o.root_module.link_libc = true; - - const libfoo = addSharedLibrary(b, opts, .{ .name = "foo" }); - addCSourceBytes(libfoo, "int foo() { return 42; }", &.{}); - - const libbar = addSharedLibrary(b, opts, .{ .name = "bar" }); - addCSourceBytes(libbar, "int bar() { return 42; }", &.{}); - - const libbaz = addSharedLibrary(b, opts, .{ .name = "baz" }); - addCSourceBytes(libbaz, - \\int foo(); - \\int baz() { return foo(); } - , &.{}); - - { - const exe = addExecutable(b, opts, .{ - .name = "test", - }); - exe.root_module.addObject(main_o); - exe.root_module.linkSystemLibrary("foo", .{ .needed = true }); - exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory()); - exe.root_module.addRPath(libfoo.getEmittedBinDirectory()); - exe.root_module.linkSystemLibrary("bar", .{ .needed = true }); - exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory()); - exe.root_module.addRPath(libbar.getEmittedBinDirectory()); - exe.root_module.linkSystemLibrary("baz", .{ .needed = true }); - exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory()); - exe.root_module.addRPath(libbaz.getEmittedBinDirectory()); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInDynamicSection(); - check.checkExact("NEEDED libfoo.so"); - check.checkExact("NEEDED libbar.so"); - check.checkExact("NEEDED libbaz.so"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ - .name = "test", - }); - exe.root_module.addObject(main_o); - exe.root_module.linkSystemLibrary("foo", .{ .needed = false }); - exe.root_module.addLibraryPath(libfoo.getEmittedBinDirectory()); - exe.root_module.addRPath(libfoo.getEmittedBinDirectory()); - exe.root_module.linkSystemLibrary("bar", .{ .needed = false }); - exe.root_module.addLibraryPath(libbar.getEmittedBinDirectory()); - exe.root_module.addRPath(libbar.getEmittedBinDirectory()); - exe.root_module.linkSystemLibrary("baz", .{ .needed = false }); - exe.root_module.addLibraryPath(libbaz.getEmittedBinDirectory()); - exe.root_module.addRPath(libbaz.getEmittedBinDirectory()); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInDynamicSection(); - check.checkNotPresent("NEEDED libbar.so"); - check.checkInDynamicSection(); - check.checkExact("NEEDED libfoo.so"); - check.checkExact("NEEDED libbaz.so"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testCanonicalPlt(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "canonical-plt", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\void *foo() { - \\ return foo; - \\} - \\void *bar() { - \\ return bar; - \\} - , &.{}); - - const b_o = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = - \\void *bar(); - \\void *baz() { - \\ return bar; - \\} - \\ - , - .pic = true, - }); - - const main_o = addObject(b, opts, .{ - .name = "main", - .c_source_bytes = - \\#include - \\void *foo(); - \\void *bar(); - \\void *baz(); - \\int main() { - \\ assert(foo == foo()); - \\ assert(bar == bar()); - \\ assert(bar == baz()); - \\ return 0; - \\} - \\ - , - .pic = false, - }); - main_o.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ - .name = "main", - }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(b_o); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - exe.pie = false; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testComdatElimination(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "comdat-elimination", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .cpp_source_bytes = - \\#include - \\inline void foo() { - \\ printf("calling foo in a\n"); - \\} - \\void hello() { - \\ foo(); - \\} - , - }); - a_o.root_module.link_libcpp = true; - - const main_o = addObject(b, opts, .{ - .name = "main", - .cpp_source_bytes = - \\#include - \\inline void foo() { - \\ printf("calling foo in main\n"); - \\} - \\void hello(); - \\int main() { - \\ foo(); - \\ hello(); - \\ return 0; - \\} - , - }); - main_o.root_module.link_libcpp = true; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(main_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\calling foo in a - \\calling foo in a - \\ - ); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(a_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\calling foo in main - \\calling foo in main - \\ - ); - test_step.dependOn(&run.step); - } - - { - const c_o = addObject(b, opts, .{ .name = "c" }); - c_o.root_module.addObject(main_o); - c_o.root_module.addObject(a_o); - - const exe = addExecutable(b, opts, .{ .name = "main3" }); - exe.root_module.addObject(c_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\calling foo in main - \\calling foo in main - \\ - ); - test_step.dependOn(&run.step); - } - - { - const d_o = addObject(b, opts, .{ .name = "d" }); - d_o.root_module.addObject(a_o); - d_o.root_module.addObject(main_o); - - const exe = addExecutable(b, opts, .{ .name = "main4" }); - exe.root_module.addObject(d_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\calling foo in a - \\calling foo in a - \\ - ); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testCommentString(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "comment-string", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = - \\pub fn main() void {} - }); - - const check = exe.checkObject(); - check.dumpSection(".comment"); - check.checkContains("zig"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testCommentStringStaticLib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "comment-string-static-lib", opts); - - const lib = addStaticLibrary(b, opts, .{ .name = "lib", .zig_source_bytes = - \\export fn foo() void {} - }); - - const check = lib.checkObject(); - check.dumpSection(".comment"); - check.checkContains("zig"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testCommonSymbols(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "common-symbols", opts); - - const exe = addExecutable(b, opts, .{ - .name = "test", - }); - addCSourceBytes(exe, - \\int foo; - \\int bar; - \\int baz = 42; - , &.{"-fcommon"}); - addCSourceBytes(exe, - \\#include - \\int foo; - \\int bar = 5; - \\int baz; - \\int main() { - \\ printf("%d %d %d\n", foo, bar, baz); - \\} - , &.{"-fcommon"}); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("0 5 42\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "common-symbols-in-archive", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = - \\#include - \\int foo; - \\int bar; - \\extern int baz; - \\__attribute__((weak)) int two(); - \\int main() { - \\ printf("%d %d %d %d\n", foo, bar, baz, two ? two() : -1); - \\} - \\ - , - .c_source_flags = &.{"-fcommon"}, - }); - a_o.root_module.link_libc = true; - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = "int foo = 5;", - .c_source_flags = &.{"-fcommon"}, - }); - - { - const c_o = addObject(b, opts, .{ - .name = "c", - .c_source_bytes = - \\int bar; - \\int two() { return 2; } - \\ - , - .c_source_flags = &.{"-fcommon"}, - }); - - const d_o = addObject(b, opts, .{ - .name = "d", - .c_source_bytes = "int baz;", - .c_source_flags = &.{"-fcommon"}, - }); - - const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); - lib.root_module.addObject(b_o); - lib.root_module.addObject(c_o); - lib.root_module.addObject(d_o); - - const exe = addExecutable(b, opts, .{ - .name = "test", - }); - exe.root_module.addObject(a_o); - exe.root_module.linkLibrary(lib); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("5 0 0 -1\n"); - test_step.dependOn(&run.step); - } - - { - const e_o = addObject(b, opts, .{ - .name = "e", - .c_source_bytes = - \\int bar = 0; - \\int baz = 7; - \\int two() { return 2; } - , - .c_source_flags = &.{"-fcommon"}, - }); - - const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); - lib.root_module.addObject(b_o); - lib.root_module.addObject(e_o); - - const exe = addExecutable(b, opts, .{ - .name = "test", - }); - exe.root_module.addObject(a_o); - exe.root_module.linkLibrary(lib); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("5 0 7 2\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testCopyrel(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "copyrel", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\int foo = 3; - \\int bar = 5; - , &.{}); - - const exe = addExecutable(b, opts, .{ - .name = "main", - .c_source_bytes = - \\#include - \\extern int foo, bar; - \\int main() { - \\ printf("%d %d\n", foo, bar); - \\ return 0; - \\} - , - }); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3 5\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testCopyrelAlias(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "copyrel-alias", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\int bruh = 31; - \\int foo = 42; - \\extern int bar __attribute__((alias("foo"))); - \\extern int baz __attribute__((alias("foo"))); - , &.{}); - - const exe = addExecutable(b, opts, .{ - .name = "main", - .pic = false, - }); - addCSourceBytes(exe, - \\#include - \\extern int foo; - \\extern int *get_bar(); - \\int main() { - \\ printf("%d %d %d\n", foo, *get_bar(), &foo == get_bar()); - \\ return 0; - \\} - , &.{}); - addCSourceBytes(exe, - \\extern int bar; - \\int *get_bar() { return &bar; } - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - exe.pie = false; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42 42 1\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testCopyrelAlignment(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "copyrel-alignment", opts); - - const a_so = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(a_so, "__attribute__((aligned(32))) int foo = 5;", &.{}); - - const b_so = addSharedLibrary(b, opts, .{ .name = "b" }); - addCSourceBytes(b_so, "__attribute__((aligned(8))) int foo = 5;", &.{}); - - const c_so = addSharedLibrary(b, opts, .{ .name = "c" }); - addCSourceBytes(c_so, "__attribute__((aligned(256))) int foo = 5;", &.{}); - - const obj = addObject(b, opts, .{ - .name = "main", - .c_source_bytes = - \\#include - \\extern int foo; - \\int main() { printf("%d\n", foo); } - \\ - , - .pic = false, - }); - obj.root_module.link_libc = true; - - const exp_stdout = "5\n"; - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(obj); - exe.root_module.linkLibrary(a_so); - exe.root_module.link_libc = true; - exe.pie = false; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkExact("name .copyrel"); - check.checkExact("addralign 20"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(obj); - exe.root_module.linkLibrary(b_so); - exe.root_module.link_libc = true; - exe.pie = false; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkExact("name .copyrel"); - check.checkExact("addralign 8"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(obj); - exe.root_module.linkLibrary(c_so); - exe.root_module.link_libc = true; - exe.pie = false; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkExact("name .copyrel"); - check.checkExact("addralign 100"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testDsoPlt(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "dso-plt", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "dso" }); - addCSourceBytes(dso, - \\#include - \\void world() { - \\ printf("world\n"); - \\} - \\void real_hello() { - \\ printf("Hello "); - \\ world(); - \\} - \\void hello() { - \\ real_hello(); - \\} - , &.{}); - dso.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ .name = "test" }); - addCSourceBytes(exe, - \\#include - \\void world() { - \\ printf("WORLD\n"); - \\} - \\void hello(); - \\int main() { - \\ hello(); - \\} - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello WORLD\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testDsoUndef(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "dso-undef", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "dso" }); - addCSourceBytes(dso, - \\extern int foo; - \\int bar = 5; - \\int baz() { return foo; } - , &.{}); - dso.root_module.link_libc = true; - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = "int foo = 3;", - }); - - const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); - lib.root_module.addObject(obj); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - exe.root_module.linkLibrary(dso); - exe.root_module.linkLibrary(lib); - addCSourceBytes(exe, - \\extern int bar; - \\int main() { - \\ return bar - 5; - \\} - , &.{}); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInDynamicSymtab(); - check.checkContains("foo"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testEmitRelocatable(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "emit-relocatable", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes = - \\const std = @import("std"); - \\extern var bar: i32; - \\export fn foo() i32 { - \\ return bar; - \\} - \\export fn printFoo() void { - \\ std.debug.print("foo={d}\n", .{foo()}); - \\} - }); - a_o.root_module.link_libc = true; - - const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes = - \\#include - \\int bar = 42; - \\void printBar() { - \\ fprintf(stderr, "bar=%d\n", bar); - \\} - }); - b_o.root_module.link_libc = true; - - const c_o = addObject(b, opts, .{ .name = "c" }); - c_o.root_module.addObject(a_o); - c_o.root_module.addObject(b_o); - - const exe = addExecutable(b, opts, .{ .name = "test", .zig_source_bytes = - \\const std = @import("std"); - \\extern fn printFoo() void; - \\extern fn printBar() void; - \\pub fn main() void { - \\ printFoo(); - \\ printBar(); - \\} - }); - exe.root_module.addObject(c_o); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdErrEqual( - \\foo=42 - \\bar=42 - \\ - ); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testEmitStaticLib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "emit-static-lib", opts); - - const obj1 = addObject(b, opts, .{ - .name = "obj1", - .c_source_bytes = - \\int foo = 0; - \\int bar = 2; - \\int fooBar() { - \\ return foo + bar; - \\} - , - }); - - const obj2 = addObject(b, opts, .{ - .name = "obj2", - .c_source_bytes = "int tentative;", - .c_source_flags = &.{"-fcommon"}, - }); - - const obj3 = addObject(b, opts, .{ - .name = "a_very_long_file_name_so_that_it_ends_up_in_strtab", - .zig_source_bytes = - \\fn weakFoo() callconv(.c) usize { - \\ return 42; - \\} - \\export var strongBar: usize = 100; - \\comptime { - \\ @export(&weakFoo, .{ .name = "weakFoo", .linkage = .weak }); - \\ @export(&strongBar, .{ .name = "strongBarAlias", .linkage = .strong }); - \\} - , - }); - - const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); - lib.root_module.addObject(obj1); - lib.root_module.addObject(obj2); - lib.root_module.addObject(obj3); - - const check = lib.checkObject(); - check.checkInArchiveSymtab(); - check.checkExact("in object obj1.o"); - check.checkExact("foo"); - check.checkInArchiveSymtab(); - check.checkExact("in object obj1.o"); - check.checkExact("bar"); - check.checkInArchiveSymtab(); - check.checkExact("in object obj1.o"); - check.checkExact("fooBar"); - check.checkInArchiveSymtab(); - check.checkExact("in object obj2.o"); - check.checkExact("tentative"); - check.checkInArchiveSymtab(); - check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o"); - check.checkExact("weakFoo"); - check.checkInArchiveSymtab(); - check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o"); - check.checkExact("strongBar"); - check.checkInArchiveSymtab(); - check.checkExact("in object a_very_long_file_name_so_that_it_ends_up_in_strtab.o"); - check.checkExact("strongBarAlias"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testEmitStaticLibZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "emit-static-lib-zig", opts); - - const obj1 = addObject(b, opts, .{ - .name = "obj1", - .zig_source_bytes = - \\export var foo: i32 = 42; - \\export var bar: i32 = 2; - , - }); - - const lib = addStaticLibrary(b, opts, .{ - .name = "lib", - .zig_source_bytes = - \\extern var foo: i32; - \\extern var bar: i32; - \\export fn fooBar() i32 { - \\ return foo + bar; - \\} - , - }); - lib.root_module.addObject(obj1); - - const exe = addExecutable(b, opts, .{ - .name = "test", - .zig_source_bytes = - \\const std = @import("std"); - \\extern fn fooBar() i32; - \\pub fn main() void { - \\ std.debug.print("{d}", .{fooBar()}); - \\} - , - }); - exe.root_module.linkLibrary(lib); - - const run = addRunArtifact(exe); - run.expectStdErrEqual("44"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testEmptyObject(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "empty-object", opts); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - addCSourceBytes(exe, "int main() { return 0; }", &.{}); - addCSourceBytes(exe, "", &.{}); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testEntryPoint(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "entry-point", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .asm_source_bytes = - \\.globl foo, bar - \\foo = 0x1000 - \\bar = 0x2000 - \\ - , - }); - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = "int main() { return 0; }", - }); - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.entry = .{ .symbol_name = "foo" }; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("entry 1000"); - test_step.dependOn(&check.step); - } - - { - // TODO looks like not assigning a unique name to this executable will - // cause an artifact collision taking the cached executable from the above - // step instead of generating a new one. - const exe = addExecutable(b, opts, .{ .name = "other" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.entry = .{ .symbol_name = "bar" }; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("entry 2000"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testExportDynamic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "export-dynamic", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .asm_source_bytes = - \\.text - \\ .globl foo - \\ .hidden foo - \\foo: - \\ nop - \\ .globl bar - \\bar: - \\ nop - \\ .globl _start - \\_start: - \\ nop - \\ - , - }); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, "int baz = 10;", &.{}); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\extern int baz; - \\int callBaz() { - \\ return baz; - \\} - , &.{}); - exe.root_module.addObject(obj); - exe.root_module.linkLibrary(dso); - exe.rdynamic = true; - - const check = exe.checkObject(); - check.checkInDynamicSymtab(); - check.checkContains("bar"); - check.checkInDynamicSymtab(); - check.checkContains("_start"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "export-symbols-from-exe", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\void expfn1(); - \\void expfn2() {} - \\ - \\void foo() { - \\ expfn1(); - \\} - , &.{}); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\void expfn1() {} - \\void expfn2() {} - \\void foo(); - \\ - \\int main() { - \\ expfn1(); - \\ expfn2(); - \\ foo(); - \\} - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInDynamicSymtab(); - check.checkContains("expfn2"); - check.checkInDynamicSymtab(); - check.checkContains("expfn1"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testFuncAddress(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "func-address", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, "void fn() {}", &.{}); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\typedef void Func(); - \\void fn(); - \\Func *const ptr = fn; - \\int main() { - \\ assert(fn == ptr); - \\} - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.pic = false; - exe.pie = false; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testGcSections(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "gc-sections", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .cpp_source_bytes = - \\#include - \\int two() { return 2; } - \\int live_var1 = 1; - \\int live_var2 = two(); - \\int dead_var1 = 3; - \\int dead_var2 = 4; - \\void live_fn1() {} - \\void live_fn2() { live_fn1(); } - \\void dead_fn1() {} - \\void dead_fn2() { dead_fn1(); } - \\int main() { - \\ printf("%d %d\n", live_var1, live_var2); - \\ live_fn2(); - \\} - , - }); - obj.link_function_sections = true; - obj.link_data_sections = true; - obj.root_module.link_libc = true; - obj.root_module.link_libcpp = true; - - { - const exe = addExecutable(b, opts, .{ .name = "test" }); - exe.root_module.addObject(obj); - exe.link_gc_sections = false; - exe.root_module.link_libc = true; - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("live_var1"); - check.checkInSymtab(); - check.checkContains("live_var2"); - check.checkInSymtab(); - check.checkContains("dead_var1"); - check.checkInSymtab(); - check.checkContains("dead_var2"); - check.checkInSymtab(); - check.checkContains("live_fn1"); - check.checkInSymtab(); - check.checkContains("live_fn2"); - check.checkInSymtab(); - check.checkContains("dead_fn1"); - check.checkInSymtab(); - check.checkContains("dead_fn2"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "test" }); - exe.root_module.addObject(obj); - exe.link_gc_sections = true; - exe.root_module.link_libc = true; - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("live_var1"); - check.checkInSymtab(); - check.checkContains("live_var2"); - check.checkInSymtab(); - check.checkNotPresent("dead_var1"); - check.checkInSymtab(); - check.checkNotPresent("dead_var2"); - check.checkInSymtab(); - check.checkContains("live_fn1"); - check.checkInSymtab(); - check.checkContains("live_fn2"); - check.checkInSymtab(); - check.checkNotPresent("dead_fn1"); - check.checkInSymtab(); - check.checkNotPresent("dead_fn2"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testGcSectionsZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "gc-sections-zig", opts); - - const obj = addObject(b, .{ - .target = opts.target, - .use_llvm = true, - }, .{ - .name = "obj", - .c_source_bytes = - \\int live_var1 = 1; - \\int live_var2 = 2; - \\int dead_var1 = 3; - \\int dead_var2 = 4; - \\void live_fn1() {} - \\void live_fn2() { live_fn1(); } - \\void dead_fn1() {} - \\void dead_fn2() { dead_fn1(); } - , - }); - obj.link_function_sections = true; - obj.link_data_sections = true; - - { - const exe = addExecutable(b, opts, .{ - .name = "test1", - .zig_source_bytes = - \\const std = @import("std"); - \\extern var live_var1: i32; - \\extern var live_var2: i32; - \\extern fn live_fn2() void; - \\pub fn main() void { - \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); - \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); - \\ live_fn2(); - \\} - , - }); - exe.root_module.addObject(obj); - exe.link_gc_sections = false; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("live_var1"); - check.checkInSymtab(); - check.checkContains("live_var2"); - check.checkInSymtab(); - check.checkContains("dead_var1"); - check.checkInSymtab(); - check.checkContains("dead_var2"); - check.checkInSymtab(); - check.checkContains("live_fn1"); - check.checkInSymtab(); - check.checkContains("live_fn2"); - check.checkInSymtab(); - check.checkContains("dead_fn1"); - check.checkInSymtab(); - check.checkContains("dead_fn2"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ - .name = "test2", - .zig_source_bytes = - \\const std = @import("std"); - \\extern var live_var1: i32; - \\extern var live_var2: i32; - \\extern fn live_fn2() void; - \\pub fn main() void { - \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); - \\ stdout_writer.interface.print("{d} {d}\n", .{ live_var1, live_var2 }) catch @panic("fail"); - \\ live_fn2(); - \\} - , - }); - exe.root_module.addObject(obj); - exe.link_gc_sections = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("live_var1"); - check.checkInSymtab(); - check.checkContains("live_var2"); - check.checkInSymtab(); - check.checkNotPresent("dead_var1"); - check.checkInSymtab(); - check.checkNotPresent("dead_var2"); - check.checkInSymtab(); - check.checkContains("live_fn1"); - check.checkInSymtab(); - check.checkContains("live_fn2"); - check.checkInSymtab(); - check.checkNotPresent("dead_fn1"); - check.checkInSymtab(); - check.checkNotPresent("dead_fn2"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testHiddenWeakUndef(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "hidden-weak-undef", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\__attribute__((weak, visibility("hidden"))) void foo(); - \\void bar() { foo(); } - , &.{}); - - const check = dso.checkObject(); - check.checkInDynamicSymtab(); - check.checkNotPresent("foo"); - check.checkInDynamicSymtab(); - check.checkContains("bar"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testIFuncAlias(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-alias", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\void foo() {} - \\int bar() __attribute__((ifunc("resolve_bar"))); - \\void *resolve_bar() { return foo; } - \\void *bar2 = bar; - \\int main() { - \\ assert(bar == bar2); - \\} - , &.{}); - exe.root_module.pic = true; - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testIFuncDlopen(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-dlopen", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\__attribute__((ifunc("resolve_foo"))) - \\void foo(void); - \\static void real_foo(void) { - \\} - \\typedef void Func(); - \\static Func *resolve_foo(void) { - \\ return real_foo; - \\} - , &.{}); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\#include - \\#include - \\typedef void Func(); - \\void foo(void); - \\int main() { - \\ void *handle = dlopen(NULL, RTLD_NOW); - \\ Func *p = dlsym(handle, "foo"); - \\ - \\ foo(); - \\ p(); - \\ assert(foo == p); - \\} - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - exe.root_module.linkSystemLibrary("dl", .{}); - exe.root_module.pic = false; - exe.pie = false; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testIFuncDso(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-dso", opts); - - const dso = addSharedLibrary(b, opts, .{ - .name = "a", - .c_source_bytes = - \\#include - \\__attribute__((ifunc("resolve_foobar"))) - \\void foobar(void); - \\static void real_foobar(void) { - \\ printf("Hello world\n"); - \\} - \\typedef void Func(); - \\static Func *resolve_foobar(void) { - \\ return real_foobar; - \\} - , - }); - dso.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ - .name = "main", - .c_source_bytes = - \\void foobar(void); - \\int main() { - \\ foobar(); - \\} - , - }); - exe.root_module.linkLibrary(dso); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testIFuncDynamic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-dynamic", opts); - - const main_c = - \\#include - \\__attribute__((ifunc("resolve_foobar"))) - \\static void foobar(void); - \\static void real_foobar(void) { - \\ printf("Hello world\n"); - \\} - \\typedef void Func(); - \\static Func *resolve_foobar(void) { - \\ return real_foobar; - \\} - \\int main() { - \\ foobar(); - \\} - ; - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, main_c, &.{}); - exe.root_module.link_libc = true; - exe.link_z_lazy = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world\n"); - test_step.dependOn(&run.step); - } - { - const exe = addExecutable(b, opts, .{ .name = "other" }); - addCSourceBytes(exe, main_c, &.{}); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testIFuncExport(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-export", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\#include - \\__attribute__((ifunc("resolve_foobar"))) - \\void foobar(void); - \\void real_foobar(void) { - \\ printf("Hello world\n"); - \\} - \\typedef void Func(); - \\Func *resolve_foobar(void) { - \\ return real_foobar; - \\} - , &.{}); - dso.root_module.link_libc = true; - - const check = dso.checkObject(); - check.checkInDynamicSymtab(); - check.checkContains("IFUNC GLOBAL DEFAULT foobar"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testIFuncFuncPtr(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-func-ptr", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\typedef int Fn(); - \\int foo() __attribute__((ifunc("resolve_foo"))); - \\int real_foo() { return 3; } - \\Fn *resolve_foo(void) { - \\ return real_foo; - \\} - , &.{}); - addCSourceBytes(exe, - \\typedef int Fn(); - \\int foo(); - \\Fn *get_foo() { return foo; } - , &.{}); - addCSourceBytes(exe, - \\#include - \\typedef int Fn(); - \\Fn *get_foo(); - \\int main() { - \\ Fn *f = get_foo(); - \\ printf("%d\n", f()); - \\} - , &.{}); - exe.root_module.pic = true; - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testIFuncNoPlt(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-noplt", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\__attribute__((ifunc("resolve_foo"))) - \\void foo(void); - \\void hello(void) { - \\ printf("Hello world\n"); - \\} - \\typedef void Fn(); - \\Fn *resolve_foo(void) { - \\ return hello; - \\} - \\int main() { - \\ foo(); - \\} - , &.{"-fno-plt"}); - exe.root_module.pic = true; - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testIFuncStatic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-static", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\void foo() __attribute__((ifunc("resolve_foo"))); - \\void hello() { - \\ printf("Hello world\n"); - \\} - \\void *resolve_foo() { - \\ return hello; - \\} - \\int main() { - \\ foo(); - \\ return 0; - \\} - , &.{}); - exe.root_module.link_libc = true; - exe.linkage = .static; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testIFuncStaticPie(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ifunc-static-pie", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\void foo() __attribute__((ifunc("resolve_foo"))); - \\void hello() { - \\ printf("Hello world\n"); - \\} - \\void *resolve_foo() { - \\ return hello; - \\} - \\int main() { - \\ foo(); - \\ return 0; - \\} - , &.{}); - exe.linkage = .static; - exe.root_module.pic = true; - exe.pie = true; - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("type DYN"); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkExact("name .dynamic"); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkNotPresent("name .interp"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testImageBase(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "image-base", opts); - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - addCSourceBytes(exe, - \\#include - \\int main() { - \\ printf("Hello World!\n"); - \\ return 0; - \\} - , &.{}); - exe.root_module.link_libc = true; - exe.image_base = 0x8000000; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello World!\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExtract("entry {addr}"); - check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0x8000000 } }); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - addCSourceBytes(exe, "void _start() {}", &.{}); - exe.image_base = 0xffffffff8000000; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExtract("entry {addr}"); - check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0xffffffff8000000 } }); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testImportingDataDynamic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "importing-data-dynamic", opts); - - const dso = addSharedLibrary(b, .{ - .target = opts.target, - .optimize = opts.optimize, - .use_llvm = true, - }, .{ - .name = "a", - .c_source_bytes = - \\#include - \\int foo = 42; - \\void printFoo() { fprintf(stderr, "lib foo=%d\n", foo); } - , - }); - dso.root_module.link_libc = true; - - const main = addExecutable(b, opts, .{ - .name = "main", - .zig_source_bytes = - \\const std = @import("std"); - \\extern var foo: i32; - \\extern fn printFoo() void; - \\pub fn main() void { - \\ std.debug.print("exe foo={d}\n", .{foo}); - \\ printFoo(); - \\ foo += 1; - \\ std.debug.print("exe foo={d}\n", .{foo}); - \\ printFoo(); - \\} - , - .strip = true, // TODO temp hack - }); - main.pie = true; - main.root_module.linkLibrary(dso); - - const run = addRunArtifact(main); - run.expectStdErrEqual( - \\exe foo=42 - \\lib foo=42 - \\exe foo=43 - \\lib foo=43 - \\ - ); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testImportingDataStatic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "importing-data-static", opts); - - const obj = addObject(b, .{ - .target = opts.target, - .optimize = opts.optimize, - .use_llvm = true, - }, .{ - .name = "a", - .c_source_bytes = "int foo = 42;", - }); - - const lib = addStaticLibrary(b, .{ - .target = opts.target, - .optimize = opts.optimize, - .use_llvm = true, - }, .{ - .name = "a", - }); - lib.root_module.addObject(obj); - - const main = addExecutable(b, opts, .{ - .name = "main", - .zig_source_bytes = - \\extern var foo: i32; - \\pub fn main() void { - \\ @import("std").debug.print("{d}\n", .{foo}); - \\} - , - .strip = true, // TODO temp hack - }); - main.root_module.linkLibrary(lib); - main.root_module.link_libc = true; - - const run = addRunArtifact(main); - run.expectStdErrEqual("42\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testInitArrayOrder(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "init-array-order", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = - \\#include - \\__attribute__((constructor(10000))) void init4() { printf("1"); } - , - }); - a_o.root_module.link_libc = true; - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = - \\#include - \\__attribute__((constructor(1000))) void init3() { printf("2"); } - , - }); - b_o.root_module.link_libc = true; - - const c_o = addObject(b, opts, .{ - .name = "c", - .c_source_bytes = - \\#include - \\__attribute__((constructor)) void init1() { printf("3"); } - , - }); - c_o.root_module.link_libc = true; - - const d_o = addObject(b, opts, .{ - .name = "d", - .c_source_bytes = - \\#include - \\__attribute__((constructor)) void init2() { printf("4"); } - , - }); - d_o.root_module.link_libc = true; - - const e_o = addObject(b, opts, .{ - .name = "e", - .c_source_bytes = - \\#include - \\__attribute__((destructor(10000))) void fini4() { printf("5"); } - , - }); - e_o.root_module.link_libc = true; - - const f_o = addObject(b, opts, .{ - .name = "f", - .c_source_bytes = - \\#include - \\__attribute__((destructor(1000))) void fini3() { printf("6"); } - , - }); - f_o.root_module.link_libc = true; - - const g_o = addObject(b, opts, .{ - .name = "g", - .c_source_bytes = - \\#include - \\__attribute__((destructor)) void fini1() { printf("7"); } - , - }); - g_o.root_module.link_libc = true; - - const h_o = addObject(b, opts, .{ .name = "h", .c_source_bytes = - \\#include - \\__attribute__((destructor)) void fini2() { printf("8"); } - }); - h_o.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, "int main() { return 0; }", &.{}); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(c_o); - exe.root_module.addObject(d_o); - exe.root_module.addObject(e_o); - exe.root_module.addObject(f_o); - exe.root_module.addObject(g_o); - exe.root_module.addObject(h_o); - - if (opts.target.result.isGnuLibC()) { - // TODO I think we need to clarify our use of `-fPIC -fPIE` flags for different targets - exe.pie = true; - } - - const run = addRunArtifact(exe); - run.expectStdOutEqual("21348756"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLargeAlignmentDso(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "large-alignment-dso", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "dso" }); - addCSourceBytes(dso, - \\#include - \\#include - \\void hello() __attribute__((aligned(32768), section(".hello"))); - \\void world() __attribute__((aligned(32768), section(".world"))); - \\void hello() { - \\ printf("Hello"); - \\} - \\void world() { - \\ printf(" world"); - \\} - \\void greet() { - \\ hello(); - \\ world(); - \\} - , &.{}); - dso.link_function_sections = true; - dso.root_module.link_libc = true; - - const check = dso.checkObject(); - check.checkInSymtab(); - check.checkExtract("{addr1} {size1} {shndx1} FUNC GLOBAL DEFAULT hello"); - check.checkInSymtab(); - check.checkExtract("{addr2} {size2} {shndx2} FUNC GLOBAL DEFAULT world"); - check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - test_step.dependOn(&check.step); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - addCSourceBytes(exe, - \\void greet(); - \\int main() { greet(); } - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLargeAlignmentExe(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "large-alignment-exe", opts); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - addCSourceBytes(exe, - \\#include - \\#include - \\ - \\void hello() __attribute__((aligned(32768), section(".hello"))); - \\void world() __attribute__((aligned(32768), section(".world"))); - \\ - \\void hello() { - \\ printf("Hello"); - \\} - \\ - \\void world() { - \\ printf(" world"); - \\} - \\ - \\int main() { - \\ hello(); - \\ world(); - \\} - , &.{}); - exe.link_function_sections = true; - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkExtract("{addr1} {size1} {shndx1} FUNC LOCAL DEFAULT hello"); - check.checkInSymtab(); - check.checkExtract("{addr2} {size2} {shndx2} FUNC LOCAL DEFAULT world"); - check.checkComputeCompare("addr1 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLargeBss(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "large-bss", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\char arr[0x100000000]; - \\int main() { - \\ return arr[2000]; - \\} - , &.{}); - exe.root_module.link_libc = true; - // Disabled to work around the ELF linker crashing. - // Can be reproduced on a x86_64-linux host by commenting out the line below. - exe.root_module.sanitize_c = .off; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLinkOrder(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "link-order", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = "void foo() {}", - .pic = true, - }); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(obj); - - const lib = addStaticLibrary(b, opts, .{ .name = "b" }); - lib.root_module.addObject(obj); - - const main_o = addObject(b, opts, .{ - .name = "main", - .c_source_bytes = - \\void foo(); - \\int main() { - \\ foo(); - \\} - , - }); - - // https://github.com/ziglang/zig/issues/17450 - // { - // const exe = addExecutable(b, opts, .{ .name = "main1"}); - // exe.root_module.addObject(main_o); - // exe.root_module.linkSystemLibrary("a", .{}); - // exe.root_module.addLibraryPath(dso.getEmittedBinDirectory()); - // exe.root_module.addRPath(dso.getEmittedBinDirectory()); - // exe.root_module.linkSystemLibrary("b", .{}); - // exe.root_module.addLibraryPath(lib.getEmittedBinDirectory()); - // exe.root_module.addRPath(lib.getEmittedBinDirectory()); - // exe.root_module.link_libc = true; - - // const check = exe.checkObject(); - // check.checkInDynamicSection(); - // check.checkContains("libb.so"); - // test_step.dependOn(&check.step); - // } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - exe.root_module.linkSystemLibrary("b", .{}); - exe.root_module.addLibraryPath(lib.getEmittedBinDirectory()); - exe.root_module.addRPath(lib.getEmittedBinDirectory()); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(dso.getEmittedBinDirectory()); - exe.root_module.addRPath(dso.getEmittedBinDirectory()); - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInDynamicSection(); - check.checkNotPresent("libb.so"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testLdScript(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ld-script", opts); - - const bar = addSharedLibrary(b, opts, .{ .name = "bar" }); - addCSourceBytes(bar, "int bar() { return 42; }", &.{}); - - const baz = addSharedLibrary(b, opts, .{ .name = "baz" }); - addCSourceBytes(baz, "int baz() { return 42; }", &.{}); - - const scripts = WriteFile.create(b); - _ = scripts.add("liba.so", "INPUT(libfoo.so libfoo2.so.1)"); - _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))"); - - // Check finding a versioned .so file that is elsewhere in the library search paths. - const scripts2 = WriteFile.create(b); - _ = scripts2.add("libfoo2.so.1", "GROUP(AS_NEEDED(-lbaz))"); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\int bar(); - \\int baz(); - \\int main() { - \\ return bar() - baz(); - \\} - , &.{}); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(scripts.getDirectory()); - exe.root_module.addLibraryPath(scripts2.getDirectory()); - exe.root_module.addLibraryPath(bar.getEmittedBinDirectory()); - exe.root_module.addLibraryPath(baz.getEmittedBinDirectory()); - exe.root_module.addRPath(bar.getEmittedBinDirectory()); - exe.root_module.addRPath(baz.getEmittedBinDirectory()); - exe.root_module.link_libc = true; - exe.allow_so_scripts = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLdScriptPathError(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ld-script-path-error", opts); - - const scripts = WriteFile.create(b); - _ = scripts.add("liba.so", "INPUT(libfoo.so)"); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, "int main() { return 0; }", &.{}); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(scripts.getDirectory()); - exe.root_module.link_libc = true; - exe.allow_so_scripts = true; - - // TODO: A future enhancement could make this error message also mention - // the file that references the missing library. - expectLinkErrors(exe, test_step, .{ - .stderr_contains = "error: libfoo.so: file listed in linker script not found", - }); - - return test_step; -} - -fn testLdScriptAllowUndefinedVersion(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ld-script-allow-undefined-version", opts); - - const so = addSharedLibrary(b, opts, .{ - .name = "add", - .zig_source_bytes = - \\export fn add(a: i32, b: i32) i32 { - \\ return a + b; - \\} - , - }); - const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }"); - so.setLinkerScript(ld); - so.linker_allow_undefined_version = true; - - const exe = addExecutable(b, opts, .{ - .name = "main", - .zig_source_bytes = - \\const std = @import("std"); - \\extern fn add(a: i32, b: i32) i32; - \\pub fn main() void { - \\ std.debug.print("{d}\n", .{add(1, 2)}); - \\} - , - }); - exe.root_module.linkLibrary(so); - exe.root_module.link_libc = true; - exe.allow_so_scripts = true; - - const run = addRunArtifact(exe); - run.expectStdErrEqual("3\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLdScriptDisallowUndefinedVersion(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "ld-script-disallow-undefined-version", opts); - - const so = addSharedLibrary(b, opts, .{ - .name = "add", - .zig_source_bytes = - \\export fn add(a: i32, b: i32) i32 { - \\ return a + b; - \\} - , - }); - const ld = b.addWriteFiles().add("add.ld", "VERSION { ADD_1.0 { global: add; sub; local: *; }; }"); - so.setLinkerScript(ld); - so.linker_allow_undefined_version = false; - so.allow_so_scripts = true; - - expectLinkErrors( - so, - test_step, - .{ - .contains = "error: ld.lld: version script assignment of 'ADD_1.0' to symbol 'sub' failed: symbol not defined", - }, - ); - - return test_step; -} - -fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts); - - const obj = addObject(b, .{ - .target = b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }), - }, .{ - .name = "a", - .c_source_bytes = "int foo;", - .strip = true, - }); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\extern int foo; - \\int main() { - \\ return foo; - \\} - , &.{}); - exe.root_module.addObject(obj); - exe.root_module.link_libc = true; - - expectLinkErrors(exe, test_step, .{ .exact = &.{ - "invalid ELF machine type: AARCH64", - "note: while parsing /?/a.o", - } }); - - return test_step; -} - -fn testLinkingC(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linking-c", opts); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - addCSourceBytes(exe, - \\#include - \\int main() { - \\ printf("Hello World!\n"); - \\ return 0; - \\} - , &.{}); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello World!\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("type EXEC"); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkNotPresent("name .dynamic"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testLinkingCpp(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linking-cpp", opts); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - addCppSourceBytes(exe, - \\#include - \\int main() { - \\ std::cout << "Hello World!" << std::endl; - \\ return 0; - \\} - , &.{}); - exe.root_module.link_libc = true; - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello World!\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("type EXEC"); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkNotPresent("name .dynamic"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testLinkingObj(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linking-obj", opts); - - const obj = addObject(b, opts, .{ - .name = "aobj", - .zig_source_bytes = - \\extern var mod: usize; - \\export fn callMe() usize { - \\ return me * mod; - \\} - \\var me: usize = 42; - , - }); - - const exe = addExecutable(b, opts, .{ - .name = "testobj", - .zig_source_bytes = - \\const std = @import("std"); - \\extern fn callMe() usize; - \\export var mod: usize = 2; - \\pub fn main() void { - \\ std.debug.print("{d}\n", .{callMe()}); - \\} - , - }); - exe.root_module.addObject(obj); - - const run = addRunArtifact(exe); - run.expectStdErrEqual("84\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLinkingStaticLib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linking-static-lib", opts); - - const obj = addObject(b, opts, .{ - .name = "bobj", - .zig_source_bytes = "export var bar: i32 = -42;", - }); - - const lib = addStaticLibrary(b, opts, .{ - .name = "alib", - .zig_source_bytes = - \\export fn foo() i32 { - \\ return 42; - \\} - , - }); - lib.root_module.addObject(obj); - - const exe = addExecutable(b, opts, .{ - .name = "testlib", - .zig_source_bytes = - \\const std = @import("std"); - \\extern fn foo() i32; - \\extern var bar: i32; - \\pub fn main() void { - \\ std.debug.print("{d}\n", .{foo() + bar}); - \\} - , - }); - exe.root_module.linkLibrary(lib); - - const run = addRunArtifact(exe); - run.expectStdErrEqual("0\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLinkingZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linking-zig-static", opts); - - const exe = addExecutable(b, opts, .{ - .name = "test", - .zig_source_bytes = - \\pub fn main() void { - \\ @import("std").debug.print("Hello World!\n", .{}); - \\} - , - }); - - const run = addRunArtifact(exe); - run.expectStdErrEqual("Hello World!\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("type EXEC"); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkNotPresent("name .dynamic"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testLinksection(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linksection", opts); - - const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes = - \\export var test_global: u32 linksection(".TestGlobal") = undefined; - \\export fn testFn() linksection(".TestFn") callconv(.c) void { - \\ TestGenericFn("A").f(); - \\} - \\fn TestGenericFn(comptime suffix: []const u8) type { - \\ return struct { - \\ fn f() linksection(".TestGenFn" ++ suffix) void {} - \\ }; - \\} - }); - - const check = obj.checkObject(); - check.checkInSymtab(); - check.checkContains("SECTION LOCAL DEFAULT .TestGlobal"); - check.checkInSymtab(); - check.checkContains("SECTION LOCAL DEFAULT .TestFn"); - check.checkInSymtab(); - check.checkContains("SECTION LOCAL DEFAULT .TestGenFnA"); - check.checkInSymtab(); - check.checkContains("OBJECT GLOBAL DEFAULT test_global"); - check.checkInSymtab(); - check.checkContains("FUNC GLOBAL DEFAULT testFn"); - - if (opts.optimize == .Debug) { - check.checkInSymtab(); - check.checkContains("FUNC LOCAL DEFAULT main.TestGenericFn("); - } - - test_step.dependOn(&check.step); - - return test_step; -} - -// Adapted from https://github.com/rui314/mold/blob/main/test/elf/mergeable-strings.sh -fn testMergeStrings(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "merge-strings", opts); - - const obj1 = addObject(b, opts, .{ .name = "a.o" }); - addCSourceBytes(obj1, - \\#include - \\#include - \\char *cstr1 = "foo"; - \\wchar_t *wide1 = L"foo"; - \\char16_t *utf16_1 = u"foo"; - \\char32_t *utf32_1 = U"foo"; - , &.{"-O2"}); - obj1.root_module.link_libc = true; - - const obj2 = addObject(b, opts, .{ .name = "b.o" }); - addCSourceBytes(obj2, - \\#include - \\#include - \\#include - \\#include - \\extern char *cstr1; - \\extern wchar_t *wide1; - \\extern char16_t *utf16_1; - \\extern char32_t *utf32_1; - \\char *cstr2 = "foo"; - \\wchar_t *wide2 = L"foo"; - \\char16_t *utf16_2 = u"foo"; - \\char32_t *utf32_2 = U"foo"; - \\int main() { - \\ printf("%p %p %p %p %p %p %p %p\n", - \\ cstr1, cstr2, wide1, wide2, utf16_1, utf16_2, utf32_1, utf32_2); - \\ assert((void*)cstr1 == (void*)cstr2); - \\ assert((void*)wide1 == (void*)wide2); - \\ assert((void*)utf16_1 == (void*)utf16_2); - \\ assert((void*)utf32_1 == (void*)utf32_2); - \\ assert((void*)wide1 == (void*)utf32_1); - \\ assert((void*)cstr1 != (void*)wide1); - \\ assert((void*)cstr1 != (void*)utf32_1); - \\ assert((void*)wide1 != (void*)utf16_1); - \\} - , &.{"-O2"}); - obj2.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(obj1); - exe.root_module.addObject(obj2); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testMergeStrings2(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "merge-strings2", opts); - - const obj1 = addObject(b, opts, .{ .name = "a", .zig_source_bytes = - \\const std = @import("std"); - \\export fn foo() void { - \\ var arr: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; - \\ const slice = std.mem.sliceTo(&arr, 3); - \\ std.testing.expectEqualSlices(u16, arr[0..2], slice) catch unreachable; - \\} - }); - - const obj2 = addObject(b, opts, .{ .name = "b", .zig_source_bytes = - \\const std = @import("std"); - \\extern fn foo() void; - \\pub fn main() void { - \\ foo(); - \\ var arr: [5:0]u16 = [_:0]u16{ 5, 4, 3, 2, 1 }; - \\ const slice = std.mem.sliceTo(&arr, 3); - \\ std.testing.expectEqualSlices(u16, arr[0..2], slice) catch unreachable; - \\} - }); - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(obj1); - exe.root_module.addObject(obj2); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.dumpSection(".rodata.str"); - check.checkContains("\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00\x00"); - check.dumpSection(".rodata.str"); - check.checkContains("\x05\x00\x04\x00\x03\x00\x02\x00\x01\x00\x00\x00"); - test_step.dependOn(&check.step); - } - - { - const obj3 = addObject(b, opts, .{ .name = "c" }); - obj3.root_module.addObject(obj1); - obj3.root_module.addObject(obj2); - - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(obj3); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.dumpSection(".rodata.str"); - check.checkContains("\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x00\x00"); - check.dumpSection(".rodata.str"); - check.checkContains("\x05\x00\x04\x00\x03\x00\x02\x00\x01\x00\x00\x00"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testNoEhFrameHdr(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "no-eh-frame-hdr", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, "int main() { return 0; }", &.{}); - exe.link_eh_frame_hdr = false; - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkNotPresent("name .eh_frame_hdr"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testPie(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "hello-pie", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\int main() { - \\ printf("Hello!\n"); - \\ return 0; - \\} - , &.{}); - exe.root_module.link_libc = true; - exe.root_module.pic = true; - exe.pie = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello!\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("type DYN"); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkExact("name .dynamic"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testPltGot(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "plt-got", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\#include - \\void ignore(void *foo) {} - \\void hello() { - \\ printf("Hello world\n"); - \\} - , &.{}); - dso.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\void ignore(void *); - \\int hello(); - \\void foo() { ignore(hello); } - \\int main() { hello(); } - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.pic = true; - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testPreinitArray(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "preinit-array", opts); - - { - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = "void _start() {}", - }); - - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(obj); - - const check = exe.checkObject(); - check.checkInDynamicSection(); - check.checkNotPresent("PREINIT_ARRAY"); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - addCSourceBytes(exe, - \\void preinit_fn() {} - \\int main() {} - \\__attribute__((section(".preinit_array"))) - \\void *preinit[] = { preinit_fn }; - , &.{}); - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInDynamicSection(); - check.checkContains("PREINIT_ARRAY"); - } - - return test_step; -} - -fn testRelocatableArchive(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "relocatable-archive", opts); - - const obj1 = addObject(b, opts, .{ - .name = "obj1", - .c_source_bytes = - \\void bar(); - \\void foo() { - \\ bar(); - \\} - , - }); - - const obj2 = addObject(b, opts, .{ - .name = "obj2", - .c_source_bytes = - \\void bar() {} - , - }); - - const obj3 = addObject(b, opts, .{ - .name = "obj3", - .c_source_bytes = - \\void baz(); - , - }); - - const obj4 = addObject(b, opts, .{ - .name = "obj4", - .c_source_bytes = - \\void foo(); - \\int main() { - \\ foo(); - \\} - , - }); - - const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); - lib.root_module.addObject(obj1); - lib.root_module.addObject(obj2); - lib.root_module.addObject(obj3); - - const obj5 = addObject(b, opts, .{ - .name = "obj5", - }); - obj5.root_module.addObject(obj4); - obj5.root_module.linkLibrary(lib); - - const check = obj5.checkObject(); - check.checkInSymtab(); - check.checkContains("foo"); - check.checkInSymtab(); - check.checkContains("bar"); - check.checkInSymtab(); - check.checkNotPresent("baz"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "relocatable-eh-frame", opts); - - const obj1 = addObject(b, opts, .{ - .name = "obj1", - .cpp_source_bytes = - \\#include - \\int try_me() { - \\ throw std::runtime_error("Oh no!"); - \\} - , - }); - obj1.root_module.link_libcpp = true; - const obj2 = addObject(b, opts, .{ - .name = "obj2", - .cpp_source_bytes = - \\extern int try_me(); - \\int try_again() { - \\ return try_me(); - \\} - , - }); - obj2.root_module.link_libcpp = true; - const obj3 = addObject(b, opts, .{ .name = "obj3", .cpp_source_bytes = - \\#include - \\#include - \\extern int try_again(); - \\int main() { - \\ try { - \\ try_again(); - \\ } catch (const std::exception &e) { - \\ std::cout << "exception=" << e.what(); - \\ } - \\ return 0; - \\} - }); - obj3.root_module.link_libcpp = true; - - { - const obj = addObject(b, opts, .{ .name = "obj" }); - obj.root_module.addObject(obj1); - obj.root_module.addObject(obj2); - obj.root_module.link_libcpp = true; - - const exe = addExecutable(b, opts, .{ .name = "test1" }); - exe.root_module.addObject(obj3); - exe.root_module.addObject(obj); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("exception=Oh no!"); - test_step.dependOn(&run.step); - } - { - // Flipping the order should not influence the end result. - const obj = addObject(b, opts, .{ .name = "obj" }); - obj.root_module.addObject(obj2); - obj.root_module.addObject(obj1); - obj.root_module.link_libcpp = true; - - const exe = addExecutable(b, opts, .{ .name = "test2" }); - exe.root_module.addObject(obj3); - exe.root_module.addObject(obj); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("exception=Oh no!"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testRelocatableEhFrameComdatHeavy(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "relocatable-eh-frame-comdat-heavy", opts); - - const obj1 = addObject(b, opts, .{ - .name = "obj1", - .cpp_source_bytes = - \\#include - \\int try_me() { - \\ throw std::runtime_error("Oh no!"); - \\} - , - }); - obj1.root_module.link_libcpp = true; - const obj2 = addObject(b, opts, .{ - .name = "obj2", - .cpp_source_bytes = - \\extern int try_me(); - \\int try_again() { - \\ return try_me(); - \\} - , - }); - obj2.root_module.link_libcpp = true; - const obj3 = addObject(b, opts, .{ - .name = "obj3", - .cpp_source_bytes = - \\#include - \\#include - \\extern int try_again(); - \\int main() { - \\ try { - \\ try_again(); - \\ } catch (const std::exception &e) { - \\ std::cout << "exception=" << e.what(); - \\ } - \\ return 0; - \\} - , - }); - obj3.root_module.link_libcpp = true; - - const obj = addObject(b, opts, .{ .name = "obj" }); - obj.root_module.addObject(obj1); - obj.root_module.addObject(obj2); - obj.root_module.addObject(obj3); - obj.root_module.link_libcpp = true; - - const exe = addExecutable(b, opts, .{ .name = "test2" }); - exe.root_module.addObject(obj); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("exception=Oh no!"); - test_step.dependOn(&run.step); - - return test_step; -} - -// Adapted from https://github.com/rui314/mold/blob/main/test/elf/relocatable-mergeable-sections.sh -fn testRelocatableMergeStrings(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "relocatable-merge-strings", opts); - - const obj1 = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.section .rodata.str1.1,"aMS",@progbits,1 - \\val1: - \\.ascii "Hello \0" - \\.section .rodata.str1.1,"aMS",@progbits,1 - \\val5: - \\.ascii "World \0" - \\.section .rodata.str1.1,"aMS",@progbits,1 - \\val7: - \\.ascii "Hello \0" - }); - - const obj2 = addObject(b, opts, .{ .name = "b" }); - obj2.root_module.addObject(obj1); - - const check = obj2.checkObject(); - check.dumpSection(".rodata.str1.1"); - check.checkExact("Hello \x00World \x00"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "relocatable-no-eh-frame", opts); - - const obj1 = addObject(b, opts, .{ - .name = "obj1", - .c_source_bytes = "int bar() { return 42; }", - .c_source_flags = &.{ - "-fno-unwind-tables", - "-fno-asynchronous-unwind-tables", - }, - }); - - const obj2 = addObject(b, opts, .{ - .name = "obj2", - }); - obj2.root_module.addObject(obj1); - - const check1 = obj1.checkObject(); - check1.checkInHeaders(); - check1.checkExact("section headers"); - check1.checkNotPresent(".eh_frame"); - test_step.dependOn(&check1.step); - - const check2 = obj2.checkObject(); - check2.checkInHeaders(); - check2.checkExact("section headers"); - check2.checkNotPresent(".eh_frame"); - test_step.dependOn(&check2.step); - - return test_step; -} - -fn testSharedAbsSymbol(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "shared-abs-symbol", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addAsmSourceBytes(dso, - \\.globl foo - \\foo = 3; - ); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = - \\#include - \\extern char foo; - \\int main() { printf("foo=%p\n", &foo); } - , - .pic = true, - }); - obj.root_module.link_libc = true; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(obj); - exe.root_module.linkLibrary(dso); - exe.pie = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("foo=0x3\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkExact("type DYN"); - // TODO fix/improve in CheckObject - // check.checkInSymtab(); - // check.checkNotPresent("foo"); - test_step.dependOn(&check.step); - } - - // https://github.com/ziglang/zig/issues/17430 - // { - // const exe = addExecutable(b, opts, .{ .name = "main2"}); - // exe.root_module.addObject(obj); - // exe.root_module.linkLibrary(dso); - // exe.pie = false; - - // const run = addRunArtifact(exe); - // run.expectStdOutEqual("foo=0x3\n"); - // test_step.dependOn(&run.step); - - // const check = exe.checkObject(); - // check.checkInHeaders(); - // check.checkExact("header"); - // check.checkExact("type EXEC"); - // // TODO fix/improve in CheckObject - // // check.checkInSymtab(); - // // check.checkNotPresent("foo"); - // test_step.dependOn(&check.step); - // } - - return test_step; -} - -fn testStrip(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "strip", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = - \\#include - \\int main() { - \\ printf("Hello!\n"); - \\ return 0; - \\} - , - }); - obj.root_module.link_libc = true; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(obj); - exe.root_module.strip = false; - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkExact("name .debug_info"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(obj); - exe.root_module.strip = true; - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("section headers"); - check.checkNotPresent("name .debug_info"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testThunks(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "thunks", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\void foo(); - \\__attribute__((section(".bar"))) void bar() { - \\ return foo(); - \\} - \\__attribute__((section(".foo"))) void foo() { - \\ return bar(); - \\} - \\int main() { - \\ foo(); - \\ bar(); - \\ return 0; - \\} - }); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("foo$thunk"); - check.checkInSymtab(); - check.checkContains("bar$thunk"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testTlsDfStaticTls(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-df-static-tls", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = - \\static _Thread_local int foo = 5; - \\void mutate() { ++foo; } - \\int bar() { return foo; } - , - .c_source_flags = &.{"-ftls-model=initial-exec"}, - .pic = true, - }); - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(obj); - // dso.link_relax = true; - - const check = dso.checkObject(); - check.checkInDynamicSection(); - check.checkContains("STATIC_TLS"); - test_step.dependOn(&check.step); - } - - // TODO add -Wl,--no-relax - // { - // const dso = addSharedLibrary(b, opts, .{ .name = "a"}); - // dso.root_module.addObject(obj); - // dso.link_relax = false; - - // const check = dso.checkObject(); - // check.checkInDynamicSection(); - // check.checkContains("STATIC_TLS"); - // test_step.dependOn(&check.step); - // } - - return test_step; -} - -fn testTlsDso(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-dso", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\extern _Thread_local int foo; - \\_Thread_local int bar; - \\int get_foo1() { return foo; } - \\int get_bar1() { return bar; } - , &.{}); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\_Thread_local int foo; - \\extern _Thread_local int bar; - \\int get_foo1(); - \\int get_bar1(); - \\int get_foo2() { return foo; } - \\int get_bar2() { return bar; } - \\int main() { - \\ foo = 5; - \\ bar = 3; - \\ printf("%d %d %d %d %d %d\n", - \\ foo, bar, - \\ get_foo1(), get_bar1(), - \\ get_foo2(), get_bar2()); - \\ return 0; - \\} - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("5 3 5 3 5 3\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsGd(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-gd", opts); - - const main_o = addObject(b, opts, .{ - .name = "main", - .c_source_bytes = - \\#include - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2; - \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3; - \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4; - \\int get_x5(); - \\int get_x6(); - \\int main() { - \\ x2 = 2; - \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6()); - \\ return 0; - \\} - , - .pic = true, - }); - main_o.root_module.link_libc = true; - - const a_o = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5; - \\int get_x5() { return x5; } - , - .pic = true, - }); - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6; - \\int get_x6() { return x6; } - , - .pic = true, - }); - - const exp_stdout = "1 2 3 4 5 6\n"; - - const dso1 = addSharedLibrary(b, opts, .{ .name = "a" }); - dso1.root_module.addObject(a_o); - - const dso2 = addSharedLibrary(b, opts, .{ .name = "b" }); - dso2.root_module.addObject(b_o); - // dso2.link_relax = false; // TODO - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.linkLibrary(dso1); - exe.root_module.linkLibrary(dso2); - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - // exe.link_relax = false; // TODO - exe.root_module.linkLibrary(dso1); - exe.root_module.linkLibrary(dso2); - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - // https://github.com/ziglang/zig/issues/17430 ?? - // { - // const exe = addExecutable(b, opts, .{ .name = "main3"}); - // exe.root_module.addObject(main_o); - // exe.root_module.linkLibrary(dso1); - // exe.root_module.linkLibrary(dso2); - // exe.linkage = .static; - - // const run = addRunArtifact(exe); - // run.expectStdOutEqual(exp_stdout); - // test_step.dependOn(&run.step); - // } - - // { - // const exe = addExecutable(b, opts, .{ .name = "main4"}); - // exe.root_module.addObject(main_o); - // // exe.link_relax = false; // TODO - // exe.root_module.linkLibrary(dso1); - // exe.root_module.linkLibrary(dso2); - // exe.linkage = .static; - - // const run = addRunArtifact(exe); - // run.expectStdOutEqual(exp_stdout); - // test_step.dependOn(&run.step); - // } - - return test_step; -} - -fn testTlsGdNoPlt(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-gd-no-plt", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = - \\#include - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2; - \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x3; - \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int x4; - \\int get_x5(); - \\int get_x6(); - \\int main() { - \\ x2 = 2; - \\ - \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6()); - \\ return 0; - \\} - , - .c_source_flags = &.{"-fno-plt"}, - .pic = true, - }); - obj.root_module.link_libc = true; - - const a_so = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(a_so, - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5; - \\int get_x5() { return x5; } - , &.{"-fno-plt"}); - - const b_so = addSharedLibrary(b, opts, .{ .name = "b" }); - addCSourceBytes(b_so, - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6; - \\int get_x6() { return x6; } - , &.{"-fno-plt"}); - // b_so.link_relax = false; // TODO - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(obj); - exe.root_module.linkLibrary(a_so); - exe.root_module.linkLibrary(b_so); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2 3 4 5 6\n"); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(obj); - exe.root_module.linkLibrary(a_so); - exe.root_module.linkLibrary(b_so); - exe.root_module.link_libc = true; - // exe.link_relax = false; // TODO - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2 3 4 5 6\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testTlsGdToIe(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-gd-to-ie", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = - \\#include - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1; - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x2 = 2; - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3; - \\int foo() { - \\ x3 = 3; - \\ - \\ printf("%d %d %d\n", x1, x2, x3); - \\ return 0; - \\} - , - .pic = true, - }); - a_o.root_module.link_libc = true; - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = - \\int foo(); - \\int main() { foo(); } - , - .pic = true, - }); - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a1" }); - dso.root_module.addObject(a_o); - - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(b_o); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2 3\n"); - test_step.dependOn(&run.step); - } - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a2" }); - dso.root_module.addObject(a_o); - // dso.link_relax = false; // TODO - - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(b_o); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2 3\n"); - test_step.dependOn(&run.step); - } - - // { - // const dso = addSharedLibrary(b, opts, .{ .name = "a"}); - // dso.root_module.addObject(a_o); - // dso.link_z_nodlopen = true; - - // const exe = addExecutable(b, opts, .{ .name = "main"}); - // exe.root_module.addObject(b_o); - // exe.root_module.linkLibrary(dso); - - // const run = addRunArtifact(exe); - // run.expectStdOutEqual("1 2 3\n"); - // test_step.dependOn(&run.step); - // } - - // { - // const dso = addSharedLibrary(b, opts, .{ .name = "a"}); - // dso.root_module.addObject(a_o); - // dso.link_relax = false; - // dso.link_z_nodlopen = true; - - // const exe = addExecutable(b, opts, .{ .name = "main"}); - // exe.root_module.addObject(b_o); - // exe.root_module.linkLibrary(dso); - - // const run = addRunArtifact(exe); - // run.expectStdOutEqual("1 2 3\n"); - // test_step.dependOn(&run.step); - // } - - return test_step; -} - -fn testTlsIe(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-ie", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\#include - \\__attribute__((tls_model("initial-exec"))) static _Thread_local int foo; - \\__attribute__((tls_model("initial-exec"))) static _Thread_local int bar; - \\void set() { - \\ foo = 3; - \\ bar = 5; - \\} - \\void print() { - \\ printf("%d %d ", foo, bar); - \\} - , &.{}); - dso.root_module.link_libc = true; - - const main_o = addObject(b, opts, .{ - .name = "main", - .c_source_bytes = - \\#include - \\_Thread_local int baz; - \\void set(); - \\void print(); - \\int main() { - \\ baz = 7; - \\ print(); - \\ set(); - \\ print(); - \\ printf("%d\n", baz); - \\} - , - }); - main_o.root_module.link_libc = true; - - const exp_stdout = "0 0 3 5 7\n"; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - // exe.link_relax = false; // TODO - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testTlsLargeAlignment(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-large-alignment", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = - \\__attribute__((section(".tdata1"))) - \\_Thread_local int x = 42; - , - .c_source_flags = &.{"-std=c11"}, - .pic = true, - }); - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = - \\__attribute__((section(".tdata2"))) - \\_Alignas(256) _Thread_local int y[] = { 1, 2, 3 }; - , - .c_source_flags = &.{"-std=c11"}, - .pic = true, - }); - - const c_o = addObject(b, opts, .{ - .name = "c", - .c_source_bytes = - \\#include - \\extern _Thread_local int x; - \\extern _Thread_local int y[]; - \\int main() { - \\ printf("%d %d %d %d\n", x, y[0], y[1], y[2]); - \\} - , - .pic = true, - }); - c_o.root_module.link_libc = true; - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(a_o); - dso.root_module.addObject(b_o); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(c_o); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42 1 2 3\n"); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(c_o); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42 1 2 3\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testTlsLargeTbss(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-large-tbss", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addAsmSourceBytes(exe, - \\.globl x, y - \\.section .tbss,"awT",@nobits - \\x: - \\.zero 1024 - \\.section .tcommon,"awT",@nobits - \\y: - \\.zero 1024 - ); - addCSourceBytes(exe, - \\#include - \\extern _Thread_local char x[1024000]; - \\extern _Thread_local char y[1024000]; - \\int main() { - \\ x[0] = 3; - \\ x[1023] = 5; - \\ printf("%d %d %d %d %d %d\n", x[0], x[1], x[1023], y[0], y[1], y[1023]); - \\} - , &.{}); - exe.root_module.link_libc = true; - // Disabled to work around the ELF linker crashing. - // Can be reproduced on a x86_64-linux host by commenting out the line below. - exe.root_module.sanitize_c = .off; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3 0 5 0 0 0\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-large-static-image", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, "_Thread_local int x[] = { 1, 2, 3, [10000] = 5 };", &.{}); - addCSourceBytes(exe, - \\#include - \\extern _Thread_local int x[]; - \\int main() { - \\ printf("%d %d %d %d %d\n", x[0], x[1], x[2], x[3], x[10000]); - \\} - , &.{}); - exe.root_module.pic = true; - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2 3 0 5\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsLd(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-ld", opts); - - const main_o = addObject(b, opts, .{ - .name = "main", - .c_source_bytes = - \\#include - \\extern _Thread_local int foo; - \\static _Thread_local int bar; - \\int *get_foo_addr() { return &foo; } - \\int *get_bar_addr() { return &bar; } - \\int main() { - \\ bar = 5; - \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar); - \\ return 0; - \\} - , - .c_source_flags = &.{"-ftls-model=local-dynamic"}, - .pic = true, - }); - main_o.root_module.link_libc = true; - - const a_o = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = "_Thread_local int foo = 3;", - .c_source_flags = &.{"-ftls-model=local-dynamic"}, - .pic = true, - }); - - const exp_stdout = "3 5 3 5\n"; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(a_o); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(a_o); - exe.root_module.link_libc = true; - // exe.link_relax = false; // TODO - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testTlsLdDso(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-ld-dso", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\static _Thread_local int def, def1; - \\int f0() { return ++def; } - \\int f1() { return ++def1 + def; } - , &.{"-ftls-model=local-dynamic"}); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\extern int f0(); - \\extern int f1(); - \\int main() { - \\ int x = f0(); - \\ int y = f1(); - \\ printf("%d %d\n", x, y); - \\ return 0; - \\} - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsLdNoPlt(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-ld-no-plt", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = - \\#include - \\extern _Thread_local int foo; - \\static _Thread_local int bar; - \\int *get_foo_addr() { return &foo; } - \\int *get_bar_addr() { return &bar; } - \\int main() { - \\ bar = 5; - \\ - \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar); - \\ return 0; - \\} - , - .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" }, - .pic = true, - }); - a_o.root_module.link_libc = true; - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = "_Thread_local int foo = 3;", - .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" }, - .pic = true, - }); - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3 5 3 5\n"); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.link_libc = true; - // exe.link_relax = false; // TODO - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3 5 3 5\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testTlsNoPic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-no-pic", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar; - \\int *get_foo_addr() { return &foo; } - \\int *get_bar_addr() { return &bar; } - \\int main() { - \\ foo = 3; - \\ bar = 5; - \\ - \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar); - \\ return 0; - \\} - , .{}); - addCSourceBytes(exe, - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo; - , &.{}); - exe.root_module.pic = false; - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3 5 3 5\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-offset-alignment", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\#include - \\#include - \\ - \\// .tdata - \\_Thread_local int x = 42; - \\// .tbss - \\__attribute__ ((aligned(64))) - \\_Thread_local int y = 0; - \\ - \\void *verify(void *unused) { - \\ assert((unsigned long)(&y) % 64 == 0); - \\ return NULL; - \\} - , &.{}); - dso.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\#include - \\#include - \\#include - \\void *(*verify)(void *); - \\ - \\int main() { - \\ void *handle = dlopen("liba.so", RTLD_NOW); - \\ if (!handle) { - \\ fprintf(stderr, "dlopen failed: %s\n", dlerror()); - \\ return 1; - \\ } - \\ *(void**)(&verify) = dlsym(handle, "verify"); - \\ assert(verify); - \\ - \\ pthread_t thread; - \\ - \\ verify(NULL); - \\ - \\ pthread_create(&thread, NULL, verify, NULL); - \\ pthread_join(thread, NULL); - \\} - , &.{}); - exe.root_module.addRPath(dso.getEmittedBinDirectory()); - exe.root_module.link_libc = true; - exe.root_module.pic = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsPic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-pic", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = - \\#include - \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo; - \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar; - \\int *get_foo_addr() { return &foo; } - \\int *get_bar_addr() { return &bar; } - \\int main() { - \\ bar = 5; - \\ - \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar); - \\ return 0; - \\} - , - .pic = true, - }); - obj.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3; - , &.{}); - exe.root_module.addObject(obj); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3 5 3 5\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsSmallAlignment(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-small-alignment", opts); - - const a_o = addObject(b, opts, .{ - .name = "a", - .asm_source_bytes = - \\.text - \\.byte 0 - \\ - , - .pic = true, - }); - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = "_Thread_local char x = 42;", - .c_source_flags = &.{"-std=c11"}, - .pic = true, - }); - - const c_o = addObject(b, opts, .{ - .name = "c", - .c_source_bytes = - \\#include - \\extern _Thread_local char x; - \\int main() { - \\ printf("%d\n", x); - \\} - , - .pic = true, - }); - c_o.root_module.link_libc = true; - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(c_o); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42\n"); - test_step.dependOn(&run.step); - } - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(a_o); - dso.root_module.addObject(b_o); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(c_o); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testTlsStatic(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-static", opts); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - addCSourceBytes(exe, - \\#include - \\_Thread_local int a = 10; - \\_Thread_local int b; - \\_Thread_local char c = 'a'; - \\int main(int argc, char* argv[]) { - \\ printf("%d %d %c\n", a, b, c); - \\ a += 1; - \\ b += 1; - \\ c += 1; - \\ printf("%d %d %c\n", a, b, c); - \\ return 0; - \\} - , &.{}); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\10 0 a - \\11 1 b - \\ - ); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testUnknownFileTypeError(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "unknown-file-type-error", opts); - - const dylib = addSharedLibrary(b, .{ - .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos }), - }, .{ - .name = "a", - .zig_source_bytes = "export var foo: i32 = 0;", - }); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\extern int foo; - \\int main() { - \\ return foo; - \\} - , &.{}); - exe.root_module.linkLibrary(dylib); - exe.root_module.link_libc = true; - - expectLinkErrors(exe, test_step, .{ - .contains = "error: failed to parse shared library: BadMagic", - }); - - return test_step; -} - -fn testUnresolvedError(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "unresolved-error", opts); - - const obj1 = addObject(b, opts, .{ - .name = "a", - .c_source_bytes = - \\#include - \\int foo(); - \\int bar() { - \\ return foo() + 1; - \\} - , - .c_source_flags = &.{"-ffunction-sections"}, - }); - obj1.root_module.link_libc = true; - - const obj2 = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = - \\#include - \\int foo(); - \\int bar(); - \\int main() { - \\ return foo() + bar(); - \\} - , - .c_source_flags = &.{"-ffunction-sections"}, - }); - obj2.root_module.link_libc = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(obj1); - exe.root_module.addObject(obj2); - exe.root_module.link_libc = true; - - expectLinkErrors(exe, test_step, .{ .exact = &.{ - "error: undefined symbol: foo", - "note: referenced by /?/a.o:.text.bar", - "note: referenced by /?/b.o:.text.main", - } }); - - return test_step; -} - -fn testWeakExports(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "weak-exports", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = - \\#include - \\__attribute__((weak)) int foo(); - \\int main() { - \\ printf("%d\n", foo ? foo() : 3); - \\} - , - .pic = true, - }); - obj.root_module.link_libc = true; - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(obj); - dso.root_module.link_libc = true; - - const check = dso.checkObject(); - check.checkInDynamicSymtab(); - check.checkContains("UND NOTYPE WEAK DEFAULT foo"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(obj); - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInDynamicSymtab(); - check.checkNotPresent("UND NOTYPE WEAK DEFAULT foo"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testWeakUndefsDso(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "weak-undef-dso", opts); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dso, - \\__attribute__((weak)) int foo(); - \\int bar() { return foo ? foo() : -1; } - , &.{}); - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\int bar(); - \\int main() { printf("bar=%d\n", bar()); } - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("bar=-1\n"); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\int foo() { return 5; } - \\int bar(); - \\int main() { printf("bar=%d\n", bar()); } - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("bar=5\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testZNow(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "z-now", opts); - - const obj = addObject(b, opts, .{ - .name = "obj", - .c_source_bytes = "int main() { return 0; }", - .pic = true, - }); - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(obj); - - const check = dso.checkObject(); - check.checkInDynamicSection(); - check.checkContains("NOW"); - test_step.dependOn(&check.step); - } - - { - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(obj); - dso.link_z_lazy = true; - - const check = dso.checkObject(); - check.checkInDynamicSection(); - check.checkNotPresent("NOW"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testZStackSize(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "z-stack-size", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, "int main() { return 0; }", &.{}); - exe.stack_size = 0x800000; - exe.root_module.link_libc = true; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("program headers"); - check.checkExact("type GNU_STACK"); - check.checkExact("memsz 800000"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testZText(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "z-text", opts); - - // Previously, following mold, this test tested text relocs present in a PIE executable. - // However, as we want to cover musl AND glibc, it is now modified to test presence of - // text relocs in a DSO which is then linked with an executable. - // According to Rich and this thread https://www.openwall.com/lists/musl/2020/09/25/4 - // musl supports only a very limited number of text relocations and only in DSOs (and - // rightly so!). - - const a_o = addObject(b, opts, .{ - .name = "a", - .asm_source_bytes = - \\.globl fn1 - \\fn1: - \\ sub $8, %rsp - \\ movabs ptr, %rax - \\ call *%rax - \\ add $8, %rsp - \\ ret - \\ - , - }); - - const b_o = addObject(b, opts, .{ - .name = "b", - .c_source_bytes = - \\int fn1(); - \\int fn2() { - \\ return 3; - \\} - \\void *ptr = fn2; - \\int fnn() { - \\ return fn1(); - \\} - , - .pic = true, - }); - - const dso = addSharedLibrary(b, opts, .{ .name = "a" }); - dso.root_module.addObject(a_o); - dso.root_module.addObject(b_o); - dso.link_z_notext = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\#include - \\int fnn(); - \\int main() { - \\ printf("%d\n", fnn()); - \\} - , &.{}); - exe.root_module.linkLibrary(dso); - exe.root_module.link_libc = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3\n"); - test_step.dependOn(&run.step); - - // Check for DT_TEXTREL in a DSO - const check = dso.checkObject(); - check.checkInDynamicSection(); - // check.checkExact("TEXTREL 0"); // TODO fix in CheckObject parser - check.checkContains("FLAGS TEXTREL"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { - return link.addTestStep(b, "elf-" ++ prefix, opts); -} - -const addAsmSourceBytes = link.addAsmSourceBytes; -const addCSourceBytes = link.addCSourceBytes; -const addCppSourceBytes = link.addCppSourceBytes; -const addExecutable = link.addExecutable; -const addObject = link.addObject; -const addRunArtifact = link.addRunArtifact; -const addSharedLibrary = link.addSharedLibrary; -const addStaticLibrary = link.addStaticLibrary; -const expectLinkErrors = link.expectLinkErrors; -const link = @import("link.zig"); -const std = @import("std"); -const builtin = @import("builtin"); - -const Build = std.Build; -const BuildOptions = link.BuildOptions; -const Options = link.Options; -const Step = Build.Step; -const WriteFile = Step.WriteFile; diff --git a/test/link/interdependent_static_c_libs/a.c b/test/link/interdependent_static_c_libs/a.c deleted file mode 100644 index ee9da97a3a..0000000000 --- a/test/link/interdependent_static_c_libs/a.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "a.h" -int32_t add(int32_t a, int32_t b) { - return a + b; -} diff --git a/test/link/interdependent_static_c_libs/a.h b/test/link/interdependent_static_c_libs/a.h deleted file mode 100644 index 7b45d54d56..0000000000 --- a/test/link/interdependent_static_c_libs/a.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -int32_t add(int32_t a, int32_t b); diff --git a/test/link/interdependent_static_c_libs/b.c b/test/link/interdependent_static_c_libs/b.c deleted file mode 100644 index ac2cc4f937..0000000000 --- a/test/link/interdependent_static_c_libs/b.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "a.h" -#include "b.h" - -int32_t sub(int32_t a, int32_t b) { - return add(a, -1 * b); -} diff --git a/test/link/interdependent_static_c_libs/b.h b/test/link/interdependent_static_c_libs/b.h deleted file mode 100644 index 6047163145..0000000000 --- a/test/link/interdependent_static_c_libs/b.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -int32_t sub(int32_t a, int32_t b); diff --git a/test/link/interdependent_static_c_libs/build.zig b/test/link/interdependent_static_c_libs/build.zig deleted file mode 100644 index ec84ebb339..0000000000 --- a/test/link/interdependent_static_c_libs/build.zig +++ /dev/null @@ -1,50 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib_a = b.addLibrary(.{ - .linkage = .static, - .name = "a", - .root_module = b.createModule(.{ - .root_source_file = null, - .optimize = optimize, - .target = b.graph.host, - }), - }); - lib_a.root_module.addCSourceFile(.{ .file = b.path("a.c"), .flags = &[_][]const u8{} }); - lib_a.root_module.addIncludePath(b.path(".")); - - const lib_b = b.addLibrary(.{ - .linkage = .static, - .name = "b", - .root_module = b.createModule(.{ - .root_source_file = null, - .optimize = optimize, - .target = b.graph.host, - }), - }); - lib_b.root_module.addCSourceFile(.{ .file = b.path("b.c"), .flags = &[_][]const u8{} }); - lib_b.root_module.addIncludePath(b.path(".")); - - const test_exe = b.addTest(.{ - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .target = b.graph.host, - .optimize = optimize, - }), - }); - test_exe.root_module.linkLibrary(lib_a); - test_exe.root_module.linkLibrary(lib_b); - test_exe.root_module.addIncludePath(b.path(".")); - - test_step.dependOn(&b.addRunArtifact(test_exe).step); -} diff --git a/test/link/interdependent_static_c_libs/main.zig b/test/link/interdependent_static_c_libs/main.zig deleted file mode 100644 index 08a35c5ddb..0000000000 --- a/test/link/interdependent_static_c_libs/main.zig +++ /dev/null @@ -1,9 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; - -extern fn sub(a: i32, b: i32) i32; - -test "import C sub" { - const result = sub(2, 1); - try expect(result == 1); -} diff --git a/test/link/link.zig b/test/link/link.zig deleted file mode 100644 index 601247e877..0000000000 --- a/test/link/link.zig +++ /dev/null @@ -1,171 +0,0 @@ -pub const BuildOptions = struct { - has_macos_sdk: bool, - has_ios_sdk: bool, - has_symlinks: bool, -}; - -pub const Options = struct { - target: std.Build.ResolvedTarget, - optimize: std.builtin.OptimizeMode = .Debug, - use_llvm: bool = true, - use_lld: bool = false, - strip: ?bool = null, -}; - -pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step { - const target = opts.target.query.zigTriple(b.allocator) catch @panic("OOM"); - const optimize = @tagName(opts.optimize); - const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm"; - const use_lld = if (opts.use_lld) "lld" else "no-lld"; - if (opts.strip) |strip| { - const s = if (strip) "strip" else "no-strip"; - const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}-{s}-{s}", .{ - prefix, target, optimize, use_llvm, use_lld, s, - }) catch @panic("OOM"); - return b.step(name, ""); - } - const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}-{s}", .{ - prefix, target, optimize, use_llvm, use_lld, - }) catch @panic("OOM"); - return b.step(name, ""); -} - -const OverlayOptions = struct { - name: []const u8, - asm_source_bytes: ?[]const u8 = null, - c_source_bytes: ?[]const u8 = null, - c_source_flags: []const []const u8 = &.{}, - cpp_source_bytes: ?[]const u8 = null, - cpp_source_flags: []const []const u8 = &.{}, - objc_source_bytes: ?[]const u8 = null, - objc_source_flags: []const []const u8 = &.{}, - objcpp_source_bytes: ?[]const u8 = null, - objcpp_source_flags: []const []const u8 = &.{}, - zig_source_bytes: ?[]const u8 = null, - pic: ?bool = null, - strip: ?bool = null, -}; - -pub fn addExecutable(b: *std.Build, base: Options, overlay: OverlayOptions) *Compile { - return b.addExecutable(.{ - .name = overlay.name, - .root_module = createModule(b, base, overlay), - .use_llvm = base.use_llvm, - .use_lld = base.use_lld, - }); -} - -pub fn addObject(b: *Build, base: Options, overlay: OverlayOptions) *Compile { - return b.addObject(.{ - .name = overlay.name, - .root_module = createModule(b, base, overlay), - .use_llvm = base.use_llvm, - .use_lld = base.use_lld, - }); -} - -pub fn addStaticLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile { - return b.addLibrary(.{ - .linkage = .static, - .name = overlay.name, - .root_module = createModule(b, base, overlay), - .use_llvm = base.use_llvm, - .use_lld = base.use_lld, - }); -} - -pub fn addSharedLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile { - return b.addLibrary(.{ - .linkage = .dynamic, - .name = overlay.name, - .root_module = createModule(b, base, overlay), - .use_llvm = base.use_llvm, - .use_lld = base.use_lld, - }); -} - -fn createModule(b: *Build, base: Options, overlay: OverlayOptions) *Build.Module { - const write_files = b.addWriteFiles(); - - const mod = b.createModule(.{ - .target = base.target, - .optimize = base.optimize, - .root_source_file = rsf: { - const bytes = overlay.zig_source_bytes orelse break :rsf null; - const name = b.fmt("{s}.zig", .{overlay.name}); - break :rsf write_files.add(name, bytes); - }, - .pic = overlay.pic, - .strip = if (base.strip) |s| s else overlay.strip, - }); - - if (overlay.objcpp_source_bytes) |bytes| { - mod.addCSourceFile(.{ - .file = write_files.add("a.mm", bytes), - .flags = overlay.objcpp_source_flags, - }); - } - if (overlay.objc_source_bytes) |bytes| { - mod.addCSourceFile(.{ - .file = write_files.add("a.m", bytes), - .flags = overlay.objc_source_flags, - }); - } - if (overlay.cpp_source_bytes) |bytes| { - mod.addCSourceFile(.{ - .file = write_files.add("a.cpp", bytes), - .flags = overlay.cpp_source_flags, - }); - } - if (overlay.c_source_bytes) |bytes| { - mod.addCSourceFile(.{ - .file = write_files.add("a.c", bytes), - .flags = overlay.c_source_flags, - }); - } - if (overlay.asm_source_bytes) |bytes| { - mod.addAssemblyFile(write_files.add("a.s", bytes)); - } - - return mod; -} - -pub fn addRunArtifact(comp: *Compile) *Run { - const b = comp.step.owner; - const run = b.addRunArtifact(comp); - run.skip_foreign_checks = true; - return run; -} - -pub fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void { - const b = comp.step.owner; - const file = WriteFile.create(b).add("a.c", bytes); - comp.root_module.addCSourceFile(.{ .file = file, .flags = flags }); -} - -pub fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void { - const b = comp.step.owner; - const file = WriteFile.create(b).add("a.cpp", bytes); - comp.root_module.addCSourceFile(.{ .file = file, .flags = flags }); -} - -pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void { - const b = comp.step.owner; - const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM"); - const file = WriteFile.create(b).add("a.s", actual_bytes); - comp.root_module.addAssemblyFile(file); -} - -pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void { - comp.expect_errors = expected_errors; - const bin_file = comp.getEmittedBin(); - bin_file.addStepDependencies(test_step); -} - -const std = @import("std"); - -const Build = std.Build; -const Compile = Step.Compile; -const Run = Step.Run; -const Step = Build.Step; -const WriteFile = Step.WriteFile; diff --git a/test/link/macho.zig b/test/link/macho.zig deleted file mode 100644 index a0743f6804..0000000000 --- a/test/link/macho.zig +++ /dev/null @@ -1,3291 +0,0 @@ -//! Here we test our MachO linker for correctness and functionality. - -pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { - const macho_step = b.step("test-macho", "Run MachO tests"); - - // https://github.com/ziglang/zig/issues/25323 - if (builtin.os.tag == .freebsd) return macho_step; - - // https://github.com/ziglang/zig/issues/25961 - if (comptime builtin.cpu.arch.endian() == .big) return macho_step; - - const x86_64_target = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, - .os_tag = .macos, - }); - const aarch64_target = b.resolveTargetQuery(.{ - .cpu_arch = .aarch64, - .os_tag = .macos, - }); - - const default_target = switch (builtin.cpu.arch) { - .x86_64, .aarch64 => b.resolveTargetQuery(.{ - .os_tag = .macos, - }), - else => aarch64_target, - }; - - // Exercise linker with self-hosted backend (no LLVM) - macho_step.dependOn(testEmptyZig(b, .{ .use_llvm = false, .target = x86_64_target })); - macho_step.dependOn(testHelloZig(b, .{ .use_llvm = false, .target = x86_64_target })); - macho_step.dependOn(testLinkingStaticLib(b, .{ .use_llvm = false, .target = x86_64_target })); - macho_step.dependOn(testReexportsZig(b, .{ .use_llvm = false, .target = x86_64_target })); - macho_step.dependOn(testRelocatableZig(b, .{ .use_llvm = false, .target = x86_64_target })); - macho_step.dependOn(testTlsZig(b, .{ .use_llvm = false, .target = x86_64_target })); - macho_step.dependOn(testUnresolvedError(b, .{ .use_llvm = false, .target = x86_64_target })); - - // Exercise linker with LLVM backend - macho_step.dependOn(testDeadStrip(b, .{ .target = default_target })); - macho_step.dependOn(testDuplicateDefinitions(b, .{ .target = default_target })); - macho_step.dependOn(testEmptyObject(b, .{ .target = default_target })); - macho_step.dependOn(testEmptyZig(b, .{ .target = default_target })); - macho_step.dependOn(testEntryPoint(b, .{ .target = default_target })); - macho_step.dependOn(testHeaderWeakFlags(b, .{ .target = default_target })); - macho_step.dependOn(testHelloC(b, .{ .target = default_target })); - macho_step.dependOn(testHelloZig(b, .{ .target = default_target })); - macho_step.dependOn(testLargeBss(b, .{ .target = default_target })); - macho_step.dependOn(testLayout(b, .{ .target = default_target })); - macho_step.dependOn(testLinkingStaticLib(b, .{ .target = default_target })); - macho_step.dependOn(testLinksection(b, .{ .target = default_target })); - macho_step.dependOn(testMergeLiteralsX64(b, .{ .target = x86_64_target })); - macho_step.dependOn(testMergeLiteralsArm64(b, .{ .target = aarch64_target })); - macho_step.dependOn(testMergeLiteralsArm642(b, .{ .target = aarch64_target })); - macho_step.dependOn(testMergeLiteralsAlignment(b, .{ .target = aarch64_target })); - macho_step.dependOn(testMhExecuteHeader(b, .{ .target = default_target })); - macho_step.dependOn(testNoDeadStrip(b, .{ .target = default_target })); - macho_step.dependOn(testNoExportsDylib(b, .{ .target = default_target })); - macho_step.dependOn(testPagezeroSize(b, .{ .target = default_target })); - macho_step.dependOn(testReexportsZig(b, .{ .target = default_target })); - macho_step.dependOn(testRelocatable(b, .{ .target = default_target })); - macho_step.dependOn(testRelocatableZig(b, .{ .target = default_target })); - macho_step.dependOn(testSectionBoundarySymbols(b, .{ .target = default_target })); - macho_step.dependOn(testSectionBoundarySymbols2(b, .{ .target = default_target })); - macho_step.dependOn(testSegmentBoundarySymbols(b, .{ .target = default_target })); - macho_step.dependOn(testSymbolStabs(b, .{ .target = default_target })); - macho_step.dependOn(testStackSize(b, .{ .target = default_target })); - macho_step.dependOn(testTentative(b, .{ .target = default_target })); - macho_step.dependOn(testThunks(b, .{ .target = aarch64_target })); - macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target })); - macho_step.dependOn(testTlsZig(b, .{ .target = default_target })); - macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target })); - macho_step.dependOn(testUndefinedDynamicLookup(b, .{ .target = default_target })); - macho_step.dependOn(testDiscardLocalSymbols(b, .{ .target = default_target })); - macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target })); - macho_step.dependOn(testUnresolvedError2(b, .{ .target = default_target })); - macho_step.dependOn(testUnwindInfo(b, .{ .target = default_target })); - macho_step.dependOn(testUnwindInfoNoSubsectionsX64(b, .{ .target = x86_64_target })); - macho_step.dependOn(testUnwindInfoNoSubsectionsArm64(b, .{ .target = aarch64_target })); - macho_step.dependOn(testEhFramePointerEncodingSdata4(b, .{ .target = aarch64_target })); - macho_step.dependOn(testWeakBind(b, .{ .target = x86_64_target })); - macho_step.dependOn(testWeakRef(b, .{ .target = b.resolveTargetQuery(.{ - .cpu_arch = .x86_64, - .os_tag = .macos, - .os_version_min = .{ .semver = .{ .major = 10, .minor = 13, .patch = 0 } }, - }) })); - - // Tests requiring symlinks - if (build_opts.has_symlinks) { - macho_step.dependOn(testEntryPointArchive(b, .{ .target = default_target })); - macho_step.dependOn(testEntryPointDylib(b, .{ .target = default_target })); - macho_step.dependOn(testDylib(b, .{ .target = default_target })); - macho_step.dependOn(testDylibVersionTbd(b, .{ .target = default_target })); - macho_step.dependOn(testNeededLibrary(b, .{ .target = default_target })); - macho_step.dependOn(testSearchStrategy(b, .{ .target = default_target })); - macho_step.dependOn(testTbdv3(b, .{ .target = default_target })); - macho_step.dependOn(testTls(b, .{ .target = default_target })); - macho_step.dependOn(testTlsPointers(b, .{ .target = default_target })); - macho_step.dependOn(testTwoLevelNamespace(b, .{ .target = default_target })); - macho_step.dependOn(testWeakLibrary(b, .{ .target = default_target })); - - // Tests requiring presence of macOS SDK in system path - if (build_opts.has_macos_sdk) { - macho_step.dependOn(testDeadStripDylibs(b, .{ .target = b.graph.host })); - macho_step.dependOn(testHeaderpad(b, .{ .target = b.graph.host })); - macho_step.dependOn(testLinkDirectlyCppTbd(b, .{ .target = b.graph.host })); - macho_step.dependOn(testMergeLiteralsObjc(b, .{ .target = b.graph.host })); - macho_step.dependOn(testNeededFramework(b, .{ .target = b.graph.host })); - macho_step.dependOn(testObjc(b, .{ .target = b.graph.host })); - macho_step.dependOn(testObjcpp(b, .{ .target = b.graph.host })); - macho_step.dependOn(testWeakFramework(b, .{ .target = b.graph.host })); - } - } - - return macho_step; -} - -fn testDeadStrip(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "dead-strip", opts); - - const obj = addObject(b, opts, .{ .name = "a", .cpp_source_bytes = - \\#include - \\int two() { return 2; } - \\int live_var1 = 1; - \\int live_var2 = two(); - \\int dead_var1 = 3; - \\int dead_var2 = 4; - \\void live_fn1() {} - \\void live_fn2() { live_fn1(); } - \\void dead_fn1() {} - \\void dead_fn2() { dead_fn1(); } - \\int main() { - \\ printf("%d %d\n", live_var1, live_var2); - \\ live_fn2(); - \\} - }); - - { - const exe = addExecutable(b, opts, .{ .name = "no_dead_strip" }); - exe.root_module.addObject(obj); - exe.link_gc_sections = false; - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("live_var1"); - check.checkInSymtab(); - check.checkContains("live_var2"); - check.checkInSymtab(); - check.checkContains("dead_var1"); - check.checkInSymtab(); - check.checkContains("dead_var2"); - check.checkInSymtab(); - check.checkContains("live_fn1"); - check.checkInSymtab(); - check.checkContains("live_fn2"); - check.checkInSymtab(); - check.checkContains("dead_fn1"); - check.checkInSymtab(); - check.checkContains("dead_fn2"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2\n"); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "yes_dead_strip" }); - exe.root_module.addObject(obj); - exe.link_gc_sections = true; - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("live_var1"); - check.checkInSymtab(); - check.checkContains("live_var2"); - check.checkInSymtab(); - check.checkNotPresent("dead_var1"); - check.checkInSymtab(); - check.checkNotPresent("dead_var2"); - check.checkInSymtab(); - check.checkContains("live_fn1"); - check.checkInSymtab(); - check.checkContains("live_fn2"); - check.checkInSymtab(); - check.checkNotPresent("dead_fn1"); - check.checkInSymtab(); - check.checkNotPresent("dead_fn2"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("1 2\n"); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testDuplicateDefinitions(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "duplicate-definitions", opts); - - const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes = - \\var x: usize = 1; - \\export fn strong() void { x += 1; } - \\export fn weak() void { x += 1; } - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = - \\var x: usize = 1; - \\export fn strong() void { x += 1; } - \\comptime { @export(&weakImpl, .{ .name = "weak", .linkage = .weak }); } - \\fn weakImpl() callconv(.c) void { x += 1; } - \\extern fn weak() void; - \\pub fn main() void { - \\ weak(); - \\ strong(); - \\} - }); - exe.root_module.addObject(obj); - - expectLinkErrors(exe, test_step, .{ .exact = &.{ - "error: duplicate symbol definition: _strong", - "note: defined by /?/a.o", - "note: defined by /?/main_zcu.o", - } }); - - return test_step; -} - -fn testDeadStripDylibs(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "dead-strip-dylibs", opts); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int main() { - \\ if (objc_getClass("NSObject") == 0) { - \\ return -1; - \\ } - \\ if (objc_getClass("NSApplication") == 0) { - \\ return -2; - \\ } - \\ return 0; - \\} - }); - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.linkFramework("Cocoa", .{}); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_DYLIB"); - check.checkContains("Cocoa"); - check.checkInHeaders(); - check.checkExact("cmd LOAD_DYLIB"); - check.checkContains("libobjc"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - exe.root_module.linkFramework("Cocoa", .{}); - exe.dead_strip_dylibs = true; - - const run = addRunArtifact(exe); - run.expectExitCode(@as(u8, @bitCast(@as(i8, -2)))); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testDylib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "dylib", opts); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = - \\#include - \\char world[] = "world"; - \\char* hello() { - \\ return "Hello"; - \\} - }); - - const check = dylib.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkNotPresent("PIE"); - test_step.dependOn(&check.step); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\char* hello(); - \\extern char world[]; - \\int main() { - \\ printf("%s %s", hello(), world); - \\ return 0; - \\} - }); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory()); - exe.root_module.addRPath(dylib.getEmittedBinDirectory()); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testDylibVersionTbd(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "dylib-version-tbd", opts); - - const tbd = tbd: { - const wf = WriteFile.create(b); - break :tbd wf.add("liba.tbd", - \\--- !tapi-tbd - \\tbd-version: 4 - \\targets: [ x86_64-macos, arm64-macos ] - \\uuids: - \\ - target: x86_64-macos - \\ value: DEADBEEF - \\ - target: arm64-macos - \\ value: BEEFDEAD - \\install-name: '@rpath/liba.dylib' - \\current-version: 1.2 - \\exports: - \\ - targets: [ x86_64-macos, arm64-macos ] - \\ symbols: [ _foo ] - ); - }; - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() {}" }); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(tbd.dirname()); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_DYLIB"); - check.checkExact("name @rpath/liba.dylib"); - check.checkExact("current version 10200"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testEmptyObject(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "empty-object", opts); - - const empty = addObject(b, opts, .{ .name = "empty", .c_source_bytes = "" }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int main() { - \\ printf("Hello world!"); - \\} - }); - exe.root_module.addObject(empty); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world!"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testEmptyZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "empty-zig", opts); - - const exe = addExecutable(b, opts, .{ .name = "empty", .zig_source_bytes = "pub fn main() void {}" }); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testEntryPoint(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "entry-point", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int non_main() { - \\ printf("%d", 42); - \\ return 0; - \\} - }); - exe.entry = .{ .symbol_name = "_non_main" }; - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("segname __TEXT"); - check.checkExtract("vmaddr {vmaddr}"); - check.checkInHeaders(); - check.checkExact("cmd MAIN"); - check.checkExtract("entryoff {entryoff}"); - check.checkInSymtab(); - check.checkExtract("{n_value} (__TEXT,__text) external _non_main"); - check.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } }); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testEntryPointArchive(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "entry-point-archive", opts); - - const lib = addStaticLibrary(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - - { - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "" }); - exe.root_module.linkSystemLibrary("main", .{}); - exe.root_module.addLibraryPath(lib.getEmittedBinDirectory()); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "" }); - exe.root_module.linkSystemLibrary("main", .{}); - exe.root_module.addLibraryPath(lib.getEmittedBinDirectory()); - exe.link_gc_sections = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testEntryPointDylib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "entry-point-dylib", opts); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dylib, - \\extern int my_main(); - \\int bootstrap() { - \\ return my_main(); - \\} - , &.{}); - dylib.linker_allow_shlib_undefined = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(dylib, - \\#include - \\int my_main() { - \\ fprintf(stdout, "Hello!\n"); - \\ return 0; - \\} - , &.{}); - exe.root_module.linkLibrary(dylib); - exe.entry = .{ .symbol_name = "_bootstrap" }; - exe.forceUndefinedSymbol("_my_main"); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("segname __TEXT"); - check.checkExtract("vmaddr {text_vmaddr}"); - check.checkInHeaders(); - check.checkExact("sectname __stubs"); - check.checkExtract("addr {stubs_vmaddr}"); - check.checkInHeaders(); - check.checkExact("sectname __stubs"); - check.checkExtract("size {stubs_vmsize}"); - check.checkInHeaders(); - check.checkExact("cmd MAIN"); - check.checkExtract("entryoff {entryoff}"); - check.checkComputeCompare("text_vmaddr entryoff +", .{ - .op = .gte, - .value = .{ .variable = "stubs_vmaddr" }, // The entrypoint should be a synthetic stub - }); - check.checkComputeCompare("text_vmaddr entryoff + stubs_vmaddr -", .{ - .op = .lt, - .value = .{ .variable = "stubs_vmsize" }, // The entrypoint should be a synthetic stub - }); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello!\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testHeaderpad(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "headerpad", opts); - - const addExe = struct { - fn addExe(bb: *Build, o: Options, name: []const u8) *Compile { - const exe = addExecutable(bb, o, .{ - .name = name, - .c_source_bytes = "int main() { return 0; }", - }); - exe.root_module.linkFramework("CoreFoundation", .{}); - exe.root_module.linkFramework("Foundation", .{}); - exe.root_module.linkFramework("Cocoa", .{}); - exe.root_module.linkFramework("CoreGraphics", .{}); - exe.root_module.linkFramework("CoreHaptics", .{}); - exe.root_module.linkFramework("CoreAudio", .{}); - exe.root_module.linkFramework("AVFoundation", .{}); - exe.root_module.linkFramework("CoreImage", .{}); - exe.root_module.linkFramework("CoreLocation", .{}); - exe.root_module.linkFramework("CoreML", .{}); - exe.root_module.linkFramework("CoreVideo", .{}); - exe.root_module.linkFramework("CoreText", .{}); - exe.root_module.linkFramework("CryptoKit", .{}); - exe.root_module.linkFramework("GameKit", .{}); - exe.root_module.linkFramework("SwiftUI", .{}); - exe.root_module.linkFramework("StoreKit", .{}); - exe.root_module.linkFramework("SpriteKit", .{}); - return exe; - } - }.addExe; - - { - const exe = addExe(b, opts, "main1"); - exe.headerpad_max_install_names = true; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("sectname __text"); - check.checkExtract("offset {offset}"); - switch (opts.target.result.cpu.arch) { - .aarch64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }), - .x86_64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }), - else => unreachable, - } - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - { - const exe = addExe(b, opts, "main2"); - exe.headerpad_size = 0x10000; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("sectname __text"); - check.checkExtract("offset {offset}"); - check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - { - const exe = addExe(b, opts, "main3"); - exe.headerpad_max_install_names = true; - exe.headerpad_size = 0x10000; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("sectname __text"); - check.checkExtract("offset {offset}"); - check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - { - const exe = addExe(b, opts, "main4"); - exe.headerpad_max_install_names = true; - exe.headerpad_size = 0x1000; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("sectname __text"); - check.checkExtract("offset {offset}"); - switch (opts.target.result.cpu.arch) { - .aarch64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }), - .x86_64 => check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }), - else => unreachable, - } - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - return test_step; -} - -// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-header-flags.s -fn testHeaderWeakFlags(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "header-weak-flags", opts); - - const obj1 = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.globl _x - \\.weak_definition _x - \\_x: - \\ ret - }); - - const lib = addSharedLibrary(b, opts, .{ .name = "a" }); - lib.root_module.addObject(obj1); - - { - const exe = addExecutable(b, opts, .{ .name = "main1", .c_source_bytes = "int main() { return 0; }" }); - exe.root_module.addObject(obj1); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkContains("WEAK_DEFINES"); - check.checkInHeaders(); - check.checkExact("header"); - check.checkContains("BINDS_TO_WEAK"); - check.checkInExports(); - check.checkExtract("[WEAK] {vmaddr} _x"); - test_step.dependOn(&check.step); - } - - { - const obj = addObject(b, opts, .{ .name = "b" }); - - switch (opts.target.result.cpu.arch) { - .aarch64 => addAsmSourceBytes(obj, - \\.globl _main - \\_main: - \\ bl _x - \\ ret - ), - .x86_64 => addAsmSourceBytes(obj, - \\.globl _main - \\_main: - \\ callq _x - \\ ret - ), - else => unreachable, - } - - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.linkLibrary(lib); - exe.root_module.addObject(obj); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkNotPresent("WEAK_DEFINES"); - check.checkInHeaders(); - check.checkExact("header"); - check.checkContains("BINDS_TO_WEAK"); - check.checkInExports(); - check.checkNotPresent("[WEAK] {vmaddr} _x"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main3", .asm_source_bytes = - \\.globl _main, _x - \\_x: - \\ - \\_main: - \\ ret - }); - exe.root_module.linkLibrary(lib); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkNotPresent("WEAK_DEFINES"); - check.checkInHeaders(); - check.checkExact("header"); - check.checkNotPresent("BINDS_TO_WEAK"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testHelloC(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "hello-c", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int main() { - \\ printf("Hello world!\n"); - \\ return 0; - \\} - }); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world!\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkContains("PIE"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testHelloZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "hello-zig", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = - \\const std = @import("std"); - \\pub fn main() void { - \\ std.Io.File.stdout().writeStreamingAll(std.Options.debug_io, "Hello world!\n") catch @panic("fail"); - \\} - }); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world!\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLargeBss(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "large-bss", opts); - - // TODO this test used use a 4GB zerofill section but this actually fails and causes every - // linker I tried misbehave in different ways. This only happened on arm64. I thought that - // maybe S_GB_ZEROFILL section is an answer to this but it doesn't seem supported by dyld - // anymore. When I get some free time I will re-investigate this. - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\char arr[0x1000000]; - \\int main() { - \\ return arr[2000]; - \\} - }); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLayout(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "layout", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int main() { - \\ printf("Hello world!"); - \\ return 0; - \\} - }); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd SEGMENT_64"); - check.checkExact("segname __LINKEDIT"); - check.checkExtract("fileoff {fileoff}"); - check.checkExtract("filesz {filesz}"); - check.checkInHeaders(); - check.checkExact("cmd DYLD_INFO_ONLY"); - check.checkExtract("rebaseoff {rebaseoff}"); - check.checkExtract("rebasesize {rebasesize}"); - check.checkExtract("bindoff {bindoff}"); - check.checkExtract("bindsize {bindsize}"); - check.checkExtract("lazybindoff {lazybindoff}"); - check.checkExtract("lazybindsize {lazybindsize}"); - check.checkExtract("exportoff {exportoff}"); - check.checkExtract("exportsize {exportsize}"); - check.checkInHeaders(); - check.checkExact("cmd FUNCTION_STARTS"); - check.checkExtract("dataoff {fstartoff}"); - check.checkExtract("datasize {fstartsize}"); - check.checkInHeaders(); - check.checkExact("cmd DATA_IN_CODE"); - check.checkExtract("dataoff {diceoff}"); - check.checkExtract("datasize {dicesize}"); - check.checkInHeaders(); - check.checkExact("cmd SYMTAB"); - check.checkExtract("symoff {symoff}"); - check.checkExtract("nsyms {symnsyms}"); - check.checkExtract("stroff {stroff}"); - check.checkExtract("strsize {strsize}"); - check.checkInHeaders(); - check.checkExact("cmd DYSYMTAB"); - check.checkExtract("indirectsymoff {dysymoff}"); - check.checkExtract("nindirectsyms {dysymnsyms}"); - - switch (opts.target.result.cpu.arch) { - .aarch64 => { - check.checkInHeaders(); - check.checkExact("cmd CODE_SIGNATURE"); - check.checkExtract("dataoff {codesigoff}"); - check.checkExtract("datasize {codesigsize}"); - }, - .x86_64 => {}, - else => unreachable, - } - - // DYLD_INFO_ONLY subsections are in order: rebase < bind < lazy < export, - // and there are no gaps between them - check.checkComputeCompare("rebaseoff rebasesize +", .{ .op = .eq, .value = .{ .variable = "bindoff" } }); - check.checkComputeCompare("bindoff bindsize +", .{ .op = .eq, .value = .{ .variable = "lazybindoff" } }); - check.checkComputeCompare("lazybindoff lazybindsize +", .{ .op = .eq, .value = .{ .variable = "exportoff" } }); - - // FUNCTION_STARTS directly follows DYLD_INFO_ONLY (no gap) - check.checkComputeCompare("exportoff exportsize +", .{ .op = .eq, .value = .{ .variable = "fstartoff" } }); - - // DATA_IN_CODE directly follows FUNCTION_STARTS (no gap) - check.checkComputeCompare("fstartoff fstartsize +", .{ .op = .eq, .value = .{ .variable = "diceoff" } }); - - // SYMTAB directly follows DATA_IN_CODE (no gap) - check.checkComputeCompare("diceoff dicesize +", .{ .op = .eq, .value = .{ .variable = "symoff" } }); - - // DYSYMTAB directly follows SYMTAB (no gap) - check.checkComputeCompare("symnsyms 16 symoff * +", .{ .op = .eq, .value = .{ .variable = "dysymoff" } }); - - // STRTAB follows DYSYMTAB with possible gap - check.checkComputeCompare("dysymnsyms 4 dysymoff * +", .{ .op = .lte, .value = .{ .variable = "stroff" } }); - - // all LINKEDIT sections apart from CODE_SIGNATURE are 8-bytes aligned - check.checkComputeCompare("rebaseoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("bindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("lazybindoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("exportoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("fstartoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("diceoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("symoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("stroff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("dysymoff 8 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - - switch (opts.target.result.cpu.arch) { - .aarch64 => { - // LINKEDIT segment does not extend beyond, or does not include, CODE_SIGNATURE data - check.checkComputeCompare("fileoff filesz codesigoff codesigsize + - -", .{ - .op = .eq, - .value = .{ .literal = 0 }, - }); - - // CODE_SIGNATURE data offset is 16-bytes aligned - check.checkComputeCompare("codesigoff 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); - }, - .x86_64 => { - // LINKEDIT segment does not extend beyond, or does not include, strtab data - check.checkComputeCompare("fileoff filesz stroff strsize + - -", .{ - .op = .eq, - .value = .{ .literal = 0 }, - }); - }, - else => unreachable, - } - - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world!"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step { - const io = b.graph.io; - const test_step = addTestStep(b, "link-directly-cpp-tbd", opts); - - const sdk = std.zig.system.darwin.getSdk(b.allocator, io, &opts.target.result) orelse - @panic("macOS SDK is required to run the test"); - - const exe = addExecutable(b, opts, .{ - .name = "main", - .cpp_source_bytes = - \\#include - \\#include - \\int main() { - \\ int *x = new int; - \\ *x = 5; - \\ fprintf(stderr, "x: %d\n", *x); - \\ delete x; - \\} - , - .cpp_source_flags = &.{ "-nostdlib++", "-nostdinc++" }, - }); - exe.root_module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include" }) }); - exe.root_module.addIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) }); - exe.root_module.addObjectFile(.{ .cwd_relative = b.pathJoin(&.{ sdk, "/usr/lib/libc++.tbd" }) }); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("[referenced dynamically] external __mh_execute_header"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testLinkingStaticLib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linking-static-lib", opts); - - const obj = addObject(b, opts, .{ - .name = "bobj", - .zig_source_bytes = "export var bar: i32 = -42;", - .strip = true, // TODO for self-hosted, we don't really emit any valid DWARF yet since we only export a global - }); - - const lib = addStaticLibrary(b, opts, .{ - .name = "alib", - .zig_source_bytes = - \\export fn foo() i32 { - \\ return 42; - \\} - , - }); - lib.root_module.addObject(obj); - - const exe = addExecutable(b, opts, .{ - .name = "testlib", - .zig_source_bytes = - \\const std = @import("std"); - \\extern fn foo() i32; - \\extern var bar: i32; - \\pub fn main() void { - \\ std.debug.print("{d}\n", .{foo() + bar}); - \\} - , - }); - exe.root_module.linkLibrary(lib); - - const run = addRunArtifact(exe); - run.expectStdErrEqual("0\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testLinksection(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "linksection", opts); - - const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes = - \\export var test_global: u32 linksection("__DATA,__TestGlobal") = undefined; - \\export fn testFn() linksection("__TEXT,__TestFn") callconv(.c) void { - \\ TestGenericFn("A").f(); - \\} - \\fn TestGenericFn(comptime suffix: []const u8) type { - \\ return struct { - \\ fn f() linksection("__TEXT,__TestGenFn" ++ suffix) void {} - \\ }; - \\} - }); - - const check = obj.checkObject(); - check.checkInSymtab(); - check.checkContains("(__DATA,__TestGlobal) external _test_global"); - check.checkInSymtab(); - check.checkContains("(__TEXT,__TestFn) external _testFn"); - - if (opts.optimize == .Debug) { - check.checkInSymtab(); - check.checkContains("(__TEXT,__TestGenFnA) _main.TestGenericFn("); - } - - test_step.dependOn(&check.step); - - return test_step; -} - -fn testMergeLiteralsX64(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "merge-literals-x64", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.globl _q1 - \\.globl _s1 - \\ - \\.align 4 - \\_q1: - \\ lea L._q1(%rip), %rax - \\ mov (%rax), %xmm0 - \\ ret - \\ - \\.section __TEXT,__cstring,cstring_literals - \\l._s1: - \\ .asciz "hello" - \\ - \\.section __TEXT,__literal8,8byte_literals - \\.align 8 - \\L._q1: - \\ .double 1.2345 - \\ - \\.section __DATA,__data - \\.align 8 - \\_s1: - \\ .quad l._s1 - }); - - const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes = - \\.globl _q2 - \\.globl _s2 - \\.globl _s3 - \\ - \\.align 4 - \\_q2: - \\ lea L._q2(%rip), %rax - \\ mov (%rax), %xmm0 - \\ ret - \\ - \\.section __TEXT,__cstring,cstring_literals - \\l._s2: - \\ .asciz "hello" - \\l._s3: - \\ .asciz "world" - \\ - \\.section __TEXT,__literal8,8byte_literals - \\.align 8 - \\L._q2: - \\ .double 1.2345 - \\ - \\.section __DATA,__data - \\.align 8 - \\_s2: - \\ .quad l._s2 - \\_s3: - \\ .quad l._s3 - }); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\extern double q1(); - \\extern double q2(); - \\extern const char* s1; - \\extern const char* s2; - \\extern const char* s3; - \\int main() { - \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2()); - \\ return 0; - \\} - }); - - const runWithChecks = struct { - fn runWithChecks(step: *Step, exe: *Compile) void { - const run = addRunArtifact(exe); - run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500"); - step.dependOn(&run.step); - - const check = exe.checkObject(); - check.dumpSection("__TEXT,__const"); - check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?"); - check.dumpSection("__TEXT,__cstring"); - check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00"); - step.dependOn(&check.step); - } - }.runWithChecks; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(main_o); - runWithChecks(test_step, exe); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(b_o); - exe.root_module.addObject(a_o); - exe.root_module.addObject(main_o); - runWithChecks(test_step, exe); - } - - { - const c_o = addObject(b, opts, .{ .name = "c" }); - c_o.root_module.addObject(a_o); - c_o.root_module.addObject(b_o); - c_o.root_module.addObject(main_o); - - const exe = addExecutable(b, opts, .{ .name = "main3" }); - exe.root_module.addObject(c_o); - runWithChecks(test_step, exe); - } - - return test_step; -} - -fn testMergeLiteralsArm64(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "merge-literals-arm64", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.globl _q1 - \\.globl _s1 - \\ - \\.align 4 - \\_q1: - \\ adrp x8, L._q1@PAGE - \\ ldr d0, [x8, L._q1@PAGEOFF] - \\ ret - \\ - \\.section __TEXT,__cstring,cstring_literals - \\l._s1: - \\ .asciz "hello" - \\ - \\.section __TEXT,__literal8,8byte_literals - \\.align 8 - \\L._q1: - \\ .double 1.2345 - \\ - \\.section __DATA,__data - \\.align 8 - \\_s1: - \\ .quad l._s1 - }); - - const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes = - \\.globl _q2 - \\.globl _s2 - \\.globl _s3 - \\ - \\.align 4 - \\_q2: - \\ adrp x8, L._q2@PAGE - \\ ldr d0, [x8, L._q2@PAGEOFF] - \\ ret - \\ - \\.section __TEXT,__cstring,cstring_literals - \\l._s2: - \\ .asciz "hello" - \\l._s3: - \\ .asciz "world" - \\ - \\.section __TEXT,__literal8,8byte_literals - \\.align 8 - \\L._q2: - \\ .double 1.2345 - \\ - \\.section __DATA,__data - \\.align 8 - \\_s2: - \\ .quad l._s2 - \\_s3: - \\ .quad l._s3 - }); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\extern double q1(); - \\extern double q2(); - \\extern const char* s1; - \\extern const char* s2; - \\extern const char* s3; - \\int main() { - \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2()); - \\ return 0; - \\} - }); - - const runWithChecks = struct { - fn runWithChecks(step: *Step, exe: *Compile) void { - const run = addRunArtifact(exe); - run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500"); - step.dependOn(&run.step); - - const check = exe.checkObject(); - check.dumpSection("__TEXT,__const"); - check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?"); - check.dumpSection("__TEXT,__cstring"); - check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00"); - step.dependOn(&check.step); - } - }.runWithChecks; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(main_o); - runWithChecks(test_step, exe); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(b_o); - exe.root_module.addObject(a_o); - exe.root_module.addObject(main_o); - runWithChecks(test_step, exe); - } - - { - const c_o = addObject(b, opts, .{ .name = "c" }); - c_o.root_module.addObject(a_o); - c_o.root_module.addObject(b_o); - c_o.root_module.addObject(main_o); - - const exe = addExecutable(b, opts, .{ .name = "main3" }); - exe.root_module.addObject(c_o); - runWithChecks(test_step, exe); - } - - return test_step; -} - -/// This particular test case will generate invalid machine code that will segfault at runtime. -/// However, this is by design as we want to test that the linker does not panic when linking it -/// which is also the case for the system linker and lld - linking succeeds, runtime segfaults. -/// It should also be mentioned that runtime segfault is not due to the linker but faulty input asm. -fn testMergeLiteralsArm642(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "merge-literals-arm64-2", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.globl _q1 - \\.globl _s1 - \\ - \\.align 4 - \\_q1: - \\ adrp x0, L._q1@PAGE - \\ ldr x0, [x0, L._q1@PAGEOFF] - \\ ret - \\ - \\.section __TEXT,__cstring,cstring_literals - \\_s1: - \\ .asciz "hello" - \\ - \\.section __TEXT,__literal8,8byte_literals - \\.align 8 - \\L._q1: - \\ .double 1.2345 - }); - - const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes = - \\.globl _q2 - \\.globl _s2 - \\.globl _s3 - \\ - \\.align 4 - \\_q2: - \\ adrp x0, L._q2@PAGE - \\ ldr x0, [x0, L._q2@PAGEOFF] - \\ ret - \\ - \\.section __TEXT,__cstring,cstring_literals - \\_s2: - \\ .asciz "hello" - \\_s3: - \\ .asciz "world" - \\ - \\.section __TEXT,__literal8,8byte_literals - \\.align 8 - \\L._q2: - \\ .double 1.2345 - }); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\extern double q1(); - \\extern double q2(); - \\extern const char* s1; - \\extern const char* s2; - \\extern const char* s3; - \\int main() { - \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2()); - \\ return 0; - \\} - }); - - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(main_o); - - const check = exe.checkObject(); - check.dumpSection("__TEXT,__const"); - check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?"); - check.dumpSection("__TEXT,__cstring"); - check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testMergeLiteralsAlignment(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "merge-literals-alignment", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.globl _s1 - \\.globl _s2 - \\ - \\.section __TEXT,__cstring,cstring_literals - \\.align 3 - \\_s1: - \\ .asciz "str1" - \\_s2: - \\ .asciz "str2" - }); - - const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes = - \\.globl _s3 - \\.globl _s4 - \\ - \\.section __TEXT,__cstring,cstring_literals - \\.align 2 - \\_s3: - \\ .asciz "str1" - \\_s4: - \\ .asciz "str2" - }); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\#include - \\#include - \\extern const char* s1; - \\extern const char* s2; - \\extern const char* s3; - \\extern const char* s4; - \\int main() { - \\ assert((uintptr_t)(&s1) % 8 == 0 && s1 == s3); - \\ assert((uintptr_t)(&s2) % 8 == 0 && s2 == s4); - \\ printf("%s%s%s%s", &s1, &s2, &s3, &s4); - \\ return 0; - \\} - , .c_source_flags = &.{"-Wno-format"} }); - - const runWithChecks = struct { - fn runWithChecks(step: *Step, exe: *Compile) void { - const run = addRunArtifact(exe); - run.expectStdOutEqual("str1str2str1str2"); - step.dependOn(&run.step); - - const check = exe.checkObject(); - check.dumpSection("__TEXT,__cstring"); - check.checkContains("str1\x00\x00\x00\x00str2\x00"); - check.checkInHeaders(); - check.checkExact("segname __TEXT"); - check.checkExact("sectname __cstring"); - check.checkExact("align 3"); - step.dependOn(&check.step); - } - }.runWithChecks; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(main_o); - runWithChecks(test_step, exe); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(b_o); - exe.root_module.addObject(a_o); - exe.root_module.addObject(main_o); - runWithChecks(test_step, exe); - } - - return test_step; -} - -fn testMergeLiteralsObjc(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "merge-literals-objc", opts); - - const main_o = addObject(b, opts, .{ .name = "main", .objc_source_bytes = - \\#import ; - \\ - \\extern void foo(); - \\ - \\int main() { - \\ NSString *thing = @"aaa"; - \\ - \\ SEL sel = @selector(lowercaseString); - \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO"); - \\ NSLog (@"Responds to lowercaseString: %@", lower); - \\ if ([thing respondsToSelector:sel]) //(lower == @"YES") - \\ NSLog(@"lowercaseString is: %@", [thing lowercaseString]); - \\ - \\ foo(); - \\} - }); - - const a_o = addObject(b, opts, .{ .name = "a", .objc_source_bytes = - \\#import ; - \\ - \\void foo() { - \\ NSString *thing = @"aaa"; - \\ SEL sel = @selector(lowercaseString); - \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO"); - \\ NSLog (@"Responds to lowercaseString in foo(): %@", lower); - \\ if ([thing respondsToSelector:sel]) //(lower == @"YES") - \\ NSLog(@"lowercaseString in foo() is: %@", [thing lowercaseString]); - \\ SEL sel2 = @selector(uppercaseString); - \\ NSString *upper = (([thing respondsToSelector:sel2]) ? @"YES" : @"NO"); - \\ NSLog (@"Responds to uppercaseString in foo(): %@", upper); - \\ if ([thing respondsToSelector:sel2]) //(upper == @"YES") - \\ NSLog(@"uppercaseString in foo() is: %@", [thing uppercaseString]); - \\} - }); - - const runWithChecks = struct { - fn runWithChecks(step: *Step, exe: *Compile) void { - const builder = step.owner; - const run = addRunArtifact(exe); - run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString: YES") }); - run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString is: aaa") }); - run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString in foo(): YES") }); - run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString in foo() is: aaa") }); - run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to uppercaseString in foo(): YES") }); - run.addCheck(.{ .expect_stderr_match = builder.dupe("uppercaseString in foo() is: AAA") }); - step.dependOn(&run.step); - - const check = exe.checkObject(); - check.dumpSection("__TEXT,__objc_methname"); - check.checkContains("lowercaseString\x00"); - check.dumpSection("__TEXT,__objc_methname"); - check.checkContains("uppercaseString\x00"); - step.dependOn(&check.step); - } - }.runWithChecks; - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(a_o); - exe.root_module.linkFramework("Foundation", .{}); - runWithChecks(test_step, exe); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(main_o); - exe.root_module.linkFramework("Foundation", .{}); - runWithChecks(test_step, exe); - } - - { - const b_o = addObject(b, opts, .{ .name = "b" }); - b_o.root_module.addObject(a_o); - b_o.root_module.addObject(main_o); - - const exe = addExecutable(b, opts, .{ .name = "main3" }); - exe.root_module.addObject(b_o); - exe.root_module.linkFramework("Foundation", .{}); - runWithChecks(test_step, exe); - } - - return test_step; -} - -fn testMhExecuteHeader(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "mh-execute-header", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("[referenced dynamically] external __mh_execute_header"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testNoDeadStrip(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "no-dead-strip", opts); - - const exe = addExecutable(b, opts, .{ .name = "name", .c_source_bytes = - \\__attribute__((used)) int bogus1 = 0; - \\int bogus2 = 0; - \\int foo = 42; - \\int main() { - \\ return foo - 42; - \\} - }); - exe.link_gc_sections = true; - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("external _bogus1"); - check.checkInSymtab(); - check.checkNotPresent("external _bogus2"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testNoExportsDylib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "no-exports-dylib", opts); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "static void abc() {}" }); - - const check = dylib.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("external _abc"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testNeededFramework(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "needed-framework", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - exe.root_module.linkFramework("Cocoa", .{ .needed = true }); - exe.dead_strip_dylibs = true; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_DYLIB"); - check.checkContains("Cocoa"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testNeededLibrary(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "needed-library", opts); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int a = 42;" }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - exe.root_module.linkSystemLibrary("a", .{ .needed = true }); - exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory()); - exe.root_module.addRPath(dylib.getEmittedBinDirectory()); - exe.dead_strip_dylibs = true; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_DYLIB"); - check.checkContains("liba.dylib"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testObjc(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "objc", opts); - - const lib = addStaticLibrary(b, opts, .{ .name = "a", .objc_source_bytes = - \\#import - \\@interface Foo : NSObject - \\@end - \\@implementation Foo - \\@end - }); - - { - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.linkFramework("Foundation", .{}); - exe.root_module.addLibraryPath(lib.getEmittedBinDirectory()); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("_OBJC_"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2", .c_source_bytes = "int main() { return 0; }" }); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.linkFramework("Foundation", .{}); - exe.root_module.addLibraryPath(lib.getEmittedBinDirectory()); - exe.force_load_objc = true; - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("_OBJC_"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testObjcpp(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "objcpp", opts); - - const foo_h = foo_h: { - const wf = WriteFile.create(b); - break :foo_h wf.add("Foo.h", - \\#import - \\@interface Foo : NSObject - \\- (NSString *)name; - \\@end - ); - }; - - const foo_o = addObject(b, opts, .{ .name = "foo", .objcpp_source_bytes = - \\#import "Foo.h" - \\@implementation Foo - \\- (NSString *)name - \\{ - \\ NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; - \\ return str; - \\} - \\@end - }); - foo_o.root_module.addIncludePath(foo_h.dirname()); - foo_o.root_module.link_libcpp = true; - - const exe = addExecutable(b, opts, .{ .name = "main", .objcpp_source_bytes = - \\#import "Foo.h" - \\#import - \\#include - \\int main(int argc, char *argv[]) - \\{ - \\ @autoreleasepool { - \\ Foo *foo = [[Foo alloc] init]; - \\ NSString *result = [foo name]; - \\ std::cout << "Hello from C++ and " << [result UTF8String]; - \\ assert([result isEqualToString:@"Zig"]); - \\ return 0; - \\ } - \\} - }); - exe.root_module.addIncludePath(foo_h.dirname()); - exe.root_module.addObject(foo_o); - exe.root_module.link_libcpp = true; - exe.root_module.linkFramework("Foundation", .{}); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello from C++ and Zig"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testPagezeroSize(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "pagezero-size", opts); - - { - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main () { return 0; }" }); - exe.pagezero_size = 0x4000; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("LC 0"); - check.checkExact("segname __PAGEZERO"); - check.checkExact("vmaddr 0"); - check.checkExact("vmsize 4000"); - check.checkInHeaders(); - check.checkExact("segname __TEXT"); - check.checkExact("vmaddr 4000"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main () { return 0; }" }); - exe.pagezero_size = 0; - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("LC 0"); - check.checkExact("segname __TEXT"); - check.checkExact("vmaddr 0"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testReexportsZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "reexports-zig", opts); - - const lib = addStaticLibrary(b, opts, .{ .name = "a", .zig_source_bytes = - \\const x: i32 = 42; - \\export fn foo() i32 { - \\ return x; - \\} - \\comptime { - \\ @export(&foo, .{ .name = "bar", .linkage = .strong }); - \\} - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\extern int foo(); - \\extern int bar(); - \\int main() { - \\ return bar() - foo(); - \\} - }); - exe.root_module.linkLibrary(lib); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testRelocatable(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "relocatable", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .cpp_source_bytes = - \\#include - \\int try_me() { - \\ throw std::runtime_error("Oh no!"); - \\} - }); - a_o.root_module.link_libcpp = true; - - const b_o = addObject(b, opts, .{ .name = "b", .cpp_source_bytes = - \\extern int try_me(); - \\int try_again() { - \\ return try_me(); - \\} - }); - - const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes = - \\#include - \\#include - \\extern int try_again(); - \\int main() { - \\ try { - \\ try_again(); - \\ } catch (const std::exception &e) { - \\ std::cout << "exception=" << e.what(); - \\ } - \\ return 0; - \\} - }); - main_o.root_module.link_libcpp = true; - - const exp_stdout = "exception=Oh no!"; - - { - const c_o = addObject(b, opts, .{ .name = "c" }); - c_o.root_module.addObject(a_o); - c_o.root_module.addObject(b_o); - - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(c_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - { - const d_o = addObject(b, opts, .{ .name = "d" }); - d_o.root_module.addObject(a_o); - d_o.root_module.addObject(b_o); - d_o.root_module.addObject(main_o); - - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(d_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testRelocatableZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "relocatable-zig", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes = - \\const std = @import("std"); - \\export var foo: i32 = 0; - \\export fn incrFoo() void { - \\ foo += 1; - \\ std.debug.print("incrFoo={d}\n", .{foo}); - \\} - }); - - const b_o = addObject(b, opts, .{ .name = "b", .zig_source_bytes = - \\const std = @import("std"); - \\extern var foo: i32; - \\export fn decrFoo() void { - \\ foo -= 1; - \\ std.debug.print("decrFoo={d}\n", .{foo}); - \\} - }); - - const main_o = addObject(b, opts, .{ .name = "main", .zig_source_bytes = - \\const std = @import("std"); - \\extern var foo: i32; - \\extern fn incrFoo() void; - \\extern fn decrFoo() void; - \\pub fn main() void { - \\ const init = foo; - \\ incrFoo(); - \\ decrFoo(); - \\ if (init == foo) @panic("Oh no!"); - \\} - }); - - const c_o = addObject(b, opts, .{ .name = "c" }); - c_o.root_module.addObject(a_o); - c_o.root_module.addObject(b_o); - c_o.root_module.addObject(main_o); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(c_o); - - const run = addRunArtifact(exe); - run.addCheck(.{ .expect_stderr_match = b.dupe("incrFoo=1") }); - run.addCheck(.{ .expect_stderr_match = b.dupe("decrFoo=0") }); - run.addCheck(.{ .expect_stderr_match = b.dupe("panic: Oh no!") }); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testSearchStrategy(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "search-strategy", opts); - - const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = - \\#include - \\char world[] = "world"; - \\char* hello() { - \\ return "Hello"; - \\} - }); - - const liba = addStaticLibrary(b, opts, .{ .name = "a" }); - liba.root_module.addObject(obj); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a" }); - dylib.root_module.addObject(obj); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\char* hello(); - \\extern char world[]; - \\int main() { - \\ printf("%s %s", hello(), world); - \\ return 0; - \\} - }); - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(main_o); - exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .mode_first }); - exe.root_module.addLibraryPath(liba.getEmittedBinDirectory()); - exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory()); - exe.root_module.addRPath(dylib.getEmittedBinDirectory()); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_DYLIB"); - check.checkContains("liba.dylib"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(main_o); - exe.root_module.linkSystemLibrary("a", .{ .use_pkg_config = .no, .search_strategy = .paths_first }); - exe.root_module.addLibraryPath(liba.getEmittedBinDirectory()); - exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory()); - exe.root_module.addRPath(dylib.getEmittedBinDirectory()); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("Hello world"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_DYLIB"); - check.checkNotPresent("liba.dylib"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testSectionBoundarySymbols(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "section-boundary-symbols", opts); - - const obj1 = addObject(b, opts, .{ - .name = "obj1", - .cpp_source_bytes = - \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase"; - , - }); - - const main_o = addObject(b, opts, .{ - .name = "main", - .zig_source_bytes = - \\const std = @import("std"); - \\extern fn interop() ?[*:0]const u8; - \\pub fn main() !void { - \\ std.debug.print("All your {s} are belong to us.\n", .{ - \\ if (interop()) |ptr| std.mem.span(ptr) else "(null)", - \\ }); - \\} - , - }); - - { - const obj2 = addObject(b, opts, .{ - .name = "obj2", - .cpp_source_bytes = - \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr"); - \\extern "C" const char* interop() { - \\ return message_pointer; - \\} - , - }); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - exe.root_module.addObject(obj1); - exe.root_module.addObject(obj2); - exe.root_module.addObject(main_o); - - const run = b.addRunArtifact(exe); - run.skip_foreign_checks = true; - run.expectStdErrEqual("All your codebase are belong to us.\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("external section$start$__DATA_CONST$__message_ptr"); - test_step.dependOn(&check.step); - } - - { - const obj3 = addObject(b, opts, .{ - .name = "obj3", - .cpp_source_bytes = - \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__not_present"); - \\extern "C" const char* interop() { - \\ return message_pointer; - \\} - , - }); - - const exe = addExecutable(b, opts, .{ .name = "test" }); - exe.root_module.addObject(obj1); - exe.root_module.addObject(obj3); - exe.root_module.addObject(main_o); - - const run = b.addRunArtifact(exe); - run.skip_foreign_checks = true; - run.expectStdErrEqual("All your (null) are belong to us.\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("external section$start$__DATA_CONST$__not_present"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testSectionBoundarySymbols2(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "section-boundary-symbols-2", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\struct pair { int a; int b; }; - \\struct pair first __attribute__((section("__DATA,__pairs"))) = { 1, 2 }; - \\struct pair second __attribute__((section("__DATA,__pairs"))) = { 3, 4 }; - \\extern struct pair pairs_start __asm("section$start$__DATA$__pairs"); - \\extern struct pair pairs_end __asm("section$end$__DATA$__pairs"); - \\int main() { - \\ printf("%d,%d\n", first.a, first.b); - \\ printf("%d,%d\n", second.a, second.b); - \\ struct pair* p; - \\ for (p = &pairs_start; p < &pairs_end; p++) { - \\ p->a = 0; - \\ } - \\ printf("%d,%d\n", first.a, first.b); - \\ printf("%d,%d\n", second.a, second.b); - \\ return 0; - \\} - }); - - const run = b.addRunArtifact(exe); - run.skip_foreign_checks = true; - run.expectStdOutEqual( - \\1,2 - \\3,4 - \\0,2 - \\0,4 - \\ - ); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testSegmentBoundarySymbols(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "segment-boundary-symbols", opts); - - const obj1 = addObject(b, opts, .{ .name = "a", .cpp_source_bytes = - \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST_1,__message_ptr"))) = "codebase"; - }); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\const char* interop(); - \\int main() { - \\ printf("All your %s are belong to us.\n", interop()); - \\ return 0; - \\} - }); - - { - const obj2 = addObject(b, opts, .{ .name = "b", .cpp_source_bytes = - \\extern const char* message_pointer __asm("segment$start$__DATA_CONST_1"); - \\extern "C" const char* interop() { - \\ return message_pointer; - \\} - }); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(obj1); - exe.root_module.addObject(obj2); - exe.root_module.addObject(main_o); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("All your codebase are belong to us.\n"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("external segment$start$__DATA_CONST_1"); - test_step.dependOn(&check.step); - } - - { - const obj2 = addObject(b, opts, .{ .name = "c", .cpp_source_bytes = - \\extern const char* message_pointer __asm("segment$start$__DATA_1"); - \\extern "C" const char* interop() { - \\ return message_pointer; - \\} - }); - - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(obj1); - exe.root_module.addObject(obj2); - exe.root_module.addObject(main_o); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd SEGMENT_64"); - check.checkExact("segname __DATA_1"); - check.checkExtract("vmsize {vmsize}"); - check.checkExtract("filesz {filesz}"); - check.checkComputeCompare("vmsize", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkComputeCompare("filesz", .{ .op = .eq, .value = .{ .literal = 0 } }); - check.checkInSymtab(); - check.checkNotPresent("external segment$start$__DATA_1"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testSymbolStabs(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "symbol-stabs", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .c_source_bytes = - \\int foo = 42; - \\int getFoo() { - \\ return foo; - \\} - }); - - const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes = - \\int bar = 24; - \\int getBar() { - \\ return bar; - \\} - }); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\extern int getFoo(); - \\extern int getBar(); - \\int main() { - \\ printf("foo=%d,bar=%d", getFoo(), getBar()); - \\ return 0; - \\} - }); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(a_o); - exe.root_module.addObject(b_o); - exe.root_module.addObject(main_o); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("foo=42,bar=24"); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("a.o"); // TODO we really should do a fuzzy search like OSO /a.o - check.checkInSymtab(); - check.checkContains("b.o"); - check.checkInSymtab(); - check.checkContains("main.o"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testStackSize(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "stack-size", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - exe.stack_size = 0x100000000; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd MAIN"); - check.checkExact("stacksize 100000000"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testTbdv3(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tbdv3", opts); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int getFoo() { return 42; }" }); - - const tbd = tbd: { - const wf = WriteFile.create(b); - break :tbd wf.add("liba.tbd", - \\--- !tapi-tbd-v3 - \\archs: [ arm64, x86_64 ] - \\uuids: [ 'arm64: DEADBEEF', 'x86_64: BEEFDEAD' ] - \\platform: macos - \\install-name: @rpath/liba.dylib - \\current-version: 0 - \\exports: - \\ - archs: [ arm64, x86_64 ] - \\ symbols: [ _getFoo ] - ); - }; - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int getFoo(); - \\int main() { - \\ return getFoo() - 42; - \\} - }); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(tbd.dirname()); - exe.root_module.addRPath(dylib.getEmittedBinDirectory()); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTentative(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tentative", opts); - - const exe = addExecutable(b, opts, .{ .name = "main" }); - addCSourceBytes(exe, - \\int foo; - \\int bar; - \\int baz = 42; - , &.{"-fcommon"}); - addCSourceBytes(exe, - \\#include - \\int foo; - \\int bar = 5; - \\int baz; - \\int main() { - \\ printf("%d %d %d\n", foo, bar, baz); - \\} - , &.{"-fcommon"}); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("0 5 42\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testThunks(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "thunks", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\void bar() { - \\ printf("bar"); - \\} - \\void foo() { - \\ fprintf(stdout, "foo"); - \\} - \\int main() { - \\ foo(); - \\ bar(); - \\ return 0; - \\} - }); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("_printf__thunk"); - check.checkInSymtab(); - check.checkContains("_fprintf__thunk"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("foobar"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTls(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls", opts); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = - \\_Thread_local int a; - \\int getA() { - \\ return a; - \\} - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\extern _Thread_local int a; - \\extern int getA(); - \\int getA2() { - \\ return a; - \\} - \\int main() { - \\ a = 2; - \\ printf("%d %d %d", a, getA(), getA2()); - \\ return 0; - \\} - }); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory()); - exe.root_module.addRPath(dylib.getEmittedBinDirectory()); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("2 2 2"); - test_step.dependOn(&run.step); - - return test_step; -} - -// https://github.com/ziglang/zig/issues/19221 -fn testTlsPointers(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-pointers", opts); - - const foo_h = foo_h: { - const wf = WriteFile.create(b); - break :foo_h wf.add("foo.h", - \\template - \\struct Foo { - \\ - \\public: - \\ static int getVar() { - \\ static int thread_local var = 0; - \\ ++var; - \\ return var; - \\} - \\}; - ); - }; - - const bar_o = addObject(b, opts, .{ .name = "bar", .cpp_source_bytes = - \\#include "foo.h" - \\int bar() { - \\ int v1 = Foo::getVar(); - \\ return v1; - \\} - }); - bar_o.root_module.addIncludePath(foo_h.dirname()); - bar_o.root_module.link_libcpp = true; - - const baz_o = addObject(b, opts, .{ .name = "baz", .cpp_source_bytes = - \\#include "foo.h" - \\int baz() { - \\ int v1 = Foo::getVar(); - \\ return v1; - \\} - }); - baz_o.root_module.addIncludePath(foo_h.dirname()); - baz_o.root_module.link_libcpp = true; - - const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes = - \\extern int bar(); - \\extern int baz(); - \\int main() { - \\ int v1 = bar(); - \\ int v2 = baz(); - \\ return v1 != v2; - \\} - }); - main_o.root_module.addIncludePath(foo_h.dirname()); - main_o.root_module.link_libcpp = true; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(bar_o); - exe.root_module.addObject(baz_o); - exe.root_module.addObject(main_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsLargeTbss(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-large-tbss", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\_Thread_local int x[0x8000]; - \\_Thread_local int y[0x8000]; - \\int main() { - \\ x[0] = 3; - \\ x[0x7fff] = 5; - \\ printf("%d %d %d %d %d %d\n", x[0], x[1], x[0x7fff], y[0], y[1], y[0x7fff]); - \\} - }); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("3 0 5 0 0 0\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTlsZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "tls-zig", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = - \\const std = @import("std"); - \\threadlocal var x: i32 = 0; - \\threadlocal var y: i32 = -1; - \\pub fn main() void { - \\ var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); - \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable; - \\ x -= 1; - \\ y += 1; - \\ stdout_writer.interface.print("{d} {d}\n", .{x, y}) catch unreachable; - \\} - }); - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\0 -1 - \\-1 0 - \\ - ); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testTwoLevelNamespace(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "two-level-namespace", opts); - - const liba = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = - \\#include - \\int foo = 1; - \\int* ptr_to_foo = &foo; - \\int getFoo() { - \\ return foo; - \\} - \\void printInA() { - \\ printf("liba: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo); - \\} - }); - - { - const check = liba.checkObject(); - check.checkInDyldLazyBind(); - check.checkNotPresent("(flat lookup) _getFoo"); - check.checkInIndirectSymtab(); - check.checkNotPresent("_getFoo"); - test_step.dependOn(&check.step); - } - - const libb = addSharedLibrary(b, opts, .{ .name = "b", .c_source_bytes = - \\#include - \\int foo = 2; - \\int* ptr_to_foo = &foo; - \\int getFoo() { - \\ return foo; - \\} - \\void printInB() { - \\ printf("libb: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo); - \\} - }); - - { - const check = libb.checkObject(); - check.checkInDyldLazyBind(); - check.checkNotPresent("(flat lookup) _getFoo"); - check.checkInIndirectSymtab(); - check.checkNotPresent("_getFoo"); - test_step.dependOn(&check.step); - } - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int getFoo(); - \\extern int* ptr_to_foo; - \\void printInA(); - \\void printInB(); - \\int main() { - \\ printf("main: getFoo()=%d, ptr_to_foo=%d\n", getFoo(), *ptr_to_foo); - \\ printInA(); - \\ printInB(); - \\ return 0; - \\} - }); - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.linkSystemLibrary("b", .{}); - exe.root_module.addLibraryPath(liba.getEmittedBinDirectory()); - exe.root_module.addLibraryPath(libb.getEmittedBinDirectory()); - exe.root_module.addRPath(liba.getEmittedBinDirectory()); - exe.root_module.addRPath(libb.getEmittedBinDirectory()); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkExact("(undefined) external _getFoo (from liba)"); - check.checkInSymtab(); - check.checkExact("(undefined) external _printInA (from liba)"); - check.checkInSymtab(); - check.checkExact("(undefined) external _printInB (from libb)"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\main: getFoo()=1, ptr_to_foo=1 - \\liba: getFoo()=1, ptr_to_foo=1 - \\libb: getFoo()=2, ptr_to_foo=2 - \\ - ); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - exe.root_module.linkSystemLibrary("b", .{}); - exe.root_module.linkSystemLibrary("a", .{}); - exe.root_module.addLibraryPath(liba.getEmittedBinDirectory()); - exe.root_module.addLibraryPath(libb.getEmittedBinDirectory()); - exe.root_module.addRPath(liba.getEmittedBinDirectory()); - exe.root_module.addRPath(libb.getEmittedBinDirectory()); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkExact("(undefined) external _getFoo (from libb)"); - check.checkInSymtab(); - check.checkExact("(undefined) external _printInA (from liba)"); - check.checkInSymtab(); - check.checkExact("(undefined) external _printInB (from libb)"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual( - \\main: getFoo()=2, ptr_to_foo=2 - \\liba: getFoo()=1, ptr_to_foo=1 - \\libb: getFoo()=2, ptr_to_foo=2 - \\ - ); - test_step.dependOn(&run.step); - } - - return test_step; -} - -fn testDiscardLocalSymbols(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "discard-local-symbols", opts); - - const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "static int foo = 42;" }); - - const lib = addStaticLibrary(b, opts, .{ .name = "a" }); - lib.root_module.addObject(obj); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - - { - const exe = addExecutable(b, opts, .{ .name = "main3" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(obj); - exe.discard_local_symbols = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("_foo"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main4" }); - exe.root_module.addObject(main_o); - exe.root_module.linkLibrary(lib); - exe.discard_local_symbols = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("_foo"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testUndefinedFlag(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "undefined-flag", opts); - - const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "int foo = 42;" }); - - const lib = addStaticLibrary(b, opts, .{ .name = "a" }); - lib.root_module.addObject(obj); - - const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - - { - const exe = addExecutable(b, opts, .{ .name = "main1" }); - exe.root_module.addObject(main_o); - exe.root_module.linkLibrary(lib); - exe.forceUndefinedSymbol("_foo"); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("_foo"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2" }); - exe.root_module.addObject(main_o); - exe.root_module.linkLibrary(lib); - exe.forceUndefinedSymbol("_foo"); - exe.link_gc_sections = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("_foo"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main3" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(obj); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("_foo"); - test_step.dependOn(&check.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main4" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(obj); - exe.link_gc_sections = true; - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkNotPresent("_foo"); - test_step.dependOn(&check.step); - } - - return test_step; -} - -fn testUndefinedDynamicLookup(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "undefined-dynamic-lookup", opts); - - // Create a dylib with an undefined external symbol reference - const dylib = addSharedLibrary(b, opts, .{ .name = "a" }); - addCSourceBytes(dylib, - \\extern int undefined_symbol(void); - \\int call_undefined(void) { - \\ return undefined_symbol(); - \\} - , &.{}); - dylib.linker_allow_shlib_undefined = true; - - // Verify the Mach-O header does NOT contain NOUNDEFS flag - const check = dylib.checkObject(); - check.checkInHeaders(); - check.checkExact("header"); - check.checkNotPresent("NOUNDEFS"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testUnresolvedError(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "unresolved-error", opts); - - const obj = addObject(b, opts, .{ .name = "a", .zig_source_bytes = - \\extern fn foo() i32; - \\export fn bar() i32 { return foo() + 1; } - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = - \\const std = @import("std"); - \\extern fn foo() i32; - \\extern fn bar() i32; - \\pub fn main() void { - \\ std.debug.print("foo() + bar() = {d}", .{foo() + bar()}); - \\} - }); - exe.root_module.addObject(obj); - - // TODO order should match across backends if possible - if (opts.use_llvm) { - expectLinkErrors(exe, test_step, .{ .exact = &.{ - "error: undefined symbol: _foo", - "note: referenced by /?/a.o:_bar", - "note: referenced by /?/main_zcu.o:_main.main", - } }); - } else { - expectLinkErrors(exe, test_step, .{ .exact = &.{ - "error: undefined symbol: _foo", - "note: referenced by /?/main.o:_main.main", - "note: referenced by /?/a.o:__TEXT$__text_zig", - } }); - } - - return test_step; -} - -fn testUnresolvedError2(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "unresolved-error-2", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = - \\pub fn main() !void { - \\ const msg_send_fn = @extern( - \\ *const fn () callconv(.c) usize, - \\ .{ .name = "objc_msgSend$initWithContentRect:styleMask:backing:defer:screen:" }, - \\ ); - \\ _ = @call( - \\ .auto, - \\ msg_send_fn, - \\ .{}, - \\ ); - \\} - }); - - expectLinkErrors(exe, test_step, .{ .exact = &.{ - "error: undefined symbol: _objc_msgSend", - "note: referenced implicitly", - } }); - - return test_step; -} - -fn testUnwindInfo(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "unwind-info", opts); - - const all_h = all_h: { - const wf = WriteFile.create(b); - break :all_h wf.add("all.h", - \\#ifndef ALL - \\#define ALL - \\ - \\#include - \\#include - \\#include - \\ - \\struct SimpleString { - \\ SimpleString(size_t max_size); - \\ ~SimpleString(); - \\ - \\ void print(const char* tag) const; - \\ bool append_line(const char* x); - \\ - \\private: - \\ size_t max_size; - \\ char* buffer; - \\ size_t length; - \\}; - \\ - \\struct SimpleStringOwner { - \\ SimpleStringOwner(const char* x); - \\ ~SimpleStringOwner(); - \\ - \\private: - \\ SimpleString string; - \\}; - \\ - \\class Error: public std::exception { - \\public: - \\ explicit Error(const char* msg) : msg{ msg } {} - \\ virtual ~Error() noexcept {} - \\ virtual const char* what() const noexcept { - \\ return msg.c_str(); - \\ } - \\ - \\protected: - \\ std::string msg; - \\}; - \\ - \\#endif - ); - }; - - const main_o = addObject(b, opts, .{ .name = "main", .cpp_source_bytes = - \\#include "all.h" - \\#include - \\ - \\void fn_c() { - \\ SimpleStringOwner c{ "cccccccccc" }; - \\} - \\ - \\void fn_b() { - \\ SimpleStringOwner b{ "b" }; - \\ fn_c(); - \\} - \\ - \\int main() { - \\ try { - \\ SimpleStringOwner a{ "a" }; - \\ fn_b(); - \\ SimpleStringOwner d{ "d" }; - \\ } catch (const Error& e) { - \\ printf("Error: %s\n", e.what()); - \\ } catch(const std::exception& e) { - \\ printf("Exception: %s\n", e.what()); - \\ } - \\ return 0; - \\} - }); - main_o.root_module.addIncludePath(all_h.dirname()); - main_o.root_module.link_libcpp = true; - - const simple_string_o = addObject(b, opts, .{ .name = "simple_string", .cpp_source_bytes = - \\#include "all.h" - \\#include - \\#include - \\ - \\SimpleString::SimpleString(size_t max_size) - \\: max_size{ max_size }, length{} { - \\ if (max_size == 0) { - \\ throw Error{ "Max size must be at least 1." }; - \\ } - \\ buffer = new char[max_size]; - \\ buffer[0] = 0; - \\} - \\ - \\SimpleString::~SimpleString() { - \\ delete[] buffer; - \\} - \\ - \\void SimpleString::print(const char* tag) const { - \\ printf("%s: %s", tag, buffer); - \\} - \\ - \\bool SimpleString::append_line(const char* x) { - \\ const auto x_len = strlen(x); - \\ if (x_len + length + 2 > max_size) return false; - \\ std::strncpy(buffer + length, x, max_size - length); - \\ length += x_len; - \\ buffer[length++] = '\n'; - \\ buffer[length] = 0; - \\ return true; - \\} - }); - simple_string_o.root_module.addIncludePath(all_h.dirname()); - simple_string_o.root_module.link_libcpp = true; - - const simple_string_owner_o = addObject(b, opts, .{ .name = "simple_string_owner", .cpp_source_bytes = - \\#include "all.h" - \\ - \\SimpleStringOwner::SimpleStringOwner(const char* x) : string{ 10 } { - \\ if (!string.append_line(x)) { - \\ throw Error{ "Not enough memory!" }; - \\ } - \\ string.print("Constructed"); - \\} - \\ - \\SimpleStringOwner::~SimpleStringOwner() { - \\ string.print("About to destroy"); - \\} - }); - simple_string_owner_o.root_module.addIncludePath(all_h.dirname()); - simple_string_owner_o.root_module.link_libcpp = true; - - const exp_stdout = - \\Constructed: a - \\Constructed: b - \\About to destroy: b - \\About to destroy: a - \\Error: Not enough memory! - \\ - ; - - const exe = addExecutable(b, opts, .{ .name = "main" }); - exe.root_module.addObject(main_o); - exe.root_module.addObject(simple_string_o); - exe.root_module.addObject(simple_string_owner_o); - exe.root_module.link_libcpp = true; - - const run = addRunArtifact(exe); - run.expectStdOutEqual(exp_stdout); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkContains("(was private external) ___gxx_personality_v0"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testEhFramePointerEncodingSdata4(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "eh_frame-pointer-encoding-sdata4", opts); - - const a_o = addObject(b, opts, .{ .name = "foo", .asm_source_bytes = - \\.global _foo - \\.align 2 - \\_foo: - \\ mov w0, #100 - \\ ret - \\LEND_foo: - \\ - \\.section __TEXT,__gcc_except_tab - \\LLSDA_foo: - \\ .byte 0xff - \\ .byte 0xff - \\ .byte 0x01 - \\ .uleb128 0 - \\ - \\.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support - \\LCIE: - \\ .long LCIE_end - LCIE_start - \\LCIE_start: - \\ .long 0 ; CIE ID - \\ .byte 1 ; Version - \\ .asciz "zLR" ; Augmentation string - \\ .uleb128 1 ; Code alignment factor - \\ .sleb128 -8 ; Data alignment factor - \\ .byte 30 ; Return address register - \\ .uleb128 2 ; Augmentation data length - \\ .byte 0x1b ; LSDA pointer encoding (DW_EH_PE_pcrel | DW_EH_PE_sdata4) - \\ .byte 0x1b ; FDE pointer encoding (DW_EH_PE_pcrel | DW_EH_PE_sdata4) - \\ .byte 0x0c ; DW_CFA_def_cfa - \\ .uleb128 31 ; Reg 31 - \\ .uleb128 0 ; Offset 0 - \\ .align 3 - \\LCIE_end: - \\LFDE: - \\ .long LFDE_end - LFDE_start - \\LFDE_start: - \\ .long LFDE_start - LCIE ; CIE pointer - \\ .long _foo - . ; PC begin - \\ .long LEND_foo - _foo ; PC range - \\ .uleb128 4 ; Augmentation data length - \\ .long LLSDA_foo - . ; LSDA pointer - \\ .align 3 - \\LFDE_end: - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int foo(); - \\int main() { - \\ printf("%d\n", foo()); - \\ return 0; - \\} - }); - exe.root_module.addObject(a_o); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("100\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "unwind-info-no-subsections-arm64", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.globl _foo - \\.align 4 - \\_foo: - \\ .cfi_startproc - \\ stp x29, x30, [sp, #-32]! - \\ .cfi_def_cfa_offset 32 - \\ .cfi_offset w30, -24 - \\ .cfi_offset w29, -32 - \\ mov x29, sp - \\ .cfi_def_cfa w29, 32 - \\ bl _bar - \\ ldp x29, x30, [sp], #32 - \\ .cfi_restore w29 - \\ .cfi_restore w30 - \\ .cfi_def_cfa_offset 0 - \\ ret - \\ .cfi_endproc - \\ - \\.globl _bar - \\.align 4 - \\_bar: - \\ .cfi_startproc - \\ sub sp, sp, #32 - \\ .cfi_def_cfa_offset -32 - \\ stp x29, x30, [sp, #16] - \\ .cfi_offset w30, -24 - \\ .cfi_offset w29, -32 - \\ mov x29, sp - \\ .cfi_def_cfa w29, 32 - \\ mov w0, #4 - \\ ldp x29, x30, [sp, #16] - \\ .cfi_restore w29 - \\ .cfi_restore w30 - \\ add sp, sp, #32 - \\ .cfi_def_cfa_offset 0 - \\ ret - \\ .cfi_endproc - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int foo(); - \\int main() { - \\ printf("%d\n", foo()); - \\ return 0; - \\} - }); - exe.root_module.addObject(a_o); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("4\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "unwind-info-no-subsections-x64", opts); - - const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = - \\.globl _foo - \\_foo: - \\ .cfi_startproc - \\ push %rbp - \\ .cfi_def_cfa_offset 8 - \\ .cfi_offset %rbp, -8 - \\ mov %rsp, %rbp - \\ .cfi_def_cfa_register %rbp - \\ call _bar - \\ pop %rbp - \\ .cfi_restore %rbp - \\ .cfi_def_cfa_offset 0 - \\ ret - \\ .cfi_endproc - \\ - \\.globl _bar - \\_bar: - \\ .cfi_startproc - \\ push %rbp - \\ .cfi_def_cfa_offset 8 - \\ .cfi_offset %rbp, -8 - \\ mov %rsp, %rbp - \\ .cfi_def_cfa_register %rbp - \\ mov $4, %rax - \\ pop %rbp - \\ .cfi_restore %rbp - \\ .cfi_def_cfa_offset 0 - \\ ret - \\ .cfi_endproc - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\int foo(); - \\int main() { - \\ printf("%d\n", foo()); - \\ return 0; - \\} - }); - exe.root_module.addObject(a_o); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("4\n"); - test_step.dependOn(&run.step); - - return test_step; -} - -// Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s -fn testWeakBind(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "weak-bind", opts); - - const lib = addSharedLibrary(b, opts, .{ .name = "foo", .asm_source_bytes = - \\.globl _weak_dysym - \\.weak_definition _weak_dysym - \\_weak_dysym: - \\ .quad 0x1234 - \\ - \\.globl _weak_dysym_for_gotpcrel - \\.weak_definition _weak_dysym_for_gotpcrel - \\_weak_dysym_for_gotpcrel: - \\ .quad 0x1234 - \\ - \\.globl _weak_dysym_fn - \\.weak_definition _weak_dysym_fn - \\_weak_dysym_fn: - \\ ret - \\ - \\.section __DATA,__thread_vars,thread_local_variables - \\ - \\.globl _weak_dysym_tlv - \\.weak_definition _weak_dysym_tlv - \\_weak_dysym_tlv: - \\ .quad 0x1234 - }); - - { - const check = lib.checkObject(); - check.checkInExports(); - check.checkExtract("[WEAK] {vmaddr1} _weak_dysym"); - check.checkExtract("[WEAK] {vmaddr2} _weak_dysym_for_gotpcrel"); - check.checkExtract("[WEAK] {vmaddr3} _weak_dysym_fn"); - check.checkExtract("[THREAD_LOCAL, WEAK] {vmaddr4} _weak_dysym_tlv"); - test_step.dependOn(&check.step); - } - - const exe = addExecutable(b, opts, .{ .name = "main", .asm_source_bytes = - \\.globl _main, _weak_external, _weak_external_for_gotpcrel, _weak_external_fn - \\.weak_definition _weak_external, _weak_external_for_gotpcrel, _weak_external_fn, _weak_internal, _weak_internal_for_gotpcrel, _weak_internal_fn - \\ - \\_main: - \\ mov _weak_dysym_for_gotpcrel@GOTPCREL(%rip), %rax - \\ mov _weak_external_for_gotpcrel@GOTPCREL(%rip), %rax - \\ mov _weak_internal_for_gotpcrel@GOTPCREL(%rip), %rax - \\ mov _weak_tlv@TLVP(%rip), %rax - \\ mov _weak_dysym_tlv@TLVP(%rip), %rax - \\ mov _weak_internal_tlv@TLVP(%rip), %rax - \\ callq _weak_dysym_fn - \\ callq _weak_external_fn - \\ callq _weak_internal_fn - \\ mov $0, %rax - \\ ret - \\ - \\_weak_external: - \\ .quad 0x1234 - \\ - \\_weak_external_for_gotpcrel: - \\ .quad 0x1234 - \\ - \\_weak_external_fn: - \\ ret - \\ - \\_weak_internal: - \\ .quad 0x1234 - \\ - \\_weak_internal_for_gotpcrel: - \\ .quad 0x1234 - \\ - \\_weak_internal_fn: - \\ ret - \\ - \\.data - \\ .quad _weak_dysym - \\ .quad _weak_external + 2 - \\ .quad _weak_internal - \\ - \\.tbss _weak_tlv$tlv$init, 4, 2 - \\.tbss _weak_internal_tlv$tlv$init, 4, 2 - \\ - \\.section __DATA,__thread_vars,thread_local_variables - \\.globl _weak_tlv - \\.weak_definition _weak_tlv, _weak_internal_tlv - \\ - \\_weak_tlv: - \\ .quad __tlv_bootstrap - \\ .quad 0 - \\ .quad _weak_tlv$tlv$init - \\ - \\_weak_internal_tlv: - \\ .quad __tlv_bootstrap - \\ .quad 0 - \\ .quad _weak_internal_tlv$tlv$init - }); - exe.root_module.linkLibrary(lib); - - { - const check = exe.checkObject(); - - check.checkInExports(); - check.checkExtract("[WEAK] {vmaddr1} _weak_external"); - check.checkExtract("[WEAK] {vmaddr2} _weak_external_for_gotpcrel"); - check.checkExtract("[WEAK] {vmaddr3} _weak_external_fn"); - check.checkExtract("[THREAD_LOCAL, WEAK] {vmaddr4} _weak_tlv"); - - check.checkInDyldBind(); - check.checkContains("(libfoo.dylib) _weak_dysym_for_gotpcrel"); - check.checkContains("(libfoo.dylib) _weak_dysym_fn"); - check.checkContains("(libfoo.dylib) _weak_dysym"); - check.checkContains("(libfoo.dylib) _weak_dysym_tlv"); - - check.checkInDyldWeakBind(); - check.checkContains("_weak_external_for_gotpcrel"); - check.checkContains("_weak_dysym_for_gotpcrel"); - check.checkContains("_weak_external_fn"); - check.checkContains("_weak_dysym_fn"); - check.checkContains("_weak_dysym"); - check.checkContains("_weak_external"); - check.checkContains("_weak_tlv"); - check.checkContains("_weak_dysym_tlv"); - - test_step.dependOn(&check.step); - } - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testWeakFramework(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "weak-framework", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); - exe.root_module.linkFramework("Cocoa", .{ .weak = true }); - - const run = addRunArtifact(exe); - run.expectExitCode(0); - test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_WEAK_DYLIB"); - check.checkContains("Cocoa"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn testWeakLibrary(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "weak-library", opts); - - const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = - \\#include - \\int a = 42; - \\const char* asStr() { - \\ static char str[3]; - \\ sprintf(str, "%d", 42); - \\ return str; - \\} - }); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\extern int a; - \\extern const char* asStr(); - \\int main() { - \\ printf("%d %s", a, asStr()); - \\ return 0; - \\} - }); - exe.root_module.linkSystemLibrary("a", .{ .weak = true }); - exe.root_module.addLibraryPath(dylib.getEmittedBinDirectory()); - exe.root_module.addRPath(dylib.getEmittedBinDirectory()); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd LOAD_WEAK_DYLIB"); - check.checkContains("liba.dylib"); - check.checkInSymtab(); - check.checkExact("(undefined) weakref external _a (from liba)"); - check.checkInSymtab(); - check.checkExact("(undefined) weakref external _asStr (from liba)"); - test_step.dependOn(&check.step); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("42 42"); - test_step.dependOn(&run.step); - - return test_step; -} - -fn testWeakRef(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "weak-ref", opts); - - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\#include - \\int main(int argc, char** argv) { - \\ printf("__darwin_check_fd_set_overflow: %p\n", __darwin_check_fd_set_overflow); - \\} - }); - - const check = exe.checkObject(); - check.checkInSymtab(); - check.checkExact("(undefined) weakref external ___darwin_check_fd_set_overflow (from libSystem.B)"); - test_step.dependOn(&check.step); - - return test_step; -} - -fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { - return link.addTestStep(b, "" ++ prefix, opts); -} - -const builtin = @import("builtin"); -const addAsmSourceBytes = link.addAsmSourceBytes; -const addCSourceBytes = link.addCSourceBytes; -const addRunArtifact = link.addRunArtifact; -const addObject = link.addObject; -const addExecutable = link.addExecutable; -const addStaticLibrary = link.addStaticLibrary; -const addSharedLibrary = link.addSharedLibrary; -const expectLinkErrors = link.expectLinkErrors; -const link = @import("link.zig"); -const std = @import("std"); - -const Build = std.Build; -const BuildOptions = link.BuildOptions; -const Compile = Step.Compile; -const Options = link.Options; -const Step = Build.Step; -const WriteFile = Step.WriteFile; diff --git a/test/link/static_libs_from_object_files/build.zig b/test/link/static_libs_from_object_files/build.zig deleted file mode 100644 index 809891112d..0000000000 --- a/test/link/static_libs_from_object_files/build.zig +++ /dev/null @@ -1,163 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -const Build = std.Build; -const LazyPath = Build.LazyPath; -const Step = Build.Step; -const Run = Step.Run; -const WriteFile = Step.WriteFile; - -pub fn build(b: *Build) void { - const nb_files = b.option(u32, "nb_files", "Number of c files to generate.") orelse 10; - - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - // generate c files - const files = b.allocator.alloc(LazyPath, nb_files) catch unreachable; - defer b.allocator.free(files); - { - for (files[0 .. nb_files - 1], 1..nb_files) |*file, i| { - const wf = WriteFile.create(b); - file.* = wf.add(b.fmt("src_{}.c", .{i}), b.fmt( - \\extern int foo_0(); - \\extern int bar_{}(); - \\extern int one_{}; - \\int one_{} = 1; - \\int foo_{}() {{ return one_{} + foo_0(); }} - \\int bar_{}() {{ return bar_{}(); }} - , .{ i - 1, i - 1, i, i, i - 1, i, i - 1 })); - } - - { - const wf = WriteFile.create(b); - files[nb_files - 1] = wf.add("src_last.c", b.fmt( - \\extern int foo_0(); - \\extern int bar_{}(); - \\extern int one_{}; - \\int foo_last() {{ return one_{} + foo_0(); }} - \\int bar_last() {{ return bar_{}(); }} - , .{ nb_files - 1, nb_files - 1, nb_files - 1, nb_files - 1 })); - } - } - - add(b, test_step, files, .Debug); - add(b, test_step, files, .ReleaseSafe); - add(b, test_step, files, .ReleaseSmall); - add(b, test_step, files, .ReleaseFast); -} - -fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.builtin.OptimizeMode) void { - const flags = [_][]const u8{ - "-Wall", - "-std=c11", - }; - - // all files at once - { - const exe = b.addExecutable(.{ - .name = "test1", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = optimize, - .target = b.graph.host, - }), - }); - - for (files) |file| { - exe.root_module.addCSourceFile(.{ .file = file, .flags = &flags }); - } - - const run_cmd = b.addRunArtifact(exe); - run_cmd.skip_foreign_checks = true; - run_cmd.expectExitCode(0); - - test_step.dependOn(&run_cmd.step); - } - - // using static librairies - { - const mod_a = b.createModule(.{ .target = b.graph.host, .optimize = optimize }); - const mod_b = b.createModule(.{ .target = b.graph.host, .optimize = optimize }); - - for (files, 1..) |file, i| { - const mod = if (i & 1 == 0) mod_a else mod_b; - mod.addCSourceFile(.{ .file = file, .flags = &flags }); - } - - const lib_a = b.addLibrary(.{ - .linkage = .static, - .name = "test2_a", - .root_module = mod_a, - }); - const lib_b = b.addLibrary(.{ - .linkage = .static, - .name = "test2_b", - .root_module = mod_b, - }); - - const exe = b.addExecutable(.{ - .name = "test2", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .target = b.graph.host, - .optimize = optimize, - }), - }); - exe.root_module.linkLibrary(lib_a); - exe.root_module.linkLibrary(lib_b); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.skip_foreign_checks = true; - run_cmd.expectExitCode(0); - - test_step.dependOn(&run_cmd.step); - } - - // using static librairies and object files - { - const mod_a = b.createModule(.{ .target = b.graph.host, .optimize = optimize }); - const mod_b = b.createModule(.{ .target = b.graph.host, .optimize = optimize }); - - for (files, 1..) |file, i| { - const obj_mod = b.createModule(.{ .target = b.graph.host, .optimize = optimize }); - obj_mod.addCSourceFile(.{ .file = file, .flags = &flags }); - - const obj = b.addObject(.{ - .name = b.fmt("obj_{}", .{i}), - .root_module = obj_mod, - }); - - const lib_mod = if (i & 1 == 0) mod_a else mod_b; - lib_mod.addObject(obj); - } - - const lib_a = b.addLibrary(.{ - .linkage = .static, - .name = "test3_a", - .root_module = mod_a, - }); - const lib_b = b.addLibrary(.{ - .linkage = .static, - .name = "test3_b", - .root_module = mod_b, - }); - - const exe = b.addExecutable(.{ - .name = "test3", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .target = b.graph.host, - .optimize = optimize, - }), - }); - exe.root_module.linkLibrary(lib_a); - exe.root_module.linkLibrary(lib_b); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.skip_foreign_checks = true; - run_cmd.expectExitCode(0); - - test_step.dependOn(&run_cmd.step); - } -} diff --git a/test/link/static_libs_from_object_files/main.zig b/test/link/static_libs_from_object_files/main.zig deleted file mode 100644 index c0f577e7df..0000000000 --- a/test/link/static_libs_from_object_files/main.zig +++ /dev/null @@ -1,20 +0,0 @@ -const std = @import("std"); - -extern fn foo_last() i32; -extern fn bar_last() i32; - -export const one_0: i32 = 1; - -export fn foo_0() i32 { - return 1234; -} -export fn bar_0() i32 { - return 5678; -} - -pub fn main() anyerror!void { - const foo_expected: i32 = 1 + 1234; - const bar_expected: i32 = 5678; - try std.testing.expectEqual(foo_expected, foo_last()); - try std.testing.expectEqual(bar_expected, bar_last()); -} diff --git a/test/link/wasm/archive/build.zig b/test/link/wasm/archive/build.zig deleted file mode 100644 index 4606f1d7cd..0000000000 --- a/test/link/wasm/archive/build.zig +++ /dev/null @@ -1,36 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - // The code in question will pull-in compiler-rt, - // and therefore link with its archive file. - const lib = b.addExecutable(.{ - .name = "main", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = optimize, - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - .strip = false, - }), - }); - lib.entry = .disabled; - lib.use_llvm = false; - lib.use_lld = false; - lib.root_module.export_symbol_names = &.{"foo"}; - - const check = lib.checkObject(); - check.checkInHeaders(); - check.checkExact("Section custom"); - check.checkExact("name __trunch"); // Ensure it was imported and resolved - - test_step.dependOn(&check.step); -} diff --git a/test/link/wasm/archive/main.zig b/test/link/wasm/archive/main.zig deleted file mode 100644 index 465f85f9f6..0000000000 --- a/test/link/wasm/archive/main.zig +++ /dev/null @@ -1,7 +0,0 @@ -export fn foo() void { - var a: f16 = 2.2; - _ = &a; - // this will pull-in compiler-rt - const b = @trunc(a); - _ = b; -} diff --git a/test/link/wasm/basic-features/build.zig b/test/link/wasm/basic-features/build.zig deleted file mode 100644 index d8c854c407..0000000000 --- a/test/link/wasm/basic-features/build.zig +++ /dev/null @@ -1,32 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - // Library with explicitly set cpu features - const lib = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = .Debug, - .target = b.resolveTargetQuery(.{ - .cpu_arch = .wasm32, - .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, - .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}), - .os_tag = .freestanding, - }), - }), - }); - lib.entry = .disabled; - lib.use_llvm = false; - lib.use_lld = false; - - // Verify the result contains the features explicitly set on the target for the library. - const check = lib.checkObject(); - check.checkInHeaders(); - check.checkExact("name target_features"); - check.checkExact("features 1"); - check.checkExact("+ atomics"); - - const test_step = b.step("test", "Run linker test"); - test_step.dependOn(&check.step); - b.default_step = test_step; -} diff --git a/test/link/wasm/basic-features/main.zig b/test/link/wasm/basic-features/main.zig deleted file mode 100644 index 0e416dbf18..0000000000 --- a/test/link/wasm/basic-features/main.zig +++ /dev/null @@ -1 +0,0 @@ -export fn foo() void {} diff --git a/test/link/wasm/export-data/build.zig b/test/link/wasm/export-data/build.zig deleted file mode 100644 index 1cddde208d..0000000000 --- a/test/link/wasm/export-data/build.zig +++ /dev/null @@ -1,30 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - b.default_step = test_step; - - const lib = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .optimize = .Debug, - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - }), - }); - lib.entry = .disabled; - // Disabled to work around the Wasm linker crashing. - // Can be reproduced by commenting out the line below. - lib.bundle_ubsan_rt = false; - lib.use_lld = false; - lib.root_module.export_symbol_names = &.{ "foo", "bar" }; - // Object being linked has neither functions nor globals named "foo" or "bar" and - // so these names correctly fail to be exported when creating an executable. - lib.expect_errors = .{ .exact = &.{ - "error: manually specified export name 'foo' undefined", - "error: manually specified export name 'bar' undefined", - } }; - _ = lib.getEmittedBin(); - - test_step.dependOn(&lib.step); -} diff --git a/test/link/wasm/export-data/lib.zig b/test/link/wasm/export-data/lib.zig deleted file mode 100644 index dffce185fa..0000000000 --- a/test/link/wasm/export-data/lib.zig +++ /dev/null @@ -1,2 +0,0 @@ -export const foo: u32 = 0xbbbbbbbb; -export const bar: u32 = 0xbbbbbbbb; diff --git a/test/link/wasm/export/build.zig b/test/link/wasm/export/build.zig deleted file mode 100644 index a3fa2c345c..0000000000 --- a/test/link/wasm/export/build.zig +++ /dev/null @@ -1,79 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const no_export = b.addExecutable(.{ - .name = "no-export", - .root_module = b.createModule(.{ - .root_source_file = b.path("main-hidden.zig"), - .optimize = optimize, - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - }), - }); - no_export.entry = .disabled; - no_export.use_llvm = false; - no_export.use_lld = false; - // Don't pull in ubsan, since we're just expecting a very minimal executable. - no_export.bundle_ubsan_rt = false; - - const dynamic_export = b.addExecutable(.{ - .name = "dynamic", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = optimize, - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - }), - }); - dynamic_export.entry = .disabled; - dynamic_export.rdynamic = true; - dynamic_export.use_llvm = false; - dynamic_export.use_lld = false; - // Don't pull in ubsan, since we're just expecting a very minimal executable. - dynamic_export.bundle_ubsan_rt = false; - - const force_export = b.addExecutable(.{ - .name = "force", - .root_module = b.createModule(.{ - .root_source_file = b.path("main-hidden.zig"), - .optimize = optimize, - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - }), - }); - force_export.entry = .disabled; - force_export.root_module.export_symbol_names = &.{"foo"}; - force_export.use_llvm = false; - force_export.use_lld = false; - // Don't pull in ubsan, since we're just expecting a very minimal executable. - force_export.bundle_ubsan_rt = false; - - const check_no_export = no_export.checkObject(); - check_no_export.checkInHeaders(); - check_no_export.checkExact("Section export"); - check_no_export.checkExact("entries 1"); - check_no_export.checkExact("name memory"); - check_no_export.checkExact("kind memory"); - - const check_dynamic_export = dynamic_export.checkObject(); - check_dynamic_export.checkInHeaders(); - check_dynamic_export.checkExact("Section export"); - check_dynamic_export.checkExact("entries 2"); - check_dynamic_export.checkExact("name foo"); - check_dynamic_export.checkExact("kind function"); - - const check_force_export = force_export.checkObject(); - check_force_export.checkInHeaders(); - check_force_export.checkExact("Section export"); - check_force_export.checkExact("entries 2"); - check_force_export.checkExact("name foo"); - check_force_export.checkExact("kind function"); - - test_step.dependOn(&check_no_export.step); - test_step.dependOn(&check_dynamic_export.step); - test_step.dependOn(&check_force_export.step); -} diff --git a/test/link/wasm/export/main-hidden.zig b/test/link/wasm/export/main-hidden.zig deleted file mode 100644 index 12589d064c..0000000000 --- a/test/link/wasm/export/main-hidden.zig +++ /dev/null @@ -1,4 +0,0 @@ -fn foo() callconv(.c) void {} -comptime { - @export(&foo, .{ .name = "foo", .visibility = .hidden }); -} diff --git a/test/link/wasm/export/main.zig b/test/link/wasm/export/main.zig deleted file mode 100644 index 0e416dbf18..0000000000 --- a/test/link/wasm/export/main.zig +++ /dev/null @@ -1 +0,0 @@ -export fn foo() void {} diff --git a/test/link/wasm/extern-mangle/a.zig b/test/link/wasm/extern-mangle/a.zig deleted file mode 100644 index 98ff08774c..0000000000 --- a/test/link/wasm/extern-mangle/a.zig +++ /dev/null @@ -1 +0,0 @@ -pub extern "a" fn hello() i32; diff --git a/test/link/wasm/extern-mangle/b.zig b/test/link/wasm/extern-mangle/b.zig deleted file mode 100644 index 06dfb5b79f..0000000000 --- a/test/link/wasm/extern-mangle/b.zig +++ /dev/null @@ -1 +0,0 @@ -pub extern "b" fn hello() i32; diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig deleted file mode 100644 index c856611e97..0000000000 --- a/test/link/wasm/extern-mangle/build.zig +++ /dev/null @@ -1,36 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - .optimize = optimize, - }), - }); - lib.entry = .disabled; - lib.import_symbols = true; // import `a` and `b` - lib.rdynamic = true; // export `foo` - - const check_lib = lib.checkObject(); - check_lib.checkInHeaders(); - check_lib.checkExact("Section import"); - check_lib.checkExact("entries 2"); // a.hello & b.hello - check_lib.checkExact("module a"); - check_lib.checkExact("name hello"); - check_lib.checkExact("module b"); - check_lib.checkExact("name hello"); - - test_step.dependOn(&check_lib.step); -} diff --git a/test/link/wasm/extern-mangle/lib.zig b/test/link/wasm/extern-mangle/lib.zig deleted file mode 100644 index d2f2126c7c..0000000000 --- a/test/link/wasm/extern-mangle/lib.zig +++ /dev/null @@ -1,6 +0,0 @@ -const a = @import("a.zig").hello; -const b = @import("b.zig").hello; -export fn foo() void { - _ = a(); - _ = b(); -} diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig deleted file mode 100644 index 74036d486d..0000000000 --- a/test/link/wasm/extern/build.zig +++ /dev/null @@ -1,28 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const exe = b.addExecutable(.{ - .name = "extern", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = optimize, - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi }), - }), - }); - exe.root_module.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} }); - exe.use_llvm = false; - exe.use_lld = false; - - const run = b.addRunArtifact(exe); - run.skip_foreign_checks = true; - run.expectStdOutEqual("Result: 30"); - - test_step.dependOn(&run.step); -} diff --git a/test/link/wasm/extern/foo.c b/test/link/wasm/extern/foo.c deleted file mode 100644 index 0dafd7e112..0000000000 --- a/test/link/wasm/extern/foo.c +++ /dev/null @@ -1 +0,0 @@ -int foo = 30; diff --git a/test/link/wasm/extern/main.zig b/test/link/wasm/extern/main.zig deleted file mode 100644 index 51c5f84181..0000000000 --- a/test/link/wasm/extern/main.zig +++ /dev/null @@ -1,8 +0,0 @@ -const std = @import("std"); - -extern const foo: u32; - -pub fn main() void { - var stdout_writer = std.Io.File.stdout().writerStreaming(std.Options.debug_io, &.{}); - stdout_writer.interface.print("Result: {d}", .{foo}) catch {}; -} diff --git a/test/link/wasm/function-table/build.zig b/test/link/wasm/function-table/build.zig deleted file mode 100644 index 156ea78805..0000000000 --- a/test/link/wasm/function-table/build.zig +++ /dev/null @@ -1,67 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const export_table = b.addExecutable(.{ - .name = "export_table", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - .optimize = optimize, - }), - }); - export_table.entry = .disabled; - export_table.use_llvm = false; - export_table.use_lld = false; - export_table.export_table = true; - export_table.link_gc_sections = false; - // Don't pull in ubsan, since we're just expecting a very minimal executable. - export_table.bundle_ubsan_rt = false; - - const regular_table = b.addExecutable(.{ - .name = "regular_table", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - .optimize = optimize, - }), - }); - regular_table.entry = .disabled; - regular_table.use_llvm = false; - regular_table.use_lld = false; - regular_table.link_gc_sections = false; // Ensure function table is not empty - // Don't pull in ubsan, since we're just expecting a very minimal executable. - regular_table.bundle_ubsan_rt = false; - - const check_export = export_table.checkObject(); - const check_regular = regular_table.checkObject(); - - check_export.checkInHeaders(); - check_export.checkExact("Section export"); - check_export.checkExact("entries 3"); - check_export.checkExact("name __indirect_function_table"); // as per linker specification - check_export.checkExact("kind table"); - - check_regular.checkInHeaders(); - check_regular.checkExact("Section table"); - check_regular.checkExact("entries 1"); - check_regular.checkExact("type funcref"); - check_regular.checkExact("min 2"); // index starts at 1 & 1 function pointer = 2. - check_regular.checkExact("max 2"); - - check_regular.checkInHeaders(); - check_regular.checkExact("Section element"); - check_regular.checkExact("entries 1"); - check_regular.checkExact("table index 0"); - check_regular.checkExact("i32.const 1"); // we want to start function indexes at 1 - check_regular.checkExact("indexes 1"); // 1 function pointer - - test_step.dependOn(&check_export.step); - test_step.dependOn(&check_regular.step); -} diff --git a/test/link/wasm/function-table/lib.zig b/test/link/wasm/function-table/lib.zig deleted file mode 100644 index ed7a85b2db..0000000000 --- a/test/link/wasm/function-table/lib.zig +++ /dev/null @@ -1,7 +0,0 @@ -var func: *const fn () void = &bar; - -export fn foo() void { - func(); -} - -fn bar() void {} diff --git a/test/link/wasm/infer-features/build.zig b/test/link/wasm/infer-features/build.zig deleted file mode 100644 index 60993af183..0000000000 --- a/test/link/wasm/infer-features/build.zig +++ /dev/null @@ -1,44 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - // Wasm Object file which we will use to infer the features from - const c_obj = b.addObject(.{ - .name = "c_obj", - .root_module = b.createModule(.{ - .root_source_file = null, - .optimize = .Debug, - .target = b.resolveTargetQuery(.{ - .cpu_arch = .wasm32, - .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, - .os_tag = .freestanding, - }), - }), - }); - c_obj.root_module.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} }); - - // Wasm library that doesn't have any features specified. This will - // infer its featureset from other linked object files. - const lib = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .optimize = .Debug, - .target = b.resolveTargetQuery(.{ - .cpu_arch = .wasm32, - .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, - .os_tag = .freestanding, - }), - }), - }); - lib.entry = .disabled; - lib.use_llvm = false; - lib.use_lld = false; - lib.root_module.addObject(c_obj); - - lib.expect_errors = .{ .contains = "error: object requires atomics but specified target features exclude atomics" }; - _ = lib.getEmittedBin(); - - const test_step = b.step("test", "Run linker test"); - test_step.dependOn(&lib.step); - b.default_step = test_step; -} diff --git a/test/link/wasm/infer-features/foo.c b/test/link/wasm/infer-features/foo.c deleted file mode 100644 index 1faba96983..0000000000 --- a/test/link/wasm/infer-features/foo.c +++ /dev/null @@ -1,3 +0,0 @@ -int foo() { - return 5; -} diff --git a/test/link/wasm/infer-features/main.zig b/test/link/wasm/infer-features/main.zig deleted file mode 100644 index 576faf61b6..0000000000 --- a/test/link/wasm/infer-features/main.zig +++ /dev/null @@ -1 +0,0 @@ -extern fn foo() c_int; diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig deleted file mode 100644 index 8989103fc3..0000000000 --- a/test/link/wasm/producers/build.zig +++ /dev/null @@ -1,45 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - .optimize = optimize, - .strip = false, - }), - }); - lib.entry = .disabled; - lib.use_llvm = false; - lib.use_lld = false; - b.installArtifact(lib); - - const version_fmt = "version " ++ builtin.zig_version_string; - - const check_lib = lib.checkObject(); - check_lib.checkInHeaders(); - check_lib.checkExact("name producers"); - check_lib.checkExact("fields 2"); - check_lib.checkExact("field_name language"); - check_lib.checkExact("values 1"); - check_lib.checkExact("value_name Zig"); - check_lib.checkExact(version_fmt); - check_lib.checkExact("field_name processed-by"); - check_lib.checkExact("values 1"); - check_lib.checkExact("value_name Zig"); - check_lib.checkExact(version_fmt); - - test_step.dependOn(&check_lib.step); -} diff --git a/test/link/wasm/producers/lib.zig b/test/link/wasm/producers/lib.zig deleted file mode 100644 index 0e416dbf18..0000000000 --- a/test/link/wasm/producers/lib.zig +++ /dev/null @@ -1 +0,0 @@ -export fn foo() void {} diff --git a/test/link/wasm/shared-memory/build.zig b/test/link/wasm/shared-memory/build.zig deleted file mode 100644 index 1cb26deb54..0000000000 --- a/test/link/wasm/shared-memory/build.zig +++ /dev/null @@ -1,99 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void { - const exe = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .target = b.resolveTargetQuery(.{ - .cpu_arch = .wasm32, - .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, - .cpu_features_add = std.Target.wasm.featureSet(&.{ .atomics, .bulk_memory }), - .os_tag = .freestanding, - }), - .optimize = optimize_mode, - .strip = false, - .single_threaded = false, - }), - }); - exe.entry = .disabled; - exe.use_lld = false; - exe.import_memory = true; - exe.export_memory = true; - exe.shared_memory = true; - exe.max_memory = 67108864; - exe.root_module.export_symbol_names = &.{"foo"}; - // Don't pull in ubsan, since we're just expecting a very minimal executable. - exe.bundle_ubsan_rt = false; - - const check_exe = exe.checkObject(); - - check_exe.checkInHeaders(); - check_exe.checkExact("Section import"); - check_exe.checkExact("entries 1"); - check_exe.checkExact("module env"); - check_exe.checkExact("name memory"); // ensure we are importing memory - - check_exe.checkInHeaders(); - check_exe.checkExact("Section export"); - check_exe.checkExact("entries 2"); - check_exe.checkExact("name foo"); - check_exe.checkExact("name memory"); // ensure we also export memory again - - // This section *must* be emit as the start function is set to the index - // of __wasm_init_memory - // release modes will have the TLS segment optimized out in our test-case. - // This means we won't have __wasm_init_memory in such case, and therefore - // should also not have a section "start" - if (optimize_mode == .Debug) { - check_exe.checkInHeaders(); - check_exe.checkExact("Section start"); - } - - // This section is only and *must* be emit when shared-memory is enabled - // release modes will have the TLS segment optimized out in our test-case. - if (optimize_mode == .Debug) { - check_exe.checkInHeaders(); - check_exe.checkExact("Section data_count"); - check_exe.checkExact("count 1"); - } - - check_exe.checkInHeaders(); - check_exe.checkExact("Section custom"); - check_exe.checkExact("name name"); - check_exe.checkExact("type function"); - if (optimize_mode == .Debug) { - check_exe.checkExact("name __wasm_init_memory"); - check_exe.checkExact("name __wasm_init_tls"); - } - check_exe.checkExact("type global"); - - // In debug mode the symbol __tls_base is resolved to an undefined symbol - // from the object file, hence its placement differs than in release modes - // where the entire tls segment is optimized away, and tls_base will have - // its original position. - if (optimize_mode == .Debug) { - check_exe.checkExact("name __tls_base"); - check_exe.checkExact("name __tls_size"); - check_exe.checkExact("name __tls_align"); - - check_exe.checkExact("type data_segment"); - check_exe.checkExact("names 1"); - check_exe.checkExact("index 0"); - check_exe.checkExact("name .tdata"); - } else { - check_exe.checkNotPresent("name __tls_base"); - check_exe.checkNotPresent("name __tls_size"); - check_exe.checkNotPresent("name __tls_align"); - } - - test_step.dependOn(&check_exe.step); -} diff --git a/test/link/wasm/shared-memory/lib.zig b/test/link/wasm/shared-memory/lib.zig deleted file mode 100644 index 714babb16c..0000000000 --- a/test/link/wasm/shared-memory/lib.zig +++ /dev/null @@ -1,5 +0,0 @@ -threadlocal var some_tls_global: u32 = 1; - -export fn foo() void { - some_tls_global = 2; -} diff --git a/test/link/wasm/stack_pointer/build.zig b/test/link/wasm/stack_pointer/build.zig deleted file mode 100644 index 793e3ae94f..0000000000 --- a/test/link/wasm/stack_pointer/build.zig +++ /dev/null @@ -1,55 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); - add(b, test_step, .ReleaseFast); - add(b, test_step, .ReleaseSmall); - add(b, test_step, .ReleaseSafe); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const lib = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - .optimize = optimize, - .strip = false, - }), - }); - lib.entry = .disabled; - lib.use_llvm = false; - lib.use_lld = false; - lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size - lib.link_gc_sections = false; - b.installArtifact(lib); - - const check_lib = lib.checkObject(); - - // ensure global exists and its initial value is equal to explitic stack size - check_lib.checkInHeaders(); - check_lib.checkExact("Section global"); - check_lib.checkExact("entries 1"); - check_lib.checkExact("type i32"); // on wasm32 the stack pointer must be i32 - check_lib.checkExact("mutable true"); // must be able to mutate the stack pointer - check_lib.checkExtract("i32.const {stack_pointer}"); - check_lib.checkComputeCompare("stack_pointer", .{ .op = .eq, .value = .{ .literal = lib.stack_size.? } }); - - // validate memory section starts after virtual stack - check_lib.checkInHeaders(); - check_lib.checkExact("Section data"); - check_lib.checkExtract("i32.const {data_start}"); - check_lib.checkComputeCompare("data_start", .{ .op = .eq, .value = .{ .variable = "stack_pointer" } }); - - // validate the name of the stack pointer - check_lib.checkInHeaders(); - check_lib.checkExact("Section custom"); - check_lib.checkExact("type global"); - check_lib.checkExact("names 1"); - check_lib.checkExact("index 0"); - check_lib.checkExact("name __stack_pointer"); - test_step.dependOn(&check_lib.step); -} diff --git a/test/link/wasm/stack_pointer/lib.zig b/test/link/wasm/stack_pointer/lib.zig deleted file mode 100644 index 0e416dbf18..0000000000 --- a/test/link/wasm/stack_pointer/lib.zig +++ /dev/null @@ -1 +0,0 @@ -export fn foo() void {} diff --git a/test/link/wasm/type/build.zig b/test/link/wasm/type/build.zig deleted file mode 100644 index 1644ddfa70..0000000000 --- a/test/link/wasm/type/build.zig +++ /dev/null @@ -1,43 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - add(b, test_step, .Debug); -} - -fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const exe = b.addExecutable(.{ - .name = "lib", - .root_module = b.createModule(.{ - .root_source_file = b.path("lib.zig"), - .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), - .optimize = optimize, - .strip = false, - }), - }); - exe.entry = .disabled; - exe.use_llvm = false; - exe.use_lld = false; - exe.root_module.export_symbol_names = &.{"foo"}; - // Don't pull in ubsan, since we're just expecting a very minimal executable. - exe.bundle_ubsan_rt = false; - b.installArtifact(exe); - - const check_exe = exe.checkObject(); - check_exe.checkInHeaders(); - check_exe.checkExact("Section type"); - // only 2 entries, although we have more functions. - // This is to test functions with the same function signature - // have their types deduplicated. - check_exe.checkExact("entries 2"); - check_exe.checkExact("params 1"); - check_exe.checkExact("type i32"); - check_exe.checkExact("returns 1"); - check_exe.checkExact("type i64"); - check_exe.checkExact("params 0"); - check_exe.checkExact("returns 0"); - - test_step.dependOn(&check_exe.step); -} diff --git a/test/link/wasm/type/lib.zig b/test/link/wasm/type/lib.zig deleted file mode 100644 index a7a1577d34..0000000000 --- a/test/link/wasm/type/lib.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn foo(x: u32) u64 { - return bar(x); -} - -fn bar(x: u32) u64 { - y(); - return x; -} - -fn y() void {} diff --git a/test/standalone/compiler_rt_panic/build.zig b/test/standalone/compiler_rt_panic/build.zig index e25fe0eff7..27f38d3691 100644 --- a/test/standalone/compiler_rt_panic/build.zig +++ b/test/standalone/compiler_rt_panic/build.zig @@ -25,10 +25,4 @@ pub fn build(b: *std.Build) void { }); exe.link_gc_sections = false; exe.bundle_compiler_rt = true; - - // Verify compiler_rt hasn't pulled in any debug handlers - const check_exe = exe.checkObject(); - check_exe.checkInSymtab(); - check_exe.checkNotPresent("debug.readElfDebugInfo"); - test_step.dependOn(&check_exe.step); } diff --git a/test/standalone/glibc_compat/build.zig b/test/standalone/glibc_compat/build.zig index a9021d4d44..2a171c2ddd 100644 --- a/test/standalone/glibc_compat/build.zig +++ b/test/standalone/glibc_compat/build.zig @@ -88,69 +88,6 @@ pub fn build(b: *std.Build) void { test_step.dependOn(&run_cmd.step); } } - const check = exe.checkObject(); - - // __errno_location is always a dynamically linked symbol - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __errno_location"); - - // before v2.32 fstat redirects through __fxstat, afterwards its a - // normal dynamic symbol - check.checkInDynamicSymtab(); - if (glibc_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) == .lt) { - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __fxstat"); - - check.checkInSymtab(); - check.checkContains("FUNC LOCAL HIDDEN fstat"); - } else { - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT fstat"); - - check.checkInSymtab(); - check.checkNotPresent("__fxstat"); - } - - // before v2.26 reallocarray is not supported - check.checkInDynamicSymtab(); - if (glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) { - check.checkNotPresent("reallocarray"); - } else { - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT reallocarray"); - } - - // before v2.38 strlcpy is not supported - check.checkInDynamicSymtab(); - if (glibc_ver.order(.{ .major = 2, .minor = 38, .patch = 0 }) == .lt) { - check.checkNotPresent("strlcpy"); - } else { - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT strlcpy"); - } - - // v2.16 introduced getauxval() - check.checkInDynamicSymtab(); - if (glibc_ver.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) { - check.checkNotPresent("getauxval"); - } else { - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT getauxval"); - } - - // Always have dynamic "exit", "pow", and "powf" references - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT exit"); - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT pow"); - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT powf"); - - if (target.result.cpu.arch != .s390x) { - // An atexit local symbol is defined, and depends on undefined dynamic - // __cxa_atexit. - check.checkInSymtab(); - check.checkContains("FUNC LOCAL HIDDEN atexit"); - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __cxa_atexit"); - } - - test_step.dependOn(&check.step); } // Build & run a Zig test case against a sampling of supported glibc versions @@ -236,63 +173,5 @@ pub fn build(b: *std.Build) void { test_step.dependOn(&run_cmd.step); } } - const check = exe.checkObject(); - - // __errno_location is always a dynamically linked symbol - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __errno_location"); - - // before v2.32 fstatat redirects through __fxstatat, afterwards its a - // normal dynamic symbol - if (glibc_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) == .lt) { - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __fxstatat"); - - check.checkInSymtab(); - check.checkContains("FUNC LOCAL HIDDEN fstatat"); - } else { - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT fstatat"); - - check.checkInSymtab(); - check.checkNotPresent("FUNC LOCAL HIDDEN fstatat"); - } - - // before v2.26 reallocarray is not supported - if (glibc_ver.order(.{ .major = 2, .minor = 26, .patch = 0 }) == .lt) { - check.checkInDynamicSymtab(); - check.checkNotPresent("reallocarray"); - } else { - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT reallocarray"); - } - - // before v2.38 strlcpy is not supported - if (glibc_ver.order(.{ .major = 2, .minor = 38, .patch = 0 }) == .lt) { - check.checkInDynamicSymtab(); - check.checkNotPresent("strlcpy"); - } else { - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT strlcpy"); - } - - // v2.16 introduced getauxval(), so always present - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT getauxval"); - - // Always have a dynamic "exit" reference - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT exit"); - - if (target.result.cpu.arch != .s390x) { - // An atexit local symbol is defined, and depends on undefined dynamic - // __cxa_atexit. - check.checkInSymtab(); - check.checkContains("FUNC LOCAL HIDDEN atexit"); - check.checkInDynamicSymtab(); - check.checkExact("0 0 UND FUNC GLOBAL DEFAULT __cxa_atexit"); - } - - test_step.dependOn(&check.step); } } diff --git a/test/standalone/ios/build.zig b/test/standalone/ios/build.zig index b87d55993b..c8d4eb6dfb 100644 --- a/test/standalone/ios/build.zig +++ b/test/standalone/ios/build.zig @@ -37,10 +37,4 @@ pub fn build(b: *std.Build) void { exe.root_module.addCSourceFile(.{ .file = b.path("main.m"), .flags = &.{} }); exe.root_module.linkFramework("Foundation", .{}); exe.root_module.linkFramework("UIKit", .{}); - - const check = exe.checkObject(); - check.checkInHeaders(); - check.checkExact("cmd BUILD_VERSION"); - check.checkExact("platform IOS"); - test_step.dependOn(&check.step); } diff --git a/test/tests.zig b/test/tests.zig index 1c77471014..061c75e0e5 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2204,27 +2204,6 @@ pub fn addStandaloneTests( return step; } -pub fn addLinkTests( - b: *std.Build, - enable_macos_sdk: bool, - enable_ios_sdk: bool, - enable_symlinks_windows: bool, -) *Step { - const step = b.step("test-link", "Run the linker tests"); - if (compilerHasPackageManager(b)) { - const test_cases_dep_name = "link_test_cases"; - const test_cases_dep = b.dependency(test_cases_dep_name, .{ - .enable_ios_sdk = enable_ios_sdk, - .enable_macos_sdk = enable_macos_sdk, - .enable_symlinks_windows = enable_symlinks_windows, - }); - const test_cases_dep_step = test_cases_dep.builder.default_step; - test_cases_dep_step.name = b.dupe(test_cases_dep_name); - step.dependOn(test_cases_dep.builder.default_step); - } - return step; -} - pub fn addCliTests(b: *std.Build) *Step { const step = b.step("test-cli", "Test the command line interface"); const s = std.fs.path.sep_str;