authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-26 09:40:01+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-04-13 10:56:03+02:00
log988b184d03fea3001b588ceb033de66a7d7ba21b
treeba8cff5b4fc72af2b4a22cf5c850741232e9d4c5
parent262e09c482d98a78531c049a18b7f24146fe157f

zld: redo symbol resolution in objects

Store only globals and undefs at the linker level, while all locals stay scoped to the actual object file they were defined in. This is fine since the relocations referencing locals will always be resolved first using the local symbol table before checking for the reference within the linker's global symbol table. This also paves the way for proper symbol resolution from within static and dynamic libraries.

6 files changed, 189 insertions(+), 330 deletions(-)

CMakeLists.txt+1
......@@ -569,6 +569,7 @@ set(ZIG_STAGE2_SOURCES
569569 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
570570 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
571571 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
572 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
572573 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
573574 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
574575 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
src/link/MachO.zig+4-1
......@@ -658,7 +658,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
658658
659659 if (use_zld) {
660660 var zld = Zld.init(self.base.allocator);
661 defer zld.deinit();
661 defer {
662 zld.closeFiles();
663 zld.deinit();
664 }
662665 zld.arch = target.cpu.arch;
663666
664667 var input_files = std.ArrayList([]const u8).init(self.base.allocator);
src/link/MachO/Archive.zig+2-27
......@@ -98,7 +98,6 @@ pub fn deinit(self: *Archive) void {
9898 entry.value.deinit(self.allocator);
9999 }
100100 self.toc.deinit(self.allocator);
101 self.file.close();
102101}
103102
104103/// Caller owns the returned Archive instance and is responsible for calling
......@@ -131,20 +130,12 @@ pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, ar_name: [
131130 .name = name,
132131 };
133132
134 var object_offsets = try self.readTableOfContents(reader);
135 defer self.allocator.free(object_offsets);
136
137 var i: usize = 1;
138 while (i < object_offsets.len) : (i += 1) {
139 const offset = object_offsets[i];
140 try reader.context.seekTo(offset);
141 try self.readObject(arch, ar_name, reader);
142 }
133 try self.parseTableOfContents(reader);
143134
144135 return self;
145136}
146137
147fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
138fn parseTableOfContents(self: *Archive, reader: anytype) !void {
148139 const symtab_size = try reader.readIntLittle(u32);
149140 var symtab = try self.allocator.alloc(u8, symtab_size);
150141 defer self.allocator.free(symtab);
......@@ -158,10 +149,6 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
158149 var symtab_stream = std.io.fixedBufferStream(symtab);
159150 var symtab_reader = symtab_stream.reader();
160151
161 var object_offsets = std.ArrayList(u32).init(self.allocator);
162 try object_offsets.append(0);
163 var last: usize = 0;
164
165152 while (true) {
166153 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
167154 error.EndOfStream => break,
......@@ -179,19 +166,7 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
179166 }
180167
181168 try res.entry.value.append(self.allocator, object_offset);
182
183 // TODO This will go once we properly use archive's TOC to pick
184 // an object which defines a missing symbol rather than pasting in
185 // all of the objects always.
186 // Here, we assume that symbols are NOT sorted in any way, and
187 // they point to objects in sequence.
188 if (object_offsets.items[last] != object_offset) {
189 try object_offsets.append(object_offset);
190 last += 1;
191 }
192169 }
193
194 return object_offsets.toOwnedSlice();
195170}
196171
197172fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void {
src/link/MachO/Object.zig+27-31
......@@ -9,6 +9,7 @@ const macho = std.macho;
99const mem = std.mem;
1010
1111const Allocator = mem.Allocator;
12const Symbol = @import("Symbol.zig");
1213const parseName = @import("Zld.zig").parseName;
1314
1415usingnamespace @import("commands.zig");
......@@ -36,7 +37,7 @@ dwarf_debug_str_index: ?u16 = null,
3637dwarf_debug_line_index: ?u16 = null,
3738dwarf_debug_ranges_index: ?u16 = null,
3839
39symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
40symtab: std.ArrayListUnmanaged(Symbol) = .{},
4041strtab: std.ArrayListUnmanaged(u8) = .{},
4142
4243data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
......@@ -53,7 +54,6 @@ pub fn deinit(self: *Object) void {
5354 if (self.ar_name) |v| {
5455 self.allocator.free(v);
5556 }
56 self.file.close();
5757}
5858
5959/// Caller owns the returned Object instance and is responsible for calling
......@@ -89,21 +89,9 @@ pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []co
8989 };
9090
9191 try self.readLoadCommands(reader, .{});
92
93 if (self.symtab_cmd_index != null) {
94 try self.readSymtab();
95 try self.readStrtab();
96 }
97
92 if (self.symtab_cmd_index != null) try self.parseSymtab();
9893 if (self.data_in_code_cmd_index != null) try self.readDataInCode();
9994
100 log.debug("\n\n", .{});
101 log.debug("{s} defines symbols", .{self.name});
102 for (self.symtab.items) |sym| {
103 const symname = self.getString(sym.n_strx);
104 log.debug("'{s}': {}", .{ symname, sym });
105 }
106
10795 return self;
10896}
10997
......@@ -174,25 +162,33 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi
174162 }
175163}
176164
177pub fn readSymtab(self: *Object) !void {
165pub fn parseSymtab(self: *Object) !void {
178166 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
179 var buffer = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
180 defer self.allocator.free(buffer);
181 _ = try self.file.preadAll(buffer, symtab_cmd.symoff);
167
168 var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
169 defer self.allocator.free(symtab);
170
171 _ = try self.file.preadAll(symtab, symtab_cmd.symoff);
182172 try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms);
183 // TODO this align case should not be needed.
184 // Probably a bug in stage1.
185 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, buffer));
186 self.symtab.appendSliceAssumeCapacity(slice);
187}
188173
189pub fn readStrtab(self: *Object) !void {
190 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;
191 var buffer = try self.allocator.alloc(u8, symtab_cmd.strsize);
192 defer self.allocator.free(buffer);
193 _ = try self.file.preadAll(buffer, symtab_cmd.stroff);
194 try self.strtab.ensureCapacity(self.allocator, symtab_cmd.strsize);
195 self.strtab.appendSliceAssumeCapacity(buffer);
174 var stream = std.io.fixedBufferStream(symtab);
175 var reader = stream.reader();
176
177 while (true) {
178 const symbol = reader.readStruct(macho.nlist_64) catch |err| switch (err) {
179 error.EndOfStream => break,
180 else => |e| return e,
181 };
182 self.symtab.appendAssumeCapacity(.{
183 .inner = symbol,
184 });
185 }
186
187 var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize);
188 defer self.allocator.free(strtab);
189
190 _ = try self.file.preadAll(strtab, symtab_cmd.stroff);
191 try self.strtab.appendSlice(self.allocator, strtab);
196192}
197193
198194pub fn getString(self: *const Object, str_off: u32) []const u8 {
src/link/MachO/Symbol.zig created+55
......@@ -0,0 +1,55 @@
1const Symbol = @This();
2
3const std = @import("std");
4const macho = std.macho;
5
6/// MachO representation of this symbol.
7inner: macho.nlist_64,
8
9/// Index of file where to locate this symbol.
10/// Depending on context, this is either an object file, or a dylib.
11file: ?u16 = null,
12
13/// Index of this symbol within the file's symbol table.
14index: ?u32 = null,
15
16pub fn isStab(self: Symbol) bool {
17 return (macho.N_STAB & self.inner.n_type) != 0;
18}
19
20pub fn isPext(self: Symbol) bool {
21 return (macho.N_PEXT & self.inner.n_type) != 0;
22}
23
24pub fn isExt(self: Symbol) bool {
25 return (macho.N_EXT & self.inner.n_type) != 0;
26}
27
28pub fn isSect(self: Symbol) bool {
29 const type_ = macho.N_TYPE & self.inner.n_type;
30 return type_ == macho.N_SECT;
31}
32
33pub fn isUndf(self: Symbol) bool {
34 const type_ = macho.N_TYPE & self.inner.n_type;
35 return type_ == macho.N_UNDF;
36}
37
38pub fn isWeakDef(self: Symbol) bool {
39 return self.inner.n_desc == macho.N_WEAK_DEF;
40}
41
42/// Symbol is local if it is either a stab or it is defined and not an extern.
43pub fn isLocal(self: Symbol) bool {
44 return self.isStab() or (self.isSect() and !self.isExt());
45}
46
47/// Symbol is global if it is defined and an extern.
48pub fn isGlobal(self: Symbol) bool {
49 return self.isSect() and self.isExt();
50}
51
52/// Symbol is undefined if it is not defined and an extern.
53pub fn isUndef(self: Symbol) bool {
54 return self.isUndf() and self.isExt();
55}
src/link/MachO/Zld.zig+100-271
......@@ -13,9 +13,10 @@ const log = std.log.scoped(.zld);
1313const aarch64 = @import("../../codegen/aarch64.zig");
1414
1515const Allocator = mem.Allocator;
16const CodeSignature = @import("CodeSignature.zig");
1716const Archive = @import("Archive.zig");
17const CodeSignature = @import("CodeSignature.zig");
1818const Object = @import("Object.zig");
19const Symbol = @import("Symbol.zig");
1920const Trie = @import("Trie.zig");
2021
2122usingnamespace @import("commands.zig");
......@@ -28,10 +29,8 @@ page_size: ?u16 = null,
2829file: ?fs.File = null,
2930out_path: ?[]const u8 = null,
3031
31// TODO Eventually, we will want to keep track of the archives themselves to be able to exclude objects
32// contained within from landing in the final artifact. For now however, since we don't optimise the binary
33// at all, we just move all objects from the archives into the final artifact.
3432objects: std.ArrayListUnmanaged(Object) = .{},
33archives: std.ArrayListUnmanaged(Archive) = .{},
3534
3635load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3736
......@@ -74,17 +73,19 @@ la_symbol_ptr_section_index: ?u16 = null,
7473data_section_index: ?u16 = null,
7574bss_section_index: ?u16 = null,
7675
77locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},
78exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},
79nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
80lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
81tlv_bootstrap: ?Import = null,
82threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
83local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
84nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
85
76globals: std.StringArrayHashMapUnmanaged(Symbol) = .{},
77undefs: std.StringArrayHashMapUnmanaged(Symbol) = .{},
8678strtab: std.ArrayListUnmanaged(u8) = .{},
8779
80// locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},
81// exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},
82// nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
83// lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
84// tlv_bootstrap: ?Import = null,
85// threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
86// local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
87// nonlazy_pointers: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
88
8889stub_helper_stubs_start_off: ?u64 = null,
8990
9091mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
......@@ -112,18 +113,6 @@ const SectionMapping = struct {
112113 offset: u32,
113114};
114115
115const Symbol = struct {
116 inner: macho.nlist_64,
117 tt: Type,
118 object_id: u16,
119
120 const Type = enum {
121 Local,
122 WeakGlobal,
123 Global,
124 };
125};
126
127116const DebugInfo = struct {
128117 inner: dwarf.DwarfInfo,
129118 debug_info: []u8,
......@@ -188,17 +177,6 @@ const DebugInfo = struct {
188177 }
189178};
190179
191pub const Import = struct {
192 /// MachO symbol table entry.
193 symbol: macho.nlist_64,
194
195 /// Id of the dynamic library where the specified entries can be found.
196 dylib_ordinal: i64,
197
198 /// Index of this import within the import list.
199 index: u32,
200};
201
202180/// Default path to dyld
203181/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
204182/// instead but this will do for now.
......@@ -218,40 +196,42 @@ pub fn init(allocator: *Allocator) Zld {
218196}
219197
220198pub fn deinit(self: *Zld) void {
221 self.threadlocal_offsets.deinit(self.allocator);
222 self.strtab.deinit(self.allocator);
223 self.local_rebases.deinit(self.allocator);
224 for (self.lazy_imports.items()) |*entry| {
225 self.allocator.free(entry.key);
199 for (self.load_commands.items) |*lc| {
200 lc.deinit(self.allocator);
226201 }
227 self.lazy_imports.deinit(self.allocator);
228 for (self.nonlazy_imports.items()) |*entry| {
229 self.allocator.free(entry.key);
202 self.load_commands.deinit(self.allocator);
203
204 for (self.objects.items) |*object| {
205 object.deinit();
230206 }
231 self.nonlazy_imports.deinit(self.allocator);
232 for (self.nonlazy_pointers.items()) |*entry| {
233 self.allocator.free(entry.key);
207 self.objects.deinit(self.allocator);
208
209 for (self.archives.items) |*archive| {
210 archive.deinit();
234211 }
235 self.nonlazy_pointers.deinit(self.allocator);
236 for (self.exports.items()) |*entry| {
212 self.archives.deinit(self.allocator);
213
214 self.mappings.deinit(self.allocator);
215 self.unhandled_sections.deinit(self.allocator);
216
217 for (self.globals.items()) |*entry| {
237218 self.allocator.free(entry.key);
238219 }
239 self.exports.deinit(self.allocator);
240 for (self.locals.items()) |*entry| {
220 self.globals.deinit(self.allocator);
221
222 for (self.undefs.items()) |*entry| {
241223 self.allocator.free(entry.key);
242 entry.value.deinit(self.allocator);
243224 }
244 self.locals.deinit(self.allocator);
225 self.undefs.deinit(self.allocator);
226}
227
228pub fn closeFiles(self: *Zld) void {
245229 for (self.objects.items) |*object| {
246 object.deinit();
230 object.file.close();
247231 }
248 self.objects.deinit(self.allocator);
249 for (self.load_commands.items) |*lc| {
250 lc.deinit(self.allocator);
232 for (self.archives.items) |*archive| {
233 archive.file.close();
251234 }
252 self.load_commands.deinit(self.allocator);
253 self.mappings.deinit(self.allocator);
254 self.unhandled_sections.deinit(self.allocator);
255235 if (self.file) |*f| f.close();
256236}
257237
......@@ -292,16 +272,15 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
292272
293273 try self.populateMetadata();
294274 try self.parseInputFiles(files);
295 try self.sortSections();
296 try self.resolveImports();
297 try self.allocateTextSegment();
298 try self.allocateDataConstSegment();
299 try self.allocateDataSegment();
300 self.allocateLinkeditSegment();
301 try self.writeStubHelperCommon();
302 try self.resolveSymbols();
303 try self.doRelocs();
304 try self.flush();
275 self.printSymtab();
276 // try self.sortSections();
277 // try self.allocateTextSegment();
278 // try self.allocateDataConstSegment();
279 // try self.allocateDataSegment();
280 // self.allocateLinkeditSegment();
281 // try self.writeStubHelperCommon();
282 // try self.doRelocs();
283 // try self.flush();
305284}
306285
307286fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
......@@ -315,7 +294,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
315294 };
316295 const index = @intCast(u16, self.objects.items.len);
317296 try self.objects.append(self.allocator, object);
318 try self.updateMetadata(index);
297 try self.resolveSymbols(index);
319298 continue;
320299 }
321300
......@@ -324,12 +303,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
324303 error.NotArchive => break :try_archive,
325304 else => |e| return e,
326305 };
327 defer archive.deinit();
328 while (archive.objects.popOrNull()) |object| {
329 const index = @intCast(u16, self.objects.items.len);
330 try self.objects.append(self.allocator, object);
331 try self.updateMetadata(index);
332 }
306 try self.archives.append(self.allocator, archive);
333307 continue;
334308 }
335309
......@@ -798,94 +772,6 @@ fn sortSections(self: *Zld) !void {
798772 }
799773}
800774
801fn resolveImports(self: *Zld) !void {
802 var imports = std.StringArrayHashMap(bool).init(self.allocator);
803 defer imports.deinit();
804
805 for (self.objects.items) |object| {
806 for (object.symtab.items) |sym| {
807 if (isLocal(&sym)) continue;
808
809 const name = object.getString(sym.n_strx);
810 const res = try imports.getOrPut(name);
811 if (isExport(&sym)) {
812 res.entry.value = false;
813 continue;
814 }
815 if (res.found_existing and !res.entry.value)
816 continue;
817 res.entry.value = true;
818 }
819 }
820
821 for (imports.items()) |entry| {
822 if (!entry.value) continue;
823
824 const sym_name = entry.key;
825 const n_strx = try self.makeString(sym_name);
826 var new_sym: macho.nlist_64 = .{
827 .n_strx = n_strx,
828 .n_type = macho.N_UNDF | macho.N_EXT,
829 .n_value = 0,
830 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
831 .n_sect = 0,
832 };
833 var key = try self.allocator.dupe(u8, sym_name);
834 // TODO handle symbol resolution from non-libc dylibs.
835 const dylib_ordinal = 1;
836
837 // TODO need to rework this. Perhaps should create a set of all possible libc
838 // symbols which are expected to be nonlazy?
839 if (mem.eql(u8, sym_name, "___stdoutp") or
840 mem.eql(u8, sym_name, "___stderrp") or
841 mem.eql(u8, sym_name, "___stdinp") or
842 mem.eql(u8, sym_name, "___stack_chk_guard") or
843 mem.eql(u8, sym_name, "_environ") or
844 mem.eql(u8, sym_name, "__DefaultRuneLocale") or
845 mem.eql(u8, sym_name, "_mach_task_self_"))
846 {
847 log.debug("writing nonlazy symbol '{s}'", .{sym_name});
848 const index = @intCast(u32, self.nonlazy_imports.items().len);
849 try self.nonlazy_imports.putNoClobber(self.allocator, key, .{
850 .symbol = new_sym,
851 .dylib_ordinal = dylib_ordinal,
852 .index = index,
853 });
854 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
855 log.debug("writing threadlocal symbol '{s}'", .{sym_name});
856 self.tlv_bootstrap = .{
857 .symbol = new_sym,
858 .dylib_ordinal = dylib_ordinal,
859 .index = 0,
860 };
861 } else {
862 log.debug("writing lazy symbol '{s}'", .{sym_name});
863 const index = @intCast(u32, self.lazy_imports.items().len);
864 try self.lazy_imports.putNoClobber(self.allocator, key, .{
865 .symbol = new_sym,
866 .dylib_ordinal = dylib_ordinal,
867 .index = index,
868 });
869 }
870 }
871
872 const n_strx = try self.makeString("dyld_stub_binder");
873 const name = try self.allocator.dupe(u8, "dyld_stub_binder");
874 log.debug("writing nonlazy symbol 'dyld_stub_binder'", .{});
875 const index = @intCast(u32, self.nonlazy_imports.items().len);
876 try self.nonlazy_imports.putNoClobber(self.allocator, name, .{
877 .symbol = .{
878 .n_strx = n_strx,
879 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
880 .n_sect = 0,
881 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
882 .n_value = 0,
883 },
884 .dylib_ordinal = 1,
885 .index = index,
886 });
887}
888
889775fn allocateTextSegment(self: *Zld) !void {
890776 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
891777 const nexterns = @intCast(u32, self.lazy_imports.items().len);
......@@ -1267,90 +1153,49 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
12671153 try self.file.?.pwriteAll(code, stub_off);
12681154}
12691155
1270fn resolveSymbols(self: *Zld) !void {
1271 for (self.objects.items) |object, object_id| {
1272 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
1273 log.debug("\n\n", .{});
1274 log.debug("resolving symbols in {s}", .{object.name});
1275
1276 for (object.symtab.items) |sym| {
1277 if (isImport(&sym)) continue;
1278
1279 const sym_name = object.getString(sym.n_strx);
1280 const out_name = try self.allocator.dupe(u8, sym_name);
1281 const locs = try self.locals.getOrPut(self.allocator, out_name);
1282 defer {
1283 if (locs.found_existing) self.allocator.free(out_name);
1284 }
1285
1286 if (!locs.found_existing) {
1287 locs.entry.value = .{};
1288 }
1289
1290 const tt: Symbol.Type = blk: {
1291 if (isLocal(&sym)) {
1292 break :blk .Local;
1293 } else if (isWeakDef(&sym)) {
1294 break :blk .WeakGlobal;
1295 } else {
1296 break :blk .Global;
1297 }
1156fn resolveSymbols(self: *Zld, object_id: u16) !void {
1157 const object = self.objects.items[object_id];
1158 log.warn("resolving symbols in '{s}'", .{object.name});
1159
1160 for (object.symtab.items) |sym, sym_id| {
1161 if (sym.isLocal()) continue; // If symbol is local to CU, we don't put it in the global symbol table.
1162
1163 const sym_name = object.getString(sym.inner.n_strx);
1164 if (sym.isGlobal()) {
1165 const global = self.globals.getEntry(sym_name) orelse {
1166 const name = try self.allocator.dupe(u8, sym_name);
1167 try self.globals.putNoClobber(self.allocator, name, .{
1168 .inner = sym.inner,
1169 .file = object_id,
1170 .index = @intCast(u32, sym_id),
1171 });
1172 _ = self.undefs.swapRemove(sym_name);
1173 continue;
12981174 };
1299 if (tt == .Global) {
1300 for (locs.entry.value.items) |ss| {
1301 if (ss.tt == .Global) {
1302 log.debug("symbol already defined '{s}'", .{sym_name});
1303 continue;
1304 // log.err("symbol '{s}' defined multiple times: {}", .{ sym_name, sym });
1305 // return error.MultipleSymbolDefinitions;
1306 }
1307 }
1308 }
13091175
1310 const source_sect_id = sym.n_sect - 1;
1311 const target_mapping = self.mappings.get(.{
1312 .object_id = @intCast(u16, object_id),
1313 .source_sect_id = source_sect_id,
1314 }) orelse {
1315 if (self.unhandled_sections.get(.{
1316 .object_id = @intCast(u16, object_id),
1317 .source_sect_id = source_sect_id,
1318 }) != null) continue;
1176 if (sym.isWeakDef()) continue; // If symbol is weak, nothing to do.
1177 if (!global.value.isWeakDef()) { // If both symbols are strong, we have a collision.
1178 log.err("symbol '{s}' defined multiple times", .{sym_name});
1179 return error.MultipleSymbolDefinitions;
1180 }
13191181
1320 log.err("section not mapped for symbol '{s}': {}", .{ sym_name, sym });
1321 return error.SectionNotMappedForSymbol;
1182 global.value = .{
1183 .inner = sym.inner,
1184 .file = object_id,
1185 .index = @intCast(u32, sym_id),
13221186 };
1323 const source_sect = seg.sections.items[source_sect_id];
1324 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
1325 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
1326 const target_addr = target_sect.addr + target_mapping.offset;
1327 const n_value = sym.n_value - source_sect.addr + target_addr;
1328
1329 log.debug("resolving '{s}':{} as {s} symbol at 0x{x}", .{ sym_name, sym, tt, n_value });
1330
1331 // TODO there might be a more generic way of doing this.
1332 var n_sect: u16 = 0;
1333 for (self.load_commands.items) |cmd, cmd_id| {
1334 if (cmd != .Segment) break;
1335 if (cmd_id == target_mapping.target_seg_id) {
1336 n_sect += target_mapping.target_sect_id + 1;
1337 break;
1338 }
1339 n_sect += @intCast(u16, cmd.Segment.sections.items.len);
1340 }
1187 } else if (sym.isUndef()) {
1188 if (self.globals.contains(sym_name)) continue; // Nothing to do if we already found a definition.
1189 if (self.undefs.contains(sym_name)) continue; // No need to reinsert the undef ref.
13411190
1342 const n_strx = try self.makeString(sym_name);
1343 try locs.entry.value.append(self.allocator, .{
1344 .inner = .{
1345 .n_strx = n_strx,
1346 .n_value = n_value,
1347 .n_type = macho.N_SECT,
1348 .n_desc = sym.n_desc,
1349 .n_sect = @intCast(u8, n_sect),
1350 },
1351 .tt = tt,
1352 .object_id = @intCast(u16, object_id),
1191 const name = try self.allocator.dupe(u8, sym_name);
1192 try self.undefs.putNoClobber(self.allocator, name, .{
1193 .inner = sym.inner,
13531194 });
1195 } else {
1196 // Oh no, unhandled symbol type, report back to the user.
1197 log.err("unhandled symbol type for symbol {any}", .{sym});
1198 return error.UnhandledSymbolType;
13541199 }
13551200 }
13561201}
......@@ -3175,7 +3020,6 @@ fn writeCodeSignature(self: *Zld) !void {
31753020 try code_sig.write(stream.writer());
31763021
31773022 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
3178
31793023 try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
31803024}
31813025
......@@ -3261,34 +3105,19 @@ pub fn parseName(name: *const [16]u8) []const u8 {
32613105 return name[0..len];
32623106}
32633107
3264fn isLocal(sym: *const macho.nlist_64) callconv(.Inline) bool {
3265 if (isExtern(sym)) return false;
3266 const tt = macho.N_TYPE & sym.n_type;
3267 return tt == macho.N_SECT;
3268}
3269
3270fn isExport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3271 if (!isExtern(sym)) return false;
3272 const tt = macho.N_TYPE & sym.n_type;
3273 return tt == macho.N_SECT;
3274}
3275
3276fn isImport(sym: *const macho.nlist_64) callconv(.Inline) bool {
3277 if (!isExtern(sym)) return false;
3278 const tt = macho.N_TYPE & sym.n_type;
3279 return tt == macho.N_UNDF;
3280}
3281
3282fn isExtern(sym: *const macho.nlist_64) callconv(.Inline) bool {
3283 if ((sym.n_type & macho.N_EXT) == 0) return false;
3284 return (sym.n_type & macho.N_PEXT) == 0;
3285}
3286
3287fn isWeakDef(sym: *const macho.nlist_64) callconv(.Inline) bool {
3288 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
3289}
3290
32913108fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool {
32923109 const group_decode = @truncate(u5, inst[3]);
32933110 return ((group_decode >> 2) == 4);
32943111}
3112
3113fn printSymtab(self: Zld) void {
3114 log.warn("globals", .{});
3115 for (self.globals.items()) |entry| {
3116 log.warn(" | {s} => {any}", .{ entry.key, entry.value });
3117 }
3118
3119 log.warn("undefs", .{});
3120 for (self.undefs.items()) |entry| {
3121 log.warn(" | {s} => {any}", .{ entry.key, entry.value });
3122 }
3123}