diff options
| author | Gabriel Schneider <[email protected]> | 2026-06-08 22:36:22 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-06-08 23:05:25 -0300 |
| commit | 34de4c7fa3a740eed14bd58b778728942bf0b005 (patch) | |
| tree | c9bc67c7e3361fa25f94fdc567506fc70cf7545b /src/elfo.zig | |
| parent | d578c82c7fb3a2ac9557926ae6690b4f13534c22 (diff) | |
| download | codenomicon-34de4c7fa3a740eed14bd58b778728942bf0b005.tar.gz codenomicon-34de4c7fa3a740eed14bd58b778728942bf0b005.zip | |
added tests
Diffstat (limited to 'src/elfo.zig')
| -rw-r--r-- | src/elfo.zig | 140 |
1 files changed, 110 insertions, 30 deletions
diff --git a/src/elfo.zig b/src/elfo.zig index 73b666e..c52dc8e 100644 --- a/src/elfo.zig +++ b/src/elfo.zig @@ -80,21 +80,22 @@ pub fn printElf( } else null; defer if (shstrtab_data) |d| gpa.free(d); - // Find .strtab by name (requires shstrtab_data) - const strtab = if (shstrtab_data) |data| - findStrtab(sections.all.items, data) - else - null; - - // Load symbol string table - const strtab_data: ?[]u8 = if (strtab) |s| blk: { + // Load .strtab (for .symtab symbols, resolved via sh_link) + const strtab_data: ?[]u8 = if (sections.strtab) |s| blk: { try reader.seekTo(s.sh_offset); break :blk try reader.interface.readAlloc(gpa, s.sh_size); } else null; defer if (strtab_data) |d| gpa.free(d); - // Collect symbols from symtab and dynsym - var symbols = try collectSymbols(gpa, header, &reader, strtab_data, sections); + // Load .dynstr (for .dynsym symbols, resolved via sh_link) + const dynstr_data: ?[]u8 = if (sections.dynstr) |s| blk: { + try reader.seekTo(s.sh_offset); + break :blk try reader.interface.readAlloc(gpa, s.sh_size); + } else null; + defer if (dynstr_data) |d| gpa.free(d); + + // Collect symbols from symtab and dynsym with their respective string tables + var symbols = try collectSymbols(gpa, header, &reader, strtab_data, dynstr_data, shstrtab_data, sections); defer { for (symbols.items) |sym| gpa.free(sym.name); symbols.deinit(gpa); @@ -216,14 +217,19 @@ fn compareWithObjdump( } else null; defer if (shstrtab_data) |d| gpa.free(d); - const strtab = if (shstrtab_data) |data| findStrtab(sections.all.items, data) else null; - const strtab_data: ?[]u8 = if (strtab) |s| blk: { + const strtab_data: ?[]u8 = if (sections.strtab) |s| blk: { try reader.seekTo(s.sh_offset); break :blk try reader.interface.readAlloc(gpa, s.sh_size); } else null; defer if (strtab_data) |d| gpa.free(d); - var symbols = try collectSymbols(gpa, header, &reader, strtab_data, sections); + const dynstr_data: ?[]u8 = if (sections.dynstr) |s| blk: { + try reader.seekTo(s.sh_offset); + break :blk try reader.interface.readAlloc(gpa, s.sh_size); + } else null; + defer if (dynstr_data) |d| gpa.free(d); + + var symbols = try collectSymbols(gpa, header, &reader, strtab_data, dynstr_data, shstrtab_data, sections); defer { for (symbols.items) |sym| gpa.free(sym.name); symbols.deinit(gpa); @@ -318,18 +324,22 @@ fn compareWithObjdump( // --- Section collection --- -const SectionInfo = struct { +pub const SectionInfo = struct { all: std.ArrayList(std.elf.Elf64_Shdr), shstrtab: ?std.elf.Elf64_Shdr = null, symtab: ?std.elf.Elf64_Shdr = null, dynsym: ?std.elf.Elf64_Shdr = null, + /// String table for .symtab (resolved via sh_link) + strtab: ?std.elf.Elf64_Shdr = null, + /// String table for .dynsym (resolved via sh_link, typically .dynstr) + dynstr: ?std.elf.Elf64_Shdr = null, - fn deinit(self: *SectionInfo, gpa: std.mem.Allocator) void { + pub fn deinit(self: *SectionInfo, gpa: std.mem.Allocator) void { self.all.deinit(gpa); } }; -fn collectSections( +pub fn collectSections( header: std.elf.Header, reader: *std.Io.File.Reader, gpa: std.mem.Allocator, @@ -350,6 +360,16 @@ fn collectSections( else => {}, } } + // Resolve linked string tables via sh_link before sorting changes indices + if (info.symtab) |st| + if (st.sh_link < info.all.items.len) { + info.strtab = info.all.items[st.sh_link]; + }; + if (info.dynsym) |ds| + if (ds.sh_link < info.all.items.len) { + info.dynstr = info.all.items[ds.sh_link]; + }; + std.mem.sort(std.elf.Elf64_Shdr, info.all.items, {}, struct { fn inner(_: void, x: std.elf.Elf64_Shdr, y: std.elf.Elf64_Shdr) bool { return x.sh_addr < y.sh_addr; @@ -358,32 +378,25 @@ fn collectSections( return info; } -fn findStrtab(sections: []const std.elf.Elf64_Shdr, shstrtab_data: []const u8) ?std.elf.Elf64_Shdr { - for (sections) |s| { - if (s.sh_type == std.elf.SHT_STRTAB and - std.mem.eql(u8, ".strtab", std.mem.sliceTo(shstrtab_data[s.sh_name..], 0))) - return s; - } - return null; -} - // --- Symbol collection --- -fn collectSymbols( +pub fn collectSymbols( gpa: std.mem.Allocator, header: std.elf.Header, reader: *std.Io.File.Reader, strtab_data: ?[]const u8, + dynstr_data: ?[]const u8, + shstrtab_data: ?[]const u8, sections: SectionInfo, ) !std.ArrayList(SymbolRange) { var syms: std.ArrayList(SymbolRange) = try .initCapacity(gpa, 8); - if (strtab_data) |data| { + if (strtab_data) |data| if (sections.symtab) |st| try collectSymbolsFrom(gpa, header, reader, st, data, &syms); - // TODO: dynsym should technically use .dynstr, not .strtab + if (dynstr_data) |data| if (sections.dynsym) |ds| try collectSymbolsFrom(gpa, header, reader, ds, data, &syms); - } + try collectPltSymbols(gpa, reader, sections.all.items, shstrtab_data, dynstr_data, header.is_64, header.endian, &syms); std.mem.sort(SymbolRange, syms.items, {}, struct { fn inner(_: void, x: SymbolRange, y: SymbolRange) bool { return x.start < y.start; @@ -392,6 +405,73 @@ fn collectSymbols( return syms; } +/// Create synthetic symbols for PLT entries by parsing .rela.plt relocations. +/// Each .rela.plt entry maps a GOT slot to a dynsym index; the corresponding +/// PLT entry is at plt_base + (1 + i) * plt_entry_size (skipping PLT0). +fn collectPltSymbols( + gpa: std.mem.Allocator, + reader: *std.Io.File.Reader, + sections: []const std.elf.Elf64_Shdr, + shstrtab_data: ?[]const u8, + dynstr_data: ?[]const u8, + is_64: bool, + endian: std.builtin.Endian, + syms: *std.ArrayList(SymbolRange), +) !void { + const strtab = shstrtab_data orelse return; + const dstr = dynstr_data orelse return; + + // Find .plt and .rela.plt by name + var plt_section: ?std.elf.Elf64_Shdr = null; + var rela_plt: ?std.elf.Elf64_Shdr = null; + var dynsym_section: ?std.elf.Elf64_Shdr = null; + for (sections) |s| { + const name = std.mem.sliceTo(strtab[s.sh_name..], 0); + if (std.mem.eql(u8, name, ".plt")) plt_section = s; + if (std.mem.eql(u8, name, ".rela.plt")) rela_plt = s; + if (s.sh_type == std.elf.SHT_DYNSYM) dynsym_section = s; + } + + const plt = plt_section orelse return; + const rela = rela_plt orelse return; + const dsym = dynsym_section orelse return; + + const entry_size: u64 = if (plt.sh_entsize > 0) plt.sh_entsize else 16; + const rela_entry_size: u64 = if (rela.sh_entsize > 0) rela.sh_entsize else @sizeOf(std.elf.Elf64_Rela); + const num_entries = rela.sh_size / rela_entry_size; + const sym_entry_size: u64 = if (is_64) @sizeOf(std.elf.Elf64_Sym) else @sizeOf(std.elf.Elf64_Sym); + + var i: u64 = 0; + while (i < num_entries) : (i += 1) { + // Read rela entry + try reader.seekTo(rela.sh_offset + i * rela_entry_size); + const rela_entry = try reader.interface.takeStruct(std.elf.Elf64_Rela, endian); + + // Extract symbol index from r_info (upper 32 bits on 64-bit ELF) + const sym_idx = rela_entry.r_info >> 32; + if (sym_idx == 0) continue; + + // Read the dynamic symbol to get its name + const sym_offset = dsym.sh_offset + sym_idx * sym_entry_size; + if (sym_offset >= dsym.sh_offset + dsym.sh_size) continue; + try reader.seekTo(sym_offset); + const sym = try reader.interface.takeStruct(std.elf.Elf64_Sym, endian); + + const base_name = std.mem.sliceTo(dstr[sym.st_name..], 0); + if (base_name.len == 0) continue; + + // PLT entry address: skip PLT0, then entry_size per relocation + const plt_addr = plt.sh_addr + (1 + i) * entry_size; + const name = try std.fmt.allocPrint(gpa, "{s}@plt", .{base_name}); + try syms.append(gpa, .{ + .start = plt_addr, + .end = plt_addr + entry_size, + .name = name, + .kind = std.elf.STT_FUNC, + }); + } +} + fn collectSymbolsFrom( gpa: std.mem.Allocator, header: std.elf.Header, @@ -558,7 +638,7 @@ fn allocComment( // --- Types --- -const SymbolRange = struct { +pub const SymbolRange = struct { start: u64, end: u64, name: []u8, |
