authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-16 00:51:21+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-16 13:02:02+02:00
log5a2bea29315158bc05fb4b09842bbb9ae0ddfada
treee807c31c7b7a204b5bc9b451d6e1d5e83bee34b0
parentf519e781c6f952b3646cb646880ab460ad7a6cce

zld: draft symbol resolver on macho.nlist_64 only


2 files changed, 549 insertions(+), 265 deletions(-)

src/link/MachO/Object.zig-5
......@@ -56,9 +56,6 @@ tu_name: ?[]const u8 = null,
5656tu_comp_dir: ?[]const u8 = null,
5757mtime: ?u64 = null,
5858
59symbols: std.ArrayListUnmanaged(*Symbol) = .{},
60sections_as_symbols: std.AutoHashMapUnmanaged(u8, *Symbol) = .{},
61
6259text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
6360
6461const DebugInfo = struct {
......@@ -165,8 +162,6 @@ pub fn deinit(self: *Object) void {
165162 self.data_in_code_entries.deinit(self.allocator);
166163 self.symtab.deinit(self.allocator);
167164 self.strtab.deinit(self.allocator);
168 self.symbols.deinit(self.allocator);
169 self.sections_as_symbols.deinit(self.allocator);
170165 self.text_blocks.deinit(self.allocator);
171166
172167 if (self.debug_info) |*db| {
src/link/MachO/Zld.zig+549-260
......@@ -16,7 +16,6 @@ const Archive = @import("Archive.zig");
1616const CodeSignature = @import("CodeSignature.zig");
1717const Dylib = @import("Dylib.zig");
1818const Object = @import("Object.zig");
19const Symbol = @import("Symbol.zig");
2019const TextBlock = @import("TextBlock.zig");
2120const Trie = @import("Trie.zig");
2221
......@@ -100,22 +99,55 @@ objc_selrefs_section_index: ?u16 = null,
10099objc_classrefs_section_index: ?u16 = null,
101100objc_data_section_index: ?u16 = null,
102101
103locals: std.ArrayListUnmanaged(*Symbol) = .{},
104imports: std.ArrayListUnmanaged(*Symbol) = .{},
105globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
102locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
103globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
104imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
105undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
106tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{},
107symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
108object_mapping: std.AutoHashMapUnmanaged(u16, []u32) = .{},
106109
107stubs: std.ArrayListUnmanaged(*Symbol) = .{},
108got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
110strtab: std.ArrayListUnmanaged(u8) = .{},
111
112// stubs: std.ArrayListUnmanaged(*Symbol) = .{},
113got_entries: std.ArrayListUnmanaged(GotEntry) = .{},
109114
110115stub_helper_stubs_start_off: ?u64 = null,
111116
112117blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
113118
114strtab: std.ArrayListUnmanaged(u8) = .{},
115
116119has_dices: bool = false,
117120has_stabs: bool = false,
118121
122const SymbolWithLoc = struct {
123 // Table where the symbol can be found.
124 where: enum {
125 global,
126 import,
127 undef,
128 tentative,
129 },
130 where_index: u32,
131 local_sym_index: u32 = 0,
132 file: u16 = 0,
133};
134
135pub const GotEntry = struct {
136 /// GOT entry can either be a local pointer or an extern (nonlazy) import.
137 kind: enum {
138 local,
139 import,
140 },
141
142 /// Id to the macho.nlist_64 from the respective table: either locals or nonlazy imports.
143 /// TODO I'm more and more inclined to just manage a single, max two symbol tables
144 /// rather than 4 as we currently do, but I'll follow up in the future PR.
145 local_sym_index: u32,
146
147 /// Index of this entry in the GOT.
148 got_index: u32,
149};
150
119151pub const Output = struct {
120152 tag: enum { exe, dylib },
121153 path: []const u8,
......@@ -130,7 +162,7 @@ pub fn init(allocator: *Allocator) !Zld {
130162}
131163
132164pub fn deinit(self: *Zld) void {
133 self.stubs.deinit(self.allocator);
165 // self.stubs.deinit(self.allocator);
134166 self.got_entries.deinit(self.allocator);
135167
136168 for (self.load_commands.items) |*lc| {
......@@ -156,20 +188,24 @@ pub fn deinit(self: *Zld) void {
156188 }
157189 self.dylibs.deinit(self.allocator);
158190
159 for (self.imports.items) |sym| {
160 self.allocator.destroy(sym);
161 }
191 self.locals.deinit(self.allocator);
192 self.globals.deinit(self.allocator);
162193 self.imports.deinit(self.allocator);
194 self.undefs.deinit(self.allocator);
195 self.tentatives.deinit(self.allocator);
163196
164 for (self.locals.items) |sym| {
165 self.allocator.destroy(sym);
197 for (self.symbol_resolver.keys()) |key| {
198 self.allocator.free(key);
166199 }
167 self.locals.deinit(self.allocator);
200 self.symbol_resolver.deinit(self.allocator);
168201
169 for (self.globals.keys()) |key| {
170 self.allocator.free(key);
202 {
203 var it = self.object_mapping.valueIterator();
204 while (it.next()) |value_ptr| {
205 self.allocator.free(value_ptr.*);
206 }
171207 }
172 self.globals.deinit(self.allocator);
208 self.object_mapping.deinit(self.allocator);
173209
174210 self.strtab.deinit(self.allocator);
175211
......@@ -213,28 +249,73 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
213249 try self.parseInputFiles(files, args.syslibroot);
214250 try self.parseLibs(args.libs, args.syslibroot);
215251 try self.resolveSymbols();
216 try self.parseTextBlocks();
217 try self.sortSections();
218 try self.addRpaths(args.rpaths);
219 try self.addDataInCodeLC();
220 try self.addCodeSignatureLC();
221 try self.allocateTextSegment();
222 try self.allocateDataConstSegment();
223 try self.allocateDataSegment();
224 self.allocateLinkeditSegment();
225 try self.allocateTextBlocks();
226
227 // var it = self.blocks.iterator();
228 // while (it.next()) |entry| {
229 // const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
230 // const sect = seg.sections.items[entry.key_ptr.sect];
231
232 // log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
233 // log.warn(" {}", .{sect});
234 // entry.value_ptr.*.print(self);
235 // }
236
237 try self.flush();
252
253 log.warn("locals", .{});
254 for (self.locals.items) |sym| {
255 log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
256 }
257
258 log.warn("globals", .{});
259 for (self.globals.items) |sym| {
260 log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
261 }
262
263 log.warn("tentatives", .{});
264 for (self.tentatives.items) |sym| {
265 log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
266 }
267
268 log.warn("undefines", .{});
269 for (self.undefs.items) |sym| {
270 log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
271 }
272
273 log.warn("imports", .{});
274 for (self.imports.items) |sym| {
275 log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
276 }
277
278 log.warn("symbol resolver", .{});
279 for (self.symbol_resolver.keys()) |key| {
280 log.warn(" | {s} => {}", .{ key, self.symbol_resolver.get(key).? });
281 }
282
283 log.warn("mappings", .{});
284 for (self.objects.items) |object, id| {
285 const object_id = @intCast(u16, id);
286 log.warn(" in object {s}", .{object.name.?});
287 for (object.symtab.items) |sym, sym_id| {
288 if (self.localSymIndex(object_id, @intCast(u32, sym_id))) |local_id| {
289 log.warn(" | {d} => {d}", .{ sym_id, local_id });
290 } else {
291 log.warn(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) });
292 }
293 }
294 }
295
296 return error.TODO;
297 // try self.parseTextBlocks();
298 // try self.sortSections();
299 // try self.addRpaths(args.rpaths);
300 // try self.addDataInCodeLC();
301 // try self.addCodeSignatureLC();
302 // try self.allocateTextSegment();
303 // try self.allocateDataConstSegment();
304 // try self.allocateDataSegment();
305 // self.allocateLinkeditSegment();
306 // try self.allocateTextBlocks();
307
308 // // var it = self.blocks.iterator();
309 // // while (it.next()) |entry| {
310 // // const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
311 // // const sect = seg.sections.items[entry.key_ptr.sect];
312
313 // // log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
314 // // log.warn(" {}", .{sect});
315 // // entry.value_ptr.*.print(self);
316 // // }
317
318 // try self.flush();
238319}
239320
240321fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {
......@@ -1328,130 +1409,242 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void {
13281409 try self.file.?.pwriteAll(code, stub_off);
13291410}
13301411
1331fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
1332 log.debug("resolving symbols in '{s}'", .{object.name});
1412fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
1413 const object = self.objects.items[object_id];
1414
1415 log.warn("resolving symbols in '{s}'", .{object.name});
1416
1417 const mapping = try self.allocator.alloc(u32, object.symtab.items.len);
1418 mem.set(u32, mapping, 0);
1419 try self.object_mapping.putNoClobber(self.allocator, object_id, mapping);
13331420
1334 for (object.symtab.items) |sym| {
1421 for (object.symtab.items) |sym, id| {
1422 const sym_id = @intCast(u32, id);
13351423 const sym_name = object.getString(sym.n_strx);
13361424
1337 if (Symbol.isStab(sym)) {
1338 log.err("unhandled symbol type: stab {s}", .{sym_name});
1339 log.err(" | first definition in {s}", .{object.name.?});
1425 if (symbolIsStab(sym)) {
1426 log.err("unhandled symbol type: stab", .{});
1427 log.err(" symbol '{s}'", .{sym_name});
1428 log.err(" first definition in '{s}'", .{object.name.?});
13401429 return error.UnhandledSymbolType;
13411430 }
13421431
1343 if (Symbol.isIndr(sym)) {
1344 log.err("unhandled symbol type: indirect {s}", .{sym_name});
1345 log.err(" | first definition in {s}", .{object.name.?});
1432 if (symbolIsIndr(sym)) {
1433 log.err("unhandled symbol type: indirect", .{});
1434 log.err(" symbol '{s}'", .{sym_name});
1435 log.err(" first definition in '{s}'", .{object.name.?});
13461436 return error.UnhandledSymbolType;
13471437 }
13481438
1349 if (Symbol.isAbs(sym)) {
1350 log.err("unhandled symbol type: absolute {s}", .{sym_name});
1351 log.err(" | first definition in {s}", .{object.name.?});
1439 if (symbolIsAbs(sym)) {
1440 log.err("unhandled symbol type: absolute", .{});
1441 log.err(" symbol '{s}'", .{sym_name});
1442 log.err(" first definition in '{s}'", .{object.name.?});
13521443 return error.UnhandledSymbolType;
13531444 }
13541445
1355 if (Symbol.isSect(sym) and !Symbol.isExt(sym)) {
1356 // Regular symbol local to translation unit
1357 const symbol = try self.allocator.create(Symbol);
1358 symbol.* = .{
1359 .strx = try self.makeString(sym_name),
1360 .payload = .{
1361 .regular = .{
1362 .linkage = .translation_unit,
1363 .address = sym.n_value,
1364 .weak_ref = Symbol.isWeakRef(sym),
1365 .file = object,
1366 .local_sym_index = @intCast(u32, self.locals.items.len),
1367 },
1368 },
1446 if (symbolIsSect(sym)) {
1447 // Defined symbol regardless of scope lands in the locals symbol table.
1448 const n_strx = blk: {
1449 if (self.symbol_resolver.get(sym_name)) |resolv| {
1450 switch (resolv.where) {
1451 .global => break :blk self.globals.items[resolv.where_index].n_strx,
1452 .tentative => break :blk self.tentatives.items[resolv.where_index].n_strx,
1453 .undef => break :blk self.undefs.items[resolv.where_index].n_strx,
1454 .import => unreachable,
1455 }
1456 }
1457 break :blk try self.makeString(sym_name);
13691458 };
1370 try self.locals.append(self.allocator, symbol);
1371 try object.symbols.append(self.allocator, symbol);
1372 continue;
1373 }
1374
1375 const symbol = self.globals.get(sym_name) orelse symbol: {
1376 // Insert new global symbol.
1377 const symbol = try self.allocator.create(Symbol);
1378 symbol.* = .{
1379 .strx = try self.makeString(sym_name),
1380 .payload = .{ .undef = .{ .file = object } },
1459 const local_sym_index = @intCast(u32, self.locals.items.len);
1460 try self.locals.append(self.allocator, .{
1461 .n_strx = n_strx,
1462 .n_type = macho.N_SECT,
1463 .n_sect = 0,
1464 .n_desc = 0,
1465 .n_value = sym.n_value,
1466 });
1467 mapping[sym_id] = local_sym_index;
1468
1469 // If the symbol's scope is not local aka translation unit, then we need work out
1470 // if we should save the symbol as a global, or potentially flag the error.
1471 if (!symbolIsExt(sym)) continue;
1472
1473 const local = self.locals.items[local_sym_index];
1474 const resolv = self.symbol_resolver.getPtr(sym_name) orelse {
1475 const global_sym_index = @intCast(u32, self.globals.items.len);
1476 try self.globals.append(self.allocator, .{
1477 .n_strx = n_strx,
1478 .n_type = sym.n_type,
1479 .n_sect = 0,
1480 .n_desc = sym.n_desc,
1481 .n_value = sym.n_value,
1482 });
1483 try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), .{
1484 .where = .global,
1485 .where_index = global_sym_index,
1486 .local_sym_index = local_sym_index,
1487 .file = object_id,
1488 });
1489 continue;
13811490 };
1382 const alloc_name = try self.allocator.dupe(u8, sym_name);
1383 try self.globals.putNoClobber(self.allocator, alloc_name, symbol);
1384 break :symbol symbol;
1385 };
13861491
1387 if (Symbol.isSect(sym)) {
1388 // Global symbol
1389 const linkage: Symbol.Regular.Linkage = if (Symbol.isWeakDef(sym) or Symbol.isPext(sym))
1390 .linkage_unit
1391 else
1392 .global;
1393
1394 const should_update = if (symbol.payload == .regular) blk: {
1395 if (symbol.payload.regular.linkage == .global and linkage == .global) {
1396 log.err("symbol '{s}' defined multiple times", .{sym_name});
1397 log.err(" | first definition in {s}", .{symbol.payload.regular.file.?.name.?});
1398 log.err(" | next definition in {s}", .{object.name.?});
1399 return error.MultipleSymbolDefinitions;
1400 }
1401 break :blk symbol.payload.regular.linkage != .global;
1402 } else true;
1403
1404 if (should_update) {
1405 symbol.payload = .{
1406 .regular = .{
1407 .linkage = linkage,
1408 .address = sym.n_value,
1409 .weak_ref = Symbol.isWeakRef(sym),
1410 .file = object,
1411 },
1412 };
1492 switch (resolv.where) {
1493 .import => unreachable,
1494 .global => {
1495 const global = &self.globals.items[resolv.where_index];
1496
1497 if (!(symbolIsWeakDef(sym) and symbolIsPext(sym)) and
1498 !(symbolIsWeakDef(global.*) and symbolIsPext(global.*)))
1499 {
1500 log.err("symbol '{s}' defined multiple times", .{sym_name});
1501 log.err(" first definition in '{s}'", .{self.objects.items[resolv.file].name.?});
1502 log.err(" next definition in '{s}'", .{object.name.?});
1503 return error.MultipleSymbolDefinitions;
1504 }
1505
1506 if (symbolIsWeakDef(sym) or symbolIsPext(sym)) continue; // Current symbol is weak, so skip it.
1507
1508 // Otherwise, update the resolver and the global symbol.
1509 global.n_type = sym.n_type;
1510 resolv.local_sym_index = local_sym_index;
1511 resolv.file = object_id;
1512
1513 continue;
1514 },
1515 .undef => {
1516 const undef = &self.undefs.items[resolv.where_index];
1517 undef.* = .{
1518 .n_strx = 0,
1519 .n_type = macho.N_UNDF,
1520 .n_sect = 0,
1521 .n_desc = 0,
1522 .n_value = 0,
1523 };
1524 },
1525 .tentative => {
1526 const tentative = &self.tentatives.items[resolv.where_index];
1527 tentative.* = .{
1528 .n_strx = 0,
1529 .n_type = macho.N_UNDF,
1530 .n_sect = 0,
1531 .n_desc = 0,
1532 .n_value = 0,
1533 };
1534 },
14131535 }
1414 } else if (sym.n_value != 0) {
1415 // Tentative definition
1416 const should_update = switch (symbol.payload) {
1417 .tentative => |tent| tent.size < sym.n_value,
1418 .undef => true,
1419 else => false,
1536
1537 const global_sym_index = @intCast(u32, self.globals.items.len);
1538 try self.globals.append(self.allocator, .{
1539 .n_strx = local.n_strx,
1540 .n_type = sym.n_type,
1541 .n_sect = 0,
1542 .n_desc = sym.n_desc,
1543 .n_value = sym.n_value,
1544 });
1545 resolv.* = .{
1546 .where = .global,
1547 .where_index = global_sym_index,
1548 .local_sym_index = local_sym_index,
1549 .file = object_id,
1550 };
1551 } else if (symbolIsTentative(sym)) {
1552 // Symbol is a tentative definition.
1553 const resolv = self.symbol_resolver.getPtr(sym_name) orelse {
1554 const tent_sym_index = @intCast(u32, self.tentatives.items.len);
1555 try self.tentatives.append(self.allocator, .{
1556 .n_strx = try self.makeString(sym_name),
1557 .n_type = sym.n_type,
1558 .n_sect = 0,
1559 .n_desc = sym.n_desc,
1560 .n_value = sym.n_value,
1561 });
1562 try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), .{
1563 .where = .tentative,
1564 .where_index = tent_sym_index,
1565 .file = object_id,
1566 });
1567 continue;
14201568 };
14211569
1422 if (should_update) {
1423 symbol.payload = .{
1424 .tentative = .{
1425 .size = sym.n_value,
1426 .alignment = (sym.n_desc >> 8) & 0x0f,
1427 .file = object,
1428 },
1429 };
1570 switch (resolv.where) {
1571 .import => unreachable,
1572 .global => {},
1573 .undef => {
1574 const undef = &self.undefs.items[resolv.where_index];
1575 const tent_sym_index = @intCast(u32, self.tentatives.items.len);
1576 try self.tentatives.append(self.allocator, .{
1577 .n_strx = undef.n_strx,
1578 .n_type = sym.n_type,
1579 .n_sect = 0,
1580 .n_desc = sym.n_desc,
1581 .n_value = sym.n_value,
1582 });
1583 resolv.* = .{
1584 .where = .tentative,
1585 .where_index = tent_sym_index,
1586 .file = object_id,
1587 };
1588 undef.* = .{
1589 .n_strx = 0,
1590 .n_type = macho.N_UNDF,
1591 .n_sect = 0,
1592 .n_desc = 0,
1593 .n_value = 0,
1594 };
1595 },
1596 .tentative => {
1597 const tentative = &self.tentatives.items[resolv.where_index];
1598 if (tentative.n_value >= sym.n_value) continue;
1599
1600 tentative.n_desc = sym.n_desc;
1601 tentative.n_value = sym.n_value;
1602 resolv.file = object_id;
1603 },
14301604 }
1431 }
1605 } else {
1606 // Symbol is undefined.
1607 if (self.symbol_resolver.contains(sym_name)) continue;
14321608
1433 try object.symbols.append(self.allocator, symbol);
1609 const undef_sym_index = @intCast(u32, self.undefs.items.len);
1610 try self.undefs.append(self.allocator, .{
1611 .n_strx = try self.makeString(sym_name),
1612 .n_type = macho.N_UNDF,
1613 .n_sect = 0,
1614 .n_desc = 0,
1615 .n_value = 0,
1616 });
1617 try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), .{
1618 .where = .undef,
1619 .where_index = undef_sym_index,
1620 .file = object_id,
1621 });
1622 }
14341623 }
14351624}
14361625
14371626fn resolveSymbols(self: *Zld) !void {
14381627 // TODO mimicking insertion of null symbol from incremental linker.
14391628 // This will need to moved.
1440 const null_sym = try self.allocator.create(Symbol);
1441 null_sym.* = .{ .strx = 0, .payload = .{ .undef = .{} } };
1442 try self.locals.append(self.allocator, null_sym);
1629 try self.locals.append(self.allocator, .{
1630 .n_strx = 0,
1631 .n_type = macho.N_UNDF,
1632 .n_sect = 0,
1633 .n_desc = 0,
1634 .n_value = 0,
1635 });
1636 try self.strtab.append(self.allocator, 0);
14431637
14441638 // First pass, resolve symbols in provided objects.
1445 for (self.objects.items) |object| {
1446 try self.resolveSymbolsInObject(object);
1639 for (self.objects.items) |_, object_id| {
1640 try self.resolveSymbolsInObject(@intCast(u16, object_id));
14471641 }
14481642
14491643 // Second pass, resolve symbols in static libraries.
1450 var sym_it = self.globals.iterator();
1451 while (sym_it.next()) |entry| {
1452 const sym_name = entry.key_ptr.*;
1453 const symbol = entry.value_ptr.*;
1454 if (symbol.payload != .undef) continue;
1644 loop: for (self.undefs.items) |sym| {
1645 if (symbolIsNull(sym)) continue;
1646
1647 const sym_name = self.getString(sym.n_strx);
14551648
14561649 for (self.archives.items) |archive| {
14571650 // Check if the entry exists in a static archive.
......@@ -1462,167 +1655,184 @@ fn resolveSymbols(self: *Zld) !void {
14621655 assert(offsets.items.len > 0);
14631656
14641657 const object = try archive.parseObject(offsets.items[0]);
1658 const object_id = @intCast(u16, self.objects.items.len);
14651659 try self.objects.append(self.allocator, object);
1466 try self.resolveSymbolsInObject(object);
1660 try self.resolveSymbolsInObject(object_id);
14671661
1468 sym_it = self.globals.iterator();
1469 break;
1662 continue :loop;
14701663 }
14711664 }
14721665
1473 // Put any globally defined regular symbol as local.
14741666 // Convert any tentative definition into a regular symbol and allocate
14751667 // text blocks for each tentative defintion.
1476 for (self.globals.values()) |symbol| {
1477 switch (symbol.payload) {
1478 .regular => |*reg| {
1479 reg.local_sym_index = @intCast(u32, self.locals.items.len);
1480 try self.locals.append(self.allocator, symbol);
1481 },
1482 .tentative => |tent| {
1483 const match: MatchingSection = blk: {
1484 if (self.common_section_index == null) {
1485 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1486 self.common_section_index = @intCast(u16, data_seg.sections.items.len);
1487 try data_seg.addSection(self.allocator, "__common", .{
1488 .flags = macho.S_ZEROFILL,
1489 });
1490 }
1491 break :blk .{
1492 .seg = self.data_segment_cmd_index.?,
1493 .sect = self.common_section_index.?,
1494 };
1495 };
1668 for (self.tentatives.items) |sym| {
1669 const sym_name = self.getString(sym.n_strx);
1670 const match: MatchingSection = blk: {
1671 if (self.common_section_index == null) {
1672 const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1673 self.common_section_index = @intCast(u16, data_seg.sections.items.len);
1674 try data_seg.addSection(self.allocator, "__common", .{
1675 .flags = macho.S_ZEROFILL,
1676 });
1677 }
1678 break :blk .{
1679 .seg = self.data_segment_cmd_index.?,
1680 .sect = self.common_section_index.?,
1681 };
1682 };
14961683
1497 const size = tent.size;
1498 const code = try self.allocator.alloc(u8, size);
1499 mem.set(u8, code, 0);
1500 const alignment = tent.alignment;
1501 const local_sym_index = @intCast(u32, self.locals.items.len);
1502
1503 symbol.payload = .{
1504 .regular = .{
1505 .linkage = .global,
1506 .segment_id = self.data_segment_cmd_index.?,
1507 .section_id = self.common_section_index.?,
1508 .local_sym_index = local_sym_index,
1509 },
1510 };
1511 try self.locals.append(self.allocator, symbol);
1512
1513 const block = try self.allocator.create(TextBlock);
1514 errdefer self.allocator.destroy(block);
1515
1516 block.* = TextBlock.init(self.allocator);
1517 block.local_sym_index = local_sym_index;
1518 block.code = code;
1519 block.size = size;
1520 block.alignment = alignment;
1521
1522 // Update target section's metadata
1523 // TODO should we update segment's size here too?
1524 // How does it tie with incremental space allocs?
1525 const tseg = &self.load_commands.items[match.seg].Segment;
1526 const tsect = &tseg.sections.items[match.sect];
1527 const new_alignment = math.max(tsect.@"align", block.alignment);
1528 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
1529 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
1530 tsect.size = new_size;
1531 tsect.@"align" = new_alignment;
1532
1533 if (self.blocks.getPtr(match)) |last| {
1534 last.*.next = block;
1535 block.prev = last.*;
1536 last.* = block;
1537 } else {
1538 try self.blocks.putNoClobber(self.allocator, match, block);
1539 }
1540 },
1541 else => {},
1684 const size = sym.n_value;
1685 const code = try self.allocator.alloc(u8, size);
1686 mem.set(u8, code, 0);
1687 const alignment = (sym.n_desc >> 8) & 0x0f;
1688
1689 const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable;
1690 const local_sym_index = @intCast(u32, self.locals.items.len);
1691 var nlist = macho.nlist_64{
1692 .n_strx = sym.n_strx,
1693 .n_type = macho.N_SECT,
1694 .n_sect = self.sectionId(match),
1695 .n_desc = 0,
1696 .n_value = 0,
1697 };
1698 try self.locals.append(self.allocator, nlist);
1699 const global_sym_index = @intCast(u32, self.globals.items.len);
1700 nlist.n_type |= macho.N_EXT;
1701 try self.globals.append(self.allocator, nlist);
1702 resolv.* = .{
1703 .where = .global,
1704 .where_index = global_sym_index,
1705 .local_sym_index = local_sym_index,
1706 };
1707
1708 const block = try self.allocator.create(TextBlock);
1709 errdefer self.allocator.destroy(block);
1710
1711 block.* = TextBlock.init(self.allocator);
1712 block.local_sym_index = local_sym_index;
1713 block.code = code;
1714 block.size = size;
1715 block.alignment = alignment;
1716
1717 // Update target section's metadata
1718 // TODO should we update segment's size here too?
1719 // How does it tie with incremental space allocs?
1720 const tseg = &self.load_commands.items[match.seg].Segment;
1721 const tsect = &tseg.sections.items[match.sect];
1722 const new_alignment = math.max(tsect.@"align", block.alignment);
1723 const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
1724 const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
1725 tsect.size = new_size;
1726 tsect.@"align" = new_alignment;
1727
1728 if (self.blocks.getPtr(match)) |last| {
1729 last.*.next = block;
1730 block.prev = last.*;
1731 last.* = block;
1732 } else {
1733 try self.blocks.putNoClobber(self.allocator, match, block);
15421734 }
15431735 }
15441736
15451737 // Third pass, resolve symbols in dynamic libraries.
15461738 {
15471739 // Put dyld_stub_binder as an undefined special symbol.
1548 const symbol = try self.allocator.create(Symbol);
1549 symbol.* = .{
1550 .strx = try self.makeString("dyld_stub_binder"),
1551 .payload = .{ .undef = .{} },
1552 };
1553 const index = @intCast(u32, self.got_entries.items.len);
1554 symbol.got_index = index;
1555 try self.got_entries.append(self.allocator, symbol);
1556 const alloc_name = try self.allocator.dupe(u8, "dyld_stub_binder");
1557 try self.globals.putNoClobber(self.allocator, alloc_name, symbol);
1740 const undef_sym_index = @intCast(u32, self.undefs.items.len);
1741 try self.undefs.append(self.allocator, .{
1742 .n_strx = try self.makeString("dyld_stub_binder"),
1743 .n_type = macho.N_UNDF,
1744 .n_sect = 0,
1745 .n_desc = 0,
1746 .n_value = 0,
1747 });
1748 try self.symbol_resolver.putNoClobber(self.allocator, try self.allocator.dupe(u8, "dyld_stub_binder"), .{
1749 .where = .undef,
1750 .where_index = undef_sym_index,
1751 });
15581752 }
15591753
15601754 var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
15611755 defer referenced.deinit();
15621756
1563 loop: for (self.globals.keys()) |sym_name| {
1564 const symbol = self.globals.get(sym_name).?;
1565 if (symbol.payload != .undef) continue;
1757 loop: for (self.undefs.items) |sym| {
1758 if (symbolIsNull(sym)) continue;
15661759
1760 const sym_name = self.getString(sym.n_strx);
15671761 for (self.dylibs.items) |dylib| {
15681762 if (!dylib.symbols.contains(sym_name)) continue;
15691763
1570 try referenced.put(dylib, {});
1571 const index = @intCast(u32, self.imports.items.len);
1572 symbol.payload = .{
1573 .proxy = .{
1574 .file = dylib,
1575 .local_sym_index = index,
1576 },
1764 if (!referenced.contains(dylib)) {
1765 // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
1766 dylib.ordinal = self.next_dylib_ordinal;
1767 const dylib_id = dylib.id orelse unreachable;
1768 var dylib_cmd = try createLoadDylibCommand(
1769 self.allocator,
1770 dylib_id.name,
1771 dylib_id.timestamp,
1772 dylib_id.current_version,
1773 dylib_id.compatibility_version,
1774 );
1775 errdefer dylib_cmd.deinit(self.allocator);
1776 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
1777 self.next_dylib_ordinal += 1;
1778 try referenced.putNoClobber(dylib, {});
1779 }
1780
1781 const resolv = self.symbol_resolver.getPtr(sym_name) orelse unreachable;
1782 const undef = &self.undefs.items[resolv.where_index];
1783 const import_sym_index = @intCast(u32, self.imports.items.len);
1784 try self.imports.append(self.allocator, .{
1785 .n_strx = undef.n_strx,
1786 .n_type = macho.N_UNDF | macho.N_EXT,
1787 .n_sect = 0,
1788 .n_desc = (dylib.ordinal.? * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
1789 .n_value = 0,
1790 });
1791 resolv.* = .{
1792 .where = .import,
1793 .where_index = import_sym_index,
15771794 };
1578 try self.imports.append(self.allocator, symbol);
1795 undef.* = .{
1796 .n_strx = 0,
1797 .n_type = macho.N_UNDF,
1798 .n_sect = 0,
1799 .n_desc = 0,
1800 .n_value = 0,
1801 };
1802
15791803 continue :loop;
15801804 }
15811805 }
15821806
1583 // Add LC_LOAD_DYLIB load command for each referenced dylib/stub.
1584 var it = referenced.iterator();
1585 while (it.next()) |entry| {
1586 const dylib = entry.key_ptr.*;
1587 dylib.ordinal = self.next_dylib_ordinal;
1588 const dylib_id = dylib.id orelse unreachable;
1589 var dylib_cmd = try createLoadDylibCommand(
1590 self.allocator,
1591 dylib_id.name,
1592 dylib_id.timestamp,
1593 dylib_id.current_version,
1594 dylib_id.compatibility_version,
1595 );
1596 errdefer dylib_cmd.deinit(self.allocator);
1597 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
1598 self.next_dylib_ordinal += 1;
1599 }
1600
16011807 // Fourth pass, handle synthetic symbols and flag any undefined references.
1602 if (self.globals.get("___dso_handle")) |symbol| {
1603 if (symbol.payload == .undef) {
1604 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1605 symbol.payload = .{
1606 .regular = .{
1607 .linkage = .translation_unit,
1608 .address = seg.inner.vmaddr,
1609 .weak_ref = true,
1610 .local_sym_index = @intCast(u32, self.locals.items.len),
1611 },
1612 };
1613 try self.locals.append(self.allocator, symbol);
1614 }
1808 if (self.symbol_resolver.getPtr("___dso_handle")) |resolv| blk: {
1809 if (resolv.where != .undef) break :blk;
1810
1811 const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1812 const undef = &self.undefs.items[resolv.where_index];
1813 const global_sym_index = @intCast(u32, self.globals.items.len);
1814 try self.globals.append(self.allocator, .{
1815 .n_strx = undef.n_strx,
1816 .n_type = macho.N_PEXT | macho.N_EXT | macho.N_SECT,
1817 .n_sect = 0,
1818 .n_desc = macho.N_WEAK_DEF,
1819 .n_value = seg.inner.vmaddr,
1820 });
1821 resolv.* = .{
1822 .where = .global,
1823 .where_index = global_sym_index,
1824 };
16151825 }
16161826
16171827 var has_undefined = false;
1618 for (self.globals.keys()) |sym_name| {
1619 const symbol = self.globals.get(sym_name).?;
1620 if (symbol.payload != .undef) continue;
1828 for (self.undefs.items) |sym| {
1829 if (symbolIsNull(sym)) continue;
1830
1831 const sym_name = self.getString(sym.n_strx);
1832 const resolv = self.symbol_resolver.get(sym_name) orelse unreachable;
16211833
16221834 log.err("undefined reference to symbol '{s}'", .{sym_name});
1623 if (symbol.payload.undef.file) |file| {
1624 log.err(" | referenced in {s}", .{file.name.?});
1625 }
1835 log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name.?});
16261836 has_undefined = true;
16271837 }
16281838
......@@ -2776,3 +2986,82 @@ pub fn getString(self: *Zld, off: u32) []const u8 {
27762986 assert(off < self.strtab.items.len);
27772987 return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
27782988}
2989
2990fn localSymIndex(self: Zld, object_id: u16, orig_id: u32) ?u32 {
2991 const mapping = self.object_mapping.get(object_id) orelse return null;
2992 const local_sym_index = mapping[orig_id];
2993 if (local_sym_index == 0) {
2994 return null;
2995 }
2996 return local_sym_index;
2997}
2998
2999pub fn symbolIsStab(sym: macho.nlist_64) bool {
3000 return (macho.N_STAB & sym.n_type) != 0;
3001}
3002
3003pub fn symbolIsPext(sym: macho.nlist_64) bool {
3004 return (macho.N_PEXT & sym.n_type) != 0;
3005}
3006
3007pub fn symbolIsExt(sym: macho.nlist_64) bool {
3008 return (macho.N_EXT & sym.n_type) != 0;
3009}
3010
3011pub fn symbolIsSect(sym: macho.nlist_64) bool {
3012 const type_ = macho.N_TYPE & sym.n_type;
3013 return type_ == macho.N_SECT;
3014}
3015
3016pub fn symbolIsUndf(sym: macho.nlist_64) bool {
3017 const type_ = macho.N_TYPE & sym.n_type;
3018 return type_ == macho.N_UNDF;
3019}
3020
3021pub fn symbolIsIndr(sym: macho.nlist_64) bool {
3022 const type_ = macho.N_TYPE & sym.n_type;
3023 return type_ == macho.N_INDR;
3024}
3025
3026pub fn symbolIsAbs(sym: macho.nlist_64) bool {
3027 const type_ = macho.N_TYPE & sym.n_type;
3028 return type_ == macho.N_ABS;
3029}
3030
3031pub fn symbolIsWeakDef(sym: macho.nlist_64) bool {
3032 return (sym.n_desc & macho.N_WEAK_DEF) != 0;
3033}
3034
3035pub fn symbolIsWeakRef(sym: macho.nlist_64) bool {
3036 return (sym.n_desc & macho.N_WEAK_REF) != 0;
3037}
3038
3039pub fn symbolIsTentative(sym: macho.nlist_64) bool {
3040 if (!symbolIsUndf(sym)) return false;
3041 return sym.n_value != 0;
3042}
3043
3044pub fn symbolIsNull(sym: macho.nlist_64) bool {
3045 return sym.n_value == 0 and sym.n_desc == 0 and sym.n_type == 0 and sym.n_strx == 0 and sym.n_sect == 0;
3046}
3047
3048pub fn symbolIsTemp(self: Zld, sym: macho.nlist_64) bool {
3049 if (!symbolIsSect(sym)) return false;
3050 if (symbolIsExt(sym)) return false;
3051 const sym_name = self.getString(sym.n_strx);
3052 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
3053}
3054
3055pub fn sectionId(self: Zld, match: MatchingSection) u8 {
3056 // TODO there might be a more generic way of doing this.
3057 var section: u8 = 0;
3058 for (self.load_commands.items) |cmd, cmd_id| {
3059 if (cmd != .Segment) break;
3060 if (cmd_id == match.seg) {
3061 section += @intCast(u8, match.sect) + 1;
3062 break;
3063 }
3064 section += @intCast(u8, cmd.Segment.sections.items.len);
3065 }
3066 return section;
3067}