Skip to content

Instantly share code, notes, and snippets.

@leroycep
Created November 20, 2024 23:07
Show Gist options
  • Save leroycep/cf4cd7625a9f929104ff3f9b73e58736 to your computer and use it in GitHub Desktop.
Save leroycep/cf4cd7625a9f929104ff3f9b73e58736 to your computer and use it in GitHub Desktop.
Finding symbol name from program address in EXEC type ELF files
const std = @import("std");
pub fn build(b: *std.Build) !void {
const exe = b.addExecutable(.{
.name = "symbol_from_address",
.root_source_file = b.path("./main.zig"),
.target = b.standardTargetOptions(.{}),
.optimize = b.standardOptimizeOption(.{}),
});
exe.root_module.omit_frame_pointer = false;
const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{
.basename = exe.out_filename, // set the name for the debuglink
.strip = .debug,
});
b.getInstallStep().dependOn(&b.addInstallBinFile(
stripped_exe.getOutput(),
exe.out_filename,
).step);
}
const std = @import("std");
const posix = std.posix;
const elf = std.elf;
const builtin = @import("builtin");
const native_endian = builtin.cpu.arch.endian();
pub fn main() !void {
const elf_file = try std.fs.openSelfExe(.{});
defer elf_file.close();
const elf_contents = try mapWholeFile(elf_file);
defer std.posix.munmap(elf_contents);
const hdr: *const elf.Ehdr = @ptrCast(&elf_contents[0]);
if (!std.mem.eql(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
const endian: std.builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) {
elf.ELFDATA2LSB => .little,
elf.ELFDATA2MSB => .big,
else => return error.InvalidElfEndian,
};
if (endian != native_endian) return error.UnimplementedDwarfForeignEndian;
const shoff = hdr.e_shoff;
const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
const str_shdr: *const elf.Shdr = @ptrCast(@alignCast(&elf_contents[std.math.cast(usize, str_section_off) orelse return error.Overflow]));
const header_strings = elf_contents[str_shdr.sh_offset..][0..str_shdr.sh_size];
const section_headers = @as(
[*]const elf.Shdr,
@ptrCast(@alignCast(&elf_contents[shoff])),
)[0..hdr.e_shnum];
const address_looking_for = thisFnAddress();
var symbol_string_table_opt: ?[]const u8 = null;
var address_section_index: ?usize = null;
for (section_headers, 0..) |section_header, i| {
const name_opt = getStringFromTable(header_strings, section_header.sh_name);
if (section_header.sh_type == elf.SHT_STRTAB and name_opt != null and std.mem.eql(u8, name_opt.?, ".strtab")) {
if (symbol_string_table_opt != null) {
@panic("TODO: handling multiple symbol string tables not implemented yet");
}
symbol_string_table_opt = elf_contents[section_header.sh_offset..][0..section_header.sh_size];
}
if (address_looking_for >= section_header.sh_addr and address_looking_for <= section_header.sh_addr + section_header.sh_size) {
// std.debug.print("Found address (0x{x}) in section \"{}\" (0x{x}, {:.0})\n", .{ address_looking_for, std.zig.fmtEscapes(name_opt orelse ""), section_header.sh_addr, std.fmt.fmtIntSizeBin(section_header.sh_size) });
address_section_index = i;
}
}
const symbol_string_table = symbol_string_table_opt orelse {
std.debug.print("no string table found", .{});
return;
};
std.debug.print("symbol\tvalue\tsize\tbind\ttype\n", .{});
for (section_headers) |section_header| {
if (section_header.sh_type != elf.SHT_SYMTAB) {
continue;
}
const symbols = @as([*]const elf.Sym, @ptrCast(@alignCast(&elf_contents[section_header.sh_offset])))[0 .. section_header.sh_size / section_header.sh_entsize];
for (symbols) |symbol| {
if (symbol.st_name == 0) continue;
if (symbol.st_shndx == elf.SHN_UNDEF) continue;
if (address_section_index != null and symbol.st_shndx != address_section_index.?) {
continue;
}
const symbol_name = getStringFromTable(symbol_string_table, symbol.st_name) orelse continue;
if (symbol.st_shndx == elf.SHN_ABS) {
std.debug.print("\"{}\"\t0x{x}\t{}\n", .{ std.zig.fmtEscapes(symbol_name), symbol.st_value, std.fmt.fmtIntSizeBin(symbol.st_size) });
continue;
}
const symbol_section_header = section_headers[symbol.st_shndx];
const symbol_start_address = switch (hdr.e_type) {
.EXEC => symbol.st_value,
.DYN => symbol_section_header.sh_addr + symbol.st_value,
else => std.debug.panic("Unhandled ELF file type: {}", .{hdr.e_type}),
};
const symbol_end_address = symbol_start_address + symbol.st_size;
// std.debug.print("\"{}\"\t0x{x}\t{}\t{}\t{s}\n", .{
// std.zig.fmtEscapes(symbol_name),
// symbol_start_address,
// std.fmt.fmtIntSizeBin(symbol.st_size),
// symbol.st_info >> 4,
// switch (symbol.st_info & 0xf) {
// elf.STT_NOTYPE => "notype",
// elf.STT_OBJECT => "object",
// elf.STT_FUNC => "fn",
// elf.STT_SECTION => "section",
// elf.STT_FILE => "file",
// elf.STT_COMMON => "common",
// elf.STT_TLS => "thread_local_storage",
// else => "other",
// },
// });
if (address_looking_for >= symbol_start_address and address_looking_for <= symbol_end_address) {
std.debug.print("\n\nFound symbol containing address:\n looking for = 0x{x}\n symbol name = \"{}\"\n symbol start = 0x{x}\n symbol end = 0x{x}\n", .{
address_looking_for,
std.zig.fmtEscapes(symbol_name),
symbol_start_address,
symbol_end_address,
});
return;
}
}
}
std.debug.print("looking_for_this\t0x{x}\t\"\"\n", .{address_looking_for});
}
fn getStringFromTable(string_table: []const u8, pos: usize) ?[]const u8 {
const section_name_end = std.mem.indexOfScalarPos(u8, string_table, pos, '\x00') orelse return null;
return string_table[pos..section_name_end];
}
noinline fn thisFnAddress() usize {
return @returnAddress();
}
fn mapWholeFile(file: std.fs.File) ![]align(std.mem.page_size) const u8 {
const file_len = std.math.cast(usize, try file.getEndPos()) orelse std.math.maxInt(usize);
const mapped_mem = try std.posix.mmap(
null,
file_len,
std.posix.PROT.READ,
.{ .TYPE = .SHARED },
file.handle,
0,
);
errdefer std.posix.munmap(mapped_mem);
return mapped_mem;
}
@leroycep
Copy link
Author

In ReleaseFast:

~/code/symbol_name_from_address> zig build -Dtarget=x86_64-linux-none -Doptimize=ReleaseSafe; ./zig-out/bin/symbol_from_address
symbol	value	size	bind	type


Found symbol containing address:
  looking for = 0x100d7e9
  symbol name = "start.posixCallMainAndExit"
  symbol start = 0x100cfa0
  symbol end = 0x100ded7

In Debug:

~/code/symbol_name_from_address> zig build -Dtarget=x86_64-linux-none -Doptimize=Debug; ./zig-out/bin/symbol_from_address
symbol	value	size	bind	type


Found symbol containing address:
  looking for = 0x103c504
  symbol name = "main.main"
  symbol start = 0x103bea0
  symbol end = 0x103cff8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment