authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-22 14:03:45+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-29 11:39:34+02:00
logda9e7e498af411de477a7b59e72ef763b22f4f5a
tree4dee3daca757de84f1be423d44096ec287e4d789
parent85f2df5050fd6cec25bd251159795bb0fa0f8731

macho: unify Atom concept between drivers


10 files changed, 980 insertions(+), 1034 deletions(-)

CMakeLists.txt-1
......@@ -588,7 +588,6 @@ set(ZIG_STAGE2_SOURCES
588588 "${CMAKE_SOURCE_DIR}/src/link/MachO/Relocation.zig"
589589 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
590590 "${CMAKE_SOURCE_DIR}/src/link/MachO/UnwindInfo.zig"
591 "${CMAKE_SOURCE_DIR}/src/link/MachO/ZldAtom.zig"
592591 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/bind.zig"
593592 "${CMAKE_SOURCE_DIR}/src/link/MachO/dyld_info/Rebase.zig"
594593 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
src/link/MachO.zig+3
......@@ -1597,8 +1597,11 @@ pub fn createAtom(self: *MachO) !Atom.Index {
15971597 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
15981598 atom.* = .{
15991599 .sym_index = sym_index,
1600 .inner_sym_index = 0,
1601 .inner_nsyms_trailing = 0,
16001602 .file = 0,
16011603 .size = 0,
1604 .alignment = 0,
16021605 .prev_index = null,
16031606 .next_index = null,
16041607 };
src/link/MachO/Atom.zig+953-14
......@@ -16,12 +16,13 @@ const Arch = std.Target.Cpu.Arch;
1616const MachO = @import("../MachO.zig");
1717pub const Relocation = @import("Relocation.zig");
1818const SymbolWithLoc = MachO.SymbolWithLoc;
19const Zld = @import("zld.zig").Zld;
1920
2021/// Each Atom always gets a symbol with the fully qualified name.
2122/// The symbol can reside in any object file context structure in `symtab` array
2223/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
2324/// a stub trampoline, it can be found in the linkers `locals` arraylist.
24/// If this field is 0, it means the codegen size = 0 and there is no symbol or
25/// If this field is 0 and file is 0, it means the codegen size = 0 and there is no symbol or
2526/// offset table entry.
2627sym_index: u32,
2728
......@@ -31,11 +32,24 @@ sym_index: u32,
3132/// the field directly.
3233file: u32,
3334
35/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
36/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
37/// this Atom contains any additional symbol references that fall within this Atom's
38/// address range. These could for example be an alias symbol which can be used
39/// internally by the relocation records, or if the Object file couldn't be split
40/// into subsections, this Atom may encompass an entire input section.
41inner_sym_index: u32,
42inner_nsyms_trailing: u32,
43
3444/// Size and alignment of this atom
3545/// Unlike in Elf, we need to store the size of this symbol as part of
3646/// the atom since macho.nlist_64 lacks this information.
3747size: u64,
3848
49/// Alignment of this atom as a power of 2.
50/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
51alignment: u32,
52
3953/// Points to the previous and next neighbours
4054/// TODO use the same trick as with symbols: reserve index 0 as null atom
4155next_index: ?Index,
......@@ -48,13 +62,15 @@ pub const Binding = struct {
4862 offset: u64,
4963};
5064
51pub const SymbolAtOffset = struct {
52 sym_index: u32,
53 offset: u64,
54};
65/// Returns `null` if the Atom is a synthetic Atom.
66/// Otherwise, returns an index into an array of Objects.
67pub fn getFile(self: Atom) ?u32 {
68 if (self.file == 0) return null;
69 return self.file - 1;
70}
5571
5672pub fn getSymbolIndex(self: Atom) ?u32 {
57 if (self.sym_index == 0) return null;
73 if (self.getFile() == null and self.sym_index == 0) return null;
5874 return self.sym_index;
5975}
6076
......@@ -66,10 +82,7 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
6682/// Returns pointer-to-symbol referencing this atom.
6783pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
6884 const sym_index = self.getSymbolIndex().?;
69 return macho_file.getSymbolPtr(.{
70 .sym_index = sym_index,
71 .file = self.file,
72 });
85 return macho_file.getSymbolPtr(.{ .sym_index = sym_index, .file = self.file });
7386}
7487
7588pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
......@@ -80,10 +93,7 @@ pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
8093/// Returns the name of this atom.
8194pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
8295 const sym_index = self.getSymbolIndex().?;
83 return macho_file.getSymbolName(.{
84 .sym_index = sym_index,
85 .file = self.file,
86 });
96 return macho_file.getSymbolName(.{ .sym_index = sym_index, .file = self.file });
8797}
8898
8999/// Returns how much room there is to grow in virtual address space.
......@@ -182,3 +192,932 @@ pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void {
182192 var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index);
183193 if (removed_bindings) |*bindings| bindings.value.deinit(gpa);
184194}
195
196const InnerSymIterator = struct {
197 sym_index: u32,
198 nsyms: u32,
199 file: u32,
200 pos: u32 = 0,
201
202 pub fn next(it: *@This()) ?SymbolWithLoc {
203 if (it.pos == it.nsyms) return null;
204 const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file };
205 it.pos += 1;
206 return res;
207 }
208};
209
210/// Returns an iterator over potentially contained symbols.
211/// Panics when called on a synthetic Atom.
212pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {
213 const atom = zld.getAtom(atom_index);
214 assert(atom.getFile() != null);
215 return .{
216 .sym_index = atom.inner_sym_index,
217 .nsyms = atom.inner_nsyms_trailing,
218 .file = atom.file,
219 };
220}
221
222/// Returns a section alias symbol if one is defined.
223/// An alias symbol is used to represent the start of an input section
224/// if there were no symbols defined within that range.
225/// Alias symbols are only used on x86_64.
226pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {
227 const atom = zld.getAtom(atom_index);
228 assert(atom.getFile() != null);
229
230 const object = zld.objects.items[atom.getFile().?];
231 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
232 const ntotal = @as(u32, @intCast(object.symtab.len));
233 var sym_index: u32 = nbase;
234 while (sym_index < ntotal) : (sym_index += 1) {
235 if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
236 if (other_atom_index == atom_index) return SymbolWithLoc{
237 .sym_index = sym_index,
238 .file = atom.file,
239 };
240 }
241 }
242 return null;
243}
244
245/// Given an index into a contained symbol within, calculates an offset wrt
246/// the start of this Atom.
247pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {
248 const atom = zld.getAtom(atom_index);
249 assert(atom.getFile() != null);
250
251 if (atom.sym_index == sym_index) return 0;
252
253 const object = zld.objects.items[atom.getFile().?];
254 const source_sym = object.getSourceSymbol(sym_index).?;
255 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
256 sym.n_value
257 else blk: {
258 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
259 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
260 const source_sect = object.getSourceSection(sect_id);
261 break :blk source_sect.addr;
262 };
263 return source_sym.n_value - base_addr;
264}
265
266pub fn scanAtomRelocs(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
267 const arch = zld.options.target.cpu.arch;
268 const atom = zld.getAtom(atom_index);
269 assert(atom.getFile() != null); // synthetic atoms do not have relocs
270
271 return switch (arch) {
272 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
273 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
274 else => unreachable,
275 };
276}
277
278const RelocContext = struct {
279 base_addr: i64 = 0,
280 base_offset: i32 = 0,
281};
282
283pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {
284 const atom = zld.getAtom(atom_index);
285 assert(atom.getFile() != null); // synthetic atoms do not have relocs
286
287 const object = zld.objects.items[atom.getFile().?];
288 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
289 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
290 return .{
291 .base_addr = @as(i64, @intCast(source_sect.addr)),
292 .base_offset = @as(i32, @intCast(source_sym.n_value - source_sect.addr)),
293 };
294 }
295 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
296 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
297 const source_sect = object.getSourceSection(sect_id);
298 return .{
299 .base_addr = @as(i64, @intCast(source_sect.addr)),
300 .base_offset = 0,
301 };
302}
303
304pub fn parseRelocTarget(zld: *Zld, ctx: struct {
305 object_id: u32,
306 rel: macho.relocation_info,
307 code: []const u8,
308 base_addr: i64 = 0,
309 base_offset: i32 = 0,
310}) SymbolWithLoc {
311 const tracy = trace(@src());
312 defer tracy.end();
313
314 const object = &zld.objects.items[ctx.object_id];
315 log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
316
317 const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
318 const sect_id = @as(u8, @intCast(ctx.rel.r_symbolnum - 1));
319 const rel_offset = @as(u32, @intCast(ctx.rel.r_address - ctx.base_offset));
320
321 const address_in_section = if (ctx.rel.r_pcrel == 0) blk: {
322 break :blk if (ctx.rel.r_length == 3)
323 mem.readIntLittle(u64, ctx.code[rel_offset..][0..8])
324 else
325 mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
326 } else blk: {
327 assert(zld.options.target.cpu.arch == .x86_64);
328 const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
329 .X86_64_RELOC_SIGNED => 0,
330 .X86_64_RELOC_SIGNED_1 => 1,
331 .X86_64_RELOC_SIGNED_2 => 2,
332 .X86_64_RELOC_SIGNED_4 => 4,
333 else => unreachable,
334 };
335 const addend = mem.readIntLittle(i32, ctx.code[rel_offset..][0..4]);
336 const target_address = @as(i64, @intCast(ctx.base_addr)) + ctx.rel.r_address + 4 + correction + addend;
337 break :blk @as(u64, @intCast(target_address));
338 };
339
340 // Find containing atom
341 log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id });
342 break :sym_index object.getSymbolByAddress(address_in_section, sect_id);
343 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
344
345 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
346 const sym = zld.getSymbol(sym_loc);
347 const target = if (sym.sect() and !sym.ext())
348 sym_loc
349 else if (object.getGlobal(sym_index)) |global_index|
350 zld.globals.items[global_index]
351 else
352 sym_loc;
353 log.debug(" | target %{d} ('{s}') in object({?d})", .{
354 target.sym_index,
355 zld.getSymbolName(target),
356 target.getFile(),
357 });
358 return target;
359}
360
361pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?Index {
362 if (is_via_got) {
363 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
364 }
365 if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
366 if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
367
368 if (target.getFile() == null) {
369 const target_sym_name = zld.getSymbolName(target);
370 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
371 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
372
373 unreachable; // referenced symbol not found
374 }
375
376 const object = zld.objects.items[target.getFile().?];
377 return object.getAtomIndexForSymbol(target.sym_index);
378}
379
380fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
381 for (relocs) |rel| {
382 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
383
384 switch (rel_type) {
385 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
386 else => {},
387 }
388
389 if (rel.r_extern == 0) continue;
390
391 const atom = zld.getAtom(atom_index);
392 const object = &zld.objects.items[atom.getFile().?];
393 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
394 const sym_loc = SymbolWithLoc{
395 .sym_index = sym_index,
396 .file = atom.file,
397 };
398
399 const target = if (object.getGlobal(sym_index)) |global_index|
400 zld.globals.items[global_index]
401 else
402 sym_loc;
403
404 switch (rel_type) {
405 .ARM64_RELOC_BRANCH26 => {
406 // TODO rewrite relocation
407 try addStub(zld, target);
408 },
409 .ARM64_RELOC_GOT_LOAD_PAGE21,
410 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
411 .ARM64_RELOC_POINTER_TO_GOT,
412 => {
413 // TODO rewrite relocation
414 try addGotEntry(zld, target);
415 },
416 .ARM64_RELOC_TLVP_LOAD_PAGE21,
417 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
418 => {
419 try addTlvPtrEntry(zld, target);
420 },
421 else => {},
422 }
423 }
424}
425
426fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
427 for (relocs) |rel| {
428 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
429
430 switch (rel_type) {
431 .X86_64_RELOC_SUBTRACTOR => continue,
432 else => {},
433 }
434
435 if (rel.r_extern == 0) continue;
436
437 const atom = zld.getAtom(atom_index);
438 const object = &zld.objects.items[atom.getFile().?];
439 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
440 const sym_loc = SymbolWithLoc{
441 .sym_index = sym_index,
442 .file = atom.file,
443 };
444
445 const target = if (object.getGlobal(sym_index)) |global_index|
446 zld.globals.items[global_index]
447 else
448 sym_loc;
449
450 switch (rel_type) {
451 .X86_64_RELOC_BRANCH => {
452 // TODO rewrite relocation
453 try addStub(zld, target);
454 },
455 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
456 // TODO rewrite relocation
457 try addGotEntry(zld, target);
458 },
459 .X86_64_RELOC_TLV => {
460 try addTlvPtrEntry(zld, target);
461 },
462 else => {},
463 }
464 }
465}
466
467fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
468 const target_sym = zld.getSymbol(target);
469 if (!target_sym.undf()) return;
470 if (zld.tlv_ptr_table.contains(target)) return;
471
472 const gpa = zld.gpa;
473 const atom_index = try zld.createTlvPtrAtom();
474 const tlv_ptr_index = @as(u32, @intCast(zld.tlv_ptr_entries.items.len));
475 try zld.tlv_ptr_entries.append(gpa, .{
476 .target = target,
477 .atom_index = atom_index,
478 });
479 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
480}
481
482pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
483 if (zld.got_table.contains(target)) return;
484 const gpa = zld.gpa;
485 const atom_index = try zld.createGotAtom();
486 const got_index = @as(u32, @intCast(zld.got_entries.items.len));
487 try zld.got_entries.append(gpa, .{
488 .target = target,
489 .atom_index = atom_index,
490 });
491 try zld.got_table.putNoClobber(gpa, target, got_index);
492}
493
494pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
495 const target_sym = zld.getSymbol(target);
496 if (!target_sym.undf()) return;
497 if (zld.stubs_table.contains(target)) return;
498
499 const gpa = zld.gpa;
500 _ = try zld.createStubHelperAtom();
501 _ = try zld.createLazyPointerAtom();
502 const atom_index = try zld.createStubAtom();
503 const stubs_index = @as(u32, @intCast(zld.stubs.items.len));
504 try zld.stubs.append(gpa, .{
505 .target = target,
506 .atom_index = atom_index,
507 });
508 try zld.stubs_table.putNoClobber(gpa, target, stubs_index);
509}
510
511pub fn resolveRelocs(
512 zld: *Zld,
513 atom_index: Index,
514 atom_code: []u8,
515 atom_relocs: []align(1) const macho.relocation_info,
516) !void {
517 const arch = zld.options.target.cpu.arch;
518 const atom = zld.getAtom(atom_index);
519 assert(atom.getFile() != null); // synthetic atoms do not have relocs
520
521 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
522 atom.sym_index,
523 zld.getSymbolName(atom.getSymbolWithLoc()),
524 });
525
526 const ctx = getRelocContext(zld, atom_index);
527
528 return switch (arch) {
529 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
530 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
531 else => unreachable,
532 };
533}
534
535pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
536 const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
537 // If there is no atom for target, we still need to check for special, atom-less
538 // symbols such as `___dso_handle`.
539 const target_name = zld.getSymbolName(target);
540 const atomless_sym = zld.getSymbol(target);
541 log.debug(" | atomless target '{s}'", .{target_name});
542 return atomless_sym.n_value;
543 };
544 const target_atom = zld.getAtom(target_atom_index);
545 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
546 target_atom.sym_index,
547 zld.getSymbolName(target_atom.getSymbolWithLoc()),
548 target_atom.getFile(),
549 });
550
551 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
552 assert(target_sym.n_desc != @import("zld.zig").N_DEAD);
553
554 // If `target` is contained within the target atom, pull its address value.
555 const offset = if (target_atom.getFile() != null) blk: {
556 const object = zld.objects.items[target_atom.getFile().?];
557 break :blk if (object.getSourceSymbol(target.sym_index)) |_|
558 Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
559 else
560 0; // section alias
561 } else 0;
562 const base_address: u64 = if (is_tlv) base_address: {
563 // For TLV relocations, the value specified as a relocation is the displacement from the
564 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
565 // defined TLV template init section in the following order:
566 // * wrt to __thread_data if defined, then
567 // * wrt to __thread_bss
568 const sect_id: u16 = sect_id: {
569 if (zld.getSectionByName("__DATA", "__thread_data")) |i| {
570 break :sect_id i;
571 } else if (zld.getSectionByName("__DATA", "__thread_bss")) |i| {
572 break :sect_id i;
573 } else {
574 log.err("threadlocal variables present but no initializer sections found", .{});
575 log.err(" __thread_data not found", .{});
576 log.err(" __thread_bss not found", .{});
577 return error.FailedToResolveRelocationTarget;
578 }
579 };
580 break :base_address zld.sections.items(.header)[sect_id].addr;
581 } else 0;
582 return target_sym.n_value + offset - base_address;
583}
584
585fn resolveRelocsArm64(
586 zld: *Zld,
587 atom_index: Index,
588 atom_code: []u8,
589 atom_relocs: []align(1) const macho.relocation_info,
590 context: RelocContext,
591) !void {
592 const atom = zld.getAtom(atom_index);
593 const object = zld.objects.items[atom.getFile().?];
594
595 var addend: ?i64 = null;
596 var subtractor: ?SymbolWithLoc = null;
597
598 for (atom_relocs) |rel| {
599 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
600
601 switch (rel_type) {
602 .ARM64_RELOC_ADDEND => {
603 assert(addend == null);
604
605 log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
606
607 addend = rel.r_symbolnum;
608 continue;
609 },
610 .ARM64_RELOC_SUBTRACTOR => {
611 assert(subtractor == null);
612
613 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
614 @tagName(rel_type),
615 rel.r_address,
616 rel.r_symbolnum,
617 atom.getFile(),
618 });
619
620 subtractor = parseRelocTarget(zld, .{
621 .object_id = atom.getFile().?,
622 .rel = rel,
623 .code = atom_code,
624 .base_addr = context.base_addr,
625 .base_offset = context.base_offset,
626 });
627 continue;
628 },
629 else => {},
630 }
631
632 const target = parseRelocTarget(zld, .{
633 .object_id = atom.getFile().?,
634 .rel = rel,
635 .code = atom_code,
636 .base_addr = context.base_addr,
637 .base_offset = context.base_offset,
638 });
639 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
640
641 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
642 @tagName(rel_type),
643 rel.r_address,
644 target.sym_index,
645 zld.getSymbolName(target),
646 target.getFile(),
647 });
648
649 const source_addr = blk: {
650 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
651 break :blk source_sym.n_value + rel_offset;
652 };
653 const is_via_got = relocRequiresGot(zld, rel);
654 const is_tlv = is_tlv: {
655 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
656 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
657 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
658 };
659 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
660
661 log.debug(" | source_addr = 0x{x}", .{source_addr});
662
663 switch (rel_type) {
664 .ARM64_RELOC_BRANCH26 => {
665 const actual_target = if (zld.getStubsAtomIndexForSymbol(target)) |stub_atom_index| inner: {
666 const stub_atom = zld.getAtom(stub_atom_index);
667 break :inner stub_atom.getSymbolWithLoc();
668 } else target;
669 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
670 zld.getSymbolName(atom.getSymbolWithLoc()),
671 atom.getFile(),
672 zld.getSymbolName(target),
673 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
674 });
675
676 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
677 source_addr,
678 zld.getSymbol(actual_target).n_value,
679 )) |disp| blk: {
680 log.debug(" | target_addr = 0x{x}", .{zld.getSymbol(actual_target).n_value});
681 break :blk disp;
682 } else |_| blk: {
683 const thunk_index = zld.thunk_table.get(atom_index).?;
684 const thunk = zld.thunks.items[thunk_index];
685 const thunk_sym = zld.getSymbol(thunk.getTrampolineForSymbol(
686 zld,
687 actual_target,
688 ).?);
689 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
690 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
691 };
692
693 const code = atom_code[rel_offset..][0..4];
694 var inst = aarch64.Instruction{
695 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
696 aarch64.Instruction,
697 aarch64.Instruction.unconditional_branch_immediate,
698 ), code),
699 };
700 inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(displacement >> 2))));
701 mem.writeIntLittle(u32, code, inst.toU32());
702 },
703
704 .ARM64_RELOC_PAGE21,
705 .ARM64_RELOC_GOT_LOAD_PAGE21,
706 .ARM64_RELOC_TLVP_LOAD_PAGE21,
707 => {
708 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
709
710 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
711
712 const pages = @as(u21, @bitCast(Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)));
713 const code = atom_code[rel_offset..][0..4];
714 var inst = aarch64.Instruction{
715 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
716 aarch64.Instruction,
717 aarch64.Instruction.pc_relative_address,
718 ), code),
719 };
720 inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
721 inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
722 mem.writeIntLittle(u32, code, inst.toU32());
723 addend = null;
724 },
725
726 .ARM64_RELOC_PAGEOFF12 => {
727 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
728
729 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
730
731 const code = atom_code[rel_offset..][0..4];
732 if (Relocation.isArithmeticOp(code)) {
733 const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
734 var inst = aarch64.Instruction{
735 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
736 aarch64.Instruction,
737 aarch64.Instruction.add_subtract_immediate,
738 ), code),
739 };
740 inst.add_subtract_immediate.imm12 = off;
741 mem.writeIntLittle(u32, code, inst.toU32());
742 } else {
743 var inst = aarch64.Instruction{
744 .load_store_register = mem.bytesToValue(meta.TagPayload(
745 aarch64.Instruction,
746 aarch64.Instruction.load_store_register,
747 ), code),
748 };
749 const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
750 0 => if (inst.load_store_register.v == 1)
751 Relocation.PageOffsetInstKind.load_store_128
752 else
753 Relocation.PageOffsetInstKind.load_store_8,
754 1 => .load_store_16,
755 2 => .load_store_32,
756 3 => .load_store_64,
757 });
758 inst.load_store_register.offset = off;
759 mem.writeIntLittle(u32, code, inst.toU32());
760 }
761 addend = null;
762 },
763
764 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
765 const code = atom_code[rel_offset..][0..4];
766 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
767
768 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
769
770 const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
771 var inst: aarch64.Instruction = .{
772 .load_store_register = mem.bytesToValue(meta.TagPayload(
773 aarch64.Instruction,
774 aarch64.Instruction.load_store_register,
775 ), code),
776 };
777 inst.load_store_register.offset = off;
778 mem.writeIntLittle(u32, code, inst.toU32());
779 addend = null;
780 },
781
782 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
783 const code = atom_code[rel_offset..][0..4];
784 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
785
786 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
787
788 const RegInfo = struct {
789 rd: u5,
790 rn: u5,
791 size: u2,
792 };
793 const reg_info: RegInfo = blk: {
794 if (Relocation.isArithmeticOp(code)) {
795 const inst = mem.bytesToValue(meta.TagPayload(
796 aarch64.Instruction,
797 aarch64.Instruction.add_subtract_immediate,
798 ), code);
799 break :blk .{
800 .rd = inst.rd,
801 .rn = inst.rn,
802 .size = inst.sf,
803 };
804 } else {
805 const inst = mem.bytesToValue(meta.TagPayload(
806 aarch64.Instruction,
807 aarch64.Instruction.load_store_register,
808 ), code);
809 break :blk .{
810 .rd = inst.rt,
811 .rn = inst.rn,
812 .size = inst.size,
813 };
814 }
815 };
816
817 var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
818 .load_store_register = .{
819 .rt = reg_info.rd,
820 .rn = reg_info.rn,
821 .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
822 .opc = 0b01,
823 .op1 = 0b01,
824 .v = 0,
825 .size = reg_info.size,
826 },
827 } else aarch64.Instruction{
828 .add_subtract_immediate = .{
829 .rd = reg_info.rd,
830 .rn = reg_info.rn,
831 .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
832 .sh = 0,
833 .s = 0,
834 .op = 0,
835 .sf = @as(u1, @truncate(reg_info.size)),
836 },
837 };
838 mem.writeIntLittle(u32, code, inst.toU32());
839 addend = null;
840 },
841
842 .ARM64_RELOC_POINTER_TO_GOT => {
843 log.debug(" | target_addr = 0x{x}", .{target_addr});
844 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
845 return error.Overflow;
846 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @bitCast(result)));
847 },
848
849 .ARM64_RELOC_UNSIGNED => {
850 var ptr_addend = if (rel.r_length == 3)
851 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
852 else
853 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
854
855 if (rel.r_extern == 0) {
856 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
857 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
858 else
859 object.source_address_lookup[target.sym_index];
860 ptr_addend -= base_addr;
861 }
862
863 const result = blk: {
864 if (subtractor) |sub| {
865 const sym = zld.getSymbol(sub);
866 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
867 } else {
868 break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
869 }
870 };
871 log.debug(" | target_addr = 0x{x}", .{result});
872
873 if (rel.r_length == 3) {
874 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
875 } else {
876 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
877 }
878
879 subtractor = null;
880 },
881
882 .ARM64_RELOC_ADDEND => unreachable,
883 .ARM64_RELOC_SUBTRACTOR => unreachable,
884 }
885 }
886}
887
888fn resolveRelocsX86(
889 zld: *Zld,
890 atom_index: Index,
891 atom_code: []u8,
892 atom_relocs: []align(1) const macho.relocation_info,
893 context: RelocContext,
894) !void {
895 const atom = zld.getAtom(atom_index);
896 const object = zld.objects.items[atom.getFile().?];
897
898 var subtractor: ?SymbolWithLoc = null;
899
900 for (atom_relocs) |rel| {
901 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
902
903 switch (rel_type) {
904 .X86_64_RELOC_SUBTRACTOR => {
905 assert(subtractor == null);
906
907 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
908 @tagName(rel_type),
909 rel.r_address,
910 rel.r_symbolnum,
911 atom.getFile(),
912 });
913
914 subtractor = parseRelocTarget(zld, .{
915 .object_id = atom.getFile().?,
916 .rel = rel,
917 .code = atom_code,
918 .base_addr = context.base_addr,
919 .base_offset = context.base_offset,
920 });
921 continue;
922 },
923 else => {},
924 }
925
926 const target = parseRelocTarget(zld, .{
927 .object_id = atom.getFile().?,
928 .rel = rel,
929 .code = atom_code,
930 .base_addr = context.base_addr,
931 .base_offset = context.base_offset,
932 });
933 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
934
935 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
936 @tagName(rel_type),
937 rel.r_address,
938 target.sym_index,
939 zld.getSymbolName(target),
940 target.getFile(),
941 });
942
943 const source_addr = blk: {
944 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
945 break :blk source_sym.n_value + rel_offset;
946 };
947 const is_via_got = relocRequiresGot(zld, rel);
948 const is_tlv = is_tlv: {
949 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
950 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
951 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
952 };
953
954 log.debug(" | source_addr = 0x{x}", .{source_addr});
955
956 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
957
958 switch (rel_type) {
959 .X86_64_RELOC_BRANCH => {
960 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
961 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
962 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
963 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
964 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
965 },
966
967 .X86_64_RELOC_GOT,
968 .X86_64_RELOC_GOT_LOAD,
969 => {
970 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
971 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
972 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
973 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
974 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
975 },
976
977 .X86_64_RELOC_TLV => {
978 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
979 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
980 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
981 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
982
983 if (zld.tlv_ptr_table.get(target) == null) {
984 // We need to rewrite the opcode from movq to leaq.
985 atom_code[rel_offset - 2] = 0x8d;
986 }
987
988 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
989 },
990
991 .X86_64_RELOC_SIGNED,
992 .X86_64_RELOC_SIGNED_1,
993 .X86_64_RELOC_SIGNED_2,
994 .X86_64_RELOC_SIGNED_4,
995 => {
996 const correction: u3 = switch (rel_type) {
997 .X86_64_RELOC_SIGNED => 0,
998 .X86_64_RELOC_SIGNED_1 => 1,
999 .X86_64_RELOC_SIGNED_2 => 2,
1000 .X86_64_RELOC_SIGNED_4 => 4,
1001 else => unreachable,
1002 };
1003 var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
1004
1005 if (rel.r_extern == 0) {
1006 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
1007 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
1008 else
1009 object.source_address_lookup[target.sym_index];
1010 addend += @as(i32, @intCast(@as(i64, @intCast(context.base_addr)) + rel.r_address + 4 -
1011 @as(i64, @intCast(base_addr))));
1012 }
1013
1014 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
1015
1016 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
1017
1018 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
1019 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
1020 },
1021
1022 .X86_64_RELOC_UNSIGNED => {
1023 var addend = if (rel.r_length == 3)
1024 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
1025 else
1026 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
1027
1028 if (rel.r_extern == 0) {
1029 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
1030 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
1031 else
1032 object.source_address_lookup[target.sym_index];
1033 addend -= base_addr;
1034 }
1035
1036 const result = blk: {
1037 if (subtractor) |sub| {
1038 const sym = zld.getSymbol(sub);
1039 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
1040 } else {
1041 break :blk @as(i64, @intCast(target_addr)) + addend;
1042 }
1043 };
1044 log.debug(" | target_addr = 0x{x}", .{result});
1045
1046 if (rel.r_length == 3) {
1047 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
1048 } else {
1049 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
1050 }
1051
1052 subtractor = null;
1053 },
1054
1055 .X86_64_RELOC_SUBTRACTOR => unreachable,
1056 }
1057 }
1058}
1059
1060pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {
1061 const atom = zld.getAtom(atom_index);
1062 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
1063 const object = zld.objects.items[atom.getFile().?];
1064 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
1065 // If there was no matching symbol present in the source symtab, this means
1066 // we are dealing with either an entire section, or part of it, but also
1067 // starting at the beginning.
1068 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
1069 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
1070 const source_sect = object.getSourceSection(sect_id);
1071 assert(!source_sect.isZerofill());
1072 const code = object.getSectionContents(source_sect);
1073 const code_len = @as(usize, @intCast(atom.size));
1074 return code[0..code_len];
1075 };
1076 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
1077 assert(!source_sect.isZerofill());
1078 const code = object.getSectionContents(source_sect);
1079 const offset = @as(usize, @intCast(source_sym.n_value - source_sect.addr));
1080 const code_len = @as(usize, @intCast(atom.size));
1081 return code[offset..][0..code_len];
1082}
1083
1084pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info {
1085 const atom = zld.getAtom(atom_index);
1086 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
1087 const object = zld.objects.items[atom.getFile().?];
1088 const cache = object.relocs_lookup[atom.sym_index];
1089
1090 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
1091 break :blk source_sym.n_sect - 1;
1092 } else blk: {
1093 // If there was no matching symbol present in the source symtab, this means
1094 // we are dealing with either an entire section, or part of it, but also
1095 // starting at the beginning.
1096 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
1097 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
1098 break :blk sect_id;
1099 };
1100 const source_sect = object.getSourceSection(source_sect_id);
1101 assert(!source_sect.isZerofill());
1102 const relocs = object.getRelocs(source_sect_id);
1103 return relocs[cache.start..][0..cache.len];
1104}
1105
1106pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
1107 switch (zld.options.target.cpu.arch) {
1108 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
1109 .ARM64_RELOC_GOT_LOAD_PAGE21,
1110 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1111 .ARM64_RELOC_POINTER_TO_GOT,
1112 => return true,
1113 else => return false,
1114 },
1115 .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
1116 .X86_64_RELOC_GOT,
1117 .X86_64_RELOC_GOT_LOAD,
1118 => return true,
1119 else => return false,
1120 },
1121 else => unreachable,
1122 }
1123}
src/link/MachO/Object.zig+1-1
......@@ -19,7 +19,7 @@ const sort = std.sort;
1919const trace = @import("../../tracy.zig").trace;
2020
2121const Allocator = mem.Allocator;
22const Atom = @import("ZldAtom.zig");
22const Atom = @import("Atom.zig");
2323const AtomIndex = @import("zld.zig").AtomIndex;
2424const DwarfInfo = @import("DwarfInfo.zig");
2525const LoadCommandIterator = macho.LoadCommandIterator;
src/link/MachO/UnwindInfo.zig+1-1
......@@ -12,7 +12,7 @@ const mem = std.mem;
1212const trace = @import("../../tracy.zig").trace;
1313
1414const Allocator = mem.Allocator;
15const Atom = @import("ZldAtom.zig");
15const Atom = @import("Atom.zig");
1616const AtomIndex = @import("zld.zig").AtomIndex;
1717const EhFrameRecord = eh_frame.EhFrameRecord;
1818const MachO = @import("../MachO.zig");
src/link/MachO/ZldAtom.zig deleted-1012
......@@ -1,1012 +0,0 @@
1//! An atom is a single smallest unit of measure that will get an
2//! allocated virtual memory address in the final linked image.
3//! For example, we parse each input section within an input relocatable
4//! object file into a set of atoms which are then laid out contiguously
5//! as they were defined in the input file.
6
7const Atom = @This();
8
9const std = @import("std");
10const build_options = @import("build_options");
11const aarch64 = @import("../../arch/aarch64/bits.zig");
12const assert = std.debug.assert;
13const log = std.log.scoped(.atom);
14const macho = std.macho;
15const math = std.math;
16const mem = std.mem;
17const meta = std.meta;
18const trace = @import("../../tracy.zig").trace;
19
20const Allocator = mem.Allocator;
21const Arch = std.Target.Cpu.Arch;
22const AtomIndex = @import("zld.zig").AtomIndex;
23const Object = @import("Object.zig");
24const Relocation = @import("Relocation.zig");
25const SymbolWithLoc = @import("../MachO.zig").SymbolWithLoc;
26const Zld = @import("zld.zig").Zld;
27
28/// Each Atom always gets a symbol with the fully qualified name.
29/// The symbol can reside in any object file context structure in `symtab` array
30/// (see `Object`), or if the symbol is a synthetic symbol such as a GOT cell or
31/// a stub trampoline, it can be found in the linkers `locals` arraylist.
32sym_index: u32,
33
34/// 0 means an Atom is a synthetic Atom such as a GOT cell defined by the linker.
35/// Otherwise, it is the index into appropriate object file (indexing from 1).
36/// Prefer using `getFile()` helper to get the file index out rather than using
37/// the field directly.
38file: u32,
39
40/// If this Atom is not a synthetic Atom, i.e., references a subsection in an
41/// Object file, `inner_sym_index` and `inner_nsyms_trailing` tell where and if
42/// this Atom contains any additional symbol references that fall within this Atom's
43/// address range. These could for example be an alias symbol which can be used
44/// internally by the relocation records, or if the Object file couldn't be split
45/// into subsections, this Atom may encompass an entire input section.
46inner_sym_index: u32,
47inner_nsyms_trailing: u32,
48
49/// Size of this atom.
50size: u64,
51
52/// Alignment of this atom as a power of 2.
53/// For instance, aligmment of 0 should be read as 2^0 = 1 byte aligned.
54alignment: u32,
55
56/// Points to the previous and next neighbours
57next_index: ?AtomIndex,
58prev_index: ?AtomIndex,
59
60pub const empty = Atom{
61 .sym_index = 0,
62 .inner_sym_index = 0,
63 .inner_nsyms_trailing = 0,
64 .file = 0,
65 .size = 0,
66 .alignment = 0,
67 .prev_index = null,
68 .next_index = null,
69};
70
71/// Returns `null` if the Atom is a synthetic Atom.
72/// Otherwise, returns an index into an array of Objects.
73pub fn getFile(self: Atom) ?u32 {
74 if (self.file == 0) return null;
75 return self.file - 1;
76}
77
78pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
79 return .{
80 .sym_index = self.sym_index,
81 .file = self.file,
82 };
83}
84
85const InnerSymIterator = struct {
86 sym_index: u32,
87 nsyms: u32,
88 file: u32,
89 pos: u32 = 0,
90
91 pub fn next(it: *@This()) ?SymbolWithLoc {
92 if (it.pos == it.nsyms) return null;
93 const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file };
94 it.pos += 1;
95 return res;
96 }
97};
98
99/// Returns an iterator over potentially contained symbols.
100/// Panics when called on a synthetic Atom.
101pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: AtomIndex) InnerSymIterator {
102 const atom = zld.getAtom(atom_index);
103 assert(atom.getFile() != null);
104 return .{
105 .sym_index = atom.inner_sym_index,
106 .nsyms = atom.inner_nsyms_trailing,
107 .file = atom.file,
108 };
109}
110
111/// Returns a section alias symbol if one is defined.
112/// An alias symbol is used to represent the start of an input section
113/// if there were no symbols defined within that range.
114/// Alias symbols are only used on x86_64.
115pub fn getSectionAlias(zld: *Zld, atom_index: AtomIndex) ?SymbolWithLoc {
116 const atom = zld.getAtom(atom_index);
117 assert(atom.getFile() != null);
118
119 const object = zld.objects.items[atom.getFile().?];
120 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
121 const ntotal = @as(u32, @intCast(object.symtab.len));
122 var sym_index: u32 = nbase;
123 while (sym_index < ntotal) : (sym_index += 1) {
124 if (object.getAtomIndexForSymbol(sym_index)) |other_atom_index| {
125 if (other_atom_index == atom_index) return SymbolWithLoc{
126 .sym_index = sym_index,
127 .file = atom.file,
128 };
129 }
130 }
131 return null;
132}
133
134/// Given an index into a contained symbol within, calculates an offset wrt
135/// the start of this Atom.
136pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u64 {
137 const atom = zld.getAtom(atom_index);
138 assert(atom.getFile() != null);
139
140 if (atom.sym_index == sym_index) return 0;
141
142 const object = zld.objects.items[atom.getFile().?];
143 const source_sym = object.getSourceSymbol(sym_index).?;
144 const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
145 sym.n_value
146 else blk: {
147 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
148 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
149 const source_sect = object.getSourceSection(sect_id);
150 break :blk source_sect.addr;
151 };
152 return source_sym.n_value - base_addr;
153}
154
155pub fn scanAtomRelocs(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
156 const arch = zld.options.target.cpu.arch;
157 const atom = zld.getAtom(atom_index);
158 assert(atom.getFile() != null); // synthetic atoms do not have relocs
159
160 return switch (arch) {
161 .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
162 .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
163 else => unreachable,
164 };
165}
166
167const RelocContext = struct {
168 base_addr: i64 = 0,
169 base_offset: i32 = 0,
170};
171
172pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext {
173 const atom = zld.getAtom(atom_index);
174 assert(atom.getFile() != null); // synthetic atoms do not have relocs
175
176 const object = zld.objects.items[atom.getFile().?];
177 if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
178 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
179 return .{
180 .base_addr = @as(i64, @intCast(source_sect.addr)),
181 .base_offset = @as(i32, @intCast(source_sym.n_value - source_sect.addr)),
182 };
183 }
184 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
185 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
186 const source_sect = object.getSourceSection(sect_id);
187 return .{
188 .base_addr = @as(i64, @intCast(source_sect.addr)),
189 .base_offset = 0,
190 };
191}
192
193pub fn parseRelocTarget(zld: *Zld, ctx: struct {
194 object_id: u32,
195 rel: macho.relocation_info,
196 code: []const u8,
197 base_addr: i64 = 0,
198 base_offset: i32 = 0,
199}) SymbolWithLoc {
200 const tracy = trace(@src());
201 defer tracy.end();
202
203 const object = &zld.objects.items[ctx.object_id];
204 log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
205
206 const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
207 const sect_id = @as(u8, @intCast(ctx.rel.r_symbolnum - 1));
208 const rel_offset = @as(u32, @intCast(ctx.rel.r_address - ctx.base_offset));
209
210 const address_in_section = if (ctx.rel.r_pcrel == 0) blk: {
211 break :blk if (ctx.rel.r_length == 3)
212 mem.readIntLittle(u64, ctx.code[rel_offset..][0..8])
213 else
214 mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
215 } else blk: {
216 assert(zld.options.target.cpu.arch == .x86_64);
217 const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
218 .X86_64_RELOC_SIGNED => 0,
219 .X86_64_RELOC_SIGNED_1 => 1,
220 .X86_64_RELOC_SIGNED_2 => 2,
221 .X86_64_RELOC_SIGNED_4 => 4,
222 else => unreachable,
223 };
224 const addend = mem.readIntLittle(i32, ctx.code[rel_offset..][0..4]);
225 const target_address = @as(i64, @intCast(ctx.base_addr)) + ctx.rel.r_address + 4 + correction + addend;
226 break :blk @as(u64, @intCast(target_address));
227 };
228
229 // Find containing atom
230 log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id });
231 break :sym_index object.getSymbolByAddress(address_in_section, sect_id);
232 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
233
234 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
235 const sym = zld.getSymbol(sym_loc);
236 const target = if (sym.sect() and !sym.ext())
237 sym_loc
238 else if (object.getGlobal(sym_index)) |global_index|
239 zld.globals.items[global_index]
240 else
241 sym_loc;
242 log.debug(" | target %{d} ('{s}') in object({?d})", .{
243 target.sym_index,
244 zld.getSymbolName(target),
245 target.getFile(),
246 });
247 return target;
248}
249
250pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc, is_via_got: bool) ?AtomIndex {
251 if (is_via_got) {
252 return zld.getGotAtomIndexForSymbol(target).?; // panic means fatal error
253 }
254 if (zld.getStubsAtomIndexForSymbol(target)) |stubs_atom| return stubs_atom;
255 if (zld.getTlvPtrAtomIndexForSymbol(target)) |tlv_ptr_atom| return tlv_ptr_atom;
256
257 if (target.getFile() == null) {
258 const target_sym_name = zld.getSymbolName(target);
259 if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
260 if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
261
262 unreachable; // referenced symbol not found
263 }
264
265 const object = zld.objects.items[target.getFile().?];
266 return object.getAtomIndexForSymbol(target.sym_index);
267}
268
269fn scanAtomRelocsArm64(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
270 for (relocs) |rel| {
271 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
272
273 switch (rel_type) {
274 .ARM64_RELOC_ADDEND, .ARM64_RELOC_SUBTRACTOR => continue,
275 else => {},
276 }
277
278 if (rel.r_extern == 0) continue;
279
280 const atom = zld.getAtom(atom_index);
281 const object = &zld.objects.items[atom.getFile().?];
282 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
283 const sym_loc = SymbolWithLoc{
284 .sym_index = sym_index,
285 .file = atom.file,
286 };
287
288 const target = if (object.getGlobal(sym_index)) |global_index|
289 zld.globals.items[global_index]
290 else
291 sym_loc;
292
293 switch (rel_type) {
294 .ARM64_RELOC_BRANCH26 => {
295 // TODO rewrite relocation
296 try addStub(zld, target);
297 },
298 .ARM64_RELOC_GOT_LOAD_PAGE21,
299 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
300 .ARM64_RELOC_POINTER_TO_GOT,
301 => {
302 // TODO rewrite relocation
303 try addGotEntry(zld, target);
304 },
305 .ARM64_RELOC_TLVP_LOAD_PAGE21,
306 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
307 => {
308 try addTlvPtrEntry(zld, target);
309 },
310 else => {},
311 }
312 }
313}
314
315fn scanAtomRelocsX86(zld: *Zld, atom_index: AtomIndex, relocs: []align(1) const macho.relocation_info) !void {
316 for (relocs) |rel| {
317 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
318
319 switch (rel_type) {
320 .X86_64_RELOC_SUBTRACTOR => continue,
321 else => {},
322 }
323
324 if (rel.r_extern == 0) continue;
325
326 const atom = zld.getAtom(atom_index);
327 const object = &zld.objects.items[atom.getFile().?];
328 const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
329 const sym_loc = SymbolWithLoc{
330 .sym_index = sym_index,
331 .file = atom.file,
332 };
333
334 const target = if (object.getGlobal(sym_index)) |global_index|
335 zld.globals.items[global_index]
336 else
337 sym_loc;
338
339 switch (rel_type) {
340 .X86_64_RELOC_BRANCH => {
341 // TODO rewrite relocation
342 try addStub(zld, target);
343 },
344 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
345 // TODO rewrite relocation
346 try addGotEntry(zld, target);
347 },
348 .X86_64_RELOC_TLV => {
349 try addTlvPtrEntry(zld, target);
350 },
351 else => {},
352 }
353 }
354}
355
356fn addTlvPtrEntry(zld: *Zld, target: SymbolWithLoc) !void {
357 const target_sym = zld.getSymbol(target);
358 if (!target_sym.undf()) return;
359 if (zld.tlv_ptr_table.contains(target)) return;
360
361 const gpa = zld.gpa;
362 const atom_index = try zld.createTlvPtrAtom();
363 const tlv_ptr_index = @as(u32, @intCast(zld.tlv_ptr_entries.items.len));
364 try zld.tlv_ptr_entries.append(gpa, .{
365 .target = target,
366 .atom_index = atom_index,
367 });
368 try zld.tlv_ptr_table.putNoClobber(gpa, target, tlv_ptr_index);
369}
370
371pub fn addGotEntry(zld: *Zld, target: SymbolWithLoc) !void {
372 if (zld.got_table.contains(target)) return;
373 const gpa = zld.gpa;
374 const atom_index = try zld.createGotAtom();
375 const got_index = @as(u32, @intCast(zld.got_entries.items.len));
376 try zld.got_entries.append(gpa, .{
377 .target = target,
378 .atom_index = atom_index,
379 });
380 try zld.got_table.putNoClobber(gpa, target, got_index);
381}
382
383pub fn addStub(zld: *Zld, target: SymbolWithLoc) !void {
384 const target_sym = zld.getSymbol(target);
385 if (!target_sym.undf()) return;
386 if (zld.stubs_table.contains(target)) return;
387
388 const gpa = zld.gpa;
389 _ = try zld.createStubHelperAtom();
390 _ = try zld.createLazyPointerAtom();
391 const atom_index = try zld.createStubAtom();
392 const stubs_index = @as(u32, @intCast(zld.stubs.items.len));
393 try zld.stubs.append(gpa, .{
394 .target = target,
395 .atom_index = atom_index,
396 });
397 try zld.stubs_table.putNoClobber(gpa, target, stubs_index);
398}
399
400pub fn resolveRelocs(
401 zld: *Zld,
402 atom_index: AtomIndex,
403 atom_code: []u8,
404 atom_relocs: []align(1) const macho.relocation_info,
405) !void {
406 const arch = zld.options.target.cpu.arch;
407 const atom = zld.getAtom(atom_index);
408 assert(atom.getFile() != null); // synthetic atoms do not have relocs
409
410 log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
411 atom.sym_index,
412 zld.getSymbolName(atom.getSymbolWithLoc()),
413 });
414
415 const ctx = getRelocContext(zld, atom_index);
416
417 return switch (arch) {
418 .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
419 .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
420 else => unreachable,
421 };
422}
423
424pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_via_got: bool, is_tlv: bool) !u64 {
425 const target_atom_index = getRelocTargetAtomIndex(zld, target, is_via_got) orelse {
426 // If there is no atom for target, we still need to check for special, atom-less
427 // symbols such as `___dso_handle`.
428 const target_name = zld.getSymbolName(target);
429 const atomless_sym = zld.getSymbol(target);
430 log.debug(" | atomless target '{s}'", .{target_name});
431 return atomless_sym.n_value;
432 };
433 const target_atom = zld.getAtom(target_atom_index);
434 log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
435 target_atom.sym_index,
436 zld.getSymbolName(target_atom.getSymbolWithLoc()),
437 target_atom.getFile(),
438 });
439
440 const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
441 assert(target_sym.n_desc != @import("zld.zig").N_DEAD);
442
443 // If `target` is contained within the target atom, pull its address value.
444 const offset = if (target_atom.getFile() != null) blk: {
445 const object = zld.objects.items[target_atom.getFile().?];
446 break :blk if (object.getSourceSymbol(target.sym_index)) |_|
447 Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
448 else
449 0; // section alias
450 } else 0;
451 const base_address: u64 = if (is_tlv) base_address: {
452 // For TLV relocations, the value specified as a relocation is the displacement from the
453 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
454 // defined TLV template init section in the following order:
455 // * wrt to __thread_data if defined, then
456 // * wrt to __thread_bss
457 const sect_id: u16 = sect_id: {
458 if (zld.getSectionByName("__DATA", "__thread_data")) |i| {
459 break :sect_id i;
460 } else if (zld.getSectionByName("__DATA", "__thread_bss")) |i| {
461 break :sect_id i;
462 } else {
463 log.err("threadlocal variables present but no initializer sections found", .{});
464 log.err(" __thread_data not found", .{});
465 log.err(" __thread_bss not found", .{});
466 return error.FailedToResolveRelocationTarget;
467 }
468 };
469 break :base_address zld.sections.items(.header)[sect_id].addr;
470 } else 0;
471 return target_sym.n_value + offset - base_address;
472}
473
474fn resolveRelocsArm64(
475 zld: *Zld,
476 atom_index: AtomIndex,
477 atom_code: []u8,
478 atom_relocs: []align(1) const macho.relocation_info,
479 context: RelocContext,
480) !void {
481 const atom = zld.getAtom(atom_index);
482 const object = zld.objects.items[atom.getFile().?];
483
484 var addend: ?i64 = null;
485 var subtractor: ?SymbolWithLoc = null;
486
487 for (atom_relocs) |rel| {
488 const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
489
490 switch (rel_type) {
491 .ARM64_RELOC_ADDEND => {
492 assert(addend == null);
493
494 log.debug(" RELA({s}) @ {x} => {x}", .{ @tagName(rel_type), rel.r_address, rel.r_symbolnum });
495
496 addend = rel.r_symbolnum;
497 continue;
498 },
499 .ARM64_RELOC_SUBTRACTOR => {
500 assert(subtractor == null);
501
502 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
503 @tagName(rel_type),
504 rel.r_address,
505 rel.r_symbolnum,
506 atom.getFile(),
507 });
508
509 subtractor = parseRelocTarget(zld, .{
510 .object_id = atom.getFile().?,
511 .rel = rel,
512 .code = atom_code,
513 .base_addr = context.base_addr,
514 .base_offset = context.base_offset,
515 });
516 continue;
517 },
518 else => {},
519 }
520
521 const target = parseRelocTarget(zld, .{
522 .object_id = atom.getFile().?,
523 .rel = rel,
524 .code = atom_code,
525 .base_addr = context.base_addr,
526 .base_offset = context.base_offset,
527 });
528 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
529
530 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
531 @tagName(rel_type),
532 rel.r_address,
533 target.sym_index,
534 zld.getSymbolName(target),
535 target.getFile(),
536 });
537
538 const source_addr = blk: {
539 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
540 break :blk source_sym.n_value + rel_offset;
541 };
542 const is_via_got = relocRequiresGot(zld, rel);
543 const is_tlv = is_tlv: {
544 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
545 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
546 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
547 };
548 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
549
550 log.debug(" | source_addr = 0x{x}", .{source_addr});
551
552 switch (rel_type) {
553 .ARM64_RELOC_BRANCH26 => {
554 const actual_target = if (zld.getStubsAtomIndexForSymbol(target)) |stub_atom_index| inner: {
555 const stub_atom = zld.getAtom(stub_atom_index);
556 break :inner stub_atom.getSymbolWithLoc();
557 } else target;
558 log.debug(" source {s} (object({?})), target {s} (object({?}))", .{
559 zld.getSymbolName(atom.getSymbolWithLoc()),
560 atom.getFile(),
561 zld.getSymbolName(target),
562 zld.getAtom(getRelocTargetAtomIndex(zld, target, is_via_got).?).getFile(),
563 });
564
565 const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
566 source_addr,
567 zld.getSymbol(actual_target).n_value,
568 )) |disp| blk: {
569 log.debug(" | target_addr = 0x{x}", .{zld.getSymbol(actual_target).n_value});
570 break :blk disp;
571 } else |_| blk: {
572 const thunk_index = zld.thunk_table.get(atom_index).?;
573 const thunk = zld.thunks.items[thunk_index];
574 const thunk_sym = zld.getSymbol(thunk.getTrampolineForSymbol(
575 zld,
576 actual_target,
577 ).?);
578 log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_sym.n_value});
579 break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_sym.n_value);
580 };
581
582 const code = atom_code[rel_offset..][0..4];
583 var inst = aarch64.Instruction{
584 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
585 aarch64.Instruction,
586 aarch64.Instruction.unconditional_branch_immediate,
587 ), code),
588 };
589 inst.unconditional_branch_immediate.imm26 = @as(u26, @truncate(@as(u28, @bitCast(displacement >> 2))));
590 mem.writeIntLittle(u32, code, inst.toU32());
591 },
592
593 .ARM64_RELOC_PAGE21,
594 .ARM64_RELOC_GOT_LOAD_PAGE21,
595 .ARM64_RELOC_TLVP_LOAD_PAGE21,
596 => {
597 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
598
599 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
600
601 const pages = @as(u21, @bitCast(Relocation.calcNumberOfPages(source_addr, adjusted_target_addr)));
602 const code = atom_code[rel_offset..][0..4];
603 var inst = aarch64.Instruction{
604 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
605 aarch64.Instruction,
606 aarch64.Instruction.pc_relative_address,
607 ), code),
608 };
609 inst.pc_relative_address.immhi = @as(u19, @truncate(pages >> 2));
610 inst.pc_relative_address.immlo = @as(u2, @truncate(pages));
611 mem.writeIntLittle(u32, code, inst.toU32());
612 addend = null;
613 },
614
615 .ARM64_RELOC_PAGEOFF12 => {
616 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
617
618 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
619
620 const code = atom_code[rel_offset..][0..4];
621 if (Relocation.isArithmeticOp(code)) {
622 const off = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic);
623 var inst = aarch64.Instruction{
624 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
625 aarch64.Instruction,
626 aarch64.Instruction.add_subtract_immediate,
627 ), code),
628 };
629 inst.add_subtract_immediate.imm12 = off;
630 mem.writeIntLittle(u32, code, inst.toU32());
631 } else {
632 var inst = aarch64.Instruction{
633 .load_store_register = mem.bytesToValue(meta.TagPayload(
634 aarch64.Instruction,
635 aarch64.Instruction.load_store_register,
636 ), code),
637 };
638 const off = try Relocation.calcPageOffset(adjusted_target_addr, switch (inst.load_store_register.size) {
639 0 => if (inst.load_store_register.v == 1)
640 Relocation.PageOffsetInstKind.load_store_128
641 else
642 Relocation.PageOffsetInstKind.load_store_8,
643 1 => .load_store_16,
644 2 => .load_store_32,
645 3 => .load_store_64,
646 });
647 inst.load_store_register.offset = off;
648 mem.writeIntLittle(u32, code, inst.toU32());
649 }
650 addend = null;
651 },
652
653 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
654 const code = atom_code[rel_offset..][0..4];
655 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
656
657 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
658
659 const off = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64);
660 var inst: aarch64.Instruction = .{
661 .load_store_register = mem.bytesToValue(meta.TagPayload(
662 aarch64.Instruction,
663 aarch64.Instruction.load_store_register,
664 ), code),
665 };
666 inst.load_store_register.offset = off;
667 mem.writeIntLittle(u32, code, inst.toU32());
668 addend = null;
669 },
670
671 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
672 const code = atom_code[rel_offset..][0..4];
673 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + (addend orelse 0)));
674
675 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
676
677 const RegInfo = struct {
678 rd: u5,
679 rn: u5,
680 size: u2,
681 };
682 const reg_info: RegInfo = blk: {
683 if (Relocation.isArithmeticOp(code)) {
684 const inst = mem.bytesToValue(meta.TagPayload(
685 aarch64.Instruction,
686 aarch64.Instruction.add_subtract_immediate,
687 ), code);
688 break :blk .{
689 .rd = inst.rd,
690 .rn = inst.rn,
691 .size = inst.sf,
692 };
693 } else {
694 const inst = mem.bytesToValue(meta.TagPayload(
695 aarch64.Instruction,
696 aarch64.Instruction.load_store_register,
697 ), code);
698 break :blk .{
699 .rd = inst.rt,
700 .rn = inst.rn,
701 .size = inst.size,
702 };
703 }
704 };
705
706 var inst = if (zld.tlv_ptr_table.contains(target)) aarch64.Instruction{
707 .load_store_register = .{
708 .rt = reg_info.rd,
709 .rn = reg_info.rn,
710 .offset = try Relocation.calcPageOffset(adjusted_target_addr, .load_store_64),
711 .opc = 0b01,
712 .op1 = 0b01,
713 .v = 0,
714 .size = reg_info.size,
715 },
716 } else aarch64.Instruction{
717 .add_subtract_immediate = .{
718 .rd = reg_info.rd,
719 .rn = reg_info.rn,
720 .imm12 = try Relocation.calcPageOffset(adjusted_target_addr, .arithmetic),
721 .sh = 0,
722 .s = 0,
723 .op = 0,
724 .sf = @as(u1, @truncate(reg_info.size)),
725 },
726 };
727 mem.writeIntLittle(u32, code, inst.toU32());
728 addend = null;
729 },
730
731 .ARM64_RELOC_POINTER_TO_GOT => {
732 log.debug(" | target_addr = 0x{x}", .{target_addr});
733 const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
734 return error.Overflow;
735 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @bitCast(result)));
736 },
737
738 .ARM64_RELOC_UNSIGNED => {
739 var ptr_addend = if (rel.r_length == 3)
740 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
741 else
742 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
743
744 if (rel.r_extern == 0) {
745 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
746 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
747 else
748 object.source_address_lookup[target.sym_index];
749 ptr_addend -= base_addr;
750 }
751
752 const result = blk: {
753 if (subtractor) |sub| {
754 const sym = zld.getSymbol(sub);
755 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
756 } else {
757 break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
758 }
759 };
760 log.debug(" | target_addr = 0x{x}", .{result});
761
762 if (rel.r_length == 3) {
763 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
764 } else {
765 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
766 }
767
768 subtractor = null;
769 },
770
771 .ARM64_RELOC_ADDEND => unreachable,
772 .ARM64_RELOC_SUBTRACTOR => unreachable,
773 }
774 }
775}
776
777fn resolveRelocsX86(
778 zld: *Zld,
779 atom_index: AtomIndex,
780 atom_code: []u8,
781 atom_relocs: []align(1) const macho.relocation_info,
782 context: RelocContext,
783) !void {
784 const atom = zld.getAtom(atom_index);
785 const object = zld.objects.items[atom.getFile().?];
786
787 var subtractor: ?SymbolWithLoc = null;
788
789 for (atom_relocs) |rel| {
790 const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
791
792 switch (rel_type) {
793 .X86_64_RELOC_SUBTRACTOR => {
794 assert(subtractor == null);
795
796 log.debug(" RELA({s}) @ {x} => %{d} in object({?d})", .{
797 @tagName(rel_type),
798 rel.r_address,
799 rel.r_symbolnum,
800 atom.getFile(),
801 });
802
803 subtractor = parseRelocTarget(zld, .{
804 .object_id = atom.getFile().?,
805 .rel = rel,
806 .code = atom_code,
807 .base_addr = context.base_addr,
808 .base_offset = context.base_offset,
809 });
810 continue;
811 },
812 else => {},
813 }
814
815 const target = parseRelocTarget(zld, .{
816 .object_id = atom.getFile().?,
817 .rel = rel,
818 .code = atom_code,
819 .base_addr = context.base_addr,
820 .base_offset = context.base_offset,
821 });
822 const rel_offset = @as(u32, @intCast(rel.r_address - context.base_offset));
823
824 log.debug(" RELA({s}) @ {x} => %{d} ('{s}') in object({?})", .{
825 @tagName(rel_type),
826 rel.r_address,
827 target.sym_index,
828 zld.getSymbolName(target),
829 target.getFile(),
830 });
831
832 const source_addr = blk: {
833 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
834 break :blk source_sym.n_value + rel_offset;
835 };
836 const is_via_got = relocRequiresGot(zld, rel);
837 const is_tlv = is_tlv: {
838 const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
839 const header = zld.sections.items(.header)[source_sym.n_sect - 1];
840 break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
841 };
842
843 log.debug(" | source_addr = 0x{x}", .{source_addr});
844
845 const target_addr = try getRelocTargetAddress(zld, target, is_via_got, is_tlv);
846
847 switch (rel_type) {
848 .X86_64_RELOC_BRANCH => {
849 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
850 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
851 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
852 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
853 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
854 },
855
856 .X86_64_RELOC_GOT,
857 .X86_64_RELOC_GOT_LOAD,
858 => {
859 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
860 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
861 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
862 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
863 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
864 },
865
866 .X86_64_RELOC_TLV => {
867 const addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
868 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
869 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
870 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
871
872 if (zld.tlv_ptr_table.get(target) == null) {
873 // We need to rewrite the opcode from movq to leaq.
874 atom_code[rel_offset - 2] = 0x8d;
875 }
876
877 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
878 },
879
880 .X86_64_RELOC_SIGNED,
881 .X86_64_RELOC_SIGNED_1,
882 .X86_64_RELOC_SIGNED_2,
883 .X86_64_RELOC_SIGNED_4,
884 => {
885 const correction: u3 = switch (rel_type) {
886 .X86_64_RELOC_SIGNED => 0,
887 .X86_64_RELOC_SIGNED_1 => 1,
888 .X86_64_RELOC_SIGNED_2 => 2,
889 .X86_64_RELOC_SIGNED_4 => 4,
890 else => unreachable,
891 };
892 var addend = mem.readIntLittle(i32, atom_code[rel_offset..][0..4]) + correction;
893
894 if (rel.r_extern == 0) {
895 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
896 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
897 else
898 object.source_address_lookup[target.sym_index];
899 addend += @as(i32, @intCast(@as(i64, @intCast(context.base_addr)) + rel.r_address + 4 -
900 @as(i64, @intCast(base_addr))));
901 }
902
903 const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
904
905 log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
906
907 const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, correction);
908 mem.writeIntLittle(i32, atom_code[rel_offset..][0..4], disp);
909 },
910
911 .X86_64_RELOC_UNSIGNED => {
912 var addend = if (rel.r_length == 3)
913 mem.readIntLittle(i64, atom_code[rel_offset..][0..8])
914 else
915 mem.readIntLittle(i32, atom_code[rel_offset..][0..4]);
916
917 if (rel.r_extern == 0) {
918 const base_addr = if (target.sym_index >= object.source_address_lookup.len)
919 @as(i64, @intCast(object.getSourceSection(@as(u8, @intCast(rel.r_symbolnum - 1))).addr))
920 else
921 object.source_address_lookup[target.sym_index];
922 addend -= base_addr;
923 }
924
925 const result = blk: {
926 if (subtractor) |sub| {
927 const sym = zld.getSymbol(sub);
928 break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
929 } else {
930 break :blk @as(i64, @intCast(target_addr)) + addend;
931 }
932 };
933 log.debug(" | target_addr = 0x{x}", .{result});
934
935 if (rel.r_length == 3) {
936 mem.writeIntLittle(u64, atom_code[rel_offset..][0..8], @as(u64, @bitCast(result)));
937 } else {
938 mem.writeIntLittle(u32, atom_code[rel_offset..][0..4], @as(u32, @truncate(@as(u64, @bitCast(result)))));
939 }
940
941 subtractor = null;
942 },
943
944 .X86_64_RELOC_SUBTRACTOR => unreachable,
945 }
946 }
947}
948
949pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 {
950 const atom = zld.getAtom(atom_index);
951 assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
952 const object = zld.objects.items[atom.getFile().?];
953 const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
954 // If there was no matching symbol present in the source symtab, this means
955 // we are dealing with either an entire section, or part of it, but also
956 // starting at the beginning.
957 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
958 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
959 const source_sect = object.getSourceSection(sect_id);
960 assert(!source_sect.isZerofill());
961 const code = object.getSectionContents(source_sect);
962 const code_len = @as(usize, @intCast(atom.size));
963 return code[0..code_len];
964 };
965 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
966 assert(!source_sect.isZerofill());
967 const code = object.getSectionContents(source_sect);
968 const offset = @as(usize, @intCast(source_sym.n_value - source_sect.addr));
969 const code_len = @as(usize, @intCast(atom.size));
970 return code[offset..][0..code_len];
971}
972
973pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_info {
974 const atom = zld.getAtom(atom_index);
975 assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
976 const object = zld.objects.items[atom.getFile().?];
977 const cache = object.relocs_lookup[atom.sym_index];
978
979 const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
980 break :blk source_sym.n_sect - 1;
981 } else blk: {
982 // If there was no matching symbol present in the source symtab, this means
983 // we are dealing with either an entire section, or part of it, but also
984 // starting at the beginning.
985 const nbase = @as(u32, @intCast(object.in_symtab.?.len));
986 const sect_id = @as(u8, @intCast(atom.sym_index - nbase));
987 break :blk sect_id;
988 };
989 const source_sect = object.getSourceSection(source_sect_id);
990 assert(!source_sect.isZerofill());
991 const relocs = object.getRelocs(source_sect_id);
992 return relocs[cache.start..][0..cache.len];
993}
994
995pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
996 switch (zld.options.target.cpu.arch) {
997 .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
998 .ARM64_RELOC_GOT_LOAD_PAGE21,
999 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
1000 .ARM64_RELOC_POINTER_TO_GOT,
1001 => return true,
1002 else => return false,
1003 },
1004 .x86_64 => switch (@as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type))) {
1005 .X86_64_RELOC_GOT,
1006 .X86_64_RELOC_GOT_LOAD,
1007 => return true,
1008 else => return false,
1009 },
1010 else => unreachable,
1011 }
1012}
src/link/MachO/dead_strip.zig+1-1
......@@ -10,7 +10,7 @@ const mem = std.mem;
1010
1111const Allocator = mem.Allocator;
1212const AtomIndex = @import("zld.zig").AtomIndex;
13const Atom = @import("ZldAtom.zig");
13const Atom = @import("Atom.zig");
1414const MachO = @import("../MachO.zig");
1515const SymbolWithLoc = MachO.SymbolWithLoc;
1616const SymbolResolver = MachO.SymbolResolver;
src/link/MachO/eh_frame.zig+1-1
......@@ -8,7 +8,7 @@ const log = std.log.scoped(.eh_frame);
88
99const Allocator = mem.Allocator;
1010const AtomIndex = @import("zld.zig").AtomIndex;
11const Atom = @import("ZldAtom.zig");
11const Atom = @import("Atom.zig");
1212const MachO = @import("../MachO.zig");
1313const Relocation = @import("Relocation.zig");
1414const SymbolWithLoc = MachO.SymbolWithLoc;
src/link/MachO/thunks.zig+1-1
......@@ -15,7 +15,7 @@ const mem = std.mem;
1515const aarch64 = @import("../../arch/aarch64/bits.zig");
1616
1717const Allocator = mem.Allocator;
18const Atom = @import("ZldAtom.zig");
18const Atom = @import("Atom.zig");
1919const AtomIndex = @import("zld.zig").AtomIndex;
2020const MachO = @import("../MachO.zig");
2121const Relocation = @import("Relocation.zig");
src/link/MachO/zld.zig+19-2
......@@ -21,7 +21,7 @@ const trace = @import("../../tracy.zig").trace;
2121
2222const Allocator = mem.Allocator;
2323const Archive = @import("Archive.zig");
24const Atom = @import("ZldAtom.zig");
24const Atom = @import("Atom.zig");
2525const Cache = std.Build.Cache;
2626const CodeSignature = @import("CodeSignature.zig");
2727const Compilation = @import("../../Compilation.zig");
......@@ -247,7 +247,16 @@ pub const Zld = struct {
247247 const gpa = self.gpa;
248248 const index = @as(AtomIndex, @intCast(self.atoms.items.len));
249249 const atom = try self.atoms.addOne(gpa);
250 atom.* = Atom.empty;
250 atom.* = .{
251 .sym_index = 0,
252 .inner_sym_index = 0,
253 .inner_nsyms_trailing = 0,
254 .file = 0,
255 .size = 0,
256 .alignment = 0,
257 .prev_index = null,
258 .next_index = null,
259 };
251260 atom.sym_index = sym_index;
252261 atom.size = size;
253262 atom.alignment = alignment;
......@@ -3169,6 +3178,14 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
31693178 };
31703179 defer zld.deinit();
31713180
3181 // Index 0 is always a null symbol.
3182 try zld.locals.append(gpa, .{
3183 .n_strx = 0,
3184 .n_type = 0,
3185 .n_sect = 0,
3186 .n_desc = 0,
3187 .n_value = 0,
3188 });
31723189 try zld.strtab.buffer.append(gpa, 0);
31733190
31743191 // Positional arguments to the linker such as object files and static archives.