mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 13:58:27 +00:00
Merge pull request #17955 from ziglang/issue-17951
macho: handle special section/segment boundary symbols
This commit is contained in:
commit
9ad03b628f
@ -251,7 +251,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
|
||||
.incremental,
|
||||
};
|
||||
|
||||
if (options.use_llvm) {
|
||||
if (options.use_llvm and options.module != null) {
|
||||
self.llvm_object = try LlvmObject.create(gpa, options);
|
||||
}
|
||||
|
||||
@ -1416,6 +1416,51 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
|
||||
seg.segName(),
|
||||
});
|
||||
}
|
||||
|
||||
for (self.globals.items) |global| {
|
||||
const sym = self.getSymbolPtr(global);
|
||||
if (sym.n_desc != N_BOUNDARY) continue;
|
||||
if (self.getSectionBoundarySymbol(global)) |bsym| {
|
||||
const sect_id = self.getSectionByName(bsym.segname, bsym.sectname) orelse {
|
||||
try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "section not found: {s},{s}", .{
|
||||
bsym.segname, bsym.sectname,
|
||||
});
|
||||
continue;
|
||||
};
|
||||
const sect = self.sections.items(.header)[sect_id];
|
||||
sym.n_sect = sect_id + 1;
|
||||
sym.n_value = switch (bsym.kind) {
|
||||
.start => sect.addr,
|
||||
.stop => sect.addr + sect.size,
|
||||
};
|
||||
|
||||
log.debug("allocating {s} at @0x{x} sect({d})", .{
|
||||
self.getSymbolName(global),
|
||||
sym.n_value,
|
||||
sym.n_sect,
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
if (self.getSegmentBoundarySymbol(global)) |bsym| {
|
||||
const seg_id = self.getSegmentByName(bsym.segname) orelse {
|
||||
try self.reportUnresolvedBoundarySymbol(self.getSymbolName(global), "segment not found: {s}", .{
|
||||
bsym.segname,
|
||||
});
|
||||
|
||||
continue;
|
||||
};
|
||||
const seg = self.segments.items[seg_id];
|
||||
sym.n_value = switch (bsym.kind) {
|
||||
.start => seg.vmaddr,
|
||||
.stop => seg.vmaddr + seg.vmsize,
|
||||
};
|
||||
|
||||
log.debug("allocating {s} at @0x{x} ", .{ self.getSymbolName(global), sym.n_value });
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CreateAtomOpts = struct {
|
||||
@ -1442,6 +1487,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {
|
||||
const sym = self.getSymbolPtr(global);
|
||||
if (!sym.tentative()) continue;
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
|
||||
log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
|
||||
global.sym_index, self.getSymbolName(global), global.file,
|
||||
@ -1630,6 +1676,13 @@ pub fn resolveSymbols(self: *MachO) !void {
|
||||
try self.createMhExecuteHeaderSymbol();
|
||||
try self.createDsoHandleSymbol();
|
||||
try self.resolveSymbolsAtLoading();
|
||||
|
||||
// Final stop, check if unresolved contain any of the special magic boundary symbols
|
||||
// * section$start$
|
||||
// * section$stop$
|
||||
// * segment$start$
|
||||
// * segment$stop$
|
||||
try self.resolveBoundarySymbols();
|
||||
}
|
||||
|
||||
fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
|
||||
@ -1845,6 +1898,34 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn resolveBoundarySymbols(self: *MachO) !void {
|
||||
var next_sym: usize = 0;
|
||||
while (next_sym < self.unresolved.count()) {
|
||||
const global_index = self.unresolved.keys()[next_sym];
|
||||
const global = &self.globals.items[global_index];
|
||||
|
||||
if (self.getSectionBoundarySymbol(global.*) != null or self.getSegmentBoundarySymbol(global.*) != null) {
|
||||
const sym_index = try self.allocateSymbol();
|
||||
const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
|
||||
const sym = self.getSymbolPtr(sym_loc);
|
||||
sym.* = .{
|
||||
.n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(global.*)),
|
||||
.n_type = macho.N_SECT | macho.N_EXT,
|
||||
.n_sect = 0,
|
||||
.n_desc = N_BOUNDARY,
|
||||
.n_value = 0,
|
||||
};
|
||||
if (global.getFile()) |file| {
|
||||
const global_object = &self.objects.items[file];
|
||||
global_object.globals_lookup[global.sym_index] = global_index;
|
||||
}
|
||||
global.* = sym_loc;
|
||||
_ = self.unresolved.swapRemove(global_index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *MachO) void {
|
||||
const gpa = self.base.allocator;
|
||||
|
||||
@ -3565,6 +3646,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = self.getSymbol(atom.getSymbolWithLoc());
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
|
||||
const sect_id = sym.n_sect - 1;
|
||||
const section = self.sections.items(.header)[sect_id];
|
||||
@ -3719,6 +3801,7 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = self.getSymbol(atom.getSymbolWithLoc());
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
|
||||
const sect_id = sym.n_sect - 1;
|
||||
const section = self.sections.items(.header)[sect_id];
|
||||
@ -3819,6 +3902,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
|
||||
if (sym.undf()) continue;
|
||||
assert(sym.ext());
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
|
||||
const sym_name = self.getSymbolName(global);
|
||||
log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
|
||||
@ -3953,7 +4037,8 @@ const asc_u64 = std.sort.asc(u64);
|
||||
fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
|
||||
const sym = self.getSymbol(sym_loc);
|
||||
if (sym.n_strx == 0) return;
|
||||
if (sym.n_desc == MachO.N_DEAD) return;
|
||||
if (sym.n_desc == N_DEAD) return;
|
||||
if (sym.n_desc == N_BOUNDARY) return;
|
||||
if (self.symbolIsTemp(sym_loc)) return;
|
||||
try addresses.append(sym.n_value);
|
||||
}
|
||||
@ -4061,7 +4146,8 @@ pub fn writeDataInCode(self: *MachO) !void {
|
||||
for (object.exec_atoms.items) |atom_index| {
|
||||
const atom = self.getAtom(atom_index);
|
||||
const sym = self.getSymbol(atom.getSymbolWithLoc());
|
||||
if (sym.n_desc == MachO.N_DEAD) continue;
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) return;
|
||||
|
||||
const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
|
||||
source_sym.n_value
|
||||
@ -4119,7 +4205,8 @@ fn writeSymtabs(self: *MachO) !void {
|
||||
fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
|
||||
const sym = self.getSymbol(sym_loc);
|
||||
if (sym.n_strx == 0) return; // no name, skip
|
||||
if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip
|
||||
if (sym.n_desc == N_DEAD) return; // garbage-collected, skip
|
||||
if (sym.n_desc == N_BOUNDARY) return; // boundary symbol, skip
|
||||
if (sym.ext()) return; // an export lands in its own symtab section, skip
|
||||
if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
|
||||
var out_sym = sym;
|
||||
@ -4157,6 +4244,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
|
||||
const sym = self.getSymbol(global);
|
||||
if (sym.undf()) continue; // import, skip
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
var out_sym = sym;
|
||||
out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
|
||||
try exports.append(out_sym);
|
||||
@ -4172,6 +4260,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
|
||||
if (sym.n_strx == 0) continue; // no name, skip
|
||||
if (!sym.undf()) continue; // not an import, skip
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
const new_index = @as(u32, @intCast(imports.items.len));
|
||||
var out_sym = sym;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
const BoundarySymbolKind = enum {
|
||||
start,
|
||||
stop,
|
||||
};
|
||||
|
||||
const SectionBoundarySymbol = struct {
|
||||
kind: BoundarySymbolKind,
|
||||
segname: []const u8,
|
||||
sectname: []const u8,
|
||||
};
|
||||
|
||||
pub fn getSectionBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SectionBoundarySymbol {
|
||||
const sym_name = self.getSymbolName(sym_with_loc);
|
||||
if (mem.startsWith(u8, sym_name, "section$")) {
|
||||
const trailing = sym_name["section$".len..];
|
||||
const kind: BoundarySymbolKind = kind: {
|
||||
if (mem.startsWith(u8, trailing, "start$")) break :kind .start;
|
||||
if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop;
|
||||
return null;
|
||||
};
|
||||
const names = trailing[@tagName(kind).len + 1 ..];
|
||||
const sep_idx = mem.indexOf(u8, names, "$") orelse return null;
|
||||
const segname = names[0..sep_idx];
|
||||
const sectname = names[sep_idx + 1 ..];
|
||||
return .{ .kind = kind, .segname = segname, .sectname = sectname };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const SegmentBoundarySymbol = struct {
|
||||
kind: BoundarySymbolKind,
|
||||
segname: []const u8,
|
||||
};
|
||||
|
||||
pub fn getSegmentBoundarySymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) ?SegmentBoundarySymbol {
|
||||
const sym_name = self.getSymbolName(sym_with_loc);
|
||||
if (mem.startsWith(u8, sym_name, "segment$")) {
|
||||
const trailing = sym_name["segment$".len..];
|
||||
const kind: BoundarySymbolKind = kind: {
|
||||
if (mem.startsWith(u8, trailing, "start$")) break :kind .start;
|
||||
if (mem.startsWith(u8, trailing, "stop$")) break :kind .stop;
|
||||
return null;
|
||||
};
|
||||
const segname = trailing[@tagName(kind).len + 1 ..];
|
||||
return .{ .kind = kind, .segname = segname };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns pointer to the global entry for `name` if one exists.
|
||||
pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc {
|
||||
const global_index = self.resolver.get(name) orelse return null;
|
||||
@ -5137,6 +5275,23 @@ pub fn reportParseError(
|
||||
});
|
||||
}
|
||||
|
||||
pub fn reportUnresolvedBoundarySymbol(
|
||||
self: *MachO,
|
||||
sym_name: []const u8,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) error{OutOfMemory}!void {
|
||||
const gpa = self.base.allocator;
|
||||
try self.misc_errors.ensureUnusedCapacity(gpa, 1);
|
||||
var notes = try gpa.alloc(File.ErrorMsg, 1);
|
||||
errdefer gpa.free(notes);
|
||||
notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while resolving {s}", .{sym_name}) };
|
||||
self.misc_errors.appendAssumeCapacity(.{
|
||||
.msg = try std.fmt.allocPrint(gpa, format, args),
|
||||
.notes = notes,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
|
||||
const gpa = self.base.allocator;
|
||||
const count = self.unresolved.count();
|
||||
@ -5340,7 +5495,8 @@ pub fn logSymtab(self: *MachO) void {
|
||||
for (self.globals.items, 0..) |global, i| {
|
||||
const sym = self.getSymbol(global);
|
||||
if (sym.undf()) continue;
|
||||
if (sym.n_desc == MachO.N_DEAD) continue;
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
|
||||
i,
|
||||
self.getSymbolName(global),
|
||||
@ -5355,7 +5511,8 @@ pub fn logSymtab(self: *MachO) void {
|
||||
for (self.globals.items, 0..) |global, i| {
|
||||
const sym = self.getSymbol(global);
|
||||
if (!sym.undf()) continue;
|
||||
if (sym.n_desc == MachO.N_DEAD) continue;
|
||||
if (sym.n_desc == N_DEAD) continue;
|
||||
if (sym.n_desc == N_BOUNDARY) continue;
|
||||
const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
|
||||
scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
|
||||
i,
|
||||
@ -5466,6 +5623,7 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
|
||||
|
||||
pub const base_tag: File.Tag = File.Tag.macho;
|
||||
pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
|
||||
pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));
|
||||
|
||||
/// Mode of operation of the linker.
|
||||
pub const Mode = enum {
|
||||
|
||||
@ -50,6 +50,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
|
||||
for (macho_file.globals.items) |global| {
|
||||
const sym = macho_file.getSymbol(global);
|
||||
if (sym.undf()) continue;
|
||||
if (sym.n_desc == MachO.N_BOUNDARY) continue;
|
||||
|
||||
if (global.getFile()) |file| {
|
||||
try addRoot(macho_file, roots, file, global);
|
||||
|
||||
@ -4,6 +4,11 @@ pub const Case = struct {
|
||||
};
|
||||
|
||||
pub const cases = [_]Case{
|
||||
.{
|
||||
.build_root = "test/link",
|
||||
.import = @import("link/link.zig"),
|
||||
},
|
||||
|
||||
.{
|
||||
.build_root = "test/link/bss",
|
||||
.import = @import("link/bss/build.zig"),
|
||||
@ -29,12 +34,6 @@ pub const cases = [_]Case{
|
||||
.import = @import("link/glibc_compat/build.zig"),
|
||||
},
|
||||
|
||||
// Elf Cases
|
||||
.{
|
||||
.build_root = "test/link",
|
||||
.import = @import("link/elf.zig"),
|
||||
},
|
||||
|
||||
// WASM Cases
|
||||
// https://github.com/ziglang/zig/issues/16938
|
||||
//.{
|
||||
|
||||
@ -2,9 +2,8 @@
|
||||
//! Currently, we support linking x86_64 Linux, but in the future we
|
||||
//! will progressively relax those to exercise more combinations.
|
||||
|
||||
pub fn build(b: *Build) void {
|
||||
pub fn testAll(b: *Build) *Step {
|
||||
const elf_step = b.step("test-elf", "Run ELF tests");
|
||||
b.default_step = elf_step;
|
||||
|
||||
const default_target = CrossTarget{
|
||||
.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 {
|
||||
elf_step.dependOn(testZNow(b, .{ .target = glibc_target }));
|
||||
elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target }));
|
||||
elf_step.dependOn(testZText(b, .{ .target = glibc_target }));
|
||||
|
||||
return elf_step;
|
||||
}
|
||||
|
||||
fn testAbsSymbols(b: *Build, opts: Options) *Step {
|
||||
@ -3479,110 +3480,25 @@ fn testZText(b: *Build, opts: Options) *Step {
|
||||
return test_step;
|
||||
}
|
||||
|
||||
const Options = struct {
|
||||
target: CrossTarget = .{ .cpu_arch = .x86_64, .os_tag = .linux },
|
||||
optimize: std.builtin.OptimizeMode = .Debug,
|
||||
use_llvm: bool = true,
|
||||
use_lld: bool = false,
|
||||
};
|
||||
|
||||
fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
|
||||
const target = opts.target.zigTriple(b.allocator) catch @panic("OOM");
|
||||
const optimize = @tagName(opts.optimize);
|
||||
const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";
|
||||
const name = std.fmt.allocPrint(b.allocator, "test-elf-" ++ prefix ++ "-{s}-{s}-{s}", .{
|
||||
target,
|
||||
optimize,
|
||||
use_llvm,
|
||||
}) catch @panic("OOM");
|
||||
return b.step(name, "");
|
||||
}
|
||||
|
||||
fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addExecutable(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addObject(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addStaticLibrary(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addSharedLibrary(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
fn addRunArtifact(comp: *Compile) *Run {
|
||||
const b = comp.step.owner;
|
||||
const run = b.addRunArtifact(comp);
|
||||
run.skip_foreign_checks = true;
|
||||
return run;
|
||||
}
|
||||
|
||||
fn addZigSourceBytes(comp: *Compile, bytes: []const u8) void {
|
||||
const b = comp.step.owner;
|
||||
const file = WriteFile.create(b).add("a.zig", bytes);
|
||||
file.addStepDependencies(&comp.step);
|
||||
comp.root_src = file;
|
||||
}
|
||||
|
||||
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.addCSourceFile(.{ .file = file, .flags = flags });
|
||||
}
|
||||
|
||||
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.addCSourceFile(.{ .file = file, .flags = flags });
|
||||
}
|
||||
|
||||
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.addAssemblyFile(file);
|
||||
}
|
||||
|
||||
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);
|
||||
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 addZigSourceBytes = link.addZigSourceBytes;
|
||||
const expectLinkErrors = link.expectLinkErrors;
|
||||
const link = @import("link.zig");
|
||||
const std = @import("std");
|
||||
|
||||
const Build = std.Build;
|
||||
const Compile = Step.Compile;
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
const LazyPath = Build.LazyPath;
|
||||
const Run = Step.Run;
|
||||
const Options = link.Options;
|
||||
const Step = Build.Step;
|
||||
const WriteFile = Step.WriteFile;
|
||||
|
||||
114
test/link/link.zig
Normal file
114
test/link/link.zig
Normal file
@ -0,0 +1,114 @@
|
||||
pub fn build(b: *Build) void {
|
||||
const test_step = b.step("test-link", "Run link tests");
|
||||
b.default_step = test_step;
|
||||
|
||||
test_step.dependOn(@import("elf.zig").testAll(b));
|
||||
test_step.dependOn(@import("macho.zig").testAll(b));
|
||||
}
|
||||
|
||||
pub const Options = struct {
|
||||
target: CrossTarget,
|
||||
optimize: std.builtin.OptimizeMode = .Debug,
|
||||
use_llvm: bool = true,
|
||||
use_lld: bool = false,
|
||||
};
|
||||
|
||||
pub fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
|
||||
const target = opts.target.zigTriple(b.allocator) catch @panic("OOM");
|
||||
const optimize = @tagName(opts.optimize);
|
||||
const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm";
|
||||
const name = std.fmt.allocPrint(b.allocator, "test-" ++ prefix ++ "-{s}-{s}-{s}", .{
|
||||
target,
|
||||
optimize,
|
||||
use_llvm,
|
||||
}) catch @panic("OOM");
|
||||
return b.step(name, "");
|
||||
}
|
||||
|
||||
pub fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addExecutable(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn addObject(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addObject(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addStaticLibrary(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile {
|
||||
return b.addSharedLibrary(.{
|
||||
.name = name,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.use_llvm = opts.use_llvm,
|
||||
.use_lld = opts.use_lld,
|
||||
});
|
||||
}
|
||||
|
||||
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 addZigSourceBytes(comp: *Compile, bytes: []const u8) void {
|
||||
const b = comp.step.owner;
|
||||
const file = WriteFile.create(b).add("a.zig", bytes);
|
||||
file.addStepDependencies(&comp.step);
|
||||
comp.root_src = file;
|
||||
}
|
||||
|
||||
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.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.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.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 CrossTarget = std.zig.CrossTarget;
|
||||
const Run = Step.Run;
|
||||
const Step = Build.Step;
|
||||
const WriteFile = Step.WriteFile;
|
||||
96
test/link/macho.zig
Normal file
96
test/link/macho.zig
Normal file
@ -0,0 +1,96 @@
|
||||
//! Here we test our MachO linker for correctness and functionality.
|
||||
//! TODO migrate standalone tests from test/link/macho/* to here.
|
||||
|
||||
pub fn testAll(b: *Build) *Step {
|
||||
const macho_step = b.step("test-macho", "Run MachO tests");
|
||||
|
||||
const default_target = CrossTarget{ .os_tag = .macos };
|
||||
|
||||
macho_step.dependOn(testResolvingBoundarySymbols(b, .{ .target = default_target }));
|
||||
|
||||
return macho_step;
|
||||
}
|
||||
|
||||
fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step {
|
||||
const test_step = addTestStep(b, "macho-resolving-boundary-symbols", opts);
|
||||
|
||||
const obj1 = addObject(b, "obj1", opts);
|
||||
addCppSourceBytes(obj1,
|
||||
\\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase";
|
||||
, &.{});
|
||||
|
||||
const main_o = addObject(b, "main", opts);
|
||||
addZigSourceBytes(main_o,
|
||||
\\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", .{
|
||||
\\ std.mem.span(interop()),
|
||||
\\ });
|
||||
\\}
|
||||
);
|
||||
|
||||
{
|
||||
const obj2 = addObject(b, "obj2", opts);
|
||||
addCppSourceBytes(obj2,
|
||||
\\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr");
|
||||
\\extern "C" const char* interop() {
|
||||
\\ return message_pointer;
|
||||
\\}
|
||||
, &.{});
|
||||
|
||||
const exe = addExecutable(b, "test", opts);
|
||||
exe.addObject(obj1);
|
||||
exe.addObject(obj2);
|
||||
exe.addObject(main_o);
|
||||
|
||||
const run = addRunArtifact(exe);
|
||||
run.expectStdErrEqual("All your codebase are belong to us.\n");
|
||||
test_step.dependOn(&run.step);
|
||||
|
||||
const check = exe.checkObject();
|
||||
check.checkInSymtab();
|
||||
check.checkNotPresent("section$start$__DATA_CONST$__message_ptr");
|
||||
test_step.dependOn(&check.step);
|
||||
}
|
||||
|
||||
{
|
||||
const obj3 = addObject(b, "obj3", opts);
|
||||
addCppSourceBytes(obj3,
|
||||
\\extern const char* message_pointer __asm("section$start$__DATA$__message_ptr");
|
||||
\\extern "C" const char* interop() {
|
||||
\\ return message_pointer;
|
||||
\\}
|
||||
, &.{});
|
||||
|
||||
const exe = addExecutable(b, "test", opts);
|
||||
exe.addObject(obj1);
|
||||
exe.addObject(obj3);
|
||||
exe.addObject(main_o);
|
||||
|
||||
expectLinkErrors(exe, test_step, .{ .exact = &.{
|
||||
"section not found: __DATA,__message_ptr",
|
||||
"note: while resolving section$start$__DATA$__message_ptr",
|
||||
} });
|
||||
}
|
||||
|
||||
return test_step;
|
||||
}
|
||||
|
||||
fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step {
|
||||
return link.addTestStep(b, "macho-" ++ prefix, opts);
|
||||
}
|
||||
|
||||
const addCppSourceBytes = link.addCppSourceBytes;
|
||||
const addExecutable = link.addExecutable;
|
||||
const addObject = link.addObject;
|
||||
const addRunArtifact = link.addRunArtifact;
|
||||
const addZigSourceBytes = link.addZigSourceBytes;
|
||||
const expectLinkErrors = link.expectLinkErrors;
|
||||
const link = @import("link.zig");
|
||||
const std = @import("std");
|
||||
|
||||
const Build = std.Build;
|
||||
const CrossTarget = std.zig.CrossTarget;
|
||||
const Options = link.Options;
|
||||
const Step = Build.Step;
|
||||
Loading…
x
Reference in New Issue
Block a user