authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-11-10 12:52:52+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-11-10 12:52:56+01:00
log0f2489d8fc314c67bec7d77832cb6ba773f48738
treed70f526927583c469cce908851102714e1701ecc
parent03adafd8023691af6a1e3d784a6e7e1f77d46859

macho: resolve special section/segment boundary symbols

Boundary symbols have a special name prefix: * section$start$segname$sectname * section$stop$segname$sectname * segment$start$segname * segment$stop$segname and will resolve to either start or end of the respective section/segment if found. If not found, we return an error stating we couldn't find the requested section/segment rather than silently failing and resolving the address to 0 which seems to be the case with Apple's ld64.

2 files changed, 164 insertions(+), 5 deletions(-)

src/link/MachO.zig+163-5
...@@ -1416,6 +1416,51 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1416,6 +1416,51 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
1416 seg.segName(),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}
14201465
1421const CreateAtomOpts = struct {1466const CreateAtomOpts = struct {
...@@ -1442,6 +1487,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {...@@ -1442,6 +1487,7 @@ pub fn createTentativeDefAtoms(self: *MachO) !void {
1442 const sym = self.getSymbolPtr(global);1487 const sym = self.getSymbolPtr(global);
1443 if (!sym.tentative()) continue;1488 if (!sym.tentative()) continue;
1444 if (sym.n_desc == N_DEAD) continue;1489 if (sym.n_desc == N_DEAD) continue;
1490 if (sym.n_desc == N_BOUNDARY) continue;
14451491
1446 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{1492 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
1447 global.sym_index, self.getSymbolName(global), global.file,1493 global.sym_index, self.getSymbolName(global), global.file,
...@@ -1630,6 +1676,13 @@ pub fn resolveSymbols(self: *MachO) !void {...@@ -1630,6 +1676,13 @@ pub fn resolveSymbols(self: *MachO) !void {
1630 try self.createMhExecuteHeaderSymbol();1676 try self.createMhExecuteHeaderSymbol();
1631 try self.createDsoHandleSymbol();1677 try self.createDsoHandleSymbol();
1632 try self.resolveSymbolsAtLoading();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}
16341687
1635fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {1688fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
...@@ -1845,6 +1898,34 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {...@@ -1845,6 +1898,34 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {
1845 }1898 }
1846}1899}
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
1848pub fn deinit(self: *MachO) void {1929pub fn deinit(self: *MachO) void {
1849 const gpa = self.base.allocator;1930 const gpa = self.base.allocator;
18501931
...@@ -3565,6 +3646,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {...@@ -3565,6 +3646,7 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3565 const atom = self.getAtom(atom_index);3646 const atom = self.getAtom(atom_index);
3566 const sym = self.getSymbol(atom.getSymbolWithLoc());3647 const sym = self.getSymbol(atom.getSymbolWithLoc());
3567 if (sym.n_desc == N_DEAD) continue;3648 if (sym.n_desc == N_DEAD) continue;
3649 if (sym.n_desc == N_BOUNDARY) continue;
35683650
3569 const sect_id = sym.n_sect - 1;3651 const sect_id = sym.n_sect - 1;
3570 const section = self.sections.items(.header)[sect_id];3652 const section = self.sections.items(.header)[sect_id];
...@@ -3719,6 +3801,7 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {...@@ -3719,6 +3801,7 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3719 const atom = self.getAtom(atom_index);3801 const atom = self.getAtom(atom_index);
3720 const sym = self.getSymbol(atom.getSymbolWithLoc());3802 const sym = self.getSymbol(atom.getSymbolWithLoc());
3721 if (sym.n_desc == N_DEAD) continue;3803 if (sym.n_desc == N_DEAD) continue;
3804 if (sym.n_desc == N_BOUNDARY) continue;
37223805
3723 const sect_id = sym.n_sect - 1;3806 const sect_id = sym.n_sect - 1;
3724 const section = self.sections.items(.header)[sect_id];3807 const section = self.sections.items(.header)[sect_id];
...@@ -3819,6 +3902,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {...@@ -3819,6 +3902,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
3819 if (sym.undf()) continue;3902 if (sym.undf()) continue;
3820 assert(sym.ext());3903 assert(sym.ext());
3821 if (sym.n_desc == N_DEAD) continue;3904 if (sym.n_desc == N_DEAD) continue;
3905 if (sym.n_desc == N_BOUNDARY) continue;
38223906
3823 const sym_name = self.getSymbolName(global);3907 const sym_name = self.getSymbolName(global);
3824 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });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,7 +4037,8 @@ const asc_u64 = std.sort.asc(u64);
3953fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {4037fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
3954 const sym = self.getSymbol(sym_loc);4038 const sym = self.getSymbol(sym_loc);
3955 if (sym.n_strx == 0) return;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 if (self.symbolIsTemp(sym_loc)) return;4042 if (self.symbolIsTemp(sym_loc)) return;
3958 try addresses.append(sym.n_value);4043 try addresses.append(sym.n_value);
3959}4044}
...@@ -4061,7 +4146,8 @@ pub fn writeDataInCode(self: *MachO) !void {...@@ -4061,7 +4146,8 @@ pub fn writeDataInCode(self: *MachO) !void {
4061 for (object.exec_atoms.items) |atom_index| {4146 for (object.exec_atoms.items) |atom_index| {
4062 const atom = self.getAtom(atom_index);4147 const atom = self.getAtom(atom_index);
4063 const sym = self.getSymbol(atom.getSymbolWithLoc());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;
40654151
4066 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|4152 const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
4067 source_sym.n_value4153 source_sym.n_value
...@@ -4119,7 +4205,8 @@ fn writeSymtabs(self: *MachO) !void {...@@ -4119,7 +4205,8 @@ fn writeSymtabs(self: *MachO) !void {
4119fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {4205fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
4120 const sym = self.getSymbol(sym_loc);4206 const sym = self.getSymbol(sym_loc);
4121 if (sym.n_strx == 0) return; // no name, skip4207 if (sym.n_strx == 0) return; // no name, skip
4122 if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip4208 if (sym.n_desc == N_DEAD) return; // garbage-collected, skip
4209 if (sym.n_desc == N_BOUNDARY) return; // boundary symbol, skip
4123 if (sym.ext()) return; // an export lands in its own symtab section, skip4210 if (sym.ext()) return; // an export lands in its own symtab section, skip
4124 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip4211 if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
4125 var out_sym = sym;4212 var out_sym = sym;
...@@ -4157,6 +4244,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -4157,6 +4244,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
4157 const sym = self.getSymbol(global);4244 const sym = self.getSymbol(global);
4158 if (sym.undf()) continue; // import, skip4245 if (sym.undf()) continue; // import, skip
4159 if (sym.n_desc == N_DEAD) continue;4246 if (sym.n_desc == N_DEAD) continue;
4247 if (sym.n_desc == N_BOUNDARY) continue;
4160 var out_sym = sym;4248 var out_sym = sym;
4161 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));4249 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
4162 try exports.append(out_sym);4250 try exports.append(out_sym);
...@@ -4172,6 +4260,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {...@@ -4172,6 +4260,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
4172 if (sym.n_strx == 0) continue; // no name, skip4260 if (sym.n_strx == 0) continue; // no name, skip
4173 if (!sym.undf()) continue; // not an import, skip4261 if (!sym.undf()) continue; // not an import, skip
4174 if (sym.n_desc == N_DEAD) continue;4262 if (sym.n_desc == N_DEAD) continue;
4263 if (sym.n_desc == N_BOUNDARY) continue;
4175 const new_index = @as(u32, @intCast(imports.items.len));4264 const new_index = @as(u32, @intCast(imports.items.len));
4176 var out_sym = sym;4265 var out_sym = sym;
4177 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));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,6 +4931,55 @@ pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8
4842 }4931 }
4843}4932}
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
4845/// Returns pointer to the global entry for `name` if one exists.4983/// Returns pointer to the global entry for `name` if one exists.
4846pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc {4984pub fn getGlobalPtr(self: *MachO, name: []const u8) ?*SymbolWithLoc {
4847 const global_index = self.resolver.get(name) orelse return null;4985 const global_index = self.resolver.get(name) orelse return null;
...@@ -5137,6 +5275,23 @@ pub fn reportParseError(...@@ -5137,6 +5275,23 @@ pub fn reportParseError(
5137 });5275 });
5138}5276}
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
5140pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {5295pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void {
5141 const gpa = self.base.allocator;5296 const gpa = self.base.allocator;
5142 const count = self.unresolved.count();5297 const count = self.unresolved.count();
...@@ -5340,7 +5495,8 @@ pub fn logSymtab(self: *MachO) void {...@@ -5340,7 +5495,8 @@ pub fn logSymtab(self: *MachO) void {
5340 for (self.globals.items, 0..) |global, i| {5495 for (self.globals.items, 0..) |global, i| {
5341 const sym = self.getSymbol(global);5496 const sym = self.getSymbol(global);
5342 if (sym.undf()) continue;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 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{5500 scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
5345 i,5501 i,
5346 self.getSymbolName(global),5502 self.getSymbolName(global),
...@@ -5355,7 +5511,8 @@ pub fn logSymtab(self: *MachO) void {...@@ -5355,7 +5511,8 @@ pub fn logSymtab(self: *MachO) void {
5355 for (self.globals.items, 0..) |global, i| {5511 for (self.globals.items, 0..) |global, i| {
5356 const sym = self.getSymbol(global);5512 const sym = self.getSymbol(global);
5357 if (!sym.undf()) continue;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 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);5516 const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
5360 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{5517 scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
5361 i,5518 i,
...@@ -5466,6 +5623,7 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {...@@ -5466,6 +5623,7 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
54665623
5467pub const base_tag: File.Tag = File.Tag.macho;5624pub const base_tag: File.Tag = File.Tag.macho;
5468pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));5625pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1)));
5626pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2)));
54695627
5470/// Mode of operation of the linker.5628/// Mode of operation of the linker.
5471pub const Mode = enum {5629pub const Mode = enum {
src/link/MachO/dead_strip.zig+1
...@@ -50,6 +50,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {...@@ -50,6 +50,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
50 for (macho_file.globals.items) |global| {50 for (macho_file.globals.items) |global| {
51 const sym = macho_file.getSymbol(global);51 const sym = macho_file.getSymbol(global);
52 if (sym.undf()) continue;52 if (sym.undf()) continue;
53 if (sym.n_desc == MachO.N_BOUNDARY) continue;
5354
54 if (global.getFile()) |file| {55 if (global.getFile()) |file| {
55 try addRoot(macho_file, roots, file, global);56 try addRoot(macho_file, roots, file, global);