Commit e401930fa8

Jakub Konka <kubkon@jakubkonka.com>
2024-02-13 16:42:28
elf: store relative offsets in atom and symbol
1 parent c22bb38
src/link/Elf/Atom.zig
@@ -54,6 +54,12 @@ pub fn name(self: Atom, elf_file: *Elf) []const u8 {
     };
 }
 
+pub fn address(self: Atom, elf_file: *Elf) u64 {
+    const shndx = self.outputShndx() orelse return self.value;
+    const shdr = elf_file.shdrs.items[shndx];
+    return shdr.sh_addr + self.value;
+}
+
 pub fn file(self: Atom, elf_file: *Elf) ?File {
     return elf_file.file(self.file_index);
 }
@@ -85,14 +91,17 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 {
 /// File offset relocation happens transparently, so it is not included in
 /// this calculation.
 pub fn capacity(self: Atom, elf_file: *Elf) u64 {
-    const next_value = if (elf_file.atom(self.next_index)) |next| next.value else std.math.maxInt(u32);
-    return next_value - self.value;
+    const next_addr = if (elf_file.atom(self.next_index)) |next|
+        next.address(elf_file)
+    else
+        std.math.maxInt(u32);
+    return next_addr - self.address(elf_file);
 }
 
 pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
     // No need to keep a free list node for the last block.
     const next = elf_file.atom(self.next_index) orelse return false;
-    const cap = next.value - self.value;
+    const cap = next.address(elf_file) - self.address(elf_file);
     const ideal_cap = Elf.padToIdeal(self.size);
     if (cap <= ideal_cap) return false;
     const surplus = cap - ideal_cap;
@@ -160,15 +169,15 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
             atom_placement = last.atom_index;
             break :blk new_start_vaddr;
         } else {
-            break :blk shdr.sh_addr;
+            break :blk 0;
         }
     };
 
     log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{
         self.atom_index,
         self.name(elf_file),
-        self.value,
-        self.value + self.size,
+        self.address(elf_file),
+        self.address(elf_file) + self.size,
     });
 
     const expand_section = if (atom_placement) |placement_index|
