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) {...@@ -27,6 +27,10 @@ const LoadCommand = union(enum) {
27 LinkeditData: macho.linkedit_data_command,27 LinkeditData: macho.linkedit_data_command,
28 Symtab: macho.symtab_command,28 Symtab: macho.symtab_command,
29 Dysymtab: macho.dysymtab_command,29 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
31 pub fn cmdsize(self: LoadCommand) u32 {35 pub fn cmdsize(self: LoadCommand) u32 {
32 return switch (self) {36 return switch (self) {
...@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {...@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {
34 .LinkeditData => |x| x.cmdsize,38 .LinkeditData => |x| x.cmdsize,
35 .Symtab => |x| x.cmdsize,39 .Symtab => |x| x.cmdsize,
36 .Dysymtab => |x| x.cmdsize,40 .Dysymtab => |x| x.cmdsize,
41 .DyldInfo => |x| x.cmdsize,
42 .Dylinker => |x| x.cmdsize,
43 .Dylib => |x| x.cmdsize,
44 .EntryPoint => |x| x.cmdsize,
37 };45 };
38 }46 }
3947
...@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {...@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {
43 .LinkeditData => |cmd| writeGeneric(cmd, file, offset),51 .LinkeditData => |cmd| writeGeneric(cmd, file, offset),
44 .Symtab => |cmd| writeGeneric(cmd, file, offset),52 .Symtab => |cmd| writeGeneric(cmd, file, offset),
45 .Dysymtab => |cmd| writeGeneric(cmd, file, offset),53 .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),
46 };58 };
47 }59 }
4860
...@@ -56,24 +68,42 @@ base: File,...@@ -56,24 +68,42 @@ base: File,
5668
57/// Table of all load commands69/// Table of all load commands
58load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},70load_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,
59segment_cmd_index: ?u16 = null,79segment_cmd_index: ?u16 = null,
80/// Dyld info
81dyld_info_cmd_index: ?u16 = null,
82/// Symbol table
60symtab_cmd_index: ?u16 = null,83symtab_cmd_index: ?u16 = null,
84/// Dynamic symbol table
61dysymtab_cmd_index: ?u16 = null,85dysymtab_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
62data_in_code_cmd_index: ?u16 = null,91data_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
64/// Table of all sections99/// Table of all sections
65sections: std.ArrayListUnmanaged(macho.section_64) = .{},100sections: std.ArrayListUnmanaged(macho.section_64) = .{},
66101
67/// __TEXT segment sections102/// __TEXT,__text section
68text_section_index: ?u16 = null,103text_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 sections105/// __DATA,__got section
75got_section_index: ?u16 = null,106got_section_index: ?u16 = null,
76const_data_section_index: ?u16 = null,
77107
78entry_addr: ?u64 = null,108entry_addr: ?u64 = null,
79109
...@@ -734,11 +764,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -734,11 +764,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
734 .n_desc = 0,764 .n_desc = 0,
735 .n_value = addr,765 .n_value = addr,
736 };766 };
767 self.offset_table.items[decl.link.macho.offset_table_index.?] = addr;
737768
738 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.769 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
739 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};770 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
740 try self.updateDeclExports(module, decl, decl_exports);771 try self.updateDeclExports(module, decl, decl_exports);
741 try self.writeSymbol(decl.link.macho.symbol_table_index.?);772 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
773 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index.?);
742774
743 const text_section = self.sections.items[self.text_section_index.?];775 const text_section = self.sections.items[self.text_section_index.?];
744 const section_offset = symbol.n_value - text_section.addr;776 const section_offset = symbol.n_value - text_section.addr;
...@@ -778,6 +810,25 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {...@@ -778,6 +810,25 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
778}810}
779811
780pub fn populateMissingMetadata(self: *MachO) !void {812pub 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 }
781 if (self.segment_cmd_index == null) {832 if (self.segment_cmd_index == null) {
782 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);833 self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
783 try self.load_commands.append(self.base.allocator, .{834 try self.load_commands.append(self.base.allocator, .{
...@@ -818,7 +869,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -818,7 +869,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
818 segment.nsects += 1;869 segment.nsects += 1;
819870
820 const file_size = self.base.options.program_code_size_hint;871 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));
822 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;873 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
823874
824 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });875 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 {...@@ -829,7 +880,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
829 .addr = 0,880 .addr = 0,
830 .size = file_size,881 .size = file_size,
831 .offset = off,882 .offset = off,
832 .@"align" = 0x1000,883 .@"align" = 12,
833 .reloff = 0,884 .reloff = 0,
834 .nreloc = 0,885 .nreloc = 0,
835 .flags = flags,886 .flags = flags,
...@@ -843,6 +894,43 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -843,6 +894,43 @@ pub fn populateMissingMetadata(self: *MachO) !void {
843 segment.fileoff = off;894 segment.fileoff = off;
844895
845 log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});896 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});
846 }934 }
847 {935 {
848 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;936 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,...@@ -875,8 +963,9 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
875 const addr = blk: {963 const addr = blk: {
876 if (self.last_text_block) |last| {964 if (self.last_text_block) |last| {
877 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];965 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
878 const end_addr = last_symbol.n_value + last.size;966 const ideal_capacity = last.size * alloc_num / alloc_den;
879 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);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);
880 block_placement = last;969 block_placement = last;
881 break :blk new_start_addr;970 break :blk new_start_addr;
882 } else {971 } else {
...@@ -893,12 +982,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -893,12 +982,6 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
893 assert(needed_size <= text_capacity); // TODO handle growth982 assert(needed_size <= text_capacity); // TODO handle growth
894983
895 self.last_text_block = text_block;984 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 }
902 }985 }
903 text_block.size = new_block_size;986 text_block.size = new_block_size;
904987
...@@ -961,11 +1044,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {...@@ -961,11 +1044,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
9611044
962fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {1045fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
963 const hdr_size: u64 = @sizeOf(macho.mach_header_64);1046 const hdr_size: u64 = @sizeOf(macho.mach_header_64);
964 if (start < hdr_size)1047 if (start < hdr_size) return hdr_size;
965 return hdr_size;
966
967 const end = start + satMul(size, alloc_num) / alloc_den;1048 const end = start + satMul(size, alloc_num) / alloc_den;
968
969 {1049 {
970 const off = @sizeOf(macho.mach_header_64);1050 const off = @sizeOf(macho.mach_header_64);
971 var tight_size: u64 = 0;1051 var tight_size: u64 = 0;
...@@ -978,7 +1058,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -978,7 +1058,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
978 return test_end;1058 return test_end;
979 }1059 }
980 }1060 }
981
982 for (self.sections.items) |section| {1061 for (self.sections.items) |section| {
983 const increased_size = satMul(section.size, alloc_num) / alloc_den;1062 const increased_size = satMul(section.size, alloc_num) / alloc_den;
984 const test_end = section.offset + increased_size;1063 const test_end = section.offset + increased_size;
...@@ -986,7 +1065,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -986,7 +1065,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
986 return test_end;1065 return test_end;
987 }1066 }
988 }1067 }
989
990 if (self.symtab_cmd_index) |symtab_index| {1068 if (self.symtab_cmd_index) |symtab_index| {
991 const symtab = self.load_commands.items[symtab_index].Symtab;1069 const symtab = self.load_commands.items[symtab_index].Symtab;
992 {1070 {
...@@ -1005,7 +1083,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {...@@ -1005,7 +1083,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
1005 }1083 }
1006 }1084 }
1007 }1085 }
1008
1009 return null;1086 return null;
1010}1087}
10111088
...@@ -1048,6 +1125,16 @@ fn writeSymbol(self: *MachO, index: usize) !void {...@@ -1048,6 +1125,16 @@ fn writeSymbol(self: *MachO, index: usize) !void {
1048 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1125 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1049}1126}
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
1051/// Writes Mach-O file header.1138/// Writes Mach-O file header.
1052/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping1139/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
1053/// variables.1140/// variables.