Commit 57a81bb559
Changed files (1)
src
link
src/link/MachO.zig
@@ -27,6 +27,10 @@ const LoadCommand = union(enum) {
LinkeditData: macho.linkedit_data_command,
Symtab: macho.symtab_command,
Dysymtab: macho.dysymtab_command,
+ DyldInfo: macho.dyld_info_command,
+ Dylinker: macho.dylinker_command,
+ Dylib: macho.dylib_command,
+ EntryPoint: macho.entry_point_command,
pub fn cmdsize(self: LoadCommand) u32 {
return switch (self) {
@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {
.LinkeditData => |x| x.cmdsize,
.Symtab => |x| x.cmdsize,
.Dysymtab => |x| x.cmdsize,
+ .DyldInfo => |x| x.cmdsize,
+ .Dylinker => |x| x.cmdsize,
+ .Dylib => |x| x.cmdsize,
+ .EntryPoint => |x| x.cmdsize,
};
}
@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {
.LinkeditData => |cmd| writeGeneric(cmd, file, offset),
.Symtab => |cmd| writeGeneric(cmd, file, offset),
.Dysymtab => |cmd| writeGeneric(cmd, file, offset),
+ .DyldInfo => |cmd| writeGeneric(cmd, file, offset),
+ .Dylinker => |cmd| writeGeneric(cmd, file, offset),
+ .Dylib => |cmd| writeGeneric(cmd, file, offset),
+ .EntryPoint => |cmd| writeGeneric(cmd, file, offset),
};
}
@@ -56,24 +68,42 @@ base: File,
/// Table of all load commands
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
+/// __PAGEZERO segment
+pagezero_segment_cmd_index: ?u16 = null,
+/// __TEXT segment
+text_segment_cmd_index: ?u16 = null,
+/// __DATA segment
+data_segment_cmd_index: ?u16 = null,
+/// __LINKEDIT segment
+linkedit_segment_cmd_index: ?u16 = null,
segment_cmd_index: ?u16 = null,
+/// Dyld info
+dyld_info_cmd_index: ?u16 = null,
+/// Symbol table
symtab_cmd_index: ?u16 = null,
+/// Dynamic symbol table
dysymtab_cmd_index: ?u16 = null,
+/// Path to dyld linker
+dylinker_cmd_index: ?u16 = null,
+/// Path to libSystem
+libsystem_cmd_index: ?u16 = null,
+/// Data-in-code section of __LINKEDIT segment
data_in_code_cmd_index: ?u16 = null,
+/// Address to entry point function
+function_starts_cmd_index: ?u16 = null,
+/// Main/entry point
+/// Specifies offset wrt __TEXT segment start address to the main entry point
+/// of the binary.
+main_cmd_index: ?u16 = null,
/// Table of all sections
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
-/// __TEXT segment sections
+/// __TEXT,__text section
text_section_index: ?u16 = null,
-cstring_section_index: ?u16 = null,
-const_text_section_index: ?u16 = null,
-stubs_section_index: ?u16 = null,
-stub_helper_section_index: ?u16 = null,
-/// __DATA segment sections
+/// __DATA,__got section
got_section_index: ?u16 = null,
-const_data_section_index: ?u16 = null,
entry_addr: ?u64 = null,
@@ -734,11 +764,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
.n_desc = 0,
.n_value = addr,
};
+ self.offset_table.items[decl.link.macho.offset_table_index.?] = addr;
// Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
try self.updateDeclExports(module, decl, decl_exports);
try self.writeSymbol(decl.link.macho.symbol_table_index.?);
+ try self.writeOffsetTableEntry(decl.link.macho.offset_table_index.?);
const text_section = self.sections.items[self.text_section_index.?];
const section_offset = symbol.n_value - text_section.addr;
@@ -778,6 +810,25 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
}
pub fn populateMissingMetadata(self: *MachO) !void {
+ if (self.pagezero_segment_cmd_index == null) {
+ self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
+ try self.load_commands.append(self.base.allocator, .{
+ .Segment = .{
+ .cmd = macho.LC_SEGMENT_64,
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = makeStaticString("__PAGEZERO"),
+ .vmaddr = 0,
+ .vmsize = 0x1000, // size always set to 4GB
+ .fileoff = 0,
+ .filesize = 0,
+ .maxprot = 0,
+ .initprot = 0,
+ .nsects = 0,
+ .flags = 0,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
if (self.segment_cmd_index == null) {
self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
@@ -818,7 +869,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
segment.nsects += 1;
const file_size = self.base.options.program_code_size_hint;
- const off = @intCast(u32, self.findFreeSpace(file_size, 1));
+ const off = @intCast(u32, self.findFreeSpace(file_size, 64));
const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
@@ -829,7 +880,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
.addr = 0,
.size = file_size,
.offset = off,
- .@"align" = 0x1000,
+ .@"align" = 12,
.reloff = 0,
.nreloc = 0,
.flags = flags,
@@ -843,6 +894,43 @@ pub fn populateMissingMetadata(self: *MachO) !void {
segment.fileoff = off;
log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
+ log.debug("update segment {}\n", .{segment});
+ }
+ if (self.got_section_index == null) {
+ self.got_section_index = @intCast(u16, self.sections.items.len);
+ const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
+ const text_sect = &self.sections.items[self.text_section_index.?];
+ segment.cmdsize += @sizeOf(macho.section_64);
+ segment.nsects += 1;
+
+ const p_align = @sizeOf(u64);
+ const file_size = p_align * self.base.options.symbol_count_hint;
+ const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
+
+ log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
+
+ const padding_size = off - text_sect.offset - text_sect.size;
+
+ try self.sections.append(self.base.allocator, .{
+ .sectname = makeStaticString("__got"),
+ .segname = makeStaticString("__DATA"),
+ .addr = text_sect.addr + text_sect.size + padding_size,
+ .size = file_size,
+ .offset = off,
+ .@"align" = 3,
+ .reloff = 0,
+ .nreloc = 0,
+ .flags = macho.S_REGULAR,
+ .reserved1 = 0,
+ .reserved2 = 0,
+ .reserved3 = 0,
+ });
+
+ segment.vmsize += file_size + padding_size;
+ segment.filesize += file_size + padding_size;
+
+ log.debug("initial got section {}\n", .{self.sections.items[self.got_section_index.?]});
+ log.debug("update segment {}\n", .{segment});
}
{
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
@@ -875,8 +963,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
const addr = blk: {
if (self.last_text_block) |last| {
const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
- const end_addr = last_symbol.n_value + last.size;
- const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
+ const ideal_capacity = last.size * alloc_num / alloc_den;
+ const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
+ const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
block_placement = last;
break :blk new_start_addr;
} else {
@@ -893,12 +982,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
assert(needed_size <= text_capacity); // TODO handle growth
self.last_text_block = text_block;
- text_section.size = needed_size;
- segment.vmsize = needed_size;
- segment.filesize = needed_size;
- if (alignment < text_section.@"align") {
- text_section.@"align" = @intCast(u32, alignment);
- }
}
text_block.size = new_block_size;
@@ -961,11 +1044,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
const hdr_size: u64 = @sizeOf(macho.mach_header_64);
- if (start < hdr_size)
- return hdr_size;
-
+ if (start < hdr_size) return hdr_size;
const end = start + satMul(size, alloc_num) / alloc_den;
-
{
const off = @sizeOf(macho.mach_header_64);
var tight_size: u64 = 0;
@@ -978,7 +1058,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
return test_end;
}
}
-
for (self.sections.items) |section| {
const increased_size = satMul(section.size, alloc_num) / alloc_den;
const test_end = section.offset + increased_size;
@@ -986,7 +1065,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
return test_end;
}
}
-
if (self.symtab_cmd_index) |symtab_index| {
const symtab = self.load_commands.items[symtab_index].Symtab;
{
@@ -1005,7 +1083,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
}
}
}
-
return null;
}
@@ -1048,6 +1125,16 @@ fn writeSymbol(self: *MachO, index: usize) !void {
try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
}
+fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
+ const sect = &self.sections.items[self.got_section_index.?];
+ const endian = self.base.options.target.cpu.arch.endian();
+ var buf: [@sizeOf(u64)]u8 = undefined;
+ mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
+ const off = sect.offset + @sizeOf(u64) * index;
+ log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off });
+ try self.base.file.?.pwriteAll(&buf, off);
+}
+
/// Writes Mach-O file header.
/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
/// variables.