@@ -176,7 +185,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void {
     else
         true;
     if (expand_section) {
-        const needed_size = (self.value + self.size) - shdr.sh_addr;
+        const needed_size = self.value + self.size;
         try elf_file.growAllocSection(self.outputShndx().?, needed_size);
         last_atom_index.* = self.atom_index;
 
@@ -301,7 +310,7 @@ pub fn relocs(self: Atom, elf_file: *Elf) []align(1) const elf.Elf64_Rela {
 }
 
 pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.Elf64_Rela)) !void {
-    relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
+    relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
 
     const file_ptr = self.file(elf_file).?;
     for (self.relocs(elf_file)) |rel| {
@@ -322,7 +331,7 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
         var r_sym: u32 = 0;
         switch (target.type(elf_file)) {
             elf.STT_SECTION => {
-                r_addend += @intCast(target.value);
+                r_addend += @intCast(target.address(.{}, elf_file));
                 r_sym = elf_file.sectionSymbolOutputSymtabIndex(target.outputShndx().?);
             },
             else => {
@@ -778,7 +787,7 @@ fn reportUndefined(
 }
 
 pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
-    relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
+    relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
 
     const file_ptr = self.file(elf_file).?;
     var stream = std.io.fixedBufferStream(code);
@@ -802,7 +811,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void {
         // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
         //
         // Address of the source atom.
-        const P = @as(i64, @intCast(self.value + rel.r_offset));
+        const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset));
         // Addend from the relocation.
         const A = rel.r_addend;
         // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
@@ -969,7 +978,7 @@ fn resolveDynAbsReloc(
 ) !void {
     const comp = elf_file.base.comp;
     const gpa = comp.gpa;
-    const P = self.value + rel.r_offset;
+    const P = self.address(elf_file) + rel.r_offset;
     const A = rel.r_addend;
     const S = @as(i64, @intCast(target.address(.{}, elf_file)));
     const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0;
@@ -1058,7 +1067,7 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void {
 }
 
 pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
-    relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
+    relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
 
     const file_ptr = self.file(elf_file).?;
     var stream = std.io.fixedBufferStream(code);
@@ -1097,7 +1106,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any
         // We will use equation format to resolve relocations:
         // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/
         //
-        const P = @as(i64, @intCast(self.value + rel.r_offset));
+        const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset));
         // Addend from the relocation.
         const A = rel.r_addend;
         // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub.
@@ -1248,7 +1257,7 @@ fn format2(
     const atom = ctx.atom;
     const elf_file = ctx.elf_file;
     try writer.print("atom({d}) : {s} : @{x} : shdr({d}) : align({x}) : size({x})", .{
-        atom.atom_index,           atom.name(elf_file), atom.value,
+        atom.atom_index,           atom.name(elf_file), atom.address(elf_file),
         atom.output_section_index, atom.alignment,      atom.size,
     });
     if (atom.fde_start != atom.fde_end) {
src/link/Elf/eh_frame.zig
@@ -409,7 +409,7 @@ fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Re
     var r_sym: u32 = 0;
     switch (sym.type(elf_file)) {
         elf.STT_SECTION => {
-            r_addend += @intCast(sym.value);
+            r_addend += @intCast(sym.address(.{}, elf_file));
             r_sym = elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
         },
         else => {
src/link/Elf/Object.zig
@@ -718,21 +718,11 @@ pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void {
         if (!gop.found_existing) gop.value_ptr.* = .{};
         try gop.value_ptr.append(gpa, atom_index);
     }
-}
-
-pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
-    for (self.atoms.items) |atom_index| {
-        const atom = elf_file.atom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
-        const shdr = elf_file.shdrs.items[atom.output_section_index];
-        atom.value += shdr.sh_addr;
-    }
 
     for (self.locals()) |local_index| {
         const local = elf_file.symbol(local_index);
         const atom = local.atom(elf_file) orelse continue;
         if (!atom.flags.alive) continue;
-        local.value += atom.value;
         local.output_section_index = atom.output_section_index;
     }
 
@@ -741,7 +731,6 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
         const atom = global.atom(elf_file) orelse continue;
         if (!atom.flags.alive) continue;
         if (global.file(elf_file).?.index() != self.index) continue;
-        global.value += atom.value;
         global.output_section_index = atom.output_section_index;
     }
 }
src/link/Elf/relocatable.zig
@@ -195,7 +195,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
 
     try allocateAllocSections(elf_file);
     try elf_file.allocateNonAllocSections();
-    elf_file.allocateAtoms();
 
     if (build_options.enable_logging) {
         state_log.debug("{}", .{elf_file.dumpState()});
src/link/Elf/Symbol.zig
@@ -104,6 +104,9 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf
         // Lazy-bound function it is!
         return symbol.pltAddress(elf_file);
     }
+    if (symbol.atom(elf_file)) |atom_ptr| {
+        return atom_ptr.address(elf_file) + symbol.value;
+    }
     return symbol.value;
 }
 
@@ -247,11 +250,11 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
             if (symbol.flags.is_canonical) break :blk symbol.address(.{}, elf_file);
             break :blk 0;
         }
-        if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.value;
+        if (st_shndx == elf.SHN_ABS or st_shndx == elf.SHN_COMMON) break :blk symbol.address(.{ .plt = false }, elf_file);
         const shdr = &elf_file.shdrs.items[st_shndx];
         if (shdr.sh_flags & elf.SHF_TLS != 0 and file_ptr != .linker_defined)
-            break :blk symbol.value - elf_file.tlsAddress();
-        break :blk symbol.value;
+            break :blk symbol.address(.{ .plt = false }, elf_file) - elf_file.tlsAddress();
+        break :blk symbol.address(.{ .plt = false }, elf_file);
     };
     out.st_info = (st_bind << 4) | st_type;
     out.st_other = esym.st_other;
