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...@@ -569,6 +569,7 @@ set(ZIG_STAGE2_SOURCES
569 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"569 "${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
570 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"570 "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
571 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"571 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
572 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
572 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"573 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
573 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"574 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
574 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"575 "${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 {...@@ -658,7 +658,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
658658
659 if (use_zld) {659 if (use_zld) {
660 var zld = Zld.init(self.base.allocator);660 var zld = Zld.init(self.base.allocator);
661 defer zld.deinit();661 defer {
662 zld.closeFiles();
663 zld.deinit();
664 }
662 zld.arch = target.cpu.arch;665 zld.arch = target.cpu.arch;
663666
664 var input_files = std.ArrayList([]const u8).init(self.base.allocator);667 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 {...@@ -98,7 +98,6 @@ pub fn deinit(self: *Archive) void {
98 entry.value.deinit(self.allocator);98 entry.value.deinit(self.allocator);
99 }99 }
100 self.toc.deinit(self.allocator);100 self.toc.deinit(self.allocator);
101 self.file.close();
102}101}
103102
104/// Caller owns the returned Archive instance and is responsible for calling103/// 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: [...@@ -131,20 +130,12 @@ pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, ar_name: [
131 .name = name,130 .name = name,
132 };131 };
133132
134 var object_offsets = try self.readTableOfContents(reader);133 try self.parseTableOfContents(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 }
143134
144 return self;135 return self;
145}136}
146137
147fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {138fn parseTableOfContents(self: *Archive, reader: anytype) !void {
148 const symtab_size = try reader.readIntLittle(u32);139 const symtab_size = try reader.readIntLittle(u32);
149 var symtab = try self.allocator.alloc(u8, symtab_size);140 var symtab = try self.allocator.alloc(u8, symtab_size);
150 defer self.allocator.free(symtab);141 defer self.allocator.free(symtab);
...@@ -158,10 +149,6 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {...@@ -158,10 +149,6 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
158 var symtab_stream = std.io.fixedBufferStream(symtab);149 var symtab_stream = std.io.fixedBufferStream(symtab);
159 var symtab_reader = symtab_stream.reader();150 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
165 while (true) {152 while (true) {
166 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {153 const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) {
167 error.EndOfStream => break,154 error.EndOfStream => break,
...@@ -179,19 +166,7 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {...@@ -179,19 +166,7 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
179 }166 }
180167
181 try res.entry.value.append(self.allocator, object_offset);168 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 }
192 }169 }
193
194 return object_offsets.toOwnedSlice();
195}170}
196171
197fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void {172fn 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;...@@ -9,6 +9,7 @@ const macho = std.macho;
9const mem = std.mem;9const mem = std.mem;
1010
11const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
12const Symbol = @import("Symbol.zig");
12const parseName = @import("Zld.zig").parseName;13const parseName = @import("Zld.zig").parseName;
1314
14usingnamespace @import("commands.zig");15usingnamespace @import("commands.zig");
...@@ -36,7 +37,7 @@ dwarf_debug_str_index: ?u16 = null,...@@ -36,7 +37,7 @@ dwarf_debug_str_index: ?u16 = null,
36dwarf_debug_line_index: ?u16 = null,37dwarf_debug_line_index: ?u16 = null,
37dwarf_debug_ranges_index: ?u16 = null,38dwarf_debug_ranges_index: ?u16 = null,
3839
39symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},40symtab: std.ArrayListUnmanaged(Symbol) = .{},
40strtab: std.ArrayListUnmanaged(u8) = .{},41strtab: std.ArrayListUnmanaged(u8) = .{},
4142
42data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},43data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
...@@ -53,7 +54,6 @@ pub fn deinit(self: *Object) void {...@@ -53,7 +54,6 @@ pub fn deinit(self: *Object) void {
53 if (self.ar_name) |v| {54 if (self.ar_name) |v| {
54 self.allocator.free(v);55 self.allocator.free(v);
55 }56 }
56 self.file.close();
57}57}
5858
59/// Caller owns the returned Object instance and is responsible for calling59/// 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...@@ -89,21 +89,9 @@ pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []co
89 };89 };
9090
91 try self.readLoadCommands(reader, .{});91 try self.readLoadCommands(reader, .{});
9292 if (self.symtab_cmd_index != null) try self.parseSymtab();
93 if (self.symtab_cmd_index != null) {
94 try self.readSymtab();
95 try self.readStrtab();
96 }
97
98 if (self.data_in_code_cmd_index != null) try self.readDataInCode();93 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
107 return self;95 return self;
108}96}
10997
...@@ -174,25 +162,33 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi...@@ -174,25 +162,33 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi
174 }162 }
175}163}
176164
177pub fn readSymtab(self: *Object) !void {165pub fn parseSymtab(self: *Object) !void {
178 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;166 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);167
180 defer self.allocator.free(buffer);168 var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
181 _ = try self.file.preadAll(buffer, symtab_cmd.symoff);169 defer self.allocator.free(symtab);
170
171 _ = try self.file.preadAll(symtab, symtab_cmd.symoff);
182 try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms);172 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 {174 var stream = std.io.fixedBufferStream(symtab);
190 const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab;175 var reader = stream.reader();
191 var buffer = try self.allocator.alloc(u8, symtab_cmd.strsize);176
192 defer self.allocator.free(buffer);177 while (true) {
193 _ = try self.file.preadAll(buffer, symtab_cmd.stroff);178 const symbol = reader.readStruct(macho.nlist_64) catch |err| switch (err) {
194 try self.strtab.ensureCapacity(self.allocator, symtab_cmd.strsize);179 error.EndOfStream => break,
195 self.strtab.appendSliceAssumeCapacity(buffer);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);
196}192}
197193
198pub fn getString(self: *const Object, str_off: u32) []const u8 {194pub 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);...@@ -13,9 +13,10 @@ const log = std.log.scoped(.zld);
13const aarch64 = @import("../../codegen/aarch64.zig");13const aarch64 = @import("../../codegen/aarch64.zig");
1414
15const Allocator = mem.Allocator;15const Allocator = mem.Allocator;
16const CodeSignature = @import("CodeSignature.zig");
17const Archive = @import("Archive.zig");16const Archive = @import("Archive.zig");
17const CodeSignature = @import("CodeSignature.zig");
18const Object = @import("Object.zig");18const Object = @import("Object.zig");
19const Symbol = @import("Symbol.zig");
19const Trie = @import("Trie.zig");20const Trie = @import("Trie.zig");
2021
21usingnamespace @import("commands.zig");22usingnamespace @import("commands.zig");
...@@ -28,10 +29,8 @@ page_size: ?u16 = null,...@@ -28,10 +29,8 @@ page_size: ?u16 = null,
28file: ?fs.File = null,29file: ?fs.File = null,
29out_path: ?[]const u8 = null,30out_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.
34objects: std.ArrayListUnmanaged(Object) = .{},32objects: std.ArrayListUnmanaged(Object) = .{},
33archives: std.ArrayListUnmanaged(Archive) = .{},
3534
36load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},35load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3736
...@@ -74,17 +73,19 @@ la_symbol_ptr_section_index: ?u16 = null,...@@ -74,17 +73,19 @@ la_symbol_ptr_section_index: ?u16 = null,
74data_section_index: ?u16 = null,73data_section_index: ?u16 = null,
75bss_section_index: ?u16 = null,74bss_section_index: ?u16 = null,
7675
77locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},76globals: std.StringArrayHashMapUnmanaged(Symbol) = .{},
78exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},77undefs: std.StringArrayHashMapUnmanaged(Symbol) = .{},
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
86strtab: std.ArrayListUnmanaged(u8) = .{},78strtab: 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
88stub_helper_stubs_start_off: ?u64 = null,89stub_helper_stubs_start_off: ?u64 = null,
8990
90mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},91mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
...@@ -112,18 +113,6 @@ const SectionMapping = struct {...@@ -112,18 +113,6 @@ const SectionMapping = struct {
112 offset: u32,113 offset: u32,
113};114};
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
127const DebugInfo = struct {116const DebugInfo = struct {
128 inner: dwarf.DwarfInfo,117 inner: dwarf.DwarfInfo,
129 debug_info: []u8,118 debug_info: []u8,
...@@ -188,17 +177,6 @@ const DebugInfo = struct {...@@ -188,17 +177,6 @@ const DebugInfo = struct {
188 }177 }
189};178};
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
202/// Default path to dyld180/// Default path to dyld
203/// TODO instead of hardcoding it, we should probably look through some env vars and search paths181/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
204/// instead but this will do for now.182/// instead but this will do for now.
...@@ -218,40 +196,42 @@ pub fn init(allocator: *Allocator) Zld {...@@ -218,40 +196,42 @@ pub fn init(allocator: *Allocator) Zld {
218}196}
219197
220pub fn deinit(self: *Zld) void {198pub fn deinit(self: *Zld) void {
221 self.threadlocal_offsets.deinit(self.allocator);199 for (self.load_commands.items) |*lc| {
222 self.strtab.deinit(self.allocator);200 lc.deinit(self.allocator);
223 self.local_rebases.deinit(self.allocator);
224 for (self.lazy_imports.items()) |*entry| {
225 self.allocator.free(entry.key);
226 }201 }
227 self.lazy_imports.deinit(self.allocator);202 self.load_commands.deinit(self.allocator);
228 for (self.nonlazy_imports.items()) |*entry| {203
229 self.allocator.free(entry.key);204 for (self.objects.items) |*object| {
205 object.deinit();
230 }206 }
231 self.nonlazy_imports.deinit(self.allocator);207 self.objects.deinit(self.allocator);
232 for (self.nonlazy_pointers.items()) |*entry| {208
233 self.allocator.free(entry.key);209 for (self.archives.items) |*archive| {
210 archive.deinit();
234 }211 }
235 self.nonlazy_pointers.deinit(self.allocator);212 self.archives.deinit(self.allocator);
236 for (self.exports.items()) |*entry| {213
214 self.mappings.deinit(self.allocator);
215 self.unhandled_sections.deinit(self.allocator);
216
217 for (self.globals.items()) |*entry| {
237 self.allocator.free(entry.key);218 self.allocator.free(entry.key);
238 }219 }
239 self.exports.deinit(self.allocator);220 self.globals.deinit(self.allocator);
240 for (self.locals.items()) |*entry| {221
222 for (self.undefs.items()) |*entry| {
241 self.allocator.free(entry.key);223 self.allocator.free(entry.key);
242 entry.value.deinit(self.allocator);
243 }224 }
244 self.locals.deinit(self.allocator);225 self.undefs.deinit(self.allocator);
226}
227
228pub fn closeFiles(self: *Zld) void {
245 for (self.objects.items) |*object| {229 for (self.objects.items) |*object| {
246 object.deinit();230 object.file.close();
247 }231 }
248 self.objects.deinit(self.allocator);232 for (self.archives.items) |*archive| {
249 for (self.load_commands.items) |*lc| {233 archive.file.close();
250 lc.deinit(self.allocator);
251 }234 }
252 self.load_commands.deinit(self.allocator);
253 self.mappings.deinit(self.allocator);
254 self.unhandled_sections.deinit(self.allocator);
255 if (self.file) |*f| f.close();235 if (self.file) |*f| f.close();
256}236}
257237
...@@ -292,16 +272,15 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {...@@ -292,16 +272,15 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
292272
293 try self.populateMetadata();273 try self.populateMetadata();
294 try self.parseInputFiles(files);274 try self.parseInputFiles(files);
295 try self.sortSections();275 self.printSymtab();
296 try self.resolveImports();276 // try self.sortSections();
297 try self.allocateTextSegment();277 // try self.allocateTextSegment();
298 try self.allocateDataConstSegment();278 // try self.allocateDataConstSegment();
299 try self.allocateDataSegment();279 // try self.allocateDataSegment();
300 self.allocateLinkeditSegment();280 // self.allocateLinkeditSegment();
301 try self.writeStubHelperCommon();281 // try self.writeStubHelperCommon();
302 try self.resolveSymbols();282 // try self.doRelocs();
303 try self.doRelocs();283 // try self.flush();
304 try self.flush();
305}284}
306285
307fn parseInputFiles(self: *Zld, files: []const []const u8) !void {286fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
...@@ -315,7 +294,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -315,7 +294,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
315 };294 };
316 const index = @intCast(u16, self.objects.items.len);295 const index = @intCast(u16, self.objects.items.len);
317 try self.objects.append(self.allocator, object);296 try self.objects.append(self.allocator, object);
318 try self.updateMetadata(index);297 try self.resolveSymbols(index);
319 continue;298 continue;
320 }299 }
321300
...@@ -324,12 +303,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {...@@ -324,12 +303,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
324 error.NotArchive => break :try_archive,303 error.NotArchive => break :try_archive,
325 else => |e| return e,304 else => |e| return e,
326 };305 };
327 defer archive.deinit();306 try self.archives.append(self.allocator, archive);
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 }
333 continue;307 continue;
334 }308 }
335309
...@@ -798,94 +772,6 @@ fn sortSections(self: *Zld) !void {...@@ -798,94 +772,6 @@ fn sortSections(self: *Zld) !void {
798 }772 }
799}773}
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
889fn allocateTextSegment(self: *Zld) !void {775fn allocateTextSegment(self: *Zld) !void {
890 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;776 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
891 const nexterns = @intCast(u32, self.lazy_imports.items().len);777 const nexterns = @intCast(u32, self.lazy_imports.items().len);
...@@ -1267,90 +1153,49 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {...@@ -1267,90 +1153,49 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
1267 try self.file.?.pwriteAll(code, stub_off);1153 try self.file.?.pwriteAll(code, stub_off);
1268}1154}
12691155
1270fn resolveSymbols(self: *Zld) !void {1156fn resolveSymbols(self: *Zld, object_id: u16) !void {
1271 for (self.objects.items) |object, object_id| {1157 const object = self.objects.items[object_id];
1272 const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;1158 log.warn("resolving symbols in '{s}'", .{object.name});
1273 log.debug("\n\n", .{});1159
1274 log.debug("resolving symbols in {s}", .{object.name});1160 for (object.symtab.items) |sym, sym_id| {
12751161 if (sym.isLocal()) continue; // If symbol is local to CU, we don't put it in the global symbol table.
1276 for (object.symtab.items) |sym| {1162
1277 if (isImport(&sym)) continue;1163 const sym_name = object.getString(sym.inner.n_strx);
12781164 if (sym.isGlobal()) {
1279 const sym_name = object.getString(sym.n_strx);1165 const global = self.globals.getEntry(sym_name) orelse {
1280 const out_name = try self.allocator.dupe(u8, sym_name);1166 const name = try self.allocator.dupe(u8, sym_name);
1281 const locs = try self.locals.getOrPut(self.allocator, out_name);1167 try self.globals.putNoClobber(self.allocator, name, .{
1282 defer {1168 .inner = sym.inner,
1283 if (locs.found_existing) self.allocator.free(out_name);1169 .file = object_id,
1284 }1170 .index = @intCast(u32, sym_id),
12851171 });
1286 if (!locs.found_existing) {1172 _ = self.undefs.swapRemove(sym_name);
1287 locs.entry.value = .{};1173 continue;
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 }
1298 };1174 };
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;1176 if (sym.isWeakDef()) continue; // If symbol is weak, nothing to do.
1311 const target_mapping = self.mappings.get(.{1177 if (!global.value.isWeakDef()) { // If both symbols are strong, we have a collision.
1312 .object_id = @intCast(u16, object_id),1178 log.err("symbol '{s}' defined multiple times", .{sym_name});
1313 .source_sect_id = source_sect_id,1179 return error.MultipleSymbolDefinitions;
1314 }) orelse {1180 }
1315 if (self.unhandled_sections.get(.{
1316 .object_id = @intCast(u16, object_id),
1317 .source_sect_id = source_sect_id,
1318 }) != null) continue;
13191181
1320 log.err("section not mapped for symbol '{s}': {}", .{ sym_name, sym });1182 global.value = .{
1321 return error.SectionNotMappedForSymbol;1183 .inner = sym.inner,
1184 .file = object_id,
1185 .index = @intCast(u32, sym_id),
1322 };1186 };
1323 const source_sect = seg.sections.items[source_sect_id];1187 } else if (sym.isUndef()) {
1324 const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;1188 if (self.globals.contains(sym_name)) continue; // Nothing to do if we already found a definition.
1325 const target_sect = target_seg.sections.items[target_mapping.target_sect_id];1189 if (self.undefs.contains(sym_name)) continue; // No need to reinsert the undef ref.
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 }
13411190
1342 const n_strx = try self.makeString(sym_name);1191 const name = try self.allocator.dupe(u8, sym_name);
1343 try locs.entry.value.append(self.allocator, .{1192 try self.undefs.putNoClobber(self.allocator, name, .{
1344 .inner = .{1193 .inner = sym.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),
1353 });1194 });
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;
1354 }1199 }
1355 }1200 }
1356}1201}
...@@ -3175,7 +3020,6 @@ fn writeCodeSignature(self: *Zld) !void {...@@ -3175,7 +3020,6 @@ fn writeCodeSignature(self: *Zld) !void {
3175 try code_sig.write(stream.writer());3020 try code_sig.write(stream.writer());
31763021
3177 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });3022 log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len });
3178
3179 try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);3023 try self.file.?.pwriteAll(buffer, code_sig_cmd.dataoff);
3180}3024}
31813025
...@@ -3261,34 +3105,19 @@ pub fn parseName(name: *const [16]u8) []const u8 {...@@ -3261,34 +3105,19 @@ pub fn parseName(name: *const [16]u8) []const u8 {
3261 return name[0..len];3105 return name[0..len];
3262}3106}
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
3291fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool {3108fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool {
3292 const group_decode = @truncate(u5, inst[3]);3109 const group_decode = @truncate(u5, inst[3]);
3293 return ((group_decode >> 2) == 4);3110 return ((group_decode >> 2) == 4);
3294}3111}
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}