| author | |
| committer | |
| log | 9306dbd6194251e816f11bc8f420fac0d1ca8835 |
| tree | d77007ecc023cd179070e8c3795f2a3972305bb1 |
| parent | 42b1b6be90fc1034875a6e16cf3cbe1c9d6030ca |
| signature |
Fixes a bug where the last written load command would accidentally
override the beginning of the __text section. Also defines missing
MachO constants and relocation structs/enums.
Signed-off-by: Jakub Konka <kubkon@jakubkonka.com>2 files changed, 132 insertions(+), 20 deletions(-)
lib/std/macho.zig+110-7| ... | @@ -647,6 +647,32 @@ pub const nlist_64 = extern struct { | ... | @@ -647,6 +647,32 @@ pub const nlist_64 = extern struct { |
| 647 | n_value: u64, | 647 | n_value: u64, |
| 648 | }; | 648 | }; |
| 649 | 649 | ||
| 650 | /// Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD | ||
| 651 | /// format. The modifications from the original format were changing the value | ||
| 652 | /// of the r_symbolnum field for "local" (r_extern == 0) relocation entries. | ||
| 653 | /// This modification is required to support symbols in an arbitrary number of | ||
| 654 | /// sections not just the three sections (text, data and bss) in a 4.3BSD file. | ||
| 655 | /// Also the last 4 bits have had the r_type tag added to them. | ||
| 656 | pub const relocation_info = packed struct { | ||
| 657 | /// offset in the section to what is being relocated | ||
| 658 | r_address: i32, | ||
| 659 | |||
| 660 | /// symbol index if r_extern == 1 or section ordinal if r_extern == 0 | ||
| 661 | r_symbolnum: u24, | ||
| 662 | |||
| 663 | /// was relocated pc relative already | ||
| 664 | r_pcrel: u1, | ||
| 665 | |||
| 666 | /// 0=byte, 1=word, 2=long, 3=quad | ||
| 667 | r_length: u2, | ||
| 668 | |||
| 669 | /// does not include value of sym referenced | ||
| 670 | r_extern: u1, | ||
| 671 | |||
| 672 | /// if not 0, machine specific relocation type | ||
| 673 | r_type: u4, | ||
| 674 | }; | ||
| 675 | |||
| 650 | /// After MacOS X 10.1 when a new load command is added that is required to be | 676 | /// After MacOS X 10.1 when a new load command is added that is required to be |
| 651 | /// understood by the dynamic linker for the image to execute properly the | 677 | /// understood by the dynamic linker for the image to execute properly the |
| 652 | /// LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic | 678 | /// LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic |
| ... | @@ -1086,13 +1112,58 @@ pub const N_ECOML = 0xe8; | ... | @@ -1086,13 +1112,58 @@ pub const N_ECOML = 0xe8; |
| 1086 | /// second stab entry with length information | 1112 | /// second stab entry with length information |
| 1087 | pub const N_LENG = 0xfe; | 1113 | pub const N_LENG = 0xfe; |
| 1088 | 1114 | ||
| 1089 | /// If a segment contains any sections marked with S_ATTR_DEBUG then all | 1115 | // For the two types of symbol pointers sections and the symbol stubs section |
| 1090 | /// sections in that segment must have this attribute. No section other than | 1116 | // they have indirect symbol table entries. For each of the entries in the |
| 1091 | /// a section marked with this attribute may reference the contents of this | 1117 | // section the indirect symbol table entries, in corresponding order in the |
| 1092 | /// section. A section with this attribute may contain no symbols and must have | 1118 | // indirect symbol table, start at the index stored in the reserved1 field |
| 1093 | /// a section type S_REGULAR. The static linker will not copy section contents | 1119 | // of the section structure. Since the indirect symbol table entries |
| 1094 | /// from sections with this attribute into its output file. These sections | 1120 | // correspond to the entries in the section the number of indirect symbol table |
| 1095 | /// generally contain DWARF debugging info. | 1121 | // entries is inferred from the size of the section divided by the size of the |
| 1122 | // entries in the section. For symbol pointers sections the size of the entries | ||
| 1123 | // in the section is 4 bytes and for symbol stubs sections the byte size of the | ||
| 1124 | // stubs is stored in the reserved2 field of the section structure. | ||
| 1125 | |||
| 1126 | /// section with only non-lazy symbol pointers | ||
| 1127 | pub const S_NON_LAZY_SYMBOL_POINTERS = 0x6; | ||
| 1128 | |||
| 1129 | /// section with only lazy symbol pointers | ||
| 1130 | pub const S_LAZY_SYMBOL_POINTERS = 0x7; | ||
| 1131 | |||
| 1132 | /// section with only symbol stubs, byte size of stub in the reserved2 field | ||
| 1133 | pub const S_SYMBOL_STUBS = 0x8; | ||
| 1134 | |||
| 1135 | /// section with only function pointers for initialization | ||
| 1136 | pub const S_MOD_INIT_FUNC_POINTERS = 0x9; | ||
| 1137 | |||
| 1138 | /// section with only function pointers for termination | ||
| 1139 | pub const S_MOD_TERM_FUNC_POINTERS = 0xa; | ||
| 1140 | |||
| 1141 | /// section contains symbols that are to be coalesced | ||
| 1142 | pub const S_COALESCED = 0xb; | ||
| 1143 | |||
| 1144 | /// zero fill on demand section (that can be larger than 4 gigabytes) | ||
| 1145 | pub const S_GB_ZEROFILL = 0xc; | ||
| 1146 | |||
| 1147 | /// section with only pairs of function pointers for interposing | ||
| 1148 | pub const S_INTERPOSING = 0xd; | ||
| 1149 | |||
| 1150 | /// section with only 16 byte literals | ||
| 1151 | pub const S_16BYTE_LITERALS = 0xe; | ||
| 1152 | |||
| 1153 | /// section contains DTrace Object Format | ||
| 1154 | pub const S_DTRACE_DOF = 0xf; | ||
| 1155 | |||
| 1156 | /// section with only lazy symbol pointers to lazy loaded dylibs | ||
| 1157 | pub const S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10; | ||
| 1158 | |||
| 1159 | // If a segment contains any sections marked with S_ATTR_DEBUG then all | ||
| 1160 | // sections in that segment must have this attribute. No section other than | ||
| 1161 | // a section marked with this attribute may reference the contents of this | ||
| 1162 | // section. A section with this attribute may contain no symbols and must have | ||
| 1163 | // a section type S_REGULAR. The static linker will not copy section contents | ||
| 1164 | // from sections with this attribute into its output file. These sections | ||
| 1165 | // generally contain DWARF debugging info. | ||
| 1166 | |||
| 1096 | /// a debug section | 1167 | /// a debug section |
| 1097 | pub const S_ATTR_DEBUG = 0x02000000; | 1168 | pub const S_ATTR_DEBUG = 0x02000000; |
| 1098 | 1169 | ||
| ... | @@ -1154,3 +1225,35 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2; | ... | @@ -1154,3 +1225,35 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2; |
| 1154 | 1225 | ||
| 1155 | /// VM execute permission | 1226 | /// VM execute permission |
| 1156 | pub const VM_PROT_EXECUTE: vm_prot_t = 0x4; | 1227 | pub const VM_PROT_EXECUTE: vm_prot_t = 0x4; |
| 1228 | |||
| 1229 | pub const reloc_type_x86_64 = packed enum(u4) { | ||
| 1230 | /// for absolute addresses | ||
| 1231 | X86_64_RELOC_UNSIGNED = 0, | ||
| 1232 | |||
| 1233 | /// for signed 32-bit displacement | ||
| 1234 | X86_64_RELOC_SIGNED, | ||
| 1235 | |||
| 1236 | /// a CALL/JMP instruction with 32-bit displacement | ||
| 1237 | X86_64_RELOC_BRANCH, | ||
| 1238 | |||
| 1239 | /// a MOVQ load of a GOT entry | ||
| 1240 | X86_64_RELOC_GOT_LOAD, | ||
| 1241 | |||
| 1242 | /// other GOT references | ||
| 1243 | X86_64_RELOC_GOT, | ||
| 1244 | |||
| 1245 | /// must be followed by a X86_64_RELOC_UNSIGNED | ||
| 1246 | X86_64_RELOC_SUBTRACTOR, | ||
| 1247 | |||
| 1248 | /// for signed 32-bit displacement with a -1 addend | ||
| 1249 | X86_64_RELOC_SIGNED_1, | ||
| 1250 | |||
| 1251 | /// for signed 32-bit displacement with a -2 addend | ||
| 1252 | X86_64_RELOC_SIGNED_2, | ||
| 1253 | |||
| 1254 | /// for signed 32-bit displacement with a -4 addend | ||
| 1255 | X86_64_RELOC_SIGNED_4, | ||
| 1256 | |||
| 1257 | /// for thread local variables | ||
| 1258 | X86_64_RELOC_TLV, | ||
| 1259 | }; |
src-self-hosted/link/MachO.zig+22-13| ... | @@ -32,6 +32,20 @@ const LoadCommand = union(enum) { | ... | @@ -32,6 +32,20 @@ const LoadCommand = union(enum) { |
| 32 | .Dysymtab => |x| x.cmdsize, | 32 | .Dysymtab => |x| x.cmdsize, |
| 33 | }; | 33 | }; |
| 34 | } | 34 | } |
| 35 | |||
| 36 | pub fn write(self: LoadCommand, file: *fs.File, offset: u64) !void { | ||
| 37 | return switch (self) { | ||
| 38 | .Segment => |cmd| writeGeneric(cmd, file, offset), | ||
| 39 | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), | ||
| 40 | .Symtab => |cmd| writeGeneric(cmd, file, offset), | ||
| 41 | .Dysymtab => |cmd| writeGeneric(cmd, file, offset), | ||
| 42 | }; | ||
| 43 | } | ||
| 44 | |||
| 45 | fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void { | ||
| 46 | const slice = [1]@TypeOf(cmd){cmd}; | ||
| 47 | return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset); | ||
| 48 | } | ||
| 35 | }; | 49 | }; |
| 36 | 50 | ||
| 37 | base: File, | 51 | base: File, |
| ... | @@ -258,8 +272,7 @@ pub fn flush(self: *MachO, module: *Module) !void { | ... | @@ -258,8 +272,7 @@ pub fn flush(self: *MachO, module: *Module) !void { |
| 258 | 272 | ||
| 259 | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); | 273 | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); |
| 260 | for (self.load_commands.items) |cmd| { | 274 | for (self.load_commands.items) |cmd| { |
| 261 | const cmd_to_write = [1]@TypeOf(cmd){cmd}; | 275 | try cmd.write(&self.base.file.?, last_cmd_offset); |
| 262 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(cmd_to_write[0..1]), last_cmd_offset); | ||
| 263 | last_cmd_offset += cmd.cmdsize(); | 276 | last_cmd_offset += cmd.cmdsize(); |
| 264 | } | 277 | } |
| 265 | const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); | 278 | const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); |
| ... | @@ -346,19 +359,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -346,19 +359,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 346 | .n_desc = 0, | 359 | .n_desc = 0, |
| 347 | .n_value = addr, | 360 | .n_value = addr, |
| 348 | }; | 361 | }; |
| 349 | self.offset_table.items[decl.link.macho.offset_table_index.?] = addr; | ||
| 350 | 362 | ||
| 363 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | ||
| 364 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | ||
| 365 | try self.updateDeclExports(module, decl, decl_exports); | ||
| 351 | try self.writeSymbol(decl.link.macho.symbol_table_index.?); | 366 | try self.writeSymbol(decl.link.macho.symbol_table_index.?); |
| 352 | 367 | ||
| 353 | const text_section = self.sections.items[self.text_section_index.?]; | 368 | const text_section = self.sections.items[self.text_section_index.?]; |
| 354 | const section_offset = symbol.n_value - text_section.addr; | 369 | const section_offset = symbol.n_value - text_section.addr; |
| 355 | const file_offset = text_section.offset + section_offset; | 370 | const file_offset = text_section.offset + section_offset; |
| 356 | log.debug("file_offset 0x{x}\n", .{file_offset}); | 371 | log.debug("file_offset 0x{x}\n", .{file_offset}); |
| 357 | try self.base.file.?.pwriteAll(code, file_offset); | ||
| 358 | 372 | ||
| 359 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 373 | try self.base.file.?.pwriteAll(code, file_offset); |
| 360 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | ||
| 361 | return self.updateDeclExports(module, decl, decl_exports); | ||
| 362 | } | 374 | } |
| 363 | 375 | ||
| 364 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} | 376 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} |
| ... | @@ -374,7 +386,7 @@ pub fn updateDeclExports( | ... | @@ -374,7 +386,7 @@ pub fn updateDeclExports( |
| 374 | 386 | ||
| 375 | if (decl.link.macho.symbol_table_index == null) return; | 387 | if (decl.link.macho.symbol_table_index == null) return; |
| 376 | 388 | ||
| 377 | var decl_sym = self.symbol_table.items[decl.link.macho.symbol_table_index.?]; | 389 | const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?]; |
| 378 | // TODO implement | 390 | // TODO implement |
| 379 | if (exports.len == 0) return; | 391 | if (exports.len == 0) return; |
| 380 | 392 | ||
| ... | @@ -504,10 +516,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -504,10 +516,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 504 | const text_capacity = self.allocatedSize(text_section.offset); | 516 | const text_capacity = self.allocatedSize(text_section.offset); |
| 505 | const needed_size = (addr + new_block_size) - text_section.addr; | 517 | const needed_size = (addr + new_block_size) - text_section.addr; |
| 506 | log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size }); | 518 | log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size }); |
| 507 | 519 | assert(needed_size <= text_capacity); // TODO handle growth | |
| 508 | if (needed_size > text_capacity) { | ||
| 509 | // TODO handle growth | ||
| 510 | } | ||
| 511 | 520 | ||
| 512 | self.last_text_block = text_block; | 521 | self.last_text_block = text_block; |
| 513 | text_section.size = needed_size; | 522 | text_section.size = needed_size; |
| ... | @@ -659,7 +668,7 @@ fn writeSymbol(self: *MachO, index: usize) !void { | ... | @@ -659,7 +668,7 @@ fn writeSymbol(self: *MachO, index: usize) !void { |
| 659 | defer tracy.end(); | 668 | defer tracy.end(); |
| 660 | 669 | ||
| 661 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 670 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 662 | var sym = [1]macho.nlist_64{self.symbol_table.items[index]}; | 671 | const sym = [1]macho.nlist_64{self.symbol_table.items[index]}; |
| 663 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; | 672 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 664 | log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off }); | 673 | log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off }); |
| 665 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); | 674 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); |