| ... | ... | @@ -63,6 +63,88 @@ const Section = struct { |
| 63 | 63 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 64 | 64 | }; |
| 65 | 65 | |
| 66 | const SectionTable = struct { |
| 67 | entries: std.ArrayListUnmanaged(SymIndex) = .{}, |
| 68 | free_list: std.ArrayListUnmanaged(Index) = .{}, |
| 69 | lookup: std.AutoHashMapUnmanaged(SymIndex, Index) = .{}, |
| 70 | |
| 71 | const SymIndex = u32; |
| 72 | const Index = u32; |
| 73 | |
| 74 | pub fn deinit(st: *ST, allocator: Allocator) void { |
| 75 | st.entries.deinit(allocator); |
| 76 | st.free_list.deinit(allocator); |
| 77 | st.lookup.deinit(allocator); |
| 78 | } |
| 79 | |
| 80 | pub fn allocateEntry(st: *ST, allocator: Allocator, target: SymIndex) !Index { |
| 81 | try st.entries.ensureUnusedCapacity(allocator, 1); |
| 82 | const index = blk: { |
| 83 | if (st.free_list.popOrNull()) |index| { |
| 84 | log.debug(" (reusing entry index {d})", .{index}); |
| 85 | break :blk index; |
| 86 | } else { |
| 87 | log.debug(" (allocating entry at index {d})", .{st.entries.items.len}); |
| 88 | const index = @intCast(u32, st.entries.items.len); |
| 89 | _ = st.entries.addOneAssumeCapacity(); |
| 90 | break :blk index; |
| 91 | } |
| 92 | }; |
| 93 | st.entries.items[index] = target; |
| 94 | try st.lookup.putNoClobber(allocator, target, index); |
| 95 | return index; |
| 96 | } |
| 97 | |
| 98 | pub fn freeEntry(st: *ST, allocator: Allocator, target: SymIndex) void { |
| 99 | const index = st.lookup.get(target) orelse return; |
| 100 | st.free_list.append(allocator, index) catch {}; |
| 101 | st.entries.items[index] = 0; |
| 102 | _ = st.lookup.remove(target); |
| 103 | } |
| 104 | |
| 105 | const FormatContext = struct { |
| 106 | ctx: *Elf, |
| 107 | st: *const ST, |
| 108 | }; |
| 109 | |
| 110 | fn fmt( |
| 111 | ctx: FormatContext, |
| 112 | comptime unused_format_string: []const u8, |
| 113 | options: std.fmt.FormatOptions, |
| 114 | writer: anytype, |
| 115 | ) @TypeOf(writer).Error!void { |
| 116 | _ = options; |
| 117 | comptime assert(unused_format_string.len == 0); |
| 118 | |
| 119 | const base_addr = ctx.ctx.program_headers.items[ctx.ctx.phdr_got_index.?].p_vaddr; |
| 120 | const target = ctx.ctx.base.options.target; |
| 121 | const ptr_bits = target.cpu.arch.ptrBitWidth(); |
| 122 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 123 | |
| 124 | try writer.writeAll("SectionTable:\n"); |
| 125 | for (ctx.st.entries.items, 0..) |entry, i| { |
| 126 | try writer.print(" {d}@{x} => local(%{d})\n", .{ i, base_addr + i * ptr_bytes, entry }); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | fn format(st: ST, comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { |
| 131 | _ = st; |
| 132 | _ = unused_format_string; |
| 133 | _ = options; |
| 134 | _ = writer; |
| 135 | @compileError("do not format SectionTable directly; use st.fmtDebug()"); |
| 136 | } |
| 137 | |
| 138 | pub fn fmtDebug(st: ST, ctx: *Elf) std.fmt.Formatter(fmt) { |
| 139 | return .{ .data = .{ |
| 140 | .ctx = ctx, |
| 141 | .st = st, |
| 142 | } }; |
| 143 | } |
| 144 | |
| 145 | const ST = @This(); |
| 146 | }; |
| 147 | |
| 66 | 148 | const LazySymbolMetadata = struct { |
| 67 | 149 | text_atom: ?Atom.Index = null, |
| 68 | 150 | rodata_atom: ?Atom.Index = null, |
| ... | ... | @@ -148,17 +230,13 @@ global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, |
| 148 | 230 | |
| 149 | 231 | local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 150 | 232 | global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 151 | | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 152 | 233 | |
| 153 | | /// Same order as in the file. The value is the absolute vaddr value. |
| 154 | | /// If the vaddr of the executable program header changes, the entire |
| 155 | | /// offset table needs to be rewritten. |
| 156 | | offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 234 | got_table: SectionTable = .{}, |
| 157 | 235 | |
| 158 | 236 | phdr_table_dirty: bool = false, |
| 159 | 237 | shdr_table_dirty: bool = false, |
| 160 | 238 | shstrtab_dirty: bool = false, |
| 161 | | offset_table_count_dirty: bool = false, |
| 239 | got_table_count_dirty: bool = false, |
| 162 | 240 | |
| 163 | 241 | debug_strtab_dirty: bool = false, |
| 164 | 242 | debug_abbrev_section_dirty: bool = false, |
| ... | ... | @@ -329,8 +407,7 @@ pub fn deinit(self: *Elf) void { |
| 329 | 407 | self.global_symbols.deinit(gpa); |
| 330 | 408 | self.global_symbol_free_list.deinit(gpa); |
| 331 | 409 | self.local_symbol_free_list.deinit(gpa); |
| 332 | | self.offset_table_free_list.deinit(gpa); |
| 333 | | self.offset_table.deinit(gpa); |
| 410 | self.got_table.deinit(gpa); |
| 334 | 411 | |
| 335 | 412 | { |
| 336 | 413 | var it = self.decls.iterator(); |
| ... | ... | @@ -1289,6 +1366,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1289 | 1366 | assert(!self.shdr_table_dirty); |
| 1290 | 1367 | assert(!self.shstrtab_dirty); |
| 1291 | 1368 | assert(!self.debug_strtab_dirty); |
| 1369 | assert(!self.got_table_count_dirty); |
| 1292 | 1370 | } |
| 1293 | 1371 | |
| 1294 | 1372 | fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | ... | @@ -2168,7 +2246,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { |
| 2168 | 2246 | _ = self.atom_by_index_table.remove(local_sym_index); |
| 2169 | 2247 | self.getAtomPtr(atom_index).local_sym_index = 0; |
| 2170 | 2248 | |
| 2171 | | self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {}; |
| 2249 | self.got_table.freeEntry(gpa, local_sym_index); |
| 2172 | 2250 | } |
| 2173 | 2251 | |
| 2174 | 2252 | fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { |
| ... | ... | @@ -2191,11 +2269,9 @@ pub fn createAtom(self: *Elf) !Atom.Index { |
| 2191 | 2269 | const atom_index = @intCast(Atom.Index, self.atoms.items.len); |
| 2192 | 2270 | const atom = try self.atoms.addOne(gpa); |
| 2193 | 2271 | const local_sym_index = try self.allocateLocalSymbol(); |
| 2194 | | const offset_table_index = try self.allocateGotOffset(); |
| 2195 | 2272 | try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index); |
| 2196 | 2273 | atom.* = .{ |
| 2197 | 2274 | .local_sym_index = local_sym_index, |
| 2198 | | .offset_table_index = offset_table_index, |
| 2199 | 2275 | .prev_index = null, |
| 2200 | 2276 | .next_index = null, |
| 2201 | 2277 | }; |
| ... | ... | @@ -2352,26 +2428,6 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 { |
| 2352 | 2428 | return index; |
| 2353 | 2429 | } |
| 2354 | 2430 | |
| 2355 | | pub fn allocateGotOffset(self: *Elf) !u32 { |
| 2356 | | try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); |
| 2357 | | |
| 2358 | | const index = blk: { |
| 2359 | | if (self.offset_table_free_list.popOrNull()) |index| { |
| 2360 | | log.debug(" (reusing GOT offset at index {d})", .{index}); |
| 2361 | | break :blk index; |
| 2362 | | } else { |
| 2363 | | log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len}); |
| 2364 | | const index = @intCast(u32, self.offset_table.items.len); |
| 2365 | | _ = self.offset_table.addOneAssumeCapacity(); |
| 2366 | | self.offset_table_count_dirty = true; |
| 2367 | | break :blk index; |
| 2368 | | } |
| 2369 | | }; |
| 2370 | | |
| 2371 | | self.offset_table.items[index] = 0; |
| 2372 | | return index; |
| 2373 | | } |
| 2374 | | |
| 2375 | 2431 | fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { |
| 2376 | 2432 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 2377 | 2433 | for (unnamed_consts.items) |atom| { |
| ... | ... | @@ -2465,6 +2521,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2465 | 2521 | const decl_metadata = self.decls.get(decl_index).?; |
| 2466 | 2522 | const atom_index = decl_metadata.atom; |
| 2467 | 2523 | const atom = self.getAtom(atom_index); |
| 2524 | const local_sym_index = atom.getSymbolIndex().?; |
| 2468 | 2525 | |
| 2469 | 2526 | const shdr_index = decl_metadata.shdr; |
| 2470 | 2527 | if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) { |
| ... | ... | @@ -2485,8 +2542,9 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2485 | 2542 | local_sym.st_value = vaddr; |
| 2486 | 2543 | |
| 2487 | 2544 | log.debug(" (writing new offset table entry)", .{}); |
| 2488 | | self.offset_table.items[atom.offset_table_index] = vaddr; |
| 2489 | | try self.writeOffsetTableEntry(atom.offset_table_index); |
| 2545 | const got_entry_index = self.got_table.lookup.get(local_sym_index).?; |
| 2546 | self.got_table.entries.items[got_entry_index] = local_sym_index; |
| 2547 | try self.writeOffsetTableEntry(got_entry_index); |
| 2490 | 2548 | } |
| 2491 | 2549 | } else if (code.len < local_sym.st_size) { |
| 2492 | 2550 | self.shrinkAtom(atom_index, code.len); |
| ... | ... | @@ -2494,7 +2552,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2494 | 2552 | local_sym.st_size = code.len; |
| 2495 | 2553 | |
| 2496 | 2554 | // TODO this write could be avoided if no fields of the symbol were changed. |
| 2497 | | try self.writeSymbol(atom.getSymbolIndex().?); |
| 2555 | try self.writeSymbol(local_sym_index); |
| 2498 | 2556 | } else { |
| 2499 | 2557 | const local_sym = atom.getSymbolPtr(self); |
| 2500 | 2558 | local_sym.* = .{ |
| ... | ... | @@ -2509,12 +2567,12 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2509 | 2567 | errdefer self.freeAtom(atom_index); |
| 2510 | 2568 | log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); |
| 2511 | 2569 | |
| 2512 | | self.offset_table.items[atom.offset_table_index] = vaddr; |
| 2513 | 2570 | local_sym.st_value = vaddr; |
| 2514 | 2571 | local_sym.st_size = code.len; |
| 2515 | 2572 | |
| 2516 | | try self.writeSymbol(atom.getSymbolIndex().?); |
| 2517 | | try self.writeOffsetTableEntry(atom.offset_table_index); |
| 2573 | try self.writeSymbol(local_sym_index); |
| 2574 | const got_entry_index = try atom.getOrCreateOffsetTableEntry(self); |
| 2575 | try self.writeOffsetTableEntry(got_entry_index); |
| 2518 | 2576 | } |
| 2519 | 2577 | |
| 2520 | 2578 | const local_sym = atom.getSymbolPtr(self); |
| ... | ... | @@ -2755,12 +2813,12 @@ fn updateLazySymbolAtom( |
| 2755 | 2813 | errdefer self.freeAtom(atom_index); |
| 2756 | 2814 | log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); |
| 2757 | 2815 | |
| 2758 | | self.offset_table.items[atom.offset_table_index] = vaddr; |
| 2759 | 2816 | local_sym.st_value = vaddr; |
| 2760 | 2817 | local_sym.st_size = code.len; |
| 2761 | 2818 | |
| 2762 | 2819 | try self.writeSymbol(local_sym_index); |
| 2763 | | try self.writeOffsetTableEntry(atom.offset_table_index); |
| 2820 | const got_entry_index = try atom.getOrCreateOffsetTableEntry(self); |
| 2821 | try self.writeOffsetTableEntry(got_entry_index); |
| 2764 | 2822 | |
| 2765 | 2823 | const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr; |
| 2766 | 2824 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; |
| ... | ... | @@ -2991,30 +3049,32 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2991 | 3049 | |
| 2992 | 3050 | fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2993 | 3051 | const entry_size: u16 = self.archPtrWidthBytes(); |
| 2994 | | if (self.offset_table_count_dirty) { |
| 2995 | | const needed_size = self.offset_table.items.len * entry_size; |
| 3052 | if (self.got_table_count_dirty) { |
| 3053 | const needed_size = self.got_table.entries.items.len * entry_size; |
| 2996 | 3054 | try self.growAllocSection(self.got_section_index.?, needed_size); |
| 2997 | | self.offset_table_count_dirty = false; |
| 3055 | self.got_table_count_dirty = false; |
| 2998 | 3056 | } |
| 2999 | 3057 | const endian = self.base.options.target.cpu.arch.endian(); |
| 3000 | 3058 | const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; |
| 3001 | 3059 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 3002 | 3060 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| 3003 | 3061 | const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; |
| 3062 | const got_entry = self.got_table.entries.items[index]; |
| 3063 | const got_value = self.getSymbol(got_entry).st_value; |
| 3004 | 3064 | switch (entry_size) { |
| 3005 | 3065 | 2 => { |
| 3006 | 3066 | var buf: [2]u8 = undefined; |
| 3007 | | mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian); |
| 3067 | mem.writeInt(u16, &buf, @intCast(u16, got_value), endian); |
| 3008 | 3068 | try self.base.file.?.pwriteAll(&buf, off); |
| 3009 | 3069 | }, |
| 3010 | 3070 | 4 => { |
| 3011 | 3071 | var buf: [4]u8 = undefined; |
| 3012 | | mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); |
| 3072 | mem.writeInt(u32, &buf, @intCast(u32, got_value), endian); |
| 3013 | 3073 | try self.base.file.?.pwriteAll(&buf, off); |
| 3014 | 3074 | }, |
| 3015 | 3075 | 8 => { |
| 3016 | 3076 | var buf: [8]u8 = undefined; |
| 3017 | | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); |
| 3077 | mem.writeInt(u64, &buf, got_value, endian); |
| 3018 | 3078 | try self.base.file.?.pwriteAll(&buf, off); |
| 3019 | 3079 | |
| 3020 | 3080 | if (self.base.child_pid) |pid| { |