From 009a349779186548480823140dd1f8758d600221 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 2 Sep 2023 17:23:49 +0200 Subject: elf: add global symbol resolver --- src/link/Elf.zig | 444 ++++++++++++++++++++++++++++---------------------- src/link/Elf/Atom.zig | 22 +-- 2 files changed, 262 insertions(+), 204 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b039b8d761..66328528a4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,99 +1,3 @@ -const Elf = @This(); - -const std = @import("std"); -const build_options = @import("build_options"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const elf = std.elf; -const fs = std.fs; -const log = std.log.scoped(.link); -const math = std.math; -const mem = std.mem; - -const codegen = @import("../codegen.zig"); -const glibc = @import("../glibc.zig"); -const link = @import("../link.zig"); -const lldMain = @import("../main.zig").lldMain; -const musl = @import("../musl.zig"); -const target_util = @import("../target.zig"); -const trace = @import("../tracy.zig").trace; - -const Air = @import("../Air.zig"); -const Allocator = std.mem.Allocator; -pub const Atom = @import("Elf/Atom.zig"); -const Cache = std.Build.Cache; -const Compilation = @import("../Compilation.zig"); -const Dwarf = @import("Dwarf.zig"); -const File = link.File; -const Liveness = @import("../Liveness.zig"); -const LlvmObject = @import("../codegen/llvm.zig").Object; -const Module = @import("../Module.zig"); -const InternPool = @import("../InternPool.zig"); -const Package = @import("../Package.zig"); -const StringTable = @import("strtab.zig").StringTable; -const TableSection = @import("table_section.zig").TableSection; -const Type = @import("../type.zig").Type; -const TypedValue = @import("../TypedValue.zig"); -const Value = @import("../value.zig").Value; - -const default_entry_addr = 0x8000000; - -pub const base_tag: File.Tag = .elf; - -const Section = struct { - shdr: elf.Elf64_Shdr, - phdr_index: u16, - - /// Index of the last allocated atom in this section. - last_atom_index: ?Atom.Index = null, - - /// A list of atoms that have surplus capacity. This list can have false - /// positives, as functions grow and shrink over time, only sometimes being added - /// or removed from the freelist. - /// - /// An atom has surplus capacity when its overcapacity value is greater than - /// padToIdeal(minimum_atom_size). That is, when it has so - /// much extra capacity, that we could fit a small new symbol in it, itself with - /// ideal_capacity or more. - /// - /// Ideal capacity is defined by size + (size / ideal_factor) - /// - /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that - /// overcapacity can be negative. A simple way to have negative overcapacity is to - /// allocate a fresh text block, which will have ideal capacity, and then grow it - /// by 1 byte. It will then have -1 overcapacity. - free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, -}; - -const LazySymbolMetadata = struct { - const State = enum { unused, pending_flush, flushed }; - text_atom: Atom.Index = undefined, - rodata_atom: Atom.Index = undefined, - text_state: State = .unused, - rodata_state: State = .unused, -}; - -const DeclMetadata = struct { - atom: Atom.Index, - shdr: u16, - /// A list of all exports aliases of this Decl. - exports: std.ArrayListUnmanaged(u32) = .{}, - - fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 { - for (m.exports.items) |exp| { - if (mem.eql(u8, name, elf_file.getGlobalName(exp))) return exp; - } - return null; - } - - fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { - for (m.exports.items) |*exp| { - if (mem.eql(u8, name, elf_file.getGlobalName(exp.*))) return exp; - } - return null; - } -}; - base: File, dwarf: ?Dwarf = null, @@ -151,11 +55,13 @@ strtab_section_index: ?u16 = null, /// local symbols, they cannot be mixed. So we must buffer all the global symbols and /// write them at the end. These are only the local symbols. The length of this array /// is the value used for sh_info in the .symtab section. -local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +locals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +globals: std.ArrayListUnmanaged(u32) = .{}, +resolver: std.StringHashMapUnmanaged(u32) = .{}, +unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, -local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, -global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, +locals_free_list: std.ArrayListUnmanaged(u32) = .{}, +globals_free_list: std.ArrayListUnmanaged(u32) = .{}, got_table: TableSection(u32) = .{}, @@ -247,14 +153,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option self.shdr_table_dirty = true; // Index 0 is always a null symbol. - try self.local_symbols.append(allocator, .{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }); + try self.locals.append(allocator, null_sym); // There must always be a null section in index 0 try self.sections.append(allocator, .{ @@ -329,11 +228,20 @@ pub fn deinit(self: *Elf) void { self.program_headers.deinit(gpa); self.shstrtab.deinit(gpa); self.strtab.deinit(gpa); - self.local_symbols.deinit(gpa); - self.global_symbols.deinit(gpa); - self.global_symbol_free_list.deinit(gpa); - self.local_symbol_free_list.deinit(gpa); + self.locals.deinit(gpa); + self.globals.deinit(gpa); + self.globals_free_list.deinit(gpa); + self.locals_free_list.deinit(gpa); self.got_table.deinit(gpa); + self.unresolved.deinit(gpa); + + { + var it = self.resolver.keyIterator(); + while (it.next()) |key_ptr| { + gpa.free(key_ptr.*); + } + self.resolver.deinit(gpa); + } { var it = self.decls.iterator(); @@ -743,7 +651,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .sh_size = file_size, // The section header index of the associated string table. .sh_link = self.strtab_section_index.?, - .sh_info = @as(u32, @intCast(self.local_symbols.items.len)), + .sh_info = @as(u32, @intCast(self.locals.items.len)), .sh_addralign = min_align, .sh_entsize = each_size, }, @@ -908,7 +816,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { { // Iterate over symbols, populating free_list and last_text_block. - if (self.local_symbols.items.len != 1) { + if (self.locals.items.len != 1) { @panic("TODO implement setting up free_list and last_text_block from existing ELF file"); } // We are starting with an empty file. The default values are correct, null and empty list. @@ -1109,7 +1017,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)}); for (relocs.items) |*reloc| { - const target_sym = self.local_symbols.items[reloc.target]; + const target_sym = self.locals.items[reloc.target]; const target_vaddr = target_sym.st_value + reloc.addend; if (target_vaddr == reloc.prev_vaddr) continue; @@ -2219,22 +2127,14 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { } // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. - const local_sym_index = atom.getSymbolIndex().?; - - log.debug("adding %{d} to local symbols free list", .{local_sym_index}); - self.local_symbol_free_list.append(gpa, local_sym_index) catch {}; - self.local_symbols.items[local_sym_index] = .{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }; - _ = self.atom_by_index_table.remove(local_sym_index); - self.getAtomPtr(atom_index).local_sym_index = 0; - - self.got_table.freeEntry(gpa, local_sym_index); + const sym_index = atom.getSymbolIndex().?; + + log.debug("adding %{d} to local symbols free list", .{sym_index}); + self.locals_free_list.append(gpa, sym_index) catch {}; + self.locals.items[sym_index] = null_sym; + _ = self.atom_by_index_table.remove(sym_index); + self.getAtomPtr(atom_index).sym_index = 0; + self.got_table.freeEntry(gpa, sym_index); } fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { @@ -2256,14 +2156,14 @@ pub fn createAtom(self: *Elf) !Atom.Index { const gpa = self.base.allocator; const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len)); const atom = try self.atoms.addOne(gpa); - const local_sym_index = try self.allocateLocalSymbol(); - try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index); + const sym_index = try self.allocateSymbol(); + try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); atom.* = .{ - .local_sym_index = local_sym_index, + .sym_index = sym_index, .prev_index = null, .next_index = null, }; - log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index }); + log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index }); return atom_index; } @@ -2389,22 +2289,20 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme return vaddr; } -pub fn allocateLocalSymbol(self: *Elf) !u32 { - try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); - +pub fn allocateSymbol(self: *Elf) !u32 { + try self.locals.ensureUnusedCapacity(self.base.allocator, 1); const index = blk: { - if (self.local_symbol_free_list.popOrNull()) |index| { + if (self.locals_free_list.popOrNull()) |index| { log.debug(" (reusing symbol index {d})", .{index}); break :blk index; } else { - log.debug(" (allocating symbol index {d})", .{self.local_symbols.items.len}); - const index = @as(u32, @intCast(self.local_symbols.items.len)); - _ = self.local_symbols.addOneAssumeCapacity(); + log.debug(" (allocating symbol index {d})", .{self.locals.items.len}); + const index = @as(u32, @intCast(self.locals.items.len)); + _ = self.locals.addOneAssumeCapacity(); break :blk index; } }; - - self.local_symbols.items[index] = .{ + self.locals.items[index] = .{ .st_name = 0, .st_info = 0, .st_other = 0, @@ -2412,7 +2310,23 @@ pub fn allocateLocalSymbol(self: *Elf) !u32 { .st_value = 0, .st_size = 0, }; + return index; +} +fn allocateGlobal(self: *Elf) !u32 { + try self.globals.ensureUnusedCapacity(self.base.allocator, 1); + const index = blk: { + if (self.globals_free_list.popOrNull()) |index| { + log.debug(" (reusing global index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating symbol index {d})", .{self.globals.items.len}); + const index = @as(u32, @intCast(self.globals.items.len)); + _ = self.globals.addOneAssumeCapacity(); + break :blk index; + } + }; + self.globals.items[index] = 0; return index; } @@ -2896,8 +2810,6 @@ pub fn updateDeclExports( const decl_metadata = self.decls.getPtr(decl_index).?; const shdr_index = decl_metadata.shdr; - try self.global_symbols.ensureUnusedCapacity(gpa, exports.len); - for (exports) |exp| { const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); if (exp.opts.section.unwrap()) |section_name| { @@ -2905,7 +2817,7 @@ pub fn updateDeclExports( try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); mod.failed_exports.putAssumeCapacityNoClobber( exp, - try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(mod), "Unimplemented: ExportOptions.section", .{}), + try Module.ErrorMsg.create(gpa, decl.srcLoc(mod), "Unimplemented: ExportOptions.section", .{}), ); continue; } @@ -2924,37 +2836,30 @@ pub fn updateDeclExports( try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); mod.failed_exports.putAssumeCapacityNoClobber( exp, - try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(mod), "Unimplemented: GlobalLinkage.LinkOnce", .{}), + try Module.ErrorMsg.create(gpa, decl.srcLoc(mod), "Unimplemented: GlobalLinkage.LinkOnce", .{}), ); continue; }, }; const stt_bits: u8 = @as(u4, @truncate(decl_sym.st_info)); - if (decl_metadata.getExport(self, exp_name)) |i| { - const sym = &self.global_symbols.items[i]; - sym.* = .{ - .st_name = try self.strtab.insert(gpa, exp_name), - .st_info = (stb_bits << 4) | stt_bits, - .st_other = 0, - .st_shndx = shdr_index, - .st_value = decl_sym.st_value, - .st_size = decl_sym.st_size, - }; - } else { - const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { - _ = self.global_symbols.addOneAssumeCapacity(); - break :blk self.global_symbols.items.len - 1; - }; - try decl_metadata.exports.append(gpa, @as(u32, @intCast(i))); - self.global_symbols.items[i] = .{ - .st_name = try self.strtab.insert(gpa, exp_name), - .st_info = (stb_bits << 4) | stt_bits, - .st_other = 0, - .st_shndx = shdr_index, - .st_value = decl_sym.st_value, - .st_size = decl_sym.st_size, - }; - } + + const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: { + const sym_index = try self.allocateSymbol(); + try decl_metadata.exports.append(gpa, sym_index); + break :blk sym_index; + }; + const sym = self.getSymbolPtr(sym_index); + sym.* = .{ + .st_name = try self.strtab.insert(gpa, exp_name), + .st_info = (stb_bits << 4) | stt_bits, + .st_other = 0, + .st_shndx = shdr_index, + .st_value = decl_sym.st_value, + .st_size = decl_sym.st_size, + }; + const sym_name = self.getSymbolName(sym_index); + const gop = try self.getOrPutGlobalPtr(sym_name); + gop.value_ptr.* = sym_index; } } @@ -2981,10 +2886,20 @@ pub fn deleteDeclExport( ) void { if (self.llvm_object) |_| return; const metadata = self.decls.getPtr(decl_index) orelse return; + const gpa = self.base.allocator; const mod = self.base.options.module.?; - const sym_index = metadata.getExportPtr(self, mod.intern_pool.stringToSlice(name)) orelse return; - self.global_symbol_free_list.append(self.base.allocator, sym_index.*) catch {}; - self.global_symbols.items[sym_index.*].st_info = 0; + const exp_name = mod.intern_pool.stringToSlice(name); + const sym_index = metadata.getExportPtr(self, exp_name) orelse return; + const sym = self.getSymbolPtr(sym_index.*); + log.debug("deleting export '{s}'", .{exp_name}); + sym.* = null_sym; + self.locals_free_list.append(gpa, sym_index.*) catch {}; + + if (self.resolver.fetchRemove(exp_name)) |entry| { + self.globals_free_list.append(gpa, entry.value) catch {}; + self.globals.items[entry.value] = 0; + } + sym_index.* = 0; } @@ -3110,10 +3025,10 @@ fn writeSymbols(self: *Elf) !void { }; const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; - shdr.sh_info = @intCast(self.local_symbols.items.len); + shdr.sh_info = @intCast(self.locals.items.len); self.markDirty(self.symtab_section_index.?, null); - const nsyms = self.local_symbols.items.len + self.global_symbols.items.len; + const nsyms = self.locals.items.len + self.globals.items.len; const needed_size = nsyms * sym_size; try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true); @@ -3124,15 +3039,15 @@ fn writeSymbols(self: *Elf) !void { const buf = try gpa.alloc(elf.Elf32_Sym, nsyms); defer gpa.free(buf); - for (buf[0..self.local_symbols.items.len], self.local_symbols.items) |*sym, local| { + for (buf[0..self.locals.items.len], self.locals.items) |*sym, local| { elf32SymFromSym(local, sym); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Sym, sym); } } - for (buf[self.local_symbols.items.len..], self.global_symbols.items) |*sym, global| { - elf32SymFromSym(global, sym); + for (buf[self.locals.items.len..], self.globals.items) |*sym, global| { + elf32SymFromSym(self.getSymbol(global), sym); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Sym, sym); } @@ -3142,15 +3057,15 @@ fn writeSymbols(self: *Elf) !void { .p64 => { const buf = try gpa.alloc(elf.Elf64_Sym, nsyms); defer gpa.free(buf); - for (buf[0..self.local_symbols.items.len], self.local_symbols.items) |*sym, local| { + for (buf[0..self.locals.items.len], self.locals.items) |*sym, local| { sym.* = local; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Sym, sym); } } - for (buf[self.local_symbols.items.len..], self.global_symbols.items) |*sym, global| { - sym.* = global; + for (buf[self.locals.items.len..], self.globals.items) |*sym, global| { + sym.* = self.getSymbol(global); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Sym, sym); } @@ -3450,11 +3365,12 @@ const CsuObjects = struct { fn logSymtab(self: Elf) void { log.debug("locals:", .{}); - for (self.local_symbols.items, 0..) |sym, id| { + for (self.locals.items, 0..) |sym, id| { log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); } log.debug("globals:", .{}); - for (self.global_symbols.items, 0..) |sym, id| { + for (self.globals.items, 0..) |global, id| { + const sym = self.getSymbol(global); log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); } } @@ -3471,24 +3387,61 @@ pub fn getProgramHeaderPtr(self: *Elf, shdr_index: u16) *elf.Elf64_Phdr { /// Returns pointer-to-symbol described at sym_index. pub fn getSymbolPtr(self: *Elf, sym_index: u32) *elf.Elf64_Sym { - return &self.local_symbols.items[sym_index]; + return &self.locals.items[sym_index]; } /// Returns symbol at sym_index. pub fn getSymbol(self: *const Elf, sym_index: u32) elf.Elf64_Sym { - return self.local_symbols.items[sym_index]; + return self.locals.items[sym_index]; } /// Returns name of the symbol at sym_index. pub fn getSymbolName(self: *const Elf, sym_index: u32) []const u8 { - const sym = self.local_symbols.items[sym_index]; + const sym = self.locals.items[sym_index]; return self.strtab.get(sym.st_name).?; } -/// Returns name of the global symbol at index. -pub fn getGlobalName(self: *const Elf, index: u32) []const u8 { - const sym = self.global_symbols.items[index]; - return self.strtab.get(sym.st_name).?; +/// Returns pointer to the global entry for `name` if one exists. +pub fn getGlobalPtr(self: *Elf, name: []const u8) ?*u32 { + const global_index = self.resolver.get(name) orelse return null; + return &self.globals.items[global_index]; +} + +/// Returns the global entry for `name` if one exists. +pub fn getGlobal(self: *const Elf, name: []const u8) ?u32 { + const global_index = self.resolver.get(name) orelse return null; + return self.globals.items[global_index]; +} + +/// Returns the index of the global entry for `name` if one exists. +pub fn getGlobalIndex(self: *const Elf, name: []const u8) ?u32 { + return self.resolver.get(name); +} + +/// Returns global entry at `index`. +pub fn getGlobalByIndex(self: *const Elf, index: u32) u32 { + assert(index < self.globals.items.len); + return self.globals.items[index]; +} + +const GetOrPutGlobalPtrResult = struct { + found_existing: bool, + value_ptr: *u32, +}; + +/// Return pointer to the global entry for `name` if one exists. +/// Puts a new global entry for `name` if one doesn't exist, and +/// returns a pointer to it. +pub fn getOrPutGlobalPtr(self: *Elf, name: []const u8) !GetOrPutGlobalPtrResult { + if (self.getGlobalPtr(name)) |ptr| { + return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; + } + const gpa = self.base.allocator; + const global_index = try self.allocateGlobal(); + const global_name = try gpa.dupe(u8, name); + _ = try self.resolver.put(gpa, global_name, global_index); + const ptr = &self.globals.items[global_index]; + return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; } pub fn getAtom(self: *const Elf, atom_index: Atom.Index) Atom { @@ -3506,3 +3459,108 @@ pub fn getAtomPtr(self: *Elf, atom_index: Atom.Index) *Atom { pub fn getAtomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { return self.atom_by_index_table.get(sym_index); } + +pub const null_sym = elf.Elf64_Sym{ + .st_name = 0, + .st_info = 0, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, +}; + +const default_entry_addr = 0x8000000; + +pub const base_tag: File.Tag = .elf; + +const Section = struct { + shdr: elf.Elf64_Shdr, + phdr_index: u16, + + /// Index of the last allocated atom in this section. + last_atom_index: ?Atom.Index = null, + + /// A list of atoms that have surplus capacity. This list can have false + /// positives, as functions grow and shrink over time, only sometimes being added + /// or removed from the freelist. + /// + /// An atom has surplus capacity when its overcapacity value is greater than + /// padToIdeal(minimum_atom_size). That is, when it has so + /// much extra capacity, that we could fit a small new symbol in it, itself with + /// ideal_capacity or more. + /// + /// Ideal capacity is defined by size + (size / ideal_factor) + /// + /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that + /// overcapacity can be negative. A simple way to have negative overcapacity is to + /// allocate a fresh text block, which will have ideal capacity, and then grow it + /// by 1 byte. It will then have -1 overcapacity. + free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, +}; + +const LazySymbolMetadata = struct { + const State = enum { unused, pending_flush, flushed }; + text_atom: Atom.Index = undefined, + rodata_atom: Atom.Index = undefined, + text_state: State = .unused, + rodata_state: State = .unused, +}; + +const DeclMetadata = struct { + atom: Atom.Index, + shdr: u16, + /// A list of all exports aliases of this Decl. + exports: std.ArrayListUnmanaged(u32) = .{}, + + fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 { + for (m.exports.items) |exp| { + if (mem.eql(u8, name, elf_file.getSymbolName(exp))) return exp; + } + return null; + } + + fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { + for (m.exports.items) |*exp| { + if (mem.eql(u8, name, elf_file.getSymbolName(exp.*))) return exp; + } + return null; + } +}; + +const Elf = @This(); + +const std = @import("std"); +const build_options = @import("build_options"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const elf = std.elf; +const fs = std.fs; +const log = std.log.scoped(.link); +const math = std.math; +const mem = std.mem; + +const codegen = @import("../codegen.zig"); +const glibc = @import("../glibc.zig"); +const link = @import("../link.zig"); +const lldMain = @import("../main.zig").lldMain; +const musl = @import("../musl.zig"); +const target_util = @import("../target.zig"); +const trace = @import("../tracy.zig").trace; + +const Air = @import("../Air.zig"); +const Allocator = std.mem.Allocator; +pub const Atom = @import("Elf/Atom.zig"); +const Cache = std.Build.Cache; +const Compilation = @import("../Compilation.zig"); +const Dwarf = @import("Dwarf.zig"); +const File = link.File; +const Liveness = @import("../Liveness.zig"); +const LlvmObject = @import("../codegen/llvm.zig").Object; +const Module = @import("../Module.zig"); +const InternPool = @import("../InternPool.zig"); +const Package = @import("../Package.zig"); +const StringTable = @import("strtab.zig").StringTable; +const TableSection = @import("table_section.zig").TableSection; +const Type = @import("../type.zig").Type; +const TypedValue = @import("../TypedValue.zig"); +const Value = @import("../value.zig").Value; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index b437be3282..bfdc2a81ca 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -1,18 +1,10 @@ -const Atom = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const elf = std.elf; - -const Elf = @import("../Elf.zig"); - /// Each decl always gets a local symbol with the fully qualified name. /// The vaddr and size are found here directly. /// The file offset is found by computing the vaddr offset from the section vaddr /// the symbol references, and adding that to the file offset of the section. /// If this field is 0, it means the codegen size = 0 and there is no symbol or /// offset table entry. -local_sym_index: u32, +sym_index: u32, /// Points to the previous and next neighbors, based on the `text_offset`. /// This can be used to find, for example, the capacity of this `TextBlock`. @@ -29,8 +21,8 @@ pub const Reloc = struct { }; pub fn getSymbolIndex(self: Atom) ?u32 { - if (self.local_sym_index == 0) return null; - return self.local_sym_index; + if (self.sym_index == 0) return null; + return self.sym_index; } pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym { @@ -106,3 +98,11 @@ pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { var removed_relocs = elf_file.relocs.fetchRemove(atom_index); if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); } + +const Atom = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const elf = std.elf; + +const Elf = @import("../Elf.zig"); -- cgit v1.2.3 From bc37c95e56b00f011a2cfb0b0795f234829c59da Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 4 Sep 2023 11:23:19 +0200 Subject: elf: simplify accessors to symbols, atoms, etc --- src/arch/aarch64/CodeGen.zig | 2 +- src/arch/arm/CodeGen.zig | 2 +- src/arch/riscv64/CodeGen.zig | 2 +- src/arch/sparc64/CodeGen.zig | 2 +- src/arch/x86_64/CodeGen.zig | 4 +- src/codegen.zig | 4 +- src/link/Elf.zig | 214 +++++++++++++++++++------------------------ src/link/Elf/Atom.zig | 38 ++++---- 8 files changed, 118 insertions(+), 150 deletions(-) (limited to 'src') diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0f088fa867..15f7397721 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4315,7 +4315,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.getAtom(atom_index); + const atom = elf_file.atom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 135f118731..234a281623 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4295,7 +4295,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.getAtom(atom_index); + const atom = elf_file.atom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index e43778510b..bead5c6d40 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1748,7 +1748,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier switch (mod.intern_pool.indexToKey(func_value.ip_index)) { .func => |func| { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.getAtom(atom_index); + const atom = elf_file.atom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 6d575a9d45..3a3f69ce37 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1350,7 +1350,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .func => |func| { const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.getAtom(atom_index); + const atom = elf_file.atom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); break :blk @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); } else unreachable; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index aec28b992c..187db5f20f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8150,7 +8150,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier }) |owner_decl| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(owner_decl); - const atom = elf_file.getAtom(atom_index); + const atom = elf_file.atom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = atom.getOffsetTableAddress(elf_file); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ @@ -10217,7 +10217,7 @@ fn genLazySymbolRef( if (self.bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.getAtom(atom_index); + const atom = elf_file.atom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); const got_addr = atom.getOffsetTableAddress(elf_file); const got_mem = diff --git a/src/codegen.zig b/src/codegen.zig index 3a802f4d86..75f42ff876 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -855,7 +855,7 @@ fn genDeclRef( if (bin_file.cast(link.File.Elf)) |elf_file| { const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.getAtom(atom_index); + const atom = elf_file.atom(atom_index); _ = try atom.getOrCreateOffsetTableEntry(elf_file); return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { @@ -892,7 +892,7 @@ fn genUnnamedConst( return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)}); }; if (bin_file.cast(link.File.Elf)) |elf_file| { - return GenResult.mcv(.{ .memory = elf_file.getSymbol(local_sym_index).st_value }); + return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).st_value }); } else if (bin_file.cast(link.File.MachO)) |_| { return GenResult.mcv(.{ .load_direct = local_sym_index }); } else if (bin_file.cast(link.File.Coff)) |_| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 66328528a4..bd2ddfb100 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -280,10 +280,10 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File. assert(self.llvm_object == null); const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); - const this_atom = self.getAtom(this_atom_index); - const target = this_atom.getSymbolIndex().?; - const vaddr = this_atom.getSymbol(self).st_value; - const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?; + const this_atom = self.atom(this_atom_index); + const target = this_atom.symbolIndex().?; + const vaddr = this_atom.symbol(self).st_value; + const atom_index = self.atomIndexForSymbol(reloc_info.parent_atom_index).?; try Atom.addRelocation(self, atom_index, .{ .target = target, .offset = reloc_info.offset, @@ -849,8 +849,8 @@ fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { // Must move the entire section. const new_offset = self.findFreeSpace(needed_size, self.page_size); const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: { - const last = self.getAtom(last_atom_index); - const sym = last.getSymbol(self); + const last = self.atom(last_atom_index); + const sym = last.symbol(self); break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; } else if (shdr_index == self.got_section_index.?) blk: { break :blk shdr.sh_size; @@ -1010,8 +1010,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node while (it.next()) |entry| { const atom_index = entry.key_ptr.*; const relocs = entry.value_ptr.*; - const atom = self.getAtom(atom_index); - const source_sym = atom.getSymbol(self); + const atom_ptr = self.atom(atom_index); + const source_sym = atom_ptr.symbol(self); const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx]; log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)}); @@ -1471,9 +1471,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append(entry); } - for (self.base.options.force_undefined_symbols.keys()) |symbol| { + for (self.base.options.force_undefined_symbols.keys()) |sym| { try argv.append("-u"); - try argv.append(symbol); + try argv.append(sym); } switch (self.base.options.hash_style) { @@ -2071,13 +2071,13 @@ fn writeElfHeader(self: *Elf) !void { } fn freeAtom(self: *Elf, atom_index: Atom.Index) void { - const atom = self.getAtom(atom_index); - log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) }); + const atom_ptr = self.atom(atom_index); + log.debug("freeAtom {d} ({s})", .{ atom_index, atom_ptr.name(self) }); Atom.freeRelocations(self, atom_index); const gpa = self.base.allocator; - const shndx = atom.getSymbol(self).st_shndx; + const shndx = atom_ptr.symbol(self).st_shndx; const free_list = &self.sections.items(.free_list)[shndx]; var already_have_free_list_node = false; { @@ -2088,7 +2088,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { _ = free_list.swapRemove(i); continue; } - if (free_list.items[i] == atom.prev_index) { + if (free_list.items[i] == atom_ptr.prev_index) { already_have_free_list_node = true; } i += 1; @@ -2098,7 +2098,7 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx]; if (maybe_last_atom_index.*) |last_atom_index| { if (last_atom_index == atom_index) { - if (atom.prev_index) |prev_index| { + if (atom_ptr.prev_index) |prev_index| { // TODO shrink the section size here maybe_last_atom_index.* = prev_index; } else { @@ -2107,9 +2107,9 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { } } - if (atom.prev_index) |prev_index| { - const prev = self.getAtomPtr(prev_index); - prev.next_index = atom.next_index; + if (atom_ptr.prev_index) |prev_index| { + const prev = self.atom(prev_index); + prev.next_index = atom_ptr.next_index; if (!already_have_free_list_node and prev.*.freeListEligible(self)) { // The free list is heuristics, it doesn't have to be perfect, so we can @@ -2117,23 +2117,23 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { free_list.append(gpa, prev_index) catch {}; } } else { - self.getAtomPtr(atom_index).prev_index = null; + atom_ptr.prev_index = null; } - if (atom.next_index) |next_index| { - self.getAtomPtr(next_index).prev_index = atom.prev_index; + if (atom_ptr.next_index) |next_index| { + self.atom(next_index).prev_index = atom_ptr.prev_index; } else { - self.getAtomPtr(atom_index).next_index = null; + atom_ptr.next_index = null; } // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. - const sym_index = atom.getSymbolIndex().?; + const sym_index = atom_ptr.symbolIndex().?; log.debug("adding %{d} to local symbols free list", .{sym_index}); self.locals_free_list.append(gpa, sym_index) catch {}; self.locals.items[sym_index] = null_sym; _ = self.atom_by_index_table.remove(sym_index); - self.getAtomPtr(atom_index).sym_index = 0; + atom_ptr.sym_index = 0; self.got_table.freeEntry(gpa, sym_index); } @@ -2144,10 +2144,10 @@ fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { } fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); + const atom_ptr = self.atom(atom_index); + const sym = atom_ptr.symbol(self); const align_ok = mem.alignBackward(u64, sym.st_value, alignment) == sym.st_value; - const need_realloc = !align_ok or new_block_size > atom.capacity(self); + const need_realloc = !align_ok or new_block_size > atom_ptr.capacity(self); if (!need_realloc) return sym.st_value; return self.allocateAtom(atom_index, new_block_size, alignment); } @@ -2155,10 +2155,10 @@ fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: pub fn createAtom(self: *Elf) !Atom.Index { const gpa = self.base.allocator; const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len)); - const atom = try self.atoms.addOne(gpa); + const atom_ptr = try self.atoms.addOne(gpa); const sym_index = try self.allocateSymbol(); try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - atom.* = .{ + atom_ptr.* = .{ .sym_index = sym_index, .prev_index = null, .next_index = null, @@ -2168,8 +2168,7 @@ pub fn createAtom(self: *Elf) !Atom.Index { } fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { - const atom = self.getAtom(atom_index); - const sym = atom.getSymbol(self); + const sym = self.atom(atom_index).symbol(self); const phdr_index = self.sections.items(.phdr_index)[sym.st_shndx]; const phdr = &self.program_headers.items[phdr_index]; const shdr = &self.sections.items(.shdr)[sym.st_shndx]; @@ -2191,10 +2190,10 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len; while (i < free_list.items.len) { const big_atom_index = free_list.items[i]; - const big_atom = self.getAtom(big_atom_index); + const big_atom = self.atom(big_atom_index); // We now have a pointer to a live atom that has too much capacity. // Is it enough that we could fit this new atom? - const big_atom_sym = big_atom.getSymbol(self); + const big_atom_sym = big_atom.symbol(self); const capacity = big_atom.capacity(self); const ideal_capacity = padToIdeal(capacity); const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity; @@ -2225,8 +2224,8 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme } break :blk new_start_vaddr; } else if (maybe_last_atom_index.*) |last_index| { - const last = self.getAtom(last_index); - const last_sym = last.getSymbol(self); + const last = self.atom(last_index); + const last_sym = last.symbol(self); const ideal_capacity = padToIdeal(last_sym.st_size); const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity; const new_start_vaddr = mem.alignForward(u64, ideal_capacity_end_vaddr, alignment); @@ -2239,7 +2238,7 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme }; const expand_section = if (atom_placement) |placement_index| - self.getAtom(placement_index).next_index == null + self.atom(placement_index).next_index == null else true; if (expand_section) { @@ -2263,23 +2262,22 @@ fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignme // This function can also reallocate an atom. // In this case we need to "unplug" it from its previous location before // plugging it in to its new location. - if (atom.prev_index) |prev_index| { - const prev = self.getAtomPtr(prev_index); - prev.next_index = atom.next_index; + const atom_ptr = self.atom(atom_index); + if (atom_ptr.prev_index) |prev_index| { + const prev = self.atom(prev_index); + prev.next_index = atom_ptr.next_index; } - if (atom.next_index) |next_index| { - const next = self.getAtomPtr(next_index); - next.prev_index = atom.prev_index; + if (atom_ptr.next_index) |next_index| { + const next = self.atom(next_index); + next.prev_index = atom_ptr.prev_index; } if (atom_placement) |big_atom_index| { - const big_atom = self.getAtomPtr(big_atom_index); - const atom_ptr = self.getAtomPtr(atom_index); + const big_atom = self.atom(big_atom_index); atom_ptr.prev_index = big_atom_index; atom_ptr.next_index = big_atom.next_index; big_atom.next_index = atom_index; } else { - const atom_ptr = self.getAtomPtr(atom_index); atom_ptr.prev_index = null; atom_ptr.next_index = null; } @@ -2320,7 +2318,7 @@ fn allocateGlobal(self: *Elf) !u32 { log.debug(" (reusing global index {d})", .{index}); break :blk index; } else { - log.debug(" (allocating symbol index {d})", .{self.globals.items.len}); + log.debug(" (allocating global index {d})", .{self.globals.items.len}); const index = @as(u32, @intCast(self.globals.items.len)); _ = self.globals.addOneAssumeCapacity(); break :blk index; @@ -2332,8 +2330,8 @@ fn allocateGlobal(self: *Elf) !u32 { fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; - for (unnamed_consts.items) |atom| { - self.freeAtom(atom); + for (unnamed_consts.items) |atom_index| { + self.freeAtom(atom_index); } unnamed_consts.clearAndFree(self.base.allocator); } @@ -2373,13 +2371,13 @@ pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Inde .flushed => {}, } metadata.state.* = .pending_flush; - const atom = metadata.atom.*; + const atom_index = metadata.atom.*; // anyerror needs to be deferred until flushModule - if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) { + if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom_index, switch (sym.kind) { .code => self.text_section_index.?, .const_data => self.rodata_section_index.?, }); - return atom; + return atom_index; } pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index { @@ -2432,18 +2430,18 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s const decl_metadata = self.decls.get(decl_index).?; const atom_index = decl_metadata.atom; - const atom = self.getAtom(atom_index); - const local_sym_index = atom.getSymbolIndex().?; + const atom_ptr = self.atom(atom_index); + const local_sym_index = atom_ptr.symbolIndex().?; + const local_sym = atom_ptr.symbol(self); const shdr_index = decl_metadata.shdr; - if (atom.getSymbol(self).st_size != 0 and self.base.child_pid == null) { - const local_sym = atom.getSymbolPtr(self); + if (atom_ptr.symbol(self).st_size != 0 and self.base.child_pid == null) { local_sym.st_name = try self.strtab.insert(gpa, decl_name); local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; local_sym.st_other = 0; local_sym.st_shndx = shdr_index; - const capacity = atom.capacity(self); + const capacity = atom_ptr.capacity(self); const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); @@ -2463,7 +2461,6 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s } local_sym.st_size = code.len; } else { - const local_sym = atom.getSymbolPtr(self); local_sym.* = .{ .st_name = try self.strtab.insert(gpa, decl_name), .st_info = (elf.STB_LOCAL << 4) | stt_bits, @@ -2479,11 +2476,10 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s local_sym.st_value = vaddr; local_sym.st_size = code.len; - const got_entry_index = try atom.getOrCreateOffsetTableEntry(self); + const got_entry_index = try atom_ptr.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); } - const local_sym = atom.getSymbolPtr(self); const phdr_index = self.sections.items(.phdr_index)[shdr_index]; const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; @@ -2594,7 +2590,6 @@ pub fn updateDecl( const atom_index = try self.getOrCreateAtomForDecl(decl_index); Atom.freeRelocations(self, atom_index); - const atom = self.getAtom(atom_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2611,14 +2606,14 @@ pub fn updateDecl( }, &code_buffer, .{ .dwarf = ds, }, .{ - .parent_atom_index = atom.getSymbolIndex().?, + .parent_atom_index = self.atom(atom_index).symbolIndex().?, }) else try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .none, .{ - .parent_atom_index = atom.getSymbolIndex().?, + .parent_atom_index = self.atom(atom_index).symbolIndex().?, }); const code = switch (res) { @@ -2669,8 +2664,8 @@ fn updateLazySymbolAtom( }; const name = self.strtab.get(name_str_index).?; - const atom = self.getAtom(atom_index); - const local_sym_index = atom.getSymbolIndex().?; + const atom_ptr = self.atom(atom_index); + const local_sym_index = atom_ptr.symbolIndex().?; const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| mod.declPtr(owner_decl).srcLoc(mod) @@ -2698,7 +2693,7 @@ fn updateLazySymbolAtom( }; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const local_sym = atom.getSymbolPtr(self); + const local_sym = atom_ptr.symbol(self); local_sym.* = .{ .st_name = name_str_index, .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT, @@ -2714,7 +2709,7 @@ fn updateLazySymbolAtom( local_sym.st_value = vaddr; local_sym.st_size = code.len; - const got_entry_index = try atom.getOrCreateOffsetTableEntry(self); + const got_entry_index = try atom_ptr.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr; @@ -2750,7 +2745,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{ .none = {}, }, .{ - .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, + .parent_atom_index = self.atom(atom_index).symbolIndex().?, }); const code = switch (res) { .ok => code_buffer.items, @@ -2765,7 +2760,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const required_alignment = typed_value.ty.abiAlignment(mod); const shdr_index = self.rodata_section_index.?; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const local_sym = self.getAtom(atom_index).getSymbolPtr(self); + const local_sym = self.atom(atom_index).symbol(self); local_sym.st_name = name_str_index; local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT; local_sym.st_other = 0; @@ -2782,7 +2777,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); - return self.getAtom(atom_index).getSymbolIndex().?; + return self.atom(atom_index).symbolIndex().?; } pub fn updateDeclExports( @@ -2805,8 +2800,8 @@ pub fn updateDeclExports( const decl = mod.declPtr(decl_index); const atom_index = try self.getOrCreateAtomForDecl(decl_index); - const atom = self.getAtom(atom_index); - const decl_sym = atom.getSymbol(self); + const atom_ptr = self.atom(atom_index); + const decl_sym = atom_ptr.symbol(self); const decl_metadata = self.decls.getPtr(decl_index).?; const shdr_index = decl_metadata.shdr; @@ -2843,12 +2838,12 @@ pub fn updateDeclExports( }; const stt_bits: u8 = @as(u4, @truncate(decl_sym.st_info)); - const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: { + const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { const sym_index = try self.allocateSymbol(); try decl_metadata.exports.append(gpa, sym_index); break :blk sym_index; }; - const sym = self.getSymbolPtr(sym_index); + const sym = self.symbol(sym_index); sym.* = .{ .st_name = try self.strtab.insert(gpa, exp_name), .st_info = (stb_bits << 4) | stt_bits, @@ -2857,8 +2852,8 @@ pub fn updateDeclExports( .st_value = decl_sym.st_value, .st_size = decl_sym.st_size, }; - const sym_name = self.getSymbolName(sym_index); - const gop = try self.getOrPutGlobalPtr(sym_name); + const sym_name = self.symbolName(sym_index); + const gop = try self.getOrPutGlobal(sym_name); gop.value_ptr.* = sym_index; } } @@ -2889,8 +2884,8 @@ pub fn deleteDeclExport( const gpa = self.base.allocator; const mod = self.base.options.module.?; const exp_name = mod.intern_pool.stringToSlice(name); - const sym_index = metadata.getExportPtr(self, exp_name) orelse return; - const sym = self.getSymbolPtr(sym_index.*); + const sym_index = metadata.@"export"(self, exp_name) orelse return; + const sym = self.symbol(sym_index.*); log.debug("deleting export '{s}'", .{exp_name}); sym.* = null_sym; self.locals_free_list.append(gpa, sym_index.*) catch {}; @@ -2960,7 +2955,7 @@ fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void const phdr = &self.program_headers.items[self.phdr_got_index.?]; const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; const got_entry = self.got_table.entries.items[index]; - const got_value = self.getSymbol(got_entry).st_value; + const got_value = self.symbol(got_entry).st_value; switch (entry_size) { 2 => { var buf: [2]u8 = undefined; @@ -3046,8 +3041,8 @@ fn writeSymbols(self: *Elf) !void { } } - for (buf[self.locals.items.len..], self.globals.items) |*sym, global| { - elf32SymFromSym(self.getSymbol(global), sym); + for (buf[self.locals.items.len..], self.globals.items) |*sym, glob| { + elf32SymFromSym(self.symbol(glob).*, sym); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Sym, sym); } @@ -3064,8 +3059,8 @@ fn writeSymbols(self: *Elf) !void { } } - for (buf[self.locals.items.len..], self.globals.items) |*sym, global| { - sym.* = self.getSymbol(global); + for (buf[self.locals.items.len..], self.globals.items) |*sym, glob| { + sym.* = self.symbol(glob).*; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Sym, sym); } @@ -3369,8 +3364,8 @@ fn logSymtab(self: Elf) void { log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); } log.debug("globals:", .{}); - for (self.globals.items, 0..) |global, id| { - const sym = self.getSymbol(global); + for (self.globals.items, 0..) |glob, id| { + const sym = self.symbol(glob); log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); } } @@ -3386,45 +3381,34 @@ pub fn getProgramHeaderPtr(self: *Elf, shdr_index: u16) *elf.Elf64_Phdr { } /// Returns pointer-to-symbol described at sym_index. -pub fn getSymbolPtr(self: *Elf, sym_index: u32) *elf.Elf64_Sym { +pub fn symbol(self: *const Elf, sym_index: u32) *elf.Elf64_Sym { return &self.locals.items[sym_index]; } -/// Returns symbol at sym_index. -pub fn getSymbol(self: *const Elf, sym_index: u32) elf.Elf64_Sym { - return self.locals.items[sym_index]; -} - /// Returns name of the symbol at sym_index. -pub fn getSymbolName(self: *const Elf, sym_index: u32) []const u8 { +pub fn symbolName(self: *const Elf, sym_index: u32) []const u8 { const sym = self.locals.items[sym_index]; return self.strtab.get(sym.st_name).?; } /// Returns pointer to the global entry for `name` if one exists. -pub fn getGlobalPtr(self: *Elf, name: []const u8) ?*u32 { +pub fn global(self: *const Elf, name: []const u8) ?*u32 { const global_index = self.resolver.get(name) orelse return null; return &self.globals.items[global_index]; } -/// Returns the global entry for `name` if one exists. -pub fn getGlobal(self: *const Elf, name: []const u8) ?u32 { - const global_index = self.resolver.get(name) orelse return null; - return self.globals.items[global_index]; -} - /// Returns the index of the global entry for `name` if one exists. -pub fn getGlobalIndex(self: *const Elf, name: []const u8) ?u32 { +pub fn globalIndex(self: *const Elf, name: []const u8) ?u32 { return self.resolver.get(name); } /// Returns global entry at `index`. -pub fn getGlobalByIndex(self: *const Elf, index: u32) u32 { +pub fn globalByIndex(self: *const Elf, index: u32) u32 { assert(index < self.globals.items.len); return self.globals.items[index]; } -const GetOrPutGlobalPtrResult = struct { +const GetOrPutGlobalResult = struct { found_existing: bool, value_ptr: *u32, }; @@ -3432,31 +3416,26 @@ const GetOrPutGlobalPtrResult = struct { /// Return pointer to the global entry for `name` if one exists. /// Puts a new global entry for `name` if one doesn't exist, and /// returns a pointer to it. -pub fn getOrPutGlobalPtr(self: *Elf, name: []const u8) !GetOrPutGlobalPtrResult { - if (self.getGlobalPtr(name)) |ptr| { - return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr }; +pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { + if (self.global(name)) |ptr| { + return GetOrPutGlobalResult{ .found_existing = true, .value_ptr = ptr }; } const gpa = self.base.allocator; const global_index = try self.allocateGlobal(); const global_name = try gpa.dupe(u8, name); _ = try self.resolver.put(gpa, global_name, global_index); const ptr = &self.globals.items[global_index]; - return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; + return GetOrPutGlobalResult{ .found_existing = false, .value_ptr = ptr }; } -pub fn getAtom(self: *const Elf, atom_index: Atom.Index) Atom { - assert(atom_index < self.atoms.items.len); - return self.atoms.items[atom_index]; -} - -pub fn getAtomPtr(self: *Elf, atom_index: Atom.Index) *Atom { +pub fn atom(self: *Elf, atom_index: Atom.Index) *Atom { assert(atom_index < self.atoms.items.len); return &self.atoms.items[atom_index]; } /// Returns atom if there is an atom referenced by the symbol. /// Returns null on failure. -pub fn getAtomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { +pub fn atomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { return self.atom_by_index_table.get(sym_index); } @@ -3512,16 +3491,9 @@ const DeclMetadata = struct { /// A list of all exports aliases of this Decl. exports: std.ArrayListUnmanaged(u32) = .{}, - fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 { - for (m.exports.items) |exp| { - if (mem.eql(u8, name, elf_file.getSymbolName(exp))) return exp; - } - return null; - } - - fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { + fn @"export"(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?*u32 { for (m.exports.items) |*exp| { - if (mem.eql(u8, name, elf_file.getSymbolName(exp.*))) return exp; + if (mem.eql(u8, name, elf_file.symbolName(exp.*))) return exp; } return null; } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index bfdc2a81ca..7edb36f0a4 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -20,35 +20,31 @@ pub const Reloc = struct { prev_vaddr: u64, }; -pub fn getSymbolIndex(self: Atom) ?u32 { +pub fn symbolIndex(self: *const Atom) ?u32 { if (self.sym_index == 0) return null; return self.sym_index; } -pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym { - return elf_file.getSymbol(self.getSymbolIndex().?); +pub fn symbol(self: *const Atom, elf_file: *Elf) *elf.Elf64_Sym { + return elf_file.symbol(self.symbolIndex().?); } -pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym { - return elf_file.getSymbolPtr(self.getSymbolIndex().?); -} - -pub fn getName(self: Atom, elf_file: *const Elf) []const u8 { - return elf_file.getSymbolName(self.getSymbolIndex().?); +pub fn name(self: *const Atom, elf_file: *Elf) []const u8 { + return elf_file.symbolName(self.symbolIndex().?); } /// If entry already exists, returns index to it. /// Otherwise, creates a new entry in the Global Offset Table for this Atom. -pub fn getOrCreateOffsetTableEntry(self: Atom, elf_file: *Elf) !u32 { - const sym_index = self.getSymbolIndex().?; +pub fn getOrCreateOffsetTableEntry(self: *const Atom, elf_file: *Elf) !u32 { + const sym_index = self.symbolIndex().?; if (elf_file.got_table.lookup.get(sym_index)) |index| return index; const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index); elf_file.got_table_count_dirty = true; return index; } -pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 { - const sym_index = self.getSymbolIndex().?; +pub fn getOffsetTableAddress(self: *const Atom, elf_file: *Elf) u64 { + const sym_index = self.symbolIndex().?; const got_entry_index = elf_file.got_table.lookup.get(sym_index).?; const target = elf_file.base.options.target; const ptr_bits = target.ptrBitWidth(); @@ -60,11 +56,11 @@ pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 { /// Returns how much room there is to grow in virtual address space. /// File offset relocation happens transparently, so it is not included in /// this calculation. -pub fn capacity(self: Atom, elf_file: *const Elf) u64 { - const self_sym = self.getSymbol(elf_file); +pub fn capacity(self: *const Atom, elf_file: *Elf) u64 { + const self_sym = self.symbol(elf_file); if (self.next_index) |next_index| { - const next = elf_file.getAtom(next_index); - const next_sym = next.getSymbol(elf_file); + const next = elf_file.atom(next_index); + const next_sym = next.symbol(elf_file); return next_sym.st_value - self_sym.st_value; } else { // We are the last block. The capacity is limited only by virtual address space. @@ -72,12 +68,12 @@ pub fn capacity(self: Atom, elf_file: *const Elf) u64 { } } -pub fn freeListEligible(self: Atom, elf_file: *const Elf) bool { +pub fn freeListEligible(self: *const Atom, elf_file: *Elf) bool { // No need to keep a free list node for the last block. const next_index = self.next_index orelse return false; - const next = elf_file.getAtom(next_index); - const self_sym = self.getSymbol(elf_file); - const next_sym = next.getSymbol(elf_file); + const next = elf_file.atom(next_index); + const self_sym = self.symbol(elf_file); + const next_sym = next.symbol(elf_file); const cap = next_sym.st_value - self_sym.st_value; const ideal_cap = Elf.padToIdeal(self_sym.st_size); if (cap <= ideal_cap) return false; -- cgit v1.2.3 From 7d396110d63c7c15d9fe12dbbfc63f1ab0700df4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 4 Sep 2023 11:31:48 +0200 Subject: elf: remove dead code --- src/link/Elf.zig | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index bd2ddfb100..38855dff7b 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3370,16 +3370,6 @@ fn logSymtab(self: Elf) void { } } -pub fn getProgramHeader(self: *const Elf, shdr_index: u16) elf.Elf64_Phdr { - const index = self.sections.items(.phdr_index)[shdr_index]; - return self.program_headers.items[index]; -} - -pub fn getProgramHeaderPtr(self: *Elf, shdr_index: u16) *elf.Elf64_Phdr { - const index = self.sections.items(.phdr_index)[shdr_index]; - return &self.program_headers.items[index]; -} - /// Returns pointer-to-symbol described at sym_index. pub fn symbol(self: *const Elf, sym_index: u32) *elf.Elf64_Sym { return &self.locals.items[sym_index]; -- cgit v1.2.3 From 02451bdebfe2685c283f6955488d978047e4e9d8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 4 Sep 2023 22:41:50 +0200 Subject: elf: atom.index of 0 reserved for null atom --- src/link/Elf.zig | 19 +++++-------------- src/link/Elf/Atom.zig | 6 +++--- 2 files changed, 8 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 38855dff7b..fd4a997333 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -154,7 +154,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option // Index 0 is always a null symbol. try self.locals.append(allocator, null_sym); - + // Allocate atom index 0 to null atom + try self.atoms.append(allocator, .{}); // There must always be a null section in index 0 try self.sections.append(allocator, .{ .shdr = .{ @@ -212,6 +213,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { if (use_llvm) { self.llvm_object = try LlvmObject.create(gpa, options); } + return self; } @@ -2158,11 +2160,7 @@ pub fn createAtom(self: *Elf) !Atom.Index { const atom_ptr = try self.atoms.addOne(gpa); const sym_index = try self.allocateSymbol(); try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - atom_ptr.* = .{ - .sym_index = sym_index, - .prev_index = null, - .next_index = null, - }; + atom_ptr.* = .{ .sym_index = sym_index }; log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index }); return atom_index; } @@ -2300,14 +2298,7 @@ pub fn allocateSymbol(self: *Elf) !u32 { break :blk index; } }; - self.locals.items[index] = .{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }; + self.locals.items[index] = null_sym; return index; } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 7edb36f0a4..aa1d3920fb 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -4,12 +4,12 @@ /// the symbol references, and adding that to the file offset of the section. /// If this field is 0, it means the codegen size = 0 and there is no symbol or /// offset table entry. -sym_index: u32, +sym_index: u32 = 0, /// Points to the previous and next neighbors, based on the `text_offset`. /// This can be used to find, for example, the capacity of this `TextBlock`. -prev_index: ?Index, -next_index: ?Index, +prev_index: ?Index = null, +next_index: ?Index = null, pub const Index = u32; -- cgit v1.2.3 From d9fffd431a89ed4104bcc0b2165bfb9917cdd82b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 5 Sep 2023 15:55:00 +0200 Subject: elf: start porting abstraction of input file --- src/link/Elf.zig | 623 ++++++++++++++++++++++------------------- src/link/Elf/LinkerDefined.zig | 132 +++++++++ src/link/Elf/Symbol.zig | 337 ++++++++++++++++++++++ src/link/Elf/ZigModule.zig | 69 +++++ src/link/Elf/file.zig | 110 ++++++++ 5 files changed, 979 insertions(+), 292 deletions(-) create mode 100644 src/link/Elf/LinkerDefined.zig create mode 100644 src/link/Elf/Symbol.zig create mode 100644 src/link/Elf/ZigModule.zig create mode 100644 src/link/Elf/file.zig (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index fd4a997333..14bfa58748 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -6,6 +6,10 @@ ptr_width: PtrWidth, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. llvm_object: ?*LlvmObject = null, +files: std.MutliArrayList(File.Entry) = .{}, +zig_module_index: ?File.Index = null, +linker_defined_index: ?File.Index = null, + /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. sections: std.MultiArrayList(Section) = .{}, @@ -51,16 +55,12 @@ debug_line_section_index: ?u16 = null, shstrtab_section_index: ?u16 = null, strtab_section_index: ?u16 = null, -/// The same order as in the file. ELF requires global symbols to all be after the -/// local symbols, they cannot be mixed. So we must buffer all the global symbols and -/// write them at the end. These are only the local symbols. The length of this array -/// is the value used for sh_info in the .symtab section. -locals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -globals: std.ArrayListUnmanaged(u32) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +globals: std.ArrayListUnmanaged(Symbol.Index) = .{}, resolver: std.StringHashMapUnmanaged(u32) = .{}, unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, -locals_free_list: std.ArrayListUnmanaged(u32) = .{}, +symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, globals_free_list: std.ArrayListUnmanaged(u32) = .{}, got_table: TableSection(u32) = .{}, @@ -77,7 +77,7 @@ debug_aranges_section_dirty: bool = false, debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, -error_flags: File.ErrorFlags = File.ErrorFlags{}, +error_flags: link.File.ErrorFlags = link.File.ErrorFlags{}, /// Table of tracked LazySymbols. lazy_syms: LazySymbolTable = .{}, @@ -153,9 +153,11 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option self.shdr_table_dirty = true; // Index 0 is always a null symbol. - try self.locals.append(allocator, null_sym); + try self.symbols.append(allocator, .{}); // Allocate atom index 0 to null atom try self.atoms.append(allocator, .{}); + // Append null file at index 0 + try self.files.append(allocator, .null); // There must always be a null section in index 0 try self.sections.append(allocator, .{ .shdr = .{ @@ -222,6 +224,15 @@ pub fn deinit(self: *Elf) void { if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); + for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) { + .null => {}, + .zig_module => data.zig_module.deinit(gpa), + .linker_defined => data.linker_defined.deinit(gpa), + // .object => data.object.deinit(gpa), + // .shared_object => data.shared_object.deinit(gpa), + }; + self.files.deinit(gpa); + for (self.sections.items(.free_list)) |*free_list| { free_list.deinit(gpa); } @@ -230,10 +241,10 @@ pub fn deinit(self: *Elf) void { self.program_headers.deinit(gpa); self.shstrtab.deinit(gpa); self.strtab.deinit(gpa); - self.locals.deinit(gpa); + self.symbols.deinit(gpa); + self.symbols_free_list.deinit(gpa); self.globals.deinit(gpa); self.globals_free_list.deinit(gpa); - self.locals_free_list.deinit(gpa); self.got_table.deinit(gpa); self.unresolved.deinit(gpa); @@ -278,7 +289,7 @@ pub fn deinit(self: *Elf) void { } } -pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { +pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { assert(self.llvm_object == null); const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); @@ -653,7 +664,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .sh_size = file_size, // The section header index of the associated string table. .sh_link = self.strtab_section_index.?, - .sh_info = @as(u32, @intCast(self.locals.items.len)), + .sh_info = @as(u32, @intCast(self.symbols.items.len)), .sh_addralign = min_align, .sh_entsize = each_size, }, @@ -818,7 +829,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { { // Iterate over symbols, populating free_list and last_text_block. - if (self.locals.items.len != 1) { + if (self.symbols.items.len != 1) { @panic("TODO implement setting up free_list and last_text_block from existing ELF file"); } // We are starting with an empty file. The default values are correct, null and empty list. @@ -975,280 +986,294 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // corresponds to the Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; - if (self.lazy_syms.getPtr(.none)) |metadata| { - // Most lazy symbols can be updated on first use, but - // anyerror needs to wait for everything to be flushed. - if (metadata.text_state != .unused) self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.code, null, module), - metadata.text_atom, - self.text_section_index.?, - ) catch |err| return switch (err) { - error.CodegenFail => error.FlushFailure, - else => |e| e, - }; - if (metadata.rodata_state != .unused) self.updateLazySymbolAtom( - File.LazySymbol.initDecl(.const_data, null, module), - metadata.rodata_atom, - self.rodata_section_index.?, - ) catch |err| return switch (err) { - error.CodegenFail => error.FlushFailure, - else => |e| e, - }; - } - for (self.lazy_syms.values()) |*metadata| { - if (metadata.text_state != .unused) metadata.text_state = .flushed; - if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; - } - - const target_endian = self.base.options.target.cpu.arch.endian(); - const foreign_endian = target_endian != builtin.cpu.arch.endian(); - - if (self.dwarf) |*dw| { - try dw.flushModule(module); - } - - { - var it = self.relocs.iterator(); - while (it.next()) |entry| { - const atom_index = entry.key_ptr.*; - const relocs = entry.value_ptr.*; - const atom_ptr = self.atom(atom_index); - const source_sym = atom_ptr.symbol(self); - const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx]; - - log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)}); - - for (relocs.items) |*reloc| { - const target_sym = self.locals.items[reloc.target]; - const target_vaddr = target_sym.st_value + reloc.addend; - - if (target_vaddr == reloc.prev_vaddr) continue; - - const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; - const file_offset = source_shdr.sh_offset + section_offset; - - log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{ - reloc.offset, - target_vaddr, - self.strtab.get(target_sym.st_name), - }); - - switch (self.ptr_width) { - .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset), - .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset), - } - - reloc.prev_vaddr = target_vaddr; - } - } - } - - try self.writeSymbols(); - - if (build_options.enable_logging) { - self.logSymtab(); - } - - if (self.dwarf) |*dw| { - if (self.debug_abbrev_section_dirty) { - try dw.writeDbgAbbrev(); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_abbrev_section_index.?); - } - self.debug_abbrev_section_dirty = false; - } - - if (self.debug_info_header_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - const low_pc = text_phdr.p_vaddr; - const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; - try dw.writeDbgInfoHeader(module, low_pc, high_pc); - self.debug_info_header_dirty = false; - } - - if (self.debug_aranges_section_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_aranges_section_index.?); - } - self.debug_aranges_section_dirty = false; - } - - if (self.debug_line_header_dirty) { - try dw.writeDbgLineHeader(); - self.debug_line_header_dirty = false; - } - } - - if (self.phdr_table_dirty) { - const phsize: u64 = switch (self.ptr_width) { - .p32 => @sizeOf(elf.Elf32_Phdr), - .p64 => @sizeOf(elf.Elf64_Phdr), - }; - - const phdr_table_index = self.phdr_table_index.?; - const phdr_table = &self.program_headers.items[phdr_table_index]; - const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?]; - - const allocated_size = self.allocatedSize(phdr_table.p_offset); - const needed_size = self.program_headers.items.len * phsize; - - if (needed_size > allocated_size) { - phdr_table.p_offset = 0; // free the space - phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align))); - } - - phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align); - const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset; - phdr_table_load.p_filesz = load_align_offset + needed_size; - phdr_table_load.p_memsz = load_align_offset + needed_size; - - phdr_table.p_filesz = needed_size; - phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset; - phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset; - phdr_table.p_memsz = needed_size; - - switch (self.ptr_width) { - .p32 => { - const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); - defer gpa.free(buf); - - for (buf, 0..) |*phdr, i| { - phdr.* = progHeaderTo32(self.program_headers.items[i]); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); - } - } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); - }, - .p64 => { - const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); - defer gpa.free(buf); - - for (buf, 0..) |*phdr, i| { - phdr.* = self.program_headers.items[i]; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); - } - } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); - }, - } - - // We don't actually care if the phdr load section overlaps, only the phdr section matters. - phdr_table_load.p_offset = 0; - phdr_table_load.p_filesz = 0; - - self.phdr_table_dirty = false; - } - - { - const shdr_index = self.shstrtab_section_index.?; - if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { - try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); - const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; - try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); - self.shstrtab_dirty = false; - } - } - - { - const shdr_index = self.strtab_section_index.?; - if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { - try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false); - const strtab_sect = self.sections.items(.shdr)[shdr_index]; - try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset); - self.strtab_dirty = false; - } - } - - if (self.dwarf) |dwarf| { - const shdr_index = self.debug_str_section_index.?; - if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { - try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); - const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; - try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); - self.debug_strtab_dirty = false; - } - } - - if (self.shdr_table_dirty) { - const shsize: u64 = switch (self.ptr_width) { - .p32 => @sizeOf(elf.Elf32_Shdr), - .p64 => @sizeOf(elf.Elf64_Shdr), - }; - const shalign: u16 = switch (self.ptr_width) { - .p32 => @alignOf(elf.Elf32_Shdr), - .p64 => @alignOf(elf.Elf64_Shdr), - }; - const allocated_size = self.allocatedSize(self.shdr_table_offset.?); - const needed_size = self.sections.slice().len * shsize; - - if (needed_size > allocated_size) { - self.shdr_table_offset = null; // free the space - self.shdr_table_offset = self.findFreeSpace(needed_size, shalign); - } + self.zig_module_index = blk: { + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + self.files.set(index, .{ .zig_module = .{ .index = index } }); + break :blk index; + }; - switch (self.ptr_width) { - .p32 => { - const slice = self.sections.slice(); - const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); - defer gpa.free(buf); + self.linker_defined = blk: { + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + self.files.set(index, .{ .linker_defined = .{} }); + break :blk index; + }; - for (buf, 0..) |*shdr, i| { - shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); - log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); - } - } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); - }, - .p64 => { - const slice = self.sections.slice(); - const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); - defer gpa.free(buf); - - for (buf, 0..) |*shdr, i| { - shdr.* = slice.items(.shdr)[i]; - log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); - } - } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); - }, - } - self.shdr_table_dirty = false; - } - if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { - log.debug("flushing. no_entry_point_found = true", .{}); - self.error_flags.no_entry_point_found = true; - } else { - log.debug("flushing. no_entry_point_found = false", .{}); - self.error_flags.no_entry_point_found = false; - try self.writeElfHeader(); - } - - // The point of flush() is to commit changes, so in theory, nothing should - // be dirty after this. However, it is possible for some things to remain - // dirty because they fail to be written in the event of compile errors, - // such as debug_line_header_dirty and debug_info_header_dirty. - assert(!self.debug_abbrev_section_dirty); - assert(!self.debug_aranges_section_dirty); - assert(!self.phdr_table_dirty); - assert(!self.shdr_table_dirty); - assert(!self.shstrtab_dirty); - assert(!self.strtab_dirty); - assert(!self.debug_strtab_dirty); - assert(!self.got_table_count_dirty); + std.debug.print("{}\n", .{self.dumpState()}); + + // if (self.lazy_syms.getPtr(.none)) |metadata| { + // // Most lazy symbols can be updated on first use, but + // // anyerror needs to wait for everything to be flushed. + // if (metadata.text_state != .unused) self.updateLazySymbolAtom( + // link.File.LazySymbol.initDecl(.code, null, module), + // metadata.text_atom, + // self.text_section_index.?, + // ) catch |err| return switch (err) { + // error.CodegenFail => error.FlushFailure, + // else => |e| e, + // }; + // if (metadata.rodata_state != .unused) self.updateLazySymbolAtom( + // link.File.LazySymbol.initDecl(.const_data, null, module), + // metadata.rodata_atom, + // self.rodata_section_index.?, + // ) catch |err| return switch (err) { + // error.CodegenFail => error.FlushFailure, + // else => |e| e, + // }; + // } + // for (self.lazy_syms.values()) |*metadata| { + // if (metadata.text_state != .unused) metadata.text_state = .flushed; + // if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; + // } + + // const target_endian = self.base.options.target.cpu.arch.endian(); + // const foreign_endian = target_endian != builtin.cpu.arch.endian(); + + // if (self.dwarf) |*dw| { + // try dw.flushModule(module); + // } + + // { + // var it = self.relocs.iterator(); + // while (it.next()) |entry| { + // const atom_index = entry.key_ptr.*; + // const relocs = entry.value_ptr.*; + // const atom_ptr = self.atom(atom_index); + // const source_sym = atom_ptr.symbol(self); + // const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx]; + + // log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)}); + + // for (relocs.items) |*reloc| { + // const target_sym = self.locals.items[reloc.target]; + // const target_vaddr = target_sym.st_value + reloc.addend; + + // if (target_vaddr == reloc.prev_vaddr) continue; + + // const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; + // const file_offset = source_shdr.sh_offset + section_offset; + + // log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{ + // reloc.offset, + // target_vaddr, + // self.strtab.get(target_sym.st_name), + // }); + + // switch (self.ptr_width) { + // .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset), + // .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset), + // } + + // reloc.prev_vaddr = target_vaddr; + // } + // } + // } + + // try self.writeSymbols(); + + // if (build_options.enable_logging) { + // self.logSymtab(); + // } + + // if (self.dwarf) |*dw| { + // if (self.debug_abbrev_section_dirty) { + // try dw.writeDbgAbbrev(); + // if (!self.shdr_table_dirty) { + // // Then it won't get written with the others and we need to do it. + // try self.writeSectHeader(self.debug_abbrev_section_index.?); + // } + // self.debug_abbrev_section_dirty = false; + // } + + // if (self.debug_info_header_dirty) { + // // Currently only one compilation unit is supported, so the address range is simply + // // identical to the main program header virtual address and memory size. + // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + // const low_pc = text_phdr.p_vaddr; + // const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; + // try dw.writeDbgInfoHeader(module, low_pc, high_pc); + // self.debug_info_header_dirty = false; + // } + + // if (self.debug_aranges_section_dirty) { + // // Currently only one compilation unit is supported, so the address range is simply + // // identical to the main program header virtual address and memory size. + // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + // try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); + // if (!self.shdr_table_dirty) { + // // Then it won't get written with the others and we need to do it. + // try self.writeSectHeader(self.debug_aranges_section_index.?); + // } + // self.debug_aranges_section_dirty = false; + // } + + // if (self.debug_line_header_dirty) { + // try dw.writeDbgLineHeader(); + // self.debug_line_header_dirty = false; + // } + // } + + // if (self.phdr_table_dirty) { + // const phsize: u64 = switch (self.ptr_width) { + // .p32 => @sizeOf(elf.Elf32_Phdr), + // .p64 => @sizeOf(elf.Elf64_Phdr), + // }; + + // const phdr_table_index = self.phdr_table_index.?; + // const phdr_table = &self.program_headers.items[phdr_table_index]; + // const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?]; + + // const allocated_size = self.allocatedSize(phdr_table.p_offset); + // const needed_size = self.program_headers.items.len * phsize; + + // if (needed_size > allocated_size) { + // phdr_table.p_offset = 0; // free the space + // phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align))); + // } + + // phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align); + // const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset; + // phdr_table_load.p_filesz = load_align_offset + needed_size; + // phdr_table_load.p_memsz = load_align_offset + needed_size; + + // phdr_table.p_filesz = needed_size; + // phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset; + // phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset; + // phdr_table.p_memsz = needed_size; + + // switch (self.ptr_width) { + // .p32 => { + // const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); + // defer gpa.free(buf); + + // for (buf, 0..) |*phdr, i| { + // phdr.* = progHeaderTo32(self.program_headers.items[i]); + // if (foreign_endian) { + // mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); + // } + // } + // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); + // }, + // .p64 => { + // const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); + // defer gpa.free(buf); + + // for (buf, 0..) |*phdr, i| { + // phdr.* = self.program_headers.items[i]; + // if (foreign_endian) { + // mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); + // } + // } + // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); + // }, + // } + + // // We don't actually care if the phdr load section overlaps, only the phdr section matters. + // phdr_table_load.p_offset = 0; + // phdr_table_load.p_filesz = 0; + + // self.phdr_table_dirty = false; + // } + + // { + // const shdr_index = self.shstrtab_section_index.?; + // if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + // try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); + // const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; + // try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); + // self.shstrtab_dirty = false; + // } + // } + + // { + // const shdr_index = self.strtab_section_index.?; + // if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + // try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false); + // const strtab_sect = self.sections.items(.shdr)[shdr_index]; + // try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset); + // self.strtab_dirty = false; + // } + // } + + // if (self.dwarf) |dwarf| { + // const shdr_index = self.debug_str_section_index.?; + // if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + // try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); + // const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; + // try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); + // self.debug_strtab_dirty = false; + // } + // } + + // if (self.shdr_table_dirty) { + // const shsize: u64 = switch (self.ptr_width) { + // .p32 => @sizeOf(elf.Elf32_Shdr), + // .p64 => @sizeOf(elf.Elf64_Shdr), + // }; + // const shalign: u16 = switch (self.ptr_width) { + // .p32 => @alignOf(elf.Elf32_Shdr), + // .p64 => @alignOf(elf.Elf64_Shdr), + // }; + // const allocated_size = self.allocatedSize(self.shdr_table_offset.?); + // const needed_size = self.sections.slice().len * shsize; + + // if (needed_size > allocated_size) { + // self.shdr_table_offset = null; // free the space + // self.shdr_table_offset = self.findFreeSpace(needed_size, shalign); + // } + + // switch (self.ptr_width) { + // .p32 => { + // const slice = self.sections.slice(); + // const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); + // defer gpa.free(buf); + + // for (buf, 0..) |*shdr, i| { + // shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); + // log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); + // if (foreign_endian) { + // mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); + // } + // } + // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); + // }, + // .p64 => { + // const slice = self.sections.slice(); + // const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); + // defer gpa.free(buf); + + // for (buf, 0..) |*shdr, i| { + // shdr.* = slice.items(.shdr)[i]; + // log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); + // if (foreign_endian) { + // mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); + // } + // } + // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); + // }, + // } + // self.shdr_table_dirty = false; + // } + // if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { + // log.debug("flushing. no_entry_point_found = true", .{}); + // self.error_flags.no_entry_point_found = true; + // } else { + // log.debug("flushing. no_entry_point_found = false", .{}); + // self.error_flags.no_entry_point_found = false; + // try self.writeElfHeader(); + // } + + // // The point of flush() is to commit changes, so in theory, nothing should + // // be dirty after this. However, it is possible for some things to remain + // // dirty because they fail to be written in the event of compile errors, + // // such as debug_line_header_dirty and debug_info_header_dirty. + // assert(!self.debug_abbrev_section_dirty); + // assert(!self.debug_aranges_section_dirty); + // assert(!self.phdr_table_dirty); + // assert(!self.shdr_table_dirty); + // assert(!self.shstrtab_dirty); + // assert(!self.strtab_dirty); + // assert(!self.debug_strtab_dirty); + // assert(!self.got_table_count_dirty); } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2347,7 +2372,7 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { } } -pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Index { +pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Atom.Index { const mod = self.base.options.module.?; const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod)); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); @@ -2559,7 +2584,7 @@ pub fn updateDecl( self: *Elf, mod: *Module, decl_index: Module.Decl.Index, -) File.UpdateDeclError!void { +) link.File.UpdateDeclError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } @@ -2634,7 +2659,7 @@ pub fn updateDecl( fn updateLazySymbolAtom( self: *Elf, - sym: File.LazySymbol, + sym: link.File.LazySymbol, atom_index: Atom.Index, shdr_index: u16, ) !void { @@ -2776,7 +2801,7 @@ pub fn updateDeclExports( mod: *Module, decl_index: Module.Decl.Index, exports: []const *Module.Export, -) File.UpdateDeclExportsError!void { +) link.File.UpdateDeclExportsError!void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } @@ -3420,6 +3445,18 @@ pub fn atomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { return self.atom_by_index_table.get(sym_index); } +fn dumpState(self: *Elf ) std.fmt.Formatter(fmtDumpState) { + return .{ .data = self }; +} + +fn fmtDumpState(self: *Elf, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + +} + pub const null_sym = elf.Elf64_Sym{ .st_name = 0, .st_info = 0, @@ -3431,7 +3468,7 @@ pub const null_sym = elf.Elf64_Sym{ const default_entry_addr = 0x8000000; -pub const base_tag: File.Tag = .elf; +pub const base_tag: link.File.Tag = .elf; const Section = struct { shdr: elf.Elf64_Shdr, @@ -3506,7 +3543,8 @@ pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); -const File = link.File; +const File = @import("Elf/File.zig"); +const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; const Module = @import("../Module.zig"); @@ -3517,3 +3555,4 @@ const TableSection = @import("table_section.zig").TableSection; const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); const Value = @import("../value.zig").Value; +const ZigModule = @import("Elf/ZigModule.zig"); diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig new file mode 100644 index 0000000000..e7cb35b769 --- /dev/null +++ b/src/link/Elf/LinkerDefined.zig @@ -0,0 +1,132 @@ +index: File.Index, +symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +alive: bool = true, + +// output_symtab_size: Elf.SymtabSize = .{}, + +pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { + self.symtab.deinit(allocator); + self.symbols.deinit(allocator); +} + +pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 { + const gpa = elf_file.base.allocator; + try self.symtab.ensureUnusedCapacity(gpa, 1); + try self.symbols.ensureUnusedCapacity(gpa, 1); + self.symtab.appendAssumeCapacity(.{ + .st_name = try elf_file.strtab.insert(gpa, name), + .st_info = elf.STB_GLOBAL << 4, + .st_other = @intFromEnum(elf.STV.HIDDEN), + .st_shndx = elf.SHN_ABS, + .st_value = 0, + .st_size = 0, + }); + const off = try elf_file.internString("{s}", .{name}); + const gop = try elf_file.getOrCreateGlobal(off); + self.symbols.addOneAssumeCapacity().* = gop.index; + return gop.index; +} + +pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { + for (self.symbols.items, 0..) |index, i| { + const sym_idx = @as(u32, @intCast(i)); + const this_sym = self.symtab.items[sym_idx]; + + if (this_sym.st_shndx == elf.SHN_UNDEF) continue; + + const global = elf_file.symbol(index); + if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) { + global.* = .{ + .value = 0, + .name = global.name, + .atom = 0, + .file = self.index, + .sym_idx = sym_idx, + .ver_idx = elf_file.default_sym_version, + }; + } + } +} + +// pub fn resetGlobals(self: *LinkerDefined, elf_file: *Elf) void { +// for (self.symbols.items) |index| { +// const global = elf_file.getSymbol(index); +// const name = global.name; +// global.* = .{}; +// global.name = name; +// } +// } + +// pub fn calcSymtabSize(self: *InternalObject, elf_file: *Elf) !void { +// if (elf_file.options.strip_all) return; + +// for (self.getGlobals()) |global_index| { +// const global = elf_file.getSymbol(global_index); +// if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; +// global.flags.output_symtab = true; +// self.output_symtab_size.nlocals += 1; +// self.output_symtab_size.strsize += @as(u32, @intCast(global.getName(elf_file).len + 1)); +// } +// } + +// pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: Elf.WriteSymtabCtx) !void { +// if (elf_file.options.strip_all) return; + +// const gpa = elf_file.base.allocator; + +// var ilocal = ctx.ilocal; +// for (self.getGlobals()) |global_index| { +// const global = elf_file.getSymbol(global_index); +// if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; +// if (!global.flags.output_symtab) continue; +// const st_name = try ctx.strtab.insert(gpa, global.getName(elf_file)); +// ctx.symtab[ilocal] = global.asElfSym(st_name, elf_file); +// ilocal += 1; +// } +// } + +pub fn asFile(self: *LinkerDefined) File { + return .{ .linker_defined = self }; +} + +pub inline fn getGlobals(self: *LinkerDefined) []const u32 { + return self.symbols.items; +} + +pub fn fmtSymtab(self: *InternalObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { + return .{ .data = .{ + .self = self, + .elf_file = elf_file, + } }; +} + +const FormatContext = struct { + self: *InternalObject, + elf_file: *Elf, +}; + +fn formatSymtab( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + try writer.writeAll(" globals\n"); + for (ctx.self.getGlobals()) |index| { + const global = ctx.elf_file.getSymbol(index); + try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + } +} + +const std = @import("std"); +const elf = std.elf; + +const Allocator = std.mem.Allocator; +const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; +const LinkerDefined = @This(); +// const Object = @import("Object.zig"); +const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig new file mode 100644 index 0000000000..ffbe91aab9 --- /dev/null +++ b/src/link/Elf/Symbol.zig @@ -0,0 +1,337 @@ +//! Represents a defined symbol. + +/// Allocated address value of this symbol. +value: u64 = 0, + +/// Offset into the linker's string table. +name_offset: u32 = 0, + +/// Index of file where this symbol is defined. +file_index: File.Index = 0, + +/// Index of atom containing this symbol. +/// Index of 0 means there is no associated atom with this symbol. +/// Use `atom` to get the pointer to the atom. +atom_index: Atom.Index = 0, + +/// Assigned output section index for this atom. +output_section_index: u16 = 0, + +/// Index of the source symbol this symbol references. +/// Use `getSourceSymbol` to pull the source symbol from the relevant file. +symbol_index: Index = 0, + +/// Index of the source version symbol this symbol references if any. +/// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL. +version_index: elf.Elf64_Versym = elf.VER_NDX_LOCAL, + +/// Misc flags for the symbol packaged as packed struct for compression. +flags: Flags = .{}, + +extra_index: u32 = 0, + +pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { + const file_ptr = symbol.file(elf_file).?; + if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; + return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.shndx == 0 + and file_ptr != .linker_defined and file_ptr != .zig_module; +} + +pub fn isLocal(symbol: Symbol) bool { + return !(symbol.flags.import or symbol.flags.@"export"); +} + +pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { + return symbol.@"type"(elf_file) == elf.STT_GNU_IFUNC; +} + +pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { + const file_ptr = symbol.file(elf_file).?; + const s_sym = symbol.sourceSymbol(elf_file); + if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC; + return s_sym.st_type(); +} + +pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 { + return elf_file.strtab.getAssumeExists(symbol.name); +} + +pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom { + return elf_file.atom(symbol.atom); +} + +pub fn file(symbol: Symbol, elf_file: *Elf) ?File { + return elf_file.file(symbol.file); +} + +pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { + const file_ptr = symbol.file(elf_file).?; + return switch (file_ptr) { + .linker_defined, .zig_module => |x| x.symtab.items[symbol.sym_idx], + inline else => |x| x.symtab[symbol.sym_idx], + }; +} + +pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { + const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32); + const sym = symbol.sourceSymbol(elf_file); + const in_archive = switch (file) { + // .object => |x| !x.alive, + else => false, + }; + return file_ptr.symbolRank(sym, in_archive); +} + +pub fn address(symbol: Symbol, opts: struct { + plt: bool = true, +}, elf_file: *Elf) u64 { + // if (symbol.flags.copy_rel) { + // return elf_file.sectionAddress(elf_file.copy_rel_sect_index.?) + symbol.value; + // } + // if (symbol.flags.plt and opts.plt) { + // const extra = symbol.getExtra(elf_file).?; + // if (!symbol.flags.is_canonical and symbol.flags.got) { + // // We have a non-lazy bound function pointer, use that! + // return elf_file.getPltGotEntryAddress(extra.plt_got); + // } + // // Lazy-bound function it is! + // return elf_file.getPltEntryAddress(extra.plt); + // } + return symbol.value; +} + +pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { + if (!symbol.flags.got) return 0; + const extra = symbol.extra(elf_file).?; + return elf_file.gotEntryAddress(extra.got); +} + +// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 { +// if (!symbol.flags.tlsgd) return 0; +// const extra = symbol.getExtra(elf_file).?; +// return elf_file.getGotEntryAddress(extra.tlsgd); +// } + +// pub fn gotTpAddress(symbol: Symbol, elf_file: *Elf) u64 { +// if (!symbol.flags.gottp) return 0; +// const extra = symbol.getExtra(elf_file).?; +// return elf_file.getGotEntryAddress(extra.gottp); +// } + +// pub fn tlsDescAddress(symbol: Symbol, elf_file: *Elf) u64 { +// if (!symbol.flags.tlsdesc) return 0; +// const extra = symbol.getExtra(elf_file).?; +// return elf_file.getGotEntryAddress(extra.tlsdesc); +// } + +// pub fn alignment(symbol: Symbol, elf_file: *Elf) !u64 { +// const file = symbol.getFile(elf_file) orelse return 0; +// const shared = file.shared; +// const s_sym = symbol.getSourceSymbol(elf_file); +// const shdr = shared.getShdrs()[s_sym.st_shndx]; +// const alignment = @max(1, shdr.sh_addralign); +// return if (s_sym.st_value == 0) +// alignment +// else +// @min(alignment, try std.math.powi(u64, 2, @ctz(s_sym.st_value))); +// } + +pub fn addExtra(symbol: *Symbol, extra: Extra, elf_file: *Elf) !void { + symbol.extra = try elf_file.addSymbolExtra(extra); +} + +pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra { + return elf_file.symbolExtra(symbol.extra); +} + +pub fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void { + elf_file.setSymbolExtra(symbol.extra, extra); +} + +pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { + const file_ptr = symbol.file(elf_file).?; + const s_sym = symbol.sourceSymbol(elf_file); + const st_type = symbol.@"type"(elf_file); + const st_bind: u8 = blk: { + if (symbol.isLocal()) break :blk 0; + if (symbol.flags.weak) break :blk elf.STB_WEAK; + // if (file_ptr == .shared) break :blk elf.STB_GLOBAL; + break :blk s_sym.st_bind(); + }; + const st_shndx = blk: { + // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?; + // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; + if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) + break :blk elf.SHN_ABS; + break :blk symbol.shndx; + }; + const st_value = blk: { + // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file); + // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) { + // if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file); + // break :blk 0; + // } + // if (st_shndx == elf.SHN_ABS) break :blk symbol.value; + // const shdr = &elf_file.sections.items(.shdr)[st_shndx]; + // if (Elf.shdrIsTls(shdr)) break :blk symbol.value - elf_file.getTlsAddress(); + break :blk symbol.value; + }; + return elf.Elf64_Sym{ + .st_name = st_name, + .st_info = (st_bind << 4) | st_type, + .st_other = s_sym.st_other, + .st_shndx = st_shndx, + .st_value = st_value, + .st_size = s_sym.st_size, + }; +} + +pub fn format( + symbol: Symbol, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = symbol; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format symbols directly"); +} + +const FormatContext = struct { + symbol: Symbol, + elf_file: *Elf, +}; + +pub fn fmtName(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(formatName) { + return .{ .data = .{ + .symbol = symbol, + .elf_file = elf_file, + } }; +} + +fn formatName( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = options; + _ = unused_fmt_string; + const elf_file = ctx.elf_file; + const symbol = ctx.symbol; + try writer.writeAll(symbol.getName(elf_file)); + switch (symbol.ver_idx & elf.VERSYM_VERSION) { + elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {}, + else => { + unreachable; + // const shared = symbol.getFile(elf_file).?.shared; + // try writer.print("@{s}", .{shared.getVersionString(symbol.ver_idx)}); + }, + } +} + +pub fn fmt(symbol: Symbol, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .symbol = symbol, + .elf_file = elf_file, + } }; +} + +fn format2( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = options; + _ = unused_fmt_string; + const symbol = ctx.symbol; + try writer.print("%{d} : {s} : @{x}", .{ symbol.sym_idx, symbol.fmtName(ctx.elf_file), symbol.value }); + if (symbol.getFile(ctx.elf_file)) |file| { + if (symbol.isAbs(ctx.elf_file)) { + if (symbol.getSourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { + try writer.writeAll(" : undef"); + } else { + try writer.writeAll(" : absolute"); + } + } else if (symbol.shndx != 0) { + try writer.print(" : sect({d})", .{symbol.shndx}); + } + if (symbol.getAtom(ctx.elf_file)) |atom| { + try writer.print(" : atom({d})", .{atom.atom_index}); + } + var buf: [2]u8 = .{'_'} ** 2; + if (symbol.flags.@"export") buf[0] = 'E'; + if (symbol.flags.import) buf[1] = 'I'; + try writer.print(" : {s}", .{&buf}); + if (symbol.flags.weak) try writer.writeAll(" : weak"); + switch (file) { + .internal => |x| try writer.print(" : internal({d})", .{x.index}), + .object => |x| try writer.print(" : object({d})", .{x.index}), + .shared => |x| try writer.print(" : shared({d})", .{x.index}), + } + } else try writer.writeAll(" : unresolved"); +} + +pub const Flags = packed struct { + /// Whether the symbol is imported at runtime. + import: bool = false, + + /// Whether the symbol is exported at runtime. + @"export": bool = false, + + /// Whether this symbol is weak. + weak: bool = false, + + /// Whether the symbol makes into the output symtab or not. + output_symtab: bool = false, + + /// Whether the symbol contains GOT indirection. + got: bool = false, + + /// Whether the symbol contains PLT indirection. + plt: bool = false, + /// Whether the PLT entry is canonical. + is_canonical: bool = false, + + /// Whether the symbol contains COPYREL directive. + copy_rel: bool = false, + has_copy_rel: bool = false, + has_dynamic: bool = false, + + /// Whether the symbol contains TLSGD indirection. + tlsgd: bool = false, + + /// Whether the symbol contains GOTTP indirection. + gottp: bool = false, + + /// Whether the symbol contains TLSDESC indirection. + tlsdesc: bool = false, +}; + +pub const Extra = struct { + got: u32 = 0, + plt: u32 = 0, + plt_got: u32 = 0, + dynamic: u32 = 0, + copy_rel: u32 = 0, + tlsgd: u32 = 0, + gottp: u32 = 0, + tlsdesc: u32 = 0, +}; + +pub const Index = u32; + +const std = @import("std"); +const assert = std.debug.assert; +const elf = std.elf; + +const Atom = @import("Atom.zig"); +const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; +const InternalObject = @import("InternalObject.zig"); +const Object = @import("Object.zig"); +const SharedObject = @import("SharedObject.zig"); +const Symbol = @This(); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig new file mode 100644 index 0000000000..b76990a29b --- /dev/null +++ b/src/link/Elf/ZigModule.zig @@ -0,0 +1,69 @@ +index: File.Index, +elf_locals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +locals: std.ArrayListUnmanaged(Symbol.Index) = .{}, +elf_globals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +globals: std.ArrayListUnmanaged(Symbol.Index) = .{}, +alive: bool = true, + +// output_symtab_size: Elf.SymtabSize = .{}, + +pub fn deinit(self: *ZigModule, allocator: Allocator) void { + self.elf_locals.deinit(allocator); + self.locals.deinit(allocator); + self.elf_globals.deinit(allocator); + self.globals.deinit(allocator); +} + +pub fn asFile(self: *ZigModule) File { + return .{ .zig_module = self }; +} + +pub fn getLocals(self: *ZigModule) []const Symbol.Index { + return self.locals.items; +} + +pub fn getGlobals(self: *ZigModule) []const Symbol.Index { + return self.globals.items; +} + +pub fn fmtSymtab(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { + return .{ .data = .{ + .self = self, + .elf_file = elf_file, + } }; +} + +const FormatContext = struct { + self: *ZigModule, + elf_file: *Elf, +}; + +fn formatSymtab( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + try writer.writeAll(" locals\n"); + for (ctx.self.getLocals()) |index| { + const local = ctx.elf_file.symbol(index); + try writer.print(" {}\n", .{local.fmt(ctx.elf_file)}); + } + try writer.writeAll(" globals\n"); + for (ctx.self.getGlobals()) |index| { + const global = ctx.elf_file.getSymbol(index); + try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + } +} + +const std = @import("std"); +const elf = std.elf; + +const Allocator = std.mem.Allocator; +const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; +const ZigModule = @This(); +// const Object = @import("Object.zig"); +const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig new file mode 100644 index 0000000000..25624389ed --- /dev/null +++ b/src/link/Elf/file.zig @@ -0,0 +1,110 @@ +pub const File = union(enum) { + zig_module: *ZigModule, + linker_defined: *LinkerDefined, + // object: *Object, + // shared_object: *SharedObject, + + pub fn index(file: File) Index { + return switch (file) { + inline else => |x| x.index, + }; + } + + pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { + return .{ .data = file }; + } + + fn formatPath( + file: File, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + switch (file) { + .zig_module => try writer.writeAll("(zig module)"), + .linker_defined => try writer.writeAll("(linker defined)"), + .object => |x| try writer.print("{}", .{x.fmtPath()}), + .shared_object => |x| try writer.writeAll(x.path), + } + } + + pub fn resolveSymbols(file: File, elf_file: *Elf) void { + switch (file) { + .zig_module => unreachable, // handled separately + inline else => |x| x.resolveSymbols(elf_file), + } + } + + // pub fn resetGlobals(file: File, elf_file: *Elf) void { + // switch (file) { + // inline else => |x| x.resetGlobals(elf_file), + // } + // } + + pub fn isAlive(file: File) bool { + return switch (file) { + .zig_module => true, + .linker_defined => true, + inline else => |x| x.alive, + }; + } + + /// Encodes symbol rank so that the following ordering applies: + /// * strong defined + /// * weak defined + /// * strong in lib (dso/archive) + /// * weak in lib (dso/archive) + /// * common + /// * common in lib (archive) + /// * unclaimed + pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 { + const base: u3 = blk: { + if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5; + if (file == .shared or in_archive) break :blk switch (sym.st_bind()) { + elf.STB_GLOBAL => 3, + else => 4, + }; + break :blk switch (sym.st_bind()) { + elf.STB_GLOBAL => 1, + else => 2, + }; + }; + return (@as(u32, base) << 24) + file.index(); + } + + pub fn setAlive(file: File) void { + switch (file) { + .zig_module, .linker_defined => {}, + inline else => |x| x.alive = true, + } + } + + pub fn markLive(file: File, elf_file: *Elf) void { + switch (file) { + .zig_module, .linker_defined => {}, + inline else => |x| x.markLive(elf_file), + } + } + + pub const Index = u32; + + pub const Entry = union(enum) { + null: void, + zig_module: ZigModule, + linker_defined: LinkerDefined, + // object: Object, + // shared_object: SharedObject, + }; +}; + +const std = @import("std"); +const elf = std.elf; + +const Allocator = std.mem.Allocator; +const Elf = @import("../Elf.zig"); +const LinkerDefined = @import("LinkerDefined.zig"); +// const Object = @import("Object.zig"); +// const SharedObject = @import("SharedObject.zig"); +const ZigModule = @import("ZigModule.zig"); -- cgit v1.2.3 From a9df098cd2dd04e2c363b439233ff2e14e198413 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 5 Sep 2023 23:10:04 +0200 Subject: elf: make everything upside down - track by Symbol.Index rather than Atom.Index --- src/arch/aarch64/CodeGen.zig | 8 +- src/arch/arm/CodeGen.zig | 8 +- src/arch/riscv64/CodeGen.zig | 8 +- src/arch/sparc64/CodeGen.zig | 8 +- src/arch/x86_64/CodeGen.zig | 16 +- src/codegen.zig | 10 +- src/link/Elf.zig | 660 +++++++++++++++-------------------------- src/link/Elf/Atom.zig | 245 +++++++++++---- src/link/Elf/LinkerDefined.zig | 22 +- src/link/Elf/Symbol.zig | 102 ++++--- src/link/Elf/ZigModule.zig | 97 ++++-- src/link/Elf/file.zig | 7 + src/link/strtab.zig | 4 +- 13 files changed, 607 insertions(+), 588 deletions(-) (limited to 'src') diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 15f7397721..35daa4a02a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4314,10 +4314,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, mod)) |func_value| { if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 234a281623..f32e104c88 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4294,10 +4294,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, mod)) |func_value| { if (func_value.getFunction(mod)) |func| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; // unsupported architecture for MachO diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index bead5c6d40..5ed60dc220 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1747,10 +1747,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (try self.air.value(callee, mod)) |func_value| { switch (mod.intern_pool.indexToKey(func_value.ip_index)) { .func => |func| { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); _ = try self.addInst(.{ .tag = .jalr, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 3a3f69ce37..b0a3573ff9 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1349,10 +1349,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier switch (mod.intern_pool.indexToKey(func_value.ip_index)) { .func => |func| { const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { - const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - break :blk @as(u32, @intCast(atom.getOffsetTableAddress(elf_file))); + const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + break :blk @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); } else unreachable; try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr }); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 187db5f20f..eaed79ef5e 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8149,10 +8149,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier else => null, }) |owner_decl| { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(owner_decl); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); + const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = sym.getOffsetTableAddress(elf_file); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr), @@ -10215,11 +10215,11 @@ fn genLazySymbolRef( lazy_sym: link.File.LazySymbol, ) InnerError!void { if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = elf_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| + const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - const got_addr = atom.getOffsetTableAddress(elf_file); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + const got_addr = sym.getOffsetTableAddress(elf_file); const got_mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) }); switch (tag) { diff --git a/src/codegen.zig b/src/codegen.zig index 75f42ff876..d3f44bb2f5 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -854,10 +854,10 @@ fn genDeclRef( const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded; if (bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); - const atom = elf_file.atom(atom_index); - _ = try atom.getOrCreateOffsetTableEntry(elf_file); - return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(elf_file) }); + const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); + const sym = elf_file.symbol(sym_index); + _ = try sym.getOrCreateOffsetTableEntry(elf_file); + return GenResult.mcv(.{ .memory = sym.getOffsetTableAddress(elf_file) }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; @@ -892,7 +892,7 @@ fn genUnnamedConst( return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)}); }; if (bin_file.cast(link.File.Elf)) |elf_file| { - return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).st_value }); + return GenResult.mcv(.{ .memory = elf_file.symbol(local_sym_index).value }); } else if (bin_file.cast(link.File.MachO)) |_| { return GenResult.mcv(.{ .load_direct = local_sym_index }); } else if (bin_file.cast(link.File.Coff)) |_| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 14bfa58748..6539946ca1 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,4 +1,4 @@ -base: File, +base: link.File, dwarf: ?Dwarf = null, ptr_width: PtrWidth, @@ -6,7 +6,7 @@ ptr_width: PtrWidth, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. llvm_object: ?*LlvmObject = null, -files: std.MutliArrayList(File.Entry) = .{}, +files: std.MultiArrayList(File.Entry) = .{}, zig_module_index: ?File.Index = null, linker_defined_index: ?File.Index = null, @@ -56,14 +56,12 @@ shstrtab_section_index: ?u16 = null, strtab_section_index: ?u16 = null, symbols: std.ArrayListUnmanaged(Symbol) = .{}, -globals: std.ArrayListUnmanaged(Symbol.Index) = .{}, -resolver: std.StringHashMapUnmanaged(u32) = .{}, +resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, -symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, -globals_free_list: std.ArrayListUnmanaged(u32) = .{}, +symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, -got_table: TableSection(u32) = .{}, +got_table: TableSection(Symbol.Index) = .{}, phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, @@ -88,9 +86,6 @@ decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, /// List of atoms that are owned directly by the linker. atoms: std.ArrayListUnmanaged(Atom) = .{}, -/// Table of atoms indexed by the symbol index. -atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, - /// Table of unnamed constants associated with a parent `Decl`. /// We store them here so that we can free the constants whenever the `Decl` /// needs updating or is freed. @@ -113,12 +108,10 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, unnamed_const_atoms: UnnamedConstTable = .{}, /// A table of relocations indexed by the owning them `TextBlock`. -/// Note that once we refactor `TextBlock`'s lifetime and ownership rules, -/// this will be a table indexed by index into the list of Atoms. relocs: RelocTable = .{}, const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc)); -const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); +const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); /// When allocating, the ideal_capacity is calculated by @@ -143,13 +136,12 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option const self = try createEmpty(allocator, options); errdefer self.base.destroy(); - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + self.base.file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options), }); - self.base.file = file; self.shdr_table_dirty = true; // Index 0 is always a null symbol. @@ -243,19 +235,10 @@ pub fn deinit(self: *Elf) void { self.strtab.deinit(gpa); self.symbols.deinit(gpa); self.symbols_free_list.deinit(gpa); - self.globals.deinit(gpa); - self.globals_free_list.deinit(gpa); self.got_table.deinit(gpa); + self.resolver.deinit(gpa); self.unresolved.deinit(gpa); - { - var it = self.resolver.keyIterator(); - while (it.next()) |key_ptr| { - gpa.free(key_ptr.*); - } - self.resolver.deinit(gpa); - } - { var it = self.decls.iterator(); while (it.next()) |entry| { @@ -265,7 +248,6 @@ pub fn deinit(self: *Elf) void { } self.atoms.deinit(gpa); - self.atom_by_index_table.deinit(gpa); self.lazy_syms.deinit(gpa); { @@ -292,13 +274,12 @@ pub fn deinit(self: *Elf) void { pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { assert(self.llvm_object == null); - const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); - const this_atom = self.atom(this_atom_index); - const target = this_atom.symbolIndex().?; - const vaddr = this_atom.symbol(self).st_value; - const atom_index = self.atomIndexForSymbol(reloc_info.parent_atom_index).?; - try Atom.addRelocation(self, atom_index, .{ - .target = target, + const this_sym_index = try self.getOrCreateMetadataForDecl(decl_index); + const this_sym = self.symbol(this_sym_index); + const vaddr = this_sym.value; + const parent_atom_index = self.symbol(reloc_info.parent_atom_index).atom_index; + try Atom.addRelocation(self, parent_atom_index, .{ + .target = this_sym, .offset = reloc_info.offset, .addend = reloc_info.addend, .prev_vaddr = vaddr, @@ -851,7 +832,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { } } -fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { +pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { // TODO Also detect virtual address collisions. const shdr = &self.sections.items(.shdr)[shdr_index]; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; @@ -863,8 +844,7 @@ fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { const new_offset = self.findFreeSpace(needed_size, self.page_size); const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: { const last = self.atom(last_atom_index); - const sym = last.symbol(self); - break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; + break :blk (last.value + last.size) - phdr.p_vaddr; } else if (shdr_index == self.got_section_index.?) blk: { break :blk shdr.sh_size; } else 0; @@ -985,6 +965,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // TODO This linker code currently assumes there is only 1 compilation unit and it // corresponds to the Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + _ = module; self.zig_module_index = blk: { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); @@ -992,9 +973,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node break :blk index; }; - self.linker_defined = blk: { + self.linker_defined_index = blk: { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); - self.files.set(index, .{ .linker_defined = .{} }); + self.files.set(index, .{ .linker_defined = .{ .index = index } }); break :blk index; }; @@ -2157,193 +2138,12 @@ fn freeAtom(self: *Elf, atom_index: Atom.Index) void { const sym_index = atom_ptr.symbolIndex().?; log.debug("adding %{d} to local symbols free list", .{sym_index}); - self.locals_free_list.append(gpa, sym_index) catch {}; - self.locals.items[sym_index] = null_sym; - _ = self.atom_by_index_table.remove(sym_index); + self.symbols_free_list.append(gpa, sym_index) catch {}; + self.symbols.items[sym_index] = .{}; atom_ptr.sym_index = 0; self.got_table.freeEntry(gpa, sym_index); } -fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { - _ = self; - _ = atom_index; - _ = new_block_size; -} - -fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { - const atom_ptr = self.atom(atom_index); - const sym = atom_ptr.symbol(self); - const align_ok = mem.alignBackward(u64, sym.st_value, alignment) == sym.st_value; - const need_realloc = !align_ok or new_block_size > atom_ptr.capacity(self); - if (!need_realloc) return sym.st_value; - return self.allocateAtom(atom_index, new_block_size, alignment); -} - -pub fn createAtom(self: *Elf) !Atom.Index { - const gpa = self.base.allocator; - const atom_index = @as(Atom.Index, @intCast(self.atoms.items.len)); - const atom_ptr = try self.atoms.addOne(gpa); - const sym_index = try self.allocateSymbol(); - try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); - atom_ptr.* = .{ .sym_index = sym_index }; - log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index }); - return atom_index; -} - -fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { - const sym = self.atom(atom_index).symbol(self); - const phdr_index = self.sections.items(.phdr_index)[sym.st_shndx]; - const phdr = &self.program_headers.items[phdr_index]; - const shdr = &self.sections.items(.shdr)[sym.st_shndx]; - const free_list = &self.sections.items(.free_list)[sym.st_shndx]; - const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sym.st_shndx]; - const new_atom_ideal_capacity = padToIdeal(new_block_size); - - // We use these to indicate our intention to update metadata, placing the new atom, - // and possibly removing a free list node. - // It would be simpler to do it inside the for loop below, but that would cause a - // problem if an error was returned later in the function. So this action - // is actually carried out at the end of the function, when errors are no longer possible. - var atom_placement: ?Atom.Index = null; - var free_list_removal: ?usize = null; - - // First we look for an appropriately sized free list node. - // The list is unordered. We'll just take the first thing that works. - const vaddr = blk: { - var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len; - while (i < free_list.items.len) { - const big_atom_index = free_list.items[i]; - const big_atom = self.atom(big_atom_index); - // We now have a pointer to a live atom that has too much capacity. - // Is it enough that we could fit this new atom? - const big_atom_sym = big_atom.symbol(self); - const capacity = big_atom.capacity(self); - const ideal_capacity = padToIdeal(capacity); - const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity; - const capacity_end_vaddr = big_atom_sym.st_value + capacity; - const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; - const new_start_vaddr = mem.alignBackward(u64, new_start_vaddr_unaligned, alignment); - if (new_start_vaddr < ideal_capacity_end_vaddr) { - // Additional bookkeeping here to notice if this free list node - // should be deleted because the block that it points to has grown to take up - // more of the extra capacity. - if (!big_atom.freeListEligible(self)) { - _ = free_list.swapRemove(i); - } else { - i += 1; - } - continue; - } - // At this point we know that we will place the new block here. But the - // remaining question is whether there is still yet enough capacity left - // over for there to still be a free list node. - const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; - const keep_free_list_node = remaining_capacity >= min_text_capacity; - - // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = big_atom_index; - if (!keep_free_list_node) { - free_list_removal = i; - } - break :blk new_start_vaddr; - } else if (maybe_last_atom_index.*) |last_index| { - const last = self.atom(last_index); - const last_sym = last.symbol(self); - const ideal_capacity = padToIdeal(last_sym.st_size); - const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity; - const new_start_vaddr = mem.alignForward(u64, ideal_capacity_end_vaddr, alignment); - // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = last_index; - break :blk new_start_vaddr; - } else { - break :blk phdr.p_vaddr; - } - }; - - const expand_section = if (atom_placement) |placement_index| - self.atom(placement_index).next_index == null - else - true; - if (expand_section) { - const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; - try self.growAllocSection(sym.st_shndx, needed_size); - maybe_last_atom_index.* = atom_index; - - if (self.dwarf) |_| { - // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address - // range of the compilation unit. When we expand the text section, this range changes, - // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. - self.debug_info_header_dirty = true; - // This becomes dirty for the same reason. We could potentially make this more - // fine-grained with the addition of support for more compilation units. It is planned to - // model each package as a different compilation unit. - self.debug_aranges_section_dirty = true; - } - } - shdr.sh_addralign = @max(shdr.sh_addralign, alignment); - - // This function can also reallocate an atom. - // In this case we need to "unplug" it from its previous location before - // plugging it in to its new location. - const atom_ptr = self.atom(atom_index); - if (atom_ptr.prev_index) |prev_index| { - const prev = self.atom(prev_index); - prev.next_index = atom_ptr.next_index; - } - if (atom_ptr.next_index) |next_index| { - const next = self.atom(next_index); - next.prev_index = atom_ptr.prev_index; - } - - if (atom_placement) |big_atom_index| { - const big_atom = self.atom(big_atom_index); - atom_ptr.prev_index = big_atom_index; - atom_ptr.next_index = big_atom.next_index; - big_atom.next_index = atom_index; - } else { - atom_ptr.prev_index = null; - atom_ptr.next_index = null; - } - if (free_list_removal) |i| { - _ = free_list.swapRemove(i); - } - return vaddr; -} - -pub fn allocateSymbol(self: *Elf) !u32 { - try self.locals.ensureUnusedCapacity(self.base.allocator, 1); - const index = blk: { - if (self.locals_free_list.popOrNull()) |index| { - log.debug(" (reusing symbol index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating symbol index {d})", .{self.locals.items.len}); - const index = @as(u32, @intCast(self.locals.items.len)); - _ = self.locals.addOneAssumeCapacity(); - break :blk index; - } - }; - self.locals.items[index] = null_sym; - return index; -} - -fn allocateGlobal(self: *Elf) !u32 { - try self.globals.ensureUnusedCapacity(self.base.allocator, 1); - const index = blk: { - if (self.globals_free_list.popOrNull()) |index| { - log.debug(" (reusing global index {d})", .{index}); - break :blk index; - } else { - log.debug(" (allocating global index {d})", .{self.globals.items.len}); - const index = @as(u32, @intCast(self.globals.items.len)); - _ = self.globals.addOneAssumeCapacity(); - break :blk index; - } - }; - self.globals.items[index] = 0; - return index; -} - fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom_index| { @@ -2372,40 +2172,50 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { } } -pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Atom.Index { +pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) !Symbol.Index { const mod = self.base.options.module.?; const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod)); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; - const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) { - .code => .{ .atom = &gop.value_ptr.text_atom, .state = &gop.value_ptr.text_state }, - .const_data => .{ .atom = &gop.value_ptr.rodata_atom, .state = &gop.value_ptr.rodata_state }, + const metadata: struct { + symbol_index: *Symbol.Index, + state: *LazySymbolMetadata.State, + } = switch (sym.kind) { + .code => .{ + .symbol_index = &gop.value_ptr.text_symbol_index, + .state = &gop.value_ptr.text_state, + }, + .const_data => .{ + .symbol_index = &gop.value_ptr.rodata_symbol_index, + .state = &gop.value_ptr.rodata_state, + }, }; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; switch (metadata.state.*) { - .unused => metadata.atom.* = try self.createAtom(), + .unused => metadata.symbol_index.* = try zig_module.createAtom(switch (sym.kind) { + .code => self.text_section_index.?, + .const_data => self.rodata_section_index.?, + }, self), .pending_flush => return metadata.atom.*, .flushed => {}, } metadata.state.* = .pending_flush; - const atom_index = metadata.atom.*; + const symbol_index = metadata.symbol_index.*; // anyerror needs to be deferred until flushModule - if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom_index, switch (sym.kind) { - .code => self.text_section_index.?, - .const_data => self.rodata_section_index.?, - }); - return atom_index; + if (sym.getDecl(mod) != .none) try self.updateLazySymbol(sym, symbol_index); + return symbol_index; } -pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index { +pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Symbol.Index { const gop = try self.decls.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { + const zig_module = self.file(self.zig_module_index.?).?.zig_module; gop.value_ptr.* = .{ - .atom = try self.createAtom(), - .shdr = self.getDeclShdrIndex(decl_index), + .symbol_index = try zig_module.createAtom(self.getDeclShdrIndex(decl_index), self), .exports = .{}, }; } - return gop.value_ptr.atom; + return gop.value_ptr.symbol_index; } fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 { @@ -2434,7 +2244,13 @@ fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 { return shdr_index; } -fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { +fn updateDeclCode( + self: *Elf, + decl_index: Module.Decl.Index, + sym_index: Symbol.Index, + code: []const u8, + stt_bits: u8, +) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); @@ -2444,60 +2260,52 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); const required_alignment = decl.getAlignment(mod); - const decl_metadata = self.decls.get(decl_index).?; - const atom_index = decl_metadata.atom; - const atom_ptr = self.atom(atom_index); - const local_sym_index = atom_ptr.symbolIndex().?; - const local_sym = atom_ptr.symbol(self); + const sym = self.symbol(sym_index); + const esym = sym.sourceSymbol(self); + const atom_ptr = sym.atom(self).?; + const shdr_index = sym.output_section_index; - const shdr_index = decl_metadata.shdr; - if (atom_ptr.symbol(self).st_size != 0 and self.base.child_pid == null) { - local_sym.st_name = try self.strtab.insert(gpa, decl_name); - local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; - local_sym.st_other = 0; - local_sym.st_shndx = shdr_index; + sym.name_offset = try self.strtab.insert(gpa, decl_name); + esym.st_name = sym.name_offset; + esym.st_info |= stt_bits; + esym.st_size = code.len; - const capacity = atom_ptr.capacity(self); - const need_realloc = code.len > capacity or - !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); + const old_size = atom_ptr.size; + atom_ptr.alignment = math.log2_int(u64, required_alignment); + atom_ptr.size = code.len; + if (old_size > 0 and self.base.child_pid == null) { + const capacity = atom_ptr.capacity(self); + const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); if (need_realloc) { - const vaddr = try self.growAtom(atom_index, code.len, required_alignment); - log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr }); - if (vaddr != local_sym.st_value) { - local_sym.st_value = vaddr; + const vaddr = try atom_ptr.grow(self); + log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); + if (vaddr != sym.value) { + sym.value = vaddr; + esym.st_value = vaddr; log.debug(" (writing new offset table entry)", .{}); - const got_entry_index = self.got_table.lookup.get(local_sym_index).?; - self.got_table.entries.items[got_entry_index] = local_sym_index; + const got_entry_index = self.got_table.lookup.get(sym_index).?; + self.got_table.entries.items[got_entry_index] = sym_index; try self.writeOffsetTableEntry(got_entry_index); } - } else if (code.len < local_sym.st_size) { - self.shrinkAtom(atom_index, code.len); + } else if (code.len < old_size) { + atom_ptr.shrink(self); } - local_sym.st_size = code.len; } else { - local_sym.* = .{ - .st_name = try self.strtab.insert(gpa, decl_name), - .st_info = (elf.STB_LOCAL << 4) | stt_bits, - .st_other = 0, - .st_shndx = shdr_index, - .st_value = 0, - .st_size = 0, - }; - const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); - errdefer self.freeAtom(atom_index); + const vaddr = try atom_ptr.allocate(self); + errdefer self.freeAtom(atom_ptr); log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); - local_sym.st_value = vaddr; - local_sym.st_size = code.len; + sym.value = vaddr; + esym.st_value = vaddr; - const got_entry_index = try atom_ptr.getOrCreateOffsetTableEntry(self); + const got_entry_index = try sym.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); } const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; + const section_offset = sym.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; if (self.base.child_pid) |pid| { @@ -2508,7 +2316,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s .iov_len = code.len, }}; var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(local_sym.st_value)))), + .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.value)))), .iov_len = code.len, }}; const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); @@ -2522,8 +2330,6 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s } try self.base.file.?.pwriteAll(code, file_offset); - - return local_sym; } pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { @@ -2539,9 +2345,9 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); - const atom_index = try self.getOrCreateAtomForDecl(decl_index); + const sym_index = try self.getOrCreateMetadataForDecl(decl_index); self.freeUnnamedConsts(decl_index); - Atom.freeRelocations(self, atom_index); + Atom.freeRelocations(self, self.symbol(sym_index).atom_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2564,13 +2370,14 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A return; }, }; - const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC); + try self.updateDeclCode(decl_index, sym_index, code, elf.STT_FUNC); if (decl_state) |*ds| { + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( mod, decl_index, - local_sym.st_value, - local_sym.st_size, + sym.value, + sym.atom(self).?.size, ds, ); } @@ -2604,8 +2411,8 @@ pub fn updateDecl( } } - const atom_index = try self.getOrCreateAtomForDecl(decl_index); - Atom.freeRelocations(self, atom_index); + const sym_index = try self.getOrCreateMetadataForDecl(decl_index); + Atom.freeRelocations(self, self.symbol(sym_index).atom_index); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2622,14 +2429,14 @@ pub fn updateDecl( }, &code_buffer, .{ .dwarf = ds, }, .{ - .parent_atom_index = self.atom(atom_index).symbolIndex().?, + .parent_atom_index = sym_index, }) else try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .none, .{ - .parent_atom_index = self.atom(atom_index).symbolIndex().?, + .parent_atom_index = sym_index, }); const code = switch (res) { @@ -2641,13 +2448,14 @@ pub fn updateDecl( }, }; - const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT); + try self.updateDeclCode(decl_index, sym_index, code, elf.STT_OBJECT); if (decl_state) |*ds| { + const sym = self.symbol(sym_index); try self.dwarf.?.commitDeclState( mod, decl_index, - local_sym.st_value, - local_sym.st_size, + sym.value, + sym.atom(self).?.size, ds, ); } @@ -2657,12 +2465,7 @@ pub fn updateDecl( return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index)); } -fn updateLazySymbolAtom( - self: *Elf, - sym: link.File.LazySymbol, - atom_index: Atom.Index, - shdr_index: u16, -) !void { +fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; @@ -2680,9 +2483,6 @@ fn updateLazySymbolAtom( }; const name = self.strtab.get(name_str_index).?; - const atom_ptr = self.atom(atom_index); - const local_sym_index = atom_ptr.symbolIndex().?; - const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| mod.declPtr(owner_decl).srcLoc(mod) else @@ -2698,7 +2498,7 @@ fn updateLazySymbolAtom( &required_alignment, &code_buffer, .none, - .{ .parent_atom_index = local_sym_index }, + .{ .parent_atom_index = symbol_index }, ); const code = switch (res) { .ok => code_buffer.items, @@ -2708,28 +2508,28 @@ fn updateLazySymbolAtom( }, }; - const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const local_sym = atom_ptr.symbol(self); - local_sym.* = .{ - .st_name = name_str_index, - .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT, - .st_other = 0, - .st_shndx = shdr_index, - .st_value = 0, - .st_size = 0, - }; - const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); - errdefer self.freeAtom(atom_index); + const local_sym = self.symbol(symbol_index); + const phdr_index = self.sections.items(.phdr_index)[local_sym.output_section_index]; + local_sym.name_offset = name_str_index; + const local_esym = local_sym.sourceSymbol(self); + local_esym.st_name = name_str_index; + local_esym.st_info |= elf.STT_OBJECT; + local_esym.st_size = code.len; + const atom_ptr = local_sym.atom(self).?; + atom_ptr.alignment = math.log2_int(u64, required_alignment); + atom_ptr.size = code.len; + const vaddr = try atom_ptr.allocate(self); + errdefer self.freeAtom(atom_ptr); log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); - local_sym.st_value = vaddr; - local_sym.st_size = code.len; + local_sym.value = vaddr; + local_esym.st_value = vaddr; - const got_entry_index = try atom_ptr.getOrCreateOffsetTableEntry(self); + const got_entry_index = try local_sym.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr; - const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; + const file_offset = self.sections.items(.shdr)[local_sym.output_section_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); } @@ -2756,12 +2556,13 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module }; const name = self.strtab.get(name_str_index).?; - const atom_index = try self.createAtom(); + const zig_module = self.file(self.zig_module_index.?).?.zig_module; + const sym_index = try zig_module.createAtom(self.rodata_section_index.?, self); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{ .none = {}, }, .{ - .parent_atom_index = self.atom(atom_index).symbolIndex().?, + .parent_atom_index = sym_index, }); const code = switch (res) { .ok => code_buffer.items, @@ -2776,24 +2577,30 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const required_alignment = typed_value.ty.abiAlignment(mod); const shdr_index = self.rodata_section_index.?; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const local_sym = self.atom(atom_index).symbol(self); - local_sym.st_name = name_str_index; - local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT; - local_sym.st_other = 0; - local_sym.st_shndx = shdr_index; - local_sym.st_size = code.len; - local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment); - errdefer self.freeAtom(atom_index); + const local_sym = self.symbol(sym_index); + local_sym.name_offset = name_str_index; + const local_esym = local_sym.sourceSymbol(self); + local_esym.st_name = name_str_index; + local_esym.st_info |= elf.STT_OBJECT; + local_esym.st_size = code.len; + const atom_ptr = local_sym.atom(self).?; + atom_ptr.alignment = math.log2_int(u64, required_alignment); + atom_ptr.size = code.len; + const vaddr = try atom_ptr.allocateAtom(self); + errdefer self.freeAtom(atom_ptr); log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value }); - try unnamed_consts.append(gpa, atom_index); + local_sym.value = vaddr; + local_esym.st_value = vaddr; - const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; + try unnamed_consts.append(gpa, atom_ptr.atom_index); + + const section_offset = local_sym.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); - return self.atom(atom_index).symbolIndex().?; + return sym_index; } pub fn updateDeclExports( @@ -2815,11 +2622,10 @@ pub fn updateDeclExports( const gpa = self.base.allocator; const decl = mod.declPtr(decl_index); - const atom_index = try self.getOrCreateAtomForDecl(decl_index); - const atom_ptr = self.atom(atom_index); - const decl_sym = atom_ptr.symbol(self); + const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index); + const decl_sym = self.symbol(decl_sym_index); + const decl_esym = symbol.sourceSymbol(self); const decl_metadata = self.decls.getPtr(decl_index).?; - const shdr_index = decl_metadata.shdr; for (exports) |exp| { const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); @@ -2838,7 +2644,7 @@ pub fn updateDeclExports( .Strong => blk: { const entry_name = self.base.options.entry orelse "_start"; if (mem.eql(u8, exp_name, entry_name)) { - self.entry_addr = decl_sym.st_value; + self.entry_addr = decl_sym.value; } break :blk elf.STB_GLOBAL; }, @@ -2855,22 +2661,18 @@ pub fn updateDeclExports( const stt_bits: u8 = @as(u4, @truncate(decl_sym.st_info)); const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { - const sym_index = try self.allocateSymbol(); + const zig_module = self.file(self.zig_module_index.?).?.zig_module; + const sym_index = try zig_module.addGlobal(exp_name, self); try decl_metadata.exports.append(gpa, sym_index); break :blk sym_index; }; const sym = self.symbol(sym_index); - sym.* = .{ - .st_name = try self.strtab.insert(gpa, exp_name), - .st_info = (stb_bits << 4) | stt_bits, - .st_other = 0, - .st_shndx = shdr_index, - .st_value = decl_sym.st_value, - .st_size = decl_sym.st_size, - }; - const sym_name = self.symbolName(sym_index); - const gop = try self.getOrPutGlobal(sym_name); - gop.value_ptr.* = sym_index; + sym.value = decl_sym.value; + sym.atom_index = decl_sym.atom_index; + sym.output_section_index = decl_sym.output_section_index; + const esym = sym.sourceSymbol(self); + esym.* = decl_esym.*; + esym.st_info = (stb_bits << 4) | stt_bits; } } @@ -2901,16 +2703,21 @@ pub fn deleteDeclExport( const mod = self.base.options.module.?; const exp_name = mod.intern_pool.stringToSlice(name); const sym_index = metadata.@"export"(self, exp_name) orelse return; - const sym = self.symbol(sym_index.*); log.debug("deleting export '{s}'", .{exp_name}); - sym.* = null_sym; - self.locals_free_list.append(gpa, sym_index.*) catch {}; - - if (self.resolver.fetchRemove(exp_name)) |entry| { - self.globals_free_list.append(gpa, entry.value) catch {}; - self.globals.items[entry.value] = 0; - } - + const sym = self.symbol(sym_index.*); + const esym = sym.sourceSymbol(self); + assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant + sym.* = .{}; + // TODO free list for esym! + esym.* = .{ + .st_name = 0, + .st_info = 0, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, + }; + self.symbols_free_list.append(gpa, sym_index.*) catch {}; sym_index.* = 0; } @@ -2971,7 +2778,7 @@ fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void const phdr = &self.program_headers.items[self.phdr_got_index.?]; const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; const got_entry = self.got_table.entries.items[index]; - const got_value = self.symbol(got_entry).st_value; + const got_value = self.symbol(got_entry).value; switch (entry_size) { 2 => { var buf: [2]u8 = undefined; @@ -3374,98 +3181,99 @@ const CsuObjects = struct { } }; -fn logSymtab(self: Elf) void { - log.debug("locals:", .{}); - for (self.locals.items, 0..) |sym, id| { - log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); - } - log.debug("globals:", .{}); - for (self.globals.items, 0..) |glob, id| { - const sym = self.symbol(glob); - log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.strtab.get(sym.st_name), sym.st_value, sym.st_shndx }); - } -} - -/// Returns pointer-to-symbol described at sym_index. -pub fn symbol(self: *const Elf, sym_index: u32) *elf.Elf64_Sym { - return &self.locals.items[sym_index]; +pub fn atom(self: *Elf, atom_index: Atom.Index) *Atom { + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; } -/// Returns name of the symbol at sym_index. -pub fn symbolName(self: *const Elf, sym_index: u32) []const u8 { - const sym = self.locals.items[sym_index]; - return self.strtab.get(sym.st_name).?; +pub fn addAtom(self: *Elf) !Atom.Index { + const index = @as(Atom.Index, @intCast(self.atoms.items.len)); + const atom_ptr = try self.atoms.addOne(self.base.allocator); + atom_ptr.* = .{ .atom_index = index }; + return index; } -/// Returns pointer to the global entry for `name` if one exists. -pub fn global(self: *const Elf, name: []const u8) ?*u32 { - const global_index = self.resolver.get(name) orelse return null; - return &self.globals.items[global_index]; +pub fn file(self: *Elf, index: File.Index) ?File { + const tag = self.files.items(.tags)[index]; + return switch (tag) { + .null => null, + .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined }, + .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module }, + }; } -/// Returns the index of the global entry for `name` if one exists. -pub fn globalIndex(self: *const Elf, name: []const u8) ?u32 { - return self.resolver.get(name); +/// Returns pointer-to-symbol described at sym_index. +pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol { + return &self.symbols.items[sym_index]; } -/// Returns global entry at `index`. -pub fn globalByIndex(self: *const Elf, index: u32) u32 { - assert(index < self.globals.items.len); - return self.globals.items[index]; +pub fn addSymbol(self: *Elf) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); + const index = blk: { + if (self.symbols_free_list.popOrNull()) |index| { + log.debug(" (reusing symbol index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating symbol index {d})", .{self.symbols.items.len}); + const index = @as(Symbol.Index, @intCast(self.symbols.items.len)); + _ = self.symbols.addOneAssumeCapacity(); + break :blk index; + } + }; + self.symbols.items[index] = .{ .symbol_index = index }; + return index; } const GetOrPutGlobalResult = struct { found_existing: bool, - value_ptr: *u32, + index: Symbol.Index, }; -/// Return pointer to the global entry for `name` if one exists. -/// Puts a new global entry for `name` if one doesn't exist, and -/// returns a pointer to it. -pub fn getOrPutGlobal(self: *Elf, name: []const u8) !GetOrPutGlobalResult { - if (self.global(name)) |ptr| { - return GetOrPutGlobalResult{ .found_existing = true, .value_ptr = ptr }; - } +pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult { const gpa = self.base.allocator; - const global_index = try self.allocateGlobal(); - const global_name = try gpa.dupe(u8, name); - _ = try self.resolver.put(gpa, global_name, global_index); - const ptr = &self.globals.items[global_index]; - return GetOrPutGlobalResult{ .found_existing = false, .value_ptr = ptr }; -} - -pub fn atom(self: *Elf, atom_index: Atom.Index) *Atom { - assert(atom_index < self.atoms.items.len); - return &self.atoms.items[atom_index]; + const gop = try self.resolver.getOrPut(gpa, name_off); + if (!gop.found_existing) { + const index = try self.addSymbol(); + const global = self.symbol(index); + global.name_offset = name_off; + gop.value_ptr.* = index; + } + return .{ + .found_existing = gop.found_existing, + .index = gop.value_ptr.*, + }; } -/// Returns atom if there is an atom referenced by the symbol. -/// Returns null on failure. -pub fn atomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { - return self.atom_by_index_table.get(sym_index); +pub fn getGlobalByName(self: *Elf, name: []const u8) ?Symbol.Index { + const name_off = self.strtab.getOffset(name) orelse return null; + return self.resolver.get(name_off); } -fn dumpState(self: *Elf ) std.fmt.Formatter(fmtDumpState) { +fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { return .{ .data = self }; } -fn fmtDumpState(self: *Elf, +fn fmtDumpState( + self: *Elf, comptime unused_fmt_string: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { + _ = unused_fmt_string; + _ = options; + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + try writer.print("zig_module({d}) : (zig module)\n", .{index}); + try writer.print("{}\n", .{zig_module.fmtSymtab(self)}); + } + if (self.linker_defined_index) |index| { + const linker_defined = self.file(index).?.linker_defined; + try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); + try writer.print("{}\n", .{linker_defined.fmtSymtab(self)}); + } } -pub const null_sym = elf.Elf64_Sym{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, -}; - const default_entry_addr = 0x8000000; pub const base_tag: link.File.Tag = .elf; @@ -3497,21 +3305,20 @@ const Section = struct { const LazySymbolMetadata = struct { const State = enum { unused, pending_flush, flushed }; - text_atom: Atom.Index = undefined, - rodata_atom: Atom.Index = undefined, + text_symbol_index: Symbol.Index = undefined, + rodata_symbol_index: Symbol.Index = undefined, text_state: State = .unused, rodata_state: State = .unused, }; const DeclMetadata = struct { - atom: Atom.Index, - shdr: u16, + symbol_index: Symbol.Index, /// A list of all exports aliases of this Decl. - exports: std.ArrayListUnmanaged(u32) = .{}, + exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, - fn @"export"(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?*u32 { + fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { for (m.exports.items) |*exp| { - if (mem.eql(u8, name, elf_file.symbolName(exp.*))) return exp; + if (mem.eql(u8, name, elf_file.symbol(exp.*).name(elf_file))) return exp; } return null; } @@ -3543,13 +3350,14 @@ pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); -const File = @import("Elf/File.zig"); +const File = @import("Elf/file.zig").File; const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); const Package = @import("../Package.zig"); +const Symbol = @import("Elf/Symbol.zig"); const StringTable = @import("strtab.zig").StringTable; const TableSection = @import("table_section.zig").TableSection; const Type = @import("../type.zig").Type; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index aa1d3920fb..faebd0b6fa 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -1,81 +1,65 @@ -/// Each decl always gets a local symbol with the fully qualified name. -/// The vaddr and size are found here directly. -/// The file offset is found by computing the vaddr offset from the section vaddr -/// the symbol references, and adding that to the file offset of the section. -/// If this field is 0, it means the codegen size = 0 and there is no symbol or -/// offset table entry. -sym_index: u32 = 0, +/// Address allocated for this Atom. +value: u64 = 0, -/// Points to the previous and next neighbors, based on the `text_offset`. -/// This can be used to find, for example, the capacity of this `TextBlock`. -prev_index: ?Index = null, -next_index: ?Index = null, +/// Name of this Atom. +name_offset: u32 = 0, -pub const Index = u32; +/// Index into linker's input file table. +file_index: File.Index = 0, -pub const Reloc = struct { - target: u32, - offset: u64, - addend: u32, - prev_vaddr: u64, -}; +/// Size of this atom +size: u64 = 0, -pub fn symbolIndex(self: *const Atom) ?u32 { - if (self.sym_index == 0) return null; - return self.sym_index; -} +/// Alignment of this atom as a power of two. +alignment: u8 = 0, -pub fn symbol(self: *const Atom, elf_file: *Elf) *elf.Elf64_Sym { - return elf_file.symbol(self.symbolIndex().?); -} +/// Index of the input section. +input_section_index: u16 = 0, -pub fn name(self: *const Atom, elf_file: *Elf) []const u8 { - return elf_file.symbolName(self.symbolIndex().?); -} +/// Index of the output section. +output_section_index: u16 = 0, -/// If entry already exists, returns index to it. -/// Otherwise, creates a new entry in the Global Offset Table for this Atom. -pub fn getOrCreateOffsetTableEntry(self: *const Atom, elf_file: *Elf) !u32 { - const sym_index = self.symbolIndex().?; - if (elf_file.got_table.lookup.get(sym_index)) |index| return index; - const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, sym_index); - elf_file.got_table_count_dirty = true; - return index; -} +/// Index of the input section containing this atom's relocs. +relocs_section_index: u16 = 0, -pub fn getOffsetTableAddress(self: *const Atom, elf_file: *Elf) u64 { - const sym_index = self.symbolIndex().?; - const got_entry_index = elf_file.got_table.lookup.get(sym_index).?; - const target = elf_file.base.options.target; - const ptr_bits = target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; - return got.p_vaddr + got_entry_index * ptr_bytes; +/// Index of this atom in the linker's atoms table. +atom_index: Index = 0, + +/// Specifies whether this atom is alive or has been garbage collected. +alive: bool = true, + +/// Specifies if the atom has been visited during garbage collection. +visited: bool = false, + +/// Start index of FDEs referencing this atom. +fde_start: u32 = 0, + +/// End index of FDEs referencing this atom. +fde_end: u32 = 0, + +/// Points to the previous and next neighbors, based on the `text_offset`. +/// This can be used to find, for example, the capacity of this `TextBlock`. +prev_index: ?Index = null, +next_index: ?Index = null, + +pub fn name(self: Atom, elf_file: *Elf) []const u8 { + return elf_file.strtab.getAssumeExists(self.name_offset); } /// Returns how much room there is to grow in virtual address space. /// File offset relocation happens transparently, so it is not included in /// this calculation. -pub fn capacity(self: *const Atom, elf_file: *Elf) u64 { - const self_sym = self.symbol(elf_file); - if (self.next_index) |next_index| { - const next = elf_file.atom(next_index); - const next_sym = next.symbol(elf_file); - return next_sym.st_value - self_sym.st_value; - } else { - // We are the last block. The capacity is limited only by virtual address space. - return std.math.maxInt(u32) - self_sym.st_value; - } +pub fn capacity(self: Atom, elf_file: *Elf) u64 { + const next_value = if (self.next_index) |next_index| elf_file.atom(next_index).value else std.math.maxInt(u32); + return next_value - self.value; } -pub fn freeListEligible(self: *const Atom, elf_file: *Elf) bool { +pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { // No need to keep a free list node for the last block. const next_index = self.next_index orelse return false; const next = elf_file.atom(next_index); - const self_sym = self.symbol(elf_file); - const next_sym = next.symbol(elf_file); - const cap = next_sym.st_value - self_sym.st_value; - const ideal_cap = Elf.padToIdeal(self_sym.st_size); + const cap = next.value - self.value; + const ideal_cap = Elf.padToIdeal(self.size); if (cap <= ideal_cap) return false; const surplus = cap - ideal_cap; return surplus >= Elf.min_text_capacity; @@ -95,10 +79,149 @@ pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); } -const Atom = @This(); +pub fn allocate(self: *Atom, elf_file: *Elf) !u64 { + const phdr_index = elf_file.sections.items(.phdr_index)[self.output_section_index]; + const phdr = &elf_file.program_headers.items[phdr_index]; + const shdr = &elf_file.sections.items(.shdr)[self.output_section_index]; + const free_list = &elf_file.sections.items(.free_list)[self.output_section_index]; + const maybe_last_atom_index = &elf_file.sections.items(.last_atom_index)[self.output_section_index]; + const new_atom_ideal_capacity = Elf.padToIdeal(self.size); + const alignment = try std.math.powi(u64, 2, self.alignment); + + // We use these to indicate our intention to update metadata, placing the new atom, + // and possibly removing a free list node. + // It would be simpler to do it inside the for loop below, but that would cause a + // problem if an error was returned later in the function. So this action + // is actually carried out at the end of the function, when errors are no longer possible. + var atom_placement: ?Atom.Index = null; + var free_list_removal: ?usize = null; + + // First we look for an appropriately sized free list node. + // The list is unordered. We'll just take the first thing that works. + const vaddr = blk: { + var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len; + while (i < free_list.items.len) { + const big_atom_index = free_list.items[i]; + const big_atom = elf_file.atom(big_atom_index); + // We now have a pointer to a live atom that has too much capacity. + // Is it enough that we could fit this new atom? + const cap = big_atom.capacity(elf_file); + const ideal_capacity = Elf.padToIdeal(cap); + const ideal_capacity_end_vaddr = std.math.add(u64, big_atom.value, ideal_capacity) catch ideal_capacity; + const capacity_end_vaddr = big_atom.value + cap; + const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; + const new_start_vaddr = std.mem.alignBackward(u64, new_start_vaddr_unaligned, alignment); + if (new_start_vaddr < ideal_capacity_end_vaddr) { + // Additional bookkeeping here to notice if this free list node + // should be deleted because the block that it points to has grown to take up + // more of the extra capacity. + if (!big_atom.freeListEligible(elf_file)) { + _ = free_list.swapRemove(i); + } else { + i += 1; + } + continue; + } + // At this point we know that we will place the new block here. But the + // remaining question is whether there is still yet enough capacity left + // over for there to still be a free list node. + const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; + const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity; + + // Set up the metadata to be updated, after errors are no longer possible. + atom_placement = big_atom_index; + if (!keep_free_list_node) { + free_list_removal = i; + } + break :blk new_start_vaddr; + } else if (maybe_last_atom_index.*) |last_index| { + const last = elf_file.atom(last_index); + const ideal_capacity = Elf.padToIdeal(last.size); + const ideal_capacity_end_vaddr = last.value + ideal_capacity; + const new_start_vaddr = std.mem.alignForward(u64, ideal_capacity_end_vaddr, alignment); + // Set up the metadata to be updated, after errors are no longer possible. + atom_placement = last_index; + break :blk new_start_vaddr; + } else { + break :blk phdr.p_vaddr; + } + }; + + const expand_section = if (atom_placement) |placement_index| + elf_file.atom(placement_index).next_index == null + else + true; + if (expand_section) { + const needed_size = (vaddr + self.size) - phdr.p_vaddr; + try elf_file.growAllocSection(self.output_section_index, needed_size); + maybe_last_atom_index.* = self.atom_index; + + if (elf_file.dwarf) |_| { + // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address + // range of the compilation unit. When we expand the text section, this range changes, + // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. + elf_file.debug_info_header_dirty = true; + // This becomes dirty for the same reason. We could potentially make this more + // fine-grained with the addition of support for more compilation units. It is planned to + // model each package as a different compilation unit. + elf_file.debug_aranges_section_dirty = true; + } + } + shdr.sh_addralign = @max(shdr.sh_addralign, alignment); + + // This function can also reallocate an atom. + // In this case we need to "unplug" it from its previous location before + // plugging it in to its new location. + if (self.prev_index) |prev_index| { + const prev = elf_file.atom(prev_index); + prev.next_index = self.next_index; + } + if (self.next_index) |next_index| { + const next = elf_file.atom(next_index); + next.prev_index = self.prev_index; + } + + if (atom_placement) |big_atom_index| { + const big_atom = elf_file.atom(big_atom_index); + self.prev_index = big_atom_index; + self.next_index = big_atom.next_index; + big_atom.next_index = self.atom_index; + } else { + self.prev_index = null; + self.next_index = null; + } + if (free_list_removal) |i| { + _ = free_list.swapRemove(i); + } + return vaddr; +} + +pub fn shrink(self: *Atom, elf_file: *Elf) void { + _ = self; + _ = elf_file; +} + +pub fn grow(self: *Atom, elf_file: *Elf) !u64 { + const alignment = try std.math.powi(u64, 2, self.alignment); + const align_ok = std.mem.alignBackward(u64, self.value, alignment) == self.value; + const need_realloc = !align_ok or self.size > self.capacity(elf_file); + if (!need_realloc) return self.value; + return self.allocate(elf_file); +} + +pub const Index = u32; + +pub const Reloc = struct { + target: u32, + offset: u64, + addend: u32, + prev_vaddr: u64, +}; const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; +const Atom = @This(); const Elf = @import("../Elf.zig"); +const File = @import("file.zig").File; diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index e7cb35b769..99125a9eb6 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -23,14 +23,14 @@ pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 .st_size = 0, }); const off = try elf_file.internString("{s}", .{name}); - const gop = try elf_file.getOrCreateGlobal(off); + const gop = try elf_file.getOrPutGlobal(off); self.symbols.addOneAssumeCapacity().* = gop.index; return gop.index; } pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { for (self.symbols.items, 0..) |index, i| { - const sym_idx = @as(u32, @intCast(i)); + const sym_idx = @as(Symbol.Index, @intCast(i)); const this_sym = self.symtab.items[sym_idx]; if (this_sym.st_shndx == elf.SHN_UNDEF) continue; @@ -86,15 +86,19 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { // } // } -pub fn asFile(self: *LinkerDefined) File { - return .{ .linker_defined = self }; +pub fn sourceSymbol(self: *LinkerDefined, symbol_index: Symbol.Index) *elf.Elf64_Sym { + return &self.symtab.items[symbol_index]; } -pub inline fn getGlobals(self: *LinkerDefined) []const u32 { +pub fn globals(self: *LinkerDefined) []const Symbol.Index { return self.symbols.items; } -pub fn fmtSymtab(self: *InternalObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { +pub fn asFile(self: *LinkerDefined) File { + return .{ .linker_defined = self }; +} + +pub fn fmtSymtab(self: *LinkerDefined, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { return .{ .data = .{ .self = self, .elf_file = elf_file, @@ -102,7 +106,7 @@ pub fn fmtSymtab(self: *InternalObject, elf_file: *Elf) std.fmt.Formatter(format } const FormatContext = struct { - self: *InternalObject, + self: *LinkerDefined, elf_file: *Elf, }; @@ -115,8 +119,8 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; try writer.writeAll(" globals\n"); - for (ctx.self.getGlobals()) |index| { - const global = ctx.elf_file.getSymbol(index); + for (ctx.self.globals()) |index| { + const global = ctx.elf_file.symbol(index); try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index ffbe91aab9..79934840c2 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -32,9 +32,8 @@ extra_index: u32 = 0, pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file_ptr = symbol.file(elf_file).?; - if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; - return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.shndx == 0 - and file_ptr != .linker_defined and file_ptr != .zig_module; + // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; + return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and file_ptr != .linker_defined and file_ptr != .zig_module; } pub fn isLocal(symbol: Symbol) bool { @@ -42,34 +41,30 @@ pub fn isLocal(symbol: Symbol) bool { } pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { - return symbol.@"type"(elf_file) == elf.STT_GNU_IFUNC; + return symbol.type(elf_file) == elf.STT_GNU_IFUNC; } pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { - const file_ptr = symbol.file(elf_file).?; const s_sym = symbol.sourceSymbol(elf_file); - if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC; + // const file_ptr = symbol.file(elf_file).?; + // if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC; return s_sym.st_type(); } pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 { - return elf_file.strtab.getAssumeExists(symbol.name); + return elf_file.strtab.getAssumeExists(symbol.name_offset); } pub fn atom(symbol: Symbol, elf_file: *Elf) ?*Atom { - return elf_file.atom(symbol.atom); + return elf_file.atom(symbol.atom_index); } pub fn file(symbol: Symbol, elf_file: *Elf) ?File { - return elf_file.file(symbol.file); + return elf_file.file(symbol.file_index); } -pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { - const file_ptr = symbol.file(elf_file).?; - return switch (file_ptr) { - .linker_defined, .zig_module => |x| x.symtab.items[symbol.sym_idx], - inline else => |x| x.symtab[symbol.sym_idx], - }; +pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) *elf.Elf64_Sym { + return symbol.file(elf_file).?.sourceSymbol(symbol.symbol_index); } pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { @@ -82,9 +77,29 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { return file_ptr.symbolRank(sym, in_archive); } +/// If entry already exists, returns index to it. +/// Otherwise, creates a new entry in the Global Offset Table for this Symbol. +pub fn getOrCreateOffsetTableEntry(self: Symbol, elf_file: *Elf) !Symbol.Index { + if (elf_file.got_table.lookup.get(self.symbol_index)) |index| return index; + const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, self.symbol_index); + elf_file.got_table_count_dirty = true; + return index; +} + +pub fn getOffsetTableAddress(self: Symbol, elf_file: *Elf) u64 { + const got_entry_index = elf_file.got_table.lookup.get(self.symbol_index).?; + const target = elf_file.base.options.target; + const ptr_bits = target.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; + return got.p_vaddr + got_entry_index * ptr_bytes; +} + pub fn address(symbol: Symbol, opts: struct { plt: bool = true, }, elf_file: *Elf) u64 { + _ = elf_file; + _ = opts; // if (symbol.flags.copy_rel) { // return elf_file.sectionAddress(elf_file.copy_rel_sect_index.?) + symbol.value; // } @@ -100,11 +115,11 @@ pub fn address(symbol: Symbol, opts: struct { return symbol.value; } -pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { - if (!symbol.flags.got) return 0; - const extra = symbol.extra(elf_file).?; - return elf_file.gotEntryAddress(extra.got); -} +// pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { +// if (!symbol.flags.got) return 0; +// const extra = symbol.extra(elf_file).?; +// return elf_file.gotEntryAddress(extra.got); +// } // pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 { // if (!symbol.flags.tlsgd) return 0; @@ -136,22 +151,22 @@ pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { // @min(alignment, try std.math.powi(u64, 2, @ctz(s_sym.st_value))); // } -pub fn addExtra(symbol: *Symbol, extra: Extra, elf_file: *Elf) !void { - symbol.extra = try elf_file.addSymbolExtra(extra); +pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void { + symbol.extra = try elf_file.addSymbolExtra(extras); } pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra { - return elf_file.symbolExtra(symbol.extra); + return elf_file.symbolExtra(symbol.extra_index); } -pub fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void { - elf_file.setSymbolExtra(symbol.extra, extra); +pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void { + elf_file.setSymbolExtra(symbol.extra_index, extras); } pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; const s_sym = symbol.sourceSymbol(elf_file); - const st_type = symbol.@"type"(elf_file); + const st_type = symbol.type(elf_file); const st_bind: u8 = blk: { if (symbol.isLocal()) break :blk 0; if (symbol.flags.weak) break :blk elf.STB_WEAK; @@ -161,7 +176,7 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { const st_shndx = blk: { // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?; // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; - if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) + if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) break :blk elf.SHN_ABS; break :blk symbol.shndx; }; @@ -221,13 +236,13 @@ fn formatName( _ = unused_fmt_string; const elf_file = ctx.elf_file; const symbol = ctx.symbol; - try writer.writeAll(symbol.getName(elf_file)); - switch (symbol.ver_idx & elf.VERSYM_VERSION) { + try writer.writeAll(symbol.name(elf_file)); + switch (symbol.version_index & elf.VERSYM_VERSION) { elf.VER_NDX_LOCAL, elf.VER_NDX_GLOBAL => {}, else => { unreachable; // const shared = symbol.getFile(elf_file).?.shared; - // try writer.print("@{s}", .{shared.getVersionString(symbol.ver_idx)}); + // try writer.print("@{s}", .{shared.getVersionString(symbol.version_index)}); }, } } @@ -248,29 +263,27 @@ fn format2( _ = options; _ = unused_fmt_string; const symbol = ctx.symbol; - try writer.print("%{d} : {s} : @{x}", .{ symbol.sym_idx, symbol.fmtName(ctx.elf_file), symbol.value }); - if (symbol.getFile(ctx.elf_file)) |file| { + try writer.print("%{d} : {s} : @{x}", .{ symbol.symbol_index, symbol.fmtName(ctx.elf_file), symbol.value }); + if (symbol.file(ctx.elf_file)) |file_ptr| { if (symbol.isAbs(ctx.elf_file)) { - if (symbol.getSourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { + if (symbol.sourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { try writer.writeAll(" : undef"); } else { try writer.writeAll(" : absolute"); } - } else if (symbol.shndx != 0) { - try writer.print(" : sect({d})", .{symbol.shndx}); + } else if (symbol.output_section_index != 0) { + try writer.print(" : sect({d})", .{symbol.output_section_index}); } - if (symbol.getAtom(ctx.elf_file)) |atom| { - try writer.print(" : atom({d})", .{atom.atom_index}); + if (symbol.atom(ctx.elf_file)) |atom_ptr| { + try writer.print(" : atom({d})", .{atom_ptr.atom_index}); } var buf: [2]u8 = .{'_'} ** 2; if (symbol.flags.@"export") buf[0] = 'E'; if (symbol.flags.import) buf[1] = 'I'; try writer.print(" : {s}", .{&buf}); if (symbol.flags.weak) try writer.writeAll(" : weak"); - switch (file) { - .internal => |x| try writer.print(" : internal({d})", .{x.index}), - .object => |x| try writer.print(" : object({d})", .{x.index}), - .shared => |x| try writer.print(" : shared({d})", .{x.index}), + switch (file_ptr) { + inline else => |x| try writer.print(" : {s}({d})", .{ @tagName(file_ptr), x.index }), } } else try writer.writeAll(" : unresolved"); } @@ -331,7 +344,8 @@ const elf = std.elf; const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; -const InternalObject = @import("InternalObject.zig"); -const Object = @import("Object.zig"); -const SharedObject = @import("SharedObject.zig"); +const LinkerDefined = @import("LinkerDefined.zig"); +// const Object = @import("Object.zig"); +// const SharedObject = @import("SharedObject.zig"); const Symbol = @This(); +const ZigModule = @import("ZigModule.zig"); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index b76990a29b..ebfcb6c9e4 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -1,29 +1,89 @@ index: File.Index, -elf_locals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -locals: std.ArrayListUnmanaged(Symbol.Index) = .{}, -elf_globals: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -globals: std.ArrayListUnmanaged(Symbol.Index) = .{}, + +elf_local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +local_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, + +elf_global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, + +atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, + alive: bool = true, // output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *ZigModule, allocator: Allocator) void { - self.elf_locals.deinit(allocator); - self.locals.deinit(allocator); - self.elf_globals.deinit(allocator); - self.globals.deinit(allocator); + self.elf_local_symbols.deinit(allocator); + self.local_symbols.deinit(allocator); + self.elf_global_symbols.deinit(allocator); + self.global_symbols.deinit(allocator); + self.atoms.deinit(allocator); } -pub fn asFile(self: *ZigModule) File { - return .{ .zig_module = self }; +pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { + const gpa = elf_file.base.allocator; + const atom_index = try elf_file.addAtom(); + const symbol_index = try elf_file.addSymbol(); + + const atom_ptr = elf_file.atom(atom_index); + atom_ptr.file_index = self.index; + atom_ptr.output_section_index = output_section_index; + + const symbol_ptr = elf_file.symbol(symbol_index); + symbol_ptr.file_index = self.index; + symbol_ptr.atom_index = atom_index; + symbol_ptr.output_section_index = output_section_index; + + const local_esym = try self.elf_local_symbols.addOne(gpa); + local_esym.* = .{ + .st_name = 0, + .st_info = elf.STB_LOCAL << 4, + .st_other = 0, + .st_shndx = output_section_index, + .st_value = 0, + .st_size = 0, + }; + + try self.atoms.append(gpa, atom_index); + try self.local_symbols.putNoClobber(gpa, symbol_index, {}); + + return symbol_index; +} + +pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.Index { + const gpa = elf_file.base.allocator; + try self.elf_global_symbols.ensureUnusedCapacity(gpa, 1); + try self.global_symbols.ensureUnusedCapacity(gpa, 1); + const off = try elf_file.strtab.insert(gpa, name); + self.elf_global_symbols.appendAssumeCapacity(.{ + .st_name = off, + .st_info = elf.STB_GLOBAL << 4, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, + }); + const gop = try elf_file.getOrPutGlobal(off); + self.global_symbols.putAssumeCapacityNoClobber(gop.index, {}); + return gop.index; +} + +pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index) *elf.Elf64_Sym { + if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[symbol_index]; + assert(self.global_symbols.get(symbol_index) != null); + return &self.elf_global_symbols.items[symbol_index]; } -pub fn getLocals(self: *ZigModule) []const Symbol.Index { - return self.locals.items; +pub fn locals(self: *ZigModule) []const Symbol.Index { + return self.local_symbols.keys(); } -pub fn getGlobals(self: *ZigModule) []const Symbol.Index { - return self.globals.items; +pub fn globals(self: *ZigModule) []const Symbol.Index { + return self.global_symbols.keys(); +} + +pub fn asFile(self: *ZigModule) File { + return .{ .zig_module = self }; } pub fn fmtSymtab(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { @@ -47,23 +107,26 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; try writer.writeAll(" locals\n"); - for (ctx.self.getLocals()) |index| { + for (ctx.self.locals()) |index| { const local = ctx.elf_file.symbol(index); try writer.print(" {}\n", .{local.fmt(ctx.elf_file)}); } try writer.writeAll(" globals\n"); - for (ctx.self.getGlobals()) |index| { - const global = ctx.elf_file.getSymbol(index); + for (ctx.self.globals()) |index| { + const global = ctx.elf_file.symbol(index); try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); } } +const assert = std.debug.assert; const std = @import("std"); const elf = std.elf; const Allocator = std.mem.Allocator; +const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const Module = @import("../../Module.zig"); const ZigModule = @This(); // const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 25624389ed..45a063cda5 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -10,6 +10,12 @@ pub const File = union(enum) { }; } + pub fn sourceSymbol(file: File, symbol_index: Symbol.Index) *elf.Elf64_Sym { + return switch (file) { + inline else => |x| x.sourceSymbol(symbol_index), + }; + } + pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { return .{ .data = file }; } @@ -107,4 +113,5 @@ const Elf = @import("../Elf.zig"); const LinkerDefined = @import("LinkerDefined.zig"); // const Object = @import("Object.zig"); // const SharedObject = @import("SharedObject.zig"); +const Symbol = @import("Symbol.zig"); const ZigModule = @import("ZigModule.zig"); diff --git a/src/link/strtab.zig b/src/link/strtab.zig index 0d71c9bf83..f854225ef6 100644 --- a/src/link/strtab.zig +++ b/src/link/strtab.zig @@ -100,13 +100,13 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type { }); } - pub fn get(self: Self, off: u32) ?[]const u8 { + pub fn get(self: Self, off: u32) ?[:0]const u8 { log.debug("getting string at 0x{x}", .{off}); if (off >= self.buffer.items.len) return null; return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.buffer.items.ptr + off)), 0); } - pub fn getAssumeExists(self: Self, off: u32) []const u8 { + pub fn getAssumeExists(self: Self, off: u32) [:0]const u8 { return self.get(off) orelse unreachable; } -- cgit v1.2.3 From 93120a81fee404e3ae90c5ea6d0ad2e71037bee0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 6 Sep 2023 14:42:32 +0200 Subject: elf: lift-off - get it to compile and run until the error! --- src/link/Elf.zig | 173 +++++++++++++++++---------------------------- src/link/Elf/Atom.zig | 71 +++++++++++++++++-- src/link/Elf/Symbol.zig | 20 ++++-- src/link/Elf/ZigModule.zig | 12 +++- src/link/Elf/file.zig | 6 -- 5 files changed, 151 insertions(+), 131 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 6539946ca1..e151846e5a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -105,7 +105,7 @@ atoms: std.ArrayListUnmanaged(Atom) = .{}, /// /// value assigned to label `foo` is an unnamed constant belonging/associated /// with `Decl` `main`, and lives as long as that `Decl`. -unnamed_const_atoms: UnnamedConstTable = .{}, +unnamed_consts: UnnamedConstTable = .{}, /// A table of relocations indexed by the owning them `TextBlock`. relocs: RelocTable = .{}, @@ -251,11 +251,11 @@ pub fn deinit(self: *Elf) void { self.lazy_syms.deinit(gpa); { - var it = self.unnamed_const_atoms.valueIterator(); - while (it.next()) |atoms| { - atoms.deinit(gpa); + var it = self.unnamed_consts.valueIterator(); + while (it.next()) |syms| { + syms.deinit(gpa); } - self.unnamed_const_atoms.deinit(gpa); + self.unnamed_consts.deinit(gpa); } { @@ -279,7 +279,7 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link. const vaddr = this_sym.value; const parent_atom_index = self.symbol(reloc_info.parent_atom_index).atom_index; try Atom.addRelocation(self, parent_atom_index, .{ - .target = this_sym, + .target = this_sym_index, .offset = reloc_info.offset, .addend = reloc_info.addend, .prev_vaddr = vaddr, @@ -830,6 +830,12 @@ pub fn populateMissingMetadata(self: *Elf) !void { try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset); } + + if (self.zig_module_index == null) { + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + self.files.set(index, .{ .zig_module = .{ .index = index } }); + self.zig_module_index = index; + } } pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { @@ -967,12 +973,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; _ = module; - self.zig_module_index = blk: { - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); - self.files.set(index, .{ .zig_module = .{ .index = index } }); - break :blk index; - }; - self.linker_defined_index = blk: { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .linker_defined = .{ .index = index } }); @@ -980,6 +980,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node }; std.debug.print("{}\n", .{self.dumpState()}); + return error.FlushFailure; // if (self.lazy_syms.getPtr(.none)) |metadata| { // // Most lazy symbols can be updated on first use, but @@ -2078,78 +2079,21 @@ fn writeElfHeader(self: *Elf) !void { try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); } -fn freeAtom(self: *Elf, atom_index: Atom.Index) void { - const atom_ptr = self.atom(atom_index); - log.debug("freeAtom {d} ({s})", .{ atom_index, atom_ptr.name(self) }); - - Atom.freeRelocations(self, atom_index); - - const gpa = self.base.allocator; - const shndx = atom_ptr.symbol(self).st_shndx; - const free_list = &self.sections.items(.free_list)[shndx]; - var already_have_free_list_node = false; - { - var i: usize = 0; - // TODO turn free_list into a hash map - while (i < free_list.items.len) { - if (free_list.items[i] == atom_index) { - _ = free_list.swapRemove(i); - continue; - } - if (free_list.items[i] == atom_ptr.prev_index) { - already_have_free_list_node = true; - } - i += 1; - } - } - - const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx]; - if (maybe_last_atom_index.*) |last_atom_index| { - if (last_atom_index == atom_index) { - if (atom_ptr.prev_index) |prev_index| { - // TODO shrink the section size here - maybe_last_atom_index.* = prev_index; - } else { - maybe_last_atom_index.* = null; - } - } - } - - if (atom_ptr.prev_index) |prev_index| { - const prev = self.atom(prev_index); - prev.next_index = atom_ptr.next_index; - - if (!already_have_free_list_node and prev.*.freeListEligible(self)) { - // The free list is heuristics, it doesn't have to be perfect, so we can - // ignore the OOM here. - free_list.append(gpa, prev_index) catch {}; - } - } else { - atom_ptr.prev_index = null; - } - - if (atom_ptr.next_index) |next_index| { - self.atom(next_index).prev_index = atom_ptr.prev_index; - } else { - atom_ptr.next_index = null; +fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { + const unnamed_consts = self.unnamed_consts.getPtr(decl_index) orelse return; + for (unnamed_consts.items) |sym_index| { + self.freeDeclMetadata(sym_index); } + unnamed_consts.clearAndFree(self.base.allocator); +} - // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. - const sym_index = atom_ptr.symbolIndex().?; - +fn freeDeclMetadata(self: *Elf, sym_index: Symbol.Index) void { + const sym = self.symbol(sym_index); + sym.atom(self).?.free(self); log.debug("adding %{d} to local symbols free list", .{sym_index}); - self.symbols_free_list.append(gpa, sym_index) catch {}; + self.symbols_free_list.append(self.base.allocator, sym_index) catch {}; self.symbols.items[sym_index] = .{}; - atom_ptr.sym_index = 0; - self.got_table.freeEntry(gpa, sym_index); -} - -fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { - const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; - for (unnamed_consts.items) |atom_index| { - self.freeAtom(atom_index); - } - unnamed_consts.clearAndFree(self.base.allocator); + self.got_table.freeEntry(self.base.allocator, sym_index); } pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { @@ -2162,7 +2106,8 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { if (self.decls.fetchRemove(decl_index)) |const_kv| { var kv = const_kv; - self.freeAtom(kv.value.atom); + const sym_index = kv.value.symbol_index; + self.freeDeclMetadata(sym_index); self.freeUnnamedConsts(decl_index); kv.value.exports.deinit(self.base.allocator); } @@ -2196,7 +2141,7 @@ pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) ! .code => self.text_section_index.?, .const_data => self.rodata_section_index.?, }, self), - .pending_flush => return metadata.atom.*, + .pending_flush => return metadata.symbol_index.*, .flushed => {}, } metadata.state.* = .pending_flush; @@ -2271,6 +2216,7 @@ fn updateDeclCode( esym.st_size = code.len; const old_size = atom_ptr.size; + const old_vaddr = atom_ptr.value; atom_ptr.alignment = math.log2_int(u64, required_alignment); atom_ptr.size = code.len; @@ -2278,11 +2224,11 @@ fn updateDeclCode( const capacity = atom_ptr.capacity(self); const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); if (need_realloc) { - const vaddr = try atom_ptr.grow(self); - log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); - if (vaddr != sym.value) { - sym.value = vaddr; - esym.st_value = vaddr; + try atom_ptr.grow(self); + log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom_ptr.value }); + if (old_vaddr != atom_ptr.value) { + sym.value = atom_ptr.value; + esym.st_value = atom_ptr.value; log.debug(" (writing new offset table entry)", .{}); const got_entry_index = self.got_table.lookup.get(sym_index).?; @@ -2293,12 +2239,16 @@ fn updateDeclCode( atom_ptr.shrink(self); } } else { - const vaddr = try atom_ptr.allocate(self); - errdefer self.freeAtom(atom_ptr); - log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); + try atom_ptr.allocate(self); + errdefer self.freeDeclMetadata(sym_index); + log.debug("allocated atom for {s} at 0x{x} to 0x{x}", .{ + decl_name, + atom_ptr.value, + atom_ptr.value + atom_ptr.size, + }); - sym.value = vaddr; - esym.st_value = vaddr; + sym.value = atom_ptr.value; + esym.st_value = atom_ptr.value; const got_entry_index = try sym.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); @@ -2518,17 +2468,23 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. const atom_ptr = local_sym.atom(self).?; atom_ptr.alignment = math.log2_int(u64, required_alignment); atom_ptr.size = code.len; - const vaddr = try atom_ptr.allocate(self); - errdefer self.freeAtom(atom_ptr); - log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); - local_sym.value = vaddr; - local_esym.st_value = vaddr; + try atom_ptr.allocate(self); + errdefer self.freeDeclMetadata(symbol_index); + + log.debug("allocated atom for {s} at 0x{x} to 0x{x}", .{ + name, + atom_ptr.value, + atom_ptr.value + atom_ptr.size, + }); + + local_sym.value = atom_ptr.value; + local_esym.st_value = atom_ptr.value; const got_entry_index = try local_sym.getOrCreateOffsetTableEntry(self); try self.writeOffsetTableEntry(got_entry_index); - const section_offset = vaddr - self.program_headers.items[phdr_index].p_vaddr; + const section_offset = atom_ptr.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[local_sym.output_section_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); } @@ -2540,7 +2496,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module defer code_buffer.deinit(); const mod = self.base.options.module.?; - const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); + const gop = try self.unnamed_consts.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -2586,17 +2542,18 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const atom_ptr = local_sym.atom(self).?; atom_ptr.alignment = math.log2_int(u64, required_alignment); atom_ptr.size = code.len; - const vaddr = try atom_ptr.allocateAtom(self); - errdefer self.freeAtom(atom_ptr); - log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value }); + try atom_ptr.allocate(self); + errdefer self.freeDeclMetadata(sym_index); + + log.debug("allocated atom for {s} at 0x{x} to 0x{x}", .{ name, atom_ptr.value, atom_ptr.value + atom_ptr.size }); - local_sym.value = vaddr; - local_esym.st_value = vaddr; + local_sym.value = atom_ptr.value; + local_esym.st_value = atom_ptr.value; try unnamed_consts.append(gpa, atom_ptr.atom_index); - const section_offset = local_sym.value - self.program_headers.items[phdr_index].p_vaddr; + const section_offset = atom_ptr.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); @@ -2624,7 +2581,7 @@ pub fn updateDeclExports( const decl = mod.declPtr(decl_index); const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index); const decl_sym = self.symbol(decl_sym_index); - const decl_esym = symbol.sourceSymbol(self); + const decl_esym = decl_sym.sourceSymbol(self); const decl_metadata = self.decls.getPtr(decl_index).?; for (exports) |exp| { @@ -2658,7 +2615,7 @@ pub fn updateDeclExports( continue; }, }; - const stt_bits: u8 = @as(u4, @truncate(decl_sym.st_info)); + const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info)); const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { const zig_module = self.file(self.zig_module_index.?).?.zig_module; @@ -3220,7 +3177,7 @@ pub fn addSymbol(self: *Elf) !Symbol.Index { break :blk index; } }; - self.symbols.items[index] = .{ .symbol_index = index }; + self.symbols.items[index] = .{ .index = index }; return index; } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index faebd0b6fa..8eb9f1af0b 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -79,7 +79,7 @@ pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); } -pub fn allocate(self: *Atom, elf_file: *Elf) !u64 { +pub fn allocate(self: *Atom, elf_file: *Elf) !void { const phdr_index = elf_file.sections.items(.phdr_index)[self.output_section_index]; const phdr = &elf_file.program_headers.items[phdr_index]; const shdr = &elf_file.sections.items(.shdr)[self.output_section_index]; @@ -98,7 +98,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !u64 { // First we look for an appropriately sized free list node. // The list is unordered. We'll just take the first thing that works. - const vaddr = blk: { + self.value = blk: { var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len; while (i < free_list.items.len) { const big_atom_index = free_list.items[i]; @@ -152,7 +152,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !u64 { else true; if (expand_section) { - const needed_size = (vaddr + self.size) - phdr.p_vaddr; + const needed_size = (self.value + self.size) - phdr.p_vaddr; try elf_file.growAllocSection(self.output_section_index, needed_size); maybe_last_atom_index.* = self.atom_index; @@ -193,7 +193,6 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !u64 { if (free_list_removal) |i| { _ = free_list.swapRemove(i); } - return vaddr; } pub fn shrink(self: *Atom, elf_file: *Elf) void { @@ -201,12 +200,69 @@ pub fn shrink(self: *Atom, elf_file: *Elf) void { _ = elf_file; } -pub fn grow(self: *Atom, elf_file: *Elf) !u64 { +pub fn grow(self: *Atom, elf_file: *Elf) !void { const alignment = try std.math.powi(u64, 2, self.alignment); const align_ok = std.mem.alignBackward(u64, self.value, alignment) == self.value; const need_realloc = !align_ok or self.size > self.capacity(elf_file); - if (!need_realloc) return self.value; - return self.allocate(elf_file); + if (need_realloc) try self.allocate(elf_file); +} + +pub fn free(self: *Atom, elf_file: *Elf) void { + log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); + + Atom.freeRelocations(elf_file, self.atom_index); + + const gpa = elf_file.base.allocator; + const shndx = self.output_section_index; + const free_list = &elf_file.sections.items(.free_list)[shndx]; + var already_have_free_list_node = false; + { + var i: usize = 0; + // TODO turn free_list into a hash map + while (i < free_list.items.len) { + if (free_list.items[i] == self.atom_index) { + _ = free_list.swapRemove(i); + continue; + } + if (free_list.items[i] == self.prev_index) { + already_have_free_list_node = true; + } + i += 1; + } + } + + const maybe_last_atom_index = &elf_file.sections.items(.last_atom_index)[shndx]; + if (maybe_last_atom_index.*) |last_atom_index| { + if (last_atom_index == self.atom_index) { + if (self.prev_index) |prev_index| { + // TODO shrink the section size here + maybe_last_atom_index.* = prev_index; + } else { + maybe_last_atom_index.* = null; + } + } + } + + if (self.prev_index) |prev_index| { + const prev = elf_file.atom(prev_index); + prev.next_index = self.next_index; + + if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) { + // The free list is heuristics, it doesn't have to be perfect, so we can + // ignore the OOM here. + free_list.append(gpa, prev_index) catch {}; + } + } else { + self.prev_index = null; + } + + if (self.next_index) |next_index| { + elf_file.atom(next_index).prev_index = self.prev_index; + } else { + self.next_index = null; + } + + self.* = .{}; } pub const Index = u32; @@ -221,6 +277,7 @@ pub const Reloc = struct { const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; +const log = std.log.scoped(.link); const Atom = @This(); const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 79934840c2..0418aa59db 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -1,5 +1,7 @@ //! Represents a defined symbol. +index: Index = 0, + /// Allocated address value of this symbol. value: u64 = 0, @@ -18,8 +20,8 @@ atom_index: Atom.Index = 0, output_section_index: u16 = 0, /// Index of the source symbol this symbol references. -/// Use `getSourceSymbol` to pull the source symbol from the relevant file. -symbol_index: Index = 0, +/// Use `sourceSymbol` to pull the source symbol from the relevant file. +esym_index: Index = 0, /// Index of the source version symbol this symbol references if any. /// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL. @@ -64,7 +66,11 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { } pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) *elf.Elf64_Sym { - return symbol.file(elf_file).?.sourceSymbol(symbol.symbol_index); + const file_ptr = symbol.file(elf_file).?; + switch (file_ptr) { + .zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file), + .linker_defined => return file_ptr.linker_defined.sourceSymbol(symbol.esym_index), + } } pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { @@ -80,14 +86,14 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { /// If entry already exists, returns index to it. /// Otherwise, creates a new entry in the Global Offset Table for this Symbol. pub fn getOrCreateOffsetTableEntry(self: Symbol, elf_file: *Elf) !Symbol.Index { - if (elf_file.got_table.lookup.get(self.symbol_index)) |index| return index; - const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, self.symbol_index); + if (elf_file.got_table.lookup.get(self.index)) |index| return index; + const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, self.index); elf_file.got_table_count_dirty = true; return index; } pub fn getOffsetTableAddress(self: Symbol, elf_file: *Elf) u64 { - const got_entry_index = elf_file.got_table.lookup.get(self.symbol_index).?; + const got_entry_index = elf_file.got_table.lookup.get(self.index).?; const target = elf_file.base.options.target; const ptr_bits = target.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -263,7 +269,7 @@ fn format2( _ = options; _ = unused_fmt_string; const symbol = ctx.symbol; - try writer.print("%{d} : {s} : @{x}", .{ symbol.symbol_index, symbol.fmtName(ctx.elf_file), symbol.value }); + try writer.print("%{d} : {s} : @{x}", .{ symbol.index, symbol.fmtName(ctx.elf_file), symbol.value }); if (symbol.file(ctx.elf_file)) |file_ptr| { if (symbol.isAbs(ctx.elf_file)) { if (symbol.sourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index ebfcb6c9e4..6cf918c8a1 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -33,6 +33,7 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) ! symbol_ptr.file_index = self.index; symbol_ptr.atom_index = atom_index; symbol_ptr.output_section_index = output_section_index; + symbol_ptr.esym_index = @as(Symbol.Index, @intCast(self.elf_local_symbols.items.len)); const local_esym = try self.elf_local_symbols.addOne(gpa); local_esym.* = .{ @@ -55,6 +56,7 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I try self.elf_global_symbols.ensureUnusedCapacity(gpa, 1); try self.global_symbols.ensureUnusedCapacity(gpa, 1); const off = try elf_file.strtab.insert(gpa, name); + const esym_index = @as(Symbol.Index, @intCast(self.elf_global_symbols.items.len)); self.elf_global_symbols.appendAssumeCapacity(.{ .st_name = off, .st_info = elf.STB_GLOBAL << 4, @@ -64,14 +66,18 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I .st_size = 0, }); const gop = try elf_file.getOrPutGlobal(off); + const sym = elf_file.symbol(gop.index); + sym.file_index = self.index; + sym.esym_index = esym_index; self.global_symbols.putAssumeCapacityNoClobber(gop.index, {}); return gop.index; } -pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index) *elf.Elf64_Sym { - if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[symbol_index]; +pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index, elf_file: *Elf) *elf.Elf64_Sym { + const sym = elf_file.symbol(symbol_index); + if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[sym.esym_index]; assert(self.global_symbols.get(symbol_index) != null); - return &self.elf_global_symbols.items[symbol_index]; + return &self.elf_global_symbols.items[sym.esym_index]; } pub fn locals(self: *ZigModule) []const Symbol.Index { diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 45a063cda5..82d2d89c29 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -10,12 +10,6 @@ pub const File = union(enum) { }; } - pub fn sourceSymbol(file: File, symbol_index: Symbol.Index) *elf.Elf64_Sym { - return switch (file) { - inline else => |x| x.sourceSymbol(symbol_index), - }; - } - pub fn fmtPath(file: File) std.fmt.Formatter(formatPath) { return .{ .data = file }; } -- cgit v1.2.3 From a2b5a464e65955f09e3034830ca275ae84ebddea Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 6 Sep 2023 15:12:43 +0200 Subject: elf: set atom names --- src/link/Elf.zig | 138 +++++++++++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e151846e5a..6c7fac1697 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -971,7 +971,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // TODO This linker code currently assumes there is only 1 compilation unit and it // corresponds to the Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; - _ = module; self.linker_defined_index = blk: { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); @@ -979,82 +978,77 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node break :blk index; }; - std.debug.print("{}\n", .{self.dumpState()}); - return error.FlushFailure; - - // if (self.lazy_syms.getPtr(.none)) |metadata| { - // // Most lazy symbols can be updated on first use, but - // // anyerror needs to wait for everything to be flushed. - // if (metadata.text_state != .unused) self.updateLazySymbolAtom( - // link.File.LazySymbol.initDecl(.code, null, module), - // metadata.text_atom, - // self.text_section_index.?, - // ) catch |err| return switch (err) { - // error.CodegenFail => error.FlushFailure, - // else => |e| e, - // }; - // if (metadata.rodata_state != .unused) self.updateLazySymbolAtom( - // link.File.LazySymbol.initDecl(.const_data, null, module), - // metadata.rodata_atom, - // self.rodata_section_index.?, - // ) catch |err| return switch (err) { - // error.CodegenFail => error.FlushFailure, - // else => |e| e, - // }; - // } - // for (self.lazy_syms.values()) |*metadata| { - // if (metadata.text_state != .unused) metadata.text_state = .flushed; - // if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; - // } + if (self.lazy_syms.getPtr(.none)) |metadata| { + // Most lazy symbols can be updated on first use, but + // anyerror needs to wait for everything to be flushed. + if (metadata.text_state != .unused) self.updateLazySymbol( + link.File.LazySymbol.initDecl(.code, null, module), + metadata.text_symbol_index, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + if (metadata.rodata_state != .unused) self.updateLazySymbol( + link.File.LazySymbol.initDecl(.const_data, null, module), + metadata.rodata_symbol_index, + ) catch |err| return switch (err) { + error.CodegenFail => error.FlushFailure, + else => |e| e, + }; + } + for (self.lazy_syms.values()) |*metadata| { + if (metadata.text_state != .unused) metadata.text_state = .flushed; + if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; + } - // const target_endian = self.base.options.target.cpu.arch.endian(); - // const foreign_endian = target_endian != builtin.cpu.arch.endian(); + const target_endian = self.base.options.target.cpu.arch.endian(); + const foreign_endian = target_endian != builtin.cpu.arch.endian(); + _ = foreign_endian; - // if (self.dwarf) |*dw| { - // try dw.flushModule(module); - // } + if (self.dwarf) |*dw| { + try dw.flushModule(module); + } - // { - // var it = self.relocs.iterator(); - // while (it.next()) |entry| { - // const atom_index = entry.key_ptr.*; - // const relocs = entry.value_ptr.*; - // const atom_ptr = self.atom(atom_index); - // const source_sym = atom_ptr.symbol(self); - // const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx]; - - // log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)}); - - // for (relocs.items) |*reloc| { - // const target_sym = self.locals.items[reloc.target]; - // const target_vaddr = target_sym.st_value + reloc.addend; - - // if (target_vaddr == reloc.prev_vaddr) continue; - - // const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; - // const file_offset = source_shdr.sh_offset + section_offset; - - // log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{ - // reloc.offset, - // target_vaddr, - // self.strtab.get(target_sym.st_name), - // }); - - // switch (self.ptr_width) { - // .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset), - // .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset), - // } + { + var it = self.relocs.iterator(); + while (it.next()) |entry| { + const atom_index = entry.key_ptr.*; + const relocs = entry.value_ptr.*; + const atom_ptr = self.atom(atom_index); + const source_shdr = self.sections.items(.shdr)[atom_ptr.output_section_index]; - // reloc.prev_vaddr = target_vaddr; - // } - // } - // } + log.debug("relocating '{s}'", .{atom_ptr.name(self)}); + + for (relocs.items) |*reloc| { + const target_sym = self.symbol(reloc.target); + const target_vaddr = target_sym.value + reloc.addend; + + if (target_vaddr == reloc.prev_vaddr) continue; + + const section_offset = (atom_ptr.value + reloc.offset) - source_shdr.sh_addr; + const file_offset = source_shdr.sh_offset + section_offset; + + log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ + reloc.offset, + target_vaddr, + target_sym.name(self), + }); + + switch (self.ptr_width) { + .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset), + .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset), + } + + reloc.prev_vaddr = target_vaddr; + } + } + } // try self.writeSymbols(); - // if (build_options.enable_logging) { - // self.logSymtab(); - // } + if (build_options.enable_logging) { + state_log.debug("{}", .{self.dumpState()}); + } // if (self.dwarf) |*dw| { // if (self.debug_abbrev_section_dirty) { @@ -2211,6 +2205,7 @@ fn updateDeclCode( const shdr_index = sym.output_section_index; sym.name_offset = try self.strtab.insert(gpa, decl_name); + atom_ptr.name_offset = sym.name_offset; esym.st_name = sym.name_offset; esym.st_info |= stt_bits; esym.st_size = code.len; @@ -2466,6 +2461,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; const atom_ptr = local_sym.atom(self).?; + atom_ptr.name_offset = name_str_index; atom_ptr.alignment = math.log2_int(u64, required_alignment); atom_ptr.size = code.len; @@ -2540,6 +2536,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; const atom_ptr = local_sym.atom(self).?; + atom_ptr.name_offset = name_str_index; atom_ptr.alignment = math.log2_int(u64, required_alignment); atom_ptr.size = code.len; @@ -3290,6 +3287,7 @@ const assert = std.debug.assert; const elf = std.elf; const fs = std.fs; const log = std.log.scoped(.link); +const state_log = std.log.scoped(.link_state); const math = std.math; const mem = std.mem; -- cgit v1.2.3 From 37e2958f8125b08d4acb9175bf0bd30cd9872961 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 6 Sep 2023 23:20:49 +0200 Subject: elf: write symbtab --- src/link/Elf.zig | 428 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 216 insertions(+), 212 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 6c7fac1697..dbf0756edd 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1003,7 +1003,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const target_endian = self.base.options.target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); - _ = foreign_endian; if (self.dwarf) |*dw| { try dw.flushModule(module); @@ -1044,212 +1043,212 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } - // try self.writeSymbols(); + try self.writeSymbols(); if (build_options.enable_logging) { state_log.debug("{}", .{self.dumpState()}); } - // if (self.dwarf) |*dw| { - // if (self.debug_abbrev_section_dirty) { - // try dw.writeDbgAbbrev(); - // if (!self.shdr_table_dirty) { - // // Then it won't get written with the others and we need to do it. - // try self.writeSectHeader(self.debug_abbrev_section_index.?); - // } - // self.debug_abbrev_section_dirty = false; - // } - - // if (self.debug_info_header_dirty) { - // // Currently only one compilation unit is supported, so the address range is simply - // // identical to the main program header virtual address and memory size. - // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - // const low_pc = text_phdr.p_vaddr; - // const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; - // try dw.writeDbgInfoHeader(module, low_pc, high_pc); - // self.debug_info_header_dirty = false; - // } - - // if (self.debug_aranges_section_dirty) { - // // Currently only one compilation unit is supported, so the address range is simply - // // identical to the main program header virtual address and memory size. - // const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - // try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); - // if (!self.shdr_table_dirty) { - // // Then it won't get written with the others and we need to do it. - // try self.writeSectHeader(self.debug_aranges_section_index.?); - // } - // self.debug_aranges_section_dirty = false; - // } - - // if (self.debug_line_header_dirty) { - // try dw.writeDbgLineHeader(); - // self.debug_line_header_dirty = false; - // } - // } - - // if (self.phdr_table_dirty) { - // const phsize: u64 = switch (self.ptr_width) { - // .p32 => @sizeOf(elf.Elf32_Phdr), - // .p64 => @sizeOf(elf.Elf64_Phdr), - // }; - - // const phdr_table_index = self.phdr_table_index.?; - // const phdr_table = &self.program_headers.items[phdr_table_index]; - // const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?]; - - // const allocated_size = self.allocatedSize(phdr_table.p_offset); - // const needed_size = self.program_headers.items.len * phsize; - - // if (needed_size > allocated_size) { - // phdr_table.p_offset = 0; // free the space - // phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align))); - // } - - // phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align); - // const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset; - // phdr_table_load.p_filesz = load_align_offset + needed_size; - // phdr_table_load.p_memsz = load_align_offset + needed_size; - - // phdr_table.p_filesz = needed_size; - // phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset; - // phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset; - // phdr_table.p_memsz = needed_size; - - // switch (self.ptr_width) { - // .p32 => { - // const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); - // defer gpa.free(buf); - - // for (buf, 0..) |*phdr, i| { - // phdr.* = progHeaderTo32(self.program_headers.items[i]); - // if (foreign_endian) { - // mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); - // } - // } - // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); - // }, - // .p64 => { - // const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); - // defer gpa.free(buf); - - // for (buf, 0..) |*phdr, i| { - // phdr.* = self.program_headers.items[i]; - // if (foreign_endian) { - // mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); - // } - // } - // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); - // }, - // } - - // // We don't actually care if the phdr load section overlaps, only the phdr section matters. - // phdr_table_load.p_offset = 0; - // phdr_table_load.p_filesz = 0; - - // self.phdr_table_dirty = false; - // } - - // { - // const shdr_index = self.shstrtab_section_index.?; - // if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { - // try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); - // const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; - // try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); - // self.shstrtab_dirty = false; - // } - // } - - // { - // const shdr_index = self.strtab_section_index.?; - // if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { - // try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false); - // const strtab_sect = self.sections.items(.shdr)[shdr_index]; - // try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset); - // self.strtab_dirty = false; - // } - // } - - // if (self.dwarf) |dwarf| { - // const shdr_index = self.debug_str_section_index.?; - // if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { - // try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); - // const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; - // try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); - // self.debug_strtab_dirty = false; - // } - // } - - // if (self.shdr_table_dirty) { - // const shsize: u64 = switch (self.ptr_width) { - // .p32 => @sizeOf(elf.Elf32_Shdr), - // .p64 => @sizeOf(elf.Elf64_Shdr), - // }; - // const shalign: u16 = switch (self.ptr_width) { - // .p32 => @alignOf(elf.Elf32_Shdr), - // .p64 => @alignOf(elf.Elf64_Shdr), - // }; - // const allocated_size = self.allocatedSize(self.shdr_table_offset.?); - // const needed_size = self.sections.slice().len * shsize; - - // if (needed_size > allocated_size) { - // self.shdr_table_offset = null; // free the space - // self.shdr_table_offset = self.findFreeSpace(needed_size, shalign); - // } - - // switch (self.ptr_width) { - // .p32 => { - // const slice = self.sections.slice(); - // const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); - // defer gpa.free(buf); - - // for (buf, 0..) |*shdr, i| { - // shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); - // log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); - // if (foreign_endian) { - // mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); - // } - // } - // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); - // }, - // .p64 => { - // const slice = self.sections.slice(); - // const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); - // defer gpa.free(buf); - - // for (buf, 0..) |*shdr, i| { - // shdr.* = slice.items(.shdr)[i]; - // log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); - // if (foreign_endian) { - // mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); - // } - // } - // try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); - // }, - // } - // self.shdr_table_dirty = false; - // } - // if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { - // log.debug("flushing. no_entry_point_found = true", .{}); - // self.error_flags.no_entry_point_found = true; - // } else { - // log.debug("flushing. no_entry_point_found = false", .{}); - // self.error_flags.no_entry_point_found = false; - // try self.writeElfHeader(); - // } - - // // The point of flush() is to commit changes, so in theory, nothing should - // // be dirty after this. However, it is possible for some things to remain - // // dirty because they fail to be written in the event of compile errors, - // // such as debug_line_header_dirty and debug_info_header_dirty. - // assert(!self.debug_abbrev_section_dirty); - // assert(!self.debug_aranges_section_dirty); - // assert(!self.phdr_table_dirty); - // assert(!self.shdr_table_dirty); - // assert(!self.shstrtab_dirty); - // assert(!self.strtab_dirty); - // assert(!self.debug_strtab_dirty); - // assert(!self.got_table_count_dirty); + if (self.dwarf) |*dw| { + if (self.debug_abbrev_section_dirty) { + try dw.writeDbgAbbrev(); + if (!self.shdr_table_dirty) { + // Then it won't get written with the others and we need to do it. + try self.writeSectHeader(self.debug_abbrev_section_index.?); + } + self.debug_abbrev_section_dirty = false; + } + + if (self.debug_info_header_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + const low_pc = text_phdr.p_vaddr; + const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; + try dw.writeDbgInfoHeader(module, low_pc, high_pc); + self.debug_info_header_dirty = false; + } + + if (self.debug_aranges_section_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); + if (!self.shdr_table_dirty) { + // Then it won't get written with the others and we need to do it. + try self.writeSectHeader(self.debug_aranges_section_index.?); + } + self.debug_aranges_section_dirty = false; + } + + if (self.debug_line_header_dirty) { + try dw.writeDbgLineHeader(); + self.debug_line_header_dirty = false; + } + } + + if (self.phdr_table_dirty) { + const phsize: u64 = switch (self.ptr_width) { + .p32 => @sizeOf(elf.Elf32_Phdr), + .p64 => @sizeOf(elf.Elf64_Phdr), + }; + + const phdr_table_index = self.phdr_table_index.?; + const phdr_table = &self.program_headers.items[phdr_table_index]; + const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?]; + + const allocated_size = self.allocatedSize(phdr_table.p_offset); + const needed_size = self.program_headers.items.len * phsize; + + if (needed_size > allocated_size) { + phdr_table.p_offset = 0; // free the space + phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align))); + } + + phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align); + const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset; + phdr_table_load.p_filesz = load_align_offset + needed_size; + phdr_table_load.p_memsz = load_align_offset + needed_size; + + phdr_table.p_filesz = needed_size; + phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset; + phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset; + phdr_table.p_memsz = needed_size; + + switch (self.ptr_width) { + .p32 => { + const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); + defer gpa.free(buf); + + for (buf, 0..) |*phdr, i| { + phdr.* = progHeaderTo32(self.program_headers.items[i]); + if (foreign_endian) { + mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); + } + } + try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); + }, + .p64 => { + const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); + defer gpa.free(buf); + + for (buf, 0..) |*phdr, i| { + phdr.* = self.program_headers.items[i]; + if (foreign_endian) { + mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); + } + } + try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); + }, + } + + // We don't actually care if the phdr load section overlaps, only the phdr section matters. + phdr_table_load.p_offset = 0; + phdr_table_load.p_filesz = 0; + + self.phdr_table_dirty = false; + } + + { + const shdr_index = self.shstrtab_section_index.?; + if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); + const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; + try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); + self.shstrtab_dirty = false; + } + } + + { + const shdr_index = self.strtab_section_index.?; + if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false); + const strtab_sect = self.sections.items(.shdr)[shdr_index]; + try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset); + self.strtab_dirty = false; + } + } + + if (self.dwarf) |dwarf| { + const shdr_index = self.debug_str_section_index.?; + if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); + const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; + try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); + self.debug_strtab_dirty = false; + } + } + + if (self.shdr_table_dirty) { + const shsize: u64 = switch (self.ptr_width) { + .p32 => @sizeOf(elf.Elf32_Shdr), + .p64 => @sizeOf(elf.Elf64_Shdr), + }; + const shalign: u16 = switch (self.ptr_width) { + .p32 => @alignOf(elf.Elf32_Shdr), + .p64 => @alignOf(elf.Elf64_Shdr), + }; + const allocated_size = self.allocatedSize(self.shdr_table_offset.?); + const needed_size = self.sections.slice().len * shsize; + + if (needed_size > allocated_size) { + self.shdr_table_offset = null; // free the space + self.shdr_table_offset = self.findFreeSpace(needed_size, shalign); + } + + switch (self.ptr_width) { + .p32 => { + const slice = self.sections.slice(); + const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); + defer gpa.free(buf); + + for (buf, 0..) |*shdr, i| { + shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); + log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); + if (foreign_endian) { + mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); + } + } + try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); + }, + .p64 => { + const slice = self.sections.slice(); + const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); + defer gpa.free(buf); + + for (buf, 0..) |*shdr, i| { + shdr.* = slice.items(.shdr)[i]; + log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); + if (foreign_endian) { + mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); + } + } + try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); + }, + } + self.shdr_table_dirty = false; + } + if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { + log.debug("flushing. no_entry_point_found = true", .{}); + self.error_flags.no_entry_point_found = true; + } else { + log.debug("flushing. no_entry_point_found = false", .{}); + self.error_flags.no_entry_point_found = false; + try self.writeElfHeader(); + } + + // The point of flush() is to commit changes, so in theory, nothing should + // be dirty after this. However, it is possible for some things to remain + // dirty because they fail to be written in the event of compile errors, + // such as debug_line_header_dirty and debug_info_header_dirty. + assert(!self.debug_abbrev_section_dirty); + assert(!self.debug_aranges_section_dirty); + assert(!self.phdr_table_dirty); + assert(!self.shdr_table_dirty); + assert(!self.shstrtab_dirty); + assert(!self.strtab_dirty); + assert(!self.debug_strtab_dirty); + assert(!self.got_table_count_dirty); } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2795,12 +2794,13 @@ fn writeSymbols(self: *Elf) !void { .p32 => @alignOf(elf.Elf32_Sym), .p64 => @alignOf(elf.Elf64_Sym), }; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; - shdr.sh_info = @intCast(self.locals.items.len); + shdr.sh_info = @intCast(zig_module.locals().len); self.markDirty(self.symtab_section_index.?, null); - const nsyms = self.locals.items.len + self.globals.items.len; + const nsyms = zig_module.locals().len + zig_module.globals().len; const needed_size = nsyms * sym_size; try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true); @@ -2811,15 +2811,17 @@ fn writeSymbols(self: *Elf) !void { const buf = try gpa.alloc(elf.Elf32_Sym, nsyms); defer gpa.free(buf); - for (buf[0..self.locals.items.len], self.locals.items) |*sym, local| { - elf32SymFromSym(local, sym); + for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| { + const local = self.symbol(local_index); + elf32SymFromSym(local.sourceSymbol(self).*, sym); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Sym, sym); } } - for (buf[self.locals.items.len..], self.globals.items) |*sym, glob| { - elf32SymFromSym(self.symbol(glob).*, sym); + for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| { + const global = self.symbol(global_index); + elf32SymFromSym(global.sourceSymbol(self).*, sym); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Sym, sym); } @@ -2829,15 +2831,17 @@ fn writeSymbols(self: *Elf) !void { .p64 => { const buf = try gpa.alloc(elf.Elf64_Sym, nsyms); defer gpa.free(buf); - for (buf[0..self.locals.items.len], self.locals.items) |*sym, local| { - sym.* = local; + for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| { + const local = self.symbol(local_index); + sym.* = local.sourceSymbol(self).*; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Sym, sym); } } - for (buf[self.locals.items.len..], self.globals.items) |*sym, glob| { - sym.* = self.symbol(glob).*; + for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| { + const global = self.symbol(global_index); + sym.* = global.sourceSymbol(self).*; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Sym, sym); } -- cgit v1.2.3 From 9691d1a30ff524be5dc2cc88855b7887cd7f26f3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 7 Sep 2023 15:45:58 +0200 Subject: elf: use zld's update mechanism for symtab for Zig module --- src/link/Elf.zig | 132 ++++++++++++++++++++++++--------------------- src/link/Elf/Symbol.zig | 13 ++--- src/link/Elf/ZigModule.zig | 81 +++++++++++++++++++++------- 3 files changed, 141 insertions(+), 85 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index dbf0756edd..3893dc947d 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1043,7 +1043,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } } - try self.writeSymbols(); + try self.updateSymtabSize(); + try self.writeSymtab(); if (build_options.enable_logging) { state_log.debug("{}", .{self.dumpState()}); @@ -2662,14 +2663,7 @@ pub fn deleteDeclExport( assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant sym.* = .{}; // TODO free list for esym! - esym.* = .{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }; + esym.* = null_sym; self.symbols_free_list.append(gpa, sym_index.*) catch {}; sym_index.* = 0; } @@ -2773,19 +2767,20 @@ fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void } } -fn elf32SymFromSym(sym: elf.Elf64_Sym, out: *elf.Elf32_Sym) void { - out.* = .{ - .st_name = sym.st_name, - .st_value = @as(u32, @intCast(sym.st_value)), - .st_size = @as(u32, @intCast(sym.st_size)), - .st_info = sym.st_info, - .st_other = sym.st_other, - .st_shndx = sym.st_shndx, - }; -} +fn updateSymtabSize(self: *Elf) !void { + var sizes = SymtabSize{}; + + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + zig_module.updateSymtabSize(self); + sizes.nlocals += zig_module.output_symtab_size.nlocals; + sizes.nglobals += zig_module.output_symtab_size.nglobals; + } + + const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; + shdr.sh_info = sizes.nlocals + 1; + self.markDirty(self.symtab_section_index.?, null); -fn writeSymbols(self: *Elf) !void { - const gpa = self.base.allocator; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), @@ -2794,59 +2789,61 @@ fn writeSymbols(self: *Elf) !void { .p32 => @alignOf(elf.Elf32_Sym), .p64 => @alignOf(elf.Elf64_Sym), }; - const zig_module = self.file(self.zig_module_index.?).?.zig_module; + const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; + try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true); +} +fn writeSymtab(self: *Elf) !void { + const gpa = self.base.allocator; const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; - shdr.sh_info = @intCast(zig_module.locals().len); - self.markDirty(self.symtab_section_index.?, null); + const sym_size: u64 = switch (self.ptr_width) { + .p32 => @sizeOf(elf.Elf32_Sym), + .p64 => @sizeOf(elf.Elf64_Sym), + }; + const nsyms = @divExact(shdr.sh_size, sym_size); - const nsyms = zig_module.locals().len + zig_module.globals().len; - const needed_size = nsyms * sym_size; - try self.growNonAllocSection(self.symtab_section_index.?, needed_size, sym_align, true); + log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset }); + + const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms); + defer gpa.free(symtab); + + var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{ + .ilocal = 1, + .iglobal = shdr.sh_info, + .symtab = symtab, + }; + + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + zig_module.writeSymtab(self, ctx); + ctx.ilocal += zig_module.output_symtab_size.nlocals; + ctx.iglobal += zig_module.output_symtab_size.nglobals; + } const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); - log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset }); switch (self.ptr_width) { .p32 => { - const buf = try gpa.alloc(elf.Elf32_Sym, nsyms); + const buf = try gpa.alloc(elf.Elf32_Sym, symtab.len); defer gpa.free(buf); - for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| { - const local = self.symbol(local_index); - elf32SymFromSym(local.sourceSymbol(self).*, sym); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Sym, sym); - } - } - - for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| { - const global = self.symbol(global_index); - elf32SymFromSym(global.sourceSymbol(self).*, sym); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Sym, sym); - } + for (buf, symtab) |*out, sym| { + out.* = .{ + .st_name = sym.st_name, + .st_info = sym.st_info, + .st_other = sym.st_other, + .st_shndx = sym.st_shndx, + .st_value = @as(u32, @intCast(sym.st_value)), + .st_size = @as(u32, @intCast(sym.st_size)), + }; + if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); } try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset); }, .p64 => { - const buf = try gpa.alloc(elf.Elf64_Sym, nsyms); - defer gpa.free(buf); - for (buf[0..zig_module.locals().len], zig_module.locals()) |*sym, local_index| { - const local = self.symbol(local_index); - sym.* = local.sourceSymbol(self).*; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Sym, sym); - } - } - - for (buf[zig_module.locals().len..], zig_module.globals()) |*sym, global_index| { - const global = self.symbol(global_index); - sym.* = global.sourceSymbol(self).*; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Sym, sym); - } + if (foreign_endian) { + for (symtab) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); } - try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), shdr.sh_offset); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(symtab), shdr.sh_offset); }, } } @@ -3282,7 +3279,19 @@ const DeclMetadata = struct { } }; -const Elf = @This(); +pub const SymtabSize = struct { + nlocals: u32 = 0, + nglobals: u32 = 0, +}; + +pub const null_sym = elf.Elf64_Sym{ + .st_name = 0, + .st_info = 0, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, +}; const std = @import("std"); const build_options = @import("build_options"); @@ -3309,6 +3318,7 @@ pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); +const Elf = @This(); const File = @import("Elf/file.zig").File; const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 0418aa59db..33b159e700 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -35,7 +35,8 @@ extra_index: u32 = 0, pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file_ptr = symbol.file(elf_file).?; // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; - return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and file_ptr != .linker_defined and file_ptr != .zig_module; + return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and + file_ptr != .linker_defined and file_ptr != .zig_module; } pub fn isLocal(symbol: Symbol) bool { @@ -169,7 +170,7 @@ pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void { elf_file.setSymbolExtra(symbol.extra_index, extras); } -pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { +pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const file_ptr = symbol.file(elf_file).?; const s_sym = symbol.sourceSymbol(elf_file); const st_type = symbol.type(elf_file); @@ -184,7 +185,7 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) break :blk elf.SHN_ABS; - break :blk symbol.shndx; + break :blk symbol.output_section_index; }; const st_value = blk: { // if (symbol.flags.copy_rel) break :blk symbol.address(.{}, elf_file); @@ -197,8 +198,8 @@ pub fn asElfSym(symbol: Symbol, st_name: u32, elf_file: *Elf) elf.Elf64_Sym { // if (Elf.shdrIsTls(shdr)) break :blk symbol.value - elf_file.getTlsAddress(); break :blk symbol.value; }; - return elf.Elf64_Sym{ - .st_name = st_name, + out.* = .{ + .st_name = symbol.name_offset, .st_info = (st_bind << 4) | st_type, .st_other = s_sym.st_other, .st_shndx = st_shndx, @@ -343,9 +344,9 @@ pub const Extra = struct { pub const Index = u32; -const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; +const std = @import("std"); const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 6cf918c8a1..0ac9d05b81 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -10,7 +10,7 @@ atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, alive: bool = true, -// output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *ZigModule, allocator: Allocator) void { self.elf_local_symbols.deinit(allocator); @@ -36,14 +36,9 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) ! symbol_ptr.esym_index = @as(Symbol.Index, @intCast(self.elf_local_symbols.items.len)); const local_esym = try self.elf_local_symbols.addOne(gpa); - local_esym.* = .{ - .st_name = 0, - .st_info = elf.STB_LOCAL << 4, - .st_other = 0, - .st_shndx = output_section_index, - .st_value = 0, - .st_size = 0, - }; + local_esym.* = Elf.null_sym; + local_esym.st_info = elf.STB_LOCAL << 4; + local_esym.st_shndx = output_section_index; try self.atoms.append(gpa, atom_index); try self.local_symbols.putNoClobber(gpa, symbol_index, {}); @@ -57,22 +52,73 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I try self.global_symbols.ensureUnusedCapacity(gpa, 1); const off = try elf_file.strtab.insert(gpa, name); const esym_index = @as(Symbol.Index, @intCast(self.elf_global_symbols.items.len)); - self.elf_global_symbols.appendAssumeCapacity(.{ - .st_name = off, - .st_info = elf.STB_GLOBAL << 4, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }); + const esym = self.elf_global_symbols.addOneAssumeCapacity(); + esym.* = Elf.null_sym; + esym.st_name = off; + esym.st_info = elf.STB_GLOBAL << 4; const gop = try elf_file.getOrPutGlobal(off); const sym = elf_file.symbol(gop.index); sym.file_index = self.index; sym.esym_index = esym_index; + sym.flags.@"export" = true; self.global_symbols.putAssumeCapacityNoClobber(gop.index, {}); return gop.index; } +pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { + for (self.locals()) |local_index| { + const local = elf_file.symbol(local_index); + const esym = self.sourceSymbol(local_index, elf_file); + switch (esym.st_type()) { + elf.STT_SECTION, elf.STT_NOTYPE => { + local.flags.output_symtab = false; + continue; + }, + else => {}, + } + local.flags.output_symtab = true; + self.output_symtab_size.nlocals += 1; + } + + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.file(elf_file)) |file| if (file.index() != self.index) { + global.flags.output_symtab = false; + continue; + }; + global.flags.output_symtab = true; + if (global.isLocal()) { + self.output_symtab_size.nlocals += 1; + } else { + self.output_symtab_size.nglobals += 1; + } + } +} + +pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void { + var ilocal = ctx.ilocal; + for (self.locals()) |local_index| { + const local = elf_file.symbol(local_index); + if (!local.flags.output_symtab) continue; + local.setOutputSym(elf_file, &ctx.symtab[ilocal]); + ilocal += 1; + } + + var iglobal = ctx.iglobal; + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.file(elf_file)) |file| if (file.index() != self.index) continue; + if (!global.flags.output_symtab) continue; + if (global.isLocal()) { + global.setOutputSym(elf_file, &ctx.symtab[ilocal]); + ilocal += 1; + } else { + global.setOutputSym(elf_file, &ctx.symtab[iglobal]); + iglobal += 1; + } + } +} + pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index, elf_file: *Elf) *elf.Elf64_Sym { const sym = elf_file.symbol(symbol_index); if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[sym.esym_index]; @@ -134,5 +180,4 @@ const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Module = @import("../../Module.zig"); const ZigModule = @This(); -// const Object = @import("Object.zig"); const Symbol = @import("Symbol.zig"); -- cgit v1.2.3 From 6ad5db030c576ab9cf944b4a9432954681af649c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 8 Sep 2023 18:12:53 +0200 Subject: elf: store GOT index in symbol extra array; use GotSection for GOT --- src/arch/aarch64/CodeGen.zig | 4 +- src/arch/arm/CodeGen.zig | 4 +- src/arch/riscv64/CodeGen.zig | 4 +- src/arch/sparc64/CodeGen.zig | 4 +- src/arch/x86_64/CodeGen.zig | 8 +- src/codegen.zig | 4 +- src/link/Elf.zig | 134 +++++------ src/link/Elf/Symbol.zig | 42 ++-- src/link/Elf/synthetic_sections.zig | 469 ++++++++++++++++++++++++++++++++++++ 9 files changed, 565 insertions(+), 108 deletions(-) create mode 100644 src/link/Elf/synthetic_sections.zig (limited to 'src') diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 35daa4a02a..2c6e3f5a7b 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4316,8 +4316,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index f32e104c88..937d4aa1dd 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4296,8 +4296,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr }); } else if (self.bin_file.cast(link.File.MachO)) |_| { unreachable; // unsupported architecture for MachO diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 5ed60dc220..d3f162ae0f 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1749,8 +1749,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .func => |func| { const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = @as(u32, @intCast(sym.gotAddress(elf_file))); try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); _ = try self.addInst(.{ .tag = .jalr, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index b0a3573ff9..df1a7cf970 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1351,8 +1351,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const sym_index = try elf_file.getOrCreateMetadataForDecl(func.owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - break :blk @as(u32, @intCast(sym.getOffsetTableAddress(elf_file))); + _ = try sym.getOrCreateGotEntry(elf_file); + break :blk @as(u32, @intCast(sym.gotAddress(elf_file))); } else unreachable; try self.genSetReg(Type.usize, .o7, .{ .memory = got_addr }); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index eaed79ef5e..07bc4c9200 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8151,8 +8151,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = sym.getOffsetTableAddress(elf_file); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = sym.gotAddress(elf_file); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr), @@ -10218,8 +10218,8 @@ fn genLazySymbolRef( const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - const got_addr = sym.getOffsetTableAddress(elf_file); + _ = try sym.getOrCreateGotEntry(elf_file); + const got_addr = sym.gotAddress(elf_file); const got_mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = @intCast(got_addr) }); switch (tag) { diff --git a/src/codegen.zig b/src/codegen.zig index d3f44bb2f5..7c6dcbdc46 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -856,8 +856,8 @@ fn genDeclRef( if (bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); const sym = elf_file.symbol(sym_index); - _ = try sym.getOrCreateOffsetTableEntry(elf_file); - return GenResult.mcv(.{ .memory = sym.getOffsetTableAddress(elf_file) }); + _ = try sym.getOrCreateGotEntry(elf_file); + return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 3893dc947d..ccd30cf1dd 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -42,6 +42,8 @@ shstrtab: StringTable(.strtab) = .{}, /// .strtab buffer strtab: StringTable(.strtab) = .{}, +got: GotSection = .{}, + symtab_section_index: ?u16 = null, text_section_index: ?u16 = null, rodata_section_index: ?u16 = null, @@ -56,18 +58,16 @@ shstrtab_section_index: ?u16 = null, strtab_section_index: ?u16 = null, symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, -got_table: TableSection(Symbol.Index) = .{}, - phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, shstrtab_dirty: bool = false, strtab_dirty: bool = false, -got_table_count_dirty: bool = false, debug_strtab_dirty: bool = false, debug_abbrev_section_dirty: bool = false, @@ -146,6 +146,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option // Index 0 is always a null symbol. try self.symbols.append(allocator, .{}); + // Index 0 is always a null symbol. + try self.symbols_extra.append(allocator, 0); // Allocate atom index 0 to null atom try self.atoms.append(allocator, .{}); // Append null file at index 0 @@ -234,8 +236,9 @@ pub fn deinit(self: *Elf) void { self.shstrtab.deinit(gpa); self.strtab.deinit(gpa); self.symbols.deinit(gpa); + self.symbols_extra.deinit(gpa); self.symbols_free_list.deinit(gpa); - self.got_table.deinit(gpa); + self.got.deinit(gpa); self.resolver.deinit(gpa); self.unresolved.deinit(gpa); @@ -1249,7 +1252,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node assert(!self.shstrtab_dirty); assert(!self.strtab_dirty); assert(!self.debug_strtab_dirty); - assert(!self.got_table_count_dirty); + assert(!self.got.dirty); } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2087,7 +2090,7 @@ fn freeDeclMetadata(self: *Elf, sym_index: Symbol.Index) void { log.debug("adding %{d} to local symbols free list", .{sym_index}); self.symbols_free_list.append(self.base.allocator, sym_index) catch {}; self.symbols.items[sym_index] = .{}; - self.got_table.freeEntry(self.base.allocator, sym_index); + // TODO free GOT entry here } pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { @@ -2226,9 +2229,8 @@ fn updateDeclCode( esym.st_value = atom_ptr.value; log.debug(" (writing new offset table entry)", .{}); - const got_entry_index = self.got_table.lookup.get(sym_index).?; - self.got_table.entries.items[got_entry_index] = sym_index; - try self.writeOffsetTableEntry(got_entry_index); + const extra = sym.extra(self).?; + try self.got.writeEntry(self, extra.got); } } else if (code.len < old_size) { atom_ptr.shrink(self); @@ -2245,8 +2247,8 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - const got_entry_index = try sym.getOrCreateOffsetTableEntry(self); - try self.writeOffsetTableEntry(got_entry_index); + const got_index = try sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, got_index); } const phdr_index = self.sections.items(.phdr_index)[shdr_index]; @@ -2477,8 +2479,8 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. local_sym.value = atom_ptr.value; local_esym.st_value = atom_ptr.value; - const got_entry_index = try local_sym.getOrCreateOffsetTableEntry(self); - try self.writeOffsetTableEntry(got_entry_index); + const got_index = try local_sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, got_index); const section_offset = atom_ptr.value - self.program_headers.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[local_sym.output_section_index].sh_offset + section_offset; @@ -2712,61 +2714,6 @@ fn writeSectHeader(self: *Elf, index: usize) !void { } } -fn writeOffsetTableEntry(self: *Elf, index: @TypeOf(self.got_table).Index) !void { - const entry_size: u16 = self.archPtrWidthBytes(); - if (self.got_table_count_dirty) { - const needed_size = self.got_table.entries.items.len * entry_size; - try self.growAllocSection(self.got_section_index.?, needed_size); - self.got_table_count_dirty = false; - } - const endian = self.base.options.target.cpu.arch.endian(); - const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; - const off = shdr.sh_offset + @as(u64, entry_size) * index; - const phdr = &self.program_headers.items[self.phdr_got_index.?]; - const vaddr = phdr.p_vaddr + @as(u64, entry_size) * index; - const got_entry = self.got_table.entries.items[index]; - const got_value = self.symbol(got_entry).value; - switch (entry_size) { - 2 => { - var buf: [2]u8 = undefined; - mem.writeInt(u16, &buf, @as(u16, @intCast(got_value)), endian); - try self.base.file.?.pwriteAll(&buf, off); - }, - 4 => { - var buf: [4]u8 = undefined; - mem.writeInt(u32, &buf, @as(u32, @intCast(got_value)), endian); - try self.base.file.?.pwriteAll(&buf, off); - }, - 8 => { - var buf: [8]u8 = undefined; - mem.writeInt(u64, &buf, got_value, endian); - try self.base.file.?.pwriteAll(&buf, off); - - if (self.base.child_pid) |pid| { - switch (builtin.os.tag) { - .linux => { - var local_vec: [1]std.os.iovec_const = .{.{ - .iov_base = &buf, - .iov_len = buf.len, - }}; - var remote_vec: [1]std.os.iovec_const = .{.{ - .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))), - .iov_len = buf.len, - }}; - const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); - switch (std.os.errno(rc)) { - .SUCCESS => assert(rc == buf.len), - else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), - } - }, - else => return error.HotSwapUnavailableOnHostOperatingSystem, - } - } - }, - else => unreachable, - } -} - fn updateSymtabSize(self: *Elf) !void { var sizes = SymtabSize{}; @@ -2858,8 +2805,8 @@ fn ptrWidthBytes(self: Elf) u8 { /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes /// in a 32-bit ELF file. -fn archPtrWidthBytes(self: Elf) u8 { - return @as(u8, @intCast(self.base.options.target.ptrBitWidth() / 8)); +pub fn archPtrWidthBytes(self: Elf) u8 { + return @as(u8, @intCast(@divExact(self.base.options.target.ptrBitWidth(), 8))); } fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { @@ -3179,6 +3126,50 @@ pub fn addSymbol(self: *Elf) !Symbol.Index { return index; } +pub fn addSymbolExtra(self: *Elf, extra: Symbol.Extra) !u32 { + const fields = @typeInfo(Symbol.Extra).Struct.fields; + try self.symbols_extra.ensureUnusedCapacity(self.base.allocator, fields.len); + return self.addSymbolExtraAssumeCapacity(extra); +} + +pub fn addSymbolExtraAssumeCapacity(self: *Elf, extra: Symbol.Extra) u32 { + const index = @as(u32, @intCast(self.symbols_extra.items.len)); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields) |field| { + self.symbols_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn symbolExtra(self: *Elf, index: u32) ?Symbol.Extra { + if (index == 0) return null; + const fields = @typeInfo(Symbol.Extra).Struct.fields; + var i: usize = index; + var result: Symbol.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.symbols_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setSymbolExtra(self: *Elf, index: u32, extra: Symbol.Extra) void { + assert(index > 0); + const fields = @typeInfo(Symbol.Extra).Struct.fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + const GetOrPutGlobalResult = struct { found_existing: bool, index: Symbol.Index, @@ -3227,6 +3218,7 @@ fn fmtDumpState( try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); try writer.print("{}\n", .{linker_defined.fmtSymtab(self)}); } + try writer.print("{}\n", .{self.got.fmt(self)}); } const default_entry_addr = 0x8000000; @@ -3311,6 +3303,7 @@ const lldMain = @import("../main.zig").lldMain; const musl = @import("../musl.zig"); const target_util = @import("../target.zig"); const trace = @import("../tracy.zig").trace; +const synthetic_sections = @import("Elf/synthetic_sections.zig"); const Air = @import("../Air.zig"); const Allocator = std.mem.Allocator; @@ -3320,6 +3313,7 @@ const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); const Elf = @This(); const File = @import("Elf/file.zig").File; +const GotSection = synthetic_sections.GotSection; const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 33b159e700..eb29d06fee 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -84,24 +84,6 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { return file_ptr.symbolRank(sym, in_archive); } -/// If entry already exists, returns index to it. -/// Otherwise, creates a new entry in the Global Offset Table for this Symbol. -pub fn getOrCreateOffsetTableEntry(self: Symbol, elf_file: *Elf) !Symbol.Index { - if (elf_file.got_table.lookup.get(self.index)) |index| return index; - const index = try elf_file.got_table.allocateEntry(elf_file.base.allocator, self.index); - elf_file.got_table_count_dirty = true; - return index; -} - -pub fn getOffsetTableAddress(self: Symbol, elf_file: *Elf) u64 { - const got_entry_index = elf_file.got_table.lookup.get(self.index).?; - const target = elf_file.base.options.target; - const ptr_bits = target.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; - return got.p_vaddr + got_entry_index * ptr_bytes; -} - pub fn address(symbol: Symbol, opts: struct { plt: bool = true, }, elf_file: *Elf) u64 { @@ -122,11 +104,21 @@ pub fn address(symbol: Symbol, opts: struct { return symbol.value; } -// pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { -// if (!symbol.flags.got) return 0; -// const extra = symbol.extra(elf_file).?; -// return elf_file.gotEntryAddress(extra.got); -// } +pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { + if (!symbol.flags.got) return 0; + const extras = symbol.extra(elf_file).?; + const entry = elf_file.got.entries.items[extras.got]; + return entry.address(elf_file); +} + +pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GotSection.Index { + const index = if (symbol.flags.got) + symbol.extra(elf_file).?.got + else + try elf_file.got.addGotSymbol(symbol.index, elf_file); + symbol.flags.got = true; + return index; +} // pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 { // if (!symbol.flags.tlsgd) return 0; @@ -159,7 +151,7 @@ pub fn address(symbol: Symbol, opts: struct { // } pub fn addExtra(symbol: *Symbol, extras: Extra, elf_file: *Elf) !void { - symbol.extra = try elf_file.addSymbolExtra(extras); + symbol.extra_index = try elf_file.addSymbolExtra(extras); } pub fn extra(symbol: Symbol, elf_file: *Elf) ?Extra { @@ -347,10 +339,12 @@ pub const Index = u32; const assert = std.debug.assert; const elf = std.elf; const std = @import("std"); +const synthetic_sections = @import("synthetic_sections.zig"); const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const GotSection = synthetic_sections.GotSection; const LinkerDefined = @import("LinkerDefined.zig"); // const Object = @import("Object.zig"); // const SharedObject = @import("SharedObject.zig"); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig new file mode 100644 index 0000000000..fce23aeddb --- /dev/null +++ b/src/link/Elf/synthetic_sections.zig @@ -0,0 +1,469 @@ +pub const GotSection = struct { + entries: std.ArrayListUnmanaged(Entry) = .{}, + needs_rela: bool = false, + dirty: bool = false, + output_symtab_size: Elf.SymtabSize = .{}, + + pub const Index = u32; + + const Tag = enum { + got, + tlsld, + tlsgd, + gottp, + tlsdesc, + }; + + const Entry = struct { + tag: Tag, + symbol_index: Symbol.Index, + cell_index: Index, + + /// Returns how many indexes in the GOT this entry uses. + pub inline fn len(entry: Entry) usize { + return switch (entry.tag) { + .got, .gottp => 1, + .tlsld, .tlsgd, .tlsdesc => 2, + }; + } + + pub fn address(entry: Entry, elf_file: *Elf) u64 { + const ptr_bytes = @as(u64, elf_file.archPtrWidthBytes()); + const shdr = &elf_file.sections.items(.shdr)[elf_file.got_section_index.?]; + return shdr.sh_addr + @as(u64, entry.cell_index) * ptr_bytes; + } + }; + + pub fn deinit(got: *GotSection, allocator: Allocator) void { + got.entries.deinit(allocator); + } + + fn allocateEntry(got: *GotSection, allocator: Allocator) !Index { + try got.entries.ensureUnusedCapacity(allocator, 1); + // TODO add free list + const index = @as(Index, @intCast(got.entries.items.len)); + const entry = got.entries.addOneAssumeCapacity(); + const cell_index: Index = if (index > 0) blk: { + const last = got.entries.items[index - 1]; + break :blk last.cell_index + @as(Index, @intCast(last.len())); + } else 0; + entry.* = .{ .tag = undefined, .symbol_index = undefined, .cell_index = cell_index }; + got.dirty = true; + return index; + } + + pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { + const index = try got.allocateEntry(elf_file.base.allocator); + const entry = &got.entries.items[index]; + entry.tag = .got; + entry.symbol_index = sym_index; + const symbol = elf_file.symbol(sym_index); + if (symbol.flags.import or symbol.isIFunc(elf_file) or (elf_file.base.options.pic and !symbol.isAbs(elf_file))) + got.needs_rela = true; + if (symbol.extra(elf_file)) |extra| { + var new_extra = extra; + new_extra.got = index; + symbol.setExtra(new_extra, elf_file); + } else try symbol.addExtra(.{ .got = index }, elf_file); + return index; + } + + // pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + // const index = got.next_index; + // const symbol = elf_file.getSymbol(sym_index); + // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true; + // if (symbol.getExtra(elf_file)) |extra| { + // var new_extra = extra; + // new_extra.tlsgd = index; + // symbol.setExtra(new_extra, elf_file); + // } else try symbol.addExtra(.{ .tlsgd = index }, elf_file); + // try got.symbols.append(elf_file.base.allocator, .{ .tlsgd = sym_index }); + // got.next_index += 2; + // } + + // pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + // const index = got.next_index; + // const symbol = elf_file.getSymbol(sym_index); + // if (symbol.flags.import or elf_file.options.output_mode == .lib) got.needs_rela = true; + // if (symbol.getExtra(elf_file)) |extra| { + // var new_extra = extra; + // new_extra.gottp = index; + // symbol.setExtra(new_extra, elf_file); + // } else try symbol.addExtra(.{ .gottp = index }, elf_file); + // try got.symbols.append(elf_file.base.allocator, .{ .gottp = sym_index }); + // got.next_index += 1; + // } + + // pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + // const index = got.next_index; + // const symbol = elf_file.getSymbol(sym_index); + // got.needs_rela = true; + // if (symbol.getExtra(elf_file)) |extra| { + // var new_extra = extra; + // new_extra.tlsdesc = index; + // symbol.setExtra(new_extra, elf_file); + // } else try symbol.addExtra(.{ .tlsdesc = index }, elf_file); + // try got.symbols.append(elf_file.base.allocator, .{ .tlsdesc = sym_index }); + // got.next_index += 2; + // } + + pub fn size(got: GotSection, elf_file: *Elf) usize { + var s: usize = 0; + for (got.entries.items) |entry| { + s += elf_file.archPtrWidthBytes() * entry.len(); + } + return s; + } + + pub fn writeEntry(got: *GotSection, elf_file: *Elf, index: Index) !void { + const entry_size: u16 = elf_file.archPtrWidthBytes(); + if (got.dirty) { + const needed_size = got.size(elf_file); + try elf_file.growAllocSection(elf_file.got_section_index.?, needed_size); + got.dirty = false; + } + const endian = elf_file.base.options.target.cpu.arch.endian(); + const entry = got.entries.items[index]; + const shdr = &elf_file.sections.items(.shdr)[elf_file.got_section_index.?]; + const off = shdr.sh_offset + @as(u64, entry_size) * entry.cell_index; + const vaddr = shdr.sh_addr + @as(u64, entry_size) * entry.cell_index; + const value = elf_file.symbol(entry.symbol_index).value; + switch (entry_size) { + 2 => { + var buf: [2]u8 = undefined; + std.mem.writeInt(u16, &buf, @as(u16, @intCast(value)), endian); + try elf_file.base.file.?.pwriteAll(&buf, off); + }, + 4 => { + var buf: [4]u8 = undefined; + std.mem.writeInt(u32, &buf, @as(u32, @intCast(value)), endian); + try elf_file.base.file.?.pwriteAll(&buf, off); + }, + 8 => { + var buf: [8]u8 = undefined; + std.mem.writeInt(u64, &buf, value, endian); + try elf_file.base.file.?.pwriteAll(&buf, off); + + if (elf_file.base.child_pid) |pid| { + switch (builtin.os.tag) { + .linux => { + var local_vec: [1]std.os.iovec_const = .{.{ + .iov_base = &buf, + .iov_len = buf.len, + }}; + var remote_vec: [1]std.os.iovec_const = .{.{ + .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))), + .iov_len = buf.len, + }}; + const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); + switch (std.os.errno(rc)) { + .SUCCESS => assert(rc == buf.len), + else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), + } + }, + else => return error.HotSwapUnavailableOnHostOperatingSystem, + } + } + }, + else => unreachable, + } + } + + // pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { + // const is_shared = elf_file.options.output_mode == .lib; + // const apply_relocs = elf_file.options.apply_dynamic_relocs; + + // for (got.symbols.items) |sym| { + // const symbol = elf_file.getSymbol(sym.getIndex()); + // switch (sym) { + // .got => { + // const value: u64 = blk: { + // const value = symbol.getAddress(.{ .plt = false }, elf_file); + // if (symbol.flags.import) break :blk 0; + // if (symbol.isIFunc(elf_file)) + // break :blk if (apply_relocs) value else 0; + // if (elf_file.options.pic and !symbol.isAbs(elf_file)) + // break :blk if (apply_relocs) value else 0; + // break :blk value; + // }; + // try writer.writeIntLittle(u64, value); + // }, + + // .tlsgd => { + // if (symbol.flags.import) { + // try writer.writeIntLittle(u64, 0); + // try writer.writeIntLittle(u64, 0); + // } else { + // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1); + // const offset = symbol.getAddress(.{}, elf_file) - elf_file.getDtpAddress(); + // try writer.writeIntLittle(u64, offset); + // } + // }, + + // .gottp => { + // if (symbol.flags.import) { + // try writer.writeIntLittle(u64, 0); + // } else if (is_shared) { + // const offset = if (apply_relocs) + // symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress() + // else + // 0; + // try writer.writeIntLittle(u64, offset); + // } else { + // const offset = @as(i64, @intCast(symbol.getAddress(.{}, elf_file))) - + // @as(i64, @intCast(elf_file.getTpAddress())); + // try writer.writeIntLittle(u64, @as(u64, @bitCast(offset))); + // } + // }, + + // .tlsdesc => { + // try writer.writeIntLittle(u64, 0); + // try writer.writeIntLittle(u64, 0); + // }, + // } + // } + + // if (got.emit_tlsld) { + // try writer.writeIntLittle(u64, if (is_shared) @as(u64, 0) else 1); + // try writer.writeIntLittle(u64, 0); + // } + // } + + // pub fn addRela(got: GotSection, elf_file: *Elf) !void { + // const is_shared = elf_file.options.output_mode == .lib; + // try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file)); + + // for (got.symbols.items) |sym| { + // const symbol = elf_file.getSymbol(sym.getIndex()); + // const extra = symbol.getExtra(elf_file).?; + + // switch (sym) { + // .got => { + // const offset = symbol.gotAddress(elf_file); + + // if (symbol.flags.import) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_GLOB_DAT, + // }); + // continue; + // } + + // if (symbol.isIFunc(elf_file)) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_IRELATIVE, + // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)), + // }); + // continue; + // } + + // if (elf_file.options.pic and !symbol.isAbs(elf_file)) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_RELATIVE, + // .addend = @intCast(symbol.getAddress(.{ .plt = false }, elf_file)), + // }); + // } + // }, + + // .tlsgd => { + // const offset = symbol.getTlsGdAddress(elf_file); + // if (symbol.flags.import) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_DTPMOD64, + // }); + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset + 8, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_DTPOFF64, + // }); + // } else if (is_shared) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_DTPMOD64, + // }); + // } + // }, + + // .gottp => { + // const offset = symbol.getGotTpAddress(elf_file); + // if (symbol.flags.import) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_TPOFF64, + // }); + // } else if (is_shared) { + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_TPOFF64, + // .addend = @intCast(symbol.getAddress(.{}, elf_file) - elf_file.getTlsAddress()), + // }); + // } + // }, + + // .tlsdesc => { + // const offset = symbol.getTlsDescAddress(elf_file); + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .sym = extra.dynamic, + // .type = elf.R_X86_64_TLSDESC, + // }); + // }, + // } + // } + + // if (is_shared and got.emit_tlsld) { + // const offset = elf_file.getTlsLdAddress(); + // elf_file.addRelaDynAssumeCapacity(.{ + // .offset = offset, + // .type = elf.R_X86_64_DTPMOD64, + // }); + // } + // } + + // pub fn numRela(got: GotSection, elf_file: *Elf) usize { + // const is_shared = elf_file.options.output_mode == .lib; + // var num: usize = 0; + // for (got.symbols.items) |sym| { + // const symbol = elf_file.symbol(sym.index()); + // switch (sym) { + // .got => if (symbol.flags.import or + // symbol.isIFunc(elf_file) or (elf_file.options.pic and !symbol.isAbs(elf_file))) + // { + // num += 1; + // }, + + // .tlsgd => if (symbol.flags.import) { + // num += 2; + // } else if (is_shared) { + // num += 1; + // }, + + // .gottp => if (symbol.flags.import or is_shared) { + // num += 1; + // }, + + // .tlsdesc => num += 1, + // } + // } + // if (is_shared and got.emit_tlsld) num += 1; + // return num; + // } + + pub fn calcSymtabSize(got: *GotSection, elf_file: *Elf) !void { + got.output_symtab_size.nlocals = @as(u32, @intCast(got.symbols.items.len)); + for (got.symbols.items) |sym| { + const suffix_len = switch (sym) { + .tlsgd => "$tlsgd".len, + .got => "$got".len, + .gottp => "$gottp".len, + .tlsdesc => "$tlsdesc".len, + }; + const symbol = elf_file.getSymbol(sym.getIndex()); + const name_len = symbol.getName(elf_file).len; + got.output_symtab_size.strsize += @as(u32, @intCast(name_len + suffix_len + 1)); + } + + if (got.emit_tlsld) { + got.output_symtab_size.nlocals += 1; + got.output_symtab_size.strsize += @as(u32, @intCast("$tlsld".len + 1)); + } + } + + pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void { + const gpa = elf_file.base.allocator; + + var ilocal = ctx.ilocal; + for (got.symbols.items) |sym| { + const suffix = switch (sym) { + .tlsgd => "$tlsgd", + .got => "$got", + .gottp => "$gottp", + .tlsdesc => "$tlsdesc", + }; + const symbol = elf_file.getSymbol(sym.getIndex()); + const name = try std.fmt.allocPrint(gpa, "{s}{s}", .{ symbol.getName(elf_file), suffix }); + defer gpa.free(name); + const st_name = try ctx.strtab.insert(gpa, name); + const st_value = switch (sym) { + // .tlsgd => symbol.tlsGdAddress(elf_file), + .got => symbol.gotAddress(elf_file), + // .gottp => symbol.gotTpAddress(elf_file), + // .tlsdesc => symbol.tlsDescAddress(elf_file), + else => unreachable, + }; + const st_size: u64 = switch (sym) { + .tlsgd, .tlsdesc => 16, + .got, .gottp => 8, + }; + ctx.symtab[ilocal] = .{ + .st_name = st_name, + .st_info = elf.STT_OBJECT, + .st_other = 0, + .st_shndx = elf_file.got_section_index.?, + .st_value = st_value, + .st_size = st_size, + }; + ilocal += 1; + } + + // if (got.emit_tlsld) { + // const st_name = try ctx.strtab.insert(gpa, "$tlsld"); + // ctx.symtab[ilocal] = .{ + // .st_name = st_name, + // .st_info = elf.STT_OBJECT, + // .st_other = 0, + // .st_shndx = elf_file.got_sect_index.?, + // .st_value = elf_file.getTlsLdAddress(), + // .st_size = 16, + // }; + // ilocal += 1; + // } + } + + const FormatCtx = struct { + got: GotSection, + elf_file: *Elf, + }; + + pub fn fmt(got: GotSection, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ .got = got, .elf_file = elf_file } }; + } + + pub fn format2( + ctx: FormatCtx, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = options; + _ = unused_fmt_string; + try writer.writeAll("GOT\n"); + for (ctx.got.entries.items) |entry| { + const symbol = ctx.elf_file.symbol(entry.symbol_index); + try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ + entry.cell_index, + entry.address(ctx.elf_file), + entry.symbol_index, + symbol.address(.{}, ctx.elf_file), + symbol.name(ctx.elf_file), + }); + } + } +}; + +const assert = std.debug.assert; +const builtin = @import("builtin"); +const elf = std.elf; +const log = std.log.scoped(.link); +const std = @import("std"); + +const Allocator = std.mem.Allocator; +const Elf = @import("../Elf.zig"); +const Symbol = @import("Symbol.zig"); -- cgit v1.2.3 From 69738a07c2309530463873538a5fd62d6174a26c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 8 Sep 2023 21:09:45 +0200 Subject: elf: store Index rather than ?Index in Atom; gen ABS STT_FILE for zig source --- src/link/Elf.zig | 36 +++++++++++++++++++--------- src/link/Elf/Atom.zig | 60 +++++++++++++++++++++------------------------- src/link/Elf/Symbol.zig | 4 ++-- src/link/Elf/ZigModule.zig | 25 +++++++++++-------- 4 files changed, 69 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ccd30cf1dd..0aeb0ebd4b 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -834,10 +834,23 @@ pub fn populateMissingMetadata(self: *Elf) !void { try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset); } - if (self.zig_module_index == null) { - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); - self.files.set(index, .{ .zig_module = .{ .index = index } }); - self.zig_module_index = index; + if (self.base.options.module) |module| { + if (self.zig_module_index == null) { + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + self.files.set(index, .{ .zig_module = .{ + .index = index, + .path = module.main_pkg.root_src_path, + } }); + self.zig_module_index = index; + const zig_module = self.file(index).?.zig_module; + const sym_index = try zig_module.addLocal(self); + const sym = self.symbol(sym_index); + const esym = sym.sourceSymbol(self); + const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path)); + sym.name_offset = name_off; + esym.st_name = name_off; + esym.st_info |= elf.STT_FILE; + } } } @@ -846,13 +859,12 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { const shdr = &self.sections.items(.shdr)[shdr_index]; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; const phdr = &self.program_headers.items[phdr_index]; - const maybe_last_atom_index = self.sections.items(.last_atom_index)[shdr_index]; + const last_atom_index = self.sections.items(.last_atom_index)[shdr_index]; if (needed_size > self.allocatedSize(shdr.sh_offset)) { // Must move the entire section. const new_offset = self.findFreeSpace(needed_size, self.page_size); - const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: { - const last = self.atom(last_atom_index); + const existing_size = if (self.atom(last_atom_index)) |last| blk: { break :blk (last.value + last.size) - phdr.p_vaddr; } else if (shdr_index == self.got_section_index.?) blk: { break :blk shdr.sh_size; @@ -1016,7 +1028,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node while (it.next()) |entry| { const atom_index = entry.key_ptr.*; const relocs = entry.value_ptr.*; - const atom_ptr = self.atom(atom_index); + const atom_ptr = self.atom(atom_index).?; const source_shdr = self.sections.items(.shdr)[atom_ptr.output_section_index]; log.debug("relocating '{s}'", .{atom_ptr.name(self)}); @@ -2753,6 +2765,7 @@ fn writeSymtab(self: *Elf) !void { const symtab = try gpa.alloc(elf.Elf64_Sym, nsyms); defer gpa.free(symtab); + symtab[0] = null_sym; var ctx: struct { ilocal: usize, iglobal: usize, symtab: []elf.Elf64_Sym } = .{ .ilocal = 1, @@ -3083,7 +3096,8 @@ const CsuObjects = struct { } }; -pub fn atom(self: *Elf, atom_index: Atom.Index) *Atom { +pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom { + if (atom_index == 0) return null; assert(atom_index < self.atoms.items.len); return &self.atoms.items[atom_index]; } @@ -3210,7 +3224,7 @@ fn fmtDumpState( if (self.zig_module_index) |index| { const zig_module = self.file(index).?.zig_module; - try writer.print("zig_module({d}) : (zig module)\n", .{index}); + try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path }); try writer.print("{}\n", .{zig_module.fmtSymtab(self)}); } if (self.linker_defined_index) |index| { @@ -3230,7 +3244,7 @@ const Section = struct { phdr_index: u16, /// Index of the last allocated atom in this section. - last_atom_index: ?Atom.Index = null, + last_atom_index: Atom.Index = 0, /// A list of atoms that have surplus capacity. This list can have false /// positives, as functions grow and shrink over time, only sometimes being added diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 8eb9f1af0b..289db3394c 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -39,8 +39,8 @@ fde_end: u32 = 0, /// Points to the previous and next neighbors, based on the `text_offset`. /// This can be used to find, for example, the capacity of this `TextBlock`. -prev_index: ?Index = null, -next_index: ?Index = null, +prev_index: Index = 0, +next_index: Index = 0, pub fn name(self: Atom, elf_file: *Elf) []const u8 { return elf_file.strtab.getAssumeExists(self.name_offset); @@ -50,14 +50,13 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 { /// File offset relocation happens transparently, so it is not included in /// this calculation. pub fn capacity(self: Atom, elf_file: *Elf) u64 { - const next_value = if (self.next_index) |next_index| elf_file.atom(next_index).value else std.math.maxInt(u32); + const next_value = if (elf_file.atom(self.next_index)) |next| next.value else std.math.maxInt(u32); return next_value - self.value; } pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { // No need to keep a free list node for the last block. - const next_index = self.next_index orelse return false; - const next = elf_file.atom(next_index); + const next = elf_file.atom(self.next_index) orelse return false; const cap = next.value - self.value; const ideal_cap = Elf.padToIdeal(self.size); if (cap <= ideal_cap) return false; @@ -84,7 +83,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { const phdr = &elf_file.program_headers.items[phdr_index]; const shdr = &elf_file.sections.items(.shdr)[self.output_section_index]; const free_list = &elf_file.sections.items(.free_list)[self.output_section_index]; - const maybe_last_atom_index = &elf_file.sections.items(.last_atom_index)[self.output_section_index]; + const last_atom_index = &elf_file.sections.items(.last_atom_index)[self.output_section_index]; const new_atom_ideal_capacity = Elf.padToIdeal(self.size); const alignment = try std.math.powi(u64, 2, self.alignment); @@ -102,7 +101,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len; while (i < free_list.items.len) { const big_atom_index = free_list.items[i]; - const big_atom = elf_file.atom(big_atom_index); + const big_atom = elf_file.atom(big_atom_index).?; // We now have a pointer to a live atom that has too much capacity. // Is it enough that we could fit this new atom? const cap = big_atom.capacity(elf_file); @@ -134,13 +133,12 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { free_list_removal = i; } break :blk new_start_vaddr; - } else if (maybe_last_atom_index.*) |last_index| { - const last = elf_file.atom(last_index); + } else if (elf_file.atom(last_atom_index.*)) |last| { const ideal_capacity = Elf.padToIdeal(last.size); const ideal_capacity_end_vaddr = last.value + ideal_capacity; const new_start_vaddr = std.mem.alignForward(u64, ideal_capacity_end_vaddr, alignment); // Set up the metadata to be updated, after errors are no longer possible. - atom_placement = last_index; + atom_placement = last.atom_index; break :blk new_start_vaddr; } else { break :blk phdr.p_vaddr; @@ -148,13 +146,13 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { }; const expand_section = if (atom_placement) |placement_index| - elf_file.atom(placement_index).next_index == null + elf_file.atom(placement_index).?.next_index == 0 else true; if (expand_section) { const needed_size = (self.value + self.size) - phdr.p_vaddr; try elf_file.growAllocSection(self.output_section_index, needed_size); - maybe_last_atom_index.* = self.atom_index; + last_atom_index.* = self.atom_index; if (elf_file.dwarf) |_| { // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address @@ -172,23 +170,21 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { // This function can also reallocate an atom. // In this case we need to "unplug" it from its previous location before // plugging it in to its new location. - if (self.prev_index) |prev_index| { - const prev = elf_file.atom(prev_index); + if (elf_file.atom(self.prev_index)) |prev| { prev.next_index = self.next_index; } - if (self.next_index) |next_index| { - const next = elf_file.atom(next_index); + if (elf_file.atom(self.next_index)) |next| { next.prev_index = self.prev_index; } if (atom_placement) |big_atom_index| { - const big_atom = elf_file.atom(big_atom_index); + const big_atom = elf_file.atom(big_atom_index).?; self.prev_index = big_atom_index; self.next_index = big_atom.next_index; big_atom.next_index = self.atom_index; } else { - self.prev_index = null; - self.next_index = null; + self.prev_index = 0; + self.next_index = 0; } if (free_list_removal) |i| { _ = free_list.swapRemove(i); @@ -231,35 +227,33 @@ pub fn free(self: *Atom, elf_file: *Elf) void { } } - const maybe_last_atom_index = &elf_file.sections.items(.last_atom_index)[shndx]; - if (maybe_last_atom_index.*) |last_atom_index| { - if (last_atom_index == self.atom_index) { - if (self.prev_index) |prev_index| { + const last_atom_index = &elf_file.sections.items(.last_atom_index)[shndx]; + if (elf_file.atom(last_atom_index.*)) |last_atom| { + if (last_atom.atom_index == self.atom_index) { + if (elf_file.atom(self.prev_index)) |_| { // TODO shrink the section size here - maybe_last_atom_index.* = prev_index; + last_atom_index.* = self.prev_index; } else { - maybe_last_atom_index.* = null; + last_atom_index.* = 0; } } } - if (self.prev_index) |prev_index| { - const prev = elf_file.atom(prev_index); + if (elf_file.atom(self.prev_index)) |prev| { prev.next_index = self.next_index; - if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) { // The free list is heuristics, it doesn't have to be perfect, so we can // ignore the OOM here. - free_list.append(gpa, prev_index) catch {}; + free_list.append(gpa, prev.atom_index) catch {}; } } else { - self.prev_index = null; + self.prev_index = 0; } - if (self.next_index) |next_index| { - elf_file.atom(next_index).prev_index = self.prev_index; + if (elf_file.atom(self.next_index)) |next| { + next.prev_index = self.prev_index; } else { - self.next_index = null; + self.next_index = 0; } self.* = .{}; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index eb29d06fee..be177922a8 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -36,7 +36,7 @@ pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file_ptr = symbol.file(elf_file).?; // if (file_ptr == .shared) return symbol.sourceSymbol(elf_file).st_shndx == elf.SHN_ABS; return !symbol.flags.import and symbol.atom(elf_file) == null and symbol.output_section_index == 0 and - file_ptr != .linker_defined and file_ptr != .zig_module; + file_ptr != .linker_defined; } pub fn isLocal(symbol: Symbol) bool { @@ -175,7 +175,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const st_shndx = blk: { // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?; // if (file_ptr == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; - if (symbol.atom(elf_file) == null and file_ptr != .linker_defined and file_ptr != .zig_module) + if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; break :blk symbol.output_section_index; }; diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 0ac9d05b81..b0d500f689 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -1,3 +1,5 @@ +/// Path is owned by Module and lives as long as *Module. +path: []const u8, index: File.Index, elf_local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, @@ -23,26 +25,29 @@ pub fn deinit(self: *ZigModule, allocator: Allocator) void { pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { const gpa = elf_file.base.allocator; const atom_index = try elf_file.addAtom(); - const symbol_index = try elf_file.addSymbol(); - - const atom_ptr = elf_file.atom(atom_index); + const symbol_index = try self.addLocal(elf_file); + const atom_ptr = elf_file.atom(atom_index).?; atom_ptr.file_index = self.index; atom_ptr.output_section_index = output_section_index; - const symbol_ptr = elf_file.symbol(symbol_index); - symbol_ptr.file_index = self.index; symbol_ptr.atom_index = atom_index; symbol_ptr.output_section_index = output_section_index; - symbol_ptr.esym_index = @as(Symbol.Index, @intCast(self.elf_local_symbols.items.len)); + const local_esym = symbol_ptr.sourceSymbol(elf_file); + local_esym.st_shndx = output_section_index; + try self.atoms.append(gpa, atom_index); + return symbol_index; +} +pub fn addLocal(self: *ZigModule, elf_file: *Elf) !Symbol.Index { + const gpa = elf_file.base.allocator; + const symbol_index = try elf_file.addSymbol(); + const symbol_ptr = elf_file.symbol(symbol_index); + symbol_ptr.file_index = self.index; + symbol_ptr.esym_index = @as(Symbol.Index, @intCast(self.elf_local_symbols.items.len)); const local_esym = try self.elf_local_symbols.addOne(gpa); local_esym.* = Elf.null_sym; local_esym.st_info = elf.STB_LOCAL << 4; - local_esym.st_shndx = output_section_index; - - try self.atoms.append(gpa, atom_index); try self.local_symbols.putNoClobber(gpa, symbol_index, {}); - return symbol_index; } -- cgit v1.2.3 From 465431807b318aba078c911419fd74d44e24a9ae Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 8 Sep 2023 22:53:53 +0200 Subject: elf: write $got symbols into the symtab --- src/link/Elf.zig | 10 +++++++ src/link/Elf/synthetic_sections.zig | 58 +++++++------------------------------ 2 files changed, 21 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 0aeb0ebd4b..4a40cb80a7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2736,6 +2736,11 @@ fn updateSymtabSize(self: *Elf) !void { sizes.nglobals += zig_module.output_symtab_size.nglobals; } + if (self.got_section_index) |_| { + self.got.updateSymtabSize(self); + sizes.nlocals += self.got.output_symtab_size.nlocals; + } + const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; shdr.sh_info = sizes.nlocals + 1; self.markDirty(self.symtab_section_index.?, null); @@ -2780,6 +2785,11 @@ fn writeSymtab(self: *Elf) !void { ctx.iglobal += zig_module.output_symtab_size.nglobals; } + if (self.got_section_index) |_| { + try self.got.writeSymtab(self, ctx); + ctx.ilocal += self.got.output_symtab_size.nlocals; + } + const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); switch (self.ptr_width) { .p32 => { diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index fce23aeddb..00d3b41b61 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -356,52 +356,30 @@ pub const GotSection = struct { // return num; // } - pub fn calcSymtabSize(got: *GotSection, elf_file: *Elf) !void { - got.output_symtab_size.nlocals = @as(u32, @intCast(got.symbols.items.len)); - for (got.symbols.items) |sym| { - const suffix_len = switch (sym) { - .tlsgd => "$tlsgd".len, - .got => "$got".len, - .gottp => "$gottp".len, - .tlsdesc => "$tlsdesc".len, - }; - const symbol = elf_file.getSymbol(sym.getIndex()); - const name_len = symbol.getName(elf_file).len; - got.output_symtab_size.strsize += @as(u32, @intCast(name_len + suffix_len + 1)); - } - - if (got.emit_tlsld) { - got.output_symtab_size.nlocals += 1; - got.output_symtab_size.strsize += @as(u32, @intCast("$tlsld".len + 1)); - } + pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void { + _ = elf_file; + got.output_symtab_size.nlocals = @as(u32, @intCast(got.entries.items.len)); } pub fn writeSymtab(got: GotSection, elf_file: *Elf, ctx: anytype) !void { const gpa = elf_file.base.allocator; - - var ilocal = ctx.ilocal; - for (got.symbols.items) |sym| { - const suffix = switch (sym) { + for (got.entries.items, ctx.ilocal..) |entry, ilocal| { + const suffix = switch (entry.tag) { + .tlsld => "$tlsld", .tlsgd => "$tlsgd", .got => "$got", .gottp => "$gottp", .tlsdesc => "$tlsdesc", }; - const symbol = elf_file.getSymbol(sym.getIndex()); - const name = try std.fmt.allocPrint(gpa, "{s}{s}", .{ symbol.getName(elf_file), suffix }); + const symbol = elf_file.symbol(entry.symbol_index); + const name = try std.fmt.allocPrint(gpa, "{s}{s}", .{ symbol.name(elf_file), suffix }); defer gpa.free(name); - const st_name = try ctx.strtab.insert(gpa, name); - const st_value = switch (sym) { - // .tlsgd => symbol.tlsGdAddress(elf_file), + const st_name = try elf_file.strtab.insert(gpa, name); + const st_value = switch (entry.tag) { .got => symbol.gotAddress(elf_file), - // .gottp => symbol.gotTpAddress(elf_file), - // .tlsdesc => symbol.tlsDescAddress(elf_file), else => unreachable, }; - const st_size: u64 = switch (sym) { - .tlsgd, .tlsdesc => 16, - .got, .gottp => 8, - }; + const st_size: u64 = entry.len() * elf_file.archPtrWidthBytes(); ctx.symtab[ilocal] = .{ .st_name = st_name, .st_info = elf.STT_OBJECT, @@ -410,21 +388,7 @@ pub const GotSection = struct { .st_value = st_value, .st_size = st_size, }; - ilocal += 1; } - - // if (got.emit_tlsld) { - // const st_name = try ctx.strtab.insert(gpa, "$tlsld"); - // ctx.symtab[ilocal] = .{ - // .st_name = st_name, - // .st_info = elf.STT_OBJECT, - // .st_other = 0, - // .st_shndx = elf_file.got_sect_index.?, - // .st_value = elf_file.getTlsLdAddress(), - // .st_size = 16, - // }; - // ilocal += 1; - // } } const FormatCtx = struct { -- cgit v1.2.3 From a3372050b184aa2d103f11afcd1f87bf6077fa3d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 8 Sep 2023 23:21:44 +0200 Subject: elf: rename program_headers to phdrs --- src/link/Elf.zig | 86 +++++++++++++++++++++++++-------------------------- src/link/Elf/Atom.zig | 6 ++-- 2 files changed, 45 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 4a40cb80a7..9e15b1f208 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -17,7 +17,7 @@ shdr_table_offset: ?u64 = null, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. -program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, +phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, /// The index into the program headers of the PT_PHDR program header phdr_table_index: ?u16 = null, /// The index into the program headers of the PT_LOAD program header containing the phdr @@ -232,7 +232,7 @@ pub fn deinit(self: *Elf) void { } self.sections.deinit(gpa); - self.program_headers.deinit(gpa); + self.phdrs.deinit(gpa); self.shstrtab.deinit(gpa); self.strtab.deinit(gpa); self.symbols.deinit(gpa); @@ -317,10 +317,10 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { return test_end; } } - for (self.program_headers.items) |program_header| { - const increased_size = padToIdeal(program_header.p_filesz); - const test_end = program_header.p_offset + increased_size; - if (end > program_header.p_offset and start < test_end) { + for (self.phdrs.items) |phdr| { + const increased_size = padToIdeal(phdr.p_filesz); + const test_end = phdr.p_offset + increased_size; + if (end > phdr.p_offset and start < test_end) { return test_end; } } @@ -338,9 +338,9 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { if (section.sh_offset <= start) continue; if (section.sh_offset < min_pos) min_pos = section.sh_offset; } - for (self.program_headers.items) |program_header| { - if (program_header.p_offset <= start) continue; - if (program_header.p_offset < min_pos) min_pos = program_header.p_offset; + for (self.phdrs.items) |phdr| { + if (phdr.p_offset <= start) continue; + if (phdr.p_offset < min_pos) min_pos = phdr.p_offset; } return min_pos - start; } @@ -364,12 +364,12 @@ pub fn populateMissingMetadata(self: *Elf) !void { const ptr_size: u8 = self.ptrWidthBytes(); if (self.phdr_table_index == null) { - self.phdr_table_index = @as(u16, @intCast(self.program_headers.items.len)); + self.phdr_table_index = @as(u16, @intCast(self.phdrs.items.len)); const p_align: u16 = switch (self.ptr_width) { .p32 => @alignOf(elf.Elf32_Phdr), .p64 => @alignOf(elf.Elf64_Phdr), }; - try self.program_headers.append(gpa, .{ + try self.phdrs.append(gpa, .{ .p_type = elf.PT_PHDR, .p_offset = 0, .p_filesz = 0, @@ -383,11 +383,11 @@ pub fn populateMissingMetadata(self: *Elf) !void { } if (self.phdr_table_load_index == null) { - self.phdr_table_load_index = @as(u16, @intCast(self.program_headers.items.len)); + self.phdr_table_load_index = @as(u16, @intCast(self.phdrs.items.len)); // TODO Same as for GOT const phdr_addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0x1000000 else 0x1000; const p_align = self.page_size; - try self.program_headers.append(gpa, .{ + try self.phdrs.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = 0, .p_filesz = 0, @@ -401,13 +401,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { } if (self.phdr_load_re_index == null) { - self.phdr_load_re_index = @as(u16, @intCast(self.program_headers.items.len)); + self.phdr_load_re_index = @as(u16, @intCast(self.phdrs.items.len)); const file_size = self.base.options.program_code_size_hint; const p_align = self.page_size; const off = self.findFreeSpace(file_size, p_align); log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size }); const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; - try self.program_headers.append(gpa, .{ + try self.phdrs.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -422,7 +422,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { } if (self.phdr_got_index == null) { - self.phdr_got_index = @as(u16, @intCast(self.program_headers.items.len)); + self.phdr_got_index = @as(u16, @intCast(self.phdrs.items.len)); const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint; // We really only need ptr alignment but since we are using PROGBITS, linux requires // page align. @@ -433,7 +433,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { // we'll need to re-use that function anyway, in case the GOT grows and overlaps something // else in virtual memory. const got_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x4000000 else 0x8000; - try self.program_headers.append(gpa, .{ + try self.phdrs.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -447,7 +447,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { } if (self.phdr_load_ro_index == null) { - self.phdr_load_ro_index = @as(u16, @intCast(self.program_headers.items.len)); + self.phdr_load_ro_index = @as(u16, @intCast(self.phdrs.items.len)); // TODO Find a hint about how much data need to be in rodata ? const file_size = 1024; // Same reason as for GOT @@ -456,7 +456,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size }); // TODO Same as for GOT const rodata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0xc000000 else 0xa000; - try self.program_headers.append(gpa, .{ + try self.phdrs.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -470,7 +470,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { } if (self.phdr_load_rw_index == null) { - self.phdr_load_rw_index = @as(u16, @intCast(self.program_headers.items.len)); + self.phdr_load_rw_index = @as(u16, @intCast(self.phdrs.items.len)); // TODO Find a hint about how much data need to be in data ? const file_size = 1024; // Same reason as for GOT @@ -479,7 +479,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size }); // TODO Same as for GOT const rwdata_addr: u32 = if (self.base.options.target.ptrBitWidth() >= 32) 0x10000000 else 0xc000; - try self.program_headers.append(gpa, .{ + try self.phdrs.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, .p_filesz = file_size, @@ -544,7 +544,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { if (self.text_section_index == null) { self.text_section_index = @as(u16, @intCast(self.sections.slice().len)); - const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + const phdr = &self.phdrs.items[self.phdr_load_re_index.?]; try self.sections.append(gpa, .{ .shdr = .{ @@ -566,7 +566,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { if (self.got_section_index == null) { self.got_section_index = @as(u16, @intCast(self.sections.slice().len)); - const phdr = &self.program_headers.items[self.phdr_got_index.?]; + const phdr = &self.phdrs.items[self.phdr_got_index.?]; try self.sections.append(gpa, .{ .shdr = .{ @@ -588,7 +588,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { if (self.rodata_section_index == null) { self.rodata_section_index = @as(u16, @intCast(self.sections.slice().len)); - const phdr = &self.program_headers.items[self.phdr_load_ro_index.?]; + const phdr = &self.phdrs.items[self.phdr_load_ro_index.?]; try self.sections.append(gpa, .{ .shdr = .{ @@ -610,7 +610,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { if (self.data_section_index == null) { self.data_section_index = @as(u16, @intCast(self.sections.slice().len)); - const phdr = &self.program_headers.items[self.phdr_load_rw_index.?]; + const phdr = &self.phdrs.items[self.phdr_load_rw_index.?]; try self.sections.append(gpa, .{ .shdr = .{ @@ -858,7 +858,7 @@ pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { // TODO Also detect virtual address collisions. const shdr = &self.sections.items(.shdr)[shdr_index]; const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const phdr = &self.program_headers.items[phdr_index]; + const phdr = &self.phdrs.items[phdr_index]; const last_atom_index = self.sections.items(.last_atom_index)[shdr_index]; if (needed_size > self.allocatedSize(shdr.sh_offset)) { @@ -1078,7 +1078,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (self.debug_info_header_dirty) { // Currently only one compilation unit is supported, so the address range is simply // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + const text_phdr = &self.phdrs.items[self.phdr_load_re_index.?]; const low_pc = text_phdr.p_vaddr; const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; try dw.writeDbgInfoHeader(module, low_pc, high_pc); @@ -1088,7 +1088,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (self.debug_aranges_section_dirty) { // Currently only one compilation unit is supported, so the address range is simply // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + const text_phdr = &self.phdrs.items[self.phdr_load_re_index.?]; try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); if (!self.shdr_table_dirty) { // Then it won't get written with the others and we need to do it. @@ -1110,11 +1110,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node }; const phdr_table_index = self.phdr_table_index.?; - const phdr_table = &self.program_headers.items[phdr_table_index]; - const phdr_table_load = &self.program_headers.items[self.phdr_table_load_index.?]; + const phdr_table = &self.phdrs.items[phdr_table_index]; + const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?]; const allocated_size = self.allocatedSize(phdr_table.p_offset); - const needed_size = self.program_headers.items.len * phsize; + const needed_size = self.phdrs.items.len * phsize; if (needed_size > allocated_size) { phdr_table.p_offset = 0; // free the space @@ -1133,11 +1133,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node switch (self.ptr_width) { .p32 => { - const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); + const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len); defer gpa.free(buf); for (buf, 0..) |*phdr, i| { - phdr.* = progHeaderTo32(self.program_headers.items[i]); + phdr.* = progHeaderTo32(self.phdrs.items[i]); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); } @@ -1145,11 +1145,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); }, .p64 => { - const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); + const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len); defer gpa.free(buf); for (buf, 0..) |*phdr, i| { - phdr.* = self.program_headers.items[i]; + phdr.* = self.phdrs.items[i]; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); } @@ -2018,7 +2018,7 @@ fn writeElfHeader(self: *Elf) !void { const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; - const phdr_table_offset = self.program_headers.items[self.phdr_table_index.?].p_offset; + const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset; switch (self.ptr_width) { .p32 => { mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian); @@ -2065,7 +2065,7 @@ fn writeElfHeader(self: *Elf) !void { mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian); index += 2; - const e_phnum = @as(u16, @intCast(self.program_headers.items.len)); + const e_phnum = @as(u16, @intCast(self.phdrs.items.len)); mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian); index += 2; @@ -2264,7 +2264,7 @@ fn updateDeclCode( } const phdr_index = self.sections.items(.phdr_index)[shdr_index]; - const section_offset = sym.value - self.program_headers.items[phdr_index].p_vaddr; + const section_offset = sym.value - self.phdrs.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; if (self.base.child_pid) |pid| { @@ -2494,7 +2494,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. const got_index = try local_sym.getOrCreateGotEntry(self); try self.got.writeEntry(self, got_index); - const section_offset = atom_ptr.value - self.program_headers.items[phdr_index].p_vaddr; + const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[local_sym.output_section_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); } @@ -2564,7 +2564,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module try unnamed_consts.append(gpa, atom_ptr.atom_index); - const section_offset = atom_ptr.value - self.program_headers.items[phdr_index].p_vaddr; + const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); @@ -2684,17 +2684,17 @@ pub fn deleteDeclExport( fn writeProgHeader(self: *Elf, index: usize) !void { const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); - const offset = self.program_headers.items[index].p_offset; + const offset = self.phdrs.items[index].p_offset; switch (self.ptr_width) { .p32 => { - var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; + var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.phdrs.items[index])}; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Phdr, &phdr[0]); } return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); }, .p64 => { - var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; + var phdr = [1]elf.Elf64_Phdr{self.phdrs.items[index]}; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Phdr, &phdr[0]); } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 289db3394c..6c83350561 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -79,8 +79,6 @@ pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { } pub fn allocate(self: *Atom, elf_file: *Elf) !void { - const phdr_index = elf_file.sections.items(.phdr_index)[self.output_section_index]; - const phdr = &elf_file.program_headers.items[phdr_index]; const shdr = &elf_file.sections.items(.shdr)[self.output_section_index]; const free_list = &elf_file.sections.items(.free_list)[self.output_section_index]; const last_atom_index = &elf_file.sections.items(.last_atom_index)[self.output_section_index]; @@ -141,7 +139,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { atom_placement = last.atom_index; break :blk new_start_vaddr; } else { - break :blk phdr.p_vaddr; + break :blk shdr.sh_addr; } }; @@ -150,7 +148,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { else true; if (expand_section) { - const needed_size = (self.value + self.size) - phdr.p_vaddr; + const needed_size = (self.value + self.size) - shdr.sh_addr; try elf_file.growAllocSection(self.output_section_index, needed_size); last_atom_index.* = self.atom_index; -- cgit v1.2.3 From a6e9163284f75a9b251167eb7efb9b77169c5640 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 9 Sep 2023 16:03:39 +0200 Subject: elf: clean up naming and remove unused functions --- src/link/Elf.zig | 81 +++++++++++++++++++++----------------------------------- 1 file changed, 30 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9e15b1f208..90b28d2e0c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -44,7 +44,6 @@ strtab: StringTable(.strtab) = .{}, got: GotSection = .{}, -symtab_section_index: ?u16 = null, text_section_index: ?u16 = null, rodata_section_index: ?u16 = null, got_section_index: ?u16 = null, @@ -56,6 +55,7 @@ debug_aranges_section_index: ?u16 = null, debug_line_section_index: ?u16 = null, shstrtab_section_index: ?u16 = null, strtab_section_index: ?u16 = null, +symtab_section_index: ?u16 = null, symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, @@ -1070,7 +1070,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try dw.writeDbgAbbrev(); if (!self.shdr_table_dirty) { // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_abbrev_section_index.?); + try self.writeShdr(self.debug_abbrev_section_index.?); } self.debug_abbrev_section_dirty = false; } @@ -1092,7 +1092,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try dw.writeDbgAranges(text_phdr.p_vaddr, text_phdr.p_memsz); if (!self.shdr_table_dirty) { // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_aranges_section_index.?); + try self.writeShdr(self.debug_aranges_section_index.?); } self.debug_aranges_section_dirty = false; } @@ -1137,7 +1137,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node defer gpa.free(buf); for (buf, 0..) |*phdr, i| { - phdr.* = progHeaderTo32(self.phdrs.items[i]); + phdr.* = phdrTo32(self.phdrs.items[i]); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); } @@ -1219,7 +1219,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node defer gpa.free(buf); for (buf, 0..) |*shdr, i| { - shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); + shdr.* = shdrTo32(slice.items(.shdr)[i]); log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); @@ -2682,50 +2682,6 @@ pub fn deleteDeclExport( sym_index.* = 0; } -fn writeProgHeader(self: *Elf, index: usize) !void { - const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); - const offset = self.phdrs.items[index].p_offset; - switch (self.ptr_width) { - .p32 => { - var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.phdrs.items[index])}; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Phdr, &phdr[0]); - } - return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); - }, - .p64 => { - var phdr = [1]elf.Elf64_Phdr{self.phdrs.items[index]}; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Phdr, &phdr[0]); - } - return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); - }, - } -} - -fn writeSectHeader(self: *Elf, index: usize) !void { - const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); - switch (self.ptr_width) { - .p32 => { - var shdr: [1]elf.Elf32_Shdr = undefined; - shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]); - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]); - } - const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); - return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); - }, - .p64 => { - var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]}; - if (foreign_endian) { - mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]); - } - const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); - return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); - }, - } -} - fn updateSymtabSize(self: *Elf) !void { var sizes = SymtabSize{}; @@ -2832,7 +2788,7 @@ pub fn archPtrWidthBytes(self: Elf) u8 { return @as(u8, @intCast(@divExact(self.base.options.target.ptrBitWidth(), 8))); } -fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { +fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { return .{ .p_type = phdr.p_type, .p_flags = phdr.p_flags, @@ -2845,7 +2801,30 @@ fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { }; } -fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { +fn writeShdr(self: *Elf, index: usize) !void { + const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); + switch (self.ptr_width) { + .p32 => { + var shdr: [1]elf.Elf32_Shdr = undefined; + shdr[0] = shdrTo32(self.sections.items(.shdr)[index]); + if (foreign_endian) { + mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]); + } + const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); + return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); + }, + .p64 => { + var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]}; + if (foreign_endian) { + mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]); + } + const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); + return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); + }, + } +} + +fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { return .{ .sh_name = shdr.sh_name, .sh_type = shdr.sh_type, -- cgit v1.2.3 From a455b5692a657d01f07f5228a1abcb38f1abcc38 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Sep 2023 08:41:13 +0200 Subject: elf: create required linker-defined symbols --- src/link/Elf.zig | 92 +++++++++++++++++++++++++++++++++++++++--- src/link/Elf/LinkerDefined.zig | 16 ++++---- src/link/Elf/Symbol.zig | 2 +- src/link/Elf/file.zig | 7 ++-- 4 files changed, 99 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 90b28d2e0c..e858a0ecb2 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -36,6 +36,7 @@ phdr_load_rw_index: ?u16 = null, entry_addr: ?u64 = null, page_size: u32, +default_sym_version: elf.Elf64_Versym, /// .shstrtab buffer shstrtab: StringTable(.strtab) = .{}, @@ -57,6 +58,23 @@ shstrtab_section_index: ?u16 = null, strtab_section_index: ?u16 = null, symtab_section_index: ?u16 = null, +// Linker-defined symbols +dynamic_index: ?Symbol.Index = null, +ehdr_start_index: ?Symbol.Index = null, +init_array_start_index: ?Symbol.Index = null, +init_array_end_index: ?Symbol.Index = null, +fini_array_start_index: ?Symbol.Index = null, +fini_array_end_index: ?Symbol.Index = null, +preinit_array_start_index: ?Symbol.Index = null, +preinit_array_end_index: ?Symbol.Index = null, +got_index: ?Symbol.Index = null, +plt_index: ?Symbol.Index = null, +end_index: ?Symbol.Index = null, +gnu_eh_frame_hdr_index: ?Symbol.Index = null, +dso_handle_index: ?Symbol.Index = null, +rela_iplt_start_index: ?Symbol.Index = null, +rela_iplt_end_index: ?Symbol.Index = null, + symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, @@ -188,6 +206,10 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { .sparc64 => 0x2000, else => 0x1000, }; + const default_sym_version: elf.Elf64_Versym = if (options.output_mode == .Lib and options.link_mode == .Dynamic) + elf.VER_NDX_GLOBAL + else + elf.VER_NDX_LOCAL; var dwarf: ?Dwarf = if (!options.strip and options.module != null) Dwarf.init(gpa, &self.base, options.target) @@ -204,6 +226,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { .dwarf = dwarf, .ptr_width = ptr_width, .page_size = page_size, + .default_sym_version = default_sym_version, }; const use_llvm = options.use_llvm; if (use_llvm) { @@ -987,11 +1010,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // corresponds to the Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; - self.linker_defined_index = blk: { - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); - self.files.set(index, .{ .linker_defined = .{ .index = index } }); - break :blk index; + const compiler_rt_path: ?[]const u8 = blk: { + if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; + if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; + break :blk null; }; + _ = compiler_rt_path; if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but @@ -1023,6 +1047,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try dw.flushModule(module); } + if (self.linker_defined_index == null) { + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + self.files.set(index, .{ .linker_defined = .{ .index = index } }); + self.linker_defined_index = index; + } + + try self.addLinkerDefinedSymbols(); + + // Beyond this point, everything has been allocated a virtual address and we can resolve + // the relocations. { var it = self.relocs.iterator(); while (it.next()) |entry| { @@ -2682,6 +2716,54 @@ pub fn deleteDeclExport( sym_index.* = 0; } +fn addLinkerDefinedSymbols(self: *Elf) !void { + const linker_defined_index = self.linker_defined_index orelse return; + const linker_defined = self.file(linker_defined_index).?.linker_defined; + self.dynamic_index = try linker_defined.addGlobal("_DYNAMIC", self); + self.ehdr_start_index = try linker_defined.addGlobal("__ehdr_start", self); + self.init_array_start_index = try linker_defined.addGlobal("__init_array_start", self); + self.init_array_end_index = try linker_defined.addGlobal("__init_array_end", self); + self.fini_array_start_index = try linker_defined.addGlobal("__fini_array_start", self); + self.fini_array_end_index = try linker_defined.addGlobal("__fini_array_end", self); + self.preinit_array_start_index = try linker_defined.addGlobal("__preinit_array_start", self); + self.preinit_array_end_index = try linker_defined.addGlobal("__preinit_array_end", self); + self.got_index = try linker_defined.addGlobal("_GLOBAL_OFFSET_TABLE_", self); + self.plt_index = try linker_defined.addGlobal("_PROCEDURE_LINKAGE_TABLE_", self); + self.end_index = try linker_defined.addGlobal("_end", self); + + if (self.base.options.eh_frame_hdr) { + self.gnu_eh_frame_hdr_index = try linker_defined.addGlobal("__GNU_EH_FRAME_HDR", self); + } + + if (self.globalByName("__dso_handle")) |index| { + if (self.symbol(index).file(self) == null) + self.dso_handle_index = try linker_defined.addGlobal("__dso_handle", self); + } + + self.rela_iplt_start_index = try linker_defined.addGlobal("__rela_iplt_start", self); + self.rela_iplt_end_index = try linker_defined.addGlobal("__rela_iplt_end", self); + + // for (self.objects.items) |index| { + // const object = self.getFile(index).?.object; + // for (object.atoms.items) |atom_index| { + // if (self.getStartStopBasename(atom_index)) |name| { + // const gpa = self.base.allocator; + // try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2); + + // const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); + // defer gpa.free(start); + // const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); + // defer gpa.free(stop); + + // self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(start, self)); + // self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(stop, self)); + // } + // } + // } + + linker_defined.resolveSymbols(self); +} + fn updateSymtabSize(self: *Elf) !void { var sizes = SymtabSize{}; @@ -3193,7 +3275,7 @@ pub fn getOrPutGlobal(self: *Elf, name_off: u32) !GetOrPutGlobalResult { }; } -pub fn getGlobalByName(self: *Elf, name: []const u8) ?Symbol.Index { +pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index { const name_off = self.strtab.getOffset(name) orelse return null; return self.resolver.get(name_off); } diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 99125a9eb6..9bfff35302 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -22,7 +22,7 @@ pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 .st_value = 0, .st_size = 0, }); - const off = try elf_file.internString("{s}", .{name}); + const off = try elf_file.strtab.insert(gpa, name); const gop = try elf_file.getOrPutGlobal(off); self.symbols.addOneAssumeCapacity().* = gop.index; return gop.index; @@ -37,14 +37,12 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { const global = elf_file.symbol(index); if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) { - global.* = .{ - .value = 0, - .name = global.name, - .atom = 0, - .file = self.index, - .sym_idx = sym_idx, - .ver_idx = elf_file.default_sym_version, - }; + global.value = 0; + global.name_offset = global.name_offset; + global.atom_index = 0; + global.file_index = self.index; + global.esym_index = sym_idx; + global.version_index = elf_file.default_sym_version; } } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index be177922a8..322e7609eb 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -81,7 +81,7 @@ pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { // .object => |x| !x.alive, else => false, }; - return file_ptr.symbolRank(sym, in_archive); + return file_ptr.symbolRank(sym.*, in_archive); } pub fn address(symbol: Symbol, opts: struct { diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 82d2d89c29..823ae5209e 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -25,8 +25,8 @@ pub const File = union(enum) { switch (file) { .zig_module => try writer.writeAll("(zig module)"), .linker_defined => try writer.writeAll("(linker defined)"), - .object => |x| try writer.print("{}", .{x.fmtPath()}), - .shared_object => |x| try writer.writeAll(x.path), + // .object => |x| try writer.print("{}", .{x.fmtPath()}), + // .shared_object => |x| try writer.writeAll(x.path), } } @@ -62,7 +62,8 @@ pub const File = union(enum) { pub fn symbolRank(file: File, sym: elf.Elf64_Sym, in_archive: bool) u32 { const base: u3 = blk: { if (sym.st_shndx == elf.SHN_COMMON) break :blk if (in_archive) 6 else 5; - if (file == .shared or in_archive) break :blk switch (sym.st_bind()) { + // if (file == .shared or in_archive) break :blk switch (sym.st_bind()) { + if (in_archive) break :blk switch (sym.st_bind()) { elf.STB_GLOBAL => 3, else => 4, }; -- cgit v1.2.3 From 1c3fd16c3777d8bf61e681a49fbcff0803dd9609 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Sep 2023 09:10:00 +0200 Subject: elf: write linker-defined symbols to symtab --- src/link/Elf.zig | 12 +++++++++++ src/link/Elf/LinkerDefined.zig | 46 +++++++++++++++++------------------------- 2 files changed, 31 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e858a0ecb2..535550c8fc 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2774,6 +2774,12 @@ fn updateSymtabSize(self: *Elf) !void { sizes.nglobals += zig_module.output_symtab_size.nglobals; } + if (self.linker_defined_index) |index| { + const linker_defined = self.file(index).?.linker_defined; + linker_defined.updateSymtabSize(self); + sizes.nlocals += linker_defined.output_symtab_size.nlocals; + } + if (self.got_section_index) |_| { self.got.updateSymtabSize(self); sizes.nlocals += self.got.output_symtab_size.nlocals; @@ -2823,6 +2829,12 @@ fn writeSymtab(self: *Elf) !void { ctx.iglobal += zig_module.output_symtab_size.nglobals; } + if (self.linker_defined_index) |index| { + const linker_defined = self.file(index).?.linker_defined; + linker_defined.writeSymtab(self, ctx); + ctx.ilocal += linker_defined.output_symtab_size.nlocals; + } + if (self.got_section_index) |_| { try self.got.writeSymtab(self, ctx); ctx.ilocal += self.got.output_symtab_size.nlocals; diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 9bfff35302..3f71e0a58e 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -3,7 +3,7 @@ symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, alive: bool = true, -// output_symtab_size: Elf.SymtabSize = .{}, +output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { self.symtab.deinit(allocator); @@ -56,33 +56,25 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { // } // } -// pub fn calcSymtabSize(self: *InternalObject, elf_file: *Elf) !void { -// if (elf_file.options.strip_all) return; - -// for (self.getGlobals()) |global_index| { -// const global = elf_file.getSymbol(global_index); -// if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; -// global.flags.output_symtab = true; -// self.output_symtab_size.nlocals += 1; -// self.output_symtab_size.strsize += @as(u32, @intCast(global.getName(elf_file).len + 1)); -// } -// } - -// pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: Elf.WriteSymtabCtx) !void { -// if (elf_file.options.strip_all) return; - -// const gpa = elf_file.base.allocator; +pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void { + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.file(elf_file)) |file| if (file.index() != self.index) continue; + global.flags.output_symtab = true; + self.output_symtab_size.nlocals += 1; + } +} -// var ilocal = ctx.ilocal; -// for (self.getGlobals()) |global_index| { -// const global = elf_file.getSymbol(global_index); -// if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; -// if (!global.flags.output_symtab) continue; -// const st_name = try ctx.strtab.insert(gpa, global.getName(elf_file)); -// ctx.symtab[ilocal] = global.asElfSym(st_name, elf_file); -// ilocal += 1; -// } -// } +pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void { + var ilocal = ctx.ilocal; + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.file(elf_file)) |file| if (file.index() != self.index) continue; + if (!global.flags.output_symtab) continue; + global.setOutputSym(elf_file, &ctx.symtab[ilocal]); + ilocal += 1; + } +} pub fn sourceSymbol(self: *LinkerDefined, symbol_index: Symbol.Index) *elf.Elf64_Sym { return &self.symtab.items[symbol_index]; -- cgit v1.2.3 From a3ce011408802ba81cb52753c65dbcd2254b9e75 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Sep 2023 11:10:16 +0200 Subject: elf: allocate linker defined symbols --- src/link/Elf.zig | 197 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 178 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 535550c8fc..c8cdd31105 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -47,8 +47,13 @@ got: GotSection = .{}, text_section_index: ?u16 = null, rodata_section_index: ?u16 = null, -got_section_index: ?u16 = null, data_section_index: ?u16 = null, +dynamic_section_index: ?u16 = null, +got_section_index: ?u16 = null, +got_plt_section_index: ?u16 = null, +plt_section_index: ?u16 = null, +eh_frame_hdr_section_index: ?u16 = null, +rela_dyn_section_index: ?u16 = null, debug_info_section_index: ?u16 = null, debug_abbrev_section_index: ?u16 = null, debug_str_section_index: ?u16 = null, @@ -74,6 +79,7 @@ gnu_eh_frame_hdr_index: ?Symbol.Index = null, dso_handle_index: ?Symbol.Index = null, rela_iplt_start_index: ?Symbol.Index = null, rela_iplt_end_index: ?Symbol.Index = null, +start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, @@ -264,6 +270,7 @@ pub fn deinit(self: *Elf) void { self.got.deinit(gpa); self.resolver.deinit(gpa); self.unresolved.deinit(gpa); + self.start_stop_indexes.deinit(gpa); { var it = self.decls.iterator(); @@ -385,6 +392,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p64 => false, }; const ptr_size: u8 = self.ptrWidthBytes(); + const image_base = self.calcImageBase(); if (self.phdr_table_index == null) { self.phdr_table_index = @as(u16, @intCast(self.phdrs.items.len)); @@ -396,8 +404,8 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p_type = elf.PT_PHDR, .p_offset = 0, .p_filesz = 0, - .p_vaddr = 0, - .p_paddr = 0, + .p_vaddr = image_base, + .p_paddr = image_base, .p_memsz = 0, .p_align = p_align, .p_flags = elf.PF_R, @@ -408,16 +416,14 @@ pub fn populateMissingMetadata(self: *Elf) !void { if (self.phdr_table_load_index == null) { self.phdr_table_load_index = @as(u16, @intCast(self.phdrs.items.len)); // TODO Same as for GOT - const phdr_addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0x1000000 else 0x1000; - const p_align = self.page_size; try self.phdrs.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = 0, .p_filesz = 0, - .p_vaddr = phdr_addr, - .p_paddr = phdr_addr, + .p_vaddr = image_base, + .p_paddr = image_base, .p_memsz = 0, - .p_align = p_align, + .p_align = self.page_size, .p_flags = elf.PF_R, }); self.phdr_table_dirty = true; @@ -429,7 +435,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { const p_align = self.page_size; const off = self.findFreeSpace(file_size, p_align); log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size }); - const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; + const entry_addr = self.defaultEntryAddress(); try self.phdrs.append(gpa, .{ .p_type = elf.PT_LOAD, .p_offset = off, @@ -1055,6 +1061,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.addLinkerDefinedSymbols(); + self.allocateLinkerDefinedSymbols(); + // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations. { @@ -2764,6 +2772,129 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { linker_defined.resolveSymbols(self); } +fn allocateLinkerDefinedSymbols(self: *Elf) void { + // _DYNAMIC + if (self.dynamic_section_index) |shndx| { + const shdr = self.sections.items(.shdr)[shndx]; + const symbol_ptr = self.symbol(self.dynamic_index.?); + symbol_ptr.value = shdr.sh_addr; + symbol_ptr.output_section_index = shndx; + } + + // __ehdr_start + { + const symbol_ptr = self.symbol(self.ehdr_start_index.?); + symbol_ptr.value = self.calcImageBase(); + symbol_ptr.output_section_index = 1; + } + + // __init_array_start, __init_array_end + if (self.sectionByName(".init_array")) |shndx| { + const start_sym = self.symbol(self.init_array_start_index.?); + const end_sym = self.symbol(self.init_array_end_index.?); + const shdr = &self.sections.items(.shdr)[shndx]; + start_sym.output_section_index = shndx; + start_sym.value = shdr.sh_addr; + end_sym.output_section_index = shndx; + end_sym.value = shdr.sh_addr + shdr.sh_size; + } + + // __fini_array_start, __fini_array_end + if (self.sectionByName(".fini_array")) |shndx| { + const start_sym = self.symbol(self.fini_array_start_index.?); + const end_sym = self.symbol(self.fini_array_end_index.?); + const shdr = &self.sections.items(.shdr)[shndx]; + start_sym.output_section_index = shndx; + start_sym.value = shdr.sh_addr; + end_sym.output_section_index = shndx; + end_sym.value = shdr.sh_addr + shdr.sh_size; + } + + // __preinit_array_start, __preinit_array_end + if (self.sectionByName(".preinit_array")) |shndx| { + const start_sym = self.symbol(self.preinit_array_start_index.?); + const end_sym = self.symbol(self.preinit_array_end_index.?); + const shdr = &self.sections.items(.shdr)[shndx]; + start_sym.output_section_index = shndx; + start_sym.value = shdr.sh_addr; + end_sym.output_section_index = shndx; + end_sym.value = shdr.sh_addr + shdr.sh_size; + } + + // _GLOBAL_OFFSET_TABLE_ + if (self.got_plt_section_index) |shndx| { + const shdr = &self.sections.items(.shdr)[shndx]; + const symbol_ptr = self.symbol(self.got_index.?); + symbol_ptr.value = shdr.sh_addr; + symbol_ptr.output_section_index = shndx; + } + + // _PROCEDURE_LINKAGE_TABLE_ + if (self.plt_section_index) |shndx| { + const shdr = &self.sections.items(.shdr)[shndx]; + const symbol_ptr = self.symbol(self.plt_index.?); + symbol_ptr.value = shdr.sh_addr; + symbol_ptr.output_section_index = shndx; + } + + // __dso_handle + if (self.dso_handle_index) |index| { + const shdr = &self.sections.items(.shdr)[1]; + const symbol_ptr = self.symbol(index); + symbol_ptr.value = shdr.sh_addr; + symbol_ptr.output_section_index = 0; + } + + // __GNU_EH_FRAME_HDR + if (self.eh_frame_hdr_section_index) |shndx| { + const shdr = &self.sections.items(.shdr)[shndx]; + const symbol_ptr = self.symbol(self.gnu_eh_frame_hdr_index.?); + symbol_ptr.value = shdr.sh_addr; + symbol_ptr.output_section_index = shndx; + } + + // __rela_iplt_start, __rela_iplt_end + if (self.rela_dyn_section_index) |shndx| blk: { + if (self.base.options.link_mode != .Static or self.base.options.pie) break :blk; + const shdr = &self.sections.items(.shdr)[shndx]; + const end_addr = shdr.sh_addr + shdr.sh_size; + const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); + const start_sym = self.symbol(self.rela_iplt_start_index.?); + const end_sym = self.symbol(self.rela_iplt_end_index.?); + start_sym.value = start_addr; + start_sym.output_section_index = shndx; + end_sym.value = end_addr; + end_sym.output_section_index = shndx; + } + + // _end + { + const end_symbol = self.symbol(self.end_index.?); + for (self.sections.items(.shdr), 0..) |shdr, shndx| { + if (shdr.sh_flags & elf.SHF_ALLOC != 0) { + end_symbol.value = shdr.sh_addr + shdr.sh_size; + end_symbol.output_section_index = @intCast(shndx); + } + } + } + + // __start_*, __stop_* + { + var index: usize = 0; + while (index < self.start_stop_indexes.items.len) : (index += 2) { + const start = self.symbol(self.start_stop_indexes.items[index]); + const name = start.name(self); + const stop = self.symbol(self.start_stop_indexes.items[index + 1]); + const shndx = self.sectionByName(name["__start_".len..]).?; + const shdr = self.sections.items(.shdr)[shndx]; + start.value = shdr.sh_addr; + start.output_section_index = shndx; + stop.value = shdr.sh_addr + shdr.sh_size; + stop.output_section_index = shndx; + } + } +} + fn updateSymtabSize(self: *Elf) !void { var sizes = SymtabSize{}; @@ -2774,17 +2905,17 @@ fn updateSymtabSize(self: *Elf) !void { sizes.nglobals += zig_module.output_symtab_size.nglobals; } + if (self.got_section_index) |_| { + self.got.updateSymtabSize(self); + sizes.nlocals += self.got.output_symtab_size.nlocals; + } + if (self.linker_defined_index) |index| { const linker_defined = self.file(index).?.linker_defined; linker_defined.updateSymtabSize(self); sizes.nlocals += linker_defined.output_symtab_size.nlocals; } - if (self.got_section_index) |_| { - self.got.updateSymtabSize(self); - sizes.nlocals += self.got.output_symtab_size.nlocals; - } - const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; shdr.sh_info = sizes.nlocals + 1; self.markDirty(self.symtab_section_index.?, null); @@ -2829,17 +2960,17 @@ fn writeSymtab(self: *Elf) !void { ctx.iglobal += zig_module.output_symtab_size.nglobals; } + if (self.got_section_index) |_| { + try self.got.writeSymtab(self, ctx); + ctx.ilocal += self.got.output_symtab_size.nlocals; + } + if (self.linker_defined_index) |index| { const linker_defined = self.file(index).?.linker_defined; linker_defined.writeSymtab(self, ctx); ctx.ilocal += linker_defined.output_symtab_size.nlocals; } - if (self.got_section_index) |_| { - try self.got.writeSymtab(self, ctx); - ctx.ilocal += self.got.output_symtab_size.nlocals; - } - const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); switch (self.ptr_width) { .p32 => { @@ -3179,6 +3310,34 @@ const CsuObjects = struct { } }; +pub fn calcImageBase(self: Elf) u64 { + if (self.base.options.pic) return 0; // TODO flag an error if PIC and image_base_override + return self.base.options.image_base_override orelse switch (self.ptr_width) { + .p32 => 0x1000, + .p64 => 0x1000000, + }; +} + +pub fn defaultEntryAddress(self: Elf) u64 { + if (self.entry_addr) |addr| return addr; + return switch (self.base.options.target.cpu.arch) { + .spu_2 => 0, + else => default_entry_addr, + }; +} + +pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 { + for (self.sections.items(.shdr), 0..) |shdr, i| { + const this_name = self.shstrtab.getAssumeExists(shdr.sh_name); + if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i)); + } else return null; +} + +pub fn calcNumIRelativeRelocs(self: *Elf) u64 { + _ = self; + unreachable; // TODO +} + pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom { if (atom_index == 0) return null; assert(atom_index < self.atoms.items.len); -- cgit v1.2.3 From 4b082d89c9cd6e40259701f21344d55eef2a8d2e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Sep 2023 21:58:42 +0200 Subject: elf: decouple notion of atom free list from shdrs Not every section will have the need for such a free list. --- src/link/Dwarf.zig | 24 +- src/link/Elf.zig | 496 +++++++++++++++++------------------- src/link/Elf/Atom.zig | 12 +- src/link/Elf/synthetic_sections.zig | 4 +- 4 files changed, 250 insertions(+), 286 deletions(-) (limited to 'src') diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 252388cc8b..fab6d3107c 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1108,7 +1108,7 @@ pub fn commitDeclState( switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_line_sect = &elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; + const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?]; const file_pos = debug_line_sect.sh_offset + src_fn.off; try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); }, @@ -1170,7 +1170,7 @@ pub fn commitDeclState( const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_line_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); - const debug_line_sect = elf_file.sections.items(.shdr)[shdr_index]; + const debug_line_sect = elf_file.shdrs.items[shdr_index]; const file_pos = debug_line_sect.sh_offset + src_fn.off; try pwriteDbgLineNops( elf_file.base.file.?, @@ -1337,7 +1337,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; + const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?]; const file_pos = debug_info_sect.sh_offset + atom.off; try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); }, @@ -1415,7 +1415,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []cons const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_info_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); - const debug_info_sect = elf_file.sections.items(.shdr)[shdr_index]; + const debug_info_sect = &elf_file.shdrs.items[shdr_index]; const file_pos = debug_info_sect.sh_offset + atom.off; try pwriteDbgInfoNops( elf_file.base.file.?, @@ -1496,7 +1496,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, mod: *Module, decl_index: Module.Decl. switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; + const shdr = elf_file.shdrs.items[elf_file.debug_line_section_index.?]; const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff(); try elf_file.base.file.?.pwriteAll(&data, file_pos); }, @@ -1713,7 +1713,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_abbrev_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false); - const debug_abbrev_sect = elf_file.sections.items(.shdr)[shdr_index]; + const debug_abbrev_sect = &elf_file.shdrs.items[shdr_index]; const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset; try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); }, @@ -1828,7 +1828,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_info_sect = elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; + const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?]; const file_pos = debug_info_sect.sh_offset; try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); }, @@ -2147,7 +2147,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_aranges_section_index.?; try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false); - const debug_aranges_sect = elf_file.sections.items(.shdr)[shdr_index]; + const debug_aranges_sect = &elf_file.shdrs.items[shdr_index]; const file_pos = debug_aranges_sect.sh_offset; try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos); }, @@ -2312,9 +2312,9 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; const shdr_index = elf_file.debug_line_section_index.?; - const needed_size = elf_file.sections.items(.shdr)[shdr_index].sh_size + delta; + const needed_size = elf_file.shdrs.items[shdr_index].sh_size + delta; try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); - const file_pos = elf_file.sections.items(.shdr)[shdr_index].sh_offset + first_fn.off; + const file_pos = elf_file.shdrs.items[shdr_index].sh_offset + first_fn.off; const amt = try elf_file.base.file.?.preadAll(buffer, file_pos); if (amt != buffer.len) return error.InputOutput; @@ -2377,7 +2377,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_line_sect = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; + const debug_line_sect = &elf_file.shdrs.items[elf_file.debug_line_section_index.?]; const file_pos = debug_line_sect.sh_offset; try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); }, @@ -2500,7 +2500,7 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { switch (self.bin_file.tag) { .elf => { const elf_file = self.bin_file.cast(File.Elf).?; - const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; + const debug_info_sect = &elf_file.shdrs.items[elf_file.debug_info_section_index.?]; break :blk debug_info_sect.sh_offset; }, .macho => { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c8cdd31105..2fade5c82f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -12,7 +12,10 @@ linker_defined_index: ?File.Index = null, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. -sections: std.MultiArrayList(Section) = .{}, +shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, +/// Given index to a section, pulls index of containing phdr if any. +phdr_to_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, +/// File offset into the shdr table. shdr_table_offset: ?u64 = null, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. @@ -85,7 +88,6 @@ symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, - symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, phdr_table_dirty: bool = false, @@ -109,6 +111,8 @@ decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, /// List of atoms that are owned directly by the linker. atoms: std.ArrayListUnmanaged(Atom) = .{}, +/// Table of last atom index in a section and matching atom free list if any. +last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFreeList) = .{}, /// Table of unnamed constants associated with a parent `Decl`. /// We store them here so that we can free the constants whenever the `Decl` @@ -176,21 +180,18 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option try self.atoms.append(allocator, .{}); // Append null file at index 0 try self.files.append(allocator, .null); - // There must always be a null section in index 0 - try self.sections.append(allocator, .{ - .shdr = .{ - .sh_name = 0, - .sh_type = elf.SHT_NULL, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = 0, - .sh_size = 0, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 0, - .sh_entsize = 0, - }, - .phdr_index = undefined, + // There must always be a null shdr in index 0 + try self.shdrs.append(allocator, .{ + .sh_name = 0, + .sh_type = elf.SHT_NULL, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = 0, + .sh_size = 0, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 0, + .sh_entsize = 0, }); try self.populateMissingMetadata(); @@ -256,11 +257,8 @@ pub fn deinit(self: *Elf) void { }; self.files.deinit(gpa); - for (self.sections.items(.free_list)) |*free_list| { - free_list.deinit(gpa); - } - self.sections.deinit(gpa); - + self.shdrs.deinit(gpa); + self.phdr_to_shdr_table.deinit(gpa); self.phdrs.deinit(gpa); self.shstrtab.deinit(gpa); self.strtab.deinit(gpa); @@ -281,6 +279,10 @@ pub fn deinit(self: *Elf) void { } self.atoms.deinit(gpa); + for (self.last_atom_and_free_list_table.values()) |*value| { + value.free_list.deinit(gpa); + } + self.last_atom_and_free_list_table.deinit(gpa); self.lazy_syms.deinit(gpa); { @@ -332,7 +334,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { if (self.shdr_table_offset) |off| { const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr); - const tight_size = self.sections.slice().len * shdr_size; + const tight_size = self.shdrs.items.len * shdr_size; const increased_size = padToIdeal(tight_size); const test_end = off + increased_size; if (end > off and start < test_end) { @@ -340,7 +342,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { } } - for (self.sections.items(.shdr)) |section| { + for (self.shdrs.items) |section| { const increased_size = padToIdeal(section.sh_size); const test_end = section.sh_offset + increased_size; if (end > section.sh_offset and start < test_end) { @@ -364,7 +366,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { if (self.shdr_table_offset) |off| { if (off > start and off < min_pos) min_pos = off; } - for (self.sections.items(.shdr)) |section| { + for (self.shdrs.items) |section| { if (section.sh_offset <= start) continue; if (section.sh_offset < min_pos) min_pos = section.sh_offset; } @@ -522,197 +524,174 @@ pub fn populateMissingMetadata(self: *Elf) !void { } if (self.shstrtab_section_index == null) { - self.shstrtab_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.shstrtab_section_index = @as(u16, @intCast(self.shdrs.items.len)); assert(self.shstrtab.buffer.items.len == 0); try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0 const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1); log.debug("found .shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len }); - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"), - .sh_type = elf.SHT_STRTAB, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = self.shstrtab.buffer.items.len, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 0, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"), + .sh_type = elf.SHT_STRTAB, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = self.shstrtab.buffer.items.len, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 0, }); self.shstrtab_dirty = true; self.shdr_table_dirty = true; } if (self.strtab_section_index == null) { - self.strtab_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.strtab_section_index = @as(u16, @intCast(self.shdrs.items.len)); assert(self.strtab.buffer.items.len == 0); try self.strtab.buffer.append(gpa, 0); // need a 0 at position 0 const off = self.findFreeSpace(self.strtab.buffer.items.len, 1); log.debug("found .strtab free space 0x{x} to 0x{x}", .{ off, off + self.strtab.buffer.items.len }); - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".strtab"), - .sh_type = elf.SHT_STRTAB, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = self.strtab.buffer.items.len, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 0, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".strtab"), + .sh_type = elf.SHT_STRTAB, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = self.strtab.buffer.items.len, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 0, }); self.strtab_dirty = true; self.shdr_table_dirty = true; } if (self.text_section_index == null) { - self.text_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.text_section_index = @as(u16, @intCast(self.shdrs.items.len)); const phdr = &self.phdrs.items[self.phdr_load_re_index.?]; - - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".text"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 0, - }, - .phdr_index = self.phdr_load_re_index.?, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".text"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 0, }); + try self.phdr_to_shdr_table.putNoClobber(gpa, self.text_section_index.?, self.phdr_load_re_index.?); + try self.last_atom_and_free_list_table.putNoClobber(gpa, self.text_section_index.?, .{}); self.shdr_table_dirty = true; } if (self.got_section_index == null) { - self.got_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.got_section_index = @as(u16, @intCast(self.shdrs.items.len)); const phdr = &self.phdrs.items[self.phdr_got_index.?]; - - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".got"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_ALLOC, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = @as(u16, ptr_size), - .sh_entsize = 0, - }, - .phdr_index = self.phdr_got_index.?, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".got"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_ALLOC, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = @as(u16, ptr_size), + .sh_entsize = 0, }); + try self.phdr_to_shdr_table.putNoClobber(gpa, self.got_section_index.?, self.phdr_got_index.?); self.shdr_table_dirty = true; } if (self.rodata_section_index == null) { - self.rodata_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.rodata_section_index = @as(u16, @intCast(self.shdrs.items.len)); const phdr = &self.phdrs.items[self.phdr_load_ro_index.?]; - - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".rodata"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_ALLOC, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 0, - }, - .phdr_index = self.phdr_load_ro_index.?, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".rodata"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_ALLOC, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 0, }); + try self.phdr_to_shdr_table.putNoClobber(gpa, self.rodata_section_index.?, self.phdr_load_ro_index.?); + try self.last_atom_and_free_list_table.putNoClobber(gpa, self.rodata_section_index.?, .{}); self.shdr_table_dirty = true; } if (self.data_section_index == null) { - self.data_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.data_section_index = @as(u16, @intCast(self.shdrs.items.len)); const phdr = &self.phdrs.items[self.phdr_load_rw_index.?]; - - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".data"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, - .sh_addr = phdr.p_vaddr, - .sh_offset = phdr.p_offset, - .sh_size = phdr.p_filesz, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = @as(u16, ptr_size), - .sh_entsize = 0, - }, - .phdr_index = self.phdr_load_rw_index.?, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".data"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, + .sh_addr = phdr.p_vaddr, + .sh_offset = phdr.p_offset, + .sh_size = phdr.p_filesz, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = @as(u16, ptr_size), + .sh_entsize = 0, }); + try self.phdr_to_shdr_table.putNoClobber(gpa, self.data_section_index.?, self.phdr_load_rw_index.?); + try self.last_atom_and_free_list_table.putNoClobber(gpa, self.data_section_index.?, .{}); self.shdr_table_dirty = true; } if (self.symtab_section_index == null) { - self.symtab_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.symtab_section_index = @as(u16, @intCast(self.shdrs.items.len)); const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); const file_size = self.base.options.symbol_count_hint * each_size; const off = self.findFreeSpace(file_size, min_align); log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size }); - - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".symtab"), - .sh_type = elf.SHT_SYMTAB, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size, - // The section header index of the associated string table. - .sh_link = self.strtab_section_index.?, - .sh_info = @as(u32, @intCast(self.symbols.items.len)), - .sh_addralign = min_align, - .sh_entsize = each_size, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".symtab"), + .sh_type = elf.SHT_SYMTAB, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size, + // The section header index of the associated string table. + .sh_link = self.strtab_section_index.?, + .sh_info = @as(u32, @intCast(self.symbols.items.len)), + .sh_addralign = min_align, + .sh_entsize = each_size, }); self.shdr_table_dirty = true; } if (self.dwarf) |*dw| { if (self.debug_str_section_index == null) { - self.debug_str_section_index = @as(u16, @intCast(self.sections.slice().len)); + self.debug_str_section_index = @as(u16, @intCast(self.shdrs.items.len)); assert(dw.strtab.buffer.items.len == 0); try dw.strtab.buffer.append(gpa, 0); - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".debug_str"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, - .sh_addr = 0, - .sh_offset = 0, - .sh_size = 0, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 1, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".debug_str"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, + .sh_addr = 0, + .sh_offset = 0, + .sh_size = 0, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 1, }); self.debug_strtab_dirty = true; self.shdr_table_dirty = true; } if (self.debug_info_section_index == null) { - self.debug_info_section_index = @as(u16, @intCast(self.sections.slice().len)); - + self.debug_info_section_index = @as(u16, @intCast(self.shdrs.items.len)); const file_size_hint = 200; const p_align = 1; const off = self.findFreeSpace(file_size_hint, p_align); @@ -720,28 +699,24 @@ pub fn populateMissingMetadata(self: *Elf) !void { off, off + file_size_hint, }); - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".debug_info"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".debug_info"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, }); self.shdr_table_dirty = true; self.debug_info_header_dirty = true; } if (self.debug_abbrev_section_index == null) { - self.debug_abbrev_section_index = @as(u16, @intCast(self.sections.slice().len)); - + self.debug_abbrev_section_index = @as(u16, @intCast(self.shdrs.items.len)); const file_size_hint = 128; const p_align = 1; const off = self.findFreeSpace(file_size_hint, p_align); @@ -749,28 +724,24 @@ pub fn populateMissingMetadata(self: *Elf) !void { off, off + file_size_hint, }); - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, }); self.shdr_table_dirty = true; self.debug_abbrev_section_dirty = true; } if (self.debug_aranges_section_index == null) { - self.debug_aranges_section_index = @as(u16, @intCast(self.sections.slice().len)); - + self.debug_aranges_section_index = @as(u16, @intCast(self.shdrs.items.len)); const file_size_hint = 160; const p_align = 16; const off = self.findFreeSpace(file_size_hint, p_align); @@ -778,28 +749,24 @@ pub fn populateMissingMetadata(self: *Elf) !void { off, off + file_size_hint, }); - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, }); self.shdr_table_dirty = true; self.debug_aranges_section_dirty = true; } if (self.debug_line_section_index == null) { - self.debug_line_section_index = @as(u16, @intCast(self.sections.slice().len)); - + self.debug_line_section_index = @as(u16, @intCast(self.shdrs.items.len)); const file_size_hint = 250; const p_align = 1; const off = self.findFreeSpace(file_size_hint, p_align); @@ -807,20 +774,17 @@ pub fn populateMissingMetadata(self: *Elf) !void { off, off + file_size_hint, }); - try self.sections.append(gpa, .{ - .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, ".debug_line"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }, - .phdr_index = undefined, + try self.shdrs.append(gpa, .{ + .sh_name = try self.shstrtab.insert(gpa, ".debug_line"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, }); self.shdr_table_dirty = true; self.debug_line_header_dirty = true; @@ -836,7 +800,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p64 => @alignOf(elf.Elf64_Shdr), }; if (self.shdr_table_offset == null) { - self.shdr_table_offset = self.findFreeSpace(self.sections.slice().len * shsize, shalign); + self.shdr_table_offset = self.findFreeSpace(self.shdrs.items.len * shsize, shalign); self.shdr_table_dirty = true; } @@ -854,7 +818,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { // offset + it's filesize. var max_file_offset: u64 = 0; - for (self.sections.items(.shdr)) |shdr| { + for (self.shdrs.items) |shdr| { if (shdr.sh_offset + shdr.sh_size > max_file_offset) { max_file_offset = shdr.sh_offset + shdr.sh_size; } @@ -885,19 +849,17 @@ pub fn populateMissingMetadata(self: *Elf) !void { pub fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { // TODO Also detect virtual address collisions. - const shdr = &self.sections.items(.shdr)[shdr_index]; - const phdr_index = self.sections.items(.phdr_index)[shdr_index]; + const shdr = &self.shdrs.items[shdr_index]; + const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; const phdr = &self.phdrs.items[phdr_index]; - const last_atom_index = self.sections.items(.last_atom_index)[shdr_index]; if (needed_size > self.allocatedSize(shdr.sh_offset)) { // Must move the entire section. const new_offset = self.findFreeSpace(needed_size, self.page_size); - const existing_size = if (self.atom(last_atom_index)) |last| blk: { + const existing_size = if (self.last_atom_and_free_list_table.get(shdr_index)) |meta| blk: { + const last = self.atom(meta.last_atom_index) orelse break :blk 0; break :blk (last.value + last.size) - phdr.p_vaddr; - } else if (shdr_index == self.got_section_index.?) blk: { - break :blk shdr.sh_size; - } else 0; + } else shdr.sh_size; shdr.sh_size = 0; log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{ @@ -927,7 +889,7 @@ pub fn growNonAllocSection( min_alignment: u32, requires_file_copy: bool, ) !void { - const shdr = &self.sections.items(.shdr)[shdr_index]; + const shdr = &self.shdrs.items[shdr_index]; if (needed_size > self.allocatedSize(shdr.sh_offset)) { const existing_size = if (self.symtab_section_index.? == shdr_index) blk: { @@ -940,7 +902,12 @@ pub fn growNonAllocSection( shdr.sh_size = 0; // Move all the symbols to a new file location. const new_offset = self.findFreeSpace(needed_size, min_alignment); - log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{ self.shstrtab.get(shdr.sh_name), shdr.sh_offset, new_offset }); + + log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{ + self.shstrtab.get(shdr.sh_name), + shdr.sh_offset, + new_offset, + }); if (requires_file_copy) { const amt = try self.base.file.?.copyRangeAll( @@ -1071,7 +1038,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const atom_index = entry.key_ptr.*; const relocs = entry.value_ptr.*; const atom_ptr = self.atom(atom_index).?; - const source_shdr = self.sections.items(.shdr)[atom_ptr.output_section_index]; + const source_shdr = &self.shdrs.items[atom_ptr.output_section_index]; log.debug("relocating '{s}'", .{atom_ptr.name(self)}); @@ -1209,9 +1176,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node { const shdr_index = self.shstrtab_section_index.?; - if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) { try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); - const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; + const shstrtab_sect = &self.shdrs.items[shdr_index]; try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); self.shstrtab_dirty = false; } @@ -1219,9 +1186,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node { const shdr_index = self.strtab_section_index.?; - if (self.strtab_dirty or self.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + if (self.strtab_dirty or self.strtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) { try self.growNonAllocSection(shdr_index, self.strtab.buffer.items.len, 1, false); - const strtab_sect = self.sections.items(.shdr)[shdr_index]; + const strtab_sect = self.shdrs.items[shdr_index]; try self.base.file.?.pwriteAll(self.strtab.buffer.items, strtab_sect.sh_offset); self.strtab_dirty = false; } @@ -1229,9 +1196,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (self.dwarf) |dwarf| { const shdr_index = self.debug_str_section_index.?; - if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { + if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.shdrs.items[shdr_index].sh_size) { try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); - const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; + const debug_strtab_sect = self.shdrs.items[shdr_index]; try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); self.debug_strtab_dirty = false; } @@ -1247,7 +1214,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node .p64 => @alignOf(elf.Elf64_Shdr), }; const allocated_size = self.allocatedSize(self.shdr_table_offset.?); - const needed_size = self.sections.slice().len * shsize; + const needed_size = self.shdrs.items.len * shsize; if (needed_size > allocated_size) { self.shdr_table_offset = null; // free the space @@ -1256,12 +1223,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node switch (self.ptr_width) { .p32 => { - const slice = self.sections.slice(); - const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); + const buf = try gpa.alloc(elf.Elf32_Shdr, self.shdrs.items.len); defer gpa.free(buf); for (buf, 0..) |*shdr, i| { - shdr.* = shdrTo32(slice.items(.shdr)[i]); + shdr.* = shdrTo32(self.shdrs.items[i]); log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); @@ -1270,12 +1236,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); }, .p64 => { - const slice = self.sections.slice(); - const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); + const buf = try gpa.alloc(elf.Elf64_Shdr, self.shdrs.items.len); defer gpa.free(buf); for (buf, 0..) |*shdr, i| { - shdr.* = slice.items(.shdr)[i]; + shdr.* = self.shdrs.items[i]; log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); @@ -2118,7 +2083,7 @@ fn writeElfHeader(self: *Elf) !void { mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian); index += 2; - const e_shnum = @as(u16, @intCast(self.sections.slice().len)); + const e_shnum = @as(u16, @intCast(self.shdrs.items.len)); mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); index += 2; @@ -2305,9 +2270,9 @@ fn updateDeclCode( try self.got.writeEntry(self, got_index); } - const phdr_index = self.sections.items(.phdr_index)[shdr_index]; + const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; const section_offset = sym.value - self.phdrs.items[phdr_index].p_vaddr; - const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; + const file_offset = self.shdrs.items[shdr_index].sh_offset + section_offset; if (self.base.child_pid) |pid| { switch (builtin.os.tag) { @@ -2510,7 +2475,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. }; const local_sym = self.symbol(symbol_index); - const phdr_index = self.sections.items(.phdr_index)[local_sym.output_section_index]; + const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?; local_sym.name_offset = name_str_index; const local_esym = local_sym.sourceSymbol(self); local_esym.st_name = name_str_index; @@ -2537,7 +2502,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. try self.got.writeEntry(self, got_index); const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; - const file_offset = self.sections.items(.shdr)[local_sym.output_section_index].sh_offset + section_offset; + const file_offset = self.shdrs.items[local_sym.output_section_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); } @@ -2584,7 +2549,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const required_alignment = typed_value.ty.abiAlignment(mod); const shdr_index = self.rodata_section_index.?; - const phdr_index = self.sections.items(.phdr_index)[shdr_index]; + const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; const local_sym = self.symbol(sym_index); local_sym.name_offset = name_str_index; const local_esym = local_sym.sourceSymbol(self); @@ -2607,7 +2572,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module try unnamed_consts.append(gpa, atom_ptr.atom_index); const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; - const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; + const file_offset = self.shdrs.items[shdr_index].sh_offset + section_offset; try self.base.file.?.pwriteAll(code, file_offset); return sym_index; @@ -2775,7 +2740,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { fn allocateLinkerDefinedSymbols(self: *Elf) void { // _DYNAMIC if (self.dynamic_section_index) |shndx| { - const shdr = self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; const symbol_ptr = self.symbol(self.dynamic_index.?); symbol_ptr.value = shdr.sh_addr; symbol_ptr.output_section_index = shndx; @@ -2792,7 +2757,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { if (self.sectionByName(".init_array")) |shndx| { const start_sym = self.symbol(self.init_array_start_index.?); const end_sym = self.symbol(self.init_array_end_index.?); - const shdr = &self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; start_sym.output_section_index = shndx; start_sym.value = shdr.sh_addr; end_sym.output_section_index = shndx; @@ -2803,7 +2768,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { if (self.sectionByName(".fini_array")) |shndx| { const start_sym = self.symbol(self.fini_array_start_index.?); const end_sym = self.symbol(self.fini_array_end_index.?); - const shdr = &self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; start_sym.output_section_index = shndx; start_sym.value = shdr.sh_addr; end_sym.output_section_index = shndx; @@ -2814,7 +2779,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { if (self.sectionByName(".preinit_array")) |shndx| { const start_sym = self.symbol(self.preinit_array_start_index.?); const end_sym = self.symbol(self.preinit_array_end_index.?); - const shdr = &self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; start_sym.output_section_index = shndx; start_sym.value = shdr.sh_addr; end_sym.output_section_index = shndx; @@ -2823,7 +2788,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // _GLOBAL_OFFSET_TABLE_ if (self.got_plt_section_index) |shndx| { - const shdr = &self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; const symbol_ptr = self.symbol(self.got_index.?); symbol_ptr.value = shdr.sh_addr; symbol_ptr.output_section_index = shndx; @@ -2831,7 +2796,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // _PROCEDURE_LINKAGE_TABLE_ if (self.plt_section_index) |shndx| { - const shdr = &self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; const symbol_ptr = self.symbol(self.plt_index.?); symbol_ptr.value = shdr.sh_addr; symbol_ptr.output_section_index = shndx; @@ -2839,7 +2804,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // __dso_handle if (self.dso_handle_index) |index| { - const shdr = &self.sections.items(.shdr)[1]; + const shdr = &self.shdrs.items[1]; const symbol_ptr = self.symbol(index); symbol_ptr.value = shdr.sh_addr; symbol_ptr.output_section_index = 0; @@ -2847,7 +2812,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // __GNU_EH_FRAME_HDR if (self.eh_frame_hdr_section_index) |shndx| { - const shdr = &self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; const symbol_ptr = self.symbol(self.gnu_eh_frame_hdr_index.?); symbol_ptr.value = shdr.sh_addr; symbol_ptr.output_section_index = shndx; @@ -2856,7 +2821,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // __rela_iplt_start, __rela_iplt_end if (self.rela_dyn_section_index) |shndx| blk: { if (self.base.options.link_mode != .Static or self.base.options.pie) break :blk; - const shdr = &self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; const end_addr = shdr.sh_addr + shdr.sh_size; const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); const start_sym = self.symbol(self.rela_iplt_start_index.?); @@ -2870,7 +2835,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // _end { const end_symbol = self.symbol(self.end_index.?); - for (self.sections.items(.shdr), 0..) |shdr, shndx| { + for (self.shdrs.items, 0..) |*shdr, shndx| { if (shdr.sh_flags & elf.SHF_ALLOC != 0) { end_symbol.value = shdr.sh_addr + shdr.sh_size; end_symbol.output_section_index = @intCast(shndx); @@ -2886,7 +2851,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { const name = start.name(self); const stop = self.symbol(self.start_stop_indexes.items[index + 1]); const shndx = self.sectionByName(name["__start_".len..]).?; - const shdr = self.sections.items(.shdr)[shndx]; + const shdr = &self.shdrs.items[shndx]; start.value = shdr.sh_addr; start.output_section_index = shndx; stop.value = shdr.sh_addr + shdr.sh_size; @@ -2916,7 +2881,7 @@ fn updateSymtabSize(self: *Elf) !void { sizes.nlocals += linker_defined.output_symtab_size.nlocals; } - const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; + const shdr = &self.shdrs.items[self.symtab_section_index.?]; shdr.sh_info = sizes.nlocals + 1; self.markDirty(self.symtab_section_index.?, null); @@ -2934,7 +2899,7 @@ fn updateSymtabSize(self: *Elf) !void { fn writeSymtab(self: *Elf) !void { const gpa = self.base.allocator; - const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?]; + const shdr = &self.shdrs.items[self.symtab_section_index.?]; const sym_size: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), @@ -3031,7 +2996,7 @@ fn writeShdr(self: *Elf, index: usize) !void { switch (self.ptr_width) { .p32 => { var shdr: [1]elf.Elf32_Shdr = undefined; - shdr[0] = shdrTo32(self.sections.items(.shdr)[index]); + shdr[0] = shdrTo32(self.shdrs.items[index]); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]); } @@ -3039,7 +3004,7 @@ fn writeShdr(self: *Elf, index: usize) !void { return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); }, .p64 => { - var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]}; + var shdr = [1]elf.Elf64_Shdr{self.shdrs.items[index]}; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]); } @@ -3327,7 +3292,7 @@ pub fn defaultEntryAddress(self: Elf) u64 { } pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 { - for (self.sections.items(.shdr), 0..) |shdr, i| { + for (self.shdrs.items, 0..) |*shdr, i| { const this_name = self.shstrtab.getAssumeExists(shdr.sh_name); if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i)); } else return null; @@ -3481,10 +3446,7 @@ const default_entry_addr = 0x8000000; pub const base_tag: link.File.Tag = .elf; -const Section = struct { - shdr: elf.Elf64_Shdr, - phdr_index: u16, - +const LastAtomAndFreeList = struct { /// Index of the last allocated atom in this section. last_atom_index: Atom.Index = 0, diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 6c83350561..ead42917a1 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -79,9 +79,10 @@ pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { } pub fn allocate(self: *Atom, elf_file: *Elf) !void { - const shdr = &elf_file.sections.items(.shdr)[self.output_section_index]; - const free_list = &elf_file.sections.items(.free_list)[self.output_section_index]; - const last_atom_index = &elf_file.sections.items(.last_atom_index)[self.output_section_index]; + const shdr = &elf_file.shdrs.items[self.output_section_index]; + const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?; + const free_list = &meta.free_list; + const last_atom_index = &meta.last_atom_index; const new_atom_ideal_capacity = Elf.padToIdeal(self.size); const alignment = try std.math.powi(u64, 2, self.alignment); @@ -208,7 +209,9 @@ pub fn free(self: *Atom, elf_file: *Elf) void { const gpa = elf_file.base.allocator; const shndx = self.output_section_index; - const free_list = &elf_file.sections.items(.free_list)[shndx]; + const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?; + const free_list = &meta.free_list; + const last_atom_index = &meta.last_atom_index; var already_have_free_list_node = false; { var i: usize = 0; @@ -225,7 +228,6 @@ pub fn free(self: *Atom, elf_file: *Elf) void { } } - const last_atom_index = &elf_file.sections.items(.last_atom_index)[shndx]; if (elf_file.atom(last_atom_index.*)) |last_atom| { if (last_atom.atom_index == self.atom_index) { if (elf_file.atom(self.prev_index)) |_| { diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 00d3b41b61..f178f1370f 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -29,7 +29,7 @@ pub const GotSection = struct { pub fn address(entry: Entry, elf_file: *Elf) u64 { const ptr_bytes = @as(u64, elf_file.archPtrWidthBytes()); - const shdr = &elf_file.sections.items(.shdr)[elf_file.got_section_index.?]; + const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?]; return shdr.sh_addr + @as(u64, entry.cell_index) * ptr_bytes; } }; @@ -124,7 +124,7 @@ pub const GotSection = struct { } const endian = elf_file.base.options.target.cpu.arch.endian(); const entry = got.entries.items[index]; - const shdr = &elf_file.sections.items(.shdr)[elf_file.got_section_index.?]; + const shdr = &elf_file.shdrs.items[elf_file.got_section_index.?]; const off = shdr.sh_offset + @as(u64, entry_size) * entry.cell_index; const vaddr = shdr.sh_addr + @as(u64, entry_size) * entry.cell_index; const value = elf_file.symbol(entry.symbol_index).value; -- cgit v1.2.3 From d07edfabd6e45a486d1611e357887d01b38db32e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Sep 2023 22:46:59 +0200 Subject: elf: simplify handling of relocs for atoms --- src/link/Elf.zig | 56 ++++--------------------------- src/link/Elf/Atom.zig | 81 +++++++++++++++++++++++++++++++-------------- src/link/Elf/Relocation.zig | 8 +++++ src/link/Elf/ZigModule.zig | 12 ++++++- 4 files changed, 82 insertions(+), 75 deletions(-) create mode 100644 src/link/Elf/Relocation.zig (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 2fade5c82f..e9b804801f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -135,10 +135,6 @@ last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFre /// with `Decl` `main`, and lives as long as that `Decl`. unnamed_consts: UnnamedConstTable = .{}, -/// A table of relocations indexed by the owning them `TextBlock`. -relocs: RelocTable = .{}, - -const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc)); const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); @@ -293,14 +289,6 @@ pub fn deinit(self: *Elf) void { self.unnamed_consts.deinit(gpa); } - { - var it = self.relocs.valueIterator(); - while (it.next()) |relocs| { - relocs.deinit(gpa); - } - self.relocs.deinit(gpa); - } - if (self.dwarf) |*dw| { dw.deinit(); } @@ -312,12 +300,11 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link. const this_sym_index = try self.getOrCreateMetadataForDecl(decl_index); const this_sym = self.symbol(this_sym_index); const vaddr = this_sym.value; - const parent_atom_index = self.symbol(reloc_info.parent_atom_index).atom_index; - try Atom.addRelocation(self, parent_atom_index, .{ + const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?; + try parent_atom.addReloc(self, .{ .target = this_sym_index, .offset = reloc_info.offset, .addend = reloc_info.addend, - .prev_vaddr = vaddr, }); return vaddr; @@ -1032,38 +1019,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations. - { - var it = self.relocs.iterator(); - while (it.next()) |entry| { - const atom_index = entry.key_ptr.*; - const relocs = entry.value_ptr.*; - const atom_ptr = self.atom(atom_index).?; - const source_shdr = &self.shdrs.items[atom_ptr.output_section_index]; - - log.debug("relocating '{s}'", .{atom_ptr.name(self)}); - - for (relocs.items) |*reloc| { - const target_sym = self.symbol(reloc.target); - const target_vaddr = target_sym.value + reloc.addend; - - if (target_vaddr == reloc.prev_vaddr) continue; - - const section_offset = (atom_ptr.value + reloc.offset) - source_shdr.sh_addr; - const file_offset = source_shdr.sh_offset + section_offset; - - log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ - reloc.offset, - target_vaddr, - target_sym.name(self), - }); - - switch (self.ptr_width) { - .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset), - .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset), - } - - reloc.prev_vaddr = target_vaddr; - } + if (self.zig_module_index) |index| { + for (self.file(index).?.zig_module.atoms.items) |atom_index| { + try self.atom(atom_index).?.resolveRelocs(self); } } @@ -2313,7 +2271,7 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A const sym_index = try self.getOrCreateMetadataForDecl(decl_index); self.freeUnnamedConsts(decl_index); - Atom.freeRelocations(self, self.symbol(sym_index).atom_index); + self.symbol(sym_index).atom(self).?.freeRelocs(self); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2378,7 +2336,7 @@ pub fn updateDecl( } const sym_index = try self.getOrCreateMetadataForDecl(decl_index); - Atom.freeRelocations(self, self.symbol(sym_index).atom_index); + self.symbol(sym_index).atom(self).?.freeRelocs(self); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index ead42917a1..69950f9f32 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -14,13 +14,13 @@ size: u64 = 0, alignment: u8 = 0, /// Index of the input section. -input_section_index: u16 = 0, +input_section_index: Index = 0, /// Index of the output section. output_section_index: u16 = 0, /// Index of the input section containing this atom's relocs. -relocs_section_index: u16 = 0, +relocs_section_index: Index = 0, /// Index of this atom in the linker's atoms table. atom_index: Index = 0, @@ -64,20 +64,6 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { return surplus >= Elf.min_text_capacity; } -pub fn addRelocation(elf_file: *Elf, atom_index: Index, reloc: Reloc) !void { - const gpa = elf_file.base.allocator; - const gop = try elf_file.relocs.getOrPut(gpa, atom_index); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - try gop.value_ptr.append(gpa, reloc); -} - -pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { - var removed_relocs = elf_file.relocs.fetchRemove(atom_index); - if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); -} - pub fn allocate(self: *Atom, elf_file: *Elf) !void { const shdr = &elf_file.shdrs.items[self.output_section_index]; const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?; @@ -205,8 +191,6 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void { pub fn free(self: *Atom, elf_file: *Elf) void { log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); - Atom.freeRelocations(elf_file, self.atom_index); - const gpa = elf_file.base.allocator; const shndx = self.output_section_index; const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?; @@ -256,23 +240,70 @@ pub fn free(self: *Atom, elf_file: *Elf) void { self.next_index = 0; } + // TODO create relocs free list + self.freeRelocs(elf_file); self.* = .{}; } -pub const Index = u32; +pub fn relocs(self: Atom, elf_file: *Elf) []const Relocation { + const file_ptr = elf_file.file(self.file_index).?; + if (file_ptr != .zig_module) @panic("TODO"); + const zig_module = file_ptr.zig_module; + return zig_module.relocs.items[self.relocs_section_index].items; +} + +pub fn addReloc(self: Atom, elf_file: *Elf, reloc: Relocation) !void { + const gpa = elf_file.base.allocator; + const file_ptr = elf_file.file(self.file_index).?; + assert(file_ptr == .zig_module); + const zig_module = file_ptr.zig_module; + const rels = &zig_module.relocs.items[self.relocs_section_index]; + try rels.append(gpa, reloc); +} + +pub fn freeRelocs(self: Atom, elf_file: *Elf) void { + const file_ptr = elf_file.file(self.file_index).?; + assert(file_ptr == .zig_module); + const zig_module = file_ptr.zig_module; + zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity(); +} + +/// TODO mark relocs dirty +pub fn resolveRelocs(self: Atom, elf_file: *Elf) !void { + relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + const shdr = &elf_file.shdrs.items[self.output_section_index]; + for (self.relocs(elf_file)) |reloc| { + const target_sym = elf_file.symbol(reloc.target); + const target_vaddr = target_sym.value + reloc.addend; + const section_offset = (self.value + reloc.offset) - shdr.sh_addr; + const file_offset = shdr.sh_offset + section_offset; + + relocs_log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ + reloc.offset, + target_vaddr, + target_sym.name(elf_file), + }); + + switch (elf_file.ptr_width) { + .p32 => try elf_file.base.file.?.pwriteAll( + std.mem.asBytes(&@as(u32, @intCast(target_vaddr))), + file_offset, + ), + .p64 => try elf_file.base.file.?.pwriteAll(std.mem.asBytes(&target_vaddr), file_offset), + } + } +} -pub const Reloc = struct { - target: u32, - offset: u64, - addend: u32, - prev_vaddr: u64, -}; +pub const Index = u32; const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; const log = std.log.scoped(.link); +const relocs_log = std.log.scoped(.link_relocs); +const Allocator = std.mem.Allocator; const Atom = @This(); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const Relocation = @import("Relocation.zig"); diff --git a/src/link/Elf/Relocation.zig b/src/link/Elf/Relocation.zig new file mode 100644 index 0000000000..719a211238 --- /dev/null +++ b/src/link/Elf/Relocation.zig @@ -0,0 +1,8 @@ +target: Symbol.Index, +offset: u64, +addend: u32, + +const std = @import("std"); + +const Symbol = @import("Symbol.zig"); +const Relocation = @This(); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index b0d500f689..b7616edc81 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -9,6 +9,7 @@ elf_global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, +relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)) = .{}, alive: bool = true, @@ -20,6 +21,10 @@ pub fn deinit(self: *ZigModule, allocator: Allocator) void { self.elf_global_symbols.deinit(allocator); self.global_symbols.deinit(allocator); self.atoms.deinit(allocator); + for (self.relocs.items) |*list| { + list.deinit(allocator); + } + self.relocs.deinit(allocator); } pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { @@ -34,6 +39,10 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) ! symbol_ptr.output_section_index = output_section_index; const local_esym = symbol_ptr.sourceSymbol(elf_file); local_esym.st_shndx = output_section_index; + const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len)); + const relocs = try self.relocs.addOne(gpa); + relocs.* = .{}; + atom_ptr.relocs_section_index = relocs_index; try self.atoms.append(gpa, atom_index); return symbol_index; } @@ -184,5 +193,6 @@ const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Module = @import("../../Module.zig"); -const ZigModule = @This(); +const Relocation = @import("Relocation.zig"); const Symbol = @import("Symbol.zig"); +const ZigModule = @This(); -- cgit v1.2.3 From 7a9eba2f8597a94e4a6def62253e9bf5220a46af Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 11 Sep 2023 10:52:30 +0200 Subject: elf: emit relocation to an extern function --- src/arch/x86_64/CodeGen.zig | 22 ++++- src/arch/x86_64/Emit.zig | 10 ++- src/link.zig | 2 +- src/link/Elf.zig | 33 ++++++-- src/link/Elf/Atom.zig | 192 +++++++++++++++++++++++++++++++++++++++----- src/link/Elf/Relocation.zig | 8 -- src/link/Elf/ZigModule.zig | 29 ++++++- 7 files changed, 252 insertions(+), 44 deletions(-) delete mode 100644 src/link/Elf/Relocation.zig (limited to 'src') diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 07bc4c9200..bd63231890 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -125,7 +125,9 @@ const Owner = union(enum) { .func_index => |func_index| { const mod = ctx.bin_file.options.module.?; const decl_index = mod.funcOwnerDeclIndex(func_index); - if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { + if (ctx.bin_file.cast(link.File.Elf)) |elf_file| { + return elf_file.getOrCreateMetadataForDecl(decl_index); + } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { const atom = try macho_file.getOrCreateAtomForDecl(decl_index); return macho_file.getAtom(atom).getSymbolIndex().?; } else if (ctx.bin_file.cast(link.File.Coff)) |coff_file| { @@ -136,7 +138,10 @@ const Owner = union(enum) { } else unreachable; }, .lazy_sym => |lazy_sym| { - if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { + if (ctx.bin_file.cast(link.File.Elf)) |elf_file| { + return elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err| + ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); + } else if (ctx.bin_file.cast(link.File.MachO)) |macho_file| { const atom = macho_file.getOrCreateAtomForLazySymbol(lazy_sym) catch |err| return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); return macho_file.getAtom(atom).getSymbolIndex().?; @@ -8178,7 +8183,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier } else if (func_value.getExternFunc(mod)) |extern_func| { const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name); const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name); - if (self.bin_file.cast(link.File.Coff)) |coff_file| { + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const atom_index = try self.owner.getSymbolIndex(self); + const sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name); + _ = try self.addInst(.{ + .tag = .call, + .ops = .extern_fn_reloc, + .data = .{ .reloc = .{ + .atom_index = atom_index, + .sym_index = sym_index, + } }, + }); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const atom_index = try self.owner.getSymbolIndex(self); const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name); _ = try self.addInst(.{ diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 050772b2ce..51820740dc 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -41,7 +41,15 @@ pub fn emitMir(emit: *Emit) Error!void { .offset = end_offset - 4, .length = @as(u5, @intCast(end_offset - start_offset)), }), - .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + .linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| { + // Add relocation to the decl. + const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; + try atom_ptr.addReloc(elf_file, .{ + .r_offset = end_offset, + .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | std.elf.R_X86_64_PLT32, + .r_addend = -4, + }); + } else if (emit.bin_file.cast(link.File.MachO)) |macho_file| { // Add relocation to the decl. const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; const target = macho_file.getGlobalByIndex(symbol.sym_index); diff --git a/src/link.zig b/src/link.zig index 634f9679c4..c7609eabb7 100644 --- a/src/link.zig +++ b/src/link.zig @@ -549,7 +549,7 @@ pub const File = struct { switch (base.tag) { // zig fmt: off .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name, lib_name), - .elf => unreachable, + .elf => return @fieldParentPtr(Elf, "base", base).getGlobalSymbol(name, lib_name), .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name, lib_name), .plan9 => unreachable, .spirv => unreachable, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e9b804801f..a71f70a1a1 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -302,9 +302,9 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link. const vaddr = this_sym.value; const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?; try parent_atom.addReloc(self, .{ - .target = this_sym_index, - .offset = reloc_info.offset, - .addend = reloc_info.addend, + .r_offset = reloc_info.offset, + .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | elf.R_X86_64_64, + .r_addend = reloc_info.addend, }); return vaddr; @@ -1020,8 +1020,17 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations. if (self.zig_module_index) |index| { - for (self.file(index).?.zig_module.atoms.items) |atom_index| { - try self.atom(atom_index).?.resolveRelocs(self); + for (self.file(index).?.zig_module.atoms.keys()) |atom_index| { + const atom_ptr = self.atom(atom_index).?; + if (!atom_ptr.alive) continue; + const shdr = &self.shdrs.items[atom_ptr.output_section_index]; + const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; + const code = try gpa.alloc(u8, atom_ptr.size); + defer gpa.free(code); + const amt = try self.base.file.?.preadAll(code, file_offset); + if (amt != code.len) return error.InputOutput; + try atom_ptr.resolveRelocs(self, code); + try self.base.file.?.pwriteAll(code, file_offset); } } @@ -2185,6 +2194,7 @@ fn updateDeclCode( const shdr_index = sym.output_section_index; sym.name_offset = try self.strtab.insert(gpa, decl_name); + atom_ptr.alive = true; atom_ptr.name_offset = sym.name_offset; esym.st_name = sym.name_offset; esym.st_info |= stt_bits; @@ -2440,6 +2450,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; const atom_ptr = local_sym.atom(self).?; + atom_ptr.alive = true; atom_ptr.name_offset = name_str_index; atom_ptr.alignment = math.log2_int(u64, required_alignment); atom_ptr.size = code.len; @@ -2515,6 +2526,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; const atom_ptr = local_sym.atom(self).?; + atom_ptr.alive = true; atom_ptr.name_offset = name_str_index; atom_ptr.alignment = math.log2_int(u64, required_alignment); atom_ptr.size = code.len; @@ -3374,6 +3386,17 @@ pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index { return self.resolver.get(name_off); } +pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { + _ = lib_name; + const gpa = self.base.allocator; + const name_off = try self.strtab.insert(gpa, name); + const gop = try self.getOrPutGlobal(name_off); + if (!gop.found_existing) { + try self.unresolved.putNoClobber(gpa, name_off, {}); + } + return gop.index; +} + fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { return .{ .data = self }; } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 69950f9f32..dacbb369df 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -26,7 +26,7 @@ relocs_section_index: Index = 0, atom_index: Index = 0, /// Specifies whether this atom is alive or has been garbage collected. -alive: bool = true, +alive: bool = false, /// Specifies if the atom has been visited during garbage collection. visited: bool = false, @@ -192,6 +192,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); const gpa = elf_file.base.allocator; + const zig_module = elf_file.file(self.file_index).?.zig_module; const shndx = self.output_section_index; const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?; const free_list = &meta.free_list; @@ -242,17 +243,18 @@ pub fn free(self: *Atom, elf_file: *Elf) void { // TODO create relocs free list self.freeRelocs(elf_file); + assert(zig_module.atoms.swapRemove(self.atom_index)); self.* = .{}; } -pub fn relocs(self: Atom, elf_file: *Elf) []const Relocation { +pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { const file_ptr = elf_file.file(self.file_index).?; if (file_ptr != .zig_module) @panic("TODO"); const zig_module = file_ptr.zig_module; return zig_module.relocs.items[self.relocs_section_index].items; } -pub fn addReloc(self: Atom, elf_file: *Elf, reloc: Relocation) !void { +pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void { const gpa = elf_file.base.allocator; const file_ptr = elf_file.file(self.file_index).?; assert(file_ptr == .zig_module); @@ -269,31 +271,178 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void { } /// TODO mark relocs dirty -pub fn resolveRelocs(self: Atom, elf_file: *Elf) !void { +pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); - const shdr = &elf_file.shdrs.items[self.output_section_index]; - for (self.relocs(elf_file)) |reloc| { - const target_sym = elf_file.symbol(reloc.target); - const target_vaddr = target_sym.value + reloc.addend; - const section_offset = (self.value + reloc.offset) - shdr.sh_addr; - const file_offset = shdr.sh_offset + section_offset; - - relocs_log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ - reloc.offset, - target_vaddr, - target_sym.name(elf_file), + + var stream = std.io.fixedBufferStream(code); + const cwriter = stream.writer(); + + for (self.relocs(elf_file)) |rel| { + const r_type = rel.r_type(); + if (r_type == elf.R_X86_64_NONE) continue; + + const target = elf_file.symbol(rel.r_sym()); + + // We will use equation format to resolve relocations: + // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ + // + // Address of the source atom. + const P = @as(i64, @intCast(self.value + rel.r_offset)); + // Addend from the relocation. + const A = rel.r_addend; + // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. + const S = @as(i64, @intCast(target.address(.{}, elf_file))); + // Address of the global offset table. + const GOT = blk: { + const shndx = if (elf_file.got_plt_section_index) |shndx| + shndx + else if (elf_file.got_section_index) |shndx| + shndx + else + null; + break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; + }; + // Relative offset to the start of the global offset table. + const G = @as(i64, @intCast(target.gotAddress(elf_file))) - GOT; + // // Address of the thread pointer. + // const TP = @as(i64, @intCast(elf_file.getTpAddress())); + // // Address of the dynamic thread pointer. + // const DTP = @as(i64, @intCast(elf_file.getDtpAddress())); + + relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{ + fmtRelocType(r_type), + rel.r_offset, + P, + S + A, + G + GOT + A, + target.name(elf_file), }); - switch (elf_file.ptr_width) { - .p32 => try elf_file.base.file.?.pwriteAll( - std.mem.asBytes(&@as(u32, @intCast(target_vaddr))), - file_offset, - ), - .p64 => try elf_file.base.file.?.pwriteAll(std.mem.asBytes(&target_vaddr), file_offset), + try stream.seekTo(rel.r_offset); + + switch (rel.r_type()) { + elf.R_X86_64_NONE => unreachable, + elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A), + elf.R_X86_64_PLT32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))), + else => @panic("TODO"), } } } +pub fn fmtRelocType(r_type: u32) std.fmt.Formatter(formatRelocType) { + return .{ .data = r_type }; +} + +fn formatRelocType( + r_type: u32, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + const str = switch (r_type) { + elf.R_X86_64_NONE => "R_X86_64_NONE", + elf.R_X86_64_64 => "R_X86_64_64", + elf.R_X86_64_PC32 => "R_X86_64_PC32", + elf.R_X86_64_GOT32 => "R_X86_64_GOT32", + elf.R_X86_64_PLT32 => "R_X86_64_PLT32", + elf.R_X86_64_COPY => "R_X86_64_COPY", + elf.R_X86_64_GLOB_DAT => "R_X86_64_GLOB_DAT", + elf.R_X86_64_JUMP_SLOT => "R_X86_64_JUMP_SLOT", + elf.R_X86_64_RELATIVE => "R_X86_64_RELATIVE", + elf.R_X86_64_GOTPCREL => "R_X86_64_GOTPCREL", + elf.R_X86_64_32 => "R_X86_64_32", + elf.R_X86_64_32S => "R_X86_64_32S", + elf.R_X86_64_16 => "R_X86_64_16", + elf.R_X86_64_PC16 => "R_X86_64_PC16", + elf.R_X86_64_8 => "R_X86_64_8", + elf.R_X86_64_PC8 => "R_X86_64_PC8", + elf.R_X86_64_DTPMOD64 => "R_X86_64_DTPMOD64", + elf.R_X86_64_DTPOFF64 => "R_X86_64_DTPOFF64", + elf.R_X86_64_TPOFF64 => "R_X86_64_TPOFF64", + elf.R_X86_64_TLSGD => "R_X86_64_TLSGD", + elf.R_X86_64_TLSLD => "R_X86_64_TLSLD", + elf.R_X86_64_DTPOFF32 => "R_X86_64_DTPOFF32", + elf.R_X86_64_GOTTPOFF => "R_X86_64_GOTTPOFF", + elf.R_X86_64_TPOFF32 => "R_X86_64_TPOFF32", + elf.R_X86_64_PC64 => "R_X86_64_PC64", + elf.R_X86_64_GOTOFF64 => "R_X86_64_GOTOFF64", + elf.R_X86_64_GOTPC32 => "R_X86_64_GOTPC32", + elf.R_X86_64_GOT64 => "R_X86_64_GOT64", + elf.R_X86_64_GOTPCREL64 => "R_X86_64_GOTPCREL64", + elf.R_X86_64_GOTPC64 => "R_X86_64_GOTPC64", + elf.R_X86_64_GOTPLT64 => "R_X86_64_GOTPLT64", + elf.R_X86_64_PLTOFF64 => "R_X86_64_PLTOFF64", + elf.R_X86_64_SIZE32 => "R_X86_64_SIZE32", + elf.R_X86_64_SIZE64 => "R_X86_64_SIZE64", + elf.R_X86_64_GOTPC32_TLSDESC => "R_X86_64_GOTPC32_TLSDESC", + elf.R_X86_64_TLSDESC_CALL => "R_X86_64_TLSDESC_CALL", + elf.R_X86_64_TLSDESC => "R_X86_64_TLSDESC", + elf.R_X86_64_IRELATIVE => "R_X86_64_IRELATIVE", + elf.R_X86_64_RELATIVE64 => "R_X86_64_RELATIVE64", + elf.R_X86_64_GOTPCRELX => "R_X86_64_GOTPCRELX", + elf.R_X86_64_REX_GOTPCRELX => "R_X86_64_REX_GOTPCRELX", + elf.R_X86_64_NUM => "R_X86_64_NUM", + else => "R_X86_64_UNKNOWN", + }; + try writer.print("{s}", .{str}); +} + +pub fn format( + atom: Atom, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = atom; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format symbols directly"); +} + +pub fn fmt(atom: Atom, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .atom = atom, + .elf_file = elf_file, + } }; +} + +const FormatContext = struct { + atom: Atom, + elf_file: *Elf, +}; + +fn format2( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = options; + _ = unused_fmt_string; + const atom = ctx.atom; + const elf_file = ctx.elf_file; + try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x})", .{ + atom.atom_index, atom.name(elf_file), atom.value, + atom.output_section_index, atom.alignment, atom.size, + }); + // if (atom.fde_start != atom.fde_end) { + // try writer.writeAll(" : fdes{ "); + // for (atom.getFdes(elf_file), atom.fde_start..) |fde, i| { + // try writer.print("{d}", .{i}); + // if (!fde.alive) try writer.writeAll("([*])"); + // if (i < atom.fde_end - 1) try writer.writeAll(", "); + // } + // try writer.writeAll(" }"); + // } + const gc_sections = if (elf_file.base.options.gc_sections) |gc_sections| gc_sections else false; + if (gc_sections and !atom.alive) { + try writer.writeAll(" : [*]"); + } +} + pub const Index = u32; const std = @import("std"); @@ -306,4 +455,3 @@ const Allocator = std.mem.Allocator; const Atom = @This(); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; -const Relocation = @import("Relocation.zig"); diff --git a/src/link/Elf/Relocation.zig b/src/link/Elf/Relocation.zig deleted file mode 100644 index 719a211238..0000000000 --- a/src/link/Elf/Relocation.zig +++ /dev/null @@ -1,8 +0,0 @@ -target: Symbol.Index, -offset: u64, -addend: u32, - -const std = @import("std"); - -const Symbol = @import("Symbol.zig"); -const Relocation = @This(); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index b7616edc81..760a71e14b 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -8,8 +8,8 @@ local_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, elf_global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, -atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, -relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)) = .{}, +atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{}, +relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, alive: bool = true, @@ -43,7 +43,7 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) ! const relocs = try self.relocs.addOne(gpa); relocs.* = .{}; atom_ptr.relocs_section_index = relocs_index; - try self.atoms.append(gpa, atom_index); + try self.atoms.putNoClobber(gpa, atom_index, {}); return symbol_index; } @@ -184,6 +184,28 @@ fn formatSymtab( } } +pub fn fmtAtoms(self: *ZigModule, elf_file: *Elf) std.fmt.Formatter(formatAtoms) { + return .{ .data = .{ + .self = self, + .elf_file = elf_file, + } }; +} + +fn formatAtoms( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + try writer.writeAll(" atoms\n"); + for (ctx.self.atoms.keys()) |atom_index| { + const atom = ctx.elf_file.atom(atom_index) orelse continue; + try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)}); + } +} + const assert = std.debug.assert; const std = @import("std"); const elf = std.elf; @@ -193,6 +215,5 @@ const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Module = @import("../../Module.zig"); -const Relocation = @import("Relocation.zig"); const Symbol = @import("Symbol.zig"); const ZigModule = @This(); -- cgit v1.2.3 From 65b9597c07ddc6328c9725e0731ae838666d9e20 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 11 Sep 2023 13:17:31 +0200 Subject: elf: report undefined symbols as errors --- src/link.zig | 1 + src/link/Elf.zig | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/link.zig b/src/link.zig index c7609eabb7..4db946658a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -849,6 +849,7 @@ pub const File = struct { pub fn miscErrors(base: *File) []const ErrorMsg { switch (base.tag) { + .elf => return @fieldParentPtr(Elf, "base", base).misc_errors.items, .macho => return @fieldParentPtr(MachO, "base", base).misc_errors.items, else => return &.{}, } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a71f70a1a1..3ebfd6132b 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -87,7 +87,7 @@ start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, -unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, +unresolved: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, phdr_table_dirty: bool = false, @@ -102,6 +102,7 @@ debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, error_flags: link.File.ErrorFlags = link.File.ErrorFlags{}, +misc_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{}, /// Table of tracked LazySymbols. lazy_syms: LazySymbolTable = .{}, @@ -292,6 +293,8 @@ pub fn deinit(self: *Elf) void { if (self.dwarf) |*dw| { dw.deinit(); } + + self.misc_errors.deinit(gpa); } pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -1015,6 +1018,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.addLinkerDefinedSymbols(); + if (self.unresolved.keys().len > 0) try self.reportUndefined(); + self.allocateLinkerDefinedSymbols(); // Beyond this point, everything has been allocated a virtual address and we can resolve @@ -3392,11 +3397,60 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 const name_off = try self.strtab.insert(gpa, name); const gop = try self.getOrPutGlobal(name_off); if (!gop.found_existing) { - try self.unresolved.putNoClobber(gpa, name_off, {}); + try self.unresolved.putNoClobber(gpa, gop.index, {}); } return gop.index; } +fn reportUndefined(self: *Elf) !void { + const gpa = self.base.allocator; + const max_notes = 4; + + try self.misc_errors.ensureUnusedCapacity(gpa, self.unresolved.keys().len); + + for (self.unresolved.keys()) |sym_index| { + const undef_sym = self.symbol(sym_index); + + var all_notes: usize = 0; + var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1); + defer notes.deinit(); + + // Collect all references across all input files + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + for (zig_module.atoms.keys()) |atom_index| { + const atom_ptr = self.atom(atom_index).?; + if (!atom_ptr.alive) continue; + + for (atom_ptr.relocs(self)) |rel| { + if (sym_index == rel.r_sym()) { + const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{ + zig_module.path, + atom_ptr.name(self), + }); + notes.appendAssumeCapacity(.{ .msg = note }); + all_notes += 1; + break; + } + } + } + } + + if (all_notes > max_notes) { + const remaining = all_notes - max_notes; + const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining}); + notes.appendAssumeCapacity(.{ .msg = note }); + } + + var err_msg = link.File.ErrorMsg{ + .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{undef_sym.name(self)}), + }; + err_msg.notes = try notes.toOwnedSlice(); + + self.misc_errors.appendAssumeCapacity(err_msg); + } +} + fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { return .{ .data = self }; } -- cgit v1.2.3 From 3df58a95835fe2f9ba16e9ff9e0d03c39d263bf3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 11 Sep 2023 16:32:58 +0200 Subject: elf: add basic error reporting for positional parsing --- src/link/Elf.zig | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 3ebfd6132b..7ef0bd558c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -980,6 +980,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node }; _ = compiler_rt_path; + // Parse input files + for (self.base.options.objects) |obj| { + const in_file = try std.fs.cwd().openFile(obj.path, .{}); + defer in_file.close(); + + var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; + self.parsePositional(in_file, obj.path, obj.must_link, &parse_ctx) catch |err| + try self.handleAndReportParseError(obj.path, err, &parse_ctx); + } + if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but // anyerror needs to wait for everything to be flushed. @@ -1246,6 +1256,36 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node assert(!self.got.dirty); } +const ParseError = error{ + UnknownFileType, + InvalidCpuArch, + OutOfMemory, + Overflow, + InputOutput, + EndOfStream, + FileSystem, + NotSupported, +} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError; + +fn parsePositional( + self: *Elf, + in_file: std.fs.File, + path: []const u8, + must_link: bool, + ctx: *ParseErrorCtx, +) ParseError!void { + const tracy = trace(@src()); + defer tracy.end(); + + _ = self; + _ = in_file; + _ = path; + _ = must_link; + _ = ctx; + + return error.UnknownFileType; +} + fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -3451,6 +3491,49 @@ fn reportUndefined(self: *Elf) !void { } } +const ParseErrorCtx = struct { + detected_cpu_arch: std.Target.Cpu.Arch, +}; + +fn handleAndReportParseError( + self: *Elf, + path: []const u8, + err: ParseError, + ctx: *const ParseErrorCtx, +) error{OutOfMemory}!void { + const cpu_arch = self.base.options.target.cpu.arch; + switch (err) { + error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}), + error.InvalidCpuArch => try self.reportParseError( + path, + "invalid cpu architecture: expected '{s}', but found '{s}'", + .{ @tagName(cpu_arch), @tagName(ctx.detected_cpu_arch) }, + ), + else => |e| try self.reportParseError( + path, + "unexpected error: parsing object failed with error {s}", + .{@errorName(e)}, + ), + } +} + +fn reportParseError( + self: *Elf, + path: []const u8, + comptime format: []const u8, + args: anytype, +) error{OutOfMemory}!void { + const gpa = self.base.allocator; + try self.misc_errors.ensureUnusedCapacity(gpa, 1); + var notes = try gpa.alloc(link.File.ErrorMsg, 1); + errdefer gpa.free(notes); + notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{path}) }; + self.misc_errors.appendAssumeCapacity(.{ + .msg = try std.fmt.allocPrint(gpa, format, args), + .notes = notes, + }); +} + fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { return .{ .data = self }; } -- cgit v1.2.3 From 67d458370dfc87753e9938e7af94cb86e3bc98cc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 11 Sep 2023 22:49:42 +0200 Subject: elf: add prelim impl of Object parsing --- src/link/Elf.zig | 161 +++++++- src/link/Elf/Atom.zig | 51 ++- src/link/Elf/LinkerDefined.zig | 5 - src/link/Elf/Object.zig | 810 +++++++++++++++++++++++++++++++++++++++++ src/link/Elf/Symbol.zig | 13 +- src/link/Elf/ZigModule.zig | 6 +- src/link/Elf/eh_frame.zig | 445 ++++++++++++++++++++++ src/link/Elf/file.zig | 8 +- 8 files changed, 1459 insertions(+), 40 deletions(-) create mode 100644 src/link/Elf/Object.zig create mode 100644 src/link/Elf/eh_frame.zig (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 7ef0bd558c..b2646d11d8 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -9,6 +9,7 @@ llvm_object: ?*LlvmObject = null, files: std.MultiArrayList(File.Entry) = .{}, zig_module_index: ?File.Index = null, linker_defined_index: ?File.Index = null, +objects: std.ArrayListUnmanaged(File.Index) = .{}, /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. /// Same order as in the file. @@ -51,11 +52,12 @@ got: GotSection = .{}, text_section_index: ?u16 = null, rodata_section_index: ?u16 = null, data_section_index: ?u16 = null, +eh_frame_section_index: ?u16 = null, +eh_frame_hdr_section_index: ?u16 = null, dynamic_section_index: ?u16 = null, got_section_index: ?u16 = null, got_plt_section_index: ?u16 = null, plt_section_index: ?u16 = null, -eh_frame_hdr_section_index: ?u16 = null, rela_dyn_section_index: ?u16 = null, debug_info_section_index: ?u16 = null, debug_abbrev_section_index: ?u16 = null, @@ -136,6 +138,10 @@ last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFre /// with `Decl` `main`, and lives as long as that `Decl`. unnamed_consts: UnnamedConstTable = .{}, +comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{}, +comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{}, +comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{}, + const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); @@ -249,10 +255,11 @@ pub fn deinit(self: *Elf) void { .null => {}, .zig_module => data.zig_module.deinit(gpa), .linker_defined => data.linker_defined.deinit(gpa), - // .object => data.object.deinit(gpa), + .object => data.object.deinit(gpa), // .shared_object => data.shared_object.deinit(gpa), }; self.files.deinit(gpa); + self.objects.deinit(gpa); self.shdrs.deinit(gpa); self.phdr_to_shdr_table.deinit(gpa); @@ -295,6 +302,9 @@ pub fn deinit(self: *Elf) void { } self.misc_errors.deinit(gpa); + self.comdat_groups.deinit(gpa); + self.comdat_groups_owners.deinit(gpa); + self.comdat_groups_table.deinit(gpa); } pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -828,7 +838,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { const zig_module = self.file(index).?.zig_module; const sym_index = try zig_module.addLocal(self); const sym = self.symbol(sym_index); - const esym = sym.sourceSymbol(self); + const esym = zig_module.sourceSymbol(sym_index, self); const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path)); sym.name_offset = name_off; esym.st_name = name_off; @@ -1276,14 +1286,35 @@ fn parsePositional( ) ParseError!void { const tracy = trace(@src()); defer tracy.end(); - - _ = self; - _ = in_file; - _ = path; _ = must_link; - _ = ctx; - return error.UnknownFileType; + if (Object.isObject(in_file)) { + try self.parseObject(in_file, path, ctx); + } else return error.UnknownFileType; +} + +fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseErrorCtx) ParseError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = self.base.allocator; + const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); + const index = @as(File.Index, @intCast(self.files.slice().len)); + + var object = Object{ + .path = path, + .data = data, + .index = index, + }; + errdefer object.deinit(gpa); + try object.parse(self); + + ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?; + if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; + + _ = try self.files.addOne(gpa); + self.files.set(index, .{ .object = object }); + try self.objects.append(gpa, index); } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2226,6 +2257,7 @@ fn updateDeclCode( ) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; const decl = mod.declPtr(decl_index); const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); @@ -2234,7 +2266,7 @@ fn updateDeclCode( const required_alignment = decl.getAlignment(mod); const sym = self.symbol(sym_index); - const esym = sym.sourceSymbol(self); + const esym = zig_module.sourceSymbol(sym_index, self); const atom_ptr = sym.atom(self).?; const shdr_index = sym.output_section_index; @@ -2447,6 +2479,7 @@ pub fn updateDecl( fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void { const gpa = self.base.allocator; const mod = self.base.options.module.?; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; var required_alignment: u32 = undefined; var code_buffer = std.ArrayList(u8).init(gpa); @@ -2490,7 +2523,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. const local_sym = self.symbol(symbol_index); const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?; local_sym.name_offset = name_str_index; - const local_esym = local_sym.sourceSymbol(self); + const local_esym = zig_module.sourceSymbol(symbol_index, self); local_esym.st_name = name_str_index; local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; @@ -2566,7 +2599,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; const local_sym = self.symbol(sym_index); local_sym.name_offset = name_str_index; - const local_esym = local_sym.sourceSymbol(self); + const local_esym = zig_module.sourceSymbol(sym_index, self); local_esym.st_name = name_str_index; local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; @@ -2650,8 +2683,8 @@ pub fn updateDeclExports( }; const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info)); + const zig_module = self.file(self.zig_module_index.?).?.zig_module; const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { - const zig_module = self.file(self.zig_module_index.?).?.zig_module; const sym_index = try zig_module.addGlobal(exp_name, self); try decl_metadata.exports.append(gpa, sym_index); break :blk sym_index; @@ -2660,8 +2693,8 @@ pub fn updateDeclExports( sym.value = decl_sym.value; sym.atom_index = decl_sym.atom_index; sym.output_section_index = decl_sym.output_section_index; - const esym = sym.sourceSymbol(self); - esym.* = decl_esym.*; + const esym = zig_module.sourceSymbol(sym_index, self); + esym.* = decl_esym; esym.st_info = (stb_bits << 4) | stt_bits; } } @@ -2691,11 +2724,12 @@ pub fn deleteDeclExport( const metadata = self.decls.getPtr(decl_index) orelse return; const gpa = self.base.allocator; const mod = self.base.options.module.?; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; const exp_name = mod.intern_pool.stringToSlice(name); const sym_index = metadata.@"export"(self, exp_name) orelse return; log.debug("deleting export '{s}'", .{exp_name}); const sym = self.symbol(sym_index.*); - const esym = sym.sourceSymbol(self); + const esym = zig_module.sourceSymbol(sym_index.*, self); assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant sym.* = .{}; // TODO free list for esym! @@ -3337,6 +3371,7 @@ pub fn file(self: *Elf, index: File.Index) ?File { .null => null, .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined }, .zig_module => .{ .zig_module = &self.files.items(.data)[index].zig_module }, + .object => .{ .object = &self.files.items(.data)[index].object }, }; } @@ -3442,6 +3477,42 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 return gop.index; } +const GetOrCreateComdatGroupOwnerResult = struct { + found_existing: bool, + index: ComdatGroupOwner.Index, +}; + +pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult { + const gpa = self.base.allocator; + const gop = try self.comdat_groups_table.getOrPut(gpa, off); + if (!gop.found_existing) { + const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len)); + const owner = try self.comdat_groups_owners.addOne(gpa); + owner.* = .{}; + gop.value_ptr.* = index; + } + return .{ + .found_existing = gop.found_existing, + .index = gop.value_ptr.*, + }; +} + +pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index { + const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); + _ = try self.comdat_groups.addOne(self.base.allocator); + return index; +} + +pub fn comdatGroup(self: *Elf, index: ComdatGroup.Index) *ComdatGroup { + assert(index < self.comdat_groups.items.len); + return &self.comdat_groups.items[index]; +} + +pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupOwner { + assert(index < self.comdat_groups_owners.items.len); + return &self.comdat_groups_owners.items[index]; +} + fn reportUndefined(self: *Elf) !void { const gpa = self.base.allocator; const max_notes = 4; @@ -3552,6 +3623,21 @@ fn fmtDumpState( try writer.print("zig_module({d}) : {s}\n", .{ index, zig_module.path }); try writer.print("{}\n", .{zig_module.fmtSymtab(self)}); } + + for (self.objects.items) |index| { + const object = self.file(index).?.object; + try writer.print("object({d}) : {}", .{ index, object.fmtPath() }); + if (!object.alive) try writer.writeAll(" : [*]"); + try writer.writeByte('\n'); + try writer.print("{}{}{}{}{}\n", .{ + object.fmtAtoms(self), + object.fmtCies(self), + object.fmtFdes(self), + object.fmtSymtab(self), + object.fmtComdatGroups(self), + }); + } + if (self.linker_defined_index) |index| { const linker_defined = self.file(index).?.linker_defined; try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); @@ -3560,6 +3646,37 @@ fn fmtDumpState( try writer.print("{}\n", .{self.got.fmt(self)}); } +/// Binary search +pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { + if (!@hasDecl(@TypeOf(predicate), "predicate")) + @compileError("Predicate is required to define fn predicate(@This(), T) bool"); + + var min: usize = 0; + var max: usize = haystack.len; + while (min < max) { + const index = (min + max) / 2; + const curr = haystack[index]; + if (predicate.predicate(curr)) { + min = index + 1; + } else { + max = index; + } + } + return min; +} + +/// Linear search +pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { + if (!@hasDecl(@TypeOf(predicate), "predicate")) + @compileError("Predicate is required to define fn predicate(@This(), T) bool"); + + var i: usize = 0; + while (i < haystack.len) : (i += 1) { + if (predicate.predicate(haystack[i])) break; + } + return i; +} + const default_entry_addr = 0x8000000; pub const base_tag: link.File.Tag = .elf; @@ -3607,6 +3724,17 @@ const DeclMetadata = struct { } }; +const ComdatGroupOwner = struct { + file: File.Index = 0, + const Index = u32; +}; + +pub const ComdatGroup = struct { + owner: ComdatGroupOwner.Index, + shndx: u16, + pub const Index = u32; +}; + pub const SymtabSize = struct { nlocals: u32 = 0, nglobals: u32 = 0, @@ -3654,6 +3782,7 @@ const LinkerDefined = @import("Elf/LinkerDefined.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; const Module = @import("../Module.zig"); +const Object = @import("Elf/Object.zig"); const InternPool = @import("../InternPool.zig"); const Package = @import("../Package.zig"); const Symbol = @import("Elf/Symbol.zig"); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index dacbb369df..c163aa8ecc 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -46,6 +46,46 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 { return elf_file.strtab.getAssumeExists(self.name_offset); } +pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr { + const object = elf_file.file(self.file_index).?.object; + return object.shdrs.items[self.input_section_index]; +} + +pub fn codeInObject(self: Atom, elf_file: *Elf) []const u8 { + const object = elf_file.file(self.file_index).?.object; + return object.shdrContents(self.input_section_index); +} + +/// Returns atom's code and optionally uncompresses data if required (for compressed sections). +/// Caller owns the memory. +pub fn codeInObjectUncompressAlloc(self: Atom, elf_file: *Elf) ![]u8 { + const gpa = elf_file.base.allocator; + const data = self.codeInObject(elf_file); + const shdr = self.inputShdr(elf_file); + if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { + const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; + switch (chdr.ch_type) { + .ZLIB => { + var stream = std.io.fixedBufferStream(data[@sizeOf(elf.Elf64_Chdr)..]); + var zlib_stream = try std.compress.zlib.decompressStream(gpa, stream.reader()); + defer zlib_stream.deinit(); + const decomp = try gpa.alloc(u8, chdr.ch_size); + const nread = try zlib_stream.reader().readAll(decomp); + if (nread != decomp.len) { + return error.Io; + } + return decomp; + }, + else => @panic("TODO unhandled compression scheme"), + } + } else return gpa.dupe(u8, data); +} + +pub fn priority(self: Atom, elf_file: *Elf) u64 { + const index = elf_file.file(self.file_index).?.index(); + return (@as(u64, @intCast(index)) << 32) | @as(u64, @intCast(self.input_section_index)); +} + /// Returns how much room there is to grow in virtual address space. /// File offset relocation happens transparently, so it is not included in /// this calculation. @@ -247,11 +287,12 @@ pub fn free(self: *Atom, elf_file: *Elf) void { self.* = .{}; } -pub fn relocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { - const file_ptr = elf_file.file(self.file_index).?; - if (file_ptr != .zig_module) @panic("TODO"); - const zig_module = file_ptr.zig_module; - return zig_module.relocs.items[self.relocs_section_index].items; +pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { + return switch (elf_file.file(self.file_index).?) { + .zig_module => |x| x.relocs.items[self.relocs_section_index].items, + .object => |x| x.getRelocs(self.relocs_section_index), + else => unreachable, + }; } pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void { diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 3f71e0a58e..98b5ff563e 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -1,7 +1,6 @@ index: File.Index, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -alive: bool = true, output_symtab_size: Elf.SymtabSize = .{}, @@ -76,10 +75,6 @@ pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf, ctx: anytype) void { } } -pub fn sourceSymbol(self: *LinkerDefined, symbol_index: Symbol.Index) *elf.Elf64_Sym { - return &self.symtab.items[symbol_index]; -} - pub fn globals(self: *LinkerDefined) []const Symbol.Index { return self.symbols.items; } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig new file mode 100644 index 0000000000..e4a1f432b6 --- /dev/null +++ b/src/link/Elf/Object.zig @@ -0,0 +1,810 @@ +archive: ?[]const u8 = null, +path: []const u8, +data: []const u8, +index: File.Index, + +header: ?elf.Elf64_Ehdr = null, +shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, +strings: StringTable(.object_strings) = .{}, +symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, +strtab: []const u8 = &[0]u8{}, +first_global: ?u32 = null, + +symbols: std.ArrayListUnmanaged(u32) = .{}, +atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, +comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, + +fdes: std.ArrayListUnmanaged(Fde) = .{}, +cies: std.ArrayListUnmanaged(Cie) = .{}, + +needs_exec_stack: bool = false, +alive: bool = true, +num_dynrelocs: u32 = 0, + +output_symtab_size: Elf.SymtabSize = .{}, + +pub fn isObject(file: std.fs.File) bool { + const reader = file.reader(); + const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; + defer file.seekTo(0) catch {}; + if (!mem.eql(u8, header.e_ident[0..4], "\x7fELF")) return false; + if (header.e_ident[elf.EI_VERSION] != 1) return false; + if (header.e_type != elf.ET.REL) return false; + if (header.e_version != 1) return false; + return true; +} + +pub fn deinit(self: *Object, allocator: Allocator) void { + allocator.free(self.data); + self.shdrs.deinit(allocator); + self.strings.deinit(allocator); + self.symbols.deinit(allocator); + self.atoms.deinit(allocator); + self.comdat_groups.deinit(allocator); + self.fdes.deinit(allocator); + self.cies.deinit(allocator); +} + +pub fn parse(self: *Object, elf_file: *Elf) !void { + var stream = std.io.fixedBufferStream(self.data); + const reader = stream.reader(); + + self.header = try reader.readStruct(elf.Elf64_Ehdr); + + if (self.header.?.e_shnum == 0) return; + + const gpa = elf_file.base.allocator; + + const shdrs = @as( + [*]align(1) const elf.Elf64_Shdr, + @ptrCast(self.data.ptr + self.header.?.e_shoff), + )[0..self.header.?.e_shnum]; + try self.shdrs.appendUnalignedSlice(gpa, shdrs); + try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx)); + + const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { + elf.SHT_SYMTAB => break @as(u16, @intCast(i)), + else => {}, + } else null; + + if (symtab_index) |index| { + const shdr = shdrs[index]; + self.first_global = shdr.sh_info; + + const symtab = self.shdrContents(index); + const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym)); + self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms]; + self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link))); + } + + try self.initAtoms(elf_file); + try self.initSymtab(elf_file); + + for (self.shdrs.items, 0..) |shdr, i| { + const atom = elf_file.atom(self.atoms.items[i]) orelse continue; + if (!atom.alive) continue; + if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame")) + try self.parseEhFrame(@as(u16, @intCast(i)), elf_file); + } +} + +fn initAtoms(self: *Object, elf_file: *Elf) !void { + const shdrs = self.shdrs.items; + try self.atoms.resize(elf_file.base.allocator, shdrs.len); + @memset(self.atoms.items, 0); + + for (shdrs, 0..) |shdr, i| { + if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and + shdr.sh_flags & elf.SHF_ALLOC == 0 and + shdr.sh_type != elf.SHT_LLVM_ADDRSIG) continue; + + switch (shdr.sh_type) { + elf.SHT_GROUP => { + if (shdr.sh_info >= self.symtab.len) { + // TODO convert into an error + log.debug("{}: invalid symbol index in sh_info", .{self.fmtPath()}); + continue; + } + const group_info_sym = self.symtab[shdr.sh_info]; + const group_signature = blk: { + if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) { + const sym_shdr = shdrs[group_info_sym.st_shndx]; + break :blk self.strings.getAssumeExists(sym_shdr.sh_name); + } + break :blk self.getString(group_info_sym.st_name); + }; + + const shndx = @as(u16, @intCast(i)); + const group_raw_data = self.shdrContents(shndx); + const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32)); + const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; + + if (group_members[0] != 0x1) { // GRP_COMDAT + // TODO convert into an error + log.debug("{}: unknown SHT_GROUP format", .{self.fmtPath()}); + continue; + } + + const group_signature_off = try self.strings.insert(elf_file.base.allocator, group_signature); + const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off); + const comdat_group_index = try elf_file.addComdatGroup(); + const comdat_group = elf_file.comdatGroup(comdat_group_index); + comdat_group.* = .{ + .owner = gop.index, + .shndx = shndx, + }; + try self.comdat_groups.append(elf_file.base.allocator, comdat_group_index); + }, + + elf.SHT_SYMTAB_SHNDX => @panic("TODO"), + + elf.SHT_NULL, + elf.SHT_REL, + elf.SHT_RELA, + elf.SHT_SYMTAB, + elf.SHT_STRTAB, + => {}, + + else => { + const name = self.strings.getAssumeExists(shdr.sh_name); + const shndx = @as(u16, @intCast(i)); + + // if (mem.eql(u8, ".note.GNU-stack", name)) { + // if (shdr.sh_flags & elf.SHF_EXECINSTR != 0) { + // if (!elf_file.options.z_execstack or !elf_file.options.z_execstack_if_needed) { + // elf_file.base.warn( + // "{}: may cause segmentation fault as this file requested executable stack", + // .{self.fmtPath()}, + // ); + // } + // self.needs_exec_stack = true; + // } + // continue; + // } + + if (self.skipShdr(shndx, elf_file)) continue; + try self.addAtom(shdr, shndx, name, elf_file); + }, + } + } + + // Parse relocs sections if any. + for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) { + elf.SHT_REL, elf.SHT_RELA => { + const atom_index = self.atoms.items[shdr.sh_info]; + if (elf_file.atom(atom_index)) |atom| { + atom.relocs_section_index = @as(u16, @intCast(i)); + } + }, + else => {}, + }; +} + +fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, elf_file: *Elf) !void { + const atom_index = try elf_file.addAtom(); + const atom = elf_file.atom(atom_index).?; + atom.atom_index = atom_index; + atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name); + atom.file_index = self.index; + atom.input_section_index = shndx; + self.atoms.items[shndx] = atom_index; + + if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { + const data = self.shdrContents(shndx); + const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; + atom.size = chdr.ch_size; + atom.alignment = math.log2_int(u64, chdr.ch_addralign); + } else { + atom.size = shdr.sh_size; + atom.alignment = math.log2_int(u64, shdr.sh_addralign); + } +} + +fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { + const shdr = self.shdrs.items[index]; + const name = self.strings.getAssumeExists(shdr.sh_name); + const ignore = blk: { + if (mem.startsWith(u8, name, ".note")) break :blk true; + if (mem.startsWith(u8, name, ".comment")) break :blk true; + if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; + if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and + mem.startsWith(u8, name, ".debug")) break :blk true; + break :blk false; + }; + return ignore; +} + +fn initSymtab(self: *Object, elf_file: *Elf) !void { + const gpa = elf_file.base.allocator; + const first_global = self.first_global orelse self.symtab.len; + const shdrs = self.shdrs.items; + + try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.len); + + for (self.symtab[0..first_global], 0..) |sym, i| { + const index = try elf_file.addSymbol(); + self.symbols.appendAssumeCapacity(index); + const sym_ptr = elf_file.symbol(index); + const name = blk: { + if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) { + const shdr = shdrs[sym.st_shndx]; + break :blk self.strings.getAssumeExists(shdr.sh_name); + } + break :blk self.getString(sym.st_name); + }; + sym_ptr.value = sym.st_value; + sym_ptr.name_offset = try elf_file.strtab.insert(gpa, name); + sym_ptr.esym_index = @as(u32, @intCast(i)); + sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx]; + sym_ptr.file_index = self.index; + } + + for (self.symtab[first_global..]) |sym| { + const name = self.getString(sym.st_name); + const off = try elf_file.strtab.insert(gpa, name); + const gop = try elf_file.getOrPutGlobal(off); + self.symbols.addOneAssumeCapacity().* = gop.index; + } +} + +fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void { + const relocs_shndx = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { + elf.SHT_RELA => if (shdr.sh_info == shndx) break @as(u16, @intCast(i)), + else => {}, + } else { + log.debug("{s}: missing reloc section for unwind info section", .{self.fmtPath()}); + return; + }; + + const gpa = elf_file.base.allocator; + const raw = self.shdrContents(shndx); + const relocs = self.getRelocs(relocs_shndx); + const fdes_start = self.fdes.items.len; + const cies_start = self.cies.items.len; + + var it = eh_frame.Iterator{ .data = raw }; + while (try it.next()) |rec| { + const rel_range = filterRelocs(relocs, rec.offset, rec.size + 4); + switch (rec.tag) { + .cie => try self.cies.append(gpa, .{ + .offset = rec.offset, + .size = rec.size, + .rel_index = @as(u32, @intCast(rel_range.start)), + .rel_num = @as(u32, @intCast(rel_range.len)), + .rel_section_index = relocs_shndx, + .input_section_index = shndx, + .file_index = self.index, + }), + .fde => try self.fdes.append(gpa, .{ + .offset = rec.offset, + .size = rec.size, + .cie_index = undefined, + .rel_index = @as(u32, @intCast(rel_range.start)), + .rel_num = @as(u32, @intCast(rel_range.len)), + .rel_section_index = relocs_shndx, + .input_section_index = shndx, + .file_index = self.index, + }), + } + } + + // Tie each FDE to its CIE + for (self.fdes.items[fdes_start..]) |*fde| { + const cie_ptr = fde.offset + 4 - fde.ciePointer(elf_file); + const cie_index = for (self.cies.items[cies_start..], cies_start..) |cie, cie_index| { + if (cie.offset == cie_ptr) break @as(u32, @intCast(cie_index)); + } else { + // TODO convert into an error + log.debug("{s}: no matching CIE found for FDE at offset {x}", .{ + self.fmtPath(), + fde.offset, + }); + continue; + }; + fde.cie_index = cie_index; + } + + // Tie each FDE record to its matching atom + const SortFdes = struct { + pub fn lessThan(ctx: *Elf, lhs: Fde, rhs: Fde) bool { + const lhs_atom = lhs.atom(ctx); + const rhs_atom = rhs.atom(ctx); + return lhs_atom.priority(ctx) < rhs_atom.priority(ctx); + } + }; + mem.sort(Fde, self.fdes.items[fdes_start..], elf_file, SortFdes.lessThan); + + // Create a back-link from atom to FDEs + var i: u32 = @as(u32, @intCast(fdes_start)); + while (i < self.fdes.items.len) { + const fde = self.fdes.items[i]; + const atom = fde.atom(elf_file); + atom.fde_start = i; + i += 1; + while (i < self.fdes.items.len) : (i += 1) { + const next_fde = self.fdes.items[i]; + if (atom.atom_index != next_fde.atom(elf_file).atom_index) break; + } + atom.fde_end = i; + } +} + +fn filterRelocs( + relocs: []align(1) const elf.Elf64_Rela, + start: u64, + len: u64, +) struct { start: u64, len: u64 } { + const Predicate = struct { + value: u64, + + pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool { + return rel.r_offset < self.value; + } + }; + const LPredicate = struct { + value: u64, + + pub fn predicate(self: @This(), rel: elf.Elf64_Rela) bool { + return rel.r_offset >= self.value; + } + }; + + const f_start = Elf.bsearch(elf.Elf64_Rela, relocs, Predicate{ .value = start }); + const f_len = Elf.lsearch(elf.Elf64_Rela, relocs[f_start..], LPredicate{ .value = start + len }); + + return .{ .start = f_start, .len = f_len }; +} + +pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { + for (self.atoms.items) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.alive) continue; + const shdr = atom.inputShdr(elf_file); + if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; + if (shdr.sh_type == elf.SHT_NOBITS) continue; + try atom.scanRelocs(elf_file); + } + + for (self.cies.items) |cie| { + for (cie.getRelocs(elf_file)) |rel| { + const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]); + if (sym.flags.import) { + if (sym.getType(elf_file) != elf.STT_FUNC) + elf_file.base.fatal("{s}: {s}: CIE referencing external data reference", .{ + self.fmtPath(), + sym.getName(elf_file), + }); + sym.flags.plt = true; + } + } + } +} + +pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (self.globals(), 0..) |index, i| { + const sym_idx = @as(u32, @intCast(first_global + i)); + const this_sym = self.symtab[sym_idx]; + + if (this_sym.st_shndx == elf.SHN_UNDEF) continue; + + if (this_sym.st_shndx != elf.SHN_ABS and this_sym.st_shndx != elf.SHN_COMMON) { + const atom_index = self.atoms.items[this_sym.st_shndx]; + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.alive) continue; + } + + const global = elf_file.symbol(index); + if (self.asFile().symbolRank(this_sym, !self.alive) < global.symbolRank(elf_file)) { + const atom = switch (this_sym.st_shndx) { + elf.SHN_ABS, elf.SHN_COMMON => 0, + else => self.atoms.items[this_sym.st_shndx], + }; + global.* = .{ + .value = this_sym.st_value, + .name = global.name, + .atom = atom, + .sym_idx = sym_idx, + .file = self.index, + .ver_idx = elf_file.default_sym_version, + }; + if (this_sym.st_bind() == elf.STB_WEAK) global.flags.weak = true; + } + } +} + +pub fn resetGlobals(self: *Object, elf_file: *Elf) void { + for (self.globals()) |index| { + const global = elf_file.symbol(index); + const name = global.name; + global.* = .{}; + global.name = name; + } +} + +pub fn markLive(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (self.globals(), 0..) |index, i| { + const sym_idx = first_global + i; + const sym = self.symtab[sym_idx]; + if (sym.st_bind() == elf.STB_WEAK) continue; + + const global = elf_file.symbol(index); + const file = global.getFile(elf_file) orelse continue; + const should_keep = sym.st_shndx == elf.SHN_UNDEF or + (sym.st_shndx == elf.SHN_COMMON and global.sourceSymbol(elf_file).st_shndx != elf.SHN_COMMON); + if (should_keep and !file.isAlive()) { + file.setAlive(); + file.markLive(elf_file); + } + } +} + +pub fn checkDuplicates(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (self.globals(), 0..) |index, i| { + const sym_idx = @as(u32, @intCast(first_global + i)); + const this_sym = self.symtab[sym_idx]; + const global = elf_file.symbol(index); + const global_file = global.getFile(elf_file) orelse continue; + + if (self.index == global_file.getIndex() or + this_sym.st_shndx == elf.SHN_UNDEF or + this_sym.st_bind() == elf.STB_WEAK or + this_sym.st_shndx == elf.SHN_COMMON) continue; + + if (this_sym.st_shndx != elf.SHN_ABS) { + const atom_index = self.atoms.items[this_sym.st_shndx]; + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.alive) continue; + } + + elf_file.base.fatal("multiple definition: {}: {}: {s}", .{ + self.fmtPath(), + global_file.fmtPath(), + global.getName(elf_file), + }); + } +} + +/// We will create dummy shdrs per each resolved common symbols to make it +/// play nicely with the rest of the system. +pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { + const first_global = self.first_global orelse return; + for (self.globals(), 0..) |index, i| { + const sym_idx = @as(u32, @intCast(first_global + i)); + const this_sym = self.symtab[sym_idx]; + if (this_sym.st_shndx != elf.SHN_COMMON) continue; + + const global = elf_file.symbol(index); + const global_file = global.getFile(elf_file).?; + if (global_file.getIndex() != self.index) { + if (elf_file.options.warn_common) { + elf_file.base.warn("{}: multiple common symbols: {s}", .{ + self.fmtPath(), + global.getName(elf_file), + }); + } + continue; + } + + const gpa = elf_file.base.allocator; + + const atom_index = try elf_file.addAtom(); + try self.atoms.append(gpa, atom_index); + + const is_tls = global.getType(elf_file) == elf.STT_TLS; + const name = if (is_tls) ".tls_common" else ".common"; + + const atom = elf_file.atom(atom_index).?; + atom.atom_index = atom_index; + atom.name = try elf_file.strtab.insert(gpa, name); + atom.file = self.index; + atom.size = this_sym.st_size; + const alignment = this_sym.st_value; + atom.alignment = math.log2_int(u64, alignment); + + var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE; + if (is_tls) sh_flags |= elf.SHF_TLS; + const shndx = @as(u16, @intCast(self.shdrs.items.len)); + const shdr = try self.shdrs.addOne(gpa); + shdr.* = .{ + .sh_name = try self.strings.insert(gpa, name), + .sh_type = elf.SHT_NOBITS, + .sh_flags = sh_flags, + .sh_addr = 0, + .sh_offset = 0, + .sh_size = this_sym.st_size, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = alignment, + .sh_entsize = 0, + }; + atom.shndx = shndx; + + global.value = 0; + global.atom = atom_index; + global.flags.weak = false; + } +} + +pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void { + if (elf_file.options.strip_all) return; + + for (self.locals()) |local_index| { + const local = elf_file.symbol(local_index); + if (local.atom(elf_file)) |atom| if (!atom.alive) continue; + const s_sym = local.getSourceSymbol(elf_file); + switch (s_sym.st_type()) { + elf.STT_SECTION, elf.STT_NOTYPE => continue, + else => {}, + } + local.flags.output_symtab = true; + self.output_symtab_size.nlocals += 1; + self.output_symtab_size.strsize += @as(u32, @intCast(local.getName(elf_file).len + 1)); + } + + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; + if (global.atom(elf_file)) |atom| if (!atom.alive) continue; + global.flags.output_symtab = true; + if (global.isLocal()) { + self.output_symtab_size.nlocals += 1; + } else { + self.output_symtab_size.nglobals += 1; + } + self.output_symtab_size.strsize += @as(u32, @intCast(global.getName(elf_file).len + 1)); + } +} + +pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: Elf.WriteSymtabCtx) !void { + if (elf_file.options.strip_all) return; + + const gpa = elf_file.base.allocator; + + var ilocal = ctx.ilocal; + for (self.locals()) |local_index| { + const local = elf_file.symbol(local_index); + if (!local.flags.output_symtab) continue; + const st_name = try ctx.strtab.insert(gpa, local.getName(elf_file)); + ctx.symtab[ilocal] = local.asElfSym(st_name, elf_file); + ilocal += 1; + } + + var iglobal = ctx.iglobal; + for (self.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; + if (!global.flags.output_symtab) continue; + const st_name = try ctx.strtab.insert(gpa, global.getName(elf_file)); + if (global.isLocal()) { + ctx.symtab[ilocal] = global.asElfSym(st_name, elf_file); + ilocal += 1; + } else { + ctx.symtab[iglobal] = global.asElfSym(st_name, elf_file); + iglobal += 1; + } + } +} + +pub fn locals(self: *Object) []const u32 { + const end = self.first_global orelse self.symbols.items.len; + return self.symbols.items[0..end]; +} + +pub fn globals(self: *Object) []const u32 { + const start = self.first_global orelse self.symbols.items.len; + return self.symbols.items[start..]; +} + +pub inline fn shdrContents(self: *Object, index: u32) []const u8 { + assert(index < self.shdrs.items.len); + const shdr = self.shdrs.items[index]; + return self.data[shdr.sh_offset..][0..shdr.sh_size]; +} + +fn getString(self: *Object, off: u32) [:0]const u8 { + assert(off < self.strtab.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0); +} + +pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 { + const raw = self.shdrContents(index); + const nmembers = @divExact(raw.len, @sizeOf(u32)); + const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers]; + return members; +} + +pub fn asFile(self: *Object) File { + return .{ .object = self }; +} + +pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela { + const raw = self.shdrContents(shndx); + const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela)); + return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; +} + +pub fn format( + self: *Object, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = self; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format objects directly"); +} + +pub fn fmtSymtab(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +const FormatContext = struct { + object: *Object, + elf_file: *Elf, +}; + +fn formatSymtab( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + const object = ctx.object; + try writer.writeAll(" locals\n"); + for (object.locals()) |index| { + const local = ctx.elf_file.symbol(index); + try writer.print(" {}\n", .{local.fmt(ctx.elf_file)}); + } + try writer.writeAll(" globals\n"); + for (object.globals()) |index| { + const global = ctx.elf_file.symbol(index); + try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + } +} + +pub fn fmtAtoms(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatAtoms) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +fn formatAtoms( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + const object = ctx.object; + try writer.writeAll(" atoms\n"); + for (object.atoms.items) |atom_index| { + const atom = ctx.elf_file.atom(atom_index) orelse continue; + try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)}); + } +} + +pub fn fmtCies(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatCies) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +fn formatCies( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + const object = ctx.object; + try writer.writeAll(" cies\n"); + for (object.cies.items, 0..) |cie, i| { + try writer.print(" cie({d}) : {}\n", .{ i, cie.fmt(ctx.elf_file) }); + } +} + +pub fn fmtFdes(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatFdes) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +fn formatFdes( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + const object = ctx.object; + try writer.writeAll(" fdes\n"); + for (object.fdes.items, 0..) |fde, i| { + try writer.print(" fde({d}) : {}\n", .{ i, fde.fmt(ctx.elf_file) }); + } +} + +pub fn fmtComdatGroups(self: *Object, elf_file: *Elf) std.fmt.Formatter(formatComdatGroups) { + return .{ .data = .{ + .object = self, + .elf_file = elf_file, + } }; +} + +fn formatComdatGroups( + ctx: FormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + const object = ctx.object; + const elf_file = ctx.elf_file; + try writer.writeAll(" comdat groups\n"); + for (object.comdat_groups.items) |cg_index| { + const cg = elf_file.comdatGroup(cg_index); + const cg_owner = elf_file.comdatGroupOwner(cg.owner); + if (cg_owner.file != object.index) continue; + for (object.comdatGroupMembers(cg.shndx)) |shndx| { + const atom_index = object.atoms.items[shndx]; + const atom = elf_file.atom(atom_index) orelse continue; + try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) }); + } + } +} + +pub fn fmtPath(self: *Object) std.fmt.Formatter(formatPath) { + return .{ .data = self }; +} + +fn formatPath( + object: *Object, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = unused_fmt_string; + _ = options; + if (object.archive) |path| { + try writer.writeAll(path); + try writer.writeByte('('); + try writer.writeAll(object.path); + try writer.writeByte(')'); + } else try writer.writeAll(object.path); +} + +const Object = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const eh_frame = @import("eh_frame.zig"); +const elf = std.elf; +const fs = std.fs; +const log = std.log.scoped(.link); +const math = std.math; +const mem = std.mem; + +const Allocator = mem.Allocator; +const Atom = @import("Atom.zig"); +const Cie = eh_frame.Cie; +const Elf = @import("../Elf.zig"); +const Fde = eh_frame.Fde; +const File = @import("file.zig").File; +const StringTable = @import("../strtab.zig").StringTable; +const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 322e7609eb..a6901d9ab7 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -66,22 +66,23 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { return elf_file.file(symbol.file_index); } -pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) *elf.Elf64_Sym { +pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; switch (file_ptr) { - .zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file), - .linker_defined => return file_ptr.linker_defined.sourceSymbol(symbol.esym_index), + .zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file).*, + .linker_defined => return file_ptr.linker_defined.symtab.items[symbol.esym_index], + .object => return file_ptr.object.symtab[symbol.esym_index], } } pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32); const sym = symbol.sourceSymbol(elf_file); - const in_archive = switch (file) { - // .object => |x| !x.alive, + const in_archive = switch (file_ptr) { + .object => |x| !x.alive, else => false, }; - return file_ptr.symbolRank(sym.*, in_archive); + return file_ptr.symbolRank(sym, in_archive); } pub fn address(symbol: Symbol, opts: struct { diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 760a71e14b..72c3ca0132 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -11,8 +11,6 @@ global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{}, relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, -alive: bool = true, - output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *ZigModule, allocator: Allocator) void { @@ -37,7 +35,7 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) ! const symbol_ptr = elf_file.symbol(symbol_index); symbol_ptr.atom_index = atom_index; symbol_ptr.output_section_index = output_section_index; - const local_esym = symbol_ptr.sourceSymbol(elf_file); + const local_esym = self.sourceSymbol(symbol_ptr.index, elf_file); local_esym.st_shndx = output_section_index; const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len)); const relocs = try self.relocs.addOne(gpa); @@ -82,7 +80,7 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); - const esym = self.sourceSymbol(local_index, elf_file); + const esym = local.sourceSymbol(elf_file); switch (esym.st_type()) { elf.STT_SECTION, elf.STT_NOTYPE => { local.flags.output_symtab = false; diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig new file mode 100644 index 0000000000..71dd5a6a22 --- /dev/null +++ b/src/link/Elf/eh_frame.zig @@ -0,0 +1,445 @@ +pub const Fde = struct { + /// Includes 4byte size cell. + offset: u64, + size: u64, + cie_index: u32, + rel_index: u32 = 0, + rel_num: u32 = 0, + rel_section_index: u32 = 0, + input_section_index: u32 = 0, + file_index: u32 = 0, + alive: bool = true, + /// Includes 4byte size cell. + out_offset: u64 = 0, + + pub fn address(fde: Fde, elf_file: *Elf) u64 { + const base: u64 = if (elf_file.eh_frame_section_index) |shndx| + elf_file.shdrs.items[shndx].sh_addr + else + 0; + return base + fde.out_offset; + } + + pub fn data(fde: Fde, elf_file: *Elf) []const u8 { + const object = elf_file.file(fde.file_index).?.object; + const contents = object.shdrContents(fde.input_section_index); + return contents[fde.offset..][0..fde.calcSize()]; + } + + pub fn cie(fde: Fde, elf_file: *Elf) Cie { + const object = elf_file.file(fde.file_index).?.object; + return object.cies.items[fde.cie_index]; + } + + pub fn ciePointer(fde: Fde, elf_file: *Elf) u32 { + return std.mem.readIntLittle(u32, fde.data(elf_file)[4..8]); + } + + pub fn calcSize(fde: Fde) u64 { + return fde.size + 4; + } + + pub fn atom(fde: Fde, elf_file: *Elf) *Atom { + const object = elf_file.file(fde.file_index).?.object; + const rel = fde.relocs(elf_file)[0]; + const sym = object.symtab[rel.r_sym()]; + const atom_index = object.atoms.items[sym.st_shndx]; + return elf_file.atom(atom_index).?; + } + + pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela { + const object = elf_file.file(fde.file_index).?.object; + return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num]; + } + + pub fn format( + fde: Fde, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = fde; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format FDEs directly"); + } + + pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .fde = fde, + .elf_file = elf_file, + } }; + } + + const FdeFormatContext = struct { + fde: Fde, + elf_file: *Elf, + }; + + fn format2( + ctx: FdeFormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + const fde = ctx.fde; + const elf_file = ctx.elf_file; + const base_addr = fde.address(elf_file); + try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ + base_addr + fde.out_offset, + fde.calcSize(), + fde.cie_index, + fde.atom(elf_file).name(elf_file), + }); + if (!fde.alive) try writer.writeAll(" : [*]"); + } +}; + +pub const Cie = struct { + /// Includes 4byte size cell. + offset: u64, + size: u64, + rel_index: u32 = 0, + rel_num: u32 = 0, + rel_section_index: u32 = 0, + input_section_index: u32 = 0, + file_index: u32 = 0, + /// Includes 4byte size cell. + out_offset: u64 = 0, + alive: bool = false, + + pub fn address(cie: Cie, elf_file: *Elf) u64 { + const base: u64 = if (elf_file.eh_frame_section_index) |shndx| + elf_file.shdrs.items[shndx].sh_addr + else + 0; + return base + cie.out_offset; + } + + pub fn data(cie: Cie, elf_file: *Elf) []const u8 { + const object = elf_file.file(cie.file_index).?.object; + const contents = object.shdrContents(cie.input_section_index); + return contents[cie.offset..][0..cie.calcSize()]; + } + + pub fn calcSize(cie: Cie) u64 { + return cie.size + 4; + } + + pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela { + const object = elf_file.file(cie.file_index).?.object; + return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num]; + } + + pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool { + if (!std.mem.eql(u8, cie.data(elf_file), other.data(elf_file))) return false; + + const cie_relocs = cie.relocs(elf_file); + const other_relocs = other.relocs(elf_file); + if (cie_relocs.len != other_relocs.len) return false; + + for (cie_relocs, other_relocs) |cie_rel, other_rel| { + if (cie_rel.r_offset - cie.offset != other_rel.r_offset - other.offset) return false; + if (cie_rel.r_type() != other_rel.r_type()) return false; + if (cie_rel.r_addend != other_rel.r_addend) return false; + + const cie_object = elf_file.file(cie.file_index).?.object; + const other_object = elf_file.file(other.file_index).?.object; + const cie_sym = cie_object.symbol(cie_rel.r_sym(), elf_file); + const other_sym = other_object.symbol(other_rel.r_sym(), elf_file); + if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false; + } + return true; + } + + pub fn format( + cie: Cie, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = cie; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format CIEs directly"); + } + + pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ + .cie = cie, + .elf_file = elf_file, + } }; + } + + const CieFormatContext = struct { + cie: Cie, + elf_file: *Elf, + }; + + fn format2( + ctx: CieFormatContext, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + const cie = ctx.cie; + const elf_file = ctx.elf_file; + const base_addr = cie.address(elf_file); + try writer.print("@{x} : size({x})", .{ + base_addr + cie.out_offset, + cie.calcSize(), + }); + if (!cie.alive) try writer.writeAll(" : [*]"); + } +}; + +pub const Iterator = struct { + data: []const u8, + pos: u64 = 0, + + pub const Record = struct { + tag: enum { fde, cie }, + offset: u64, + size: u64, + }; + + pub fn next(it: *Iterator) !?Record { + if (it.pos >= it.data.len) return null; + + var stream = std.io.fixedBufferStream(it.data[it.pos..]); + const reader = stream.reader(); + + var size = try reader.readIntLittle(u32); + if (size == 0xFFFFFFFF) @panic("TODO"); + + const id = try reader.readIntLittle(u32); + const record = Record{ + .tag = if (id == 0) .cie else .fde, + .offset = it.pos, + .size = size, + }; + it.pos += size + 4; + + return record; + } +}; + +pub fn calcEhFrameSize(elf_file: *Elf) !usize { + var offset: u64 = 0; + + var cies = std.ArrayList(Cie).init(elf_file.base.allocator); + defer cies.deinit(); + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + + outer: for (object.cies.items) |*cie| { + for (cies.items) |other| { + if (other.eql(cie.*, elf_file)) { + // We already have a CIE record that has the exact same contents, so instead of + // duplicating them, we mark this one dead and set its output offset to be + // equal to that of the alive record. This way, we won't have to rewrite + // Fde.cie_index field when committing the records to file. + cie.out_offset = other.out_offset; + continue :outer; + } + } + cie.alive = true; + cie.out_offset = offset; + offset += cie.calcSize(); + try cies.append(cie.*); + } + } + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + for (object.fdes.items) |*fde| { + if (!fde.alive) continue; + fde.out_offset = offset; + offset += fde.calcSize(); + } + } + + return offset + 4; // NULL terminator +} + +pub fn calcEhFrameHdrSize(elf_file: *Elf) usize { + var count: usize = 0; + for (elf_file.objects.items) |index| { + for (elf_file.file(index).?.object.fdes.items) |fde| { + if (!fde.alive) continue; + count += 1; + } + } + return eh_frame_hdr_header_size + count * 8; +} + +fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void { + const offset = rel.r_offset - rec.offset; + const P = @as(i64, @intCast(rec.address(elf_file) + offset)); + const S = @as(i64, @intCast(sym.address(.{}, elf_file))); + const A = rel.r_addend; + + relocs_log.debug(" {s}: {x}: [{x} => {x}] ({s})", .{ + Atom.fmtRelocType(rel.r_type()), + offset, + P, + S + A, + sym.name(elf_file), + }); + + var where = contents[offset..]; + switch (rel.r_type()) { + elf.R_X86_64_32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @truncate(S + A))), + elf.R_X86_64_64 => std.mem.writeIntLittle(i64, where[0..8], S + A), + elf.R_X86_64_PC32 => std.mem.writeIntLittle(i32, where[0..4], @as(i32, @intCast(S - P + A))), + elf.R_X86_64_PC64 => std.mem.writeIntLittle(i64, where[0..8], S - P + A), + else => unreachable, + } +} + +pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { + const gpa = elf_file.base.allocator; + + relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr}); + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + + for (object.cies.items) |cie| { + if (!cie.alive) continue; + + const contents = try gpa.dupe(u8, cie.data(elf_file)); + defer gpa.free(contents); + + for (cie.relocs(elf_file)) |rel| { + const sym = object.symbol(rel.r_sym(), elf_file); + try resolveReloc(cie, sym, rel, elf_file, contents); + } + + try writer.writeAll(contents); + } + } + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + + for (object.fdes.items) |fde| { + if (!fde.alive) continue; + + const contents = try gpa.dupe(u8, fde.data(elf_file)); + defer gpa.free(contents); + + std.mem.writeIntLittle( + i32, + contents[4..8], + @as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))), + ); + + for (fde.relocs(elf_file)) |rel| { + const sym = object.symbol(rel.r_sym(), elf_file); + try resolveReloc(fde, sym, rel, elf_file, contents); + } + + try writer.writeAll(contents); + } + } + + try writer.writeIntLittle(u32, 0); +} + +pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { + try writer.writeByte(1); // version + try writer.writeByte(EH_PE.pcrel | EH_PE.sdata4); + try writer.writeByte(EH_PE.udata4); + try writer.writeByte(EH_PE.datarel | EH_PE.sdata4); + + const eh_frame_shdr = elf_file.shdrs.items[elf_file.eh_frame_section_index.?]; + const eh_frame_hdr_shdr = elf_file.shdrs.items[elf_file.eh_frame_hdr_section_index.?]; + const num_fdes = @as(u32, @intCast(@divExact(eh_frame_hdr_shdr.sh_size - eh_frame_hdr_header_size, 8))); + try writer.writeIntLittle( + u32, + @as(u32, @bitCast(@as( + i32, + @truncate(@as(i64, @intCast(eh_frame_shdr.sh_addr)) - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)) - 4), + ))), + ); + try writer.writeIntLittle(u32, num_fdes); + + const Entry = struct { + init_addr: u32, + fde_addr: u32, + + pub fn lessThan(ctx: void, lhs: @This(), rhs: @This()) bool { + _ = ctx; + return lhs.init_addr < rhs.init_addr; + } + }; + + var entries = std.ArrayList(Entry).init(elf_file.base.allocator); + defer entries.deinit(); + try entries.ensureTotalCapacityPrecise(num_fdes); + + for (elf_file.objects.items) |index| { + const object = elf_file.file(index).?.object; + for (object.fdes.items) |fde| { + if (!fde.alive) continue; + + const relocs = fde.relocs(elf_file); + assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... + const rel = relocs[0]; + const sym = object.symbol(rel.r_sym(), elf_file); + const P = @as(i64, @intCast(fde.address(elf_file))); + const S = @as(i64, @intCast(sym.address(.{}, elf_file))); + const A = rel.r_addend; + entries.appendAssumeCapacity(.{ + .init_addr = @as(u32, @bitCast(@as(i32, @truncate(S + A - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)))))), + .fde_addr = @as( + u32, + @bitCast(@as(i32, @truncate(P - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr))))), + ), + }); + } + } + + std.mem.sort(Entry, entries.items, {}, Entry.lessThan); + try writer.writeAll(std.mem.sliceAsBytes(entries.items)); +} + +const eh_frame_hdr_header_size: u64 = 12; + +const EH_PE = struct { + pub const absptr = 0x00; + pub const uleb128 = 0x01; + pub const udata2 = 0x02; + pub const udata4 = 0x03; + pub const udata8 = 0x04; + pub const sleb128 = 0x09; + pub const sdata2 = 0x0A; + pub const sdata4 = 0x0B; + pub const sdata8 = 0x0C; + pub const pcrel = 0x10; + pub const textrel = 0x20; + pub const datarel = 0x30; + pub const funcrel = 0x40; + pub const aligned = 0x50; + pub const indirect = 0x80; + pub const omit = 0xFF; +}; + +const std = @import("std"); +const assert = std.debug.assert; +const elf = std.elf; +const relocs_log = std.log.scoped(.link_relocs); + +const Allocator = std.mem.Allocator; +const Atom = @import("Atom.zig"); +const Elf = @import("../Elf.zig"); +const Object = @import("Object.zig"); +const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 823ae5209e..0d7e62da67 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -1,7 +1,7 @@ pub const File = union(enum) { zig_module: *ZigModule, linker_defined: *LinkerDefined, - // object: *Object, + object: *Object, // shared_object: *SharedObject, pub fn index(file: File) Index { @@ -25,7 +25,7 @@ pub const File = union(enum) { switch (file) { .zig_module => try writer.writeAll("(zig module)"), .linker_defined => try writer.writeAll("(linker defined)"), - // .object => |x| try writer.print("{}", .{x.fmtPath()}), + .object => |x| try writer.print("{}", .{x.fmtPath()}), // .shared_object => |x| try writer.writeAll(x.path), } } @@ -95,7 +95,7 @@ pub const File = union(enum) { null: void, zig_module: ZigModule, linker_defined: LinkerDefined, - // object: Object, + object: Object, // shared_object: SharedObject, }; }; @@ -106,7 +106,7 @@ const elf = std.elf; const Allocator = std.mem.Allocator; const Elf = @import("../Elf.zig"); const LinkerDefined = @import("LinkerDefined.zig"); -// const Object = @import("Object.zig"); +const Object = @import("Object.zig"); // const SharedObject = @import("SharedObject.zig"); const Symbol = @import("Symbol.zig"); const ZigModule = @import("ZigModule.zig"); -- cgit v1.2.3 From 00787885f4f99a92d85b496ff9e03993d69b1cf6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 00:10:54 +0200 Subject: elf: report undefined symbols in objects --- src/link/Elf.zig | 116 +++++++++++++++++++++++++++-------------- src/link/Elf/LinkerDefined.zig | 9 ---- src/link/Elf/Object.zig | 9 +++- src/link/Elf/ZigModule.zig | 6 ++- 4 files changed, 88 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b2646d11d8..02b463ff16 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -843,6 +843,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { sym.name_offset = name_off; esym.st_name = name_off; esym.st_info |= elf.STT_FILE; + esym.st_shndx = elf.SHN_ABS; } } } @@ -1299,22 +1300,19 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr const gpa = self.base.allocator; const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); - const index = @as(File.Index, @intCast(self.files.slice().len)); - - var object = Object{ + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + self.files.set(index, .{ .object = .{ .path = path, .data = data, .index = index, - }; - errdefer object.deinit(gpa); + } }); + try self.objects.append(gpa, index); + + const object = self.file(index).?.object; try object.parse(self); ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?; if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; - - _ = try self.files.addOne(gpa); - self.files.set(index, .{ .object = object }); - try self.objects.append(gpa, index); } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2690,12 +2688,14 @@ pub fn updateDeclExports( break :blk sym_index; }; const sym = self.symbol(sym_index); + sym.flags.@"export" = true; sym.value = decl_sym.value; sym.atom_index = decl_sym.atom_index; sym.output_section_index = decl_sym.output_section_index; const esym = zig_module.sourceSymbol(sym_index, self); esym.* = decl_esym; esym.st_info = (stb_bits << 4) | stt_bits; + _ = self.unresolved.swapRemove(sym_index); } } @@ -3468,13 +3468,8 @@ pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index { pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; - const gpa = self.base.allocator; - const name_off = try self.strtab.insert(gpa, name); - const gop = try self.getOrPutGlobal(name_off); - if (!gop.found_existing) { - try self.unresolved.putNoClobber(gpa, gop.index, {}); - } - return gop.index; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; + return zig_module.addGlobal(name, self); } const GetOrCreateComdatGroupOwnerResult = struct { @@ -3519,42 +3514,83 @@ fn reportUndefined(self: *Elf) !void { try self.misc_errors.ensureUnusedCapacity(gpa, self.unresolved.keys().len); - for (self.unresolved.keys()) |sym_index| { - const undef_sym = self.symbol(sym_index); + const CollectStruct = struct { + notes: [max_notes]link.File.ErrorMsg = [_]link.File.ErrorMsg{.{ .msg = undefined }} ** max_notes, + notes_len: u3 = 0, + notes_count: usize = 0, + }; - var all_notes: usize = 0; - var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1); - defer notes.deinit(); + const collect: []CollectStruct = try gpa.alloc(CollectStruct, self.unresolved.keys().len); + defer gpa.free(collect); + @memset(collect, .{}); - // Collect all references across all input files - if (self.zig_module_index) |index| { - const zig_module = self.file(index).?.zig_module; - for (zig_module.atoms.keys()) |atom_index| { - const atom_ptr = self.atom(atom_index).?; - if (!atom_ptr.alive) continue; - - for (atom_ptr.relocs(self)) |rel| { - if (sym_index == rel.r_sym()) { - const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{ - zig_module.path, - atom_ptr.name(self), - }); - notes.appendAssumeCapacity(.{ .msg = note }); - all_notes += 1; - break; + // Collect all references across all input files + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + for (zig_module.atoms.keys()) |atom_index| { + const atom_ptr = self.atom(atom_index).?; + if (!atom_ptr.alive) continue; + + for (atom_ptr.relocs(self)) |rel| { + if (self.unresolved.getIndex(rel.r_sym())) |bin_index| { + const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{ + zig_module.path, + atom_ptr.name(self), + }); + const bin = &collect[bin_index]; + if (bin.notes_len < max_notes) { + bin.notes[bin.notes_len] = .{ .msg = note }; + bin.notes_len += 1; } + bin.notes_count += 1; } } } + } + + for (self.objects.items) |index| { + const object = self.file(index).?.object; + for (object.atoms.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + + for (atom_ptr.relocs(self)) |rel| { + const sym_index = object.symbols.items[rel.r_sym()]; + if (self.unresolved.getIndex(sym_index)) |bin_index| { + const note = try std.fmt.allocPrint(gpa, "referenced by {}:{s}", .{ + object.fmtPath(), + atom_ptr.name(self), + }); + const bin = &collect[bin_index]; + if (bin.notes_len < max_notes) { + bin.notes[bin.notes_len] = .{ .msg = note }; + bin.notes_len += 1; + } + bin.notes_count += 1; + } + } + } + } + + // Generate error notes + for (self.unresolved.keys(), 0..) |sym_index, bin_index| { + const collected = &collect[bin_index]; + + var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1); + defer notes.deinit(); + + for (collected.notes[0..collected.notes_len]) |note| { + notes.appendAssumeCapacity(note); + } - if (all_notes > max_notes) { - const remaining = all_notes - max_notes; + if (collected.notes_count > max_notes) { + const remaining = collected.notes_count - max_notes; const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining}); notes.appendAssumeCapacity(.{ .msg = note }); } var err_msg = link.File.ErrorMsg{ - .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{undef_sym.name(self)}), + .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(sym_index).name(self)}), }; err_msg.notes = try notes.toOwnedSlice(); diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index 98b5ff563e..0c6666e8bb 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -46,15 +46,6 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void { } } -// pub fn resetGlobals(self: *LinkerDefined, elf_file: *Elf) void { -// for (self.symbols.items) |index| { -// const global = elf_file.getSymbol(index); -// const name = global.name; -// global.* = .{}; -// global.name = name; -// } -// } - pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void { for (self.globals()) |global_index| { const global = elf_file.symbol(global_index); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index e4a1f432b6..aff1fa9182 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -187,6 +187,7 @@ fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name); atom.file_index = self.index; atom.input_section_index = shndx; + atom.alive = true; self.atoms.items[shndx] = atom_index; if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { @@ -244,6 +245,10 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void { const off = try elf_file.strtab.insert(gpa, name); const gop = try elf_file.getOrPutGlobal(off); self.symbols.addOneAssumeCapacity().* = gop.index; + + if (sym.st_shndx == elf.SHN_UNDEF) { + try elf_file.unresolved.put(gpa, gop.index, {}); + } } } @@ -394,6 +399,8 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { if (!atom.alive) continue; } + _ = elf_file.unresolved.swapRemove(index); + const global = elf_file.symbol(index); if (self.asFile().symbolRank(this_sym, !self.alive) < global.symbolRank(elf_file)) { const atom = switch (this_sym.st_shndx) { @@ -598,7 +605,7 @@ pub fn globals(self: *Object) []const u32 { return self.symbols.items[start..]; } -pub inline fn shdrContents(self: *Object, index: u32) []const u8 { +pub fn shdrContents(self: *Object, index: u32) []const u8 { assert(index < self.shdrs.items.len); const shdr = self.shdrs.items[index]; return self.data[shdr.sh_offset..][0..shdr.sh_size]; diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 72c3ca0132..cf14fcc169 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -58,7 +58,7 @@ pub fn addLocal(self: *ZigModule, elf_file: *Elf) !Symbol.Index { return symbol_index; } -pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.Index { +pub fn addGlobal(self: *ZigModule, name: []const u8, elf_file: *Elf) !Symbol.Index { const gpa = elf_file.base.allocator; try self.elf_global_symbols.ensureUnusedCapacity(gpa, 1); try self.global_symbols.ensureUnusedCapacity(gpa, 1); @@ -69,10 +69,12 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I esym.st_name = off; esym.st_info = elf.STB_GLOBAL << 4; const gop = try elf_file.getOrPutGlobal(off); + if (!gop.found_existing) { + try elf_file.unresolved.putNoClobber(gpa, gop.index, {}); + } const sym = elf_file.symbol(gop.index); sym.file_index = self.index; sym.esym_index = esym_index; - sym.flags.@"export" = true; self.global_symbols.putAssumeCapacityNoClobber(gop.index, {}); return gop.index; } -- cgit v1.2.3 From 53c3757c007bc48e5db1b933b0713df099499d2c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 13:58:18 +0200 Subject: elf: start fixing symbol resolution --- src/link/Elf.zig | 83 ++++++++++++++++++++++++--------------- src/link/Elf/Object.zig | 4 +- src/link/Elf/Symbol.zig | 15 +++++-- src/link/Elf/ZigModule.zig | 97 +++++++++++++++++++++------------------------- src/link/Elf/file.zig | 1 - 5 files changed, 109 insertions(+), 91 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 02b463ff16..b1051a7fe4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -836,14 +836,20 @@ pub fn populateMissingMetadata(self: *Elf) !void { } }); self.zig_module_index = index; const zig_module = self.file(index).?.zig_module; - const sym_index = try zig_module.addLocal(self); - const sym = self.symbol(sym_index); - const esym = zig_module.sourceSymbol(sym_index, self); + const name_off = try self.strtab.insert(gpa, std.fs.path.stem(module.main_pkg.root_src_path)); - sym.name_offset = name_off; + const symbol_index = try self.addSymbol(); + try zig_module.local_symbols.append(gpa, symbol_index); + const symbol_ptr = self.symbol(symbol_index); + symbol_ptr.file_index = zig_module.index; + symbol_ptr.name_offset = name_off; + + const esym_index = try zig_module.addLocalEsym(gpa); + const esym = &zig_module.local_esyms.items[esym_index]; esym.st_name = name_off; esym.st_info |= elf.STT_FILE; esym.st_shndx = elf.SHN_ABS; + symbol_ptr.esym_index = esym_index; } } } @@ -2194,7 +2200,7 @@ pub fn getOrCreateMetadataForLazySymbol(self: *Elf, sym: link.File.LazySymbol) ! }; const zig_module = self.file(self.zig_module_index.?).?.zig_module; switch (metadata.state.*) { - .unused => metadata.symbol_index.* = try zig_module.createAtom(switch (sym.kind) { + .unused => metadata.symbol_index.* = try zig_module.addAtom(switch (sym.kind) { .code => self.text_section_index.?, .const_data => self.rodata_section_index.?, }, self), @@ -2213,7 +2219,7 @@ pub fn getOrCreateMetadataForDecl(self: *Elf, decl_index: Module.Decl.Index) !Sy if (!gop.found_existing) { const zig_module = self.file(self.zig_module_index.?).?.zig_module; gop.value_ptr.* = .{ - .symbol_index = try zig_module.createAtom(self.getDeclShdrIndex(decl_index), self), + .symbol_index = try zig_module.addAtom(self.getDeclShdrIndex(decl_index), self), .exports = .{}, }; } @@ -2264,7 +2270,7 @@ fn updateDeclCode( const required_alignment = decl.getAlignment(mod); const sym = self.symbol(sym_index); - const esym = zig_module.sourceSymbol(sym_index, self); + const esym = &zig_module.local_esyms.items[sym.esym_index]; const atom_ptr = sym.atom(self).?; const shdr_index = sym.output_section_index; @@ -2521,7 +2527,7 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. const local_sym = self.symbol(symbol_index); const phdr_index = self.phdr_to_shdr_table.get(local_sym.output_section_index).?; local_sym.name_offset = name_str_index; - const local_esym = zig_module.sourceSymbol(symbol_index, self); + const local_esym = &zig_module.local_esyms.items[local_sym.esym_index]; local_esym.st_name = name_str_index; local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; @@ -2575,7 +2581,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const name = self.strtab.get(name_str_index).?; const zig_module = self.file(self.zig_module_index.?).?.zig_module; - const sym_index = try zig_module.createAtom(self.rodata_section_index.?, self); + const sym_index = try zig_module.addAtom(self.rodata_section_index.?, self); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{ .none = {}, @@ -2597,7 +2603,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; const local_sym = self.symbol(sym_index); local_sym.name_offset = name_str_index; - const local_esym = zig_module.sourceSymbol(sym_index, self); + const local_esym = &zig_module.local_esyms.items[local_sym.esym_index]; local_esym.st_name = name_str_index; local_esym.st_info |= elf.STT_OBJECT; local_esym.st_size = code.len; @@ -2642,10 +2648,11 @@ pub fn updateDeclExports( const gpa = self.base.allocator; + const zig_module = self.file(self.zig_module_index.?).?.zig_module; const decl = mod.declPtr(decl_index); const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index); const decl_sym = self.symbol(decl_sym_index); - const decl_esym = decl_sym.sourceSymbol(self); + const decl_esym = zig_module.local_esyms.items[decl_sym.esym_index]; const decl_metadata = self.decls.getPtr(decl_index).?; for (exports) |exp| { @@ -2681,21 +2688,21 @@ pub fn updateDeclExports( }; const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info)); - const zig_module = self.file(self.zig_module_index.?).?.zig_module; const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { - const sym_index = try zig_module.addGlobal(exp_name, self); + const sym_index = try zig_module.addGlobalEsym(gpa); + _ = try zig_module.global_symbols.addOne(gpa); try decl_metadata.exports.append(gpa, sym_index); break :blk sym_index; }; - const sym = self.symbol(sym_index); - sym.flags.@"export" = true; - sym.value = decl_sym.value; - sym.atom_index = decl_sym.atom_index; - sym.output_section_index = decl_sym.output_section_index; - const esym = zig_module.sourceSymbol(sym_index, self); - esym.* = decl_esym; + const name_off = try self.strtab.insert(gpa, exp_name); + const esym = &zig_module.global_esyms.items[sym_index]; + esym.st_value = decl_sym.value; + esym.st_shndx = decl_sym.output_section_index; esym.st_info = (stb_bits << 4) | stt_bits; - _ = self.unresolved.swapRemove(sym_index); + esym.st_name = name_off; + + const gop = try self.getOrPutGlobal(name_off); + zig_module.global_symbols.items[sym_index] = gop.index; } } @@ -2722,20 +2729,20 @@ pub fn deleteDeclExport( ) void { if (self.llvm_object) |_| return; const metadata = self.decls.getPtr(decl_index) orelse return; - const gpa = self.base.allocator; const mod = self.base.options.module.?; const zig_module = self.file(self.zig_module_index.?).?.zig_module; const exp_name = mod.intern_pool.stringToSlice(name); - const sym_index = metadata.@"export"(self, exp_name) orelse return; + const esym_index = metadata.@"export"(self, exp_name) orelse return; log.debug("deleting export '{s}'", .{exp_name}); - const sym = self.symbol(sym_index.*); - const esym = zig_module.sourceSymbol(sym_index.*, self); - assert(self.resolver.fetchSwapRemove(sym.name_offset) != null); // TODO don't delete it if it's not dominant - sym.* = .{}; - // TODO free list for esym! + const esym = &zig_module.global_esyms.items[esym_index.*]; + _ = zig_module.globals_lookup.remove(esym.st_name); + const sym_index = self.resolver.get(esym.st_name).?; + const sym = self.symbol(sym_index); + if (sym.file_index == zig_module.index) { + _ = self.resolver.swapRemove(esym.st_name); + sym.* = .{}; + } esym.* = null_sym; - self.symbols_free_list.append(gpa, sym_index.*) catch {}; - sym_index.* = 0; } fn addLinkerDefinedSymbols(self: *Elf) !void { @@ -3468,8 +3475,18 @@ pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index { pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; + const gpa = self.base.allocator; + const off = try self.strtab.insert(gpa, name); + const gop = try self.getOrPutGlobal(off); const zig_module = self.file(self.zig_module_index.?).?.zig_module; - return zig_module.addGlobal(name, self); + const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, off); + if (!lookup_gop.found_existing) { + const esym_index = try zig_module.addGlobalEsym(gpa); + const esym = &zig_module.global_esyms.items[esym_index]; + esym.st_name = off; + lookup_gop.value_ptr.* = esym_index; + } + return gop.index; } const GetOrCreateComdatGroupOwnerResult = struct { @@ -3753,8 +3770,10 @@ const DeclMetadata = struct { exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { + const zig_module = elf_file.file(elf_file.zig_module_index.?).?.zig_module; for (m.exports.items) |*exp| { - if (mem.eql(u8, name, elf_file.symbol(exp.*).name(elf_file))) return exp; + const exp_name = elf_file.strtab.getAssumeExists(zig_module.global_esyms.items[exp.*].st_name); + if (mem.eql(u8, name, exp_name)) return exp; } return null; } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index aff1fa9182..868ccb33c4 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -8,9 +8,9 @@ shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, strings: StringTable(.object_strings) = .{}, symtab: []align(1) const elf.Elf64_Sym = &[0]elf.Elf64_Sym{}, strtab: []const u8 = &[0]u8{}, -first_global: ?u32 = null, +first_global: ?Symbol.Index = null, -symbols: std.ArrayListUnmanaged(u32) = .{}, +symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index a6901d9ab7..0f9c574555 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -69,9 +69,13 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; switch (file_ptr) { - .zig_module => return file_ptr.zig_module.sourceSymbol(symbol.index, elf_file).*, - .linker_defined => return file_ptr.linker_defined.symtab.items[symbol.esym_index], - .object => return file_ptr.object.symtab[symbol.esym_index], + .zig_module => |x| { + const is_global = x.globals_lookup.contains(symbol.name_offset); + if (is_global) return x.global_esyms.items[symbol.esym_index]; + return x.local_esyms.items[symbol.esym_index]; + }, + .linker_defined => |x| return x.symtab.items[symbol.esym_index], + .object => |x| return x.symtab[symbol.esym_index], } } @@ -164,7 +168,10 @@ pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void { } pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { - const file_ptr = symbol.file(elf_file).?; + const file_ptr = symbol.file(elf_file) orelse { + out.* = Elf.null_sym; + return; + }; const s_sym = symbol.sourceSymbol(elf_file); const st_type = symbol.type(elf_file); const st_bind: u8 = blk: { diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index cf14fcc169..8951894638 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -2,11 +2,11 @@ path: []const u8, index: File.Index, -elf_local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -local_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, - -elf_global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, +local_esyms: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +global_esyms: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, +local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, atoms: std.AutoArrayHashMapUnmanaged(Atom.Index, void) = .{}, relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, @@ -14,10 +14,11 @@ relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(elf.Elf64_Rela)) = .{}, output_symtab_size: Elf.SymtabSize = .{}, pub fn deinit(self: *ZigModule, allocator: Allocator) void { - self.elf_local_symbols.deinit(allocator); + self.local_esyms.deinit(allocator); + self.global_esyms.deinit(allocator); self.local_symbols.deinit(allocator); - self.elf_global_symbols.deinit(allocator); self.global_symbols.deinit(allocator); + self.globals_lookup.deinit(allocator); self.atoms.deinit(allocator); for (self.relocs.items) |*list| { list.deinit(allocator); @@ -25,58 +26,57 @@ pub fn deinit(self: *ZigModule, allocator: Allocator) void { self.relocs.deinit(allocator); } -pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { +pub fn addLocalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index { + try self.local_esyms.ensureUnusedCapacity(allocator, 1); + const index = @as(Symbol.Index, @intCast(self.local_esyms.items.len)); + const esym = self.local_esyms.addOneAssumeCapacity(); + esym.* = Elf.null_sym; + esym.st_info = elf.STB_LOCAL << 4; + return index; +} + +pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index { + try self.global_esyms.ensureUnusedCapacity(allocator, 1); + const index = @as(Symbol.Index, @intCast(self.global_esyms.items.len)); + const esym = self.global_esyms.addOneAssumeCapacity(); + esym.* = Elf.null_sym; + esym.st_info = elf.STB_GLOBAL << 4; + return index; +} + +pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { const gpa = elf_file.base.allocator; + const atom_index = try elf_file.addAtom(); - const symbol_index = try self.addLocal(elf_file); + try self.atoms.putNoClobber(gpa, atom_index, {}); const atom_ptr = elf_file.atom(atom_index).?; atom_ptr.file_index = self.index; atom_ptr.output_section_index = output_section_index; + + const symbol_index = try elf_file.addSymbol(); + try self.local_symbols.append(gpa, symbol_index); const symbol_ptr = elf_file.symbol(symbol_index); + symbol_ptr.file_index = self.index; symbol_ptr.atom_index = atom_index; symbol_ptr.output_section_index = output_section_index; - const local_esym = self.sourceSymbol(symbol_ptr.index, elf_file); - local_esym.st_shndx = output_section_index; + + const esym_index = try self.addLocalEsym(gpa); + const esym = &self.local_esyms.items[esym_index]; + esym.st_shndx = output_section_index; + symbol_ptr.esym_index = esym_index; + const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len)); const relocs = try self.relocs.addOne(gpa); relocs.* = .{}; atom_ptr.relocs_section_index = relocs_index; - try self.atoms.putNoClobber(gpa, atom_index, {}); - return symbol_index; -} -pub fn addLocal(self: *ZigModule, elf_file: *Elf) !Symbol.Index { - const gpa = elf_file.base.allocator; - const symbol_index = try elf_file.addSymbol(); - const symbol_ptr = elf_file.symbol(symbol_index); - symbol_ptr.file_index = self.index; - symbol_ptr.esym_index = @as(Symbol.Index, @intCast(self.elf_local_symbols.items.len)); - const local_esym = try self.elf_local_symbols.addOne(gpa); - local_esym.* = Elf.null_sym; - local_esym.st_info = elf.STB_LOCAL << 4; - try self.local_symbols.putNoClobber(gpa, symbol_index, {}); return symbol_index; } -pub fn addGlobal(self: *ZigModule, name: []const u8, elf_file: *Elf) !Symbol.Index { - const gpa = elf_file.base.allocator; - try self.elf_global_symbols.ensureUnusedCapacity(gpa, 1); - try self.global_symbols.ensureUnusedCapacity(gpa, 1); - const off = try elf_file.strtab.insert(gpa, name); - const esym_index = @as(Symbol.Index, @intCast(self.elf_global_symbols.items.len)); - const esym = self.elf_global_symbols.addOneAssumeCapacity(); - esym.* = Elf.null_sym; - esym.st_name = off; - esym.st_info = elf.STB_GLOBAL << 4; - const gop = try elf_file.getOrPutGlobal(off); - if (!gop.found_existing) { - try elf_file.unresolved.putNoClobber(gpa, gop.index, {}); - } - const sym = elf_file.symbol(gop.index); - sym.file_index = self.index; - sym.esym_index = esym_index; - self.global_symbols.putAssumeCapacityNoClobber(gop.index, {}); - return gop.index; +pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { + _ = self; + _ = elf_file; + @panic("TODO"); } pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { @@ -133,19 +133,12 @@ pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void { } } -pub fn sourceSymbol(self: *ZigModule, symbol_index: Symbol.Index, elf_file: *Elf) *elf.Elf64_Sym { - const sym = elf_file.symbol(symbol_index); - if (self.local_symbols.get(symbol_index)) |_| return &self.elf_local_symbols.items[sym.esym_index]; - assert(self.global_symbols.get(symbol_index) != null); - return &self.elf_global_symbols.items[sym.esym_index]; -} - pub fn locals(self: *ZigModule) []const Symbol.Index { - return self.local_symbols.keys(); + return self.local_symbols.items; } pub fn globals(self: *ZigModule) []const Symbol.Index { - return self.global_symbols.keys(); + return self.global_symbols.items; } pub fn asFile(self: *ZigModule) File { diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 0d7e62da67..828d4f4c1f 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -32,7 +32,6 @@ pub const File = union(enum) { pub fn resolveSymbols(file: File, elf_file: *Elf) void { switch (file) { - .zig_module => unreachable, // handled separately inline else => |x| x.resolveSymbols(elf_file), } } -- cgit v1.2.3 From 962b46148d573f62146a2752a0ab8cfd5f8da132 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 14:36:55 +0200 Subject: elf: add simplistic symbol resolution --- src/link/Elf.zig | 17 ++++++++++++++++- src/link/Elf/Atom.zig | 4 ++-- src/link/Elf/Object.zig | 37 ++++++++++++++----------------------- src/link/Elf/Symbol.zig | 7 ++++--- src/link/Elf/ZigModule.zig | 31 +++++++++++++++++++++++++++---- src/link/Elf/file.zig | 12 ------------ 6 files changed, 63 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index b1051a7fe4..cde8243d57 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1045,6 +1045,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.addLinkerDefinedSymbols(); + // Resolve symbols + self.resolveSymbols(); + if (self.unresolved.keys().len > 0) try self.reportUndefined(); self.allocateLinkerDefinedSymbols(); @@ -1321,6 +1324,18 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch; } +fn resolveSymbols(self: *Elf) void { + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + zig_module.resolveSymbols(self); + } + + for (self.objects.items) |index| { + const object = self.file(index).?.object; + object.resolveSymbols(self); + } +} + fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2697,7 +2712,7 @@ pub fn updateDeclExports( const name_off = try self.strtab.insert(gpa, exp_name); const esym = &zig_module.global_esyms.items[sym_index]; esym.st_value = decl_sym.value; - esym.st_shndx = decl_sym.output_section_index; + esym.st_shndx = decl_sym.atom_index; esym.st_info = (stb_bits << 4) | stt_bits; esym.st_name = name_off; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index c163aa8ecc..6a029d97a2 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -17,7 +17,7 @@ alignment: u8 = 0, input_section_index: Index = 0, /// Index of the output section. -output_section_index: u16 = 0, +output_section_index: Index = 0, /// Index of the input section containing this atom's relocs. relocs_section_index: Index = 0, @@ -484,7 +484,7 @@ fn format2( } } -pub const Index = u32; +pub const Index = u16; const std = @import("std"); const assert = std.debug.assert; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 868ccb33c4..e2b45f45e4 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -245,10 +245,6 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void { const off = try elf_file.strtab.insert(gpa, name); const gop = try elf_file.getOrPutGlobal(off); self.symbols.addOneAssumeCapacity().* = gop.index; - - if (sym.st_shndx == elf.SHN_UNDEF) { - try elf_file.unresolved.put(gpa, gop.index, {}); - } } } @@ -388,34 +384,29 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; for (self.globals(), 0..) |index, i| { - const sym_idx = @as(u32, @intCast(first_global + i)); - const this_sym = self.symtab[sym_idx]; + const esym_index = @as(Symbol.Index, @intCast(first_global + i)); + const esym = self.symtab[esym_index]; - if (this_sym.st_shndx == elf.SHN_UNDEF) continue; + if (esym.st_shndx == elf.SHN_UNDEF) continue; - if (this_sym.st_shndx != elf.SHN_ABS and this_sym.st_shndx != elf.SHN_COMMON) { - const atom_index = self.atoms.items[this_sym.st_shndx]; + if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { + const atom_index = self.atoms.items[esym.st_shndx]; const atom = elf_file.atom(atom_index) orelse continue; if (!atom.alive) continue; } - _ = elf_file.unresolved.swapRemove(index); - const global = elf_file.symbol(index); - if (self.asFile().symbolRank(this_sym, !self.alive) < global.symbolRank(elf_file)) { - const atom = switch (this_sym.st_shndx) { + if (self.asFile().symbolRank(esym, !self.alive) < global.symbolRank(elf_file)) { + const atom_index = switch (esym.st_shndx) { elf.SHN_ABS, elf.SHN_COMMON => 0, - else => self.atoms.items[this_sym.st_shndx], - }; - global.* = .{ - .value = this_sym.st_value, - .name = global.name, - .atom = atom, - .sym_idx = sym_idx, - .file = self.index, - .ver_idx = elf_file.default_sym_version, + else => self.atoms.items[esym.st_shndx], }; - if (this_sym.st_bind() == elf.STB_WEAK) global.flags.weak = true; + global.value = esym.st_value; + global.atom_index = atom_index; + global.esym_index = esym_index; + global.file_index = self.index; + global.version_index = elf_file.default_sym_version; + if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; } } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 0f9c574555..22d3376b31 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -70,9 +70,10 @@ pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; switch (file_ptr) { .zig_module => |x| { - const is_global = x.globals_lookup.contains(symbol.name_offset); - if (is_global) return x.global_esyms.items[symbol.esym_index]; - return x.local_esyms.items[symbol.esym_index]; + const is_global = symbol.esym_index & 0x10000000 != 0; + const esym_index = symbol.esym_index & 0x0fffffff; + if (is_global) return x.global_esyms.items[esym_index]; + return x.local_esyms.items[esym_index]; }, .linker_defined => |x| return x.symtab.items[symbol.esym_index], .object => |x| return x.symtab[symbol.esym_index], diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 8951894638..8a4e712310 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -62,7 +62,7 @@ pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Sym const esym_index = try self.addLocalEsym(gpa); const esym = &self.local_esyms.items[esym_index]; - esym.st_shndx = output_section_index; + esym.st_shndx = atom_index; symbol_ptr.esym_index = esym_index; const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len)); @@ -74,9 +74,32 @@ pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Sym } pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { - _ = self; - _ = elf_file; - @panic("TODO"); + for (self.globals(), 0..) |index, i| { + const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000; + const esym = self.global_esyms.items[i]; + + if (esym.st_shndx == elf.SHN_UNDEF) continue; + + if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { + const atom_index = self.atoms.keys()[esym.st_shndx]; + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.alive) continue; + } + + const global = elf_file.symbol(index); + if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) { + const atom_index = switch (esym.st_shndx) { + elf.SHN_ABS, elf.SHN_COMMON => 0, + else => self.atoms.keys()[esym.st_shndx], + }; + global.value = esym.st_value; + global.atom_index = atom_index; + global.esym_index = esym_index; + global.file_index = self.index; + global.version_index = elf_file.default_sym_version; + if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; + } + } } pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 828d4f4c1f..e7fb789217 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -30,18 +30,6 @@ pub const File = union(enum) { } } - pub fn resolveSymbols(file: File, elf_file: *Elf) void { - switch (file) { - inline else => |x| x.resolveSymbols(elf_file), - } - } - - // pub fn resetGlobals(file: File, elf_file: *Elf) void { - // switch (file) { - // inline else => |x| x.resetGlobals(elf_file), - // } - // } - pub fn isAlive(file: File) bool { return switch (file) { .zig_module => true, -- cgit v1.2.3 From b478a0dd1ab6acab92e2b1a4198fdf6428caad8d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 15:14:38 +0200 Subject: elf: mark imports-exports; populate symtab with objects --- src/link/Elf.zig | 61 ++++++++++++++++++++++++++++++++++++++++++++++ src/link/Elf/Object.zig | 30 ++++++++--------------- src/link/Elf/Symbol.zig | 18 +++++++------- src/link/Elf/ZigModule.zig | 2 +- 4 files changed, 81 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index cde8243d57..24140cd1d0 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1047,6 +1047,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Resolve symbols self.resolveSymbols(); + self.markImportsExports(); if (self.unresolved.keys().len > 0) try self.reportUndefined(); @@ -1336,6 +1337,52 @@ fn resolveSymbols(self: *Elf) void { } } +fn markImportsExports(self: *Elf) void { + const is_dyn_lib = self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic; + + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + for (zig_module.globals()) |global_index| { + const global = self.symbol(global_index); + if (global.version_index == elf.VER_NDX_LOCAL) continue; + const file_ptr = global.file(self) orelse continue; + const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other)); + if (vis == .HIDDEN) continue; + // if (file == .shared and !global.isAbs(self)) { + // global.flags.import = true; + // continue; + // } + if (file_ptr.index() == index) { + global.flags.@"export" = true; + if (is_dyn_lib and vis != .PROTECTED) { + global.flags.import = true; + } + } + } + } + + for (self.objects.items) |index| { + const object = self.file(index).?.object; + for (object.globals()) |global_index| { + const global = self.symbol(global_index); + if (global.version_index == elf.VER_NDX_LOCAL) continue; + const file_ptr = global.file(self) orelse continue; + const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other)); + if (vis == .HIDDEN) continue; + // if (file == .shared and !global.isAbs(self)) { + // global.flags.import = true; + // continue; + // } + if (file_ptr.index() == index) { + global.flags.@"export" = true; + if (is_dyn_lib and vis != .PROTECTED) { + global.flags.import = true; + } + } + } + } +} + fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2941,6 +2988,13 @@ fn updateSymtabSize(self: *Elf) !void { sizes.nglobals += zig_module.output_symtab_size.nglobals; } + for (self.objects.items) |index| { + const object = self.file(index).?.object; + object.updateSymtabSize(self); + sizes.nlocals += object.output_symtab_size.nlocals; + sizes.nglobals += object.output_symtab_size.nglobals; + } + if (self.got_section_index) |_| { self.got.updateSymtabSize(self); sizes.nlocals += self.got.output_symtab_size.nlocals; @@ -2996,6 +3050,13 @@ fn writeSymtab(self: *Elf) !void { ctx.iglobal += zig_module.output_symtab_size.nglobals; } + for (self.objects.items) |index| { + const object = self.file(index).?.object; + object.writeSymtab(self, ctx); + ctx.ilocal += object.output_symtab_size.nlocals; + ctx.iglobal += object.output_symtab_size.nglobals; + } + if (self.got_section_index) |_| { try self.got.writeSymtab(self, ctx); ctx.ilocal += self.got.output_symtab_size.nlocals; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index e2b45f45e4..7d85509901 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -430,7 +430,7 @@ pub fn markLive(self: *Object, elf_file: *Elf) void { const global = elf_file.symbol(index); const file = global.getFile(elf_file) orelse continue; const should_keep = sym.st_shndx == elf.SHN_UNDEF or - (sym.st_shndx == elf.SHN_COMMON and global.sourceSymbol(elf_file).st_shndx != elf.SHN_COMMON); + (sym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON); if (should_keep and !file.isAlive()) { file.setAlive(); file.markLive(elf_file); @@ -526,25 +526,22 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { } } -pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void { - if (elf_file.options.strip_all) return; - +pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); if (local.atom(elf_file)) |atom| if (!atom.alive) continue; - const s_sym = local.getSourceSymbol(elf_file); - switch (s_sym.st_type()) { + const esym = local.elfSym(elf_file); + switch (esym.st_type()) { elf.STT_SECTION, elf.STT_NOTYPE => continue, else => {}, } local.flags.output_symtab = true; self.output_symtab_size.nlocals += 1; - self.output_symtab_size.strsize += @as(u32, @intCast(local.getName(elf_file).len + 1)); } for (self.globals()) |global_index| { const global = elf_file.symbol(global_index); - if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; + if (global.file(elf_file)) |file| if (file.index() != self.index) continue; if (global.atom(elf_file)) |atom| if (!atom.alive) continue; global.flags.output_symtab = true; if (global.isLocal()) { @@ -552,35 +549,28 @@ pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void { } else { self.output_symtab_size.nglobals += 1; } - self.output_symtab_size.strsize += @as(u32, @intCast(global.getName(elf_file).len + 1)); } } -pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: Elf.WriteSymtabCtx) !void { - if (elf_file.options.strip_all) return; - - const gpa = elf_file.base.allocator; - +pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void { var ilocal = ctx.ilocal; for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); if (!local.flags.output_symtab) continue; - const st_name = try ctx.strtab.insert(gpa, local.getName(elf_file)); - ctx.symtab[ilocal] = local.asElfSym(st_name, elf_file); + local.setOutputSym(elf_file, &ctx.symtab[ilocal]); ilocal += 1; } var iglobal = ctx.iglobal; for (self.globals()) |global_index| { const global = elf_file.symbol(global_index); - if (global.getFile(elf_file)) |file| if (file.getIndex() != self.index) continue; + if (global.file(elf_file)) |file| if (file.index() != self.index) continue; if (!global.flags.output_symtab) continue; - const st_name = try ctx.strtab.insert(gpa, global.getName(elf_file)); if (global.isLocal()) { - ctx.symtab[ilocal] = global.asElfSym(st_name, elf_file); + global.setOutputSym(elf_file, &ctx.symtab[ilocal]); ilocal += 1; } else { - ctx.symtab[iglobal] = global.asElfSym(st_name, elf_file); + global.setOutputSym(elf_file, &ctx.symtab[iglobal]); iglobal += 1; } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 22d3376b31..db7e69d5e4 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -20,7 +20,7 @@ atom_index: Atom.Index = 0, output_section_index: u16 = 0, /// Index of the source symbol this symbol references. -/// Use `sourceSymbol` to pull the source symbol from the relevant file. +/// Use `elfSym` to pull the source symbol from the relevant file. esym_index: Index = 0, /// Index of the source version symbol this symbol references if any. @@ -48,7 +48,7 @@ pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { } pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 { - const s_sym = symbol.sourceSymbol(elf_file); + const s_sym = symbol.elfSym(elf_file); // const file_ptr = symbol.file(elf_file).?; // if (s_sym.st_type() == elf.STT_GNU_IFUNC and file_ptr == .shared) return elf.STT_FUNC; return s_sym.st_type(); @@ -66,7 +66,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { return elf_file.file(symbol.file_index); } -pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { +pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; switch (file_ptr) { .zig_module => |x| { @@ -82,7 +82,7 @@ pub fn sourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { pub fn symbolRank(symbol: Symbol, elf_file: *Elf) u32 { const file_ptr = symbol.file(elf_file) orelse return std.math.maxInt(u32); - const sym = symbol.sourceSymbol(elf_file); + const sym = symbol.elfSym(elf_file); const in_archive = switch (file_ptr) { .object => |x| !x.alive, else => false, @@ -173,13 +173,13 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { out.* = Elf.null_sym; return; }; - const s_sym = symbol.sourceSymbol(elf_file); + const esym = symbol.elfSym(elf_file); const st_type = symbol.type(elf_file); const st_bind: u8 = blk: { if (symbol.isLocal()) break :blk 0; if (symbol.flags.weak) break :blk elf.STB_WEAK; // if (file_ptr == .shared) break :blk elf.STB_GLOBAL; - break :blk s_sym.st_bind(); + break :blk esym.st_bind(); }; const st_shndx = blk: { // if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?; @@ -202,10 +202,10 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { out.* = .{ .st_name = symbol.name_offset, .st_info = (st_bind << 4) | st_type, - .st_other = s_sym.st_other, + .st_other = esym.st_other, .st_shndx = st_shndx, .st_value = st_value, - .st_size = s_sym.st_size, + .st_size = esym.st_size, }; } @@ -274,7 +274,7 @@ fn format2( try writer.print("%{d} : {s} : @{x}", .{ symbol.index, symbol.fmtName(ctx.elf_file), symbol.value }); if (symbol.file(ctx.elf_file)) |file_ptr| { if (symbol.isAbs(ctx.elf_file)) { - if (symbol.sourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { + if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { try writer.writeAll(" : undef"); } else { try writer.writeAll(" : absolute"); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 8a4e712310..f9df0d33ea 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -105,7 +105,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); - const esym = local.sourceSymbol(elf_file); + const esym = local.elfSym(elf_file); switch (esym.st_type()) { elf.STT_SECTION, elf.STT_NOTYPE => { local.flags.output_symtab = false; -- cgit v1.2.3 From c654f3b0ee8d02d809bb458e1e006b4aa7c3cbc6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 15:44:16 +0200 Subject: elf: claim unresolved dangling symbols that can be claimed --- src/link/Elf.zig | 72 ++++++++++++++++++++++++---------------------- src/link/Elf/Object.zig | 32 +++++++++++++++++++-- src/link/Elf/ZigModule.zig | 28 ++++++++++++++++++ src/link/Elf/file.zig | 6 ++++ 4 files changed, 101 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 24140cd1d0..efd3dc9d54 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1048,6 +1048,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Resolve symbols self.resolveSymbols(); self.markImportsExports(); + self.claimUnresolved(); if (self.unresolved.keys().len > 0) try self.reportUndefined(); @@ -1338,48 +1339,45 @@ fn resolveSymbols(self: *Elf) void { } fn markImportsExports(self: *Elf) void { - const is_dyn_lib = self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic; - - if (self.zig_module_index) |index| { - const zig_module = self.file(index).?.zig_module; - for (zig_module.globals()) |global_index| { - const global = self.symbol(global_index); - if (global.version_index == elf.VER_NDX_LOCAL) continue; - const file_ptr = global.file(self) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other)); - if (vis == .HIDDEN) continue; - // if (file == .shared and !global.isAbs(self)) { - // global.flags.import = true; - // continue; - // } - if (file_ptr.index() == index) { - global.flags.@"export" = true; - if (is_dyn_lib and vis != .PROTECTED) { - global.flags.import = true; + const mark = struct { + fn mark(elf_file: *Elf, file_index: File.Index) void { + for (elf_file.file(file_index).?.globals()) |global_index| { + const global = elf_file.symbol(global_index); + if (global.version_index == elf.VER_NDX_LOCAL) continue; + const file_ptr = global.file(elf_file) orelse continue; + const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other)); + if (vis == .HIDDEN) continue; + // if (file == .shared and !global.isAbs(self)) { + // global.flags.import = true; + // continue; + // } + if (file_ptr.index() == file_index) { + global.flags.@"export" = true; + if (elf_file.isDynLib() and vis != .PROTECTED) { + global.flags.import = true; + } } } } + }.mark; + + if (self.zig_module_index) |index| { + mark(self, index); + } + + for (self.objects.items) |index| { + mark(self, index); } +} +fn claimUnresolved(self: *Elf) void { + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + zig_module.claimUnresolved(self); + } for (self.objects.items) |index| { const object = self.file(index).?.object; - for (object.globals()) |global_index| { - const global = self.symbol(global_index); - if (global.version_index == elf.VER_NDX_LOCAL) continue; - const file_ptr = global.file(self) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other)); - if (vis == .HIDDEN) continue; - // if (file == .shared and !global.isAbs(self)) { - // global.flags.import = true; - // continue; - // } - if (file_ptr.index() == index) { - global.flags.@"export" = true; - if (is_dyn_lib and vis != .PROTECTED) { - global.flags.import = true; - } - } - } + object.claimUnresolved(self); } } @@ -3423,6 +3421,10 @@ pub fn defaultEntryAddress(self: Elf) u64 { }; } +pub fn isDynLib(self: Elf) bool { + return self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic; +} + pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 { for (self.shdrs.items, 0..) |*shdr, i| { const this_name = self.shstrtab.getAssumeExists(shdr.sh_name); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 7d85509901..e99f7c63f8 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -411,6 +411,34 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { } } +pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (self.globals(), 0..) |index, i| { + const esym_index = @as(u32, @intCast(first_global + i)); + const esym = self.symtab[esym_index]; + if (esym.st_shndx != elf.SHN_UNDEF) continue; + + const global = elf_file.symbol(index); + if (global.file(elf_file)) |_| { + if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue; + } + + const is_import = blk: { + if (!elf_file.isDynLib()) break :blk false; + const vis = @as(elf.STV, @enumFromInt(esym.st_other)); + if (vis == .HIDDEN) break :blk false; + break :blk true; + }; + + global.value = 0; + global.atom_index = 0; + global.esym_index = esym_index; + global.file_index = self.index; + global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; + global.flags.import = is_import; + } +} + pub fn resetGlobals(self: *Object, elf_file: *Elf) void { for (self.globals()) |index| { const global = elf_file.symbol(index); @@ -576,12 +604,12 @@ pub fn writeSymtab(self: *Object, elf_file: *Elf, ctx: anytype) void { } } -pub fn locals(self: *Object) []const u32 { +pub fn locals(self: *Object) []const Symbol.Index { const end = self.first_global orelse self.symbols.items.len; return self.symbols.items[0..end]; } -pub fn globals(self: *Object) []const u32 { +pub fn globals(self: *Object) []const Symbol.Index { const start = self.first_global orelse self.symbols.items.len; return self.symbols.items[start..]; } diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index f9df0d33ea..75fd3842e2 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -102,6 +102,34 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { } } +pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void { + for (self.globals(), 0..) |index, i| { + const esym_index = @as(Symbol.Index, @intCast(i)) | 0x10000000; + const esym = self.global_esyms.items[i]; + + if (esym.st_shndx != elf.SHN_UNDEF) continue; + + const global = elf_file.symbol(index); + if (global.file(elf_file)) |_| { + if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue; + } + + const is_import = blk: { + if (!elf_file.isDynLib()) break :blk false; + const vis = @as(elf.STV, @enumFromInt(esym.st_other)); + if (vis == .HIDDEN) break :blk false; + break :blk true; + }; + + global.value = 0; + global.atom_index = 0; + global.esym_index = esym_index; + global.file_index = self.index; + global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; + global.flags.import = is_import; + } +} + pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index e7fb789217..e75755949d 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -76,6 +76,12 @@ pub const File = union(enum) { } } + pub fn globals(file: File) []const Symbol.Index { + return switch (file) { + inline else => |x| x.globals(), + }; + } + pub const Index = u32; pub const Entry = union(enum) { -- cgit v1.2.3 From 44e84af874bc01fe0657d20548f69801cf18dccd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 16:32:55 +0200 Subject: elf: add simplistic reloc scanning mechanism --- src/arch/x86_64/CodeGen.zig | 2 ++ src/codegen.zig | 1 + src/link/Elf.zig | 35 ++++++++++++++++++++++++----- src/link/Elf/Atom.zig | 54 +++++++++++++++++++++++++++++++++++++++++++++ src/link/Elf/Object.zig | 11 ++++----- src/link/Elf/Symbol.zig | 24 ++++++++++++-------- src/link/Elf/ZigModule.zig | 8 +++++++ 7 files changed, 115 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bd63231890..e535894604 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8156,6 +8156,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); const sym = elf_file.symbol(sym_index); + sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(elf_file); const got_addr = sym.gotAddress(elf_file); try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ @@ -10234,6 +10235,7 @@ fn genLazySymbolRef( const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym = elf_file.symbol(sym_index); + sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(elf_file); const got_addr = sym.gotAddress(elf_file); const got_mem = diff --git a/src/codegen.zig b/src/codegen.zig index 7c6dcbdc46..cc53a903cd 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -856,6 +856,7 @@ fn genDeclRef( if (bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index); const sym = elf_file.symbol(sym_index); + sym.flags.needs_got = true; _ = try sym.getOrCreateGotEntry(elf_file); return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) }); } else if (bin_file.cast(link.File.MachO)) |macho_file| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index efd3dc9d54..994cf14089 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1049,8 +1049,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.resolveSymbols(); self.markImportsExports(); self.claimUnresolved(); - - if (self.unresolved.keys().len > 0) try self.reportUndefined(); + try self.scanRelocs(); self.allocateLinkerDefinedSymbols(); @@ -1381,6 +1380,28 @@ fn claimUnresolved(self: *Elf) void { } } +fn scanRelocs(self: *Elf) !void { + if (self.zig_module_index) |index| { + const zig_module = self.file(index).?.zig_module; + try zig_module.scanRelocs(self); + } + for (self.objects.items) |index| { + const object = self.file(index).?.object; + try object.scanRelocs(self); + } + + // try self.reportUndefined(); + + for (self.symbols.items) |*sym| { + if (sym.flags.needs_got) { + log.debug("'{s}' needs GOT", .{sym.name(self)}); + // TODO how can we tell we need to write it again, aka the entry is dirty? + const gop = try sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, gop.index); + } + } +} + fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2375,8 +2396,9 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - const got_index = try sym.getOrCreateGotEntry(self); - try self.got.writeEntry(self, got_index); + sym.flags.needs_got = true; + const gop = try sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, gop.index); } const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?; @@ -2609,8 +2631,9 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol. local_sym.value = atom_ptr.value; local_esym.st_value = atom_ptr.value; - const got_index = try local_sym.getOrCreateGotEntry(self); - try self.got.writeEntry(self, got_index); + local_sym.flags.needs_got = true; + const gop = try local_sym.getOrCreateGotEntry(self); + try self.got.writeEntry(self, gop.index); const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr; const file_offset = self.shdrs.items[local_sym.output_section_index].sh_offset + section_offset; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 6a029d97a2..d3a8de1302 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -311,6 +311,57 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void { zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity(); } +pub fn scanRelocs(self: Atom, elf_file: *Elf) !void { + const file_ptr = elf_file.file(self.file_index).?; + const rels = self.relocs(elf_file); + var i: usize = 0; + while (i < rels.len) : (i += 1) { + const rel = rels[i]; + + if (rel.r_type() == elf.R_X86_64_NONE) continue; + + const symbol = switch (file_ptr) { + .zig_module => elf_file.symbol(rel.r_sym()), + .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), + else => unreachable, + }; + + // While traversing relocations, mark symbols that require special handling such as + // pointer indirection via GOT, or a stub trampoline via PLT. + switch (rel.r_type()) { + elf.R_X86_64_64 => {}, + + elf.R_X86_64_32, + elf.R_X86_64_32S, + => {}, + + elf.R_X86_64_GOT32, + elf.R_X86_64_GOT64, + elf.R_X86_64_GOTPC32, + elf.R_X86_64_GOTPC64, + elf.R_X86_64_GOTPCREL, + elf.R_X86_64_GOTPCREL64, + elf.R_X86_64_GOTPCRELX, + elf.R_X86_64_REX_GOTPCRELX, + => { + symbol.flags.needs_got = true; + }, + + elf.R_X86_64_PLT32, + elf.R_X86_64_PLTOFF64, + => { + if (symbol.flags.import) { + symbol.flags.needs_plt = true; + } + }, + + elf.R_X86_64_PC32 => {}, + + else => @panic("TODO"), + } + } +} + /// TODO mark relocs dirty pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); @@ -484,6 +535,9 @@ fn format2( } } +// TODO this has to be u32 but for now, to avoid redesigning elfSym machinery for +// ZigModule, keep it at u16 with the intention of bumping it to u32 in the near +// future. pub const Index = u16; const std = @import("std"); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index e99f7c63f8..527b2e5c61 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -367,15 +367,16 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { } for (self.cies.items) |cie| { - for (cie.getRelocs(elf_file)) |rel| { + for (cie.relocs(elf_file)) |rel| { const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]); if (sym.flags.import) { - if (sym.getType(elf_file) != elf.STT_FUNC) - elf_file.base.fatal("{s}: {s}: CIE referencing external data reference", .{ + if (sym.type(elf_file) != elf.STT_FUNC) + // TODO convert into an error + log.debug("{s}: {s}: CIE referencing external data reference", .{ self.fmtPath(), - sym.getName(elf_file), + sym.name(elf_file), }); - sym.flags.plt = true; + sym.flags.needs_plt = true; } } } diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index db7e69d5e4..18c3e9021e 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -111,19 +111,23 @@ pub fn address(symbol: Symbol, opts: struct { } pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 { - if (!symbol.flags.got) return 0; + if (!symbol.flags.has_got) return 0; const extras = symbol.extra(elf_file).?; const entry = elf_file.got.entries.items[extras.got]; return entry.address(elf_file); } -pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GotSection.Index { - const index = if (symbol.flags.got) - symbol.extra(elf_file).?.got - else - try elf_file.got.addGotSymbol(symbol.index, elf_file); - symbol.flags.got = true; - return index; +const GetOrCreateGotEntryResult = struct { + found_existing: bool, + index: GotSection.Index, +}; + +pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GetOrCreateGotEntryResult { + assert(symbol.flags.needs_got); + if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got }; + const index = try elf_file.got.addGotSymbol(symbol.index, elf_file); + symbol.flags.has_got = true; + return .{ .found_existing = false, .index = index }; } // pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 { @@ -310,9 +314,11 @@ pub const Flags = packed struct { output_symtab: bool = false, /// Whether the symbol contains GOT indirection. - got: bool = false, + needs_got: bool = false, + has_got: bool = false, /// Whether the symbol contains PLT indirection. + needs_plt: bool = false, plt: bool = false, /// Whether the PLT entry is canonical. is_canonical: bool = false, diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 75fd3842e2..5d231dc02a 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -130,6 +130,14 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void { } } +pub fn scanRelocs(self: *ZigModule, elf_file: *Elf) !void { + for (self.atoms.keys()) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.alive) continue; + try atom.scanRelocs(elf_file); + } +} + pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index); -- cgit v1.2.3 From 472d326a8c88570f629d58539b9829c69730a96b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 17:35:56 +0200 Subject: elf: set output section index when parsing objects --- src/link/Elf.zig | 11 +++++-- src/link/Elf/Object.zig | 78 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 994cf14089..9ee1389397 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1043,14 +1043,17 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.linker_defined_index = index; } + // Symbol resolution happens here try self.addLinkerDefinedSymbols(); - - // Resolve symbols self.resolveSymbols(); self.markImportsExports(); self.claimUnresolved(); + + // Scan and create missing synthetic entries such as GOT indirection try self.scanRelocs(); + // Allocate atoms parsed from input object files + self.allocateObjects(); self.allocateLinkerDefinedSymbols(); // Beyond this point, everything has been allocated a virtual address and we can resolve @@ -1402,6 +1405,10 @@ fn scanRelocs(self: *Elf) !void { } } +fn allocateObjects(self: *Elf) void { + _ = self; +} + fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 527b2e5c61..d06dfe3b3d 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -17,7 +17,6 @@ comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, fdes: std.ArrayListUnmanaged(Fde) = .{}, cies: std.ArrayListUnmanaged(Cie) = .{}, -needs_exec_stack: bool = false, alive: bool = true, num_dynrelocs: u32 = 0, @@ -80,12 +79,12 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { try self.initAtoms(elf_file); try self.initSymtab(elf_file); - for (self.shdrs.items, 0..) |shdr, i| { - const atom = elf_file.atom(self.atoms.items[i]) orelse continue; - if (!atom.alive) continue; - if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame")) - try self.parseEhFrame(@as(u16, @intCast(i)), elf_file); - } + // for (self.shdrs.items, 0..) |shdr, i| { + // const atom = elf_file.atom(self.atoms.items[i]) orelse continue; + // if (!atom.alive) continue; + // if (shdr.sh_type == elf.SHT_X86_64_UNWIND or mem.eql(u8, atom.name(elf_file), ".eh_frame")) + // try self.parseEhFrame(@as(u16, @intCast(i)), elf_file); + // } } fn initAtoms(self: *Object, elf_file: *Elf) !void { @@ -148,20 +147,6 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { else => { const name = self.strings.getAssumeExists(shdr.sh_name); const shndx = @as(u16, @intCast(i)); - - // if (mem.eql(u8, ".note.GNU-stack", name)) { - // if (shdr.sh_flags & elf.SHF_EXECINSTR != 0) { - // if (!elf_file.options.z_execstack or !elf_file.options.z_execstack_if_needed) { - // elf_file.base.warn( - // "{}: may cause segmentation fault as this file requested executable stack", - // .{self.fmtPath()}, - // ); - // } - // self.needs_exec_stack = true; - // } - // continue; - // } - if (self.skipShdr(shndx, elf_file)) continue; try self.addAtom(shdr, shndx, name, elf_file); }, @@ -187,6 +172,7 @@ fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name); atom.file_index = self.index; atom.input_section_index = shndx; + atom.output_section_index = self.getOutputSectionIndex(elf_file, shdr); atom.alive = true; self.atoms.items[shndx] = atom_index; @@ -201,15 +187,61 @@ fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, } } +fn getOutputSectionIndex(self: *Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) u16 { + const name = blk: { + const name = self.strings.getAssumeExists(shdr.sh_name); + // if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; + const sh_name_prefixes: []const [:0]const u8 = &.{ + ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", + ".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors", + ".dtors", ".gnu.warning", + }; + inline for (sh_name_prefixes) |prefix| { + if (std.mem.eql(u8, name, prefix) or std.mem.startsWith(u8, name, prefix ++ ".")) { + break :blk prefix; + } + } + break :blk name; + }; + const @"type" = switch (shdr.sh_type) { + elf.SHT_NULL => unreachable, + elf.SHT_PROGBITS => blk: { + if (std.mem.eql(u8, name, ".init_array") or std.mem.startsWith(u8, name, ".init_array.")) + break :blk elf.SHT_INIT_ARRAY; + if (std.mem.eql(u8, name, ".fini_array") or std.mem.startsWith(u8, name, ".fini_array.")) + break :blk elf.SHT_FINI_ARRAY; + break :blk shdr.sh_type; + }, + elf.SHT_X86_64_UNWIND => elf.SHT_PROGBITS, + else => shdr.sh_type, + }; + const flags = blk: { + const flags = shdr.sh_flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); + break :blk switch (@"type") { + elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE, + else => flags, + }; + }; + _ = flags; + const out_shndx = elf_file.sectionByName(name) orelse { + log.err("{}: output section {s} not found", .{ self.fmtPath(), name }); + @panic("TODO: missing output section!"); + }; + return out_shndx; +} + fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { + _ = elf_file; const shdr = self.shdrs.items[index]; const name = self.strings.getAssumeExists(shdr.sh_name); const ignore = blk: { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".comment")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; - if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and - mem.startsWith(u8, name, ".debug")) break :blk true; + if (mem.startsWith(u8, name, ".eh_frame")) break :blk true; + // if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and + // mem.startsWith(u8, name, ".debug")) break :blk true; + if (shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true; break :blk false; }; return ignore; -- cgit v1.2.3 From 9db472cff6573fd1d0f50c8ea1b5ecb536aeff1e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 17:52:55 +0200 Subject: elf: set output section index of a global when resolving --- src/link/Elf/Object.zig | 5 +++++ src/link/Elf/ZigModule.zig | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index d06dfe3b3d..b40ebf8365 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -434,10 +434,15 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { elf.SHN_ABS, elf.SHN_COMMON => 0, else => self.atoms.items[esym.st_shndx], }; + const output_section_index = if (elf_file.atom(atom_index)) |atom| + atom.output_section_index + else + 0; global.value = esym.st_value; global.atom_index = atom_index; global.esym_index = esym_index; global.file_index = self.index; + global.output_section_index = output_section_index; global.version_index = elf_file.default_sym_version; if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; } diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 5d231dc02a..5e655fc274 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -81,7 +81,7 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { if (esym.st_shndx == elf.SHN_UNDEF) continue; if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { - const atom_index = self.atoms.keys()[esym.st_shndx]; + const atom_index = esym.st_shndx; const atom = elf_file.atom(atom_index) orelse continue; if (!atom.alive) continue; } @@ -90,12 +90,17 @@ pub fn resolveSymbols(self: *ZigModule, elf_file: *Elf) void { if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) { const atom_index = switch (esym.st_shndx) { elf.SHN_ABS, elf.SHN_COMMON => 0, - else => self.atoms.keys()[esym.st_shndx], + else => esym.st_shndx, }; + const output_section_index = if (elf_file.atom(atom_index)) |atom| + atom.output_section_index + else + 0; global.value = esym.st_value; global.atom_index = atom_index; global.esym_index = esym_index; global.file_index = self.index; + global.output_section_index = output_section_index; global.version_index = elf_file.default_sym_version; if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true; } -- cgit v1.2.3 From 652ebf3b6a62007d37fb7fd4def393f11bb6159f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 18:07:10 +0200 Subject: elf: allocate objects, currently atom-by-atom --- src/link/Elf.zig | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9ee1389397..ca75a1e863 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1053,7 +1053,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.scanRelocs(); // Allocate atoms parsed from input object files - self.allocateObjects(); + try self.allocateObjects(); self.allocateLinkerDefinedSymbols(); // Beyond this point, everything has been allocated a virtual address and we can resolve @@ -1405,8 +1405,22 @@ fn scanRelocs(self: *Elf) !void { } } -fn allocateObjects(self: *Elf) void { - _ = self; +fn allocateObjects(self: *Elf) !void { + for (self.objects.items) |index| { + const object = self.file(index).?.object; + for (object.atoms.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + try atom_ptr.allocate(self); + } + + for (object.globals()) |global_index| { + const global = self.symbol(global_index); + if (global.file_index == index) { + global.value = global.atom(self).?.value; + } + } + } } fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { -- cgit v1.2.3 From ae74a36af0aa8c876b118973969aec1a10c25665 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 19:17:57 +0200 Subject: elf: resolve and write objects to file --- src/arch/x86_64/Emit.zig | 2 +- src/link/Elf.zig | 28 ++++++++++++++++++++++++++++ src/link/Elf/Atom.zig | 26 ++++++++++++++++++++------ src/link/Elf/Object.zig | 4 ++++ 4 files changed, 53 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 51820740dc..ab1d63e64c 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -45,7 +45,7 @@ pub fn emitMir(emit: *Emit) Error!void { // Add relocation to the decl. const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; try atom_ptr.addReloc(elf_file, .{ - .r_offset = end_offset, + .r_offset = end_offset - 4, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | std.elf.R_X86_64_PLT32, .r_addend = -4, }); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ca75a1e863..85ddb80cfd 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1072,6 +1072,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.base.file.?.pwriteAll(code, file_offset); } } + try self.writeObjects(); try self.updateSymtabSize(); try self.writeSymtab(); @@ -1414,6 +1415,13 @@ fn allocateObjects(self: *Elf) !void { try atom_ptr.allocate(self); } + for (object.locals()) |local_index| { + const local = self.symbol(local_index); + const atom_ptr = local.atom(self) orelse continue; + if (!atom_ptr.alive) continue; + local.value = atom_ptr.value; + } + for (object.globals()) |global_index| { const global = self.symbol(global_index); if (global.file_index == index) { @@ -1423,6 +1431,26 @@ fn allocateObjects(self: *Elf) !void { } } +fn writeObjects(self: *Elf) !void { + const gpa = self.base.allocator; + + for (self.objects.items) |index| { + const object = self.file(index).?.object; + for (object.atoms.items) |atom_index| { + const atom_ptr = self.atom(atom_index) orelse continue; + if (!atom_ptr.alive) continue; + + const shdr = &self.shdrs.items[atom_ptr.output_section_index]; + const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; + const code = try atom_ptr.codeInObjectUncompressAlloc(self); + defer gpa.free(code); + + try atom_ptr.resolveRelocs(self, code); + try self.base.file.?.pwriteAll(code, file_offset); + } + } +} + fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index d3a8de1302..871570843d 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -67,12 +67,13 @@ pub fn codeInObjectUncompressAlloc(self: Atom, elf_file: *Elf) ![]u8 { switch (chdr.ch_type) { .ZLIB => { var stream = std.io.fixedBufferStream(data[@sizeOf(elf.Elf64_Chdr)..]); - var zlib_stream = try std.compress.zlib.decompressStream(gpa, stream.reader()); + var zlib_stream = std.compress.zlib.decompressStream(gpa, stream.reader()) catch + return error.InputOutput; defer zlib_stream.deinit(); const decomp = try gpa.alloc(u8, chdr.ch_size); - const nread = try zlib_stream.reader().readAll(decomp); + const nread = zlib_stream.reader().readAll(decomp) catch return error.InputOutput; if (nread != decomp.len) { - return error.Io; + return error.InputOutput; } return decomp; }, @@ -366,6 +367,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void { pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + const file_ptr = elf_file.file(self.file_index).?; var stream = std.io.fixedBufferStream(code); const cwriter = stream.writer(); @@ -373,7 +375,11 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { const r_type = rel.r_type(); if (r_type == elf.R_X86_64_NONE) continue; - const target = elf_file.symbol(rel.r_sym()); + const target = switch (file_ptr) { + .zig_module => elf_file.symbol(rel.r_sym()), + .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), + else => unreachable, + }; // We will use equation format to resolve relocations: // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ @@ -414,9 +420,17 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { switch (rel.r_type()) { elf.R_X86_64_NONE => unreachable, + elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A), - elf.R_X86_64_PLT32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))), - else => @panic("TODO"), + + elf.R_X86_64_PLT32, + elf.R_X86_64_PC32, + => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))), + + else => { + log.err("TODO: unhandled relocation type {}", .{fmtRelocType(rel.r_type())}); + @panic("TODO unhandled relocation type"); + }, } } } diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index b40ebf8365..14892302a4 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -270,6 +270,10 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void { sym_ptr.esym_index = @as(u32, @intCast(i)); sym_ptr.atom_index = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx]; sym_ptr.file_index = self.index; + sym_ptr.output_section_index = if (sym_ptr.atom(elf_file)) |atom_ptr| + atom_ptr.output_section_index + else + 0; } for (self.symtab[first_global..]) |sym| { -- cgit v1.2.3 From 9719fa7412371243aaac6f307f80d783c7ac50cb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 19:26:51 +0200 Subject: elf: include C compilation artifacts on the linker line --- src/link/Elf.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 85ddb80cfd..51f909cb50 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -998,7 +998,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node _ = compiler_rt_path; // Parse input files - for (self.base.options.objects) |obj| { + var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); + defer positionals.deinit(); + try positionals.ensureUnusedCapacity(self.base.options.objects.len); + positionals.appendSliceAssumeCapacity(self.base.options.objects); + + for (comp.c_object_table.keys()) |key| { + try positionals.append(.{ .path = key.status.success.object_path }); + } + + for (positionals.items) |obj| { const in_file = try std.fs.cwd().openFile(obj.path, .{}); defer in_file.close(); -- cgit v1.2.3 From 1a6d12ea92eb9033aed686c88b53ff406ec2fbe7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Sep 2023 23:27:11 +0200 Subject: elf: clean up and unify symbol ref handling in relocs Also, this lets us re-enable proper undefined symbols tracking. --- src/link/Elf.zig | 170 +++++++++++++++++++++------------------------ src/link/Elf/Atom.zig | 43 +++++++++++- src/link/Elf/Object.zig | 4 +- src/link/Elf/Symbol.zig | 9 +-- src/link/Elf/ZigModule.zig | 25 ++++++- src/link/Elf/file.zig | 2 +- 6 files changed, 146 insertions(+), 107 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 51f909cb50..48209ec3d9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -6,6 +6,9 @@ ptr_width: PtrWidth, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. llvm_object: ?*LlvmObject = null, +/// A list of all input files. +/// Index of each input file also encodes the priority or precedence of one input file +/// over another. files: std.MultiArrayList(File.Entry) = .{}, zig_module_index: ?File.Index = null, linker_defined_index: ?File.Index = null, @@ -47,6 +50,7 @@ shstrtab: StringTable(.strtab) = .{}, /// .strtab buffer strtab: StringTable(.strtab) = .{}, +/// Representation of the GOT table as committed to the file. got: GotSection = .{}, text_section_index: ?u16 = null, @@ -86,10 +90,10 @@ rela_iplt_start_index: ?Symbol.Index = null, rela_iplt_end_index: ?Symbol.Index = null, start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, +/// An array of symbols parsed across all input files. symbols: std.ArrayListUnmanaged(Symbol) = .{}, symbols_extra: std.ArrayListUnmanaged(u32) = .{}, resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, -unresolved: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, phdr_table_dirty: bool = false, @@ -271,7 +275,6 @@ pub fn deinit(self: *Elf) void { self.symbols_free_list.deinit(gpa); self.got.deinit(gpa); self.resolver.deinit(gpa); - self.unresolved.deinit(gpa); self.start_stop_indexes.deinit(gpa); { @@ -316,7 +319,7 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link. const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?; try parent_atom.addReloc(self, .{ .r_offset = reloc_info.offset, - .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | elf.R_X86_64_64, + .r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | elf.R_X86_64_64, .r_addend = reloc_info.addend, }); @@ -997,12 +1000,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node }; _ = compiler_rt_path; - // Parse input files + // Here we will parse input positional and library files (if referenced). + // This will roughly match in any linker backend we support. var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); try positionals.ensureUnusedCapacity(self.base.options.objects.len); positionals.appendSliceAssumeCapacity(self.base.options.objects); + // This is a set of object files emitted by clang in a single `build-exe` invocation. + // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up + // in this set. for (comp.c_object_table.keys()) |key| { try positionals.append(.{ .path = key.status.success.object_path }); } @@ -1016,6 +1023,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.handleAndReportParseError(obj.path, err, &parse_ctx); } + // Handle any lazy symbols that were emitted by incremental compilation. if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but // anyerror needs to wait for everything to be flushed. @@ -1046,27 +1054,34 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try dw.flushModule(module); } + // If we haven't already, create a linker-generated input file comprising of + // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. if (self.linker_defined_index == null) { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .linker_defined = .{ .index = index } }); self.linker_defined_index = index; } - - // Symbol resolution happens here try self.addLinkerDefinedSymbols(); + + // Now, we are ready to resolve the symbols across all input files. + // We will first resolve the files in the ZigModule, next in the parsed + // input Object files. + // Any qualifing unresolved symbol will be upgraded to an absolute, weak + // symbol for potential resolution at load-time. self.resolveSymbols(); self.markImportsExports(); self.claimUnresolved(); - // Scan and create missing synthetic entries such as GOT indirection + // Scan and create missing synthetic entries such as GOT indirection. try self.scanRelocs(); - // Allocate atoms parsed from input object files + // Allocate atoms parsed from input object files, followed by allocating + // linker-defined synthetic symbols. try self.allocateObjects(); self.allocateLinkerDefinedSymbols(); // Beyond this point, everything has been allocated a virtual address and we can resolve - // the relocations. + // the relocations, and commit objects to file. if (self.zig_module_index) |index| { for (self.file(index).?.zig_module.atoms.keys()) |atom_index| { const atom_ptr = self.atom(atom_index).?; @@ -1083,9 +1098,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } try self.writeObjects(); + // Generate and emit the symbol table. try self.updateSymtabSize(); try self.writeSymtab(); + // Dump the state for easy debugging. + // State can be dumped via `--debug-log link_state`. if (build_options.enable_logging) { state_log.debug("{}", .{self.dumpState()}); } @@ -1393,17 +1411,32 @@ fn claimUnresolved(self: *Elf) void { } } +/// In scanRelocs we will go over all live atoms and scan their relocs. +/// This will help us work out what synthetics to emit, GOT indirection, etc. +/// This is also the point where we will report undefined symbols for any +/// alloc sections. fn scanRelocs(self: *Elf) !void { + const gpa = self.base.allocator; + + var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa); + defer { + var it = undefs.iterator(); + while (it.next()) |entry| { + entry.value_ptr.deinit(); + } + undefs.deinit(); + } + if (self.zig_module_index) |index| { const zig_module = self.file(index).?.zig_module; - try zig_module.scanRelocs(self); + try zig_module.scanRelocs(self, &undefs); } for (self.objects.items) |index| { const object = self.file(index).?.object; - try object.scanRelocs(self); + try object.scanRelocs(self, &undefs); } - // try self.reportUndefined(); + try self.reportUndefined(&undefs); for (self.symbols.items) |*sym| { if (sym.flags.needs_got) { @@ -1433,8 +1466,10 @@ fn allocateObjects(self: *Elf) !void { for (object.globals()) |global_index| { const global = self.symbol(global_index); + const atom_ptr = global.atom(self) orelse continue; + if (!atom_ptr.alive) continue; if (global.file_index == index) { - global.value = global.atom(self).?.value; + global.value = atom_ptr.value; } } } @@ -2829,21 +2864,23 @@ pub fn updateDeclExports( }; const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info)); + const name_off = try self.strtab.insert(gpa, exp_name); const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { const sym_index = try zig_module.addGlobalEsym(gpa); - _ = try zig_module.global_symbols.addOne(gpa); + const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, name_off); + const esym = zig_module.elfSym(sym_index); + esym.st_name = name_off; + lookup_gop.value_ptr.* = sym_index; try decl_metadata.exports.append(gpa, sym_index); + const gop = try self.getOrPutGlobal(name_off); + try zig_module.global_symbols.append(gpa, gop.index); break :blk sym_index; }; - const name_off = try self.strtab.insert(gpa, exp_name); - const esym = &zig_module.global_esyms.items[sym_index]; + const esym = &zig_module.global_esyms.items[sym_index & 0x0fffffff]; esym.st_value = decl_sym.value; esym.st_shndx = decl_sym.atom_index; esym.st_info = (stb_bits << 4) | stt_bits; esym.st_name = name_off; - - const gop = try self.getOrPutGlobal(name_off); - zig_module.global_symbols.items[sym_index] = gop.index; } } @@ -3636,16 +3673,17 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 _ = lib_name; const gpa = self.base.allocator; const off = try self.strtab.insert(gpa, name); - const gop = try self.getOrPutGlobal(off); const zig_module = self.file(self.zig_module_index.?).?.zig_module; const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { const esym_index = try zig_module.addGlobalEsym(gpa); - const esym = &zig_module.global_esyms.items[esym_index]; + const esym = zig_module.elfSym(esym_index); esym.st_name = off; lookup_gop.value_ptr.* = esym_index; + const gop = try self.getOrPutGlobal(off); + try zig_module.global_symbols.append(gpa, gop.index); } - return gop.index; + return lookup_gop.value_ptr.*; } const GetOrCreateComdatGroupOwnerResult = struct { @@ -3684,89 +3722,39 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO return &self.comdat_groups_owners.items[index]; } -fn reportUndefined(self: *Elf) !void { +fn reportUndefined(self: *Elf, undefs: anytype) !void { const gpa = self.base.allocator; const max_notes = 4; - try self.misc_errors.ensureUnusedCapacity(gpa, self.unresolved.keys().len); - - const CollectStruct = struct { - notes: [max_notes]link.File.ErrorMsg = [_]link.File.ErrorMsg{.{ .msg = undefined }} ** max_notes, - notes_len: u3 = 0, - notes_count: usize = 0, - }; - - const collect: []CollectStruct = try gpa.alloc(CollectStruct, self.unresolved.keys().len); - defer gpa.free(collect); - @memset(collect, .{}); - - // Collect all references across all input files - if (self.zig_module_index) |index| { - const zig_module = self.file(index).?.zig_module; - for (zig_module.atoms.keys()) |atom_index| { - const atom_ptr = self.atom(atom_index).?; - if (!atom_ptr.alive) continue; - - for (atom_ptr.relocs(self)) |rel| { - if (self.unresolved.getIndex(rel.r_sym())) |bin_index| { - const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{ - zig_module.path, - atom_ptr.name(self), - }); - const bin = &collect[bin_index]; - if (bin.notes_len < max_notes) { - bin.notes[bin.notes_len] = .{ .msg = note }; - bin.notes_len += 1; - } - bin.notes_count += 1; - } - } - } - } - - for (self.objects.items) |index| { - const object = self.file(index).?.object; - for (object.atoms.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.alive) continue; + try self.misc_errors.ensureUnusedCapacity(gpa, undefs.count()); - for (atom_ptr.relocs(self)) |rel| { - const sym_index = object.symbols.items[rel.r_sym()]; - if (self.unresolved.getIndex(sym_index)) |bin_index| { - const note = try std.fmt.allocPrint(gpa, "referenced by {}:{s}", .{ - object.fmtPath(), - atom_ptr.name(self), - }); - const bin = &collect[bin_index]; - if (bin.notes_len < max_notes) { - bin.notes[bin.notes_len] = .{ .msg = note }; - bin.notes_len += 1; - } - bin.notes_count += 1; - } - } - } - } - - // Generate error notes - for (self.unresolved.keys(), 0..) |sym_index, bin_index| { - const collected = &collect[bin_index]; + var it = undefs.iterator(); + while (it.next()) |entry| { + const undef_index = entry.key_ptr.*; + const atoms = entry.value_ptr.*.items; + const nnotes = @min(atoms.len, max_notes); var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1); defer notes.deinit(); - for (collected.notes[0..collected.notes_len]) |note| { - notes.appendAssumeCapacity(note); + for (atoms[0..nnotes]) |atom_index| { + const atom_ptr = self.atom(atom_index).?; + const file_ptr = self.file(atom_ptr.file_index).?; + const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{ + file_ptr.fmtPath(), + atom_ptr.name(self), + }); + notes.appendAssumeCapacity(.{ .msg = note }); } - if (collected.notes_count > max_notes) { - const remaining = collected.notes_count - max_notes; + if (atoms.len > max_notes) { + const remaining = atoms.len - max_notes; const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining}); notes.appendAssumeCapacity(.{ .msg = note }); } var err_msg = link.File.ErrorMsg{ - .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(sym_index).name(self)}), + .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)}), }; err_msg.notes = try notes.toOwnedSlice(); @@ -3931,7 +3919,7 @@ const DeclMetadata = struct { fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { const zig_module = elf_file.file(elf_file.zig_module_index.?).?.zig_module; for (m.exports.items) |*exp| { - const exp_name = elf_file.strtab.getAssumeExists(zig_module.global_esyms.items[exp.*].st_name); + const exp_name = elf_file.strtab.getAssumeExists(zig_module.elfSym(exp.*).st_name); if (mem.eql(u8, name, exp_name)) return exp; } return null; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 871570843d..67686fec41 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -312,7 +312,7 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void { zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity(); } -pub fn scanRelocs(self: Atom, elf_file: *Elf) !void { +pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { const file_ptr = elf_file.file(self.file_index).?; const rels = self.relocs(elf_file); var i: usize = 0; @@ -322,11 +322,25 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void { if (rel.r_type() == elf.R_X86_64_NONE) continue; const symbol = switch (file_ptr) { - .zig_module => elf_file.symbol(rel.r_sym()), + .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())), .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), else => unreachable, }; + // Check for violation of One Definition Rule for COMDATs. + if (symbol.file(elf_file) == null) { + // TODO convert into an error + log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ + file_ptr.fmtPath(), + self.name(elf_file), + symbol.name(elf_file), + }); + continue; + } + + // Report an undefined symbol. + try self.reportUndefined(elf_file, symbol, rel, undefs); + // While traversing relocations, mark symbols that require special handling such as // pointer indirection via GOT, or a stub trampoline via PLT. switch (rel.r_type()) { @@ -363,6 +377,28 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void { } } +// This function will report any undefined non-weak symbols that are not imports. +fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf64_Rela, undefs: anytype) !void { + const rel_esym = switch (elf_file.file(self.file_index).?) { + .zig_module => |x| x.elfSym(rel.r_sym()).*, + .object => |x| x.symtab[rel.r_sym()], + else => unreachable, + }; + const esym = sym.elfSym(elf_file); + if (rel_esym.st_shndx == elf.SHN_UNDEF and + rel_esym.st_bind() == elf.STB_GLOBAL and + sym.esym_index > 0 and + !sym.flags.import and + esym.st_shndx == elf.SHN_UNDEF) + { + const gop = try undefs.getOrPut(sym.index); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList(Atom.Index).init(elf_file.base.allocator); + } + try gop.value_ptr.append(self.atom_index); + } +} + /// TODO mark relocs dirty pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); @@ -376,7 +412,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { if (r_type == elf.R_X86_64_NONE) continue; const target = switch (file_ptr) { - .zig_module => elf_file.symbol(rel.r_sym()), + .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())), .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]), else => unreachable, }; @@ -564,3 +600,4 @@ const Allocator = std.mem.Allocator; const Atom = @This(); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 14892302a4..f095a5f99e 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -392,14 +392,14 @@ fn filterRelocs( return .{ .start = f_start, .len = f_len }; } -pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { +pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.alive) continue; const shdr = atom.inputShdr(elf_file); if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; - try atom.scanRelocs(elf_file); + try atom.scanRelocs(elf_file, undefs); } for (self.cies.items) |cie| { diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 18c3e9021e..42b9b81ef9 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -43,7 +43,7 @@ pub fn isLocal(symbol: Symbol) bool { return !(symbol.flags.import or symbol.flags.@"export"); } -pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { +pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { return symbol.type(elf_file) == elf.STT_GNU_IFUNC; } @@ -69,12 +69,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File { pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { const file_ptr = symbol.file(elf_file).?; switch (file_ptr) { - .zig_module => |x| { - const is_global = symbol.esym_index & 0x10000000 != 0; - const esym_index = symbol.esym_index & 0x0fffffff; - if (is_global) return x.global_esyms.items[esym_index]; - return x.local_esyms.items[esym_index]; - }, + .zig_module => |x| return x.elfSym(symbol.esym_index).*, .linker_defined => |x| return x.symtab.items[symbol.esym_index], .object => |x| return x.symtab[symbol.esym_index], } diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index 5e655fc274..46a382abf9 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -1,3 +1,8 @@ +//! ZigModule encapsulates the state of the incrementally compiled Zig module. +//! It stores the associated input local and global symbols, allocated atoms, +//! and any relocations that may have been emitted. +//! Think about this as fake in-memory Object file for the Zig module. + /// Path is owned by Module and lives as long as *Module. path: []const u8, index: File.Index, @@ -41,7 +46,7 @@ pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index { const esym = self.global_esyms.addOneAssumeCapacity(); esym.* = Elf.null_sym; esym.st_info = elf.STB_GLOBAL << 4; - return index; + return index | 0x10000000; } pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { @@ -135,11 +140,11 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void { } } -pub fn scanRelocs(self: *ZigModule, elf_file: *Elf) !void { +pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void { for (self.atoms.keys()) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.alive) continue; - try atom.scanRelocs(elf_file); + try atom.scanRelocs(elf_file, undefs); } } @@ -197,6 +202,20 @@ pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void { } } +pub fn symbol(self: *ZigModule, index: Symbol.Index) Symbol.Index { + const is_global = index & 0x10000000 != 0; + const actual_index = index & 0x0fffffff; + if (is_global) return self.global_symbols.items[actual_index]; + return self.local_symbols.items[actual_index]; +} + +pub fn elfSym(self: *ZigModule, index: Symbol.Index) *elf.Elf64_Sym { + const is_global = index & 0x10000000 != 0; + const actual_index = index & 0x0fffffff; + if (is_global) return &self.global_esyms.items[actual_index]; + return &self.local_esyms.items[actual_index]; +} + pub fn locals(self: *ZigModule) []const Symbol.Index { return self.local_symbols.items; } diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index e75755949d..2b49f43bf1 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -23,7 +23,7 @@ pub const File = union(enum) { _ = unused_fmt_string; _ = options; switch (file) { - .zig_module => try writer.writeAll("(zig module)"), + .zig_module => |x| try writer.print("{s}", .{x.path}), .linker_defined => try writer.writeAll("(linker defined)"), .object => |x| try writer.print("{}", .{x.fmtPath()}), // .shared_object => |x| try writer.writeAll(x.path), -- cgit v1.2.3 From 6910a50ae59d978b47897e6cc04601f4e7e395c8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 13 Sep 2023 00:31:41 +0200 Subject: elf: add u64 to usize casts where required --- src/link/Elf.zig | 5 +++-- src/link/Elf/Atom.zig | 13 +++++++------ src/link/Elf/Object.zig | 36 ++++++++++++++++++++---------------- src/link/Elf/eh_frame.zig | 44 ++++++++++++++++++++++++-------------------- 4 files changed, 54 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 48209ec3d9..3a600662bc 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1088,7 +1088,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (!atom_ptr.alive) continue; const shdr = &self.shdrs.items[atom_ptr.output_section_index]; const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; - const code = try gpa.alloc(u8, atom_ptr.size); + const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; + const code = try gpa.alloc(u8, size); defer gpa.free(code); const amt = try self.base.file.?.preadAll(code, file_offset); if (amt != code.len) return error.InputOutput; @@ -3145,7 +3146,7 @@ fn writeSymtab(self: *Elf) !void { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; - const nsyms = @divExact(shdr.sh_size, sym_size); + const nsyms = math.cast(usize, @divExact(shdr.sh_size, sym_size)) orelse return error.Overflow; log.debug("writing {d} symbols at 0x{x}", .{ nsyms, shdr.sh_offset }); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 67686fec41..82c2b46d1d 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -51,7 +51,7 @@ pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr { return object.shdrs.items[self.input_section_index]; } -pub fn codeInObject(self: Atom, elf_file: *Elf) []const u8 { +pub fn codeInObject(self: Atom, elf_file: *Elf) error{Overflow}![]const u8 { const object = elf_file.file(self.file_index).?.object; return object.shdrContents(self.input_section_index); } @@ -60,7 +60,7 @@ pub fn codeInObject(self: Atom, elf_file: *Elf) []const u8 { /// Caller owns the memory. pub fn codeInObjectUncompressAlloc(self: Atom, elf_file: *Elf) ![]u8 { const gpa = elf_file.base.allocator; - const data = self.codeInObject(elf_file); + const data = try self.codeInObject(elf_file); const shdr = self.inputShdr(elf_file); if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; @@ -70,7 +70,8 @@ pub fn codeInObjectUncompressAlloc(self: Atom, elf_file: *Elf) ![]u8 { var zlib_stream = std.compress.zlib.decompressStream(gpa, stream.reader()) catch return error.InputOutput; defer zlib_stream.deinit(); - const decomp = try gpa.alloc(u8, chdr.ch_size); + const size = std.math.cast(usize, chdr.ch_size) orelse return error.Overflow; + const decomp = try gpa.alloc(u8, size); const nread = zlib_stream.reader().readAll(decomp) catch return error.InputOutput; if (nread != decomp.len) { return error.InputOutput; @@ -288,7 +289,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { self.* = .{}; } -pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela { +pub fn relocs(self: Atom, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela { return switch (elf_file.file(self.file_index).?) { .zig_module => |x| x.relocs.items[self.relocs_section_index].items, .object => |x| x.getRelocs(self.relocs_section_index), @@ -314,7 +315,7 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void { pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void { const file_ptr = elf_file.file(self.file_index).?; - const rels = self.relocs(elf_file); + const rels = try self.relocs(elf_file); var i: usize = 0; while (i < rels.len) : (i += 1) { const rel = rels[i]; @@ -407,7 +408,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void { var stream = std.io.fixedBufferStream(code); const cwriter = stream.writer(); - for (self.relocs(elf_file)) |rel| { + for (try self.relocs(elf_file)) |rel| { const r_type = rel.r_type(); if (r_type == elf.R_X86_64_NONE) continue; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index f095a5f99e..b0a6ef2a1c 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -54,12 +54,13 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; + const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow; const shdrs = @as( [*]align(1) const elf.Elf64_Shdr, - @ptrCast(self.data.ptr + self.header.?.e_shoff), + @ptrCast(self.data.ptr + shoff), )[0..self.header.?.e_shnum]; try self.shdrs.appendUnalignedSlice(gpa, shdrs); - try self.strings.buffer.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx)); + try self.strings.buffer.appendSlice(gpa, try self.shdrContents(self.header.?.e_shstrndx)); const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_SYMTAB => break @as(u16, @intCast(i)), @@ -70,10 +71,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const shdr = shdrs[index]; self.first_global = shdr.sh_info; - const symtab = self.shdrContents(index); + const symtab = try self.shdrContents(index); const nsyms = @divExact(symtab.len, @sizeOf(elf.Elf64_Sym)); self.symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms]; - self.strtab = self.shdrContents(@as(u16, @intCast(shdr.sh_link))); + self.strtab = try self.shdrContents(@as(u16, @intCast(shdr.sh_link))); } try self.initAtoms(elf_file); @@ -114,7 +115,7 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { }; const shndx = @as(u16, @intCast(i)); - const group_raw_data = self.shdrContents(shndx); + const group_raw_data = try self.shdrContents(shndx); const group_nmembers = @divExact(group_raw_data.len, @sizeOf(u32)); const group_members = @as([*]align(1) const u32, @ptrCast(group_raw_data.ptr))[0..group_nmembers]; @@ -177,7 +178,7 @@ fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8, self.atoms.items[shndx] = atom_index; if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { - const data = self.shdrContents(shndx); + const data = try self.shdrContents(shndx); const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; atom.size = chdr.ch_size; atom.alignment = math.log2_int(u64, chdr.ch_addralign); @@ -294,8 +295,8 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void { }; const gpa = elf_file.base.allocator; - const raw = self.shdrContents(shndx); - const relocs = self.getRelocs(relocs_shndx); + const raw = try self.shdrContents(shndx); + const relocs = try self.getRelocs(relocs_shndx); const fdes_start = self.fdes.items.len; const cies_start = self.cies.items.len; @@ -403,7 +404,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { } for (self.cies.items) |cie| { - for (cie.relocs(elf_file)) |rel| { + for (try cie.relocs(elf_file)) |rel| { const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]); if (sym.flags.import) { if (sym.type(elf_file) != elf.STT_FUNC) @@ -656,10 +657,12 @@ pub fn globals(self: *Object) []const Symbol.Index { return self.symbols.items[start..]; } -pub fn shdrContents(self: *Object, index: u32) []const u8 { +pub fn shdrContents(self: *Object, index: u32) error{Overflow}![]const u8 { assert(index < self.shdrs.items.len); const shdr = self.shdrs.items[index]; - return self.data[shdr.sh_offset..][0..shdr.sh_size]; + const offset = math.cast(usize, shdr.sh_offset) orelse return error.Overflow; + const size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + return self.data[offset..][0..size]; } fn getString(self: *Object, off: u32) [:0]const u8 { @@ -667,8 +670,8 @@ fn getString(self: *Object, off: u32) [:0]const u8 { return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0); } -pub fn comdatGroupMembers(self: *Object, index: u16) []align(1) const u32 { - const raw = self.shdrContents(index); +pub fn comdatGroupMembers(self: *Object, index: u16) error{Overflow}![]align(1) const u32 { + const raw = try self.shdrContents(index); const nmembers = @divExact(raw.len, @sizeOf(u32)); const members = @as([*]align(1) const u32, @ptrCast(raw.ptr))[1..nmembers]; return members; @@ -678,8 +681,8 @@ pub fn asFile(self: *Object) File { return .{ .object = self }; } -pub fn getRelocs(self: *Object, shndx: u32) []align(1) const elf.Elf64_Rela { - const raw = self.shdrContents(shndx); +pub fn getRelocs(self: *Object, shndx: u32) error{Overflow}![]align(1) const elf.Elf64_Rela { + const raw = try self.shdrContents(shndx); const num = @divExact(raw.len, @sizeOf(elf.Elf64_Rela)); return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; } @@ -819,7 +822,8 @@ fn formatComdatGroups( const cg = elf_file.comdatGroup(cg_index); const cg_owner = elf_file.comdatGroupOwner(cg.owner); if (cg_owner.file != object.index) continue; - for (object.comdatGroupMembers(cg.shndx)) |shndx| { + const cg_members = object.comdatGroupMembers(cg.shndx) catch continue; + for (cg_members) |shndx| { const atom_index = object.atoms.items[shndx]; const atom = elf_file.atom(atom_index) orelse continue; try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.name(elf_file) }); diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 71dd5a6a22..8c676504e2 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -20,9 +20,9 @@ pub const Fde = struct { return base + fde.out_offset; } - pub fn data(fde: Fde, elf_file: *Elf) []const u8 { + pub fn data(fde: Fde, elf_file: *Elf) error{Overflow}![]const u8 { const object = elf_file.file(fde.file_index).?.object; - const contents = object.shdrContents(fde.input_section_index); + const contents = try object.shdrContents(fde.input_section_index); return contents[fde.offset..][0..fde.calcSize()]; } @@ -39,17 +39,17 @@ pub const Fde = struct { return fde.size + 4; } - pub fn atom(fde: Fde, elf_file: *Elf) *Atom { + pub fn atom(fde: Fde, elf_file: *Elf) error{Overflow}!*Atom { const object = elf_file.file(fde.file_index).?.object; - const rel = fde.relocs(elf_file)[0]; + const rel = (try fde.relocs(elf_file))[0]; const sym = object.symtab[rel.r_sym()]; const atom_index = object.atoms.items[sym.st_shndx]; return elf_file.atom(atom_index).?; } - pub fn relocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela { + pub fn relocs(fde: Fde, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela { const object = elf_file.file(fde.file_index).?.object; - return object.getRelocs(fde.rel_section_index)[fde.rel_index..][0..fde.rel_num]; + return (try object.getRelocs(fde.rel_section_index))[fde.rel_index..][0..fde.rel_num]; } pub fn format( @@ -88,11 +88,15 @@ pub const Fde = struct { const fde = ctx.fde; const elf_file = ctx.elf_file; const base_addr = fde.address(elf_file); + const atom_name = if (fde.atom(elf_file)) |atom_ptr| + atom_ptr.name(elf_file) + else |_| + ""; try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ base_addr + fde.out_offset, fde.calcSize(), fde.cie_index, - fde.atom(elf_file).name(elf_file), + atom_name, }); if (!fde.alive) try writer.writeAll(" : [*]"); } @@ -119,9 +123,9 @@ pub const Cie = struct { return base + cie.out_offset; } - pub fn data(cie: Cie, elf_file: *Elf) []const u8 { + pub fn data(cie: Cie, elf_file: *Elf) error{Overflow}![]const u8 { const object = elf_file.file(cie.file_index).?.object; - const contents = object.shdrContents(cie.input_section_index); + const contents = try object.shdrContents(cie.input_section_index); return contents[cie.offset..][0..cie.calcSize()]; } @@ -129,16 +133,16 @@ pub const Cie = struct { return cie.size + 4; } - pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela { + pub fn relocs(cie: Cie, elf_file: *Elf) error{Overflow}![]align(1) const elf.Elf64_Rela { const object = elf_file.file(cie.file_index).?.object; - return object.getRelocs(cie.rel_section_index)[cie.rel_index..][0..cie.rel_num]; + return (try object.getRelocs(cie.rel_section_index))[cie.rel_index..][0..cie.rel_num]; } - pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool { - if (!std.mem.eql(u8, cie.data(elf_file), other.data(elf_file))) return false; + pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) error{Overflow}!bool { + if (!std.mem.eql(u8, try cie.data(elf_file), try other.data(elf_file))) return false; - const cie_relocs = cie.relocs(elf_file); - const other_relocs = other.relocs(elf_file); + const cie_relocs = try cie.relocs(elf_file); + const other_relocs = try other.relocs(elf_file); if (cie_relocs.len != other_relocs.len) return false; for (cie_relocs, other_relocs) |cie_rel, other_rel| { @@ -315,10 +319,10 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { for (object.cies.items) |cie| { if (!cie.alive) continue; - const contents = try gpa.dupe(u8, cie.data(elf_file)); + const contents = try gpa.dupe(u8, try cie.data(elf_file)); defer gpa.free(contents); - for (cie.relocs(elf_file)) |rel| { + for (try cie.relocs(elf_file)) |rel| { const sym = object.symbol(rel.r_sym(), elf_file); try resolveReloc(cie, sym, rel, elf_file, contents); } @@ -333,7 +337,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { for (object.fdes.items) |fde| { if (!fde.alive) continue; - const contents = try gpa.dupe(u8, fde.data(elf_file)); + const contents = try gpa.dupe(u8, try fde.data(elf_file)); defer gpa.free(contents); std.mem.writeIntLittle( @@ -342,7 +346,7 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { @as(i32, @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(elf_file).out_offset)))), ); - for (fde.relocs(elf_file)) |rel| { + for (try fde.relocs(elf_file)) |rel| { const sym = object.symbol(rel.r_sym(), elf_file); try resolveReloc(fde, sym, rel, elf_file, contents); } @@ -391,7 +395,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { for (object.fdes.items) |fde| { if (!fde.alive) continue; - const relocs = fde.relocs(elf_file); + const relocs = try fde.relocs(elf_file); assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips... const rel = relocs[0]; const sym = object.symbol(rel.r_sym(), elf_file); -- cgit v1.2.3