| author | |
| committer | |
| log | 8c578ba02ccff63a64093b5acdefcf7b95cc8c46 |
| tree | aad7a4e9fc7a29863f1e747151613ba1a8e44081 |
| parent | c7de5e511125a738269a802318695b98a88b2791 |
5 files changed, 464 insertions(+), 24 deletions(-)
src/link/MachO.zig+13| ... | @@ -70,6 +70,7 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | ... | @@ -70,6 +70,7 @@ symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 70 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 70 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 71 | indsymtab: Indsymtab = .{}, | 71 | indsymtab: Indsymtab = .{}, |
| 72 | got: GotSection = .{}, | 72 | got: GotSection = .{}, |
| 73 | zig_got: ZigGotSection = .{}, | ||
| 73 | stubs: StubsSection = .{}, | 74 | stubs: StubsSection = .{}, |
| 74 | stubs_helper: StubsHelperSection = .{}, | 75 | stubs_helper: StubsHelperSection = .{}, |
| 75 | objc_stubs: ObjcStubsSection = .{}, | 76 | objc_stubs: ObjcStubsSection = .{}, |
| ... | @@ -337,6 +338,7 @@ pub fn deinit(self: *MachO) void { | ... | @@ -337,6 +338,7 @@ pub fn deinit(self: *MachO) void { |
| 337 | self.symtab.deinit(gpa); | 338 | self.symtab.deinit(gpa); |
| 338 | self.strtab.deinit(gpa); | 339 | self.strtab.deinit(gpa); |
| 339 | self.got.deinit(gpa); | 340 | self.got.deinit(gpa); |
| 341 | self.zig_got.deinit(gpa); | ||
| 340 | self.stubs.deinit(gpa); | 342 | self.stubs.deinit(gpa); |
| 341 | self.objc_stubs.deinit(gpa); | 343 | self.objc_stubs.deinit(gpa); |
| 342 | self.tlv_ptr.deinit(gpa); | 344 | self.tlv_ptr.deinit(gpa); |
| ... | @@ -3157,6 +3159,13 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { | ... | @@ -3157,6 +3159,13 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { |
| 3157 | } | 3159 | } |
| 3158 | } | 3160 | } |
| 3159 | 3161 | ||
| 3162 | pub fn growSection(self: *MachO, sect_index: u8, size: u64) !void { | ||
| 3163 | _ = self; | ||
| 3164 | _ = sect_index; | ||
| 3165 | _ = size; | ||
| 3166 | @panic("TODO growSection"); | ||
| 3167 | } | ||
| 3168 | |||
| 3160 | pub fn getTarget(self: MachO) std.Target { | 3169 | pub fn getTarget(self: MachO) std.Target { |
| 3161 | return self.base.comp.root_mod.resolved_target.result; | 3170 | return self.base.comp.root_mod.resolved_target.result; |
| 3162 | } | 3171 | } |
| ... | @@ -3657,6 +3666,7 @@ fn fmtDumpState( | ... | @@ -3657,6 +3666,7 @@ fn fmtDumpState( |
| 3657 | try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)}); | 3666 | try writer.print("stubs\n{}\n", .{self.stubs.fmt(self)}); |
| 3658 | try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)}); | 3667 | try writer.print("objc_stubs\n{}\n", .{self.objc_stubs.fmt(self)}); |
| 3659 | try writer.print("got\n{}\n", .{self.got.fmt(self)}); | 3668 | try writer.print("got\n{}\n", .{self.got.fmt(self)}); |
| 3669 | try writer.print("zig_got\n{}\n", .{self.zig_got.fmt(self)}); | ||
| 3660 | try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)}); | 3670 | try writer.print("tlv_ptr\n{}\n", .{self.tlv_ptr.fmt(self)}); |
| 3661 | try writer.writeByte('\n'); | 3671 | try writer.writeByte('\n'); |
| 3662 | try writer.print("sections\n{}\n", .{self.fmtSections()}); | 3672 | try writer.print("sections\n{}\n", .{self.fmtSections()}); |
| ... | @@ -3759,6 +3769,8 @@ const Section = struct { | ... | @@ -3759,6 +3769,8 @@ const Section = struct { |
| 3759 | header: macho.section_64, | 3769 | header: macho.section_64, |
| 3760 | segment_id: u8, | 3770 | segment_id: u8, |
| 3761 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, | 3771 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 3772 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, | ||
| 3773 | last_atom_index: Atom.Index = 0, | ||
| 3762 | }; | 3774 | }; |
| 3763 | 3775 | ||
| 3764 | const HotUpdateState = struct { | 3776 | const HotUpdateState = struct { |
| ... | @@ -4125,4 +4137,5 @@ const TlvPtrSection = synthetic.TlvPtrSection; | ... | @@ -4125,4 +4137,5 @@ const TlvPtrSection = synthetic.TlvPtrSection; |
| 4125 | const TypedValue = @import("../TypedValue.zig"); | 4137 | const TypedValue = @import("../TypedValue.zig"); |
| 4126 | const UnwindInfo = @import("MachO/UnwindInfo.zig"); | 4138 | const UnwindInfo = @import("MachO/UnwindInfo.zig"); |
| 4127 | const WeakBindSection = synthetic.WeakBindSection; | 4139 | const WeakBindSection = synthetic.WeakBindSection; |
| 4140 | const ZigGotSection = synthetic.ZigGotSection; | ||
| 4128 | const ZigObject = @import("MachO/ZigObject.zig"); | 4141 | const ZigObject = @import("MachO/ZigObject.zig"); |
src/link/MachO/Atom.zig+153| ... | @@ -37,6 +37,11 @@ unwind_records: Loc = .{}, | ... | @@ -37,6 +37,11 @@ unwind_records: Loc = .{}, |
| 37 | 37 | ||
| 38 | flags: Flags = .{}, | 38 | flags: Flags = .{}, |
| 39 | 39 | ||
| 40 | /// Points to the previous and next neighbors, based on the `text_offset`. | ||
| 41 | /// This can be used to find, for example, the capacity of this `TextBlock`. | ||
| 42 | prev_index: Index = 0, | ||
| 43 | next_index: Index = 0, | ||
| 44 | |||
| 40 | pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 { | 45 | pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 { |
| 41 | return macho_file.strings.getAssumeExists(self.name); | 46 | return macho_file.strings.getAssumeExists(self.name); |
| 42 | } | 47 | } |
| ... | @@ -171,6 +176,154 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { | ... | @@ -171,6 +176,154 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 { |
| 171 | return osec; | 176 | return osec; |
| 172 | } | 177 | } |
| 173 | 178 | ||
| 179 | /// Returns how much room there is to grow in virtual address space. | ||
| 180 | /// File offset relocation happens transparently, so it is not included in | ||
| 181 | /// this calculation. | ||
| 182 | pub fn capacity(self: Atom, macho_file: *MachO) u64 { | ||
| 183 | const next_value = if (macho_file.getAtom(self.next_index)) |next| next.value else std.math.maxInt(u32); | ||
| 184 | return next_value - self.value; | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { | ||
| 188 | // No need to keep a free list node for the last block. | ||
| 189 | const next = macho_file.getAtom(self.next_index) orelse return false; | ||
| 190 | const cap = next.value - self.value; | ||
| 191 | const ideal_cap = MachO.padToIdeal(self.size); | ||
| 192 | if (cap <= ideal_cap) return false; | ||
| 193 | const surplus = cap - ideal_cap; | ||
| 194 | return surplus >= MachO.min_text_capacity; | ||
| 195 | } | ||
| 196 | |||
| 197 | pub fn allocate(self: *Atom, macho_file: *MachO) !void { | ||
| 198 | const sect = &macho_file.sections.items(.header)[self.out_n_sect]; | ||
| 199 | const free_list = &macho_file.sections.items(.free_list)[self.out_n_sect]; | ||
| 200 | const last_atom_index = &macho_file.sections.items(.last_atom_index)[self.out_n_sect]; | ||
| 201 | const new_atom_ideal_capacity = MachO.padToIdeal(self.size); | ||
| 202 | |||
| 203 | // We use these to indicate our intention to update metadata, placing the new atom, | ||
| 204 | // and possibly removing a free list node. | ||
| 205 | // It would be simpler to do it inside the for loop below, but that would cause a | ||
| 206 | // problem if an error was returned later in the function. So this action | ||
| 207 | // is actually carried out at the end of the function, when errors are no longer possible. | ||
| 208 | var atom_placement: ?Atom.Index = null; | ||
| 209 | var free_list_removal: ?usize = null; | ||
| 210 | |||
| 211 | // First we look for an appropriately sized free list node. | ||
| 212 | // The list is unordered. We'll just take the first thing that works. | ||
| 213 | self.value = blk: { | ||
| 214 | var i: usize = free_list.items.len; | ||
| 215 | while (i < free_list.items.len) { | ||
| 216 | const big_atom_index = free_list.items[i]; | ||
| 217 | const big_atom = macho_file.getAtom(big_atom_index).?; | ||
| 218 | // We now have a pointer to a live atom that has too much capacity. | ||
| 219 | // Is it enough that we could fit this new atom? | ||
| 220 | const cap = big_atom.capacity(macho_file); | ||
| 221 | const ideal_capacity = MachO.padToIdeal(cap); | ||
| 222 | const ideal_capacity_end_vaddr = std.math.add(u64, big_atom.value, ideal_capacity) catch ideal_capacity; | ||
| 223 | const capacity_end_vaddr = big_atom.value + cap; | ||
| 224 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; | ||
| 225 | const new_start_vaddr = self.alignment.backward(new_start_vaddr_unaligned); | ||
| 226 | if (new_start_vaddr < ideal_capacity_end_vaddr) { | ||
| 227 | // Additional bookkeeping here to notice if this free list node | ||
| 228 | // should be deleted because the block that it points to has grown to take up | ||
| 229 | // more of the extra capacity. | ||
| 230 | if (!big_atom.freeListEligible(macho_file)) { | ||
| 231 | _ = free_list.swapRemove(i); | ||
| 232 | } else { | ||
| 233 | i += 1; | ||
| 234 | } | ||
| 235 | continue; | ||
| 236 | } | ||
| 237 | // At this point we know that we will place the new block here. But the | ||
| 238 | // remaining question is whether there is still yet enough capacity left | ||
| 239 | // over for there to still be a free list node. | ||
| 240 | const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; | ||
| 241 | const keep_free_list_node = remaining_capacity >= MachO.min_text_capacity; | ||
| 242 | |||
| 243 | // Set up the metadata to be updated, after errors are no longer possible. | ||
| 244 | atom_placement = big_atom_index; | ||
| 245 | if (!keep_free_list_node) { | ||
| 246 | free_list_removal = i; | ||
| 247 | } | ||
| 248 | break :blk new_start_vaddr; | ||
| 249 | } else if (macho_file.getAtom(last_atom_index.*)) |last| { | ||
| 250 | const ideal_capacity = MachO.padToIdeal(last.size); | ||
| 251 | const ideal_capacity_end_vaddr = last.value + ideal_capacity; | ||
| 252 | const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); | ||
| 253 | // Set up the metadata to be updated, after errors are no longer possible. | ||
| 254 | atom_placement = last.atom_index; | ||
| 255 | break :blk new_start_vaddr; | ||
| 256 | } else { | ||
| 257 | break :blk sect.addr; | ||
| 258 | } | ||
| 259 | }; | ||
| 260 | |||
| 261 | log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{ | ||
| 262 | self.atom_index, | ||
| 263 | self.getName(macho_file), | ||
| 264 | self.value, | ||
| 265 | self.value + self.size, | ||
| 266 | }); | ||
| 267 | |||
| 268 | const expand_section = if (atom_placement) |placement_index| | ||
| 269 | macho_file.getAtom(placement_index).?.next_index == 0 | ||
| 270 | else | ||
| 271 | true; | ||
| 272 | if (expand_section) { | ||
| 273 | const needed_size = (self.value + self.size) - sect.addr; | ||
| 274 | try macho_file.growSection(self.out_n_sect, needed_size); | ||
| 275 | last_atom_index.* = self.atom_index; | ||
| 276 | |||
| 277 | // const zig_object = macho_file_file.getZigObject().?; | ||
| 278 | // if (zig_object.dwarf) |_| { | ||
| 279 | // // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address | ||
| 280 | // // range of the compilation unit. When we expand the text section, this range changes, | ||
| 281 | // // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. | ||
| 282 | // zig_object.debug_info_header_dirty = true; | ||
| 283 | // // This becomes dirty for the same reason. We could potentially make this more | ||
| 284 | // // fine-grained with the addition of support for more compilation units. It is planned to | ||
| 285 | // // model each package as a different compilation unit. | ||
| 286 | // zig_object.debug_aranges_section_dirty = true; | ||
| 287 | // } | ||
| 288 | } | ||
| 289 | sect.@"align" = @max(sect.@"align", self.alignment.toLog2Units()); | ||
| 290 | |||
| 291 | // This function can also reallocate an atom. | ||
| 292 | // In this case we need to "unplug" it from its previous location before | ||
| 293 | // plugging it in to its new location. | ||
| 294 | if (macho_file.getAtom(self.prev_index)) |prev| { | ||
| 295 | prev.next_index = self.next_index; | ||
| 296 | } | ||
| 297 | if (macho_file.getAtom(self.next_index)) |next| { | ||
| 298 | next.prev_index = self.prev_index; | ||
| 299 | } | ||
| 300 | |||
| 301 | if (atom_placement) |big_atom_index| { | ||
| 302 | const big_atom = macho_file.getAtom(big_atom_index).?; | ||
| 303 | self.prev_index = big_atom_index; | ||
| 304 | self.next_index = big_atom.next_index; | ||
| 305 | big_atom.next_index = self.atom_index; | ||
| 306 | } else { | ||
| 307 | self.prev_index = 0; | ||
| 308 | self.next_index = 0; | ||
| 309 | } | ||
| 310 | if (free_list_removal) |i| { | ||
| 311 | _ = free_list.swapRemove(i); | ||
| 312 | } | ||
| 313 | |||
| 314 | self.flags.alive = true; | ||
| 315 | } | ||
| 316 | |||
| 317 | pub fn shrink(self: *Atom, macho_file: *MachO) void { | ||
| 318 | _ = self; | ||
| 319 | _ = macho_file; | ||
| 320 | } | ||
| 321 | |||
| 322 | pub fn grow(self: *Atom, macho_file: *MachO) !void { | ||
| 323 | if (!self.alignment.check(self.value) or self.size > self.capacity(macho_file)) | ||
| 324 | try self.allocate(macho_file); | ||
| 325 | } | ||
| 326 | |||
| 174 | pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { | 327 | pub fn scanRelocs(self: Atom, macho_file: *MachO) !void { |
| 175 | const tracy = trace(@src()); | 328 | const tracy = trace(@src()); |
| 176 | defer tracy.end(); | 329 | defer tracy.end(); |
src/link/MachO/Symbol.zig+22| ... | @@ -149,6 +149,25 @@ pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 { | ... | @@ -149,6 +149,25 @@ pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 { |
| 149 | return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file); | 149 | return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | const GetOrCreateZigGotEntryResult = struct { | ||
| 153 | found_existing: bool, | ||
| 154 | index: ZigGotSection.Index, | ||
| 155 | }; | ||
| 156 | |||
| 157 | pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult { | ||
| 158 | assert(!macho_file.base.isRelocatable()); | ||
| 159 | assert(symbol.flags.needs_zig_got); | ||
| 160 | if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got }; | ||
| 161 | const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file); | ||
| 162 | return .{ .found_existing = false, .index = index }; | ||
| 163 | } | ||
| 164 | |||
| 165 | pub fn zigGotAddress(symbol: Symbol, macho_file: *MachO) u64 { | ||
| 166 | if (!symbol.flags.has_zig_got) return 0; | ||
| 167 | const extras = symbol.getExtra(macho_file).?; | ||
| 168 | return macho_file.zig_got.entryAddress(extras.zig_got, macho_file); | ||
| 169 | } | ||
| 170 | |||
| 152 | pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 { | 171 | pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 { |
| 153 | if (!symbol.flags.output_symtab) return null; | 172 | if (!symbol.flags.output_symtab) return null; |
| 154 | assert(!symbol.isSymbolStab(macho_file)); | 173 | assert(!symbol.isSymbolStab(macho_file)); |
| ... | @@ -170,6 +189,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 { | ... | @@ -170,6 +189,7 @@ pub fn getOutputSymtabIndex(symbol: Symbol, macho_file: *MachO) ?u32 { |
| 170 | 189 | ||
| 171 | const AddExtraOpts = struct { | 190 | const AddExtraOpts = struct { |
| 172 | got: ?u32 = null, | 191 | got: ?u32 = null, |
| 192 | zig_got: ?u32 = null, | ||
| 173 | stubs: ?u32 = null, | 193 | stubs: ?u32 = null, |
| 174 | objc_stubs: ?u32 = null, | 194 | objc_stubs: ?u32 = null, |
| 175 | objc_selrefs: ?u32 = null, | 195 | objc_selrefs: ?u32 = null, |
| ... | @@ -374,6 +394,7 @@ pub const Visibility = enum { | ... | @@ -374,6 +394,7 @@ pub const Visibility = enum { |
| 374 | 394 | ||
| 375 | pub const Extra = struct { | 395 | pub const Extra = struct { |
| 376 | got: u32 = 0, | 396 | got: u32 = 0, |
| 397 | zig_got: u32 = 0, | ||
| 377 | stubs: u32 = 0, | 398 | stubs: u32 = 0, |
| 378 | objc_stubs: u32 = 0, | 399 | objc_stubs: u32 = 0, |
| 379 | objc_selrefs: u32 = 0, | 400 | objc_selrefs: u32 = 0, |
| ... | @@ -393,3 +414,4 @@ const MachO = @import("../MachO.zig"); | ... | @@ -393,3 +414,4 @@ const MachO = @import("../MachO.zig"); |
| 393 | const Nlist = Object.Nlist; | 414 | const Nlist = Object.Nlist; |
| 394 | const Object = @import("Object.zig"); | 415 | const Object = @import("Object.zig"); |
| 395 | const Symbol = @This(); | 416 | const Symbol = @This(); |
| 417 | const ZigGotSection = @import("synthetic.zig").ZigGotSection; |
src/link/MachO/ZigObject.zig+158-24| ... | @@ -7,9 +7,36 @@ symtab: std.MultiArrayList(Nlist) = .{}, | ... | @@ -7,9 +7,36 @@ symtab: std.MultiArrayList(Nlist) = .{}, |
| 7 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | 7 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 8 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, | 8 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 9 | 9 | ||
| 10 | /// Table of tracked LazySymbols. | ||
| 11 | lazy_syms: LazySymbolTable = .{}, | ||
| 12 | |||
| 10 | /// Table of tracked Decls. | 13 | /// Table of tracked Decls. |
| 11 | decls: DeclTable = .{}, | 14 | decls: DeclTable = .{}, |
| 12 | 15 | ||
| 16 | /// Table of unnamed constants associated with a parent `Decl`. | ||
| 17 | /// We store them here so that we can free the constants whenever the `Decl` | ||
| 18 | /// needs updating or is freed. | ||
| 19 | /// | ||
| 20 | /// For example, | ||
| 21 | /// | ||
| 22 | /// ```zig | ||
| 23 | /// const Foo = struct{ | ||
| 24 | /// a: u8, | ||
| 25 | /// }; | ||
| 26 | /// | ||
| 27 | /// pub fn main() void { | ||
| 28 | /// var foo = Foo{ .a = 1 }; | ||
| 29 | /// _ = foo; | ||
| 30 | /// } | ||
| 31 | /// ``` | ||
| 32 | /// | ||
| 33 | /// value assigned to label `foo` is an unnamed constant belonging/associated | ||
| 34 | /// with `Decl` `main`, and lives as long as that `Decl`. | ||
| 35 | unnamed_consts: UnnamedConstTable = .{}, | ||
| 36 | |||
| 37 | /// Table of tracked AnonDecls. | ||
| 38 | anon_decls: AnonDeclTable = .{}, | ||
| 39 | |||
| 13 | /// A table of relocations. | 40 | /// A table of relocations. |
| 14 | relocs: RelocationTable = .{}, | 41 | relocs: RelocationTable = .{}, |
| 15 | 42 | ||
| ... | @@ -35,6 +62,24 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { | ... | @@ -35,6 +62,24 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { |
| 35 | self.decls.deinit(allocator); | 62 | self.decls.deinit(allocator); |
| 36 | } | 63 | } |
| 37 | 64 | ||
| 65 | self.lazy_syms.deinit(allocator); | ||
| 66 | |||
| 67 | { | ||
| 68 | var it = self.unnamed_consts.valueIterator(); | ||
| 69 | while (it.next()) |syms| { | ||
| 70 | syms.deinit(allocator); | ||
| 71 | } | ||
| 72 | self.unnamed_consts.deinit(allocator); | ||
| 73 | } | ||
| 74 | |||
| 75 | { | ||
| 76 | var it = self.anon_decls.iterator(); | ||
| 77 | while (it.next()) |entry| { | ||
| 78 | entry.value_ptr.exports.deinit(allocator); | ||
| 79 | } | ||
| 80 | self.anon_decls.deinit(allocator); | ||
| 81 | } | ||
| 82 | |||
| 38 | for (self.relocs.items) |*list| { | 83 | for (self.relocs.items) |*list| { |
| 39 | list.deinit(allocator); | 84 | list.deinit(allocator); |
| 40 | } | 85 | } |
| ... | @@ -296,7 +341,7 @@ pub fn updateDecl( | ... | @@ -296,7 +341,7 @@ pub fn updateDecl( |
| 296 | }, | 341 | }, |
| 297 | }; | 342 | }; |
| 298 | const sect_index = try self.getDeclOutputSection(macho_file, decl, code); | 343 | const sect_index = try self.getDeclOutputSection(macho_file, decl, code); |
| 299 | const is_threadlocal = switch (macho_file.sections.items(.header[sect_index].type())) { | 344 | const is_threadlocal = switch (macho_file.sections.items(.header)[sect_index].type()) { |
| 300 | macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true, | 345 | macho.S_THREAD_LOCAL_ZEROFILL, macho.S_THREAD_LOCAL_REGULAR => true, |
| 301 | else => false, | 346 | else => false, |
| 302 | }; | 347 | }; |
| ... | @@ -317,21 +362,21 @@ pub fn updateDecl( | ... | @@ -317,21 +362,21 @@ pub fn updateDecl( |
| 317 | // ); | 362 | // ); |
| 318 | // } | 363 | // } |
| 319 | 364 | ||
| 320 | // // Since we updated the vaddr and the size, each corresponding export symbol also | 365 | // Since we updated the vaddr and the size, each corresponding export symbol also |
| 321 | // // needs to be updated. | 366 | // needs to be updated. |
| 322 | // try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); | 367 | try self.updateExports(macho_file, mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); |
| 323 | } | 368 | } |
| 324 | 369 | ||
| 325 | fn updateDeclCode( | 370 | fn updateDeclCode( |
| 326 | self: *ZigObject, | 371 | self: *ZigObject, |
| 327 | macho_file: *MachO, | 372 | macho_file: *MachO, |
| 328 | decl_index: Module.Decl.Index, | 373 | decl_index: InternPool.DeclIndex, |
| 329 | sym_index: Symbol.Index, | 374 | sym_index: Symbol.Index, |
| 330 | sect_index: u8, | 375 | sect_index: u8, |
| 331 | code: []const u8, | 376 | code: []const u8, |
| 332 | ) !void { | 377 | ) !void { |
| 333 | const gpa = self.base.comp.gpa; | 378 | const gpa = macho_file.base.comp.gpa; |
| 334 | const mod = self.base.comp.module.?; | 379 | const mod = macho_file.base.comp.module.?; |
| 335 | const decl = mod.declPtr(decl_index); | 380 | const decl = mod.declPtr(decl_index); |
| 336 | const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); | 381 | const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); |
| 337 | 382 | ||
| ... | @@ -342,7 +387,6 @@ fn updateDeclCode( | ... | @@ -342,7 +387,6 @@ fn updateDeclCode( |
| 342 | const sect = &macho_file.sections.items(.header)[sect_index]; | 387 | const sect = &macho_file.sections.items(.header)[sect_index]; |
| 343 | const sym = macho_file.getSymbol(sym_index); | 388 | const sym = macho_file.getSymbol(sym_index); |
| 344 | const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; | 389 | const nlist = &self.symtab.items(.nlist)[sym.nlist_idx]; |
| 345 | const size = &self.symtab.items(.size)[sym.nlist_idx]; | ||
| 346 | const atom = sym.getAtom(macho_file).?; | 390 | const atom = sym.getAtom(macho_file).?; |
| 347 | 391 | ||
| 348 | sym.out_n_sect = sect_index; | 392 | sym.out_n_sect = sect_index; |
| ... | @@ -354,7 +398,7 @@ fn updateDeclCode( | ... | @@ -354,7 +398,7 @@ fn updateDeclCode( |
| 354 | nlist.n_strx = sym.name; | 398 | nlist.n_strx = sym.name; |
| 355 | nlist.n_type = macho.N_SECT; | 399 | nlist.n_type = macho.N_SECT; |
| 356 | nlist.n_sect = sect_index + 1; | 400 | nlist.n_sect = sect_index + 1; |
| 357 | size = code.len; | 401 | self.symtab.items(.size)[sym.nlist_idx] = code.len; |
| 358 | 402 | ||
| 359 | const old_size = atom.size; | 403 | const old_size = atom.size; |
| 360 | const old_vaddr = atom.value; | 404 | const old_vaddr = atom.value; |
| ... | @@ -363,14 +407,14 @@ fn updateDeclCode( | ... | @@ -363,14 +407,14 @@ fn updateDeclCode( |
| 363 | 407 | ||
| 364 | if (old_size > 0) { | 408 | if (old_size > 0) { |
| 365 | const capacity = atom.capacity(macho_file); | 409 | const capacity = atom.capacity(macho_file); |
| 366 | const need_realloc = code.len > capacity or !required_alignment.check(sym.value); | 410 | const need_realloc = code.len > capacity or !required_alignment.check(sym.getAddress(.{}, macho_file)); |
| 367 | 411 | ||
| 368 | if (need_realloc) { | 412 | if (need_realloc) { |
| 369 | try atom.grow(macho_file); | 413 | try atom.grow(macho_file); |
| 370 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom.value }); | 414 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom.value }); |
| 371 | if (old_vaddr != atom.value) { | 415 | if (old_vaddr != atom.value) { |
| 372 | sym.value = atom.value; | 416 | sym.value = 0; |
| 373 | nlist.n_value = atom.value; | 417 | nlist.n_value = 0; |
| 374 | 418 | ||
| 375 | if (!macho_file.base.isRelocatable()) { | 419 | if (!macho_file.base.isRelocatable()) { |
| 376 | log.debug(" (updating offset table entry)", .{}); | 420 | log.debug(" (updating offset table entry)", .{}); |
| ... | @@ -381,17 +425,17 @@ fn updateDeclCode( | ... | @@ -381,17 +425,17 @@ fn updateDeclCode( |
| 381 | } | 425 | } |
| 382 | } else if (code.len < old_size) { | 426 | } else if (code.len < old_size) { |
| 383 | atom.shrink(macho_file); | 427 | atom.shrink(macho_file); |
| 384 | } else if (atom.next_index == null) { | 428 | } else if (macho_file.getAtom(atom.next_index) == null) { |
| 385 | const needed_size = (sym.value + code.len) - sect.addr; | 429 | const needed_size = (sym.getAddress(.{}, macho_file) + code.len) - sect.addr; |
| 386 | sect.size = needed_size; | 430 | sect.size = needed_size; |
| 387 | } | 431 | } |
| 388 | } else { | 432 | } else { |
| 389 | try atom.allocate(macho_file); | 433 | try atom.allocate(macho_file); |
| 390 | // TODO: freeDeclMetadata in case of error | 434 | // TODO: freeDeclMetadata in case of error |
| 391 | 435 | ||
| 392 | sym.value = atom.value; | 436 | sym.value = 0; |
| 393 | sym.flags.needs_zig_got = true; | 437 | sym.flags.needs_zig_got = true; |
| 394 | nlist.n_value = atom.value; | 438 | nlist.n_value = 0; |
| 395 | 439 | ||
| 396 | if (!macho_file.base.isRelocatable()) { | 440 | if (!macho_file.base.isRelocatable()) { |
| 397 | const gop = try sym.getOrCreateZigGotEntry(sym_index, macho_file); | 441 | const gop = try sym.getOrCreateZigGotEntry(sym_index, macho_file); |
| ... | @@ -400,7 +444,7 @@ fn updateDeclCode( | ... | @@ -400,7 +444,7 @@ fn updateDeclCode( |
| 400 | } | 444 | } |
| 401 | 445 | ||
| 402 | if (!sect.isZerofill()) { | 446 | if (!sect.isZerofill()) { |
| 403 | const file_offset = sect.offset + sym.value - sect.addr; | 447 | const file_offset = sect.offset + sym.getAddress(.{}, macho_file) - sect.addr; |
| 404 | try macho_file.base.file.?.pwriteAll(code, file_offset); | 448 | try macho_file.base.file.?.pwriteAll(code, file_offset); |
| 405 | } | 449 | } |
| 406 | } | 450 | } |
| ... | @@ -478,12 +522,91 @@ pub fn updateExports( | ... | @@ -478,12 +522,91 @@ pub fn updateExports( |
| 478 | exported: Module.Exported, | 522 | exported: Module.Exported, |
| 479 | exports: []const *Module.Export, | 523 | exports: []const *Module.Export, |
| 480 | ) link.File.UpdateExportsError!void { | 524 | ) link.File.UpdateExportsError!void { |
| 481 | _ = self; | 525 | const tracy = trace(@src()); |
| 482 | _ = macho_file; | 526 | defer tracy.end(); |
| 483 | _ = mod; | 527 | |
| 484 | _ = exported; | 528 | const gpa = macho_file.base.comp.gpa; |
| 485 | _ = exports; | 529 | const metadata = switch (exported) { |
| 486 | @panic("TODO updateExports"); | 530 | .decl_index => |decl_index| blk: { |
| 531 | _ = try self.getOrCreateMetadataForDecl(macho_file, decl_index); | ||
| 532 | break :blk self.decls.getPtr(decl_index).?; | ||
| 533 | }, | ||
| 534 | .value => |value| self.anon_decls.getPtr(value) orelse blk: { | ||
| 535 | const first_exp = exports[0]; | ||
| 536 | const res = try self.lowerAnonDecl(macho_file, value, .none, first_exp.getSrcLoc(mod)); | ||
| 537 | switch (res) { | ||
| 538 | .ok => {}, | ||
| 539 | .fail => |em| { | ||
| 540 | // TODO maybe it's enough to return an error here and let Module.processExportsInner | ||
| 541 | // handle the error? | ||
| 542 | try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); | ||
| 543 | mod.failed_exports.putAssumeCapacityNoClobber(first_exp, em); | ||
| 544 | return; | ||
| 545 | }, | ||
| 546 | } | ||
| 547 | break :blk self.anon_decls.getPtr(value).?; | ||
| 548 | }, | ||
| 549 | }; | ||
| 550 | const sym_index = metadata.symbol_index; | ||
| 551 | const nlist_idx = macho_file.getSymbol(sym_index).nlist_idx; | ||
| 552 | const nlist = self.symtab.items(.nlist)[nlist_idx]; | ||
| 553 | |||
| 554 | for (exports) |exp| { | ||
| 555 | if (exp.opts.section.unwrap()) |section_name| { | ||
| 556 | if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) { | ||
| 557 | try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); | ||
| 558 | mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create( | ||
| 559 | gpa, | ||
| 560 | exp.getSrcLoc(mod), | ||
| 561 | "Unimplemented: ExportOptions.section", | ||
| 562 | .{}, | ||
| 563 | )); | ||
| 564 | continue; | ||
| 565 | } | ||
| 566 | } | ||
| 567 | if (exp.opts.linkage == .LinkOnce) { | ||
| 568 | try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( | ||
| 569 | gpa, | ||
| 570 | exp.getSrcLoc(mod), | ||
| 571 | "Unimplemented: GlobalLinkage.LinkOnce", | ||
| 572 | .{}, | ||
| 573 | )); | ||
| 574 | continue; | ||
| 575 | } | ||
| 576 | |||
| 577 | const exp_name = try std.fmt.allocPrint(gpa, "_{}", .{exp.opts.name.fmt(&mod.intern_pool)}); | ||
| 578 | defer gpa.free(exp_name); | ||
| 579 | |||
| 580 | const name_off = try macho_file.strings.insert(gpa, exp_name); | ||
| 581 | const global_nlist_index = if (metadata.@"export"(self, macho_file, exp_name)) |exp_index| | ||
| 582 | exp_index.* | ||
| 583 | else blk: { | ||
| 584 | const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null); | ||
| 585 | try metadata.exports.append(gpa, global_nlist_index); | ||
| 586 | break :blk global_nlist_index; | ||
| 587 | }; | ||
| 588 | const global_nlist = &self.symtab.items(.nlist)[global_nlist_index]; | ||
| 589 | global_nlist.n_strx = name_off; | ||
| 590 | global_nlist.n_value = nlist.n_value; | ||
| 591 | global_nlist.n_sect = nlist.n_sect; | ||
| 592 | global_nlist.n_type = macho.N_EXT | macho.N_SECT; | ||
| 593 | self.symtab.items(.size)[global_nlist_index] = self.symtab.items(.size)[nlist_idx]; | ||
| 594 | self.symtab.items(.atom)[global_nlist_index] = self.symtab.items(.atom)[nlist_idx]; | ||
| 595 | |||
| 596 | switch (exp.opts.linkage) { | ||
| 597 | .Internal => { | ||
| 598 | // Symbol should be hidden, or in MachO lingo, private extern. | ||
| 599 | global_nlist.n_type |= macho.N_PEXT; | ||
| 600 | }, | ||
| 601 | .Strong => {}, | ||
| 602 | .Weak => { | ||
| 603 | // Weak linkage is specified as part of n_desc field. | ||
| 604 | // Symbol's n_type is like for a symbol with strong linkage. | ||
| 605 | global_nlist.n_desc |= macho.N_WEAK_DEF; | ||
| 606 | }, | ||
| 607 | else => unreachable, | ||
| 608 | } | ||
| 609 | } | ||
| 487 | } | 610 | } |
| 488 | 611 | ||
| 489 | /// Must be called only after a successful call to `updateDecl`. | 612 | /// Must be called only after a successful call to `updateDecl`. |
| ... | @@ -612,8 +735,19 @@ const DeclMetadata = struct { | ... | @@ -612,8 +735,19 @@ const DeclMetadata = struct { |
| 612 | return null; | 735 | return null; |
| 613 | } | 736 | } |
| 614 | }; | 737 | }; |
| 615 | const DeclTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata); | ||
| 616 | 738 | ||
| 739 | const LazySymbolMetadata = struct { | ||
| 740 | const State = enum { unused, pending_flush, flushed }; | ||
| 741 | text_symbol_index: Symbol.Index = undefined, | ||
| 742 | data_const_symbol_index: Symbol.Index = undefined, | ||
| 743 | text_state: State = .unused, | ||
| 744 | rodata_state: State = .unused, | ||
| 745 | }; | ||
| 746 | |||
| 747 | const DeclTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclMetadata); | ||
| 748 | const UnnamedConstTable = std.AutoHashMapUnmanaged(InternPool.DeclIndex, std.ArrayListUnmanaged(Symbol.Index)); | ||
| 749 | const AnonDeclTable = std.AutoHashMapUnmanaged(InternPool.Index, DeclMetadata); | ||
| 750 | const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.OptionalDeclIndex, LazySymbolMetadata); | ||
| 617 | const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)); | 751 | const RelocationTable = std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)); |
| 618 | 752 | ||
| 619 | const assert = std.debug.assert; | 753 | const assert = std.debug.assert; |
src/link/MachO/synthetic.zig+118| ... | @@ -1,3 +1,121 @@ | ... | @@ -1,3 +1,121 @@ |
| 1 | pub const ZigGotSection = struct { | ||
| 2 | entries: std.ArrayListUnmanaged(Symbol.Index) = .{}, | ||
| 3 | dirty: bool = false, | ||
| 4 | |||
| 5 | pub const Index = u32; | ||
| 6 | |||
| 7 | pub fn deinit(zig_got: *ZigGotSection, allocator: Allocator) void { | ||
| 8 | zig_got.entries.deinit(allocator); | ||
| 9 | } | ||
| 10 | |||
| 11 | fn allocateEntry(zig_got: *ZigGotSection, allocator: Allocator) !Index { | ||
| 12 | try zig_got.entries.ensureUnusedCapacity(allocator, 1); | ||
| 13 | // TODO add free list | ||
| 14 | const index = @as(Index, @intCast(zig_got.entries.items.len)); | ||
| 15 | _ = zig_got.entries.addOneAssumeCapacity(); | ||
| 16 | zig_got.dirty = true; | ||
| 17 | return index; | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, macho_file: *MachO) !Index { | ||
| 21 | const comp = macho_file.base.comp; | ||
| 22 | const gpa = comp.gpa; | ||
| 23 | const index = try zig_got.allocateEntry(gpa); | ||
| 24 | const entry = &zig_got.entries.items[index]; | ||
| 25 | entry.* = sym_index; | ||
| 26 | const symbol = macho_file.getSymbol(sym_index); | ||
| 27 | symbol.flags.has_zig_got = true; | ||
| 28 | try symbol.addExtra(.{ .zig_got = index }, macho_file); | ||
| 29 | return index; | ||
| 30 | } | ||
| 31 | |||
| 32 | pub fn entryOffset(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 { | ||
| 33 | _ = zig_got; | ||
| 34 | const sect = macho_file.sections.items(.header)[macho_file.zig_got_section_index.?]; | ||
| 35 | return sect.offset + @sizeOf(u64) * index; | ||
| 36 | } | ||
| 37 | |||
| 38 | pub fn entryAddress(zig_got: ZigGotSection, index: Index, macho_file: *MachO) u64 { | ||
| 39 | _ = zig_got; | ||
| 40 | const sect = macho_file.sections.items(.header)[macho_file.zig_got_section_index.?]; | ||
| 41 | return sect.addr + @sizeOf(u64) * index; | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn size(zig_got: ZigGotSection, macho_file: *MachO) usize { | ||
| 45 | _ = macho_file; | ||
| 46 | return @sizeOf(u64) * zig_got.entries.items.len; | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn writeOne(zig_got: *ZigGotSection, macho_file: *MachO, index: Index) !void { | ||
| 50 | if (zig_got.dirty) { | ||
| 51 | const needed_size = zig_got.size(macho_file); | ||
| 52 | try macho_file.growSection(macho_file.zig_got_section_index.?, needed_size); | ||
| 53 | zig_got.dirty = false; | ||
| 54 | } | ||
| 55 | const off = zig_got.entryOffset(index, macho_file); | ||
| 56 | const entry = zig_got.entries.items[index]; | ||
| 57 | const value = macho_file.getSymbol(entry).getAddress(.{ .stubs = false }, macho_file); | ||
| 58 | |||
| 59 | var buf: [8]u8 = undefined; | ||
| 60 | std.mem.writeInt(u64, &buf, value, .little); | ||
| 61 | try macho_file.base.file.?.pwriteAll(&buf, off); | ||
| 62 | } | ||
| 63 | |||
| 64 | pub fn writeAll(zig_got: ZigGotSection, macho_file: *MachO, writer: anytype) !void { | ||
| 65 | for (zig_got.entries.items) |entry| { | ||
| 66 | const symbol = macho_file.getSymbol(entry); | ||
| 67 | const value = symbol.address(.{ .stubs = false }, macho_file); | ||
| 68 | try writer.writeInt(u64, value, .little); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn addDyldRelocs(zig_got: ZigGotSection, macho_file: *MachO) !void { | ||
| 73 | const tracy = trace(@src()); | ||
| 74 | defer tracy.end(); | ||
| 75 | const gpa = macho_file.base.comp.gpa; | ||
| 76 | const seg_id = macho_file.sections.items(.segment_id)[macho_file.zig_got_sect_index.?]; | ||
| 77 | const seg = macho_file.segments.items[seg_id]; | ||
| 78 | |||
| 79 | for (0..zig_got.symbols.items.len) |idx| { | ||
| 80 | const addr = zig_got.entryAddress(@intCast(idx), macho_file); | ||
| 81 | try macho_file.rebase.entries.append(gpa, .{ | ||
| 82 | .offset = addr - seg.vmaddr, | ||
| 83 | .segment_id = seg_id, | ||
| 84 | }); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | const FormatCtx = struct { | ||
| 89 | zig_got: ZigGotSection, | ||
| 90 | macho_file: *MachO, | ||
| 91 | }; | ||
| 92 | |||
| 93 | pub fn fmt(zig_got: ZigGotSection, macho_file: *MachO) std.fmt.Formatter(format2) { | ||
| 94 | return .{ .data = .{ .zig_got = zig_got, .macho_file = macho_file } }; | ||
| 95 | } | ||
| 96 | |||
| 97 | pub fn format2( | ||
| 98 | ctx: FormatCtx, | ||
| 99 | comptime unused_fmt_string: []const u8, | ||
| 100 | options: std.fmt.FormatOptions, | ||
| 101 | writer: anytype, | ||
| 102 | ) !void { | ||
| 103 | _ = options; | ||
| 104 | _ = unused_fmt_string; | ||
| 105 | try writer.writeAll("__zig_got\n"); | ||
| 106 | for (ctx.zig_got.entries.items, 0..) |entry, index| { | ||
| 107 | const symbol = ctx.macho_file.getSymbol(entry); | ||
| 108 | try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ | ||
| 109 | index, | ||
| 110 | ctx.zig_got.entryAddress(@intCast(index), ctx.macho_file), | ||
| 111 | entry, | ||
| 112 | symbol.getAddress(.{}, ctx.macho_file), | ||
| 113 | symbol.getName(ctx.macho_file), | ||
| 114 | }); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | }; | ||
| 118 | |||
| 1 | pub const GotSection = struct { | 119 | pub const GotSection = struct { |
| 2 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, | 120 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 3 | 121 |