mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
std.DynLib: add support for DT_GNU_HASH sections in elf files
This commit is contained in:
parent
b7512c3e5d
commit
6ef2384c07
2 changed files with 182 additions and 13 deletions
|
|
@ -140,13 +140,18 @@ const ElfDynLibError = error{
|
||||||
pub const ElfDynLib = struct {
|
pub const ElfDynLib = struct {
|
||||||
strings: [*:0]u8,
|
strings: [*:0]u8,
|
||||||
syms: [*]elf.Sym,
|
syms: [*]elf.Sym,
|
||||||
hashtab: [*]posix.Elf_Symndx,
|
hash_table: HashTable,
|
||||||
versym: ?[*]elf.Versym,
|
versym: ?[*]elf.Versym,
|
||||||
verdef: ?*elf.Verdef,
|
verdef: ?*elf.Verdef,
|
||||||
memory: []align(std.heap.page_size_min) u8,
|
memory: []align(std.heap.page_size_min) u8,
|
||||||
|
|
||||||
pub const Error = ElfDynLibError;
|
pub const Error = ElfDynLibError;
|
||||||
|
|
||||||
|
const HashTable = union(enum) {
|
||||||
|
dt_hash: [*]posix.Elf_Symndx,
|
||||||
|
dt_gnu_hash: *elf.gnu_hash.Header,
|
||||||
|
};
|
||||||
|
|
||||||
fn openPath(path: []const u8) !std.fs.Dir {
|
fn openPath(path: []const u8) !std.fs.Dir {
|
||||||
if (path.len == 0) return error.NotDir;
|
if (path.len == 0) return error.NotDir;
|
||||||
var parts = std.mem.tokenizeScalar(u8, path, '/');
|
var parts = std.mem.tokenizeScalar(u8, path, '/');
|
||||||
|
|
@ -321,6 +326,7 @@ pub const ElfDynLib = struct {
|
||||||
var maybe_strings: ?[*:0]u8 = null;
|
var maybe_strings: ?[*:0]u8 = null;
|
||||||
var maybe_syms: ?[*]elf.Sym = null;
|
var maybe_syms: ?[*]elf.Sym = null;
|
||||||
var maybe_hashtab: ?[*]posix.Elf_Symndx = null;
|
var maybe_hashtab: ?[*]posix.Elf_Symndx = null;
|
||||||
|
var maybe_gnu_hash: ?*elf.gnu_hash.Header = null;
|
||||||
var maybe_versym: ?[*]elf.Versym = null;
|
var maybe_versym: ?[*]elf.Versym = null;
|
||||||
var maybe_verdef: ?*elf.Verdef = null;
|
var maybe_verdef: ?*elf.Verdef = null;
|
||||||
|
|
||||||
|
|
@ -332,6 +338,7 @@ pub const ElfDynLib = struct {
|
||||||
elf.DT_STRTAB => maybe_strings = @ptrFromInt(p),
|
elf.DT_STRTAB => maybe_strings = @ptrFromInt(p),
|
||||||
elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p),
|
elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p),
|
||||||
elf.DT_HASH => maybe_hashtab = @ptrFromInt(p),
|
elf.DT_HASH => maybe_hashtab = @ptrFromInt(p),
|
||||||
|
elf.DT_GNU_HASH => maybe_gnu_hash = @ptrFromInt(p),
|
||||||
elf.DT_VERSYM => maybe_versym = @ptrFromInt(p),
|
elf.DT_VERSYM => maybe_versym = @ptrFromInt(p),
|
||||||
elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p),
|
elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p),
|
||||||
else => {},
|
else => {},
|
||||||
|
|
@ -339,11 +346,18 @@ pub const ElfDynLib = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hash_table: HashTable = if (maybe_gnu_hash) |gnu_hash|
|
||||||
|
.{ .dt_gnu_hash = gnu_hash }
|
||||||
|
else if (maybe_hashtab) |hashtab|
|
||||||
|
.{ .dt_hash = hashtab }
|
||||||
|
else
|
||||||
|
return error.ElfHashTableNotFound;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.memory = all_loaded_mem,
|
.memory = all_loaded_mem,
|
||||||
.strings = maybe_strings orelse return error.ElfStringSectionNotFound,
|
.strings = maybe_strings orelse return error.ElfStringSectionNotFound,
|
||||||
.syms = maybe_syms orelse return error.ElfSymSectionNotFound,
|
.syms = maybe_syms orelse return error.ElfSymSectionNotFound,
|
||||||
.hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound,
|
.hash_table = hash_table,
|
||||||
.versym = maybe_versym,
|
.versym = maybe_versym,
|
||||||
.verdef = maybe_verdef,
|
.verdef = maybe_verdef,
|
||||||
};
|
};
|
||||||
|
|
@ -368,6 +382,60 @@ pub const ElfDynLib = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const GnuHashSection32 = struct {
|
||||||
|
symoffset: u32,
|
||||||
|
bloom_shift: u32,
|
||||||
|
bloom: []u32,
|
||||||
|
buckets: []u32,
|
||||||
|
chain: [*]elf.gnu_hash.ChainEntry,
|
||||||
|
|
||||||
|
pub fn fromPtr(header: *elf.gnu_hash.Header) @This() {
|
||||||
|
const header_offset = @intFromPtr(header);
|
||||||
|
const bloom_offset = header_offset + @sizeOf(elf.gnu_hash.Header);
|
||||||
|
const buckets_offset = bloom_offset + header.bloom_size * @sizeOf(u32);
|
||||||
|
const chain_offset = buckets_offset + header.nbuckets * @sizeOf(u32);
|
||||||
|
|
||||||
|
const bloom_ptr: [*]u32 = @ptrFromInt(bloom_offset);
|
||||||
|
const buckets_ptr: [*]u32 = @ptrFromInt(buckets_offset);
|
||||||
|
const chain_ptr: [*]u32 = @ptrFromInt(chain_offset);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.symoffset = header.symoffset,
|
||||||
|
.bloom_shift = header.bloom_shift,
|
||||||
|
.bloom = bloom_ptr[0..header.bloom_size],
|
||||||
|
.buckets = buckets_ptr[0..header.nbuckets],
|
||||||
|
.chain = chain_ptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GnuHashSection64 = struct {
|
||||||
|
symoffset: u32,
|
||||||
|
bloom_shift: u32,
|
||||||
|
bloom: []u64,
|
||||||
|
buckets: []u32,
|
||||||
|
chain: [*]elf.gnu_hash.ChainEntry,
|
||||||
|
|
||||||
|
pub fn fromPtr(header: *elf.gnu_hash.Header) @This() {
|
||||||
|
const header_offset = @intFromPtr(header);
|
||||||
|
const bloom_offset = header_offset + @sizeOf(elf.gnu_hash.Header);
|
||||||
|
const buckets_offset = bloom_offset + header.bloom_size * @sizeOf(u64);
|
||||||
|
const chain_offset = buckets_offset + header.nbuckets * @sizeOf(u32);
|
||||||
|
|
||||||
|
const bloom_ptr: [*]u64 = @ptrFromInt(bloom_offset);
|
||||||
|
const buckets_ptr: [*]u32 = @ptrFromInt(buckets_offset);
|
||||||
|
const chain_ptr: [*]elf.gnu_hash.ChainEntry = @ptrFromInt(chain_offset);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.symoffset = header.symoffset,
|
||||||
|
.bloom_shift = header.bloom_shift,
|
||||||
|
.bloom = bloom_ptr[0..header.bloom_size],
|
||||||
|
.buckets = buckets_ptr[0..header.nbuckets],
|
||||||
|
.chain = chain_ptr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// ElfDynLib specific
|
/// ElfDynLib specific
|
||||||
/// Returns the address of the symbol
|
/// Returns the address of the symbol
|
||||||
pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize {
|
pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize {
|
||||||
|
|
@ -376,17 +444,81 @@ pub const ElfDynLib = struct {
|
||||||
const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
|
const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
|
||||||
const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE);
|
const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE);
|
||||||
|
|
||||||
var i: usize = 0;
|
switch (self.hash_table) {
|
||||||
while (i < self.hashtab[1]) : (i += 1) {
|
.dt_hash => |hashtab| {
|
||||||
if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info & 0xf)) & OK_TYPES)) continue;
|
var i: usize = 0;
|
||||||
if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info >> 4)) & OK_BINDS)) continue;
|
while (i < hashtab[1]) : (i += 1) {
|
||||||
if (0 == self.syms[i].st_shndx) continue;
|
if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info & 0xf)) & OK_TYPES)) continue;
|
||||||
if (!mem.eql(u8, name, mem.sliceTo(self.strings + self.syms[i].st_name, 0))) continue;
|
if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info >> 4)) & OK_BINDS)) continue;
|
||||||
if (maybe_versym) |versym| {
|
if (0 == self.syms[i].st_shndx) continue;
|
||||||
if (!checkver(self.verdef.?, versym[i], vername, self.strings))
|
if (!mem.eql(u8, name, mem.sliceTo(self.strings + self.syms[i].st_name, 0))) continue;
|
||||||
continue;
|
if (maybe_versym) |versym| {
|
||||||
}
|
if (!checkver(self.verdef.?, versym[i], vername, self.strings))
|
||||||
return @intFromPtr(self.memory.ptr) + self.syms[i].st_value;
|
continue;
|
||||||
|
}
|
||||||
|
return @intFromPtr(self.memory.ptr) + self.syms[i].st_value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.dt_gnu_hash => |gnu_hash_header| {
|
||||||
|
const GnuHashSection = switch (@bitSizeOf(usize)) {
|
||||||
|
32 => GnuHashSection32,
|
||||||
|
64 => GnuHashSection64,
|
||||||
|
else => |bit_size| @compileError("Unsupported bit size " ++ bit_size),
|
||||||
|
};
|
||||||
|
|
||||||
|
const gnu_hash_section: GnuHashSection = .fromPtr(gnu_hash_header);
|
||||||
|
const hash = elf.gnu_hash.calculate(name);
|
||||||
|
|
||||||
|
const bloom_index = (hash / @bitSizeOf(usize)) % gnu_hash_header.bloom_size;
|
||||||
|
const bloom_val = gnu_hash_section.bloom[bloom_index];
|
||||||
|
|
||||||
|
const bit_index_0 = hash % @bitSizeOf(usize);
|
||||||
|
const bit_index_1 = (hash >> @intCast(gnu_hash_header.bloom_shift)) % @bitSizeOf(usize);
|
||||||
|
|
||||||
|
const one: usize = 1;
|
||||||
|
const bit_mask: usize = (one << @intCast(bit_index_0)) | (one << @intCast(bit_index_1));
|
||||||
|
|
||||||
|
if (bloom_val & bit_mask != bit_mask) {
|
||||||
|
// Symbol is not in bloom filter, so it definitely isn't here.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bucket_index = hash % gnu_hash_header.nbuckets;
|
||||||
|
const chain_index = gnu_hash_section.buckets[bucket_index] - gnu_hash_header.symoffset;
|
||||||
|
|
||||||
|
const chains = gnu_hash_section.chain;
|
||||||
|
const hash_as_entry: elf.gnu_hash.ChainEntry = @bitCast(hash);
|
||||||
|
|
||||||
|
var current_index = chain_index;
|
||||||
|
var at_end_of_chain = false;
|
||||||
|
while (!at_end_of_chain) : (current_index += 1) {
|
||||||
|
const current_entry = chains[current_index];
|
||||||
|
at_end_of_chain = current_entry.end_of_chain;
|
||||||
|
|
||||||
|
if (current_entry.hash != hash_as_entry.hash) continue;
|
||||||
|
|
||||||
|
// check that symbol matches
|
||||||
|
const symbol_index = current_index + gnu_hash_header.symoffset;
|
||||||
|
const symbol = self.syms[symbol_index];
|
||||||
|
|
||||||
|
if (0 == (@as(u32, 1) << @as(u5, @intCast(symbol.st_info & 0xf)) & OK_TYPES)) continue;
|
||||||
|
if (0 == (@as(u32, 1) << @as(u5, @intCast(symbol.st_info >> 4)) & OK_BINDS)) continue;
|
||||||
|
if (0 == symbol.st_shndx) continue;
|
||||||
|
|
||||||
|
const symbol_name = mem.sliceTo(self.strings + symbol.st_name, 0);
|
||||||
|
if (!mem.eql(u8, name, symbol_name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maybe_versym) |versym| {
|
||||||
|
if (!checkver(self.verdef.?, versym[symbol_index], vername, self.strings)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return @intFromPtr(self.memory.ptr) + symbol.st_value;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -2395,3 +2395,40 @@ pub const STRNAME = genSpecialMemberName("//");
|
||||||
pub const SYM64NAME = genSpecialMemberName("/SYM64/");
|
pub const SYM64NAME = genSpecialMemberName("/SYM64/");
|
||||||
pub const SYMDEFNAME = genSpecialMemberName("__.SYMDEF");
|
pub const SYMDEFNAME = genSpecialMemberName("__.SYMDEF");
|
||||||
pub const SYMDEFSORTEDNAME = genSpecialMemberName("__.SYMDEF SORTED");
|
pub const SYMDEFSORTEDNAME = genSpecialMemberName("__.SYMDEF SORTED");
|
||||||
|
|
||||||
|
pub const gnu_hash = struct {
|
||||||
|
|
||||||
|
// See https://flapenguin.me/elf-dt-gnu-hash
|
||||||
|
|
||||||
|
pub const Header = extern struct {
|
||||||
|
nbuckets: u32,
|
||||||
|
symoffset: u32,
|
||||||
|
bloom_size: u32,
|
||||||
|
bloom_shift: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ChainEntry = packed struct(u32) {
|
||||||
|
end_of_chain: bool,
|
||||||
|
/// Contains the top bits of the hash value.
|
||||||
|
hash: u31,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Calculate the hash value for a name
|
||||||
|
pub fn calculate(name: []const u8) u32 {
|
||||||
|
var hash: u32 = 5381;
|
||||||
|
|
||||||
|
for (name) |char| {
|
||||||
|
hash = (hash << 5) +% hash +% char;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
test calculate {
|
||||||
|
try std.testing.expectEqual(0x00001505, calculate(""));
|
||||||
|
try std.testing.expectEqual(0x156b2bb8, calculate("printf"));
|
||||||
|
try std.testing.expectEqual(0x7c967e3f, calculate("exit"));
|
||||||
|
try std.testing.expectEqual(0xbac212a0, calculate("syscall"));
|
||||||
|
try std.testing.expectEqual(0x8ae9f18e, calculate("flapenguin.me"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue