| ... | ... | @@ -159,6 +159,14 @@ strtab_needs_relocation: bool = false, |
| 159 | 159 | has_dices: bool = false, |
| 160 | 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 | 170 | /// A list of text blocks that have surplus capacity. This list can have false |
| 163 | 171 | /// positives, as functions grow and shrink over time, only sometimes being added |
| 164 | 172 | /// or removed from the freelist. |
| ... | ... | @@ -179,6 +187,7 @@ text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 179 | 187 | /// Pointer to the last allocated text block |
| 180 | 188 | last_text_block: ?*TextBlock = null, |
| 181 | 189 | |
| 190 | managed_blocks: std.ArrayListUnmanaged(TextBlock) = .{}, |
| 182 | 191 | blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, |
| 183 | 192 | |
| 184 | 193 | /// A list of all PIE fixups required for this run of the linker. |
| ... | ... | @@ -190,13 +199,6 @@ blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{}, |
| 190 | 199 | /// backends. |
| 191 | 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 | 202 | const SymbolWithLoc = struct { |
| 201 | 203 | // Table where the symbol can be found. |
| 202 | 204 | where: enum { |
| ... | ... | @@ -229,19 +231,6 @@ pub const PIEFixup = struct { |
| 229 | 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 | 234 | /// When allocating, the ideal_capacity is calculated by |
| 246 | 235 | /// actual_capacity + (actual_capacity / ideal_factor) |
| 247 | 236 | const ideal_factor = 2; |
| ... | ... | @@ -2244,9 +2233,7 @@ fn resolveSymbols(self: *MachO) !void { |
| 2244 | 2233 | .local_sym_index = local_sym_index, |
| 2245 | 2234 | }; |
| 2246 | 2235 | |
| 2247 | | const block = try self.base.allocator.create(TextBlock); |
| 2248 | | errdefer self.base.allocator.destroy(block); |
| 2249 | | |
| 2236 | const block = try self.managed_blocks.addOne(self.base.allocator); |
| 2250 | 2237 | block.* = TextBlock.empty; |
| 2251 | 2238 | block.local_sym_index = local_sym_index; |
| 2252 | 2239 | block.code = code; |
| ... | ... | @@ -2382,9 +2369,7 @@ fn resolveSymbols(self: *MachO) !void { |
| 2382 | 2369 | // We create an empty atom for this symbol. |
| 2383 | 2370 | // TODO perhaps we should special-case special symbols? Create a separate |
| 2384 | 2371 | // linked list of atoms? |
| 2385 | | const block = try self.base.allocator.create(TextBlock); |
| 2386 | | errdefer self.base.allocator.destroy(block); |
| 2387 | | |
| 2372 | const block = try self.managed_blocks.addOne(self.base.allocator); |
| 2388 | 2373 | block.* = TextBlock.empty; |
| 2389 | 2374 | block.local_sym_index = local_sym_index; |
| 2390 | 2375 | block.code = try self.base.allocator.alloc(u8, 0); |
| ... | ... | @@ -3243,9 +3228,8 @@ pub fn deinit(self: *MachO) void { |
| 3243 | 3228 | ds.deinit(self.base.allocator); |
| 3244 | 3229 | } |
| 3245 | 3230 | |
| 3231 | self.pending_updates.deinit(self.base.allocator); |
| 3246 | 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 | 3233 | self.got_entries.deinit(self.base.allocator); |
| 3250 | 3234 | self.got_entries_map.deinit(self.base.allocator); |
| 3251 | 3235 | self.got_entries_free_list.deinit(self.base.allocator); |
| ... | ... | @@ -3288,8 +3272,12 @@ pub fn deinit(self: *MachO) void { |
| 3288 | 3272 | } |
| 3289 | 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 | 3279 | self.blocks.deinit(self.base.allocator); |
| 3280 | self.text_block_free_list.deinit(self.base.allocator); |
| 3293 | 3281 | } |
| 3294 | 3282 | |
| 3295 | 3283 | pub fn closeFiles(self: MachO) void { |
| ... | ... | @@ -3302,6 +3290,9 @@ pub fn closeFiles(self: MachO) void { |
| 3302 | 3290 | } |
| 3303 | 3291 | |
| 3304 | 3292 | fn freeTextBlock(self: *MachO, text_block: *TextBlock) void { |
| 3293 | log.debug("freeTextBlock {*}", .{text_block}); |
| 3294 | // text_block.deinit(self.base.allocator); |
| 3295 | |
| 3305 | 3296 | var already_have_free_list_node = false; |
| 3306 | 3297 | { |
| 3307 | 3298 | var i: usize = 0; |
| ... | ... | @@ -3467,18 +3458,22 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3467 | 3458 | .val = decl.val, |
| 3468 | 3459 | }, &code_buffer, .none); |
| 3469 | 3460 | |
| 3470 | | const code = switch (res) { |
| 3471 | | .externally_managed => |x| x, |
| 3472 | | .appended => code_buffer.items, |
| 3473 | | .fail => |em| { |
| 3474 | | // Clear any PIE fixups for this decl. |
| 3475 | | self.pie_fixups.shrinkRetainingCapacity(0); |
| 3476 | | // Clear any stub fixups for this decl. |
| 3477 | | self.stub_fixups.shrinkRetainingCapacity(0); |
| 3478 | | decl.analysis = .codegen_failure; |
| 3479 | | try module.failed_decls.put(module.gpa, decl, em); |
| 3480 | | return; |
| 3481 | | }, |
| 3461 | const code = blk: { |
| 3462 | switch (res) { |
| 3463 | .externally_managed => |x| break :blk x, |
| 3464 | .appended => { |
| 3465 | decl.link.macho.code = code_buffer.toOwnedSlice(); |
| 3466 | log.warn("WAT", .{}); |
| 3467 | break :blk decl.link.macho.code; |
| 3468 | }, |
| 3469 | .fail => |em| { |
| 3470 | // Clear any PIE fixups for this decl. |
| 3471 | self.pie_fixups.shrinkRetainingCapacity(0); |
| 3472 | decl.analysis = .codegen_failure; |
| 3473 | try module.failed_decls.put(module.gpa, decl, em); |
| 3474 | return; |
| 3475 | }, |
| 3476 | } |
| 3482 | 3477 | }; |
| 3483 | 3478 | |
| 3484 | 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 | 3554 | switch (self.base.options.target.cpu.arch) { |
| 3560 | 3555 | .x86_64 => { |
| 3561 | 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 | 3559 | .aarch64 => { |
| 3565 | 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 | 3563 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 3569 | 3564 | aarch64.Instruction, |
| 3570 | 3565 | aarch64.Instruction.pc_relative_address, |
| ... | ... | @@ -3576,7 +3571,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 3576 | 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 | 3575 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 3581 | 3576 | aarch64.Instruction, |
| 3582 | 3577 | aarch64.Instruction.load_store_register, |
| ... | ... | @@ -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) |
| 3594 | | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 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); |
| 3588 | // Resolve relocations |
| 3589 | try decl.link.macho.resolveRelocs(self); |
| 3619 | 3590 | |
| 3620 | | self.rebase_info_dirty = true; |
| 3621 | | self.lazy_binding_info_dirty = true; |
| 3591 | // Apply pending updates |
| 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 | 3606 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 3627 | 3607 | const section_offset = symbol.n_value - text_section.addr; |
| 3628 | 3608 | const file_offset = text_section.offset + section_offset; |
| ... | ... | @@ -3756,6 +3736,7 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 3756 | 3736 | } |
| 3757 | 3737 | |
| 3758 | 3738 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { |
| 3739 | log.debug("freeDecl {*}", .{decl}); |
| 3759 | 3740 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 3760 | 3741 | self.freeTextBlock(&decl.link.macho); |
| 3761 | 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 | 4295 | // should be deleted because the block that it points to has grown to take up |
| 4315 | 4296 | // more of the extra capacity. |
| 4316 | 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 | 4300 | } else { |
| 4319 | 4301 | i += 1; |
| 4320 | 4302 | } |
| ... | ... | @@ -4386,25 +4368,43 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 4386 | 4368 | return vaddr; |
| 4387 | 4369 | } |
| 4388 | 4370 | |
| 4389 | | pub fn addExternFn(self: *MachO, name: []const u8) !SymbolWithLoc { |
| 4390 | | log.debug("adding new extern function '{s}' with dylib ordinal 1", .{name}); |
| 4371 | pub fn addExternFn(self: *MachO, name: []const u8) !u32 { |
| 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 | 4382 | const import_sym_index = @intCast(u32, self.imports.items.len); |
| 4392 | 4383 | try self.imports.append(self.base.allocator, .{ |
| 4393 | | .n_strx = try self.makeString(name), |
| 4384 | .n_strx = try self.makeString(sym_name), |
| 4394 | 4385 | .n_type = macho.N_UNDF | macho.N_EXT, |
| 4395 | 4386 | .n_sect = 0, |
| 4396 | 4387 | .n_desc = packDylibOrdinal(1), |
| 4397 | 4388 | .n_value = 0, |
| 4398 | 4389 | }); |
| 4399 | | const resolv = .{ |
| 4390 | try self.symbol_resolver.putNoClobber(self.base.allocator, sym_name, .{ |
| 4400 | 4391 | .where = .import, |
| 4401 | 4392 | .where_index = import_sym_index, |
| 4402 | | }; |
| 4403 | | try self.symbol_resolver.putNoClobber(self.base.allocator, try self.base.allocator.dupe(u8, name), resolv); |
| 4393 | }); |
| 4394 | |
| 4404 | 4395 | const stubs_index = @intCast(u32, self.stubs.items.len); |
| 4405 | 4396 | try self.stubs.append(self.base.allocator, import_sym_index); |
| 4406 | 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 | 4410 | const NextSegmentAddressAndOffset = struct { |