| 1 | const SharedObject = @This(); |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const Io = std.Io; |
| 5 | const assert = std.debug.assert; |
| 6 | const elf = std.elf; |
| 7 | const log = std.log.scoped(.elf); |
| 8 | const mem = std.mem; |
| 9 | const Path = std.Build.Cache.Path; |
| 10 | const Stat = std.Build.Cache.File.Stat; |
| 11 | const Allocator = mem.Allocator; |
| 12 | |
| 13 | const Elf = @import("../Elf.zig"); |
| 14 | const File = @import("file.zig").File; |
| 15 | const Symbol = @import("Symbol.zig"); |
| 16 | const Diags = @import("../../link.zig").Diags; |
| 17 | |
| 18 | path: Path, |
| 19 | index: File.Index, |
| 20 | |
| 21 | parsed: Parsed, |
| 22 | |
| 23 | symbols: std.ArrayList(Symbol), |
| 24 | symbols_extra: std.ArrayList(u32), |
| 25 | symbols_resolver: std.ArrayList(Elf.SymbolResolver.Index), |
| 26 | |
| 27 | aliases: ?std.ArrayList(u32), |
| 28 | |
| 29 | needed: bool, |
| 30 | alive: bool, |
| 31 | |
| 32 | output_symtab_ctx: Elf.SymtabCtx, |
| 33 | |
| 34 | pub fn deinit(so: *SharedObject, gpa: Allocator) void { |
| 35 | gpa.free(so.path.sub_path); |
| 36 | so.parsed.deinit(gpa); |
| 37 | so.symbols.deinit(gpa); |
| 38 | so.symbols_extra.deinit(gpa); |
| 39 | so.symbols_resolver.deinit(gpa); |
| 40 | if (so.aliases) |*aliases| aliases.deinit(gpa); |
| 41 | so.* = undefined; |
| 42 | } |
| 43 | |
| 44 | pub const Header = struct { |
| 45 | dynamic_table: []const elf.Elf64_Dyn, |
| 46 | soname_index: ?u32, |
| 47 | verdefnum: ?u32, |
| 48 | |
| 49 | sections: []const elf.Elf64_Shdr, |
| 50 | dynsym_sect_index: ?u32, |
| 51 | versym_sect_index: ?u32, |
| 52 | verdef_sect_index: ?u32, |
| 53 | |
| 54 | stat: Stat, |
| 55 | strtab: std.ArrayList(u8), |
| 56 | |
| 57 | pub fn deinit(header: *Header, gpa: Allocator) void { |
| 58 | gpa.free(header.sections); |
| 59 | gpa.free(header.dynamic_table); |
| 60 | header.strtab.deinit(gpa); |
| 61 | header.* = undefined; |
| 62 | } |
| 63 | |
| 64 | pub fn soname(header: Header) ?[]const u8 { |
| 65 | const i = header.soname_index orelse return null; |
| 66 | return Elf.stringTableLookup(header.strtab.items, i); |
| 67 | } |
| 68 | }; |
| 69 | |
| 70 | pub const Parsed = struct { |
| 71 | stat: Stat, |
| 72 | strtab: []const u8, |
| 73 | soname_index: ?u32, |
| 74 | sections: []const elf.Elf64_Shdr, |
| 75 | |
| 76 | /// Nonlocal symbols only. |
| 77 | symtab: []const elf.Elf64_Sym, |
| 78 | /// Version symtab contains version strings of the symbols if present. |
| 79 | /// Nonlocal symbols only. |
| 80 | versyms: []const elf.Versym, |
| 81 | /// Nonlocal symbols only. |
| 82 | symbols: []const Parsed.Symbol, |
| 83 | |
| 84 | verstrings: []const u32, |
| 85 | |
| 86 | const Symbol = struct { |
| 87 | mangled_name: u32, |
| 88 | }; |
| 89 | |
| 90 | pub fn deinit(p: *Parsed, gpa: Allocator) void { |
| 91 | gpa.free(p.strtab); |
| 92 | gpa.free(p.sections); |
| 93 | gpa.free(p.symtab); |
| 94 | gpa.free(p.versyms); |
| 95 | gpa.free(p.symbols); |
| 96 | gpa.free(p.verstrings); |
| 97 | p.* = undefined; |
| 98 | } |
| 99 | |
| 100 | pub fn versionString(p: Parsed, index: elf.Versym) [:0]const u8 { |
| 101 | return versionStringLookup(p.strtab, p.verstrings, index); |
| 102 | } |
| 103 | |
| 104 | pub fn soname(p: Parsed) ?[]const u8 { |
| 105 | const i = p.soname_index orelse return null; |
| 106 | return Elf.stringTableLookup(p.strtab, i); |
| 107 | } |
| 108 | }; |
| 109 | |
| 110 | pub fn parseHeader( |
| 111 | gpa: Allocator, |
| 112 | io: Io, |
| 113 | diags: *Diags, |
| 114 | file_path: Path, |
| 115 | file: Io.File, |
| 116 | stat: Stat, |
| 117 | target: *const std.Target, |
| 118 | ) !Header { |
| 119 | var ehdr: elf.Elf64_Ehdr = undefined; |
| 120 | { |
| 121 | const buf = mem.asBytes(&ehdr); |
| 122 | const amt = try file.readPositionalAll(io, buf, 0); |
| 123 | if (amt != buf.len) return error.UnexpectedEndOfFile; |
| 124 | } |
| 125 | if (!mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")) return error.BadMagic; |
| 126 | if (ehdr.e_ident[elf.EI.VERSION] != 1) return error.BadElfVersion; |
| 127 | if (ehdr.e_type != elf.ET.DYN) return error.NotSharedObject; |
| 128 | |
| 129 | if (target.toElfMachine() != ehdr.e_machine) |
| 130 | return diags.failParse(file_path, "invalid ELF machine type: {s}", .{@tagName(ehdr.e_machine)}); |
| 131 | |
| 132 | const shoff = std.math.cast(usize, ehdr.e_shoff) orelse return error.Overflow; |
| 133 | const shnum = std.math.cast(u32, ehdr.e_shnum) orelse return error.Overflow; |
| 134 | |
| 135 | const sections = try gpa.alloc(elf.Elf64_Shdr, shnum); |
| 136 | errdefer gpa.free(sections); |
| 137 | { |
| 138 | const buf = mem.sliceAsBytes(sections); |
| 139 | const amt = try file.readPositionalAll(io, buf, shoff); |
| 140 | if (amt != buf.len) return error.UnexpectedEndOfFile; |
| 141 | } |
| 142 | |
| 143 | var dynsym_sect_index: ?u32 = null; |
| 144 | var dynamic_sect_index: ?u32 = null; |
| 145 | var versym_sect_index: ?u32 = null; |
| 146 | var verdef_sect_index: ?u32 = null; |
| 147 | for (sections, 0..) |shdr, i_usize| { |
| 148 | const i: u32 = @intCast(i_usize); |
| 149 | switch (shdr.sh_type) { |
| 150 | elf.SHT_DYNSYM => dynsym_sect_index = i, |
| 151 | elf.SHT_DYNAMIC => dynamic_sect_index = i, |
| 152 | elf.SHT_GNU_VERSYM => versym_sect_index = i, |
| 153 | elf.SHT_GNU_VERDEF => verdef_sect_index = i, |
| 154 | else => continue, |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | const dynamic_table: []elf.Elf64_Dyn = if (dynamic_sect_index) |index| dt: { |
| 159 | const shdr = sections[index]; |
| 160 | const n = std.math.cast(usize, shdr.sh_size / @sizeOf(elf.Elf64_Dyn)) orelse return error.Overflow; |
| 161 | const dynamic_table = try gpa.alloc(elf.Elf64_Dyn, n); |
| 162 | errdefer gpa.free(dynamic_table); |
| 163 | const buf = mem.sliceAsBytes(dynamic_table); |
| 164 | const amt = try file.readPositionalAll(io, buf, shdr.sh_offset); |
| 165 | if (amt != buf.len) return error.UnexpectedEndOfFile; |
| 166 | break :dt dynamic_table; |
| 167 | } else &.{}; |
| 168 | errdefer gpa.free(dynamic_table); |
| 169 | |
| 170 | var strtab: std.ArrayList(u8) = .empty; |
| 171 | errdefer strtab.deinit(gpa); |
| 172 | |
| 173 | if (dynsym_sect_index) |index| { |
| 174 | const dynsym_shdr = sections[index]; |
| 175 | if (dynsym_shdr.sh_link >= sections.len) return error.BadStringTableIndex; |
| 176 | const strtab_shdr = sections[dynsym_shdr.sh_link]; |
| 177 | const n = std.math.cast(usize, strtab_shdr.sh_size) orelse return error.Overflow; |
| 178 | const buf = try strtab.addManyAsSlice(gpa, n); |
| 179 | const amt = try file.readPositionalAll(io, buf, strtab_shdr.sh_offset); |
| 180 | if (amt != buf.len) return error.UnexpectedEndOfFile; |
| 181 | } |
| 182 | |
| 183 | var soname_index: ?u32 = null; |
| 184 | var verdefnum: ?u32 = null; |
| 185 | for (dynamic_table) |entry| switch (entry.d_tag) { |
| 186 | elf.DT_SONAME => { |
| 187 | if (entry.d_val >= strtab.items.len) return error.BadSonameIndex; |
| 188 | soname_index = @intCast(entry.d_val); |
| 189 | }, |
| 190 | elf.DT_VERDEFNUM => { |
| 191 | verdefnum = @intCast(entry.d_val); |
| 192 | }, |
| 193 | else => continue, |
| 194 | }; |
| 195 | |
| 196 | return .{ |
| 197 | .dynamic_table = dynamic_table, |
| 198 | .soname_index = soname_index, |
| 199 | .verdefnum = verdefnum, |
| 200 | .sections = sections, |
| 201 | .dynsym_sect_index = dynsym_sect_index, |
| 202 | .versym_sect_index = versym_sect_index, |
| 203 | .verdef_sect_index = verdef_sect_index, |
| 204 | .strtab = strtab, |
| 205 | .stat = stat, |
| 206 | }; |
| 207 | } |
| 208 | |
| 209 | pub fn parse( |
| 210 | gpa: Allocator, |
| 211 | io: Io, |
| 212 | /// Moves resources from header. Caller may unconditionally deinit. |
| 213 | header: *Header, |
| 214 | file: Io.File, |
| 215 | ) !Parsed { |
| 216 | const symtab = if (header.dynsym_sect_index) |index| st: { |
| 217 | const shdr = header.sections[index]; |
| 218 | const n = std.math.cast(usize, shdr.sh_size / @sizeOf(elf.Elf64_Sym)) orelse return error.Overflow; |
| 219 | const symtab = try gpa.alloc(elf.Elf64_Sym, n); |
| 220 | errdefer gpa.free(symtab); |
| 221 | const buf = mem.sliceAsBytes(symtab); |
| 222 | const amt = try file.readPositionalAll(io, buf, shdr.sh_offset); |
| 223 | if (amt != buf.len) return error.UnexpectedEndOfFile; |
| 224 | break :st symtab; |
| 225 | } else &.{}; |
| 226 | defer gpa.free(symtab); |
| 227 | |
| 228 | var verstrings: std.ArrayList(u32) = .empty; |
| 229 | defer verstrings.deinit(gpa); |
| 230 | |
| 231 | if (header.verdef_sect_index) |shndx| { |
| 232 | const shdr = header.sections[shndx]; |
| 233 | const verdefs = try Elf.preadAllAlloc(gpa, io, file, shdr.sh_offset, shdr.sh_size); |
| 234 | defer gpa.free(verdefs); |
| 235 | |
| 236 | var offset: u32 = 0; |
| 237 | while (true) { |
| 238 | const verdef = mem.bytesAsValue(elf.Verdef, verdefs[offset..][0..@sizeOf(elf.Verdef)]); |
| 239 | if (verdef.ndx == .UNSPECIFIED) return error.VerDefSymbolTooLarge; |
| 240 | |
| 241 | if (verstrings.items.len <= @backingInt(verdef.ndx)) |
| 242 | try verstrings.appendNTimes(gpa, 0, @backingInt(verdef.ndx) + 1 - verstrings.items.len); |
| 243 | |
| 244 | const aux = mem.bytesAsValue(elf.Verdaux, verdefs[offset + verdef.aux ..][0..@sizeOf(elf.Verdaux)]); |
| 245 | verstrings.items[@backingInt(verdef.ndx)] = aux.name; |
| 246 | |
| 247 | if (verdef.next == 0) break; |
| 248 | offset += verdef.next; |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | const versyms = if (header.versym_sect_index) |versym_sect_index| vs: { |
| 253 | const shdr = header.sections[versym_sect_index]; |
| 254 | if (shdr.sh_size != symtab.len * @sizeOf(elf.Versym)) return error.BadVerSymSectionSize; |
| 255 | |
| 256 | const versyms = try gpa.alloc(elf.Versym, symtab.len); |
| 257 | errdefer gpa.free(versyms); |
| 258 | const buf = mem.sliceAsBytes(versyms); |
| 259 | const amt = try file.readPositionalAll(io, buf, shdr.sh_offset); |
| 260 | if (amt != buf.len) return error.UnexpectedEndOfFile; |
| 261 | break :vs versyms; |
| 262 | } else &.{}; |
| 263 | defer gpa.free(versyms); |
| 264 | |
| 265 | var nonlocal_esyms: std.ArrayList(elf.Elf64_Sym) = .empty; |
| 266 | defer nonlocal_esyms.deinit(gpa); |
| 267 | |
| 268 | var nonlocal_versyms: std.ArrayList(elf.Versym) = .empty; |
| 269 | defer nonlocal_versyms.deinit(gpa); |
| 270 | |
| 271 | var nonlocal_symbols: std.ArrayList(Parsed.Symbol) = .empty; |
| 272 | defer nonlocal_symbols.deinit(gpa); |
| 273 | |
| 274 | var strtab = header.strtab; |
| 275 | header.strtab = .empty; |
| 276 | defer strtab.deinit(gpa); |
| 277 | |
| 278 | for (symtab, 0..) |sym, i| { |
| 279 | const ver: elf.Versym = if (versyms.len == 0 or sym.st_shndx == elf.SHN_UNDEF) |
| 280 | .GLOBAL |
| 281 | else |
| 282 | .{ .VERSION = versyms[i].VERSION, .HIDDEN = false }; |
| 283 | |
| 284 | if (ver == elf.Versym.LOCAL) continue; |
| 285 | |
| 286 | try nonlocal_esyms.ensureUnusedCapacity(gpa, 1); |
| 287 | try nonlocal_versyms.ensureUnusedCapacity(gpa, 1); |
| 288 | try nonlocal_symbols.ensureUnusedCapacity(gpa, 1); |
| 289 | |
| 290 | const name = Elf.stringTableLookup(strtab.items, sym.st_name); |
| 291 | const is_default = versyms.len == 0 or !versyms[i].HIDDEN; |
| 292 | const mangled_name = if (is_default) sym.st_name else mn: { |
| 293 | const off: u32 = @intCast(strtab.items.len); |
| 294 | const version_string = versionStringLookup(strtab.items, verstrings.items, versyms[i]); |
| 295 | try strtab.ensureUnusedCapacity(gpa, name.len + version_string.len + 2); |
| 296 | // Reload since the string table might have been resized. |
| 297 | const name2 = Elf.stringTableLookup(strtab.items, sym.st_name); |
| 298 | const version_string2 = versionStringLookup(strtab.items, verstrings.items, versyms[i]); |
| 299 | strtab.appendSliceAssumeCapacity(name2); |
| 300 | strtab.appendAssumeCapacity('@'); |
| 301 | strtab.appendSliceAssumeCapacity(version_string2); |
| 302 | strtab.appendAssumeCapacity(0); |
| 303 | break :mn off; |
| 304 | }; |
| 305 | |
| 306 | nonlocal_esyms.appendAssumeCapacity(sym); |
| 307 | nonlocal_versyms.appendAssumeCapacity(ver); |
| 308 | nonlocal_symbols.appendAssumeCapacity(.{ |
| 309 | .mangled_name = mangled_name, |
| 310 | }); |
| 311 | } |
| 312 | |
| 313 | const sections = header.sections; |
| 314 | header.sections = &.{}; |
| 315 | errdefer gpa.free(sections); |
| 316 | |
| 317 | try strtab.shrinkToLen(gpa); |
| 318 | try nonlocal_esyms.shrinkToLen(gpa); |
| 319 | try nonlocal_versyms.shrinkToLen(gpa); |
| 320 | try nonlocal_symbols.shrinkToLen(gpa); |
| 321 | try verstrings.shrinkToLen(gpa); |
| 322 | |
| 323 | return .{ |
| 324 | .sections = sections, |
| 325 | .stat = header.stat, |
| 326 | .soname_index = header.soname_index, |
| 327 | .strtab = strtab.toOwnedSliceAssert(), |
| 328 | .symtab = nonlocal_esyms.toOwnedSliceAssert(), |
| 329 | .versyms = nonlocal_versyms.toOwnedSliceAssert(), |
| 330 | .symbols = nonlocal_symbols.toOwnedSliceAssert(), |
| 331 | .verstrings = verstrings.toOwnedSliceAssert(), |
| 332 | }; |
| 333 | } |
| 334 | |
| 335 | pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) !void { |
| 336 | const gpa = elf_file.base.comp.gpa; |
| 337 | |
| 338 | for (self.parsed.symtab, self.symbols_resolver.items, 0..) |esym, *resolv, i| { |
| 339 | const gop = try elf_file.resolver.getOrPut(gpa, .{ |
| 340 | .index = @intCast(i), |
| 341 | .file = self.index, |
| 342 | }, elf_file); |
| 343 | if (!gop.found_existing) { |
| 344 | gop.ref.* = .{ .index = 0, .file = 0 }; |
| 345 | } |
| 346 | resolv.* = gop.index; |
| 347 | |
| 348 | if (esym.st_shndx == elf.SHN_UNDEF) continue; |
| 349 | if (elf_file.symbol(gop.ref.*) == null) { |
| 350 | gop.ref.* = .{ .index = @intCast(i), .file = self.index }; |
| 351 | continue; |
| 352 | } |
| 353 | |
| 354 | if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) { |
| 355 | gop.ref.* = .{ .index = @intCast(i), .file = self.index }; |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | pub fn markLive(self: *SharedObject, elf_file: *Elf) void { |
| 361 | for (self.parsed.symtab, 0..) |esym, i| { |
| 362 | if (esym.st_shndx != elf.SHN_UNDEF) continue; |
| 363 | |
| 364 | const ref = self.resolveSymbol(@intCast(i), elf_file); |
| 365 | const sym = elf_file.symbol(ref) orelse continue; |
| 366 | const file = sym.file(elf_file).?; |
| 367 | const should_drop = switch (file) { |
| 368 | .shared_object => |sh| !sh.needed and esym.st_bind() == elf.STB_WEAK, |
| 369 | else => false, |
| 370 | }; |
| 371 | if (!should_drop and !file.isAlive()) { |
| 372 | file.setAlive(); |
| 373 | file.markLive(elf_file); |
| 374 | } |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | pub fn markImportExports(self: *SharedObject, elf_file: *Elf) void { |
| 379 | for (0..self.symbols.items.len) |i| { |
| 380 | const ref = self.resolveSymbol(@intCast(i), elf_file); |
| 381 | const ref_sym = elf_file.symbol(ref) orelse continue; |
| 382 | const ref_file = ref_sym.file(elf_file).?; |
| 383 | const vis: elf.STV = @fromBackingInt(@intCast(@as(u3, @truncate(ref_sym.elfSym(elf_file).st_other)))); |
| 384 | if (ref_file != .shared_object and vis != .HIDDEN) ref_sym.flags.@"export" = true; |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void { |
| 389 | for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| { |
| 390 | const ref = elf_file.resolver.get(resolv).?; |
| 391 | const ref_sym = elf_file.symbol(ref) orelse continue; |
| 392 | if (ref_sym.file(elf_file).?.index() != self.index) continue; |
| 393 | if (global.isLocal(elf_file)) continue; |
| 394 | global.flags.output_symtab = true; |
| 395 | global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file); |
| 396 | self.output_symtab_ctx.nglobals += 1; |
| 397 | self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1; |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | pub fn writeSymtab(self: *SharedObject, elf_file: *Elf) void { |
| 402 | for (self.symbols.items, self.symbols_resolver.items) |global, resolv| { |
| 403 | const ref = elf_file.resolver.get(resolv).?; |
| 404 | const ref_sym = elf_file.symbol(ref) orelse continue; |
| 405 | if (ref_sym.file(elf_file).?.index() != self.index) continue; |
| 406 | const idx = global.outputSymtabIndex(elf_file) orelse continue; |
| 407 | const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); |
| 408 | elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file)); |
| 409 | elf_file.strtab.appendAssumeCapacity(0); |
| 410 | const out_sym = &elf_file.symtab.items[idx]; |
| 411 | out_sym.st_name = st_name; |
| 412 | global.setOutputSym(elf_file, out_sym); |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | pub fn versionString(self: SharedObject, index: elf.Versym) [:0]const u8 { |
| 417 | return self.parsed.versionString(index); |
| 418 | } |
| 419 | |
| 420 | fn versionStringLookup(strtab: []const u8, verstrings: []const u32, index: elf.Versym) [:0]const u8 { |
| 421 | const off = verstrings[index.VERSION]; |
| 422 | return Elf.stringTableLookup(strtab, off); |
| 423 | } |
| 424 | |
| 425 | pub fn asFile(self: *SharedObject) File { |
| 426 | return .{ .shared_object = self }; |
| 427 | } |
| 428 | |
| 429 | pub fn soname(self: *SharedObject) []const u8 { |
| 430 | return self.parsed.soname() orelse self.path.basename(); |
| 431 | } |
| 432 | |
| 433 | pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { |
| 434 | assert(self.aliases == null); |
| 435 | |
| 436 | const SortAlias = struct { |
| 437 | so: *SharedObject, |
| 438 | ef: *Elf, |
| 439 | |
| 440 | pub fn lessThan(ctx: @This(), lhs: Symbol.Index, rhs: Symbol.Index) bool { |
| 441 | const lhs_sym = ctx.so.symbols.items[lhs].elfSym(ctx.ef); |
| 442 | const rhs_sym = ctx.so.symbols.items[rhs].elfSym(ctx.ef); |
| 443 | return lhs_sym.st_value < rhs_sym.st_value; |
| 444 | } |
| 445 | }; |
| 446 | |
| 447 | const comp = elf_file.base.comp; |
| 448 | const gpa = comp.gpa; |
| 449 | var aliases = std.array_list.Managed(Symbol.Index).init(gpa); |
| 450 | defer aliases.deinit(); |
| 451 | try aliases.ensureTotalCapacityPrecise(self.symbols.items.len); |
| 452 | |
| 453 | for (self.symbols_resolver.items, 0..) |resolv, index| { |
| 454 | const ref = elf_file.resolver.get(resolv).?; |
| 455 | const ref_sym = elf_file.symbol(ref) orelse continue; |
| 456 | if (ref_sym.file(elf_file).?.index() != self.index) continue; |
| 457 | aliases.appendAssumeCapacity(@intCast(index)); |
| 458 | } |
| 459 | |
| 460 | mem.sort(u32, aliases.items, SortAlias{ .so = self, .ef = elf_file }, SortAlias.lessThan); |
| 461 | |
| 462 | self.aliases = aliases.moveToUnmanaged(); |
| 463 | } |
| 464 | |
| 465 | pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u32 { |
| 466 | assert(self.aliases != null); |
| 467 | |
| 468 | const symbol = self.symbols.items[index].elfSym(elf_file); |
| 469 | const aliases = self.aliases.?; |
| 470 | |
| 471 | const start = for (aliases.items, 0..) |alias, i| { |
| 472 | const alias_sym = self.symbols.items[alias].elfSym(elf_file); |
| 473 | if (symbol.st_value == alias_sym.st_value) break i; |
| 474 | } else aliases.items.len; |
| 475 | |
| 476 | const end = for (aliases.items[start..], 0..) |alias, i| { |
| 477 | const alias_sym = self.symbols.items[alias].elfSym(elf_file); |
| 478 | if (symbol.st_value < alias_sym.st_value) break i + start; |
| 479 | } else aliases.items.len; |
| 480 | |
| 481 | return aliases.items[start..end]; |
| 482 | } |
| 483 | |
| 484 | pub fn getString(self: SharedObject, off: u32) [:0]const u8 { |
| 485 | return Elf.stringTableLookup(self.parsed.strtab, off); |
| 486 | } |
| 487 | |
| 488 | pub fn resolveSymbol(self: SharedObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref { |
| 489 | const resolv = self.symbols_resolver.items[index]; |
| 490 | return elf_file.resolver.get(resolv).?; |
| 491 | } |
| 492 | |
| 493 | pub fn addSymbolAssumeCapacity(self: *SharedObject) Symbol.Index { |
| 494 | const index: Symbol.Index = @intCast(self.symbols.items.len); |
| 495 | self.symbols.appendAssumeCapacity(.{ .file_index = self.index }); |
| 496 | return index; |
| 497 | } |
| 498 | |
| 499 | pub fn addSymbolExtraAssumeCapacity(self: *SharedObject, extra: Symbol.Extra) u32 { |
| 500 | const index: u32 = @intCast(self.symbols_extra.items.len); |
| 501 | const info = @typeInfo(Symbol.Extra).@"struct"; |
| 502 | inline for (info.field_names, info.field_types) |field_name, field_type| { |
| 503 | self.symbols_extra.appendAssumeCapacity(switch (field_type) { |
| 504 | u32 => @field(extra, field_name), |
| 505 | else => @compileError("bad field type"), |
| 506 | }); |
| 507 | } |
| 508 | return index; |
| 509 | } |
| 510 | |
| 511 | pub fn symbolExtra(self: *SharedObject, index: u32) Symbol.Extra { |
| 512 | const info = @typeInfo(Symbol.Extra).@"struct"; |
| 513 | var i: usize = index; |
| 514 | var result: Symbol.Extra = undefined; |
| 515 | inline for (info.field_names, info.field_types) |field_name, field_type| { |
| 516 | @field(result, field_name) = switch (field_type) { |
| 517 | u32 => self.symbols_extra.items[i], |
| 518 | else => @compileError("bad field type"), |
| 519 | }; |
| 520 | i += 1; |
| 521 | } |
| 522 | return result; |
| 523 | } |
| 524 | |
| 525 | pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void { |
| 526 | const info = @typeInfo(Symbol.Extra).@"struct"; |
| 527 | inline for (info.field_names, info.field_types, 0..) |field_name, field_type, i| { |
| 528 | self.symbols_extra.items[index + i] = switch (field_type) { |
| 529 | u32 => @field(extra, field_name), |
| 530 | else => @compileError("bad field type"), |
| 531 | }; |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | pub fn fmtSymtab(self: SharedObject, elf_file: *Elf) std.fmt.Alt(Format, Format.symtab) { |
| 536 | return .{ .data = .{ |
| 537 | .shared = self, |
| 538 | .elf_file = elf_file, |
| 539 | } }; |
| 540 | } |
| 541 | |
| 542 | const Format = struct { |
| 543 | shared: SharedObject, |
| 544 | elf_file: *Elf, |
| 545 | |
| 546 | fn symtab(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 547 | const shared = f.shared; |
| 548 | const elf_file = f.elf_file; |
| 549 | try writer.writeAll(" globals\n"); |
| 550 | for (shared.symbols.items, 0..) |sym, i| { |
| 551 | const ref = shared.resolveSymbol(@intCast(i), elf_file); |
| 552 | if (elf_file.symbol(ref)) |ref_sym| { |
| 553 | try writer.print(" {f}\n", .{ref_sym.fmt(elf_file)}); |
| 554 | } else { |
| 555 | try writer.print(" {s} : unclaimed\n", .{sym.name(elf_file)}); |
| 556 | } |
| 557 | } |
| 558 | } |
| 559 | }; |