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