macho: remove all Zld codepaths

This commit is contained in:
Jakub Konka 2021-09-03 00:21:45 +02:00
parent 5af13f35f9
commit 4b07da7090

View File

@ -761,43 +761,34 @@ pub fn flush(self: *MachO, comp: *Compilation) !void {
try self.addDataInCodeLC();
try self.addCodeSignatureLC();
if (use_stage1) {
try self.parseTextBlocks();
try self.allocateTextSegment();
try self.allocateDataConstSegment();
try self.allocateDataSegment();
self.allocateLinkeditSegment();
try self.allocateTextBlocks();
try self.flushZld();
} else {
try self.parseTextBlocks();
try self.allocateGlobalSymbols();
{
log.debug("locals:", .{});
for (self.locals.items) |sym| {
log.debug(" {s}: {}", .{ self.getString(sym.n_strx), sym });
}
log.debug("globals:", .{});
for (self.globals.items) |sym| {
log.debug(" {s}: {}", .{ self.getString(sym.n_strx), sym });
}
log.debug("undefs:", .{});
for (self.undefs.items) |sym| {
log.debug(" {s}: {}", .{ self.getString(sym.n_strx), sym });
}
log.debug("unresolved:", .{});
for (self.unresolved.keys()) |key| {
log.debug(" {d} => {s}", .{ key, self.unresolved.get(key).? });
}
log.debug("resolved:", .{});
var it = self.symbol_resolver.iterator();
while (it.next()) |entry| {
log.debug(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
}
try self.parseTextBlocks();
try self.allocateGlobalSymbols();
{
log.debug("locals:", .{});
for (self.locals.items) |sym| {
log.debug(" {s}: {}", .{ self.getString(sym.n_strx), sym });
}
log.debug("globals:", .{});
for (self.globals.items) |sym| {
log.debug(" {s}: {}", .{ self.getString(sym.n_strx), sym });
}
log.debug("undefs:", .{});
for (self.undefs.items) |sym| {
log.debug(" {s}: {}", .{ self.getString(sym.n_strx), sym });
}
log.debug("unresolved:", .{});
for (self.unresolved.keys()) |key| {
log.debug(" {d} => {s}", .{ key, self.unresolved.get(key).? });
}
log.debug("resolved:", .{});
var it = self.symbol_resolver.iterator();
while (it.next()) |entry| {
log.debug(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
}
try self.writeAtoms();
try self.flushModule(comp);
}
try self.writeAtoms();
try self.writeDices();
try self.flushModule(comp);
}
if (!self.base.options.disable_lld_caching) {
@ -1638,248 +1629,6 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
return res;
}
fn allocateTextSegment(self: *MachO) !void {
const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
seg.inner.fileoff = 0;
seg.inner.vmaddr = base_vmaddr;
var sizeofcmds: u64 = 0;
for (self.load_commands.items) |lc| {
sizeofcmds += lc.cmdsize();
}
try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds);
// Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
var min_alignment: u32 = 0;
for (seg.sections.items) |sect| {
const alignment = try math.powi(u32, 2, sect.@"align");
min_alignment = math.max(min_alignment, alignment);
}
assert(min_alignment > 0);
const last_sect_idx = seg.sections.items.len - 1;
const last_sect = seg.sections.items[last_sect_idx];
const shift: u32 = blk: {
const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
const factor = @divTrunc(diff, min_alignment);
break :blk @intCast(u32, factor * min_alignment);
};
if (shift > 0) {
for (seg.sections.items) |*sect| {
sect.offset += shift;
sect.addr += shift;
}
}
}
fn allocateDataConstSegment(self: *MachO) !void {
const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
}
fn allocateDataSegment(self: *MachO) !void {
const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
try self.allocateSegment(self.data_segment_cmd_index.?, 0);
}
fn allocateLinkeditSegment(self: *MachO) void {
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
}
fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {
const seg = &self.load_commands.items[index].Segment;
// Allocate the sections according to their alignment at the beginning of the segment.
var start: u64 = offset;
for (seg.sections.items) |*sect, sect_id| {
const alignment = try math.powi(u32, 2, sect.@"align");
const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
const end_aligned = mem.alignForwardGeneric(u64, start_aligned + sect.size, alignment);
const file_offset = @intCast(u32, seg.inner.fileoff + start_aligned);
blk: {
if (index == self.data_segment_cmd_index.?) {
if (self.bss_section_index) |idx| {
if (sect_id == idx) {
self.bss_file_offset = file_offset;
break :blk;
}
}
if (self.tlv_bss_section_index) |idx| {
if (sect_id == idx) {
self.tlv_bss_file_offset = file_offset;
break :blk;
}
}
}
sect.offset = @intCast(u32, seg.inner.fileoff + start_aligned);
}
sect.addr = seg.inner.vmaddr + start_aligned;
start = end_aligned;
}
const seg_size_aligned = mem.alignForwardGeneric(u64, start, self.page_size);
seg.inner.filesize = seg_size_aligned;
seg.inner.vmsize = seg_size_aligned;
}
fn allocateTextBlocks(self: *MachO) !void {
var it = self.blocks.iterator();
while (it.next()) |entry| {
const match = entry.key_ptr.*;
var block: *TextBlock = entry.value_ptr.*;
// Find the first block
while (block.prev) |prev| {
block = prev;
}
const seg = self.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
var base_addr: u64 = sect.addr;
const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
log.debug(" within section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
log.debug(" {}", .{sect});
while (true) {
const block_alignment = try math.powi(u32, 2, block.alignment);
base_addr = mem.alignForwardGeneric(u64, base_addr, block_alignment);
const sym = &self.locals.items[block.local_sym_index];
sym.n_value = base_addr;
sym.n_sect = n_sect;
log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
self.getString(sym.n_strx),
base_addr,
base_addr + block.size,
block.size,
block.alignment,
});
// Update each alias (if any)
for (block.aliases.items) |index| {
const alias_sym = &self.locals.items[index];
alias_sym.n_value = base_addr;
alias_sym.n_sect = n_sect;
}
// Update each symbol contained within the TextBlock
for (block.contained.items) |sym_at_off| {
const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
contained_sym.n_value = base_addr + sym_at_off.offset;
contained_sym.n_sect = n_sect;
}
base_addr += block.size;
if (block.next) |next| {
block = next;
} else break;
}
}
// Update globals
{
var sym_it = self.symbol_resolver.valueIterator();
while (sym_it.next()) |resolv| {
if (resolv.where != .global) continue;
assert(resolv.local_sym_index != 0);
const local_sym = self.locals.items[resolv.local_sym_index];
const sym = &self.globals.items[resolv.where_index];
sym.n_value = local_sym.n_value;
sym.n_sect = local_sym.n_sect;
}
}
}
fn writeTextBlocks(self: *MachO) !void {
var it = self.blocks.iterator();
while (it.next()) |entry| {
const match = entry.key_ptr.*;
var block: *TextBlock = entry.value_ptr.*;
while (block.prev) |prev| {
block = prev;
}
const seg = self.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
const sect_type = commands.sectionType(sect);
log.debug(" for section {s},{s}", .{ commands.segmentName(sect), commands.sectionName(sect) });
log.debug(" {}", .{sect});
var code = try self.base.allocator.alloc(u8, sect.size);
defer self.base.allocator.free(code);
const file_offset: u64 = blk: {
if (self.data_segment_cmd_index.? == match.seg) {
if (self.bss_section_index) |idx| {
if (idx == match.sect) break :blk self.bss_file_offset.?;
}
if (self.tlv_bss_section_index) |idx| {
if (idx == match.sect) break :blk self.tlv_bss_file_offset.?;
}
}
break :blk sect.offset;
};
if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) {
mem.set(u8, code, 0);
} else {
var base_off: u64 = 0;
while (true) {
const block_alignment = try math.powi(u32, 2, block.alignment);
const aligned_base_off = mem.alignForwardGeneric(u64, base_off, block_alignment);
const sym = self.locals.items[block.local_sym_index];
log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
self.getString(sym.n_strx),
aligned_base_off,
aligned_base_off + block.size,
block.size,
block.alignment,
});
try block.resolveRelocs(self);
mem.copy(u8, code[aligned_base_off..][0..block.size], block.code.items);
// TODO NOP for machine code instead of just zeroing out
const padding_len = aligned_base_off - base_off;
mem.set(u8, code[base_off..][0..padding_len], 0);
base_off = aligned_base_off + block.size;
if (block.next) |next| {
block = next;
} else break;
}
mem.set(u8, code[base_off..], 0);
}
try self.base.file.?.pwriteAll(code, file_offset);
}
}
pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*TextBlock {
const code = try self.base.allocator.alloc(u8, size);
defer self.base.allocator.free(code);
@ -2920,17 +2669,23 @@ fn parseTextBlocks(self: *MachO) !void {
}
fn addDataInCodeLC(self: *MachO) !void {
if (self.data_in_code_cmd_index == null) {
self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.LinkeditData = .{
.cmd = macho.LC_DATA_IN_CODE,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
},
});
}
if (self.data_in_code_cmd_index != null) return;
self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.LinkeditData = .{
.cmd = macho.LC_DATA_IN_CODE,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
},
});
const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
const needed_size = 10 * @sizeOf(macho.data_in_code_entry);
const dataoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.data_in_code_entry), null);
log.debug("found data-in-code free space 0x{x} to 0x{x}", .{ dataoff, dataoff + needed_size });
dice_cmd.dataoff = @intCast(u32, dataoff);
dice_cmd.datasize = needed_size;
self.load_commands_dirty = true;
}
fn addCodeSignatureLC(self: *MachO) !void {
@ -2982,42 +2737,6 @@ fn addLoadDylibLCs(self: *MachO) !void {
}
}
fn flushZld(self: *MachO) !void {
try self.writeTextBlocks();
try self.setEntryPoint();
try self.writeRebaseInfoTableZld();
try self.writeBindInfoTableZld();
try self.writeLazyBindInfoTableZld();
try self.writeExportInfoZld();
try self.writeDices();
{
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
}
try self.writeSymbolTable();
try self.writeStringTableZld();
{
// Seal __LINKEDIT size
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
}
if (self.requires_adhoc_codesig) {
try self.writeCodeSignaturePadding();
}
try self.writeLoadCommands();
try self.writeHeader();
if (self.requires_adhoc_codesig) {
try self.writeCodeSignature();
}
}
fn setEntryPoint(self: *MachO) !void {
if (self.base.options.output_mode != .Exe) return;
@ -3039,343 +2758,6 @@ fn setEntryPoint(self: *MachO) !void {
self.load_commands_dirty = true;
}
fn writeRebaseInfoTableZld(self: *MachO) !void {
var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
defer pointers.deinit();
{
var it = self.blocks.iterator();
while (it.next()) |entry| {
const match = entry.key_ptr.*;
var block: *TextBlock = entry.value_ptr.*;
if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
const seg = self.load_commands.items[match.seg].Segment;
while (true) {
const sym = self.locals.items[block.local_sym_index];
const base_offset = sym.n_value - seg.inner.vmaddr;
for (block.rebases.items) |offset| {
try pointers.append(.{
.offset = base_offset + offset,
.segment_id = match.seg,
});
}
if (block.prev) |prev| {
block = prev;
} else break;
}
}
}
const size = try bind.rebaseInfoSize(pointers.items);
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
defer self.base.allocator.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
try bind.writeRebaseInfo(pointers.items, stream.writer());
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff);
dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @sizeOf(u64)));
seg.inner.filesize += dyld_info.rebase_size;
log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
}
fn writeBindInfoTableZld(self: *MachO) !void {
var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
defer pointers.deinit();
{
var it = self.blocks.iterator();
while (it.next()) |entry| {
const match = entry.key_ptr.*;
var block: *TextBlock = entry.value_ptr.*;
if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
const seg = self.load_commands.items[match.seg].Segment;
while (true) {
const sym = self.locals.items[block.local_sym_index];
const base_offset = sym.n_value - seg.inner.vmaddr;
for (block.bindings.items) |binding| {
const bind_sym = self.undefs.items[binding.local_sym_index];
try pointers.append(.{
.offset = binding.offset + base_offset,
.segment_id = match.seg,
.dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
.name = self.getString(bind_sym.n_strx),
});
}
if (block.prev) |prev| {
block = prev;
} else break;
}
}
}
const size = try bind.bindInfoSize(pointers.items);
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
defer self.base.allocator.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
try bind.writeBindInfo(pointers.items, stream.writer());
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
dyld_info.bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
seg.inner.filesize += dyld_info.bind_size;
log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size });
try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
}
fn writeLazyBindInfoTableZld(self: *MachO) !void {
var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);
defer pointers.deinit();
if (self.la_symbol_ptr_section_index) |sect| blk: {
var atom = self.blocks.get(.{
.seg = self.data_segment_cmd_index.?,
.sect = sect,
}) orelse break :blk;
const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
while (true) {
const sym = self.locals.items[atom.local_sym_index];
const base_offset = sym.n_value - seg.inner.vmaddr;
for (atom.lazy_bindings.items) |binding| {
const bind_sym = self.undefs.items[binding.local_sym_index];
try pointers.append(.{
.offset = binding.offset + base_offset,
.segment_id = self.data_segment_cmd_index.?,
.dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER),
.name = self.getString(bind_sym.n_strx),
});
}
if (atom.prev) |prev| {
atom = prev;
} else break;
}
}
const size = try bind.lazyBindInfoSize(pointers.items);
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
defer self.base.allocator.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
try bind.writeLazyBindInfo(pointers.items, stream.writer());
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
dyld_info.lazy_bind_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
seg.inner.filesize += dyld_info.lazy_bind_size;
log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size });
try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
try self.populateLazyBindOffsetsInStubHelper(buffer);
}
fn writeExportInfoZld(self: *MachO) !void {
var trie: Trie = .{};
defer trie.deinit(self.base.allocator);
const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const base_address = text_segment.inner.vmaddr;
// TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
log.debug("writing export trie", .{});
for (self.globals.items) |sym| {
const sym_name = self.getString(sym.n_strx);
log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
try trie.put(self.base.allocator, .{
.name = sym_name,
.vmaddr_offset = sym.n_value - base_address,
.export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
});
}
try trie.finalize(self.base.allocator);
var buffer = try self.base.allocator.alloc(u8, @intCast(usize, trie.size));
defer self.base.allocator.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
const nwritten = try trie.write(stream.writer());
assert(nwritten == trie.size);
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
dyld_info.export_off = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)));
seg.inner.filesize += dyld_info.export_size;
log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
try self.base.file.?.pwriteAll(buffer, dyld_info.export_off);
}
fn writeSymbolTable(self: *MachO) !void {
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
defer locals.deinit();
try locals.appendSlice(self.locals.items);
if (self.has_stabs) {
for (self.objects.items) |object| {
if (object.debug_info == null) continue;
// Open scope
try locals.ensureUnusedCapacity(3);
locals.appendAssumeCapacity(.{
.n_strx = try self.makeString(object.tu_comp_dir.?),
.n_type = macho.N_SO,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
locals.appendAssumeCapacity(.{
.n_strx = try self.makeString(object.tu_name.?),
.n_type = macho.N_SO,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
locals.appendAssumeCapacity(.{
.n_strx = try self.makeString(object.name),
.n_type = macho.N_OSO,
.n_sect = 0,
.n_desc = 1,
.n_value = object.mtime orelse 0,
});
for (object.text_blocks.items) |block| {
if (block.stab) |stab| {
const nlists = try stab.asNlists(block.local_sym_index, self);
defer self.base.allocator.free(nlists);
try locals.appendSlice(nlists);
} else {
for (block.contained.items) |sym_at_off| {
const stab = sym_at_off.stab orelse continue;
const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);
defer self.base.allocator.free(nlists);
try locals.appendSlice(nlists);
}
}
}
// Close scope
try locals.append(.{
.n_strx = 0,
.n_type = macho.N_SO,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
}
}
const nlocals = locals.items.len;
const nexports = self.globals.items.len;
const nundefs = self.undefs.items.len;
const locals_off = symtab.symoff;
const locals_size = nlocals * @sizeOf(macho.nlist_64);
log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
const exports_off = locals_off + locals_size;
const exports_size = nexports * @sizeOf(macho.nlist_64);
log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off);
const undefs_off = exports_off + exports_size;
const undefs_size = nundefs * @sizeOf(macho.nlist_64);
log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off);
symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);
seg.inner.filesize += locals_size + exports_size + undefs_size;
// Update dynamic symbol table.
const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
dysymtab.nlocalsym = @intCast(u32, nlocals);
dysymtab.iextdefsym = dysymtab.nlocalsym;
dysymtab.nextdefsym = @intCast(u32, nexports);
dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
dysymtab.nundefsym = @intCast(u32, nundefs);
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const stubs = &text_segment.sections.items[self.stubs_section_index.?];
const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const got = &data_const_segment.sections.items[self.got_section_index.?];
const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
const nstubs = @intCast(u32, self.stubs_map.keys().len);
const ngot_entries = @intCast(u32, self.got_entries_map.keys().len);
dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
const needed_size = dysymtab.nindirectsyms * @sizeOf(u32);
seg.inner.filesize += needed_size;
log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{
dysymtab.indirectsymoff,
dysymtab.indirectsymoff + needed_size,
});
var buf = try self.base.allocator.alloc(u8, needed_size);
defer self.base.allocator.free(buf);
var stream = std.io.fixedBufferStream(buf);
var writer = stream.writer();
stubs.reserved1 = 0;
for (self.stubs_map.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
got.reserved1 = nstubs;
for (self.got_entries_map.keys()) |key| {
switch (key.where) {
.undef => {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index);
},
.local => {
try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
},
}
}
la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
for (self.stubs_map.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
}
pub fn deinit(self: *MachO) void {
if (build_options.have_llvm) {
if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
@ -3812,12 +3194,6 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64
try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
}
// // Resolve relocations
// try decl.link.macho.resolveRelocs(self);
// // TODO this requires further investigation: should we dispose of resolved relocs, or keep them
// // so that we can reapply them when moving/growing sections?
// decl.link.macho.relocs.clearAndFree(self.base.allocator);
return symbol;
}
@ -5003,9 +4379,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
fn writeDices(self: *MachO) !void {
if (!self.has_dices) return;
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
const fileoff = seg.inner.fileoff + seg.inner.filesize;
const tracy = trace(@src());
defer tracy.end();
var buf = std.ArrayList(u8).init(self.base.allocator);
defer buf.deinit();
@ -5043,15 +4418,26 @@ fn writeDices(self: *MachO) !void {
} else break;
}
const datasize = @intCast(u32, buf.items.len);
const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
const allocated_size = self.allocatedSizeLinkedit(dice_cmd.dataoff);
const needed_size = @intCast(u32, buf.items.len);
dice_cmd.dataoff = @intCast(u32, fileoff);
dice_cmd.datasize = datasize;
seg.inner.filesize += datasize;
if (needed_size > allocated_size) {
dice_cmd.datasize = 0;
dice_cmd.dataoff = @intCast(u32, self.findFreeSpaceLinkedit(
needed_size,
@alignOf(macho.data_in_code_entry),
dice_cmd.dataoff,
));
}
dice_cmd.datasize = needed_size;
log.debug("writing data-in-code from 0x{x} to 0x{x}", .{
dice_cmd.dataoff,
dice_cmd.dataoff + dice_cmd.datasize,
});
log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ fileoff, fileoff + datasize });
try self.base.file.?.pwriteAll(buf.items, fileoff);
try self.base.file.?.pwriteAll(buf.items, dice_cmd.dataoff);
self.load_commands_dirty = true;
}
fn writeCodeSignaturePadding(self: *MachO) !void {
@ -5130,7 +4516,7 @@ fn writeExportInfo(self: *MachO) !void {
for (self.globals.items) |sym| {
const sym_name = self.getString(sym.n_strx);
log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
try trie.put(self.base.allocator, .{
.name = sym_name,
@ -5453,30 +4839,16 @@ fn writeStringTable(self: *MachO) !void {
self.strtab_needs_relocation = false;
}
symtab.strsize = @intCast(u32, needed_size);
log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
log.debug("writing string table from 0x{x} to 0x{x}", .{
symtab.stroff,
symtab.stroff + symtab.strsize,
});
try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);
self.load_commands_dirty = true;
self.strtab_dirty = false;
}
fn writeStringTableZld(self: *MachO) !void {
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)));
seg.inner.filesize += symtab.strsize;
log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);
if (symtab.strsize > self.strtab.items.len) {
// This is potentially the last section, so we need to pad it out.
try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1);
}
}
fn updateLinkeditSegmentSizes(self: *MachO) !void {
if (!self.load_commands_dirty) return;