elf: start porting abstraction of input file

This commit is contained in:
Jakub Konka 2023-09-05 15:55:00 +02:00
parent 02451bdebf
commit d9fffd431a
5 changed files with 945 additions and 258 deletions

View File

@ -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");

View File

@ -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");

337
src/link/Elf/Symbol.zig Normal file
View File

@ -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();

View File

@ -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");

110
src/link/Elf/file.zig Normal file
View File

@ -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");