authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-09-29 08:40:00+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-04 15:31:47+02:00
log57a81bb5596f7487d728cf9a91013347e368912b
tree2d33fad5d252c2390aad8d02c2206ed6c0f26041
parent0e2d858d69ee1595bb58d936f83f0e0248d54d68

Re-draft required elements for minimal MachO binary


1 files changed, 111 insertions(+), 24 deletions(-)

src/link/MachO.zig+111-24
......@@ -27,6 +27,10 @@ const LoadCommand = union(enum) {
2727 LinkeditData: macho.linkedit_data_command,
2828 Symtab: macho.symtab_command,
2929 Dysymtab: macho.dysymtab_command,
30 DyldInfo: macho.dyld_info_command,
31 Dylinker: macho.dylinker_command,
32 Dylib: macho.dylib_command,
33 EntryPoint: macho.entry_point_command,
3034
3135 pub fn cmdsize(self: LoadCommand) u32 {
3236 return switch (self) {
......@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {
3438 .LinkeditData => |x| x.cmdsize,
3539 .Symtab => |x| x.cmdsize,
3640 .Dysymtab => |x| x.cmdsize,
41 .DyldInfo => |x| x.cmdsize,
42 .Dylinker => |x| x.cmdsize,
43 .Dylib => |x| x.cmdsize,
44 .EntryPoint => |x| x.cmdsize,
3745 };
3846 }
3947
......@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {
4351 .LinkeditData => |cmd| writeGeneric(cmd, file, offset),
4452 .Symtab => |cmd| writeGeneric(cmd, file, offset),
4553 .Dysymtab => |cmd| writeGeneric(cmd, file, offset),
54 .DyldInfo => |cmd| writeGeneric(cmd, file, offset),
55 .Dylinker => |cmd| writeGeneric(cmd, file, offset),
56 .Dylib => |cmd| writeGeneric(cmd, file, offset),
57 .EntryPoint => |cmd| writeGeneric(cmd, file, offset),
4658 };
4759 }
4860
......@@ -56,24 +68,42 @@ base: File,
5668
5769/// Table of all load commands
5870load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
71/// __PAGEZERO segment
72pagezero_segment_cmd_index: ?u16 = null,
73/// __TEXT segment
74text_segment_cmd_index: ?u16 = null,
75/// __DATA segment
76data_segment_cmd_index: ?u16 = null,
77/// __LINKEDIT segment
78linkedit_segment_cmd_index: ?u16 = null,
5979segment_cmd_index: ?u16 = null,
80/// Dyld info
81dyld_info_cmd_index: ?u16 = null,
82/// Symbol table
6083symtab_cmd_index: ?u16 = null,
84/// Dynamic symbol table
6185dysymtab_cmd_index: ?u16 = null,
86/// Path to dyld linker
87dylinker_cmd_index: ?u16 = null,
88/// Path to libSystem
89libsystem_cmd_index: ?u16 = null,
90/// Data-in-code section of __LINKEDIT segment
6291data_in_code_cmd_index: ?u16 = null,
92/// Address to entry point function
93function_starts_cmd_index: ?u16 = null,
94/// Main/entry point
95/// Specifies offset wrt __TEXT segment start address to the main entry point
96/// of the binary.
97main_cmd_index: ?u16 = null,
6398
6499/// Table of all sections
65100sections: std.ArrayListUnmanaged(macho.section_64) = .{},
66101
67/// __TEXT segment sections
102/// __TEXT,__text section
68103text_section_index: ?u16 = null,
69cstring_section_index: ?u16 = null,
70const_text_section_index: ?u16 = null,
71stubs_section_index: ?u16 = null,
72stub_helper_section_index: ?u16 = null,
73104
74/// __DATA segment sections
105/// __DATA,__got section
75106got_section_index: ?u16 = null,
76const_data_section_index: ?u16 = null,
77107
78108entry_addr: ?u64 = null,
79109
......@@ -734,11 +764,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
734764 .n_desc = 0,
735765 .n_value = addr,
736766 };
767 self.offset_table.items[decl.link.macho.offset_table_index.?] = addr;
737768
738769 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
739770 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
740771 try self.updateDeclExports(module, decl, decl_exports);
741772 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
773 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index.?);
742774
743775 const text_section = self.sections.items[self.text_section_index.?];
744776 const section_offset = symbol.n_value - text_section.addr;
......@@ -778,6 +810,25 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
778810}
779811
780812pub fn populateMissingMetadata(self: *MachO) !void {
813 if (self.pagezero_segment_cmd_index == null) {
814 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
815 try self.load_commands.append(self.base.allocator, .{
816 .Segment = .{
817 .cmd = macho.LC_SEGMENT_64,
818 .cmdsize = @sizeOf(macho.segment_command_64),
819 .segname = makeStaticString("__PAGEZERO"),
820 .vmaddr = 0,
821 .vmsize = 0x1000, // size always set to 4GB
822 .fileoff = 0,
823 .filesize = 0,
824 .maxprot = 0,
825 .initprot = 0,
826 .nsects = 0,
827 .flags = 0,
828 },
829 });
830 self.cmd_table_dirty = true;
831 }
781832 if (self.segment_cmd_index == null) {
782833 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
783834 try self.load_commands.append(self.base.allocator, .{
......@@ -818,7 +869,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
818869 segment.nsects += 1;
819870
820871 const file_size = self.base.options.program_code_size_hint;
821 const off = @intCast(u32, self.findFreeSpace(file_size, 1));
872 const off = @intCast(u32, self.findFreeSpace(file_size, 64));
822873 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
823874
824875 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 {
829880 .addr = 0,
830881 .size = file_size,
831882 .offset = off,
832 .@"align" = 0x1000,
883 .@"align" = 12,
833884 .reloff = 0,
834885 .nreloc = 0,
835886 .flags = flags,
......@@ -843,6 +894,43 @@ pub fn populateMissingMetadata(self: *MachO) !void {
843894 segment.fileoff = off;
844895
845896 log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
897 log.debug("update segment {}\n", .{segment});
898 }
899 if (self.got_section_index == null) {
900 self.got_section_index = @intCast(u16, self.sections.items.len);
901 const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
902 const text_sect = &self.sections.items[self.text_section_index.?];
903 segment.cmdsize += @sizeOf(macho.section_64);
904 segment.nsects += 1;
905
906 const p_align = @sizeOf(u64);
907 const file_size = p_align * self.base.options.symbol_count_hint;
908 const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
909
910 log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
911
912 const padding_size = off - text_sect.offset - text_sect.size;
913
914 try self.sections.append(self.base.allocator, .{
915 .sectname = makeStaticString("__got"),
916 .segname = makeStaticString("__DATA"),
917 .addr = text_sect.addr + text_sect.size + padding_size,
918 .size = file_size,
919 .offset = off,
920 .@"align" = 3,
921 .reloff = 0,
922 .nreloc = 0,
923 .flags = macho.S_REGULAR,
924 .reserved1 = 0,
925 .reserved2 = 0,
926 .reserved3 = 0,
927 });
928
929 segment.vmsize += file_size + padding_size;
930 segment.filesize += file_size + padding_size;
931
932 log.debug("initial got section {}\n", .{self.sections.items[self.got_section_index.?]});
933 log.debug("update segment {}\n", .{segment});
846934 }
847935 {
848936 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,
875963 const addr = blk: {
876964 if (self.last_text_block) |last| {
877965 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
878 const end_addr = last_symbol.n_value + last.size;
879 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
966 const ideal_capacity = last.size * alloc_num / alloc_den;
967 const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
968 const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
880969 block_placement = last;
881970 break :blk new_start_addr;
882971 } else {
......@@ -893,12 +982,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
893982 assert(needed_size <= text_capacity); // TODO handle growth
894983
895984 self.last_text_block = text_block;
896 text_section.size = needed_size;
897 segment.vmsize = needed_size;
898 segment.filesize = needed_size;
899 if (alignment < text_section.@"align") {
900 text_section.@"align" = @intCast(u32, alignment);
901 }
902985 }
903986 text_block.size = new_block_size;
904987
......@@ -961,11 +1044,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
9611044
9621045fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
9631046 const hdr_size: u64 = @sizeOf(macho.mach_header_64);
964 if (start < hdr_size)
965 return hdr_size;
966
1047 if (start < hdr_size) return hdr_size;
9671048 const end = start + satMul(size, alloc_num) / alloc_den;
968
9691049 {
9701050 const off = @sizeOf(macho.mach_header_64);
9711051 var tight_size: u64 = 0;
......@@ -978,7 +1058,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
9781058 return test_end;
9791059 }
9801060 }
981
9821061 for (self.sections.items) |section| {
9831062 const increased_size = satMul(section.size, alloc_num) / alloc_den;
9841063 const test_end = section.offset + increased_size;
......@@ -986,7 +1065,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
9861065 return test_end;
9871066 }
9881067 }
989
9901068 if (self.symtab_cmd_index) |symtab_index| {
9911069 const symtab = self.load_commands.items[symtab_index].Symtab;
9921070 {
......@@ -1005,7 +1083,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
10051083 }
10061084 }
10071085 }
1008
10091086 return null;
10101087}
10111088
......@@ -1048,6 +1125,16 @@ fn writeSymbol(self: *MachO, index: usize) !void {
10481125 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
10491126}
10501127
1128fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
1129 const sect = &self.sections.items[self.got_section_index.?];
1130 const endian = self.base.options.target.cpu.arch.endian();
1131 var buf: [@sizeOf(u64)]u8 = undefined;
1132 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1133 const off = sect.offset + @sizeOf(u64) * index;
1134 log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off });
1135 try self.base.file.?.pwriteAll(&buf, off);
1136}
1137
10511138/// Writes Mach-O file header.
10521139/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
10531140/// variables.