| ... | ... | @@ -140,13 +140,18 @@ const ElfDynLibError = error{ |
| 140 | 140 | pub const ElfDynLib = struct { |
| 141 | 141 | strings: [*:0]u8, |
| 142 | 142 | syms: [*]elf.Sym, |
| 143 | | hashtab: [*]posix.Elf_Symndx, |
| 143 | hash_table: HashTable, |
| 144 | 144 | versym: ?[*]elf.Versym, |
| 145 | 145 | verdef: ?*elf.Verdef, |
| 146 | 146 | memory: []align(std.heap.page_size_min) u8, |
| 147 | 147 | |
| 148 | 148 | pub const Error = ElfDynLibError; |
| 149 | 149 | |
| 150 | const HashTable = union(enum) { |
| 151 | dt_hash: [*]posix.Elf_Symndx, |
| 152 | dt_gnu_hash: *elf.gnu_hash.Header, |
| 153 | }; |
| 154 | |
| 150 | 155 | fn openPath(path: []const u8) !std.fs.Dir { |
| 151 | 156 | if (path.len == 0) return error.NotDir; |
| 152 | 157 | var parts = std.mem.tokenizeScalar(u8, path, '/'); |
| ... | ... | @@ -321,6 +326,7 @@ pub const ElfDynLib = struct { |
| 321 | 326 | var maybe_strings: ?[*:0]u8 = null; |
| 322 | 327 | var maybe_syms: ?[*]elf.Sym = null; |
| 323 | 328 | var maybe_hashtab: ?[*]posix.Elf_Symndx = null; |
| 329 | var maybe_gnu_hash: ?*elf.gnu_hash.Header = null; |
| 324 | 330 | var maybe_versym: ?[*]elf.Versym = null; |
| 325 | 331 | var maybe_verdef: ?*elf.Verdef = null; |
| 326 | 332 | |
| ... | ... | @@ -332,6 +338,7 @@ pub const ElfDynLib = struct { |
| 332 | 338 | elf.DT_STRTAB => maybe_strings = @ptrFromInt(p), |
| 333 | 339 | elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p), |
| 334 | 340 | elf.DT_HASH => maybe_hashtab = @ptrFromInt(p), |
| 341 | elf.DT_GNU_HASH => maybe_gnu_hash = @ptrFromInt(p), |
| 335 | 342 | elf.DT_VERSYM => maybe_versym = @ptrFromInt(p), |
| 336 | 343 | elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p), |
| 337 | 344 | else => {}, |
| ... | ... | @@ -339,11 +346,18 @@ pub const ElfDynLib = struct { |
| 339 | 346 | } |
| 340 | 347 | } |
| 341 | 348 | |
| 349 | const hash_table: HashTable = if (maybe_gnu_hash) |gnu_hash| |
| 350 | .{ .dt_gnu_hash = gnu_hash } |
| 351 | else if (maybe_hashtab) |hashtab| |
| 352 | .{ .dt_hash = hashtab } |
| 353 | else |
| 354 | return error.ElfHashTableNotFound; |
| 355 | |
| 342 | 356 | return .{ |
| 343 | 357 | .memory = all_loaded_mem, |
| 344 | 358 | .strings = maybe_strings orelse return error.ElfStringSectionNotFound, |
| 345 | 359 | .syms = maybe_syms orelse return error.ElfSymSectionNotFound, |
| 346 | | .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, |
| 360 | .hash_table = hash_table, |
| 347 | 361 | .versym = maybe_versym, |
| 348 | 362 | .verdef = maybe_verdef, |
| 349 | 363 | }; |
| ... | ... | @@ -368,6 +382,60 @@ pub const ElfDynLib = struct { |
| 368 | 382 | } |
| 369 | 383 | } |
| 370 | 384 | |
| 385 | pub const GnuHashSection32 = struct { |
| 386 | symoffset: u32, |
| 387 | bloom_shift: u32, |
| 388 | bloom: []u32, |
| 389 | buckets: []u32, |
| 390 | chain: [*]elf.gnu_hash.ChainEntry, |
| 391 | |
| 392 | pub fn fromPtr(header: *elf.gnu_hash.Header) @This() { |
| 393 | const header_offset = @intFromPtr(header); |
| 394 | const bloom_offset = header_offset + @sizeOf(elf.gnu_hash.Header); |
| 395 | const buckets_offset = bloom_offset + header.bloom_size * @sizeOf(u32); |
| 396 | const chain_offset = buckets_offset + header.nbuckets * @sizeOf(u32); |
| 397 | |
| 398 | const bloom_ptr: [*]u32 = @ptrFromInt(bloom_offset); |
| 399 | const buckets_ptr: [*]u32 = @ptrFromInt(buckets_offset); |
| 400 | const chain_ptr: [*]u32 = @ptrFromInt(chain_offset); |
| 401 | |
| 402 | return .{ |
| 403 | .symoffset = header.symoffset, |
| 404 | .bloom_shift = header.bloom_shift, |
| 405 | .bloom = bloom_ptr[0..header.bloom_size], |
| 406 | .buckets = buckets_ptr[0..header.nbuckets], |
| 407 | .chain = chain_ptr, |
| 408 | }; |
| 409 | } |
| 410 | }; |
| 411 | |
| 412 | pub const GnuHashSection64 = struct { |
| 413 | symoffset: u32, |
| 414 | bloom_shift: u32, |
| 415 | bloom: []u64, |
| 416 | buckets: []u32, |
| 417 | chain: [*]elf.gnu_hash.ChainEntry, |
| 418 | |
| 419 | pub fn fromPtr(header: *elf.gnu_hash.Header) @This() { |
| 420 | const header_offset = @intFromPtr(header); |
| 421 | const bloom_offset = header_offset + @sizeOf(elf.gnu_hash.Header); |
| 422 | const buckets_offset = bloom_offset + header.bloom_size * @sizeOf(u64); |
| 423 | const chain_offset = buckets_offset + header.nbuckets * @sizeOf(u32); |
| 424 | |
| 425 | const bloom_ptr: [*]u64 = @ptrFromInt(bloom_offset); |
| 426 | const buckets_ptr: [*]u32 = @ptrFromInt(buckets_offset); |
| 427 | const chain_ptr: [*]elf.gnu_hash.ChainEntry = @ptrFromInt(chain_offset); |
| 428 | |
| 429 | return .{ |
| 430 | .symoffset = header.symoffset, |
| 431 | .bloom_shift = header.bloom_shift, |
| 432 | .bloom = bloom_ptr[0..header.bloom_size], |
| 433 | .buckets = buckets_ptr[0..header.nbuckets], |
| 434 | .chain = chain_ptr, |
| 435 | }; |
| 436 | } |
| 437 | }; |
| 438 | |
| 371 | 439 | /// ElfDynLib specific |
| 372 | 440 | /// Returns the address of the symbol |
| 373 | 441 | pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize { |
| ... | ... | @@ -376,17 +444,81 @@ pub const ElfDynLib = struct { |
| 376 | 444 | const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); |
| 377 | 445 | const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE); |
| 378 | 446 | |
| 379 | | var i: usize = 0; |
| 380 | | while (i < self.hashtab[1]) : (i += 1) { |
| 381 | | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info & 0xf)) & OK_TYPES)) continue; |
| 382 | | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info >> 4)) & OK_BINDS)) continue; |
| 383 | | if (0 == self.syms[i].st_shndx) continue; |
| 384 | | if (!mem.eql(u8, name, mem.sliceTo(self.strings + self.syms[i].st_name, 0))) continue; |
| 385 | | if (maybe_versym) |versym| { |
| 386 | | if (!checkver(self.verdef.?, versym[i], vername, self.strings)) |
| 387 | | continue; |
| 388 | | } |
| 389 | | return @intFromPtr(self.memory.ptr) + self.syms[i].st_value; |
| 447 | switch (self.hash_table) { |
| 448 | .dt_hash => |hashtab| { |
| 449 | var i: usize = 0; |
| 450 | while (i < hashtab[1]) : (i += 1) { |
| 451 | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info & 0xf)) & OK_TYPES)) continue; |
| 452 | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info >> 4)) & OK_BINDS)) continue; |
| 453 | if (0 == self.syms[i].st_shndx) continue; |
| 454 | if (!mem.eql(u8, name, mem.sliceTo(self.strings + self.syms[i].st_name, 0))) continue; |
| 455 | if (maybe_versym) |versym| { |
| 456 | if (!checkver(self.verdef.?, versym[i], vername, self.strings)) |
| 457 | continue; |
| 458 | } |
| 459 | return @intFromPtr(self.memory.ptr) + self.syms[i].st_value; |
| 460 | } |
| 461 | }, |
| 462 | .dt_gnu_hash => |gnu_hash_header| { |
| 463 | const GnuHashSection = switch (@bitSizeOf(usize)) { |
| 464 | 32 => GnuHashSection32, |
| 465 | 64 => GnuHashSection64, |
| 466 | else => |bit_size| @compileError("Unsupported bit size " ++ bit_size), |
| 467 | }; |
| 468 | |
| 469 | const gnu_hash_section: GnuHashSection = .fromPtr(gnu_hash_header); |
| 470 | const hash = elf.gnu_hash.calculate(name); |
| 471 | |
| 472 | const bloom_index = (hash / @bitSizeOf(usize)) % gnu_hash_header.bloom_size; |
| 473 | const bloom_val = gnu_hash_section.bloom[bloom_index]; |
| 474 | |
| 475 | const bit_index_0 = hash % @bitSizeOf(usize); |
| 476 | const bit_index_1 = (hash >> @intCast(gnu_hash_header.bloom_shift)) % @bitSizeOf(usize); |
| 477 | |
| 478 | const one: usize = 1; |
| 479 | const bit_mask: usize = (one << @intCast(bit_index_0)) | (one << @intCast(bit_index_1)); |
| 480 | |
| 481 | if (bloom_val & bit_mask != bit_mask) { |
| 482 | // Symbol is not in bloom filter, so it definitely isn't here. |
| 483 | return null; |
| 484 | } |
| 485 | |
| 486 | const bucket_index = hash % gnu_hash_header.nbuckets; |
| 487 | const chain_index = gnu_hash_section.buckets[bucket_index] - gnu_hash_header.symoffset; |
| 488 | |
| 489 | const chains = gnu_hash_section.chain; |
| 490 | const hash_as_entry: elf.gnu_hash.ChainEntry = @bitCast(hash); |
| 491 | |
| 492 | var current_index = chain_index; |
| 493 | var at_end_of_chain = false; |
| 494 | while (!at_end_of_chain) : (current_index += 1) { |
| 495 | const current_entry = chains[current_index]; |
| 496 | at_end_of_chain = current_entry.end_of_chain; |
| 497 | |
| 498 | if (current_entry.hash != hash_as_entry.hash) continue; |
| 499 | |
| 500 | // check that symbol matches |
| 501 | const symbol_index = current_index + gnu_hash_header.symoffset; |
| 502 | const symbol = self.syms[symbol_index]; |
| 503 | |
| 504 | if (0 == (@as(u32, 1) << @as(u5, @intCast(symbol.st_info & 0xf)) & OK_TYPES)) continue; |
| 505 | if (0 == (@as(u32, 1) << @as(u5, @intCast(symbol.st_info >> 4)) & OK_BINDS)) continue; |
| 506 | if (0 == symbol.st_shndx) continue; |
| 507 | |
| 508 | const symbol_name = mem.sliceTo(self.strings + symbol.st_name, 0); |
| 509 | if (!mem.eql(u8, name, symbol_name)) { |
| 510 | continue; |
| 511 | } |
| 512 | |
| 513 | if (maybe_versym) |versym| { |
| 514 | if (!checkver(self.verdef.?, versym[symbol_index], vername, self.strings)) { |
| 515 | continue; |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | return @intFromPtr(self.memory.ptr) + symbol.st_value; |
| 520 | } |
| 521 | }, |
| 390 | 522 | } |
| 391 | 523 | |
| 392 | 524 | return null; |