Re-draft required elements for minimal MachO binary

This commit is contained in:
Jakub Konka
2020-09-29 08:40:00 +02:00
parent 0e2d858d69
commit 57a81bb559

View File

@@ -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.