| ... | @@ -140,13 +140,18 @@ const ElfDynLibError = error{ | ... | @@ -140,13 +140,18 @@ const ElfDynLibError = error{ |
| 140 | pub const ElfDynLib = struct { | 140 | pub const ElfDynLib = struct { |
| 141 | strings: [*:0]u8, | 141 | strings: [*:0]u8, |
| 142 | syms: [*]elf.Sym, | 142 | syms: [*]elf.Sym, |
| 143 | hashtab: [*]posix.Elf_Symndx, | 143 | hash_table: HashTable, |
| 144 | versym: ?[*]elf.Versym, | 144 | versym: ?[*]elf.Versym, |
| 145 | verdef: ?*elf.Verdef, | 145 | verdef: ?*elf.Verdef, |
| 146 | memory: []align(std.heap.page_size_min) u8, | 146 | memory: []align(std.heap.page_size_min) u8, |
| 147 | | 147 | |
| 148 | pub const Error = ElfDynLibError; | 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 | fn openPath(path: []const u8) !std.fs.Dir { | 155 | fn openPath(path: []const u8) !std.fs.Dir { |
| 151 | if (path.len == 0) return error.NotDir; | 156 | if (path.len == 0) return error.NotDir; |
| 152 | var parts = std.mem.tokenizeScalar(u8, path, '/'); | 157 | var parts = std.mem.tokenizeScalar(u8, path, '/'); |
| ... | @@ -321,6 +326,7 @@ pub const ElfDynLib = struct { | ... | @@ -321,6 +326,7 @@ pub const ElfDynLib = struct { |
| 321 | var maybe_strings: ?[*:0]u8 = null; | 326 | var maybe_strings: ?[*:0]u8 = null; |
| 322 | var maybe_syms: ?[*]elf.Sym = null; | 327 | var maybe_syms: ?[*]elf.Sym = null; |
| 323 | var maybe_hashtab: ?[*]posix.Elf_Symndx = null; | 328 | var maybe_hashtab: ?[*]posix.Elf_Symndx = null; |
| | 329 | var maybe_gnu_hash: ?*elf.gnu_hash.Header = null; |
| 324 | var maybe_versym: ?[*]elf.Versym = null; | 330 | var maybe_versym: ?[*]elf.Versym = null; |
| 325 | var maybe_verdef: ?*elf.Verdef = null; | 331 | var maybe_verdef: ?*elf.Verdef = null; |
| 326 | | 332 | |
| ... | @@ -332,6 +338,7 @@ pub const ElfDynLib = struct { | ... | @@ -332,6 +338,7 @@ pub const ElfDynLib = struct { |
| 332 | elf.DT_STRTAB => maybe_strings = @ptrFromInt(p), | 338 | elf.DT_STRTAB => maybe_strings = @ptrFromInt(p), |
| 333 | elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p), | 339 | elf.DT_SYMTAB => maybe_syms = @ptrFromInt(p), |
| 334 | elf.DT_HASH => maybe_hashtab = @ptrFromInt(p), | 340 | elf.DT_HASH => maybe_hashtab = @ptrFromInt(p), |
| | 341 | elf.DT_GNU_HASH => maybe_gnu_hash = @ptrFromInt(p), |
| 335 | elf.DT_VERSYM => maybe_versym = @ptrFromInt(p), | 342 | elf.DT_VERSYM => maybe_versym = @ptrFromInt(p), |
| 336 | elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p), | 343 | elf.DT_VERDEF => maybe_verdef = @ptrFromInt(p), |
| 337 | else => {}, | 344 | else => {}, |
| ... | @@ -339,11 +346,18 @@ pub const ElfDynLib = struct { | ... | @@ -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 | return .{ | 356 | return .{ |
| 343 | .memory = all_loaded_mem, | 357 | .memory = all_loaded_mem, |
| 344 | .strings = maybe_strings orelse return error.ElfStringSectionNotFound, | 358 | .strings = maybe_strings orelse return error.ElfStringSectionNotFound, |
| 345 | .syms = maybe_syms orelse return error.ElfSymSectionNotFound, | 359 | .syms = maybe_syms orelse return error.ElfSymSectionNotFound, |
| 346 | .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, | 360 | .hash_table = hash_table, |
| 347 | .versym = maybe_versym, | 361 | .versym = maybe_versym, |
| 348 | .verdef = maybe_verdef, | 362 | .verdef = maybe_verdef, |
| 349 | }; | 363 | }; |
| ... | @@ -368,6 +382,60 @@ pub const ElfDynLib = struct { | ... | @@ -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 | /// ElfDynLib specific | 439 | /// ElfDynLib specific |
| 372 | /// Returns the address of the symbol | 440 | /// Returns the address of the symbol |
| 373 | pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize { | 441 | pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize { |
| ... | @@ -376,17 +444,81 @@ pub const ElfDynLib = struct { | ... | @@ -376,17 +444,81 @@ pub const ElfDynLib = struct { |
| 376 | const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); | 444 | const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); |
| 377 | const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE); | 445 | const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE); |
| 378 | | 446 | |
| 379 | var i: usize = 0; | 447 | switch (self.hash_table) { |
| 380 | while (i < self.hashtab[1]) : (i += 1) { | 448 | .dt_hash => |hashtab| { |
| 381 | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info & 0xf)) & OK_TYPES)) continue; | 449 | var i: usize = 0; |
| 382 | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info >> 4)) & OK_BINDS)) continue; | 450 | while (i < hashtab[1]) : (i += 1) { |
| 383 | if (0 == self.syms[i].st_shndx) continue; | 451 | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info & 0xf)) & OK_TYPES)) continue; |
| 384 | if (!mem.eql(u8, name, mem.sliceTo(self.strings + self.syms[i].st_name, 0))) continue; | 452 | if (0 == (@as(u32, 1) << @as(u5, @intCast(self.syms[i].st_info >> 4)) & OK_BINDS)) continue; |
| 385 | if (maybe_versym) |versym| { | 453 | if (0 == self.syms[i].st_shndx) continue; |
| 386 | if (!checkver(self.verdef.?, versym[i], vername, self.strings)) | 454 | if (!mem.eql(u8, name, mem.sliceTo(self.strings + self.syms[i].st_name, 0))) continue; |
| 387 | continue; | 455 | if (maybe_versym) |versym| { |
| 388 | } | 456 | if (!checkver(self.verdef.?, versym[i], vername, self.strings)) |
| 389 | return @intFromPtr(self.memory.ptr) + self.syms[i].st_value; | 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 | return null; | 524 | return null; |