authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-11-10 18:36:06+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-10 18:36:06+01:00
log9ad03b628f5d4770f9f26e646019292b5ae9cf9b
tree40ed7ff9e5b9e895ab686e19df6f943732961932
parentec934c6d3294f1febe69453266556a82f64b59f0
parent17177727c03d09303093c39c741a850abcc724f3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17955 from ziglang/issue-17951

macho: handle special section/segment boundary symbols

6 files changed, 398 insertions(+), 114 deletions(-)

src/link/MachO.zig+164-6
......@@ -251,7 +251,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
251251 .incremental,
252252 };
253253
254 if (options.use_llvm) {
254 if (options.use_llvm and options.module != null) {
255255 self.llvm_object = try LlvmObject.create(gpa, options);
256256 }
257257
......@@ -1416,6 +1416,51 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
14161416 seg.segName(),
14171417 });
14181418 }
1419
1420 for (self.globals.items) |global| {
1421 const sym = self.getSymbolPtr(global);
1422 if (sym.n_desc != N_BOUNDARY) continue;
1423 if (self.getSectionBoundarySymbol(global)) |bsym| {
1424 const sect_id = self.getSectionByName(bsym.segname, bsym.sectname) orelse {
1425 try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "section not found: {s},{s}", .{
1426 bsym.segname, bsym.sectname,
1427 });
1428 continue;
1429 };
1430 const sect = self.sections.items(.header)[sect_id];
1431 sym.n_sect = sect_id + 1;
1432 sym.n_value = switch (bsym.kind) {
1433 .start => sect.addr,
1434 .stop => sect.addr + sect.size,
1435 };
1436
1437 log.debug("allocating {s} at @0x{x} sect({d})", .{
1438 self.getSymbolName(global),
1439 sym.n_value,
1440 sym.n_sect,
1441 });
1442
1443 continue;
1444 }
1445 if (self.getSegmentBoundarySymbol(global)) |bsym| {
1446 const seg_id = self.getSegmentByName(bsym.segname) orelse {
1447 try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "segment not found: {s}", .{
1448 bsym.segname,
1449 });
1450
1451 continue;
1452 };
1453 const seg = self.segments.items[seg_id];
1454 sym.n_value = switch (bsym.kind) {
1455 .start => seg.vmaddr,
1456 .stop => seg.vmaddr + seg.vmsize,
1457 };
1458
1459 log.debug("allocating {s} at @0x{x} ", .{ self.getSymbolName(global), sym.n_value });
1460
1461 continue;
1462 }
1463 }
14191464}
14201465
14211466const CreateAtomOpts = struct {
......@@ -1442,6 +1487,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {
14421487 const sym = self.getSymbolPtr(global);
14431488 if (!sym.tentative()) continue;
14441489 if (sym.n_desc == N_DEAD) continue;
1490 if (sym.n_desc == N_BOUNDARY) continue;
14451491
14461492 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
14471493 global.sym_index, self.getSymbolName(global), global.file,
......@@ -1630,6 +1676,13 @@ pub fn resolveSymbols(self: *MachO) !void {
16301676 try self.createMhExecuteHeaderSymbol();
16311677 try self.createDsoHandleSymbol();
16321678 try self.resolveSymbolsAtLoading();
1679
1680 // Final stop, check if unresolved contain any of the special magic boundary symbols
1681 // * section$start$
1682 // * section$stop$
1683 // * segment$start$
1684 // * segment$stop$
1685 try self.resolveBoundarySymbols();
16331686}
16341687
16351688fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
......@@ -1845,6 +1898,34 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {
18451898 }
18461899}
18471900
1901fn resolveBoundarySymbols(self: *MachO) !void {
1902 var next_sym: usize = 0;
1903 while (next_sym < self.unresolved.count()) {
1904 const global_index = self.unresolved.keys()[next_sym];
1905 const global = &self.globals.items[global_index];
1906
1907 if (self.getSectionBoundarySymbol(global.*) != null or self.getSegmentBoundarySymbol(global.*) != null) {
1908 const sym_index = try self.allocateSymbol();
1909 const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
1910 const sym = self.getSymbolPtr(sym_loc);
1911 sym.* = .{
1912 .n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(global.*)),
1913 .n_type = macho.N_SECT | macho.N_EXT,
1914 .n_sect = 0,
1915 .n_desc = N_BOUNDARY,
1916 .n_value = 0,
1917 };
1918 if (global.getFile()) |file| {
1919 const global_object = &self.objects.items[file];
1920 global_object.globals_lookup[global.sym_index] = global_index;
1921 }
1922 global.* = sym_loc;
1923 _ = self.unresolved.swapRemove(global_index);
1924 continue;
1925 }
1926 }
1927}
1928
18481929pub fn deinit(self: *MachO) void {
18491930 const gpa = self.base.allocator;
18501931
......@@ -3565,6 +3646,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
35653646 const atom = self.getAtom(atom_index);
35663647 const sym = self.getSymbol(atom.getSymbolWithLoc());
35673648 if (sym.n_desc == N_DEAD) continue;
3649 if (sym.n_desc == N_BOUNDARY) continue;
35683650
35693651 const sect_id = sym.n_sect - 1;
35703652 const section = self.sections.items(.header)[sect_id];
......@@ -3719,6 +3801,7 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
37193801 const atom = self.getAtom(atom_index);
37203802 const sym = self.getSymbol(atom.getSymbolWithLoc());
37213803 if (sym.n_desc == N_DEAD) continue;
3804 if (sym.n_desc == N_BOUNDARY) continue;
37223805
37233806 const sect_id = sym.n_sect - 1;
37243807 const section = self.sections.items(.header)[sect_id];
......@@ -3819,6 +3902,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
38193902 if (sym.undf()) continue;
38203903 assert(sym.ext());
38213904 if (sym.n_desc == N_DEAD) continue;
3905 if (sym.n_desc == N_BOUNDARY) continue;
38223906
38233907 const sym_name = self.getSymbolName(global);
38243908 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
......@@ -3953,7 +4037,8 @@ const asc_u64 = std.sort.asc(u64);
39534037fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
39544038 const sym = self.getSymbol(sym_loc);
39554039 if (sym.n_strx == 0) return;
3956 if (sym.n_desc == MachO.N_DEAD) return;
4040 if (sym.n_desc == N_DEAD) return;
4041 if (sym.n_desc == N_BOUNDARY) return;
39574042 if (self.symbolIsTemp(sym_loc)) return;
39584043 try addresses.append(sym.n_value);
39594044}
......@@ -4061,7 +4146,8 @@ pub fn writeDataInCode(self: *MachO) !void {
40614146 for (object.exec_atoms.items) |atom_index| {
40624147 const atom = self.getAtom(atom_index);
40634148 const sym = self.getSymbol(atom.getSymbolWithLoc());
4064 if (sym.n_desc == MachO.N_DEAD) continue;
4149 if (sym.n_desc == N_DEAD) continue;
4150 if (sym.n_desc == N_BOUNDARY) return;
40654151
40664152 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
40674153 source_sym.n_value
......@@ -4119,7 +4205,8 @@ fn writeSymtabs(self: *MachO) !void {
41194205fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
41204206 const sym = self.getSymbol(sym_loc);
41214207 if (sym.n_strx == 0) return; // no name, skip
4122 if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip
4208 if (sym.n_desc == N_DEAD) return; // garbage-collected, skip
4209 if (sym.n_desc == N_BOUNDARY) return; // boundary symbol, skip
41234210 if (sym.ext()) return; // an export lands in its own symtab section, skip
41244211 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
41254212 var out_sym = sym;
......@@ -4157,6 +4244,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
41574244 const sym = self.getSymbol(global);
41584245 if (sym.undf()) continue; // import, skip
41594246 if (sym.n_desc == N_DEAD) continue;
4247 if (sym.n_desc == N_BOUNDARY) continue;
41604248 var out_sym = sym;
41614249 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
41624250 try exports.append(out_sym);
......@@ -4172,6 +4260,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
41724260 if (sym.n_strx == 0) continue; // no name, skip
41734261 if (!sym.undf()) continue; // not an import, skip
41744262 if (sym.n_desc == N_DEAD) continue;
4263 if (sym.n_desc == N_BOUNDARY) continue;
41754264 const new_index = @as(u32, @intCast(imports.items.len));
41764265 var out_sym = sym;
41774266 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
......@@ -4842,6 +4931,55 @@ pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8
48424931 }
48434932}
48444933
4934const BoundarySymbolKind = enum {
4935 start,
4936 stop,
4937};
4938
4939const SectionBoundarySymbol = struct {
4940 kind: BoundarySymbolKind,
4941 segname: []const u8,
4942 sectname: []const u8,
4943};
4944
4945pub fn getSectionBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SectionBoundarySymbol {
4946 const sym_name = self.getSymbolName(sym_with_loc);
4947 if (mem.startsWith(u8, sym_name, "section$")) {
4948 const trailing = sym_name["section$".len..];
4949 const kind: BoundarySymbolKind = kind: {
4950 if (mem.startsWith(u8, trailing, "start$")) break :kind .start;
4951 if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop;
4952 return null;
4953 };
4954 const names = trailing[@tagName(kind).len + 1 ..];
4955 const sep_idx = mem.indexOf(u8, names, "$") orelse return null;
4956 const segname = names[0..sep_idx];
4957 const sectname = names[sep_idx + 1 ..];
4958 return .{ .kind = kind, .segname = segname, .sectname = sectname };
4959 }
4960 return null;
4961}
4962
4963const SegmentBoundarySymbol = struct {
4964 kind: BoundarySymbolKind,
4965 segname: []const u8,
4966};
4967
4968pub fn getSegmentBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SegmentBoundarySymbol {
4969 const sym_name = self.getSymbolName(sym_with_loc);
4970 if (mem.startsWith(u8, sym_name, "segment$")) {
4971 const trailing = sym_name["segment$".len..];
4972 const kind: BoundarySymbolKind = kind: {
4973 if (mem.startsWith(u8, trailing, "start$")) break :kind .start;
4974 if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop;
4975 return null;
4976 };
4977 const segname = trailing[@tagName(kind).len + 1 ..];
4978 return .{ .kind = kind, .segname = segname };
4979 }
4980 return null;
4981}
4982
48454983/// Returns pointer to the global entry for `name` if one exists.
48464984pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc {
48474985 const global_index = self.resolver.get(name) orelse return null;
......@@ -5137,6 +5275,23 @@ pub fn reportParseError(
51375275 });
51385276}
51395277
5278pub fn reportUnresolvedBoundarySymbol(
5279 self: *MachO,
5280 sym_name: []const u8,
5281 comptime format: []const u8,
5282 args: anytype,
5283) error{OutOfMemory}!void {
5284 const gpa = self.base.allocator;
5285 try self.misc_errors.ensureUnusedCapacity(gpa, 1);
5286 var notes = try gpa.alloc(File.ErrorMsg, 1);
5287 errdefer gpa.free(notes);
5288 notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while resolving {s}", .{sym_name}) };
5289 self.misc_errors.appendAssumeCapacity(.{
5290 .msg = try std.fmt.allocPrint(gpa, format, args),
5291 .notes = notes,
5292 });
5293}
5294
51405295pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
51415296 const gpa = self.base.allocator;
51425297 const count = self.unresolved.count();
......@@ -5340,7 +5495,8 @@ pub fn logSymtab(self: *MachO) void {
53405495 for (self.globals.items, 0..) |global, i| {
53415496 const sym = self.getSymbol(global);
53425497 if (sym.undf()) continue;
5343 if (sym.n_desc == MachO.N_DEAD) continue;
5498 if (sym.n_desc == N_DEAD) continue;
5499 if (sym.n_desc == N_BOUNDARY) continue;
53445500 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
53455501 i,
53465502 self.getSymbolName(global),
......@@ -5355,7 +5511,8 @@ pub fn logSymtab(self: *MachO) void {
53555511 for (self.globals.items, 0..) |global, i| {
53565512 const sym = self.getSymbol(global);
53575513 if (!sym.undf()) continue;
5358 if (sym.n_desc == MachO.N_DEAD) continue;
5514 if (sym.n_desc == N_DEAD) continue;
5515 if (sym.n_desc == N_BOUNDARY) continue;
53595516 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
53605517 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
53615518 i,
......@@ -5466,6 +5623,7 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
54665623
54675624pub const base_tag: File.Tag = File.Tag.macho;
54685625pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
5626pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));
54695627
54705628/// Mode of operation of the linker.
54715629pub const Mode = enum {
src/link/MachO/dead_strip.zig+1
......@@ -50,6 +50,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
5050 for (macho_file.globals.items) |global| {
5151 const sym = macho_file.getSymbol(global);
5252 if (sym.undf()) continue;
53 if (sym.n_desc == MachO.N_BOUNDARY) continue;
5354
5455 if (global.getFile()) |file| {
5556 try addRoot(macho_file, roots, file, global);
test/link.zig+5-6
......@@ -4,6 +4,11 @@ pub const Case = struct {
44};
55
66pub const cases = [_]Case{
7 .{
8 .build_root = "test/link",
9 .import = @import("link/link.zig"),
10 },
11
712 .{
813 .build_root = "test/link/bss",
914 .import = @import("link/bss/build.zig"),
......@@ -29,12 +34,6 @@ pub const cases = [_]Case{
2934 .import = @import("link/glibc_compat/build.zig"),
3035 },
3136
32 // Elf Cases
33 .{
34 .build_root = "test/link",
35 .import = @import("link/elf.zig"),
36 },
37
3837 // WASM Cases
3938 // https://github.com/ziglang/zig/issues/16938
4039 //.{
test/link/elf.zig+18-102
......@@ -2,9 +2,8 @@
22//! Currently, we support linking x86_64 Linux, but in the future we
33//! will progressively relax those to exercise more combinations.
44
5pub fn build(b: *Build) void {
5pub fn testAll(b: *Build) *Step {
66 const elf_step = b.step("test-elf", "Run ELF tests");
7 b.default_step = elf_step;
87
98 const default_target = CrossTarget{
109 .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs
......@@ -123,6 +122,8 @@ pub fn build(b: *Build) void {
123122 elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));
124123 elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target }));
125124 elf_step.dependOn(testZText(b, .{ .target = glibc_target }));
125
126 return elf_step;
126127}
127128
128129fn testAbsSymbols(b: *Build, opts: Options) *Step {
......@@ -3479,110 +3480,25 @@ fn testZText(b: *Build, opts: Options) *Step {
34793480 return test_step;
34803481}
34813482
3482const Options = struct {
3483 target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
3484 optimize: std.builtin.OptimizeMode = .Debug,
3485 use_llvm: bool = true,
3486 use_lld: bool = false,
3487};
3488
34893483fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
3490 const target = opts.target.zigTriple(b.allocator) catch @panic("OOM");
3491 const optimize = @tagName(opts.optimize);
3492 const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";
3493 const name = std.fmt.allocPrint(b.allocator, "test-elf-" ++ prefix ++ "-{s}-{s}-{s}", .{
3494 target,
3495 optimize,
3496 use_llvm,
3497 }) catch @panic("OOM");
3498 return b.step(name, "");
3499}
3500
3501fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
3502 return b.addExecutable(.{
3503 .name = name,
3504 .target = opts.target,
3505 .optimize = opts.optimize,
3506 .use_llvm = opts.use_llvm,
3507 .use_lld = opts.use_lld,
3508 });
3509}
3510
3511fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
3512 return b.addObject(.{
3513 .name = name,
3514 .target = opts.target,
3515 .optimize = opts.optimize,
3516 .use_llvm = opts.use_llvm,
3517 .use_lld = opts.use_lld,
3518 });
3519}
3520
3521fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
3522 return b.addStaticLibrary(.{
3523 .name = name,
3524 .target = opts.target,
3525 .optimize = opts.optimize,
3526 .use_llvm = opts.use_llvm,
3527 .use_lld = opts.use_lld,
3528 });
3529}
3530
3531fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
3532 return b.addSharedLibrary(.{
3533 .name = name,
3534 .target = opts.target,
3535 .optimize = opts.optimize,
3536 .use_llvm = opts.use_llvm,
3537 .use_lld = opts.use_lld,
3538 });
3539}
3540
3541fn addRunArtifact(comp: *Compile) *Run {
3542 const b = comp.step.owner;
3543 const run = b.addRunArtifact(comp);
3544 run.skip_foreign_checks = true;
3545 return run;
3546}
3547
3548fn addZigSourceBytes(comp: *Compile, bytes: []const u8) void {
3549 const b = comp.step.owner;
3550 const file = WriteFile.create(b).add("a.zig", bytes);
3551 file.addStepDependencies(&comp.step);
3552 comp.root_src = file;
3553}
3554
3555fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
3556 const b = comp.step.owner;
3557 const file = WriteFile.create(b).add("a.c", bytes);
3558 comp.addCSourceFile(.{ .file = file, .flags = flags });
3559}
3560
3561fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
3562 const b = comp.step.owner;
3563 const file = WriteFile.create(b).add("a.cpp", bytes);
3564 comp.addCSourceFile(.{ .file = file, .flags = flags });
3565}
3566
3567fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
3568 const b = comp.step.owner;
3569 const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
3570 const file = WriteFile.create(b).add("a.s", actual_bytes);
3571 comp.addAssemblyFile(file);
3572}
3573
3574fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
3575 comp.expect_errors = expected_errors;
3576 const bin_file = comp.getEmittedBin();
3577 bin_file.addStepDependencies(test_step);
3578}
3579
3484 return link.addTestStep(b, "elf-" ++ prefix, opts);
3485}
3486
3487const addAsmSourceBytes = link.addAsmSourceBytes;
3488const addCSourceBytes = link.addCSourceBytes;
3489const addCppSourceBytes = link.addCppSourceBytes;
3490const addExecutable = link.addExecutable;
3491const addObject = link.addObject;
3492const addRunArtifact = link.addRunArtifact;
3493const addSharedLibrary = link.addSharedLibrary;
3494const addStaticLibrary = link.addStaticLibrary;
3495const addZigSourceBytes = link.addZigSourceBytes;
3496const expectLinkErrors = link.expectLinkErrors;
3497const link = @import("link.zig");
35803498const std = @import("std");
35813499
35823500const Build = std.Build;
3583const Compile = Step.Compile;
35843501const CrossTarget = std.zig.CrossTarget;
3585const LazyPath = Build.LazyPath;
3586const Run = Step.Run;
3502const Options = link.Options;
35873503const Step = Build.Step;
35883504const WriteFile = Step.WriteFile;
test/link/link.zig created+114
......@@ -0,0 +1,114 @@
1pub fn build(b: *Build) void {
2 const test_step = b.step("test-link", "Run link tests");
3 b.default_step = test_step;
4
5 test_step.dependOn(@import("elf.zig").testAll(b));
6 test_step.dependOn(@import("macho.zig").testAll(b));
7}
8
9pub const Options = struct {
10 target: CrossTarget,
11 optimize: std.builtin.OptimizeMode = .Debug,
12 use_llvm: bool = true,
13 use_lld: bool = false,
14};
15
16pub fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
17 const target = opts.target.zigTriple(b.allocator) catch @panic("OOM");
18 const optimize = @tagName(opts.optimize);
19 const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";
20 const name = std.fmt.allocPrint(b.allocator, "test-" ++ prefix ++ "-{s}-{s}-{s}", .{
21 target,
22 optimize,
23 use_llvm,
24 }) catch @panic("OOM");
25 return b.step(name, "");
26}
27
28pub fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
29 return b.addExecutable(.{
30 .name = name,
31 .target = opts.target,
32 .optimize = opts.optimize,
33 .use_llvm = opts.use_llvm,
34 .use_lld = opts.use_lld,
35 });
36}
37
38pub fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
39 return b.addObject(.{
40 .name = name,
41 .target = opts.target,
42 .optimize = opts.optimize,
43 .use_llvm = opts.use_llvm,
44 .use_lld = opts.use_lld,
45 });
46}
47
48pub fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
49 return b.addStaticLibrary(.{
50 .name = name,
51 .target = opts.target,
52 .optimize = opts.optimize,
53 .use_llvm = opts.use_llvm,
54 .use_lld = opts.use_lld,
55 });
56}
57
58pub fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
59 return b.addSharedLibrary(.{
60 .name = name,
61 .target = opts.target,
62 .optimize = opts.optimize,
63 .use_llvm = opts.use_llvm,
64 .use_lld = opts.use_lld,
65 });
66}
67
68pub fn addRunArtifact(comp: *Compile) *Run {
69 const b = comp.step.owner;
70 const run = b.addRunArtifact(comp);
71 run.skip_foreign_checks = true;
72 return run;
73}
74
75pub fn addZigSourceBytes(comp: *Compile, bytes: []const u8) void {
76 const b = comp.step.owner;
77 const file = WriteFile.create(b).add("a.zig", bytes);
78 file.addStepDependencies(&comp.step);
79 comp.root_src = file;
80}
81
82pub fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
83 const b = comp.step.owner;
84 const file = WriteFile.create(b).add("a.c", bytes);
85 comp.addCSourceFile(.{ .file = file, .flags = flags });
86}
87
88pub fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void {
89 const b = comp.step.owner;
90 const file = WriteFile.create(b).add("a.cpp", bytes);
91 comp.addCSourceFile(.{ .file = file, .flags = flags });
92}
93
94pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void {
95 const b = comp.step.owner;
96 const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM");
97 const file = WriteFile.create(b).add("a.s", actual_bytes);
98 comp.addAssemblyFile(file);
99}
100
101pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void {
102 comp.expect_errors = expected_errors;
103 const bin_file = comp.getEmittedBin();
104 bin_file.addStepDependencies(test_step);
105}
106
107const std = @import("std");
108
109const Build = std.Build;
110const Compile = Step.Compile;
111const CrossTarget = std.zig.CrossTarget;
112const Run = Step.Run;
113const Step = Build.Step;
114const WriteFile = Step.WriteFile;
test/link/macho.zig created+96
......@@ -0,0 +1,96 @@
1//! Here we test our MachO linker for correctness and functionality.
2//! TODO migrate standalone tests from test/link/macho/* to here.
3
4pub fn testAll(b: *Build) *Step {
5 const macho_step = b.step("test-macho", "Run MachO tests");
6
7 const default_target = CrossTarget{ .os_tag = .macos };
8
9 macho_step.dependOn(testResolvingBoundarySymbols(b, .{ .target = default_target }));
10
11 return macho_step;
12}
13
14fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step {
15 const test_step = addTestStep(b, "macho-resolving-boundary-symbols", opts);
16
17 const obj1 = addObject(b, "obj1", opts);
18 addCppSourceBytes(obj1,
19 \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase";
20 , &.{});
21
22 const main_o = addObject(b, "main", opts);
23 addZigSourceBytes(main_o,
24 \\const std = @import("std");
25 \\extern fn interop() [*:0]const u8;
26 \\pub fn main() !void {
27 \\ std.debug.print("All your {s} are belong to us.\n", .{
28 \\ std.mem.span(interop()),
29 \\ });
30 \\}
31 );
32
33 {
34 const obj2 = addObject(b, "obj2", opts);
35 addCppSourceBytes(obj2,
36 \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr");
37 \\extern "C" const char* interop() {
38 \\ return message_pointer;
39 \\}
40 , &.{});
41
42 const exe = addExecutable(b, "test", opts);
43 exe.addObject(obj1);
44 exe.addObject(obj2);
45 exe.addObject(main_o);
46
47 const run = addRunArtifact(exe);
48 run.expectStdErrEqual("All your codebase are belong to us.\n");
49 test_step.dependOn(&run.step);
50
51 const check = exe.checkObject();
52 check.checkInSymtab();
53 check.checkNotPresent("section$start$__DATA_CONST$__message_ptr");
54 test_step.dependOn(&check.step);
55 }
56
57 {
58 const obj3 = addObject(b, "obj3", opts);
59 addCppSourceBytes(obj3,
60 \\extern const char* message_pointer __asm("section$start$__DATA$__message_ptr");
61 \\extern "C" const char* interop() {
62 \\ return message_pointer;
63 \\}
64 , &.{});
65
66 const exe = addExecutable(b, "test", opts);
67 exe.addObject(obj1);
68 exe.addObject(obj3);
69 exe.addObject(main_o);
70
71 expectLinkErrors(exe, test_step, .{ .exact = &.{
72 "section not found: __DATA,__message_ptr",
73 "note: while resolving section$start$__DATA$__message_ptr",
74 } });
75 }
76
77 return test_step;
78}
79
80fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
81 return link.addTestStep(b, "macho-" ++ prefix, opts);
82}
83
84const addCppSourceBytes = link.addCppSourceBytes;
85const addExecutable = link.addExecutable;
86const addObject = link.addObject;
87const addRunArtifact = link.addRunArtifact;
88const addZigSourceBytes = link.addZigSourceBytes;
89const expectLinkErrors = link.expectLinkErrors;
90const link = @import("link.zig");
91const std = @import("std");
92
93const Build = std.Build;
94const CrossTarget = std.zig.CrossTarget;
95const Options = link.Options;
96const Step = Build.Step;