| author | |
| committer | |
| log | 9ad03b628f5d4770f9f26e646019292b5ae9cf9b |
| tree | 40ed7ff9e5b9e895ab686e19df6f943732961932 |
| parent | ec934c6d3294f1febe69453266556a82f64b59f0 |
| parent | 17177727c03d09303093c39c741a850abcc724f3 |
| signature |
macho: handle special section/segment boundary symbols6 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 { |
| 251 | 251 | .incremental, |
| 252 | 252 | }; |
| 253 | 253 | |
| 254 | if (options.use_llvm) { | |
| 254 | if (options.use_llvm and options.module != null) { | |
| 255 | 255 | self.llvm_object = try LlvmObject.create(gpa, options); |
| 256 | 256 | } |
| 257 | 257 | |
| ... | ... | @@ -1416,6 +1416,51 @@ pub fn allocateSpecialSymbols(self: *MachO) !void { |
| 1416 | 1416 | seg.segName(), |
| 1417 | 1417 | }); |
| 1418 | 1418 | } |
| 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 | } | |
| 1419 | 1464 | } |
| 1420 | 1465 | |
| 1421 | 1466 | const CreateAtomOpts = struct { |
| ... | ... | @@ -1442,6 +1487,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void { |
| 1442 | 1487 | const sym = self.getSymbolPtr(global); |
| 1443 | 1488 | if (!sym.tentative()) continue; |
| 1444 | 1489 | if (sym.n_desc == N_DEAD) continue; |
| 1490 | if (sym.n_desc == N_BOUNDARY) continue; | |
| 1445 | 1491 | |
| 1446 | 1492 | log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{ |
| 1447 | 1493 | global.sym_index, self.getSymbolName(global), global.file, |
| ... | ... | @@ -1630,6 +1676,13 @@ pub fn resolveSymbols(self: *MachO) !void { |
| 1630 | 1676 | try self.createMhExecuteHeaderSymbol(); |
| 1631 | 1677 | try self.createDsoHandleSymbol(); |
| 1632 | 1678 | 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(); | |
| 1633 | 1686 | } |
| 1634 | 1687 | |
| 1635 | 1688 | fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void { |
| ... | ... | @@ -1845,6 +1898,34 @@ fn resolveSymbolsAtLoading(self: *MachO) !void { |
| 1845 | 1898 | } |
| 1846 | 1899 | } |
| 1847 | 1900 | |
| 1901 | fn 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 | ||
| 1848 | 1929 | pub fn deinit(self: *MachO) void { |
| 1849 | 1930 | const gpa = self.base.allocator; |
| 1850 | 1931 | |
| ... | ... | @@ -3565,6 +3646,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { |
| 3565 | 3646 | const atom = self.getAtom(atom_index); |
| 3566 | 3647 | const sym = self.getSymbol(atom.getSymbolWithLoc()); |
| 3567 | 3648 | if (sym.n_desc == N_DEAD) continue; |
| 3649 | if (sym.n_desc == N_BOUNDARY) continue; | |
| 3568 | 3650 | |
| 3569 | 3651 | const sect_id = sym.n_sect - 1; |
| 3570 | 3652 | const section = self.sections.items(.header)[sect_id]; |
| ... | ... | @@ -3719,6 +3801,7 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { |
| 3719 | 3801 | const atom = self.getAtom(atom_index); |
| 3720 | 3802 | const sym = self.getSymbol(atom.getSymbolWithLoc()); |
| 3721 | 3803 | if (sym.n_desc == N_DEAD) continue; |
| 3804 | if (sym.n_desc == N_BOUNDARY) continue; | |
| 3722 | 3805 | |
| 3723 | 3806 | const sect_id = sym.n_sect - 1; |
| 3724 | 3807 | const section = self.sections.items(.header)[sect_id]; |
| ... | ... | @@ -3819,6 +3902,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void { |
| 3819 | 3902 | if (sym.undf()) continue; |
| 3820 | 3903 | assert(sym.ext()); |
| 3821 | 3904 | if (sym.n_desc == N_DEAD) continue; |
| 3905 | if (sym.n_desc == N_BOUNDARY) continue; | |
| 3822 | 3906 | |
| 3823 | 3907 | const sym_name = self.getSymbolName(global); |
| 3824 | 3908 | log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); |
| ... | ... | @@ -3953,7 +4037,8 @@ const asc_u64 = std.sort.asc(u64); |
| 3953 | 4037 | fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void { |
| 3954 | 4038 | const sym = self.getSymbol(sym_loc); |
| 3955 | 4039 | 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; | |
| 3957 | 4042 | if (self.symbolIsTemp(sym_loc)) return; |
| 3958 | 4043 | try addresses.append(sym.n_value); |
| 3959 | 4044 | } |
| ... | ... | @@ -4061,7 +4146,8 @@ pub fn writeDataInCode(self: *MachO) !void { |
| 4061 | 4146 | for (object.exec_atoms.items) |atom_index| { |
| 4062 | 4147 | const atom = self.getAtom(atom_index); |
| 4063 | 4148 | 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; | |
| 4065 | 4151 | |
| 4066 | 4152 | const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym| |
| 4067 | 4153 | source_sym.n_value |
| ... | ... | @@ -4119,7 +4205,8 @@ fn writeSymtabs(self: *MachO) !void { |
| 4119 | 4205 | fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void { |
| 4120 | 4206 | const sym = self.getSymbol(sym_loc); |
| 4121 | 4207 | 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 | |
| 4123 | 4210 | if (sym.ext()) return; // an export lands in its own symtab section, skip |
| 4124 | 4211 | if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip |
| 4125 | 4212 | var out_sym = sym; |
| ... | ... | @@ -4157,6 +4244,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 4157 | 4244 | const sym = self.getSymbol(global); |
| 4158 | 4245 | if (sym.undf()) continue; // import, skip |
| 4159 | 4246 | if (sym.n_desc == N_DEAD) continue; |
| 4247 | if (sym.n_desc == N_BOUNDARY) continue; | |
| 4160 | 4248 | var out_sym = sym; |
| 4161 | 4249 | out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global)); |
| 4162 | 4250 | try exports.append(out_sym); |
| ... | ... | @@ -4172,6 +4260,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { |
| 4172 | 4260 | if (sym.n_strx == 0) continue; // no name, skip |
| 4173 | 4261 | if (!sym.undf()) continue; // not an import, skip |
| 4174 | 4262 | if (sym.n_desc == N_DEAD) continue; |
| 4263 | if (sym.n_desc == N_BOUNDARY) continue; | |
| 4175 | 4264 | const new_index = @as(u32, @intCast(imports.items.len)); |
| 4176 | 4265 | var out_sym = sym; |
| 4177 | 4266 | 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 |
| 4842 | 4931 | } |
| 4843 | 4932 | } |
| 4844 | 4933 | |
| 4934 | const BoundarySymbolKind = enum { | |
| 4935 | start, | |
| 4936 | stop, | |
| 4937 | }; | |
| 4938 | ||
| 4939 | const SectionBoundarySymbol = struct { | |
| 4940 | kind: BoundarySymbolKind, | |
| 4941 | segname: []const u8, | |
| 4942 | sectname: []const u8, | |
| 4943 | }; | |
| 4944 | ||
| 4945 | pub 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 | ||
| 4963 | const SegmentBoundarySymbol = struct { | |
| 4964 | kind: BoundarySymbolKind, | |
| 4965 | segname: []const u8, | |
| 4966 | }; | |
| 4967 | ||
| 4968 | pub 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 | ||
| 4845 | 4983 | /// Returns pointer to the global entry for `name` if one exists. |
| 4846 | 4984 | pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc { |
| 4847 | 4985 | const global_index = self.resolver.get(name) orelse return null; |
| ... | ... | @@ -5137,6 +5275,23 @@ pub fn reportParseError( |
| 5137 | 5275 | }); |
| 5138 | 5276 | } |
| 5139 | 5277 | |
| 5278 | pub 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 | ||
| 5140 | 5295 | pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { |
| 5141 | 5296 | const gpa = self.base.allocator; |
| 5142 | 5297 | const count = self.unresolved.count(); |
| ... | ... | @@ -5340,7 +5495,8 @@ pub fn logSymtab(self: *MachO) void { |
| 5340 | 5495 | for (self.globals.items, 0..) |global, i| { |
| 5341 | 5496 | const sym = self.getSymbol(global); |
| 5342 | 5497 | 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; | |
| 5344 | 5500 | scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{ |
| 5345 | 5501 | i, |
| 5346 | 5502 | self.getSymbolName(global), |
| ... | ... | @@ -5355,7 +5511,8 @@ pub fn logSymtab(self: *MachO) void { |
| 5355 | 5511 | for (self.globals.items, 0..) |global, i| { |
| 5356 | 5512 | const sym = self.getSymbol(global); |
| 5357 | 5513 | 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; | |
| 5359 | 5516 | const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER); |
| 5360 | 5517 | scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{ |
| 5361 | 5518 | i, |
| ... | ... | @@ -5466,6 +5623,7 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void { |
| 5466 | 5623 | |
| 5467 | 5624 | pub const base_tag: File.Tag = File.Tag.macho; |
| 5468 | 5625 | pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1))); |
| 5626 | pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2))); | |
| 5469 | 5627 | |
| 5470 | 5628 | /// Mode of operation of the linker. |
| 5471 | 5629 | pub const Mode = enum { |
src/link/MachO/dead_strip.zig+1| ... | ... | @@ -50,6 +50,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void { |
| 50 | 50 | for (macho_file.globals.items) |global| { |
| 51 | 51 | const sym = macho_file.getSymbol(global); |
| 52 | 52 | if (sym.undf()) continue; |
| 53 | if (sym.n_desc == MachO.N_BOUNDARY) continue; | |
| 53 | 54 | |
| 54 | 55 | if (global.getFile()) |file| { |
| 55 | 56 | try addRoot(macho_file, roots, file, global); |
test/link.zig+5-6| ... | ... | @@ -4,6 +4,11 @@ pub const Case = struct { |
| 4 | 4 | }; |
| 5 | 5 | |
| 6 | 6 | pub const cases = [_]Case{ |
| 7 | .{ | |
| 8 | .build_root = "test/link", | |
| 9 | .import = @import("link/link.zig"), | |
| 10 | }, | |
| 11 | ||
| 7 | 12 | .{ |
| 8 | 13 | .build_root = "test/link/bss", |
| 9 | 14 | .import = @import("link/bss/build.zig"), |
| ... | ... | @@ -29,12 +34,6 @@ pub const cases = [_]Case{ |
| 29 | 34 | .import = @import("link/glibc_compat/build.zig"), |
| 30 | 35 | }, |
| 31 | 36 | |
| 32 | // Elf Cases | |
| 33 | .{ | |
| 34 | .build_root = "test/link", | |
| 35 | .import = @import("link/elf.zig"), | |
| 36 | }, | |
| 37 | ||
| 38 | 37 | // WASM Cases |
| 39 | 38 | // https://github.com/ziglang/zig/issues/16938 |
| 40 | 39 | //.{ |
test/link/elf.zig+18-102| ... | ... | @@ -2,9 +2,8 @@ |
| 2 | 2 | //! Currently, we support linking x86_64 Linux, but in the future we |
| 3 | 3 | //! will progressively relax those to exercise more combinations. |
| 4 | 4 | |
| 5 | pub fn build(b: *Build) void { | |
| 5 | pub fn testAll(b: *Build) *Step { | |
| 6 | 6 | const elf_step = b.step("test-elf", "Run ELF tests"); |
| 7 | b.default_step = elf_step; | |
| 8 | 7 | |
| 9 | 8 | const default_target = CrossTarget{ |
| 10 | 9 | .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 { |
| 123 | 122 | elf_step.dependOn(testZNow(b, .{ .target = glibc_target })); |
| 124 | 123 | elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target })); |
| 125 | 124 | elf_step.dependOn(testZText(b, .{ .target = glibc_target })); |
| 125 | ||
| 126 | return elf_step; | |
| 126 | 127 | } |
| 127 | 128 | |
| 128 | 129 | fn testAbsSymbols(b: *Build, opts: Options) *Step { |
| ... | ... | @@ -3479,110 +3480,25 @@ fn testZText(b: *Build, opts: Options) *Step { |
| 3479 | 3480 | return test_step; |
| 3480 | 3481 | } |
| 3481 | 3482 | |
| 3482 | const 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 | ||
| 3489 | 3483 | fn 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 | ||
| 3501 | fn 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 | ||
| 3511 | fn 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 | ||
| 3521 | fn 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 | ||
| 3531 | fn 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 | ||
| 3541 | fn 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 | ||
| 3548 | fn 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 | ||
| 3555 | fn 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 | ||
| 3561 | fn 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 | ||
| 3567 | fn 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 | ||
| 3574 | fn 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 | ||
| 3487 | const addAsmSourceBytes = link.addAsmSourceBytes; | |
| 3488 | const addCSourceBytes = link.addCSourceBytes; | |
| 3489 | const addCppSourceBytes = link.addCppSourceBytes; | |
| 3490 | const addExecutable = link.addExecutable; | |
| 3491 | const addObject = link.addObject; | |
| 3492 | const addRunArtifact = link.addRunArtifact; | |
| 3493 | const addSharedLibrary = link.addSharedLibrary; | |
| 3494 | const addStaticLibrary = link.addStaticLibrary; | |
| 3495 | const addZigSourceBytes = link.addZigSourceBytes; | |
| 3496 | const expectLinkErrors = link.expectLinkErrors; | |
| 3497 | const link = @import("link.zig"); | |
| 3580 | 3498 | const std = @import("std"); |
| 3581 | 3499 | |
| 3582 | 3500 | const Build = std.Build; |
| 3583 | const Compile = Step.Compile; | |
| 3584 | 3501 | const CrossTarget = std.zig.CrossTarget; |
| 3585 | const LazyPath = Build.LazyPath; | |
| 3586 | const Run = Step.Run; | |
| 3502 | const Options = link.Options; | |
| 3587 | 3503 | const Step = Build.Step; |
| 3588 | 3504 | const WriteFile = Step.WriteFile; |
test/link/link.zig created+114| ... | ... | @@ -0,0 +1,114 @@ |
| 1 | pub 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 | ||
| 9 | pub const Options = struct { | |
| 10 | target: CrossTarget, | |
| 11 | optimize: std.builtin.OptimizeMode = .Debug, | |
| 12 | use_llvm: bool = true, | |
| 13 | use_lld: bool = false, | |
| 14 | }; | |
| 15 | ||
| 16 | pub 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 | ||
| 28 | pub 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 | ||
| 38 | pub 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 | ||
| 48 | pub 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 | ||
| 58 | pub 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 | ||
| 68 | pub 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 | ||
| 75 | pub 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 | ||
| 82 | pub 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 | ||
| 88 | pub 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 | ||
| 94 | pub 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 | ||
| 101 | pub 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 | ||
| 107 | const std = @import("std"); | |
| 108 | ||
| 109 | const Build = std.Build; | |
| 110 | const Compile = Step.Compile; | |
| 111 | const CrossTarget = std.zig.CrossTarget; | |
| 112 | const Run = Step.Run; | |
| 113 | const Step = Build.Step; | |
| 114 | const 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 | ||
| 4 | pub 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 | ||
| 14 | fn 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 | ||
| 80 | fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { | |
| 81 | return link.addTestStep(b, "macho-" ++ prefix, opts); | |
| 82 | } | |
| 83 | ||
| 84 | const addCppSourceBytes = link.addCppSourceBytes; | |
| 85 | const addExecutable = link.addExecutable; | |
| 86 | const addObject = link.addObject; | |
| 87 | const addRunArtifact = link.addRunArtifact; | |
| 88 | const addZigSourceBytes = link.addZigSourceBytes; | |
| 89 | const expectLinkErrors = link.expectLinkErrors; | |
| 90 | const link = @import("link.zig"); | |
| 91 | const std = @import("std"); | |
| 92 | ||
| 93 | const Build = std.Build; | |
| 94 | const CrossTarget = std.zig.CrossTarget; | |
| 95 | const Options = link.Options; | |
| 96 | const Step = Build.Step; |