From d9fffd431a89ed4104bcc0b2165bfb9917cdd82b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 5 Sep 2023 15:55:00 +0200 Subject: [PATCH] elf: start porting abstraction of input file --- src/link/Elf.zig | 555 ++++++++++++++++++--------------- 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, 945 insertions(+), 258 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 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; - } + 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; + }; - const target_endian = self.base.options.target.cpu.arch.endian(); - const foreign_endian = target_endian != builtin.cpu.arch.endian(); + self.linker_defined = blk: { + const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + self.files.set(index, .{ .linker_defined = .{} }); + break :blk index; + }; - if (self.dwarf) |*dw| { - try dw.flushModule(module); - } + std.debug.print("{}\n", .{self.dumpState()}); - { - 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]; + // 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; + // } - log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)}); + // const target_endian = self.base.options.target.cpu.arch.endian(); + // const foreign_endian = target_endian != builtin.cpu.arch.endian(); - for (relocs.items) |*reloc| { - const target_sym = self.locals.items[reloc.target]; - const target_vaddr = target_sym.st_value + reloc.addend; + // if (self.dwarf) |*dw| { + // try dw.flushModule(module); + // } - if (target_vaddr == reloc.prev_vaddr) continue; + // { + // 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]; - const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; - const file_offset = source_shdr.sh_offset + section_offset; + // log.debug("relocating '{?s}'", .{self.strtab.get(source_sym.st_name)}); - log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{ - reloc.offset, - target_vaddr, - self.strtab.get(target_sym.st_name), - }); + // for (relocs.items) |*reloc| { + // const target_sym = self.locals.items[reloc.target]; + // const target_vaddr = target_sym.st_value + reloc.addend; - 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), - } + // if (target_vaddr == reloc.prev_vaddr) continue; - reloc.prev_vaddr = target_vaddr; - } - } - } + // const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; + // const file_offset = source_shdr.sh_offset + section_offset; - try self.writeSymbols(); + // log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{ + // reloc.offset, + // target_vaddr, + // self.strtab.get(target_sym.st_name), + // }); - if (build_options.enable_logging) { - self.logSymtab(); - } + // 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), + // } - 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; - } + // reloc.prev_vaddr = target_vaddr; + // } + // } + // } - 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; - } + // try self.writeSymbols(); - 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 (build_options.enable_logging) { + // self.logSymtab(); + // } - if (self.debug_line_header_dirty) { - try dw.writeDbgLineHeader(); - self.debug_line_header_dirty = false; - } - } + // 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.phdr_table_dirty) { - const phsize: u64 = switch (self.ptr_width) { - .p32 => @sizeOf(elf.Elf32_Phdr), - .p64 => @sizeOf(elf.Elf64_Phdr), - }; + // 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; + // } - 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.?]; + // 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; + // } - const allocated_size = self.allocatedSize(phdr_table.p_offset); - const needed_size = self.program_headers.items.len * phsize; + // if (self.debug_line_header_dirty) { + // try dw.writeDbgLineHeader(); + // self.debug_line_header_dirty = false; + // } + // } - 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))); - } + // if (self.phdr_table_dirty) { + // const phsize: u64 = switch (self.ptr_width) { + // .p32 => @sizeOf(elf.Elf32_Phdr), + // .p64 => @sizeOf(elf.Elf64_Phdr), + // }; - 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; + // 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.?]; - 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; + // const allocated_size = self.allocatedSize(phdr_table.p_offset); + // const needed_size = self.program_headers.items.len * phsize; - switch (self.ptr_width) { - .p32 => { - const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); - defer gpa.free(buf); + // 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))); + // } - 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); + // 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; - 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); - }, - } + // 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; - // 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; + // switch (self.ptr_width) { + // .p32 => { + // const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); + // defer gpa.free(buf); - self.phdr_table_dirty = false; - } + // 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); - { - 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; - } - } + // 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); + // }, + // } - { - 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; - } - } + // // 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; - 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; - } - } + // self.phdr_table_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; + // { + // 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; + // } + // } - if (needed_size > allocated_size) { - self.shdr_table_offset = null; // free the space - self.shdr_table_offset = self.findFreeSpace(needed_size, shalign); - } + // { + // 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; + // } + // } - switch (self.ptr_width) { - .p32 => { - const slice = self.sections.slice(); - const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); - defer gpa.free(buf); + // 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; + // } + // } - 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); + // 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; - 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(); - } + // if (needed_size > allocated_size) { + // self.shdr_table_offset = null; // free the space + // self.shdr_table_offset = self.findFreeSpace(needed_size, shalign); + // } - // 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); + // 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");