@@ -323,7 +326,11 @@ fn format2(
     _ = options;
     _ = unused_fmt_string;
     const symbol = ctx.symbol;
-    try writer.print("%{d} : {s} : @{x}", .{ symbol.esym_index, symbol.fmtName(ctx.elf_file), symbol.value });
+    try writer.print("%{d} : {s} : @{x}", .{
+        symbol.esym_index,
+        symbol.fmtName(ctx.elf_file),
+        symbol.address(.{}, ctx.elf_file),
+    });
     if (symbol.file(ctx.elf_file)) |file_ptr| {
         if (symbol.isAbs(ctx.elf_file)) {
             if (symbol.elfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) {
src/link/Elf/synthetic_sections.zig
@@ -297,7 +297,7 @@ pub const ZigGotSection = struct {
         const off = zig_got.entryOffset(index, elf_file);
         const vaddr = zig_got.entryAddress(index, elf_file);
         const entry = zig_got.entries.items[index];
-        const value = elf_file.symbol(entry).value;
+        const value = elf_file.symbol(entry).address(.{}, elf_file);
         switch (entry_size) {
             2 => {
                 var buf: [2]u8 = undefined;
@@ -1004,7 +1004,7 @@ pub const GotPltSection = struct {
         {
             // [0]: _DYNAMIC
             const symbol = elf_file.symbol(elf_file.dynamic_index.?);
-            try writer.writeInt(u64, symbol.value, .little);
+            try writer.writeInt(u64, symbol.address(.{}, elf_file), .little);
         }
         // [1]: 0x0
         // [2]: 0x0
src/link/Elf/ZigObject.zig
@@ -401,19 +401,6 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
     }
 }
 
-pub fn allocateTlvAtoms(self: ZigObject, elf_file: *Elf) void {
-    for (self.tls_variables.keys(), self.tls_variables.values()) |atom_index, tlv| {
-        const atom = elf_file.atom(atom_index) orelse continue;
-        if (!atom.flags.alive) continue;
-        const local = elf_file.symbol(tlv.symbol_index);
-        const shdr = elf_file.shdrs.items[atom.output_section_index];
-        atom.value += shdr.sh_addr;
-        local.value = atom.value;
-
-        // TODO exported TLS vars
-    }
-}
-
 pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
     const gpa = elf_file.base.comp.gpa;
     for (self.atoms.items) |atom_index| {
@@ -644,7 +631,7 @@ pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8
         return code;
     }
 
-    const file_offset = shdr.sh_offset + atom.value - shdr.sh_addr;
+    const file_offset = shdr.sh_offset + atom.value;
     const size = std.math.cast(usize, atom.size) orelse return error.Overflow;
     const code = try gpa.alloc(u8, size);
     errdefer gpa.free(code);
@@ -664,7 +651,7 @@ pub fn getDeclVAddr(
 ) !u64 {
     const this_sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
     const this_sym = elf_file.symbol(this_sym_index);
-    const vaddr = this_sym.value;
+    const vaddr = this_sym.address(.{}, elf_file);
     const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
     try parent_atom.addReloc(elf_file, .{
         .r_offset = reloc_info.offset,
@@ -682,7 +669,7 @@ pub fn getAnonDeclVAddr(
 ) !u64 {
     const sym_index = self.anon_decls.get(decl_val).?.symbol_index;
     const sym = elf_file.symbol(sym_index);
-    const vaddr = sym.value;
+    const vaddr = sym.address(.{}, elf_file);
     const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
     try parent_atom.addReloc(elf_file, .{
         .r_offset = reloc_info.offset,
@@ -941,13 +928,13 @@ fn updateDeclCode(
 
     if (old_size > 0 and elf_file.base.child_pid == null) {
         const capacity = atom_ptr.capacity(elf_file);
-        const need_realloc = code.len > capacity or !required_alignment.check(sym.value);
+        const need_realloc = code.len > capacity or !required_alignment.check(atom_ptr.value);
         if (need_realloc) {
             try atom_ptr.grow(elf_file);
             log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom_ptr.value });
             if (old_vaddr != atom_ptr.value) {
-                sym.value = atom_ptr.value;
-                esym.st_value = atom_ptr.value;
+                sym.value = 0;
+                esym.st_value = 0;
 
                 if (!elf_file.base.isRelocatable()) {
                     log.debug("  (writing new offset table entry)", .{});
@@ -963,9 +950,9 @@ fn updateDeclCode(
         try atom_ptr.allocate(elf_file);
         errdefer self.freeDeclMetadata(elf_file, sym_index);
 
-        sym.value = atom_ptr.value;
+        sym.value = 0;
         sym.flags.needs_zig_got = true;
-        esym.st_value = atom_ptr.value;
+        esym.st_value = 0;
 
         if (!elf_file.base.isRelocatable()) {
             const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
@@ -981,7 +968,7 @@ fn updateDeclCode(
                     .iov_len = code.len,
                 }};
                 var remote_vec: [1]std.os.iovec_const = .{.{
-                    .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.value)))),
+                    .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))),
                     .iov_len = code.len,
                 }};
                 const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
@@ -996,7 +983,7 @@ fn updateDeclCode(
 
     const shdr = elf_file.shdrs.items[shdr_index];
     if (shdr.sh_type != elf.SHT_NOBITS) {
-        const file_offset = shdr.sh_offset + sym.value - shdr.sh_addr;
+        const file_offset = shdr.sh_offset + atom_ptr.value;
         try elf_file.base.file.?.pwriteAll(code, file_offset);
     }
 }
@@ -1022,12 +1009,14 @@ fn updateTlv(
     const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
     const atom_ptr = sym.atom(elf_file).?;
 
+    sym.value = 0;
     sym.output_section_index = shndx;
     atom_ptr.output_section_index = shndx;
 
     sym.name_offset = try self.strtab.insert(gpa, decl_name);
     atom_ptr.flags.alive = true;
     atom_ptr.name_offset = sym.name_offset;
+    esym.st_value = 0;
     esym.st_name = sym.name_offset;
     esym.st_info = elf.STT_TLS;
     esym.st_size = code.len;
@@ -1117,7 +1106,7 @@ pub fn updateFunc(
         try self.dwarf.?.commitDeclState(
             mod,
             decl_index,
-            sym.value,
+            sym.address(.{}, elf_file),
             sym.atom(elf_file).?.size,
             ds,
         );
@@ -1202,7 +1191,7 @@ pub fn updateDecl(
         try self.dwarf.?.commitDeclState(
             mod,
             decl_index,
-            sym.value,
+            sym.address(.{}, elf_file),
             sym.atom(elf_file).?.size,
             ds,
         );
@@ -1281,9 +1270,9 @@ fn updateLazySymbol(
     try atom_ptr.allocate(elf_file);
     errdefer self.freeDeclMetadata(elf_file, symbol_index);
 
-    local_sym.value = atom_ptr.value;
+    local_sym.value = 0;
     local_sym.flags.needs_zig_got = true;
-    local_esym.st_value = atom_ptr.value;
+    local_esym.st_value = 0;
 
     if (!elf_file.base.isRelocatable()) {
         const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file);
@@ -1291,7 +1280,7 @@ fn updateLazySymbol(
     }
 
     const shdr = elf_file.shdrs.items[output_section_index];
-    const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
+    const file_offset = shdr.sh_offset + atom_ptr.value;
     try elf_file.base.file.?.pwriteAll(code, file_offset);
 }
 
@@ -1384,11 +1373,11 @@ fn lowerConst(
     // TODO rename and re-audit this method
     errdefer self.freeDeclMetadata(elf_file, sym_index);
 
-    local_sym.value = atom_ptr.value;
-    local_esym.st_value = atom_ptr.value;
+    local_sym.value = 0;
+    local_esym.st_value = 0;
 
     const shdr = elf_file.shdrs.items[output_section_index];
-    const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
+    const file_offset = shdr.sh_offset + atom_ptr.value;
     try elf_file.base.file.?.pwriteAll(code, file_offset);
 
     return .{ .ok = sym_index };
src/link/Elf.zig
@@ -1332,7 +1332,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
     try self.sortPhdrs();
     try self.allocateNonAllocSections();
     self.allocateSpecialPhdrs();
-    self.allocateAtoms();
     self.allocateLinkerDefinedSymbols();
 
     // Dump the state for easy debugging.
@@ -1352,7 +1351,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
             if (shdr.sh_type == elf.SHT_NOBITS) continue;
             const code = try zig_object.codeAlloc(self, atom_index);
             defer gpa.free(code);
-            const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr;
+            const file_offset = shdr.sh_offset + atom_ptr.value;
             atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {
                 // TODO
                 error.RelaxFail, error.InvalidInstruction, error.CannotEncode => {
@@ -2907,7 +2906,7 @@ pub fn writeElfHeader(self: *Elf) !void {
     mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
     index += 4;
 
-    const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0;
+    const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).address(.{}, self) else 0;
     const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0;
     switch (self.ptr_width) {
         .p32 => {
@@ -4402,15 +4401,6 @@ fn allocateSpecialPhdrs(self: *Elf) void {
     }
 }
 
-pub fn allocateAtoms(self: *Elf) void {
-    if (self.zigObjectPtr()) |zig_object| {
-        zig_object.allocateTlvAtoms(self);
-    }
-    for (self.objects.items) |index| {
-        self.file(index).?.object.allocateAtoms(self);
-    }
-}
-
 fn writeAtoms(self: *Elf) !void {
     const gpa = self.base.comp.gpa;
 
@@ -4464,7 +4454,7 @@ fn writeAtoms(self: *Elf) !void {
             const atom_ptr = self.atom(atom_index).?;
             assert(atom_ptr.flags.alive);
 
-            const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
+            const offset = math.cast(usize, atom_ptr.value - base_offset) orelse
                 return error.Overflow;
             const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;