Commit a3ce011408

Jakub Konka <kubkon@jakubkonka.com>
2023-09-10 11:10:16
elf: allocate linker defined symbols
1 parent 1c3fd16
Changed files (1)
src
link
src/link/Elf.zig
@@ -47,8 +47,13 @@ got: GotSection = .{},
 
 text_section_index: ?u16 = null,
 rodata_section_index: ?u16 = null,
-got_section_index: ?u16 = null,
 data_section_index: ?u16 = null,
+dynamic_section_index: ?u16 = null,
+got_section_index: ?u16 = null,
+got_plt_section_index: ?u16 = null,
+plt_section_index: ?u16 = null,
+eh_frame_hdr_section_index: ?u16 = null,
+rela_dyn_section_index: ?u16 = null,
 debug_info_section_index: ?u16 = null,
 debug_abbrev_section_index: ?u16 = null,
 debug_str_section_index: ?u16 = null,
@@ -74,6 +79,7 @@ gnu_eh_frame_hdr_index: ?Symbol.Index = null,
 dso_handle_index: ?Symbol.Index = null,
 rela_iplt_start_index: ?Symbol.Index = null,
 rela_iplt_end_index: ?Symbol.Index = null,
+start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
 
 symbols: std.ArrayListUnmanaged(Symbol) = .{},
 symbols_extra: std.ArrayListUnmanaged(u32) = .{},
