| ... | @@ -159,6 +159,14 @@ strtab_needs_relocation: bool = false, | ... | @@ -159,6 +159,14 @@ strtab_needs_relocation: bool = false, |
| 159 | has_dices: bool = false, | 159 | has_dices: bool = false, |
| 160 | has_stabs: bool = false, | 160 | has_stabs: bool = false, |
| 161 | | 161 | |
| | 162 | pending_updates: std.ArrayListUnmanaged(struct { |
| | 163 | kind: enum { |
| | 164 | got, |
| | 165 | stub, |
| | 166 | }, |
| | 167 | index: u32, |
| | 168 | }) = .{}, |
| | 169 | |
| 162 | /// A list of text blocks that have surplus capacity. This list can have false | 170 | /// A list of text blocks that have surplus capacity. This list can have false |
| 163 | /// positives, as functions grow and shrink over time, only sometimes being added | 171 | /// positives, as functions grow and shrink over time, only sometimes being added |
| 164 | /// or removed from the freelist. | 172 | /// or removed from the freelist. |
| ... | @@ -179,6 +187,7 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, | ... | @@ -179,6 +187,7 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 179 | /// Pointer to the last allocated text block | 187 | /// Pointer to the last allocated text block |
| 180 | last_text_block: ?*TextBlock = null, | 188 | last_text_block: ?*TextBlock = null, |
| 181 | | 189 | |
| | 190 | managed_blocks: std.ArrayListUnmanaged(TextBlock) = .{}, |
| 182 | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, | 191 | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, |
| 183 | | 192 | |
| 184 | /// A list of all PIE fixups required for this run of the linker. | 193 | /// A list of all PIE fixups required for this run of the linker. |
| ... | @@ -190,13 +199,6 @@ blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, | ... | @@ -190,13 +199,6 @@ blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, |
| 190 | /// backends. | 199 | /// backends. |
| 191 | pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{}, | 200 | pie_fixups: std.ArrayListUnmanaged(PIEFixup) = .{}, |
| 192 | | 201 | |
| 193 | /// A list of all stub (extern decls) fixups required for this run of the linker. | | |
| 194 | /// Warning, this is currently NOT thread-safe. See the TODO below. | | |
| 195 | /// TODO Move this list inside `updateDecl` where it should be allocated | | |
| 196 | /// prior to calling `generateSymbol`, and then immediately deallocated | | |
| 197 | /// rather than sitting in the global scope. | | |
| 198 | stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{}, | | |
| 199 | | | |
| 200 | const SymbolWithLoc = struct { | 202 | const SymbolWithLoc = struct { |
| 201 | // Table where the symbol can be found. | 203 | // Table where the symbol can be found. |
| 202 | where: enum { | 204 | where: enum { |
| ... | @@ -229,19 +231,6 @@ pub const PIEFixup = struct { | ... | @@ -229,19 +231,6 @@ pub const PIEFixup = struct { |
| 229 | size: usize, | 231 | size: usize, |
| 230 | }; | 232 | }; |
| 231 | | 233 | |
| 232 | pub const StubFixup = struct { | | |
| 233 | /// Id of extern (lazy) symbol. | | |
| 234 | symbol: u32, | | |
| 235 | /// Signals whether the symbol has already been declared before. If so, | | |
| 236 | /// then there is no need to rewrite the stub entry and related. | | |
| 237 | already_defined: bool, | | |
| 238 | /// Where in the byte stream we should perform the fixup. | | |
| 239 | start: usize, | | |
| 240 | /// The length of the byte stream. For x86_64, this will be | | |
| 241 | /// variable. For aarch64, it will be fixed at 4 bytes. | | |
| 242 | len: usize, | | |
| 243 | }; | | |
| 244 | | | |
| 245 | /// When allocating, the ideal_capacity is calculated by | 234 | /// When allocating, the ideal_capacity is calculated by |
| 246 | /// actual_capacity + (actual_capacity / ideal_factor) | 235 | /// actual_capacity + (actual_capacity / ideal_factor) |
| 247 | const ideal_factor = 2; | 236 | const ideal_factor = 2; |
| ... | @@ -2244,9 +2233,7 @@ fn resolveSymbols(self: *MachO) !void { | ... | @@ -2244,9 +2233,7 @@ fn resolveSymbols(self: *MachO) !void { |
| 2244 | .local_sym_index = local_sym_index, | 2233 | .local_sym_index = local_sym_index, |
| 2245 | }; | 2234 | }; |
| 2246 | | 2235 | |
| 2247 | const block = try self.base.allocator.create(TextBlock); | 2236 | const block = try self.managed_blocks.addOne(self.base.allocator); |
| 2248 | errdefer self.base.allocator.destroy(block); | | |
| 2249 | | | |
| 2250 | block.* = TextBlock.empty; | 2237 | block.* = TextBlock.empty; |
| 2251 | block.local_sym_index = local_sym_index; | 2238 | block.local_sym_index = local_sym_index; |
| 2252 | block.code = code; | 2239 | block.code = code; |
| ... | @@ -2382,9 +2369,7 @@ fn resolveSymbols(self: *MachO) !void { | ... | @@ -2382,9 +2369,7 @@ fn resolveSymbols(self: *MachO) !void { |
| 2382 | // We create an empty atom for this symbol. | 2369 | // We create an empty atom for this symbol. |
| 2383 | // TODO perhaps we should special-case special symbols? Create a separate | 2370 | // TODO perhaps we should special-case special symbols? Create a separate |
| 2384 | // linked list of atoms? | 2371 | // linked list of atoms? |
| 2385 | const block = try self.base.allocator.create(TextBlock); | 2372 | const block = try self.managed_blocks.addOne(self.base.allocator); |
| 2386 | errdefer self.base.allocator.destroy(block); | | |
| 2387 | | | |
| 2388 | block.* = TextBlock.empty; | 2373 | block.* = TextBlock.empty; |
| 2389 | block.local_sym_index = local_sym_index; | 2374 | block.local_sym_index = local_sym_index; |
| 2390 | block.code = try self.base.allocator.alloc(u8, 0); | 2375 | block.code = try self.base.allocator.alloc(u8, 0); |
| ... | @@ -3243,9 +3228,8 @@ pub fn deinit(self: *MachO) void { | ... | @@ -3243,9 +3228,8 @@ pub fn deinit(self: *MachO) void { |
| 3243 | ds.deinit(self.base.allocator); | 3228 | ds.deinit(self.base.allocator); |
| 3244 | } | 3229 | } |
| 3245 | | 3230 | |
| | 3231 | self.pending_updates.deinit(self.base.allocator); |
| 3246 | self.pie_fixups.deinit(self.base.allocator); | 3232 | self.pie_fixups.deinit(self.base.allocator); |
| 3247 | self.stub_fixups.deinit(self.base.allocator); | | |
| 3248 | self.text_block_free_list.deinit(self.base.allocator); | | |
| 3249 | self.got_entries.deinit(self.base.allocator); | 3233 | self.got_entries.deinit(self.base.allocator); |
| 3250 | self.got_entries_map.deinit(self.base.allocator); | 3234 | self.got_entries_map.deinit(self.base.allocator); |
| 3251 | self.got_entries_free_list.deinit(self.base.allocator); | 3235 | self.got_entries_free_list.deinit(self.base.allocator); |
| ... | @@ -3288,8 +3272,12 @@ pub fn deinit(self: *MachO) void { | ... | @@ -3288,8 +3272,12 @@ pub fn deinit(self: *MachO) void { |
| 3288 | } | 3272 | } |
| 3289 | self.load_commands.deinit(self.base.allocator); | 3273 | self.load_commands.deinit(self.base.allocator); |
| 3290 | | 3274 | |
| 3291 | // TODO dealloc all blocks | 3275 | for (self.managed_blocks.items) |*block| { |
| | 3276 | block.deinit(self.base.allocator); |
| | 3277 | } |
| | 3278 | self.managed_blocks.deinit(self.base.allocator); |
| 3292 | self.blocks.deinit(self.base.allocator); | 3279 | self.blocks.deinit(self.base.allocator); |
| | 3280 | self.text_block_free_list.deinit(self.base.allocator); |
| 3293 | } | 3281 | } |
| 3294 | | 3282 | |
| 3295 | pub fn closeFiles(self: MachO) void { | 3283 | pub fn closeFiles(self: MachO) void { |
| ... | @@ -3302,6 +3290,9 @@ pub fn closeFiles(self: MachO) void { | ... | @@ -3302,6 +3290,9 @@ pub fn closeFiles(self: MachO) void { |
| 3302 | } | 3290 | } |
| 3303 | | 3291 | |
| 3304 | fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { | 3292 | fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { |
| | 3293 | log.debug("freeTextBlock {*}", .{text_block}); |
| | 3294 | // text_block.deinit(self.base.allocator); |
| | 3295 | |
| 3305 | var already_have_free_list_node = false; | 3296 | var already_have_free_list_node = false; |
| 3306 | { | 3297 | { |
| 3307 | var i: usize = 0; | 3298 | var i: usize = 0; |
| ... | @@ -3467,18 +3458,22 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -3467,18 +3458,22 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3467 | .val = decl.val, | 3458 | .val = decl.val, |
| 3468 | }, &code_buffer, .none); | 3459 | }, &code_buffer, .none); |
| 3469 | | 3460 | |
| 3470 | const code = switch (res) { | 3461 | const code = blk: { |
| 3471 | .externally_managed => |x| x, | 3462 | switch (res) { |
| 3472 | .appended => code_buffer.items, | 3463 | .externally_managed => |x| break :blk x, |
| 3473 | .fail => |em| { | 3464 | .appended => { |
| 3474 | // Clear any PIE fixups for this decl. | 3465 | decl.link.macho.code = code_buffer.toOwnedSlice(); |
| 3475 | self.pie_fixups.shrinkRetainingCapacity(0); | 3466 | log.warn("WAT", .{}); |
| 3476 | // Clear any stub fixups for this decl. | 3467 | break :blk decl.link.macho.code; |
| 3477 | self.stub_fixups.shrinkRetainingCapacity(0); | 3468 | }, |
| 3478 | decl.analysis = .codegen_failure; | 3469 | .fail => |em| { |
| 3479 | try module.failed_decls.put(module.gpa, decl, em); | 3470 | // Clear any PIE fixups for this decl. |
| 3480 | return; | 3471 | self.pie_fixups.shrinkRetainingCapacity(0); |
| 3481 | }, | 3472 | decl.analysis = .codegen_failure; |
| | 3473 | try module.failed_decls.put(module.gpa, decl, em); |
| | 3474 | return; |
| | 3475 | }, |
| | 3476 | } |
| 3482 | }; | 3477 | }; |
| 3483 | | 3478 | |
| 3484 | const required_alignment = decl.ty.abiAlignment(self.base.options.target); | 3479 | const required_alignment = decl.ty.abiAlignment(self.base.options.target); |
| ... | @@ -3559,12 +3554,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -3559,12 +3554,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3559 | switch (self.base.options.target.cpu.arch) { | 3554 | switch (self.base.options.target.cpu.arch) { |
| 3560 | .x86_64 => { | 3555 | .x86_64 => { |
| 3561 | const displacement = try math.cast(u32, target_addr - this_addr - 4); | 3556 | const displacement = try math.cast(u32, target_addr - this_addr - 4); |
| 3562 | mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement); | 3557 | mem.writeIntLittle(u32, decl.link.macho.code[fixup.offset..][0..4], displacement); |
| 3563 | }, | 3558 | }, |
| 3564 | .aarch64 => { | 3559 | .aarch64 => { |
| 3565 | // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible). | 3560 | // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible). |
| 3566 | { | 3561 | { |
| 3567 | const inst = code_buffer.items[fixup.offset..][0..4]; | 3562 | const inst = decl.link.macho.code[fixup.offset..][0..4]; |
| 3568 | var parsed = mem.bytesAsValue(meta.TagPayload( | 3563 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 3569 | aarch64.Instruction, | 3564 | aarch64.Instruction, |
| 3570 | aarch64.Instruction.pc_relative_address, | 3565 | aarch64.Instruction.pc_relative_address, |
| ... | @@ -3576,7 +3571,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -3576,7 +3571,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3576 | parsed.immlo = @truncate(u2, pages); | 3571 | parsed.immlo = @truncate(u2, pages); |
| 3577 | } | 3572 | } |
| 3578 | { | 3573 | { |
| 3579 | const inst = code_buffer.items[fixup.offset + 4 ..][0..4]; | 3574 | const inst = decl.link.macho.code[fixup.offset + 4 ..][0..4]; |
| 3580 | var parsed = mem.bytesAsValue(meta.TagPayload( | 3575 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 3581 | aarch64.Instruction, | 3576 | aarch64.Instruction, |
| 3582 | aarch64.Instruction.load_store_register, | 3577 | aarch64.Instruction.load_store_register, |
| ... | @@ -3590,39 +3585,24 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -3590,39 +3585,24 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3590 | } | 3585 | } |
| 3591 | } | 3586 | } |
| 3592 | | 3587 | |
| 3593 | // Resolve stubs (if any) | 3588 | // Resolve relocations |
| 3594 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | 3589 | try decl.link.macho.resolveRelocs(self); |
| 3595 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; | | |
| 3596 | for (self.stub_fixups.items) |fixup| { | | |
| 3597 | const stubs_index = self.stubs_map.get(fixup.symbol) orelse unreachable; | | |
| 3598 | const stub_addr = stubs.addr + stubs_index * stubs.reserved2; | | |
| 3599 | const text_addr = symbol.n_value + fixup.start; | | |
| 3600 | switch (self.base.options.target.cpu.arch) { | | |
| 3601 | .x86_64 => { | | |
| 3602 | assert(stub_addr >= text_addr + fixup.len); | | |
| 3603 | const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len); | | |
| 3604 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; | | |
| 3605 | mem.writeIntSliceLittle(u32, placeholder, displacement); | | |
| 3606 | }, | | |
| 3607 | .aarch64 => { | | |
| 3608 | assert(stub_addr >= text_addr); | | |
| 3609 | const displacement = try math.cast(i28, stub_addr - text_addr); | | |
| 3610 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; | | |
| 3611 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32()); | | |
| 3612 | }, | | |
| 3613 | else => unreachable, // unsupported target architecture | | |
| 3614 | } | | |
| 3615 | if (!fixup.already_defined) { | | |
| 3616 | try self.writeStub(stubs_index); | | |
| 3617 | try self.writeStubInStubHelper(stubs_index); | | |
| 3618 | try self.writeLazySymbolPointer(stubs_index); | | |
| 3619 | | 3590 | |
| 3620 | self.rebase_info_dirty = true; | 3591 | // Apply pending updates |
| 3621 | self.lazy_binding_info_dirty = true; | 3592 | while (self.pending_updates.popOrNull()) |update| { |
| | 3593 | switch (update.kind) { |
| | 3594 | .got => unreachable, |
| | 3595 | .stub => { |
| | 3596 | try self.writeStub(update.index); |
| | 3597 | try self.writeStubInStubHelper(update.index); |
| | 3598 | try self.writeLazySymbolPointer(update.index); |
| | 3599 | self.rebase_info_dirty = true; |
| | 3600 | self.lazy_binding_info_dirty = true; |
| | 3601 | }, |
| 3622 | } | 3602 | } |
| 3623 | } | 3603 | } |
| 3624 | self.stub_fixups.shrinkRetainingCapacity(0); | | |
| 3625 | | 3604 | |
| | 3605 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 3626 | const text_section = text_segment.sections.items[self.text_section_index.?]; | 3606 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 3627 | const section_offset = symbol.n_value - text_section.addr; | 3607 | const section_offset = symbol.n_value - text_section.addr; |
| 3628 | const file_offset = text_section.offset + section_offset; | 3608 | const file_offset = text_section.offset + section_offset; |
| ... | @@ -3756,6 +3736,7 @@ pub fn deleteExport(self: *MachO, exp: Export) void { | ... | @@ -3756,6 +3736,7 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 3756 | } | 3736 | } |
| 3757 | | 3737 | |
| 3758 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { | 3738 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| | 3739 | log.debug("freeDecl {*}", .{decl}); |
| 3759 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | 3740 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 3760 | self.freeTextBlock(&decl.link.macho); | 3741 | self.freeTextBlock(&decl.link.macho); |
| 3761 | if (decl.link.macho.local_sym_index != 0) { | 3742 | if (decl.link.macho.local_sym_index != 0) { |
| ... | @@ -4314,7 +4295,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -4314,7 +4295,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4314 | // should be deleted because the block that it points to has grown to take up | 4295 | // should be deleted because the block that it points to has grown to take up |
| 4315 | // more of the extra capacity. | 4296 | // more of the extra capacity. |
| 4316 | if (!big_block.freeListEligible(self.*)) { | 4297 | if (!big_block.freeListEligible(self.*)) { |
| 4317 | _ = self.text_block_free_list.swapRemove(i); | 4298 | const bl = self.text_block_free_list.swapRemove(i); |
| | 4299 | bl.deinit(self.base.allocator); |
| 4318 | } else { | 4300 | } else { |
| 4319 | i += 1; | 4301 | i += 1; |
| 4320 | } | 4302 | } |
| ... | @@ -4386,25 +4368,43 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -4386,25 +4368,43 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4386 | return vaddr; | 4368 | return vaddr; |
| 4387 | } | 4369 | } |
| 4388 | | 4370 | |
| 4389 | pub fn addExternFn(self: *MachO, name: []const u8) !SymbolWithLoc { | 4371 | pub fn addExternFn(self: *MachO, name: []const u8) !u32 { |
| 4390 | log.debug("adding new extern function '{s}' with dylib ordinal 1", .{name}); | 4372 | const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name}); |
| | 4373 | const already_defined = self.symbol_resolver.contains(sym_name); |
| | 4374 | |
| | 4375 | if (already_defined) { |
| | 4376 | const resolv = self.symbol_resolver.get(sym_name) orelse unreachable; |
| | 4377 | self.base.allocator.free(sym_name); |
| | 4378 | return resolv.where_index; |
| | 4379 | } |
| | 4380 | |
| | 4381 | log.debug("adding new extern function '{s}' with dylib ordinal 1", .{sym_name}); |
| 4391 | const import_sym_index = @intCast(u32, self.imports.items.len); | 4382 | const import_sym_index = @intCast(u32, self.imports.items.len); |
| 4392 | try self.imports.append(self.base.allocator, .{ | 4383 | try self.imports.append(self.base.allocator, .{ |
| 4393 | .n_strx = try self.makeString(name), | 4384 | .n_strx = try self.makeString(sym_name), |
| 4394 | .n_type = macho.N_UNDF | macho.N_EXT, | 4385 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 4395 | .n_sect = 0, | 4386 | .n_sect = 0, |
| 4396 | .n_desc = packDylibOrdinal(1), | 4387 | .n_desc = packDylibOrdinal(1), |
| 4397 | .n_value = 0, | 4388 | .n_value = 0, |
| 4398 | }); | 4389 | }); |
| 4399 | const resolv = .{ | 4390 | try self.symbol_resolver.putNoClobber(self.base.allocator, sym_name, .{ |
| 4400 | .where = .import, | 4391 | .where = .import, |
| 4401 | .where_index = import_sym_index, | 4392 | .where_index = import_sym_index, |
| 4402 | }; | 4393 | }); |
| 4403 | try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, name), resolv); | 4394 | |
| 4404 | const stubs_index = @intCast(u32, self.stubs.items.len); | 4395 | const stubs_index = @intCast(u32, self.stubs.items.len); |
| 4405 | try self.stubs.append(self.base.allocator, import_sym_index); | 4396 | try self.stubs.append(self.base.allocator, import_sym_index); |
| 4406 | try self.stubs_map.putNoClobber(self.base.allocator, import_sym_index, stubs_index); | 4397 | try self.stubs_map.putNoClobber(self.base.allocator, import_sym_index, stubs_index); |
| 4407 | return resolv; | 4398 | |
| | 4399 | // TODO discuss this. The caller context expects codegen.InnerError{ OutOfMemory, CodegenFail }, |
| | 4400 | // which obviously doesn't include file writing op errors. So instead of trying to write the stub |
| | 4401 | // entry right here and now, queue it up and dispose of when updating decl. |
| | 4402 | try self.pending_updates.append(self.base.allocator, .{ |
| | 4403 | .kind = .stub, |
| | 4404 | .index = stubs_index, |
| | 4405 | }); |
| | 4406 | |
| | 4407 | return import_sym_index; |
| 4408 | } | 4408 | } |
| 4409 | | 4409 | |
| 4410 | const NextSegmentAddressAndOffset = struct { | 4410 | const NextSegmentAddressAndOffset = struct { |