authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-02 21:24:38+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-04 15:31:47+02:00
logc4054f8e0a6a7dea8570507d2a38b38a392ddd0d
treed1ee1b48a87ab9aaded253a69c9201d1dfd44153
parent737a8bf2041158b3036bda9130f9ce3f6c1ad582

Refactor flushing of MachO exe

This commit refactors free space analysis (and VM analysis) somewhat. This is still far from perfect, but at least it's not using any hardcoded values. This commit also reorganizes different flushing bits here and there, so that bit common to Exe and Obj are put in the common path, while Exe-specific are behind an appropriate switch/if statement.

1 files changed, 139 insertions(+), 114 deletions(-)

src/link/MachO.zig+139-114
...@@ -247,14 +247,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -247,14 +247,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
247 const tracy = trace(@src());247 const tracy = trace(@src());
248 defer tracy.end();248 defer tracy.end();
249249
250 switch (self.base.options.output_mode) {
251 .Exe => {},
252 .Obj => return error.TODOImplementWritingObjFiles,
253 .Lib => return error.TODOImplementWritingLibFiles,
254 }
255
256 try self.writeExportTrie();
257
258 // Unfortunately these have to be buffered and done at the end because MachO does not allow250 // Unfortunately these have to be buffered and done at the end because MachO does not allow
259 // mixing local, global and undefined symbols within a symbol table.251 // mixing local, global and undefined symbols within a symbol table.
260 try self.writeAllGlobalSymbols();252 try self.writeAllGlobalSymbols();
...@@ -262,79 +254,76 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -262,79 +254,76 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
262254
263 try self.writeStringTable();255 try self.writeStringTable();
264256
257 switch (self.base.options.output_mode) {
258 .Exe => {
259 if (self.entry_addr) |addr| {
260 // Write export trie.
261 try self.writeExportTrie();
262
263 // Update LC_MAIN with entry offset
264 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
265 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
266 main_cmd.entryoff = addr - text_segment.vmaddr;
267 }
268
269 {
270 // Update dynamic symbol table.
271 const nlocals = @intCast(u32, self.local_symbols.items.len);
272 const nglobals = @intCast(u32, self.global_symbols.items.len);
273 const nundefs = @intCast(u32, self.undef_symbols.items.len);
274 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
275 dysymtab.nlocalsym = nlocals;
276 dysymtab.iextdefsym = nlocals;
277 dysymtab.nextdefsym = nglobals;
278 dysymtab.iundefsym = nlocals + nglobals;
279 dysymtab.nundefsym = nundefs;
280 }
281 {
282 // Write path to dyld loader.
283 var off: usize = @sizeOf(macho.mach_header_64);
284 for (self.load_commands.items) |cmd| {
285 if (cmd == .Dylinker) break;
286 off += cmd.cmdsize();
287 }
288 const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker;
289 off += cmd.name;
290 const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command);
291 log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off });
292 try self.addPadding(padding, off);
293 log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off});
294 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off);
295 }
296 {
297 // Write path to libSystem.
298 var off: usize = @sizeOf(macho.mach_header_64);
299 for (self.load_commands.items) |cmd| {
300 if (cmd == .Dylib) break;
301 off += cmd.cmdsize();
302 }
303 const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib;
304 off += cmd.dylib.name;
305 const padding = cmd.cmdsize - @sizeOf(macho.dylib_command);
306 log.debug("writing LC_LOAD_DYLIB padding of size {} at 0x{x}\n", .{ padding, off });
307 try self.addPadding(padding, off);
308 log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off});
309 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off);
310 }
311 },
312 .Obj => {},
313 .Lib => return error.TODOImplementWritingLibFiles,
314 }
315
316 if (self.cmd_table_dirty) try self.writeCmdHeaders();
317
265 {318 {
266 // update Symtab and Dysymtab commands with symbol counts319 // Update symbol table.
267 // TODO this could probably be done incrementally
268 const nlocals = @intCast(u32, self.local_symbols.items.len);320 const nlocals = @intCast(u32, self.local_symbols.items.len);
269 const nglobals = @intCast(u32, self.global_symbols.items.len);321 const nglobals = @intCast(u32, self.global_symbols.items.len);
270 const nundefs = @intCast(u32, self.undef_symbols.items.len);322 const nundefs = @intCast(u32, self.undef_symbols.items.len);
271
272 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;323 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
273 symtab.nsyms = nlocals + nglobals + nundefs;324 symtab.nsyms = nlocals + nglobals + nundefs;
274
275 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
276 dysymtab.nlocalsym = nlocals;
277 dysymtab.iextdefsym = nlocals;
278 dysymtab.nextdefsym = nglobals;
279 dysymtab.iundefsym = nlocals + nglobals;
280 dysymtab.nundefsym = nundefs;
281 }
282 if (self.entry_addr) |addr| {
283 // update LC_MAIN with entry offset
284 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
285 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
286 main_cmd.entryoff = addr - text_segment.vmaddr;
287 }
288 {
289 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
290 for (self.load_commands.items) |cmd| {
291 try cmd.write(&self.base.file.?, last_cmd_offset);
292 last_cmd_offset += cmd.cmdsize();
293 }
294 }
295 {
296 // write __text section
297 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64) * 2;
298 log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[0..1], off });
299 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[0..1]), off);
300 }
301 {
302 // write __got section
303 const text = &self.load_commands.items[self.text_segment_cmd_index.?];
304 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64) * 2 + text.cmdsize();
305 log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[1..2], off });
306 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[1..2]), off);
307 }
308 {
309 // write path to dyld
310 var off: usize = @sizeOf(macho.mach_header_64);
311 for (self.load_commands.items) |cmd| {
312 if (cmd == .Dylinker) break;
313 off += cmd.cmdsize();
314 }
315 const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker;
316 off += cmd.name;
317 const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command);
318 log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off });
319 try self.addPadding(padding, off);
320 log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off});
321 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off);
322 }
323 {
324 // write path to libSystem
325 var off: usize = @sizeOf(macho.mach_header_64);
326 for (self.load_commands.items) |cmd| {
327 if (cmd == .Dylib) break;
328 off += cmd.cmdsize();
329 }
330 const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib;
331 off += cmd.dylib.name;
332 const padding = cmd.cmdsize - @sizeOf(macho.dylib_command);
333 log.debug("writing LC_LOAD_DYLIB padding of size {} at 0x{x}\n", .{ padding, off });
334 try self.addPadding(padding, off);
335 log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off});
336 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off);
337 }325 }
326
338 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {327 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
339 log.debug("flushing. no_entry_point_found = true\n", .{});328 log.debug("flushing. no_entry_point_found = true\n", .{});
340 self.error_flags.no_entry_point_found = true;329 self.error_flags.no_entry_point_found = true;
...@@ -775,7 +764,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -775,7 +764,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
775 return;764 return;
776 },765 },
777 };766 };
778 log.debug("generated code {}\n", .{code});
779767
780 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);768 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
781 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];769 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];
...@@ -784,7 +772,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -784,7 +772,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
784 const name_str_index = try self.makeString(decl_name);772 const name_str_index = try self.makeString(decl_name);
785 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);773 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
786 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });774 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
787 log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]});
788775
789 symbol.* = .{776 symbol.* = .{
790 .n_strx = name_str_index,777 .n_strx = name_str_index,
...@@ -801,7 +788,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -801,7 +788,6 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
801 const text_section = self.sections.items[self.text_section_index.?];788 const text_section = self.sections.items[self.text_section_index.?];
802 const section_offset = symbol.n_value - text_section.addr;789 const section_offset = symbol.n_value - text_section.addr;
803 const file_offset = text_section.offset + section_offset;790 const file_offset = text_section.offset + section_offset;
804 log.debug("file_offset 0x{x}\n", .{file_offset});
805791
806 try self.base.file.?.pwriteAll(code, file_offset);792 try self.base.file.?.pwriteAll(code, file_offset);
807793
...@@ -889,6 +875,12 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {...@@ -889,6 +875,12 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
889}875}
890876
891pub fn populateMissingMetadata(self: *MachO) !void {877pub fn populateMissingMetadata(self: *MachO) !void {
878 switch (self.base.options.output_mode) {
879 .Exe => {},
880 .Obj => return error.TODOImplementWritingObjFiles,
881 .Lib => return error.TODOImplementWritingLibFiles,
882 }
883
892 if (self.pagezero_segment_cmd_index == null) {884 if (self.pagezero_segment_cmd_index == null) {
893 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);885 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
894 try self.load_commands.append(self.base.allocator, .{886 try self.load_commands.append(self.base.allocator, .{
...@@ -934,7 +926,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -934,7 +926,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
934 text_segment.cmdsize += @sizeOf(macho.section_64);926 text_segment.cmdsize += @sizeOf(macho.section_64);
935 text_segment.nsects += 1;927 text_segment.nsects += 1;
936928
937 const file_size = self.base.options.program_code_size_hint;929 const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000);
938 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly?930 const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly?
939 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;931 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
940932
...@@ -946,7 +938,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -946,7 +938,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
946 .addr = text_segment.vmaddr + off,938 .addr = text_segment.vmaddr + off,
947 .size = file_size,939 .size = file_size,
948 .offset = off,940 .offset = off,
949 .@"align" = 12,941 .@"align" = 12, // 2^12 = 4096
950 .reloff = 0,942 .reloff = 0,
951 .nreloc = 0,943 .nreloc = 0,
952 .flags = flags,944 .flags = flags,
...@@ -955,11 +947,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -955,11 +947,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
955 .reserved3 = 0,947 .reserved3 = 0,
956 });948 });
957949
958 text_segment.vmsize = file_size + off;950 text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
959 text_segment.filesize = file_size + off;951 text_segment.filesize = file_size + off;
960
961 log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
962 log.debug("updated text segment {}\n", .{text_segment});
963 }952 }
964 if (self.data_segment_cmd_index == null) {953 if (self.data_segment_cmd_index == null) {
965 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);954 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
...@@ -970,7 +959,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -970,7 +959,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
970 .cmd = macho.LC_SEGMENT_64,959 .cmd = macho.LC_SEGMENT_64,
971 .cmdsize = @sizeOf(macho.segment_command_64),960 .cmdsize = @sizeOf(macho.segment_command_64),
972 .segname = makeStaticString("__DATA"),961 .segname = makeStaticString("__DATA"),
973 .vmaddr = text_segment.vmaddr + text_segment.vmsize, // TODO this should be found when running findFreeSpace962 .vmaddr = text_segment.vmaddr + text_segment.vmsize,
974 .vmsize = 0,963 .vmsize = 0,
975 .fileoff = 0,964 .fileoff = 0,
976 .filesize = 0,965 .filesize = 0,
...@@ -999,7 +988,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -999,7 +988,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
999 .addr = data_segment.vmaddr,988 .addr = data_segment.vmaddr,
1000 .size = file_size,989 .size = file_size,
1001 .offset = off,990 .offset = off,
1002 .@"align" = 3,991 .@"align" = 3, // 2^3 = 8
1003 .reloff = 0,992 .reloff = 0,
1004 .nreloc = 0,993 .nreloc = 0,
1005 .flags = macho.S_REGULAR,994 .flags = macho.S_REGULAR,
...@@ -1012,9 +1001,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1012,9 +1001,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1012 data_segment.vmsize = segment_size;1001 data_segment.vmsize = segment_size;
1013 data_segment.filesize = segment_size;1002 data_segment.filesize = segment_size;
1014 data_segment.fileoff = off;1003 data_segment.fileoff = off;
1015
1016 log.debug("initial got section {}\n", .{self.sections.items[self.got_section_index.?]});
1017 log.debug("updated data segment {}\n", .{data_segment});
1018 }1004 }
1019 if (self.linkedit_segment_cmd_index == null) {1005 if (self.linkedit_segment_cmd_index == null) {
1020 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);1006 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
...@@ -1025,7 +1011,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1025,7 +1011,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1025 .cmd = macho.LC_SEGMENT_64,1011 .cmd = macho.LC_SEGMENT_64,
1026 .cmdsize = @sizeOf(macho.segment_command_64),1012 .cmdsize = @sizeOf(macho.segment_command_64),
1027 .segname = makeStaticString("__LINKEDIT"),1013 .segname = makeStaticString("__LINKEDIT"),
1028 .vmaddr = data_segment.vmaddr + data_segment.vmsize, // TODO this should be found when running findFreeSpace1014 .vmaddr = data_segment.vmaddr + data_segment.vmsize,
1029 .vmsize = 0,1015 .vmsize = 0,
1030 .fileoff = 0,1016 .fileoff = 0,
1031 .filesize = 0,1017 .filesize = 0,
...@@ -1101,11 +1087,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1101,11 +1087,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1101 }1087 }
1102 if (self.dylinker_cmd_index == null) {1088 if (self.dylinker_cmd_index == null) {
1103 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);1089 self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
1104 const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));1090 const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64));
1105 try self.load_commands.append(self.base.allocator, .{1091 try self.load_commands.append(self.base.allocator, .{
1106 .Dylinker = .{1092 .Dylinker = .{
1107 .cmd = macho.LC_LOAD_DYLINKER,1093 .cmd = macho.LC_LOAD_DYLINKER,
1108 .cmdsize = cmdsize,1094 .cmdsize = @intCast(u32, cmdsize),
1109 .name = @sizeOf(macho.dylinker_command),1095 .name = @sizeOf(macho.dylinker_command),
1110 },1096 },
1111 });1097 });
...@@ -1113,7 +1099,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1113,7 +1099,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1113 }1099 }
1114 if (self.libsystem_cmd_index == null) {1100 if (self.libsystem_cmd_index == null) {
1115 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);1101 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
1116 const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));1102 const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64));
1117 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.1103 // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
1118 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.1104 // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
1119 const min_version = 0x10000;1105 const min_version = 0x10000;
...@@ -1126,7 +1112,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1126,7 +1112,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1126 try self.load_commands.append(self.base.allocator, .{1112 try self.load_commands.append(self.base.allocator, .{
1127 .Dylib = .{1113 .Dylib = .{
1128 .cmd = macho.LC_LOAD_DYLIB,1114 .cmd = macho.LC_LOAD_DYLIB,
1129 .cmdsize = cmdsize,1115 .cmdsize = @intCast(u32, cmdsize),
1130 .dylib = dylib,1116 .dylib = dylib,
1131 },1117 },
1132 });1118 });
...@@ -1156,10 +1142,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1156,10 +1142,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1156 dyld_info.export_size = @intCast(u32, file_size);1142 dyld_info.export_size = @intCast(u32, file_size);
11571143
1158 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);1144 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1159 linkedit.vmsize = 4 * segment_size;1145 linkedit.vmsize += segment_size;
1160 linkedit.fileoff = off;1146 linkedit.fileoff = off;
1161
1162 log.debug("updated linkedit segment {}\n", .{linkedit});
1163 }1147 }
1164 }1148 }
1165 {1149 {
...@@ -1172,6 +1156,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1172,6 +1156,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1172 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });1156 log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
1173 symtab.symoff = off;1157 symtab.symoff = off;
1174 symtab.nsyms = @intCast(u32, nsyms);1158 symtab.nsyms = @intCast(u32, nsyms);
1159
1160 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1161 linkedit.vmsize += segment_size;
1175 }1162 }
1176 if (symtab.stroff == 0) {1163 if (symtab.stroff == 0) {
1177 try self.string_table.append(self.base.allocator, 0);1164 try self.string_table.append(self.base.allocator, 0);
...@@ -1180,6 +1167,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1180,6 +1167,9 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1180 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });1167 log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
1181 symtab.stroff = off;1168 symtab.stroff = off;
1182 symtab.strsize = file_size;1169 symtab.strsize = file_size;
1170
1171 const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
1172 linkedit.vmsize += segment_size;
1183 }1173 }
1184 }1174 }
1185 if (self.dyld_stub_binder_index == null) {1175 if (self.dyld_stub_binder_index == null) {
...@@ -1215,13 +1205,11 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -1215,13 +1205,11 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
1215 break :blk text_section.addr;1205 break :blk text_section.addr;
1216 }1206 }
1217 };1207 };
1218 log.debug("computed symbol address 0x{x}\n", .{addr});
12191208
1220 const expand_text_section = block_placement == null or block_placement.?.next == null;1209 const expand_text_section = block_placement == null or block_placement.?.next == null;
1221 if (expand_text_section) {1210 if (expand_text_section) {
1222 const text_capacity = self.allocatedSize(text_section.offset);1211 const text_capacity = self.allocatedSize(text_section.offset);
1223 const needed_size = (addr + new_block_size) - text_section.addr;1212 const needed_size = (addr + new_block_size) - text_section.addr;
1224 log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
1225 assert(needed_size <= text_capacity); // TODO handle growth1213 assert(needed_size <= text_capacity); // TODO handle growth
12261214
1227 self.last_text_block = text_block;1215 self.last_text_block = text_block;
...@@ -1276,18 +1264,6 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {...@@ -1276,18 +1264,6 @@ fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
1276 return self.makeString(new_name);1264 return self.makeString(new_name);
1277}1265}
12781266
1279fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
1280 const size = @intCast(Int, min_size);
1281 if (size % alignment == 0) return size;
1282
1283 const div = size / alignment;
1284 return (div + 1) * alignment;
1285}
1286
1287fn commandSize(min_size: anytype) u32 {
1288 return alignSize(u32, min_size, @sizeOf(u64));
1289}
1290
1291fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {1267fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
1292 if (size == 0) return;1268 if (size == 0) return;
12931269
...@@ -1424,7 +1400,7 @@ fn writeAllUndefSymbols(self: *MachO) !void {...@@ -1424,7 +1400,7 @@ fn writeAllUndefSymbols(self: *MachO) !void {
1424}1400}
14251401
1426fn writeExportTrie(self: *MachO) !void {1402fn writeExportTrie(self: *MachO) !void {
1427 if (self.entry_addr == null) return;1403 assert(self.entry_addr != null);
14281404
1429 // TODO implement mechanism for generating a prefix tree of the exported symbols1405 // TODO implement mechanism for generating a prefix tree of the exported symbols
1430 // single branch export trie1406 // single branch export trie
...@@ -1440,7 +1416,6 @@ fn writeExportTrie(self: *MachO) !void {...@@ -1440,7 +1416,6 @@ fn writeExportTrie(self: *MachO) !void {
1440 const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr);1416 const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr);
1441 buf[10] = @intCast(u8, written) + 1;1417 buf[10] = @intCast(u8, written) + 1;
1442 buf[11] = 0;1418 buf[11] = 0;
1443 log.debug("WAT = {}, {x}\n", .{ written, buf[0..] });
14441419
1445 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;1420 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
1446 try self.base.file.?.pwriteAll(buf[0..], dyld_info.export_off);1421 try self.base.file.?.pwriteAll(buf[0..], dyld_info.export_off);
...@@ -1450,7 +1425,6 @@ fn writeStringTable(self: *MachO) !void {...@@ -1450,7 +1425,6 @@ fn writeStringTable(self: *MachO) !void {
1450 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;1425 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1451 const allocated_size = self.allocatedSize(symtab.stroff);1426 const allocated_size = self.allocatedSize(symtab.stroff);
1452 const needed_size = self.string_table.items.len;1427 const needed_size = self.string_table.items.len;
1453 log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
14541428
1455 if (needed_size > allocated_size) {1429 if (needed_size > allocated_size) {
1456 symtab.strsize = 0;1430 symtab.strsize = 0;
...@@ -1462,11 +1436,62 @@ fn writeStringTable(self: *MachO) !void {...@@ -1462,11 +1436,62 @@ fn writeStringTable(self: *MachO) !void {
14621436
1463 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);1437 try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
14641438
1465 // FIXME1439 // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of
1440 // doing dynamic updates like this.
1466 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;1441 const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
1467 linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff;1442 linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff;
1468}1443}
14691444
1445fn writeCmdHeaders(self: *MachO) !void {
1446 assert(self.cmd_table_dirty);
1447
1448 // Write all load command headers first.
1449 // Since command sizes are up-to-date and accurate, we will correctly
1450 // leave space for any section headers that any of the segment load
1451 // commands might consist of.
1452 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
1453 for (self.load_commands.items) |cmd| {
1454 try cmd.write(&self.base.file.?, last_cmd_offset);
1455 last_cmd_offset += cmd.cmdsize();
1456 }
1457 {
1458 // write __text section header
1459 const off = if (self.text_segment_cmd_index) |text_segment_index| blk: {
1460 var i: usize = 0;
1461 var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
1462 while (i < text_segment_index) : (i += 1) {
1463 cmdsize += self.load_commands.items[i].cmdsize();
1464 }
1465 break :blk cmdsize;
1466 } else {
1467 // If we've landed in here, we are building a MachO object file, so we have
1468 // only one, noname segment to append this section header to.
1469 return error.TODOImplementWritingObjFiles;
1470 };
1471 const idx = self.text_section_index.?;
1472 log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
1473 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
1474 }
1475 {
1476 // write __got section header
1477 const off = if (self.data_segment_cmd_index) |data_segment_index| blk: {
1478 var i: usize = 0;
1479 var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
1480 while (i < data_segment_index) : (i += 1) {
1481 cmdsize += self.load_commands.items[i].cmdsize();
1482 }
1483 break :blk cmdsize;
1484 } else {
1485 // If we've landed in here, we are building a MachO object file, so we have
1486 // only one, noname segment to append this section header to.
1487 return error.TODOImplementWritingObjFiles;
1488 };
1489 const idx = self.got_section_index.?;
1490 log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
1491 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
1492 }
1493}
1494
1470/// Writes Mach-O file header.1495/// Writes Mach-O file header.
1471/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping1496/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
1472/// variables.1497/// variables.