@@ -264,6 +270,7 @@ pub fn deinit(self: *Elf) void {
     self.got.deinit(gpa);
     self.resolver.deinit(gpa);
     self.unresolved.deinit(gpa);
+    self.start_stop_indexes.deinit(gpa);
 
     {
         var it = self.decls.iterator();
@@ -385,6 +392,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
         .p64 => false,
     };
     const ptr_size: u8 = self.ptrWidthBytes();
+    const image_base = self.calcImageBase();
 
     if (self.phdr_table_index == null) {
         self.phdr_table_index = @as(u16, @intCast(self.phdrs.items.len));
@@ -396,8 +404,8 @@ pub fn populateMissingMetadata(self: *Elf) !void {
             .p_type = elf.PT_PHDR,
             .p_offset = 0,
             .p_filesz = 0,
-            .p_vaddr = 0,
-            .p_paddr = 0,
+            .p_vaddr = image_base,
+            .p_paddr = image_base,
             .p_memsz = 0,
             .p_align = p_align,
             .p_flags = elf.PF_R,
@@ -408,16 +416,14 @@ pub fn populateMissingMetadata(self: *Elf) !void {
     if (self.phdr_table_load_index == null) {
         self.phdr_table_load_index = @as(u16, @intCast(self.phdrs.items.len));
         // TODO Same as for GOT
-        const phdr_addr: u64 = if (self.base.options.target.ptrBitWidth() >= 32) 0x1000000 else 0x1000;
-        const p_align = self.page_size;
         try self.phdrs.append(gpa, .{
             .p_type = elf.PT_LOAD,
             .p_offset = 0,
             .p_filesz = 0,
-            .p_vaddr = phdr_addr,
-            .p_paddr = phdr_addr,
+            .p_vaddr = image_base,
+            .p_paddr = image_base,
             .p_memsz = 0,
-            .p_align = p_align,
+            .p_align = self.page_size,
             .p_flags = elf.PF_R,
         });
         self.phdr_table_dirty = true;
@@ -429,7 +435,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
         const p_align = self.page_size;
         const off = self.findFreeSpace(file_size, p_align);
         log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });
-        const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
+        const entry_addr = self.defaultEntryAddress();
         try self.phdrs.append(gpa, .{
             .p_type = elf.PT_LOAD,
             .p_offset = off,
@@ -1055,6 +1061,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
 
     try self.addLinkerDefinedSymbols();
 
+    self.allocateLinkerDefinedSymbols();
+
     // Beyond this point, everything has been allocated a virtual address and we can resolve
     // the relocations.
     {
@@ -2764,6 +2772,129 @@ fn addLinkerDefinedSymbols(self: *Elf) !void {
     linker_defined.resolveSymbols(self);
 }
 
+fn allocateLinkerDefinedSymbols(self: *Elf) void {
+    // _DYNAMIC
+    if (self.dynamic_section_index) |shndx| {
+        const shdr = self.sections.items(.shdr)[shndx];
+        const symbol_ptr = self.symbol(self.dynamic_index.?);
+        symbol_ptr.value = shdr.sh_addr;
+        symbol_ptr.output_section_index = shndx;
+    }
+
+    // __ehdr_start
+    {
+        const symbol_ptr = self.symbol(self.ehdr_start_index.?);
+        symbol_ptr.value = self.calcImageBase();
+        symbol_ptr.output_section_index = 1;
+    }
+
+    // __init_array_start, __init_array_end
+    if (self.sectionByName(".init_array")) |shndx| {
+        const start_sym = self.symbol(self.init_array_start_index.?);
+        const end_sym = self.symbol(self.init_array_end_index.?);
+        const shdr = &self.sections.items(.shdr)[shndx];
+        start_sym.output_section_index = shndx;
+        start_sym.value = shdr.sh_addr;
+        end_sym.output_section_index = shndx;
+        end_sym.value = shdr.sh_addr + shdr.sh_size;
+    }
+
+    // __fini_array_start, __fini_array_end
+    if (self.sectionByName(".fini_array")) |shndx| {
+        const start_sym = self.symbol(self.fini_array_start_index.?);
+        const end_sym = self.symbol(self.fini_array_end_index.?);
+        const shdr = &self.sections.items(.shdr)[shndx];
+        start_sym.output_section_index = shndx;
+        start_sym.value = shdr.sh_addr;
+        end_sym.output_section_index = shndx;
+        end_sym.value = shdr.sh_addr + shdr.sh_size;
+    }
+
+    // __preinit_array_start, __preinit_array_end
+    if (self.sectionByName(".preinit_array")) |shndx| {
+        const start_sym = self.symbol(self.preinit_array_start_index.?);
+        const end_sym = self.symbol(self.preinit_array_end_index.?);
+        const shdr = &self.sections.items(.shdr)[shndx];
+        start_sym.output_section_index = shndx;
+        start_sym.value = shdr.sh_addr;
+        end_sym.output_section_index = shndx;
+        end_sym.value = shdr.sh_addr + shdr.sh_size;
+    }
+
+    // _GLOBAL_OFFSET_TABLE_
+    if (self.got_plt_section_index) |shndx| {
+        const shdr = &self.sections.items(.shdr)[shndx];
+        const symbol_ptr = self.symbol(self.got_index.?);
+        symbol_ptr.value = shdr.sh_addr;
+        symbol_ptr.output_section_index = shndx;
+    }
+
+    // _PROCEDURE_LINKAGE_TABLE_
+    if (self.plt_section_index) |shndx| {
+        const shdr = &self.sections.items(.shdr)[shndx];
+        const symbol_ptr = self.symbol(self.plt_index.?);
+        symbol_ptr.value = shdr.sh_addr;
+        symbol_ptr.output_section_index = shndx;
+    }
+
+    // __dso_handle
+    if (self.dso_handle_index) |index| {
+        const shdr = &self.sections.items(.shdr)[1];
+        const symbol_ptr = self.symbol(index);
+        symbol_ptr.value = shdr.sh_addr;
+        symbol_ptr.output_section_index = 0;
+    }
+
+    // __GNU_EH_FRAME_HDR
+    if (self.eh_frame_hdr_section_index) |shndx| {
+        const shdr = &self.sections.items(.shdr)[shndx];
+        const symbol_ptr = self.symbol(self.gnu_eh_frame_hdr_index.?);
+        symbol_ptr.value = shdr.sh_addr;
+        symbol_ptr.output_section_index = shndx;
+    }
+
+    // __rela_iplt_start, __rela_iplt_end
+    if (self.rela_dyn_section_index) |shndx| blk: {
+        if (self.base.options.link_mode != .Static or self.base.options.pie) break :blk;
+        const shdr = &self.sections.items(.shdr)[shndx];
+        const end_addr = shdr.sh_addr + shdr.sh_size;
+        const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela);
+        const start_sym = self.symbol(self.rela_iplt_start_index.?);
+        const end_sym = self.symbol(self.rela_iplt_end_index.?);
+        start_sym.value = start_addr;
+        start_sym.output_section_index = shndx;
+        end_sym.value = end_addr;
+        end_sym.output_section_index = shndx;
+    }
+
+    // _end
+    {
+        const end_symbol = self.symbol(self.end_index.?);
+        for (self.sections.items(.shdr), 0..) |shdr, shndx| {
+            if (shdr.sh_flags & elf.SHF_ALLOC != 0) {
+                end_symbol.value = shdr.sh_addr + shdr.sh_size;
+                end_symbol.output_section_index = @intCast(shndx);
+            }
+        }
+    }
+
+    // __start_*, __stop_*
+    {
+        var index: usize = 0;
+        while (index < self.start_stop_indexes.items.len) : (index += 2) {
+            const start = self.symbol(self.start_stop_indexes.items[index]);
+            const name = start.name(self);
+            const stop = self.symbol(self.start_stop_indexes.items[index + 1]);
+            const shndx = self.sectionByName(name["__start_".len..]).?;
+            const shdr = self.sections.items(.shdr)[shndx];
+            start.value = shdr.sh_addr;
+            start.output_section_index = shndx;
+            stop.value = shdr.sh_addr + shdr.sh_size;
+            stop.output_section_index = shndx;
+        }
+    }
+}
+
 fn updateSymtabSize(self: *Elf) !void {
     var sizes = SymtabSize{};
 
@@ -2774,17 +2905,17 @@ fn updateSymtabSize(self: *Elf) !void {
         sizes.nglobals += zig_module.output_symtab_size.nglobals;
     }
 
+    if (self.got_section_index) |_| {
+        self.got.updateSymtabSize(self);
+        sizes.nlocals += self.got.output_symtab_size.nlocals;
+    }
+
     if (self.linker_defined_index) |index| {
         const linker_defined = self.file(index).?.linker_defined;
         linker_defined.updateSymtabSize(self);
         sizes.nlocals += linker_defined.output_symtab_size.nlocals;
     }
 
-    if (self.got_section_index) |_| {
-        self.got.updateSymtabSize(self);
-        sizes.nlocals += self.got.output_symtab_size.nlocals;
-    }
-
     const shdr = &self.sections.items(.shdr)[self.symtab_section_index.?];
     shdr.sh_info = sizes.nlocals + 1;
     self.markDirty(self.symtab_section_index.?, null);
@@ -2829,17 +2960,17 @@ fn writeSymtab(self: *Elf) !void {
         ctx.iglobal += zig_module.output_symtab_size.nglobals;
     }
 
+    if (self.got_section_index) |_| {
+        try self.got.writeSymtab(self, ctx);
+        ctx.ilocal += self.got.output_symtab_size.nlocals;
+    }
+
     if (self.linker_defined_index) |index| {
         const linker_defined = self.file(index).?.linker_defined;
         linker_defined.writeSymtab(self, ctx);
         ctx.ilocal += linker_defined.output_symtab_size.nlocals;
     }
 
-    if (self.got_section_index) |_| {
-        try self.got.writeSymtab(self, ctx);
-        ctx.ilocal += self.got.output_symtab_size.nlocals;
-    }
-
     const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
     switch (self.ptr_width) {
         .p32 => {
@@ -3179,6 +3310,34 @@ const CsuObjects = struct {
     }
 };
 
+pub fn calcImageBase(self: Elf) u64 {
+    if (self.base.options.pic) return 0; // TODO flag an error if PIC and image_base_override
+    return self.base.options.image_base_override orelse switch (self.ptr_width) {
+        .p32 => 0x1000,
+        .p64 => 0x1000000,
+    };
+}
+
+pub fn defaultEntryAddress(self: Elf) u64 {
+    if (self.entry_addr) |addr| return addr;
+    return switch (self.base.options.target.cpu.arch) {
+        .spu_2 => 0,
+        else => default_entry_addr,
+    };
+}
+
+pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u16 {
+    for (self.sections.items(.shdr), 0..) |shdr, i| {
+        const this_name = self.shstrtab.getAssumeExists(shdr.sh_name);
+        if (mem.eql(u8, this_name, name)) return @as(u16, @intCast(i));
+    } else return null;
+}
+
+pub fn calcNumIRelativeRelocs(self: *Elf) u64 {
+    _ = self;
+    unreachable; // TODO
+}
+
 pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom {
     if (atom_index == 0) return null;
     assert(atom_index < self.atoms.items.len);