╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ gen_stubs.zig ────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
87 .i386, 88 .mips, 89 .mipsn32, 90 .powerpc, 91 .riscv32, 92 => 4, 93 .aarch64, 94 .loongarch64, 95 .mips64, 96 .powerpc64, 97 .riscv64, 98 .s390x, 99 .x86_64, 100 => 8, 101 }; 102 } 103 104 pub fn isTime32(arch: Arch) bool { 105 return switch (arch) { 106 // This list will never grow; newer 32-bit ports will be time64 (e.g. riscv32). 107 .arm, 108 .i386, 109 .mips, 110 .mipsn32, 111 .powerpc, 112 => true, 113 else => false, 114 }; 115 } 116 117 pub fn family(arch: Arch) Family { 118 return switch (arch) { 119 .aarch64 => .aarch64, 120 .arm => .arm, 121 .i386, .x86_64 => .x86, 122 .hexagon => .hexagon, 123 .loongarch64 => .loongarch, 124 .mips, .mips64, .mipsn32 => .mips, 125 .powerpc, .powerpc64 => .powerpc, 126 .riscv32, .riscv64 => .riscv, 127 .s390x => .s390x, 128 }; 129 } 130 }; 131 132 const Family = enum { 133 aarch64, 134 arm, 135 hexagon, 136 loongarch, 137 mips, 138 powerpc, 139 riscv, 140 s390x, 141 x86, 142 }; 143 144 const arches: [@typeInfo(Arch).@"enum".field_names.len]Arch = blk: { 145 var result: [@typeInfo(Arch).@"enum".field_names.len]Arch = undefined; 146 for (@typeInfo(Arch).@"enum".field_values) |field_value| { 147 const arch: Arch = @fromBackingInt(@intCast(field_value)); 148 result[archIndex(arch)] = arch; 149 } 150 break :blk result; 151 }; 152 153 const MultiSym = struct { 154 size: [arches.len]u64, 155 present: [arches.len]bool, 156 binding: [arches.len]u4, 157 section: u16, 158 ty: u4, 159 visib: elf.STV, 160 161 fn isSingleArch(ms: MultiSym) ?Arch { 162 var result: ?Arch = null; 163 inline for (@typeInfo(Arch).@"enum".field_values) |field_value| { 164 const arch: Arch = @fromBackingInt(@intCast(field_value)); 165 if (ms.present[archIndex(arch)]) { 166 if (result != null) return null; 167 result = arch; 168 } 169 } 170 return result; 171 } 172 173 fn isFamily(ms: MultiSym) ?Family { 174 var result: ?Family = null; 175 inline for (@typeInfo(Arch).@"enum".field_values) |field_value| { 176 const arch: Arch = @fromBackingInt(@intCast(field_value)); 177 if (ms.present[archIndex(arch)]) { 178 const family = arch.family(); 179 if (result) |r| if (family != r) return null; 180 result = family; 181 } 182 } 183 return result; 184 } 185 186 fn allPresent(ms: MultiSym) bool { 187 for (arches, 0..) |_, i| { 188 if (!ms.present[i]) { 189 return false; 190 } 191 } 192 return true; 193 } 194 195 fn isTime32Only(ms: MultiSym) bool { 196 inline for (@typeInfo(Arch).@"enum".field_values) |field_value| { 197 const arch: Arch = @fromBackingInt(@intCast(field_value)); 198 if (ms.present[archIndex(arch)] != arch.isTime32()) { 199 return false; 200 } 201 } 202 return true; 203 } 204 205 fn commonSize(ms: MultiSym) ?u64 { 206 var size: ?u64 = null; 207 for (arches, 0..) |_, i| { 208 if (!ms.present[i]) continue; 209 if (size) |s| { 210 if (ms.size[i] != s) { 211 return null; 212 } 213 } else { 214 size = ms.size[i]; 215 } 216 } 217 return size.?; 218 } 219 220 fn commonBinding(ms: MultiSym) ?u4 { 221 var binding: ?u4 = null; 222 for (arches, 0..) |_, i| { 223 if (!ms.present[i]) continue; 224 if (binding) |b| { 225 if (ms.binding[i] != b) { 226 return null; 227 } 228 } else { 229 binding = ms.binding[i]; 230 } 231 } 232 return binding.?; 233 } 234 235 fn isPtrSize(ms: MultiSym, mult: u16) bool { 236 inline for (@typeInfo(Arch).@"enum".field_values) |field_value| { 237 const arch: Arch = @fromBackingInt(@intCast(field_value)); 238 const arch_index = archIndex(arch); 239 if (ms.present[arch_index] and ms.size[arch_index] != arch.ptrSize() * mult) { 240 return false; 241 } 242 } 243 return true; 244 } 245 246 fn isWeak64(ms: MultiSym) bool { 247 inline for (@typeInfo(Arch).@"enum".field_values) |field_value| { 248 const arch: Arch = @fromBackingInt(@intCast(field_value)); 249 const arch_index = archIndex(arch); 250 const binding: u4 = switch (arch.ptrSize()) { 251 4 => std.elf.STB_GLOBAL, 252 8 => std.elf.STB_WEAK, 253 else => unreachable, 254 }; 255 if (ms.present[arch_index] and ms.binding[arch_index] != binding) { 256 return false; 257 } 258 } 259 return true; 260 } 261 262 fn isWeakTime64(ms: MultiSym) bool { 263 inline for (@typeInfo(Arch).@"enum".field_values) |field_value| { 264 const arch: Arch = @fromBackingInt(@intCast(field_value)); 265 const arch_index = archIndex(arch); 266 const binding: u4 = if (arch.isTime32()) std.elf.STB_GLOBAL else std.elf.STB_WEAK; 267 if (ms.present[arch_index] and ms.binding[arch_index] != binding) { 268 return false; 269 } 270 } 271 return true; 272 } 273 }; 274 275 const Parse = struct { 276 arena: mem.Allocator, 277 sym_table: *std.array_hash_map.String(MultiSym), 278 sections: *std.array_hash_map.String(void), 279 elf_bytes: []align(@alignOf(elf.Elf64_Ehdr)) u8, 280 header: elf.Header, 281 arch: Arch, 282 }; 283 284 pub fn main(init: std.process.Init) !void { 285 const arena = init.arena.allocator(); 286 const io = init.io; 287 const args = try init.minimal.args.toSlice(arena); 288 const build_all_path = args[1]; 289 290 var build_all_dir = try Io.Dir.cwd().openDir(io, build_all_path, .{}); 291 292 var sym_table: std.array_hash_map.String(MultiSym) = .empty; 293 var sections: std.array_hash_map.String(void) = .empty; 294 295 for (arches) |arch| { 296 const libc_so_path = try std.fmt.allocPrint(arena, "{t}/lib/libc.so", .{arch}); 297 298 // Read the ELF header. 299 const elf_bytes = build_all_dir.readFileAllocOptions( 300 io, 301 libc_so_path, 302 arena, 303 .limited(100 * 1024 * 1024), 304 .of(elf.Elf64_Ehdr), 305 null, 306 ) catch |err| { 307 std.debug.panic("unable to read '{s}/{s}': {t}", .{ build_all_path, libc_so_path, err }); 308 }; 309 var stream: std.Io.Reader = .fixed(elf_bytes); 310 const header = try elf.Header.read(&stream); 311 312 const parse: Parse = .{ 313 .arena = arena, 314 .sym_table = &sym_table, 315 .sections = &sections, 316 .elf_bytes = elf_bytes, 317 .header = header, 318 .arch = arch, 319 }; 320 321 switch (header.is_64) { 322 true => switch (header.endian) { 323 .big => try parseElf(parse, true, .big), 324 .little => try parseElf(parse, true, .little), 325 }, 326 false => switch (header.endian) { 327 .big => try parseElf(parse, false, .big), 328 .little => try parseElf(parse, false, .little), 329 }, 330 } 331 } 332 333 var stdout_buffer: [2000]u8 = undefined; 334 var stdout_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); 335 const stdout = &stdout_writer.interface; 336 try stdout.writeAll( 337 \\#ifdef PTR64 338 \\#define WEAK64 .weak 339 \\#define PTR_SIZE_BYTES 8 340 \\#define PTR2_SIZE_BYTES 16 341 \\#else 342 \\#define WEAK64 .globl 343 \\#define PTR_SIZE_BYTES 4 344 \\#define PTR2_SIZE_BYTES 8 345 \\#endif 346 \\ 347 \\#ifdef TIME32 348 \\#define WEAKTIME64 .globl 349 \\#else 350 \\#define WEAKTIME64 .weak 351 \\#endif 352 \\ 353 \\ 354 ); 355 356 // Sort the symbols for deterministic output and cleaner vcs diffs. 357 const SymTableSort = struct { 358 sections: *const std.array_hash_map.String(void), 359 sym_table: *const std.array_hash_map.String(MultiSym), 360 361 /// Sort first by section name, then by symbol name 362 pub fn lessThan(ctx: @This(), index_a: usize, index_b: usize) bool { 363 const multi_sym_a = ctx.sym_table.values()[index_a]; 364 const multi_sym_b = ctx.sym_table.values()[index_b]; 365 366 const section_a = ctx.sections.keys()[multi_sym_a.section]; 367 const section_b = ctx.sections.keys()[multi_sym_b.section]; 368 369 switch (mem.order(u8, section_a, section_b)) { 370 .lt => return true, 371 .gt => return false, 372 .eq => {}, 373 } 374 375 const symbol_a = ctx.sym_table.keys()[index_a]; 376 const symbol_b = ctx.sym_table.keys()[index_b]; 377 378 switch (mem.order(u8, symbol_a, symbol_b)) { 379 .lt => return true, 380 .gt, .eq => return false, 381 } 382 } 383 }; 384 sym_table.sort(SymTableSort{ .sym_table = &sym_table, .sections = &sections }); 385 386 var prev_section: u16 = std.math.maxInt(u16); 387 var prev_pp_state: union(enum) { all, single: Arch, multi, family: Family, time32 } = .all; 388 for (sym_table.values(), 0..) |multi_sym, sym_index| { 389 const name = sym_table.keys()[sym_index]; 390 391 if (multi_sym.section != prev_section) { 392 prev_section = multi_sym.section; 393 const sh_name = sections.keys()[multi_sym.section]; 394 try stdout.print("{s}\n", .{sh_name}); 395 } 396 397 if (multi_sym.allPresent()) { 398 switch (prev_pp_state) { 399 .all => {}, 400 .single, .multi, .family, .time32 => { 401 try stdout.writeAll("#endif\n"); 402 prev_pp_state = .all; 403 }, 404 } 405 } else if (multi_sym.isSingleArch()) |arch| { 406 switch (prev_pp_state) { 407 .all => { 408 try stdout.print("#ifdef ARCH_{s}\n", .{@tagName(arch)}); 409 prev_pp_state = .{ .single = arch }; 410 }, 411 .multi, .family, .time32 => { 412 try stdout.print("#endif\n#ifdef ARCH_{s}\n", .{@tagName(arch)}); 413 prev_pp_state = .{ .single = arch }; 414 }, 415 .single => |prev_arch| { 416 if (arch != prev_arch) { 417 try stdout.print("#endif\n#ifdef ARCH_{s}\n", .{@tagName(arch)}); 418 prev_pp_state = .{ .single = arch }; 419 } 420 }, 421 } 422 } else if (multi_sym.isFamily()) |family| { 423 switch (prev_pp_state) { 424 .all => { 425 try stdout.print("#ifdef FAMILY_{s}\n", .{@tagName(family)}); 426 prev_pp_state = .{ .family = family }; 427 }, 428 .single, .multi, .time32 => { 429 try stdout.print("#endif\n#ifdef FAMILY_{s}\n", .{@tagName(family)}); 430 prev_pp_state = .{ .family = family }; 431 }, 432 .family => |prev_family| { 433 if (family != prev_family) { 434 try stdout.print("#endif\n#ifdef FAMILY_{s}\n", .{@tagName(family)}); 435 prev_pp_state = .{ .family = family }; 436 } 437 }, 438 } 439 } else if (multi_sym.isTime32Only()) { 440 switch (prev_pp_state) { 441 .all => { 442 try stdout.writeAll("#ifdef TIME32\n"); 443 prev_pp_state = .time32; 444 }, 445 .single, .multi, .family => { 446 try stdout.writeAll("#endif\n#ifdef TIME32\n"); 447 prev_pp_state = .time32; 448 }, 449 .time32 => {}, 450 } 451 } else { 452 switch (prev_pp_state) { 453 .all => {}, 454 .single, .multi, .family, .time32 => { 455 try stdout.writeAll("#endif\n"); 456 }, 457 } 458 prev_pp_state = .multi; 459 460 var first = true; 461 try stdout.writeAll("#if "); 462 463 for (arches, 0..) |arch, i| { 464 if (multi_sym.present[i]) continue; 465 466 if (!first) try stdout.writeAll(" && "); 467 first = false; 468 try stdout.print("!defined(ARCH_{s})", .{@tagName(arch)}); 469 } 470 471 try stdout.writeAll("\n"); 472 } 473 474 if (multi_sym.commonBinding()) |binding| { 475 switch (binding) { 476 elf.STB_GLOBAL => { 477 try stdout.print(".globl {s}\n", .{name}); 478 }, 479 elf.STB_WEAK => { 480 try stdout.print(".weak {s}\n", .{name}); 481 }, 482 else => unreachable, 483 } 484 } else if (multi_sym.isWeak64()) { 485 try stdout.print("WEAK64 {s}\n", .{name}); 486 } else if (multi_sym.isWeakTime64()) { 487 try stdout.print("WEAKTIME64 {s}\n", .{name}); 488 } else { 489 for (arches, 0..) |arch, i| { 490 log.info("symbol '{s}' binding on {s}: {d}", .{ 491 name, @tagName(arch), multi_sym.binding[i], 492 }); 493 } 494 } 495 496 switch (multi_sym.ty) { 497 elf.STT_NOTYPE => {}, 498 elf.STT_FUNC => { 499 try stdout.print(".type {s}, %function;\n", .{name}); 500 // omitting the size is OK for functions 501 }, 502 elf.STT_OBJECT => { 503 try stdout.print(".type {s}, %object;\n", .{name}); 504 if (multi_sym.commonSize()) |size| { 505 try stdout.print(".size {s}, {d}\n", .{ name, size }); 506 } else if (multi_sym.isPtrSize(1)) { 507 try stdout.print(".size {s}, PTR_SIZE_BYTES\n", .{name}); 508 } else if (multi_sym.isPtrSize(2)) { 509 try stdout.print(".size {s}, PTR2_SIZE_BYTES\n", .{name}); 510 } else { 511 for (arches, 0..) |arch, i| { 512 log.info("symbol '{s}' size on {s}: {d}", .{ 513 name, @tagName(arch), multi_sym.size[i], 514 }); 515 } 516 //try stdout.print(".size {s}, {d}\n", .{ name, size }); 517 } 518 }, 519 else => unreachable, 520 } 521 522 switch (multi_sym.visib) { 523 .DEFAULT => {}, 524 .PROTECTED => try stdout.print(".protected {s}\n", .{name}), 525 .INTERNAL, .HIDDEN => unreachable, 526 } 527 528 try stdout.print("{s}:\n", .{name}); 529 } 530 531 switch (prev_pp_state) { 532 .all => {}, 533 .single, .multi, .family, .time32 => try stdout.writeAll("#endif\n"), 534 } 535 536 try stdout.flush(); 537 } 538 539 fn parseElf(parse: Parse, comptime is_64: bool, comptime endian: std.builtin.Endian) !void { 540 const arena = parse.arena; 541 const elf_bytes = parse.elf_bytes; 542 const header = parse.header; 543 const Sym = if (is_64) elf.Elf64_Sym else elf.Elf32_Sym; 544 const S = struct { 545 fn endianSwap(x: anytype) @TypeOf(x) { 546 if (endian != native_endian) { 547 return @byteSwap(x); 548 } else { 549 return x; 550 } 551 } 552 fn symbolAddrLessThan(_: void, lhs: Sym, rhs: Sym) bool { 553 return endianSwap(lhs.st_value) < endianSwap(rhs.st_value); 554 } 555 }; 556 // A little helper to do endian swapping. 557 const s = S.endianSwap; 558 559 // Obtain list of sections. 560 const Shdr = if (is_64) elf.Elf64_Shdr else elf.Elf32_Shdr; 561 const shdrs = mem.bytesAsSlice(Shdr, elf_bytes[header.shoff..])[0..header.shnum]; 562 563 // Obtain the section header string table. 564 const shstrtab_offset = s(shdrs[header.shstrndx].sh_offset); 565 log.debug("shstrtab is at offset {d}", .{shstrtab_offset}); 566 const shstrtab = elf_bytes[shstrtab_offset..]; 567 568 // Maps this ELF file's section header index to the multi arch section ArrayHashMap index. 569 const section_index_map = try arena.alloc(u16, shdrs.len); 570 571 // Find the offset of the dynamic symbol table. 572 var dynsym_index: u16 = 0; 573 for (shdrs, 0..) |shdr, i| { 574 const sh_name = try arena.dupe(u8, mem.sliceTo(shstrtab[s(shdr.sh_name)..], 0)); 575 log.debug("found section: {s}", .{sh_name}); 576 if (mem.eql(u8, sh_name, ".dynsym")) { 577 dynsym_index = @as(u16, @intCast(i)); 578 } 579 const gop = try parse.sections.getOrPut(arena, sh_name); 580 section_index_map[i] = @as(u16, @intCast(gop.index)); 581 } 582 if (dynsym_index == 0) @panic("did not find the .dynsym section"); 583 584 log.debug("found .dynsym section at index {d}", .{dynsym_index}); 585 586 // Read the dynamic symbols into a list. 587 const dyn_syms_off = s(shdrs[dynsym_index].sh_offset); 588 const dyn_syms_size = s(shdrs[dynsym_index].sh_size); 589 const dyn_syms = mem.bytesAsSlice(Sym, elf_bytes[dyn_syms_off..][0..dyn_syms_size]); 590 591 const dynstr_offset = s(shdrs[s(shdrs[dynsym_index].sh_link)].sh_offset); 592 const dynstr = elf_bytes[dynstr_offset..]; 593 594 // Sort the list by address, ascending. 595 // We need a copy to fix alignment. 596 const copied_dyn_syms = copy: { 597 const ptr = try arena.alloc(Sym, dyn_syms.len); 598 @memcpy(ptr, dyn_syms); 599 break :copy ptr; 600 }; 601 mem.sort(Sym, copied_dyn_syms, {}, S.symbolAddrLessThan); 602 603 for (copied_dyn_syms) |sym| { 604 const this_section = s(sym.st_shndx); 605 const name = try arena.dupe(u8, mem.sliceTo(dynstr[s(sym.st_name)..], 0)); 606 const ty = @as(u4, @truncate(sym.st_info)); 607 const binding = @as(u4, @truncate(sym.st_info >> 4)); 608 const visib = @as(elf.STV, @fromBackingInt(@intCast(@as(u3, @truncate(sym.st_other))))); 609 const size = s(sym.st_size); 610 611 if (size == 0) { 612 log.warn("{s}: symbol '{s}' has size 0", .{ @tagName(parse.arch), name }); 613 } 614 615 if (sym.st_shndx == elf.SHN_UNDEF) { 616 log.debug("{s}: skipping '{s}' due to it being undefined", .{ 617 @tagName(parse.arch), name, 618 }); 619 continue; 620 } 621 622 switch (binding) { 623 elf.STB_GLOBAL, elf.STB_WEAK => {}, 624 else => { 625 log.debug("{s}: skipping '{s}' due to it having binding '{d}'", .{ 626 @tagName(parse.arch), name, binding, 627 }); 628 continue; 629 }, 630 } 631 632 switch (ty) { 633 elf.STT_NOTYPE, elf.STT_FUNC, elf.STT_OBJECT => {}, 634 else => { 635 log.debug("{s}: skipping '{s}' due to it having type '{d}'", .{ 636 @tagName(parse.arch), name, ty, 637 }); 638 continue; 639 }, 640 } 641 642 switch (visib) { 643 .DEFAULT, .PROTECTED => {}, 644 .INTERNAL, .HIDDEN => { 645 log.debug("{s}: skipping '{s}' due to it having visibility '{s}'", .{ 646 @tagName(parse.arch), name, @tagName(visib), 647 }); 648 continue; 649 }, 650 } 651 652 const gop = try parse.sym_table.getOrPut(arena, name); 653 if (gop.found_existing) { 654 if (gop.value_ptr.section != section_index_map[this_section]) { 655 const sh_name = mem.sliceTo(shstrtab[s(shdrs[this_section].sh_name)..], 0); 656 fatal("symbol '{s}' in arch {s} is in section {s} but in arch {s} is in section {s}", .{ 657 name, 658 @tagName(parse.arch), 659 sh_name, 660 archSetName(gop.value_ptr.present), 661 parse.sections.keys()[gop.value_ptr.section], 662 }); 663 } 664 if (gop.value_ptr.ty != ty) blk: { 665 if (ty == elf.STT_NOTYPE) { 666 log.warn("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}. going with the one that is not STT_NOTYPE", .{ 667 name, 668 @tagName(parse.arch), 669 ty, 670 archSetName(gop.value_ptr.present), 671 gop.value_ptr.ty, 672 }); 673 break :blk; 674 } 675 if (gop.value_ptr.ty == elf.STT_NOTYPE) { 676 log.warn("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}. going with the one that is not STT_NOTYPE", .{ 677 name, 678 @tagName(parse.arch), 679 ty, 680 archSetName(gop.value_ptr.present), 681 gop.value_ptr.ty, 682 }); 683 gop.value_ptr.ty = ty; 684 break :blk; 685 } 686 fatal("symbol '{s}' in arch {s} has type {d} but in arch {s} has type {d}", .{ 687 name, 688 @tagName(parse.arch), 689 ty, 690 archSetName(gop.value_ptr.present), 691 gop.value_ptr.ty, 692 }); 693 } 694 if (gop.value_ptr.visib != visib) { 695 fatal("symbol '{s}' in arch {s} has visib {s} but in arch {s} has visib {s}", .{ 696 name, 697 @tagName(parse.arch), 698 @tagName(visib), 699 archSetName(gop.value_ptr.present), 700 @tagName(gop.value_ptr.visib), 701 }); 702 } 703 } else { 704 gop.value_ptr.* = .{ 705 .present = @splat(false), 706 .section = section_index_map[this_section], 707 .ty = ty, 708 .binding = @splat(0), 709 .visib = visib, 710 .size = @splat(0), 711 }; 712 } 713 gop.value_ptr.present[archIndex(parse.arch)] = true; 714 gop.value_ptr.size[archIndex(parse.arch)] = size; 715 gop.value_ptr.binding[archIndex(parse.arch)] = binding; 716 } 717 } 718 719 fn archIndex(arch: Arch) u8 { 720 return @backingInt(arch); 721 } 722 723 fn archSetName(arch_set: [arches.len]bool) []const u8 { 724 for (arches, arch_set) |arch, set_item| { 725 if (set_item) { 726 return @tagName(arch); 727 } 728 } 729 return "(none)"; 730 } 731 732 fn fatal(comptime format: []const u8, args: anytype) noreturn { 733 log.err(format, args); 734 std.process.exit(1); 735 } 736