summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-06-08 22:36:22 -0300
committerGabriel Schneider <[email protected]>2026-06-08 23:05:25 -0300
commit34de4c7fa3a740eed14bd58b778728942bf0b005 (patch)
treec9bc67c7e3361fa25f94fdc567506fc70cf7545b /src
parentd578c82c7fb3a2ac9557926ae6690b4f13534c22 (diff)
downloadcodenomicon-34de4c7fa3a740eed14bd58b778728942bf0b005.tar.gz
codenomicon-34de4c7fa3a740eed14bd58b778728942bf0b005.zip
added tests
Diffstat (limited to 'src')
-rw-r--r--src/elfo.zig140
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,