blob c8faaa33 (213302B) - Raw
1 base: link.File, 2 rpath_table: std.StringArrayHashMapUnmanaged(void), 3 image_base: u64, 4 emit_relocs: bool, 5 z_nodelete: bool, 6 z_notext: bool, 7 z_defs: bool, 8 z_origin: bool, 9 z_nocopyreloc: bool, 10 z_now: bool, 11 z_relro: bool, 12 /// TODO make this non optional and resolve the default in open() 13 z_common_page_size: ?u64, 14 /// TODO make this non optional and resolve the default in open() 15 z_max_page_size: ?u64, 16 lib_dirs: []const []const u8, 17 hash_style: HashStyle, 18 compress_debug_sections: CompressDebugSections, 19 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), 20 sort_section: ?SortSection, 21 soname: ?[]const u8, 22 bind_global_refs_locally: bool, 23 linker_script: ?[]const u8, 24 version_script: ?[]const u8, 25 allow_undefined_version: bool, 26 enable_new_dtags: ?bool, 27 print_icf_sections: bool, 28 print_map: bool, 29 entry_name: ?[]const u8, 30 31 ptr_width: PtrWidth, 32 33 /// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. 34 llvm_object: ?LlvmObject.Ptr = null, 35 36 /// A list of all input files. 37 /// Index of each input file also encodes the priority or precedence of one input file 38 /// over another. 39 files: std.MultiArrayList(File.Entry) = .{}, 40 /// Long-lived list of all file descriptors. 41 /// We store them globally rather than per actual File so that we can re-use 42 /// one file handle per every object file within an archive. 43 file_handles: std.ArrayListUnmanaged(File.Handle) = .empty, 44 zig_object_index: ?File.Index = null, 45 linker_defined_index: ?File.Index = null, 46 objects: std.ArrayListUnmanaged(File.Index) = .empty, 47 shared_objects: std.ArrayListUnmanaged(File.Index) = .empty, 48 49 /// List of all output sections and their associated metadata. 50 sections: std.MultiArrayList(Section) = .{}, 51 /// File offset into the shdr table. 52 shdr_table_offset: ?u64 = null, 53 54 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. 55 /// Same order as in the file. 56 phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .empty, 57 58 /// Special program headers 59 /// PT_PHDR 60 phdr_table_index: ?u16 = null, 61 /// PT_LOAD for PHDR table 62 /// We add this special load segment to ensure the EHDR and PHDR table are always 63 /// loaded into memory. 64 phdr_table_load_index: ?u16 = null, 65 /// PT_INTERP 66 phdr_interp_index: ?u16 = null, 67 /// PT_DYNAMIC 68 phdr_dynamic_index: ?u16 = null, 69 /// PT_GNU_EH_FRAME 70 phdr_gnu_eh_frame_index: ?u16 = null, 71 /// PT_GNU_STACK 72 phdr_gnu_stack_index: ?u16 = null, 73 /// PT_TLS 74 /// TODO I think ELF permits multiple TLS segments but for now, assume one per file. 75 phdr_tls_index: ?u16 = null, 76 77 page_size: u32, 78 default_sym_version: elf.Elf64_Versym, 79 80 /// .shstrtab buffer 81 shstrtab: std.ArrayListUnmanaged(u8) = .empty, 82 /// .symtab buffer 83 symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .empty, 84 /// .strtab buffer 85 strtab: std.ArrayListUnmanaged(u8) = .empty, 86 /// Dynamic symbol table. Only populated and emitted when linking dynamically. 87 dynsym: DynsymSection = .{}, 88 /// .dynstrtab buffer 89 dynstrtab: std.ArrayListUnmanaged(u8) = .empty, 90 /// Version symbol table. Only populated and emitted when linking dynamically. 91 versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .empty, 92 /// .verneed section 93 verneed: VerneedSection = .{}, 94 /// .got section 95 got: GotSection = .{}, 96 /// .rela.dyn section 97 rela_dyn: std.ArrayListUnmanaged(elf.Elf64_Rela) = .empty, 98 /// .dynamic section 99 dynamic: DynamicSection = .{}, 100 /// .hash section 101 hash: HashSection = .{}, 102 /// .gnu.hash section 103 gnu_hash: GnuHashSection = .{}, 104 /// .plt section 105 plt: PltSection = .{}, 106 /// .got.plt section 107 got_plt: GotPltSection = .{}, 108 /// .plt.got section 109 plt_got: PltGotSection = .{}, 110 /// .copyrel section 111 copy_rel: CopyRelSection = .{}, 112 /// .rela.plt section 113 rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .empty, 114 /// SHT_GROUP sections 115 /// Applies only to a relocatable. 116 comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .empty, 117 118 copy_rel_section_index: ?u32 = null, 119 dynamic_section_index: ?u32 = null, 120 dynstrtab_section_index: ?u32 = null, 121 dynsymtab_section_index: ?u32 = null, 122 eh_frame_section_index: ?u32 = null, 123 eh_frame_rela_section_index: ?u32 = null, 124 eh_frame_hdr_section_index: ?u32 = null, 125 hash_section_index: ?u32 = null, 126 gnu_hash_section_index: ?u32 = null, 127 got_section_index: ?u32 = null, 128 got_plt_section_index: ?u32 = null, 129 interp_section_index: ?u32 = null, 130 plt_section_index: ?u32 = null, 131 plt_got_section_index: ?u32 = null, 132 rela_dyn_section_index: ?u32 = null, 133 rela_plt_section_index: ?u32 = null, 134 versym_section_index: ?u32 = null, 135 verneed_section_index: ?u32 = null, 136 137 shstrtab_section_index: ?u32 = null, 138 strtab_section_index: ?u32 = null, 139 symtab_section_index: ?u32 = null, 140 141 resolver: SymbolResolver = .{}, 142 143 has_text_reloc: bool = false, 144 num_ifunc_dynrelocs: usize = 0, 145 146 /// List of range extension thunks. 147 thunks: std.ArrayListUnmanaged(Thunk) = .empty, 148 149 /// List of output merge sections with deduped contents. 150 merge_sections: std.ArrayListUnmanaged(MergeSection) = .empty, 151 comment_merge_section_index: ?MergeSection.Index = null, 152 153 first_eflags: ?elf.Elf64_Word = null, 154 155 /// When allocating, the ideal_capacity is calculated by 156 /// actual_capacity + (actual_capacity / ideal_factor) 157 const ideal_factor = 3; 158 159 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at 160 /// it as a possible place to put new symbols, it must have enough room for this many bytes 161 /// (plus extra for reserved capacity). 162 const minimum_atom_size = 64; 163 pub const min_text_capacity = padToIdeal(minimum_atom_size); 164 165 pub const PtrWidth = enum { p32, p64 }; 166 pub const HashStyle = enum { sysv, gnu, both }; 167 pub const CompressDebugSections = enum { none, zlib, zstd }; 168 pub const SortSection = enum { name, alignment }; 169 170 pub fn createEmpty( 171 arena: Allocator, 172 comp: *Compilation, 173 emit: Path, 174 options: link.File.OpenOptions, 175 ) !*Elf { 176 const target = comp.root_mod.resolved_target.result; 177 assert(target.ofmt == .elf); 178 179 const use_lld = build_options.have_llvm and comp.config.use_lld; 180 const use_llvm = comp.config.use_llvm; 181 const opt_zcu = comp.zcu; 182 const output_mode = comp.config.output_mode; 183 const link_mode = comp.config.link_mode; 184 const optimize_mode = comp.root_mod.optimize_mode; 185 const is_native_os = comp.root_mod.resolved_target.is_native_os; 186 const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { 187 0...32 => .p32, 188 33...64 => .p64, 189 else => return error.UnsupportedELFArchitecture, 190 }; 191 192 // This is the max page size that the target system can run with, aka the ABI page size. Not to 193 // be confused with the common page size, which is the page size that's used in practice on most 194 // systems. 195 const page_size: u32 = switch (target.cpu.arch) { 196 .bpfel, 197 .bpfeb, 198 .sparc64, 199 => 0x100000, 200 .aarch64, 201 .aarch64_be, 202 .amdgcn, 203 .hexagon, 204 .mips, 205 .mipsel, 206 .mips64, 207 .mips64el, 208 .powerpc, 209 .powerpcle, 210 .powerpc64, 211 .powerpc64le, 212 .sparc, 213 => 0x10000, 214 .loongarch32, 215 .loongarch64, 216 => 0x4000, 217 .arc, 218 .m68k, 219 => 0x2000, 220 .msp430, 221 => 0x4, 222 .avr, 223 => 0x1, 224 else => 0x1000, 225 }; 226 227 const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic; 228 const default_sym_version: elf.Elf64_Versym = if (is_dyn_lib or comp.config.rdynamic) 229 elf.VER_NDX_GLOBAL 230 else 231 elf.VER_NDX_LOCAL; 232 233 // If using LLD to link, this code should produce an object file so that it 234 // can be passed to LLD. 235 // If using LLVM to generate the object file for the zig compilation unit, 236 // we need a place to put the object file so that it can be subsequently 237 // handled. 238 const zcu_object_sub_path = if (!use_lld and !use_llvm) 239 null 240 else 241 try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); 242 243 var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty; 244 try rpath_table.entries.resize(arena, options.rpath_list.len); 245 @memcpy(rpath_table.entries.items(.key), options.rpath_list); 246 try rpath_table.reIndex(arena); 247 248 const self = try arena.create(Elf); 249 self.* = .{ 250 .base = .{ 251 .tag = .elf, 252 .comp = comp, 253 .emit = emit, 254 .zcu_object_sub_path = zcu_object_sub_path, 255 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), 256 .print_gc_sections = options.print_gc_sections, 257 .stack_size = options.stack_size orelse 16777216, 258 .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os, 259 .file = null, 260 .disable_lld_caching = options.disable_lld_caching, 261 .build_id = options.build_id, 262 }, 263 .rpath_table = rpath_table, 264 .ptr_width = ptr_width, 265 .page_size = page_size, 266 .default_sym_version = default_sym_version, 267 268 .entry_name = switch (options.entry) { 269 .disabled => null, 270 .default => if (output_mode != .Exe) null else defaultEntrySymbolName(target.cpu.arch), 271 .enabled => defaultEntrySymbolName(target.cpu.arch), 272 .named => |name| name, 273 }, 274 275 .image_base = b: { 276 if (is_dyn_lib) break :b 0; 277 if (output_mode == .Exe and comp.config.pie) break :b 0; 278 break :b options.image_base orelse switch (ptr_width) { 279 .p32 => 0x10000, 280 .p64 => 0x1000000, 281 }; 282 }, 283 284 .emit_relocs = options.emit_relocs, 285 .z_nodelete = options.z_nodelete, 286 .z_notext = options.z_notext, 287 .z_defs = options.z_defs, 288 .z_origin = options.z_origin, 289 .z_nocopyreloc = options.z_nocopyreloc, 290 .z_now = options.z_now, 291 .z_relro = options.z_relro, 292 .z_common_page_size = options.z_common_page_size, 293 .z_max_page_size = options.z_max_page_size, 294 .lib_dirs = options.lib_dirs, 295 .hash_style = options.hash_style, 296 .compress_debug_sections = options.compress_debug_sections, 297 .symbol_wrap_set = options.symbol_wrap_set, 298 .sort_section = options.sort_section, 299 .soname = options.soname, 300 .bind_global_refs_locally = options.bind_global_refs_locally, 301 .linker_script = options.linker_script, 302 .version_script = options.version_script, 303 .allow_undefined_version = options.allow_undefined_version, 304 .enable_new_dtags = options.enable_new_dtags, 305 .print_icf_sections = options.print_icf_sections, 306 .print_map = options.print_map, 307 }; 308 if (use_llvm and comp.config.have_zcu) { 309 self.llvm_object = try LlvmObject.create(arena, comp); 310 } 311 errdefer self.base.destroy(); 312 313 if (use_lld and (use_llvm or !comp.config.have_zcu)) { 314 // LLVM emits the object file (if any); LLD links it into the final product. 315 return self; 316 } 317 318 const is_obj = output_mode == .Obj; 319 const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static); 320 321 // What path should this ELF linker code output to? 322 // If using LLD to link, this code should produce an object file so that it 323 // can be passed to LLD. 324 const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; 325 self.base.file = try emit.root_dir.handle.createFile(sub_path, .{ 326 .truncate = true, 327 .read = true, 328 .mode = link.File.determineMode(use_lld, output_mode, link_mode), 329 }); 330 331 const gpa = comp.gpa; 332 333 // Append null file at index 0 334 try self.files.append(gpa, .null); 335 // Append null byte to string tables 336 try self.shstrtab.append(gpa, 0); 337 try self.strtab.append(gpa, 0); 338 // There must always be a null shdr in index 0 339 _ = try self.addSection(.{}); 340 // Append null symbol in output symtab 341 try self.symtab.append(gpa, null_sym); 342 343 if (!is_obj_or_ar) { 344 try self.dynstrtab.append(gpa, 0); 345 346 // Initialize PT_PHDR program header 347 const p_align: u16 = switch (self.ptr_width) { 348 .p32 => @alignOf(elf.Elf32_Phdr), 349 .p64 => @alignOf(elf.Elf64_Phdr), 350 }; 351 const ehsize: u64 = switch (self.ptr_width) { 352 .p32 => @sizeOf(elf.Elf32_Ehdr), 353 .p64 => @sizeOf(elf.Elf64_Ehdr), 354 }; 355 const phsize: u64 = switch (self.ptr_width) { 356 .p32 => @sizeOf(elf.Elf32_Phdr), 357 .p64 => @sizeOf(elf.Elf64_Phdr), 358 }; 359 const max_nphdrs = comptime getMaxNumberOfPhdrs(); 360 const reserved: u64 = mem.alignForward(u64, padToIdeal(max_nphdrs * phsize), self.page_size); 361 self.phdr_table_index = try self.addPhdr(.{ 362 .type = elf.PT_PHDR, 363 .flags = elf.PF_R, 364 .@"align" = p_align, 365 .addr = self.image_base + ehsize, 366 .offset = ehsize, 367 .filesz = reserved, 368 .memsz = reserved, 369 }); 370 self.phdr_table_load_index = try self.addPhdr(.{ 371 .type = elf.PT_LOAD, 372 .flags = elf.PF_R, 373 .@"align" = self.page_size, 374 .addr = self.image_base, 375 .offset = 0, 376 .filesz = reserved + ehsize, 377 .memsz = reserved + ehsize, 378 }); 379 } 380 381 if (opt_zcu) |zcu| { 382 if (!use_llvm) { 383 const index: File.Index = @intCast(try self.files.addOne(gpa)); 384 self.files.set(index, .{ .zig_object = .{ 385 .index = index, 386 .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem( 387 zcu.main_mod.root_src_path, 388 )}), 389 } }); 390 self.zig_object_index = index; 391 try self.zigObjectPtr().?.init(self, .{ 392 .symbol_count_hint = options.symbol_count_hint, 393 .program_code_size_hint = options.program_code_size_hint, 394 }); 395 } 396 } 397 398 return self; 399 } 400 401 pub fn open( 402 arena: Allocator, 403 comp: *Compilation, 404 emit: Path, 405 options: link.File.OpenOptions, 406 ) !*Elf { 407 // TODO: restore saved linker state, don't truncate the file, and 408 // participate in incremental compilation. 409 return createEmpty(arena, comp, emit, options); 410 } 411 412 pub fn deinit(self: *Elf) void { 413 const gpa = self.base.comp.gpa; 414 415 if (self.llvm_object) |llvm_object| llvm_object.deinit(); 416 417 for (self.file_handles.items) |fh| { 418 fh.close(); 419 } 420 self.file_handles.deinit(gpa); 421 422 for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) { 423 .null => {}, 424 .zig_object => data.zig_object.deinit(gpa), 425 .linker_defined => data.linker_defined.deinit(gpa), 426 .object => data.object.deinit(gpa), 427 .shared_object => data.shared_object.deinit(gpa), 428 }; 429 self.files.deinit(gpa); 430 self.objects.deinit(gpa); 431 self.shared_objects.deinit(gpa); 432 433 for (self.sections.items(.atom_list_2), self.sections.items(.atom_list), self.sections.items(.free_list)) |*atom_list, *atoms, *free_list| { 434 atom_list.deinit(gpa); 435 atoms.deinit(gpa); 436 free_list.deinit(gpa); 437 } 438 self.sections.deinit(gpa); 439 self.phdrs.deinit(gpa); 440 self.shstrtab.deinit(gpa); 441 self.symtab.deinit(gpa); 442 self.strtab.deinit(gpa); 443 self.resolver.deinit(gpa); 444 445 for (self.thunks.items) |*th| { 446 th.deinit(gpa); 447 } 448 self.thunks.deinit(gpa); 449 for (self.merge_sections.items) |*sect| { 450 sect.deinit(gpa); 451 } 452 self.merge_sections.deinit(gpa); 453 454 self.got.deinit(gpa); 455 self.plt.deinit(gpa); 456 self.plt_got.deinit(gpa); 457 self.dynsym.deinit(gpa); 458 self.dynstrtab.deinit(gpa); 459 self.dynamic.deinit(gpa); 460 self.hash.deinit(gpa); 461 self.versym.deinit(gpa); 462 self.verneed.deinit(gpa); 463 self.copy_rel.deinit(gpa); 464 self.rela_dyn.deinit(gpa); 465 self.rela_plt.deinit(gpa); 466 self.comdat_group_sections.deinit(gpa); 467 } 468 469 pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 { 470 assert(self.llvm_object == null); 471 return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info); 472 } 473 474 pub fn lowerUav( 475 self: *Elf, 476 pt: Zcu.PerThread, 477 uav: InternPool.Index, 478 explicit_alignment: InternPool.Alignment, 479 src_loc: Zcu.LazySrcLoc, 480 ) !codegen.GenResult { 481 return self.zigObjectPtr().?.lowerUav(self, pt, uav, explicit_alignment, src_loc); 482 } 483 484 pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { 485 assert(self.llvm_object == null); 486 return self.zigObjectPtr().?.getUavVAddr(self, uav, reloc_info); 487 } 488 489 /// Returns end pos of collision, if any. 490 fn detectAllocCollision(self: *Elf, start: u64, size: u64) !?u64 { 491 const small_ptr = self.ptr_width == .p32; 492 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); 493 if (start < ehdr_size) 494 return ehdr_size; 495 496 var at_end = true; 497 const end = start + padToIdeal(size); 498 499 if (self.shdr_table_offset) |off| { 500 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr); 501 const tight_size = self.sections.items(.shdr).len * shdr_size; 502 const increased_size = padToIdeal(tight_size); 503 const test_end = off +| increased_size; 504 if (start < test_end) { 505 if (end > off) return test_end; 506 if (test_end < std.math.maxInt(u64)) at_end = false; 507 } 508 } 509 510 for (self.sections.items(.shdr)) |shdr| { 511 if (shdr.sh_type == elf.SHT_NOBITS) continue; 512 const increased_size = padToIdeal(shdr.sh_size); 513 const test_end = shdr.sh_offset +| increased_size; 514 if (start < test_end) { 515 if (end > shdr.sh_offset) return test_end; 516 if (test_end < std.math.maxInt(u64)) at_end = false; 517 } 518 } 519 520 for (self.phdrs.items) |phdr| { 521 if (phdr.p_type != elf.PT_LOAD) continue; 522 const increased_size = padToIdeal(phdr.p_filesz); 523 const test_end = phdr.p_offset +| increased_size; 524 if (start < test_end) { 525 if (end > phdr.p_offset) return test_end; 526 if (test_end < std.math.maxInt(u64)) at_end = false; 527 } 528 } 529 530 if (at_end) try self.base.file.?.setEndPos(end); 531 return null; 532 } 533 534 pub fn allocatedSize(self: *Elf, start: u64) u64 { 535 if (start == 0) return 0; 536 var min_pos: u64 = std.math.maxInt(u64); 537 if (self.shdr_table_offset) |off| { 538 if (off > start and off < min_pos) min_pos = off; 539 } 540 for (self.sections.items(.shdr)) |section| { 541 if (section.sh_offset <= start) continue; 542 if (section.sh_offset < min_pos) min_pos = section.sh_offset; 543 } 544 for (self.phdrs.items) |phdr| { 545 if (phdr.p_offset <= start) continue; 546 if (phdr.p_offset < min_pos) min_pos = phdr.p_offset; 547 } 548 return min_pos - start; 549 } 550 551 pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) !u64 { 552 var start: u64 = 0; 553 while (try self.detectAllocCollision(start, object_size)) |item_end| { 554 start = mem.alignForward(u64, item_end, min_alignment); 555 } 556 return start; 557 } 558 559 pub fn growSection(self: *Elf, shdr_index: u32, needed_size: u64, min_alignment: u64) !void { 560 const shdr = &self.sections.items(.shdr)[shdr_index]; 561 562 if (shdr.sh_type != elf.SHT_NOBITS) { 563 const allocated_size = self.allocatedSize(shdr.sh_offset); 564 log.debug("allocated size {x} of '{s}', needed size {x}", .{ 565 allocated_size, 566 self.getShString(shdr.sh_name), 567 needed_size, 568 }); 569 570 if (needed_size > allocated_size) { 571 const existing_size = shdr.sh_size; 572 shdr.sh_size = 0; 573 // Must move the entire section. 574 const new_offset = try self.findFreeSpace(needed_size, min_alignment); 575 576 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ 577 self.getShString(shdr.sh_name), 578 new_offset, 579 new_offset + existing_size, 580 }); 581 582 const amt = try self.base.file.?.copyRangeAll( 583 shdr.sh_offset, 584 self.base.file.?, 585 new_offset, 586 existing_size, 587 ); 588 // TODO figure out what to about this error condition - how to communicate it up. 589 if (amt != existing_size) return error.InputOutput; 590 591 shdr.sh_offset = new_offset; 592 } else if (shdr.sh_offset + allocated_size == std.math.maxInt(u64)) { 593 try self.base.file.?.setEndPos(shdr.sh_offset + needed_size); 594 } 595 } 596 597 shdr.sh_size = needed_size; 598 self.markDirty(shdr_index); 599 } 600 601 fn markDirty(self: *Elf, shdr_index: u32) void { 602 if (self.zigObjectPtr()) |zo| { 603 for ([_]?Symbol.Index{ 604 zo.debug_info_index, 605 zo.debug_abbrev_index, 606 zo.debug_aranges_index, 607 zo.debug_str_index, 608 zo.debug_line_index, 609 zo.debug_line_str_index, 610 zo.debug_loclists_index, 611 zo.debug_rnglists_index, 612 }, [_]*bool{ 613 &zo.debug_info_section_dirty, 614 &zo.debug_abbrev_section_dirty, 615 &zo.debug_aranges_section_dirty, 616 &zo.debug_str_section_dirty, 617 &zo.debug_line_section_dirty, 618 &zo.debug_line_str_section_dirty, 619 &zo.debug_loclists_section_dirty, 620 &zo.debug_rnglists_section_dirty, 621 }) |maybe_sym_index, dirty| { 622 const sym_index = maybe_sym_index orelse continue; 623 if (zo.symbol(sym_index).atom(self).?.output_section_index == shdr_index) { 624 dirty.* = true; 625 break; 626 } 627 } 628 } 629 } 630 631 const AllocateChunkResult = struct { 632 value: u64, 633 placement: Ref, 634 }; 635 636 pub fn allocateChunk(self: *Elf, args: struct { 637 size: u64, 638 shndx: u32, 639 alignment: Atom.Alignment, 640 requires_padding: bool = true, 641 }) !AllocateChunkResult { 642 const slice = self.sections.slice(); 643 const shdr = &slice.items(.shdr)[args.shndx]; 644 const free_list = &slice.items(.free_list)[args.shndx]; 645 const last_atom_ref = &slice.items(.last_atom)[args.shndx]; 646 const new_atom_ideal_capacity = if (args.requires_padding) padToIdeal(args.size) else args.size; 647 648 // First we look for an appropriately sized free list node. 649 // The list is unordered. We'll just take the first thing that works. 650 const res: AllocateChunkResult = blk: { 651 var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len; 652 while (i < free_list.items.len) { 653 const big_atom_ref = free_list.items[i]; 654 const big_atom = self.atom(big_atom_ref).?; 655 // We now have a pointer to a live atom that has too much capacity. 656 // Is it enough that we could fit this new atom? 657 const cap = big_atom.capacity(self); 658 const ideal_capacity = if (args.requires_padding) padToIdeal(cap) else cap; 659 const ideal_capacity_end_vaddr = std.math.add(u64, @intCast(big_atom.value), ideal_capacity) catch ideal_capacity; 660 const capacity_end_vaddr = @as(u64, @intCast(big_atom.value)) + cap; 661 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; 662 const new_start_vaddr = args.alignment.backward(new_start_vaddr_unaligned); 663 if (new_start_vaddr < ideal_capacity_end_vaddr) { 664 // Additional bookkeeping here to notice if this free list node 665 // should be deleted because the block that it points to has grown to take up 666 // more of the extra capacity. 667 if (!big_atom.freeListEligible(self)) { 668 _ = free_list.swapRemove(i); 669 } else { 670 i += 1; 671 } 672 continue; 673 } 674 // At this point we know that we will place the new block here. But the 675 // remaining question is whether there is still yet enough capacity left 676 // over for there to still be a free list node. 677 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; 678 const keep_free_list_node = remaining_capacity >= min_text_capacity; 679 680 if (!keep_free_list_node) { 681 _ = free_list.swapRemove(i); 682 } 683 break :blk .{ .value = new_start_vaddr, .placement = big_atom_ref }; 684 } else if (self.atom(last_atom_ref.*)) |last_atom| { 685 const ideal_capacity = if (args.requires_padding) padToIdeal(last_atom.size) else last_atom.size; 686 const ideal_capacity_end_vaddr = @as(u64, @intCast(last_atom.value)) + ideal_capacity; 687 const new_start_vaddr = args.alignment.forward(ideal_capacity_end_vaddr); 688 break :blk .{ .value = new_start_vaddr, .placement = last_atom.ref() }; 689 } else { 690 break :blk .{ .value = 0, .placement = .{} }; 691 } 692 }; 693 694 log.debug("allocated chunk (size({x}),align({x})) at 0x{x} (file(0x{x}))", .{ 695 args.size, 696 args.alignment.toByteUnits().?, 697 shdr.sh_addr + res.value, 698 shdr.sh_offset + res.value, 699 }); 700 701 const expand_section = if (self.atom(res.placement)) |placement_atom| 702 placement_atom.nextAtom(self) == null 703 else 704 true; 705 if (expand_section) { 706 const needed_size = res.value + args.size; 707 try self.growSection(args.shndx, needed_size, args.alignment.toByteUnits().?); 708 } 709 710 return res; 711 } 712 713 pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { 714 const use_lld = build_options.have_llvm and self.base.comp.config.use_lld; 715 if (use_lld) { 716 return self.linkWithLLD(arena, tid, prog_node); 717 } 718 try self.flushModule(arena, tid, prog_node); 719 } 720 721 pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void { 722 const tracy = trace(@src()); 723 defer tracy.end(); 724 725 const comp = self.base.comp; 726 const gpa = comp.gpa; 727 728 if (self.llvm_object) |llvm_object| { 729 try self.base.emitLlvmObject(arena, llvm_object, prog_node); 730 const use_lld = build_options.have_llvm and comp.config.use_lld; 731 if (use_lld) return; 732 } 733 734 const sub_prog_node = prog_node.start("ELF Flush", 0); 735 defer sub_prog_node.end(); 736 737 const target = self.getTarget(); 738 const link_mode = comp.config.link_mode; 739 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. 740 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { 741 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); 742 if (fs.path.dirname(full_out_path)) |dirname| { 743 break :blk try fs.path.join(arena, &.{ dirname, path }); 744 } else { 745 break :blk path; 746 } 747 } else null; 748 749 // --verbose-link 750 if (comp.verbose_link) try self.dumpArgv(comp); 751 752 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self, tid); 753 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); 754 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); 755 756 const csu = try CsuObjects.init(arena, comp); 757 758 // csu prelude 759 if (csu.crt0) |path| try parseObjectReportingFailure(self, path); 760 if (csu.crti) |path| try parseObjectReportingFailure(self, path); 761 if (csu.crtbegin) |path| try parseObjectReportingFailure(self, path); 762 763 for (comp.objects) |obj| { 764 if (obj.isObject()) { 765 try parseObjectReportingFailure(self, obj.path); 766 } else { 767 try parseLibraryReportingFailure(self, .{ .path = obj.path }, obj.must_link); 768 } 769 } 770 771 // This is a set of object files emitted by clang in a single `build-exe` invocation. 772 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up 773 // in this set. 774 for (comp.c_object_table.keys()) |key| { 775 try parseObjectReportingFailure(self, key.status.success.object_path); 776 } 777 778 if (module_obj_path) |path| try parseObjectReportingFailure(self, path); 779 780 if (comp.config.any_sanitize_thread) try parseCrtFileReportingFailure(self, comp.tsan_lib.?); 781 if (comp.config.any_fuzz) try parseCrtFileReportingFailure(self, comp.fuzzer_lib.?); 782 783 // libc 784 if (!comp.skip_linker_dependencies and !comp.config.link_libc) { 785 if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib); 786 } 787 788 var system_libs = std.ArrayList(SystemLib).init(arena); 789 790 try system_libs.ensureUnusedCapacity(comp.system_libs.values().len); 791 for (comp.system_libs.values()) |lib_info| { 792 system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? }); 793 } 794 795 // libc++ dep 796 if (comp.config.link_libcpp) { 797 try system_libs.ensureUnusedCapacity(2); 798 system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); 799 system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); 800 } 801 802 // libunwind dep 803 if (comp.config.link_libunwind) { 804 try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path }); 805 } 806 807 // libc dep 808 comp.link_error_flags.missing_libc = false; 809 if (comp.config.link_libc) { 810 if (comp.libc_installation) |lc| { 811 const flags = target_util.libcFullLinkFlags(target); 812 try system_libs.ensureUnusedCapacity(flags.len); 813 814 var test_path = std.ArrayList(u8).init(arena); 815 var checked_paths = std.ArrayList([]const u8).init(arena); 816 817 for (flags) |flag| { 818 checked_paths.clearRetainingCapacity(); 819 const lib_name = flag["-l".len..]; 820 821 success: { 822 if (!self.base.isStatic()) { 823 if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .dynamic)) 824 break :success; 825 } 826 if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .static)) 827 break :success; 828 829 try self.reportMissingLibraryError( 830 checked_paths.items, 831 "missing system library: '{s}' was not found", 832 .{lib_name}, 833 ); 834 835 continue; 836 } 837 838 const resolved_path = try arena.dupe(u8, test_path.items); 839 system_libs.appendAssumeCapacity(.{ .path = resolved_path }); 840 } 841 } else if (target.isGnuLibC()) { 842 try system_libs.ensureUnusedCapacity(glibc.libs.len + 1); 843 for (glibc.libs) |lib| { 844 if (lib.removed_in) |rem_in| { 845 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; 846 } 847 848 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ 849 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, 850 }); 851 system_libs.appendAssumeCapacity(.{ .path = lib_path }); 852 } 853 system_libs.appendAssumeCapacity(.{ 854 .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"), 855 }); 856 } else if (target.isMusl()) { 857 const path = try comp.get_libc_crt_file(arena, switch (link_mode) { 858 .static => "libc.a", 859 .dynamic => "libc.so", 860 }); 861 try system_libs.append(.{ .path = path }); 862 } else { 863 comp.link_error_flags.missing_libc = true; 864 } 865 } 866 867 for (system_libs.items) |lib| { 868 try self.parseLibraryReportingFailure(lib, false); 869 } 870 871 // Finally, as the last input objects we add compiler_rt and CSU postlude (if any). 872 873 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs 874 // to be after the shared libraries, so they are picked up from the shared 875 // libraries, not libcompiler_rt. 876 if (comp.compiler_rt_lib) |crt_file| { 877 try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false); 878 } else if (comp.compiler_rt_obj) |crt_file| { 879 try parseObjectReportingFailure(self, crt_file.full_object_path); 880 } 881 882 // csu postlude 883 if (csu.crtend) |path| try parseObjectReportingFailure(self, path); 884 if (csu.crtn) |path| try parseObjectReportingFailure(self, path); 885 886 if (self.base.hasErrors()) return error.FlushFailure; 887 888 // Dedup shared objects 889 { 890 var seen_dsos = std.StringHashMap(void).init(gpa); 891 defer seen_dsos.deinit(); 892 try seen_dsos.ensureTotalCapacity(@as(u32, @intCast(self.shared_objects.items.len))); 893 894 var i: usize = 0; 895 while (i < self.shared_objects.items.len) { 896 const index = self.shared_objects.items[i]; 897 const shared_object = self.file(index).?.shared_object; 898 const soname = shared_object.soname(); 899 const gop = seen_dsos.getOrPutAssumeCapacity(soname); 900 if (gop.found_existing) { 901 _ = self.shared_objects.orderedRemove(i); 902 } else i += 1; 903 } 904 } 905 906 // If we haven't already, create a linker-generated input file comprising of 907 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc. 908 if (self.linker_defined_index == null) { 909 const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); 910 self.files.set(index, .{ .linker_defined = .{ .index = index } }); 911 self.linker_defined_index = index; 912 const object = self.linkerDefinedPtr().?; 913 try object.init(gpa); 914 try object.initSymbols(self); 915 } 916 917 // Now, we are ready to resolve the symbols across all input files. 918 // We will first resolve the files in the ZigObject, next in the parsed 919 // input Object files. 920 // Any qualifing unresolved symbol will be upgraded to an absolute, weak 921 // symbol for potential resolution at load-time. 922 try self.resolveSymbols(); 923 self.markEhFrameAtomsDead(); 924 try self.resolveMergeSections(); 925 926 for (self.objects.items) |index| { 927 try self.file(index).?.object.convertCommonSymbols(self); 928 } 929 self.markImportsExports(); 930 931 if (self.base.gc_sections) { 932 try gc.gcAtoms(self); 933 934 if (self.base.print_gc_sections) { 935 try gc.dumpPrunedAtoms(self); 936 } 937 } 938 939 self.checkDuplicates() catch |err| switch (err) { 940 error.HasDuplicates => return error.FlushFailure, 941 else => |e| return e, 942 }; 943 944 try self.addCommentString(); 945 try self.finalizeMergeSections(); 946 try self.initOutputSections(); 947 if (self.linkerDefinedPtr()) |obj| { 948 try obj.initStartStopSymbols(self); 949 } 950 self.claimUnresolved(); 951 952 // Scan and create missing synthetic entries such as GOT indirection. 953 try self.scanRelocs(); 954 955 // Generate and emit synthetic sections. 956 try self.initSyntheticSections(); 957 try self.initSpecialPhdrs(); 958 try self.sortShdrs(); 959 960 try self.setDynamicSection(self.rpath_table.keys()); 961 self.sortDynamicSymtab(); 962 try self.setHashSections(); 963 try self.setVersionSymtab(); 964 965 try self.sortInitFini(); 966 try self.updateMergeSectionSizes(); 967 try self.updateSectionSizes(); 968 969 try self.addLoadPhdrs(); 970 try self.allocatePhdrTable(); 971 try self.allocateAllocSections(); 972 try self.sortPhdrs(); 973 try self.allocateNonAllocSections(); 974 self.allocateSpecialPhdrs(); 975 if (self.linkerDefinedPtr()) |obj| { 976 obj.allocateSymbols(self); 977 } 978 979 // Dump the state for easy debugging. 980 // State can be dumped via `--debug-log link_state`. 981 if (build_options.enable_logging) { 982 state_log.debug("{}", .{self.dumpState()}); 983 } 984 985 // Beyond this point, everything has been allocated a virtual address and we can resolve 986 // the relocations, and commit objects to file. 987 for (self.objects.items) |index| { 988 self.file(index).?.object.dirty = false; 989 } 990 // TODO: would state tracking be more appropriate here? perhaps even custom relocation type? 991 self.rela_dyn.clearRetainingCapacity(); 992 self.rela_plt.clearRetainingCapacity(); 993 994 if (self.zigObjectPtr()) |zo| { 995 var has_reloc_errors = false; 996 for (zo.atoms_indexes.items) |atom_index| { 997 const atom_ptr = zo.atom(atom_index) orelse continue; 998 if (!atom_ptr.alive) continue; 999 const out_shndx = atom_ptr.output_section_index; 1000 const shdr = &self.sections.items(.shdr)[out_shndx]; 1001 if (shdr.sh_type == elf.SHT_NOBITS) continue; 1002 const code = try zo.codeAlloc(self, atom_index); 1003 defer gpa.free(code); 1004 const file_offset = atom_ptr.offset(self); 1005 atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) { 1006 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, 1007 error.UnsupportedCpuArch => { 1008 try self.reportUnsupportedCpuArch(); 1009 return error.FlushFailure; 1010 }, 1011 else => |e| return e, 1012 }; 1013 try self.base.file.?.pwriteAll(code, file_offset); 1014 } 1015 1016 if (has_reloc_errors) return error.FlushFailure; 1017 } 1018 1019 try self.writePhdrTable(); 1020 try self.writeShdrTable(); 1021 try self.writeAtoms(); 1022 try self.writeMergeSections(); 1023 1024 self.writeSyntheticSections() catch |err| switch (err) { 1025 error.RelocFailure => return error.FlushFailure, 1026 error.UnsupportedCpuArch => { 1027 try self.reportUnsupportedCpuArch(); 1028 return error.FlushFailure; 1029 }, 1030 else => |e| return e, 1031 }; 1032 1033 if (self.base.isExe() and self.linkerDefinedPtr().?.entry_index == null) { 1034 log.debug("flushing. no_entry_point_found = true", .{}); 1035 comp.link_error_flags.no_entry_point_found = true; 1036 } else { 1037 log.debug("flushing. no_entry_point_found = false", .{}); 1038 comp.link_error_flags.no_entry_point_found = false; 1039 try self.writeElfHeader(); 1040 } 1041 1042 if (self.base.hasErrors()) return error.FlushFailure; 1043 } 1044 1045 /// --verbose-link output 1046 fn dumpArgv(self: *Elf, comp: *Compilation) !void { 1047 const gpa = self.base.comp.gpa; 1048 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 1049 defer arena_allocator.deinit(); 1050 const arena = arena_allocator.allocator(); 1051 1052 const target = self.getTarget(); 1053 const link_mode = self.base.comp.config.link_mode; 1054 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. 1055 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); 1056 const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { 1057 if (fs.path.dirname(full_out_path)) |dirname| { 1058 break :blk try fs.path.join(arena, &.{ dirname, path }); 1059 } else { 1060 break :blk path; 1061 } 1062 } else null; 1063 1064 const csu = try CsuObjects.init(arena, comp); 1065 const compiler_rt_path: ?[]const u8 = blk: { 1066 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; 1067 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; 1068 break :blk null; 1069 }; 1070 1071 var argv = std.ArrayList([]const u8).init(arena); 1072 1073 try argv.append("zig"); 1074 1075 if (self.base.isStaticLib()) { 1076 try argv.append("ar"); 1077 } else { 1078 try argv.append("ld"); 1079 } 1080 1081 if (self.base.isObject()) { 1082 try argv.append("-r"); 1083 } 1084 1085 try argv.append("-o"); 1086 try argv.append(full_out_path); 1087 1088 if (self.base.isRelocatable()) { 1089 for (comp.objects) |obj| { 1090 try argv.append(obj.path); 1091 } 1092 1093 for (comp.c_object_table.keys()) |key| { 1094 try argv.append(key.status.success.object_path); 1095 } 1096 1097 if (module_obj_path) |p| { 1098 try argv.append(p); 1099 } 1100 } else { 1101 if (!self.base.isStatic()) { 1102 if (target.dynamic_linker.get()) |path| { 1103 try argv.append("-dynamic-linker"); 1104 try argv.append(path); 1105 } 1106 } 1107 1108 if (self.base.isDynLib()) { 1109 if (self.soname) |name| { 1110 try argv.append("-soname"); 1111 try argv.append(name); 1112 } 1113 } 1114 1115 if (self.entry_name) |name| { 1116 try argv.appendSlice(&.{ "--entry", name }); 1117 } 1118 1119 for (self.rpath_table.keys()) |rpath| { 1120 try argv.appendSlice(&.{ "-rpath", rpath }); 1121 } 1122 1123 try argv.appendSlice(&.{ 1124 "-z", 1125 try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}), 1126 }); 1127 1128 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); 1129 1130 if (self.base.gc_sections) { 1131 try argv.append("--gc-sections"); 1132 } 1133 1134 if (self.base.print_gc_sections) { 1135 try argv.append("--print-gc-sections"); 1136 } 1137 1138 if (comp.link_eh_frame_hdr) { 1139 try argv.append("--eh-frame-hdr"); 1140 } 1141 1142 if (comp.config.rdynamic) { 1143 try argv.append("--export-dynamic"); 1144 } 1145 1146 if (self.z_notext) { 1147 try argv.append("-z"); 1148 try argv.append("notext"); 1149 } 1150 1151 if (self.z_nocopyreloc) { 1152 try argv.append("-z"); 1153 try argv.append("nocopyreloc"); 1154 } 1155 1156 if (self.z_now) { 1157 try argv.append("-z"); 1158 try argv.append("now"); 1159 } 1160 1161 if (self.base.isStatic()) { 1162 try argv.append("-static"); 1163 } else if (self.isEffectivelyDynLib()) { 1164 try argv.append("-shared"); 1165 } 1166 1167 if (comp.config.pie and self.base.isExe()) { 1168 try argv.append("-pie"); 1169 } 1170 1171 if (comp.config.debug_format == .strip) { 1172 try argv.append("-s"); 1173 } 1174 1175 // csu prelude 1176 if (csu.crt0) |v| try argv.append(v); 1177 if (csu.crti) |v| try argv.append(v); 1178 if (csu.crtbegin) |v| try argv.append(v); 1179 1180 for (self.lib_dirs) |lib_dir| { 1181 try argv.append("-L"); 1182 try argv.append(lib_dir); 1183 } 1184 1185 if (comp.config.link_libc) { 1186 if (self.base.comp.libc_installation) |libc_installation| { 1187 try argv.append("-L"); 1188 try argv.append(libc_installation.crt_dir.?); 1189 } 1190 } 1191 1192 var whole_archive = false; 1193 for (comp.objects) |obj| { 1194 if (obj.must_link and !whole_archive) { 1195 try argv.append("-whole-archive"); 1196 whole_archive = true; 1197 } else if (!obj.must_link and whole_archive) { 1198 try argv.append("-no-whole-archive"); 1199 whole_archive = false; 1200 } 1201 1202 if (obj.loption) { 1203 assert(obj.path[0] == ':'); 1204 try argv.append("-l"); 1205 } 1206 try argv.append(obj.path); 1207 } 1208 if (whole_archive) { 1209 try argv.append("-no-whole-archive"); 1210 whole_archive = false; 1211 } 1212 1213 for (comp.c_object_table.keys()) |key| { 1214 try argv.append(key.status.success.object_path); 1215 } 1216 1217 if (module_obj_path) |p| { 1218 try argv.append(p); 1219 } 1220 1221 if (comp.config.any_sanitize_thread) { 1222 try argv.append(comp.tsan_lib.?.full_object_path); 1223 } 1224 1225 if (comp.config.any_fuzz) { 1226 try argv.append(comp.fuzzer_lib.?.full_object_path); 1227 } 1228 1229 // libc 1230 if (!comp.skip_linker_dependencies and !comp.config.link_libc) { 1231 if (comp.libc_static_lib) |lib| { 1232 try argv.append(lib.full_object_path); 1233 } 1234 } 1235 1236 // Shared libraries. 1237 // Worst-case, we need an --as-needed argument for every lib, as well 1238 // as one before and one after. 1239 try argv.ensureUnusedCapacity(self.base.comp.system_libs.keys().len * 2 + 2); 1240 argv.appendAssumeCapacity("--as-needed"); 1241 var as_needed = true; 1242 1243 for (self.base.comp.system_libs.values()) |lib_info| { 1244 const lib_as_needed = !lib_info.needed; 1245 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { 1246 0b00, 0b11 => {}, 1247 0b01 => { 1248 argv.appendAssumeCapacity("--no-as-needed"); 1249 as_needed = false; 1250 }, 1251 0b10 => { 1252 argv.appendAssumeCapacity("--as-needed"); 1253 as_needed = true; 1254 }, 1255 } 1256 argv.appendAssumeCapacity(lib_info.path.?); 1257 } 1258 1259 if (!as_needed) { 1260 argv.appendAssumeCapacity("--as-needed"); 1261 as_needed = true; 1262 } 1263 1264 // libc++ dep 1265 if (comp.config.link_libcpp) { 1266 try argv.append(comp.libcxxabi_static_lib.?.full_object_path); 1267 try argv.append(comp.libcxx_static_lib.?.full_object_path); 1268 } 1269 1270 // libunwind dep 1271 if (comp.config.link_libunwind) { 1272 try argv.append(comp.libunwind_static_lib.?.full_object_path); 1273 } 1274 1275 // libc dep 1276 if (comp.config.link_libc) { 1277 if (self.base.comp.libc_installation != null) { 1278 const needs_grouping = link_mode == .static; 1279 if (needs_grouping) try argv.append("--start-group"); 1280 try argv.appendSlice(target_util.libcFullLinkFlags(target)); 1281 if (needs_grouping) try argv.append("--end-group"); 1282 } else if (target.isGnuLibC()) { 1283 for (glibc.libs) |lib| { 1284 if (lib.removed_in) |rem_in| { 1285 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; 1286 } 1287 1288 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ 1289 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, 1290 }); 1291 try argv.append(lib_path); 1292 } 1293 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); 1294 } else if (target.isMusl()) { 1295 try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { 1296 .static => "libc.a", 1297 .dynamic => "libc.so", 1298 })); 1299 } 1300 } 1301 1302 // compiler-rt 1303 if (compiler_rt_path) |p| { 1304 try argv.append(p); 1305 } 1306 1307 // crt postlude 1308 if (csu.crtend) |v| try argv.append(v); 1309 if (csu.crtn) |v| try argv.append(v); 1310 } 1311 1312 Compilation.dump_argv(argv.items); 1313 } 1314 1315 pub const ParseError = error{ 1316 /// Indicates the error is already reported on `Compilation.link_errors`. 1317 LinkFailure, 1318 1319 OutOfMemory, 1320 Overflow, 1321 InputOutput, 1322 EndOfStream, 1323 FileSystem, 1324 NotSupported, 1325 InvalidCharacter, 1326 UnknownFileType, 1327 } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; 1328 1329 fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error{OutOfMemory}!void { 1330 if (crt_file.isObject()) { 1331 try parseObjectReportingFailure(self, crt_file.full_object_path); 1332 } else { 1333 try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false); 1334 } 1335 } 1336 1337 pub fn parseObjectReportingFailure(self: *Elf, path: []const u8) error{OutOfMemory}!void { 1338 self.parseObject(path) catch |err| switch (err) { 1339 error.LinkFailure => return, // already reported 1340 error.OutOfMemory => return error.OutOfMemory, 1341 else => |e| try self.addParseError(path, "unable to parse object: {s}", .{@errorName(e)}), 1342 }; 1343 } 1344 1345 pub fn parseLibraryReportingFailure(self: *Elf, lib: SystemLib, must_link: bool) error{OutOfMemory}!void { 1346 self.parseLibrary(lib, must_link) catch |err| switch (err) { 1347 error.LinkFailure => return, // already reported 1348 error.OutOfMemory => return error.OutOfMemory, 1349 else => |e| try self.addParseError(lib.path, "unable to parse library: {s}", .{@errorName(e)}), 1350 }; 1351 } 1352 1353 fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void { 1354 const tracy = trace(@src()); 1355 defer tracy.end(); 1356 if (try Archive.isArchive(lib.path)) { 1357 try self.parseArchive(lib.path, must_link); 1358 } else if (try SharedObject.isSharedObject(lib.path)) { 1359 try self.parseSharedObject(lib); 1360 } else { 1361 try self.parseLdScript(lib); 1362 } 1363 } 1364 1365 fn parseObject(self: *Elf, path: []const u8) ParseError!void { 1366 const tracy = trace(@src()); 1367 defer tracy.end(); 1368 1369 const gpa = self.base.comp.gpa; 1370 const handle = try fs.cwd().openFile(path, .{}); 1371 const fh = try self.addFileHandle(handle); 1372 1373 const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); 1374 self.files.set(index, .{ .object = .{ 1375 .path = try gpa.dupe(u8, path), 1376 .file_handle = fh, 1377 .index = index, 1378 } }); 1379 try self.objects.append(gpa, index); 1380 1381 const object = self.file(index).?.object; 1382 try object.parse(self); 1383 } 1384 1385 fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void { 1386 const tracy = trace(@src()); 1387 defer tracy.end(); 1388 1389 const gpa = self.base.comp.gpa; 1390 const handle = try fs.cwd().openFile(path, .{}); 1391 const fh = try self.addFileHandle(handle); 1392 1393 var archive = Archive{}; 1394 defer archive.deinit(gpa); 1395 try archive.parse(self, path, fh); 1396 1397 const objects = try archive.objects.toOwnedSlice(gpa); 1398 defer gpa.free(objects); 1399 1400 for (objects) |extracted| { 1401 const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); 1402 self.files.set(index, .{ .object = extracted }); 1403 const object = &self.files.items(.data)[index].object; 1404 object.index = index; 1405 object.alive = must_link; 1406 try object.parse(self); 1407 try self.objects.append(gpa, index); 1408 } 1409 } 1410 1411 fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { 1412 const tracy = trace(@src()); 1413 defer tracy.end(); 1414 1415 const gpa = self.base.comp.gpa; 1416 const handle = try fs.cwd().openFile(lib.path, .{}); 1417 defer handle.close(); 1418 1419 const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); 1420 self.files.set(index, .{ .shared_object = .{ 1421 .path = try gpa.dupe(u8, lib.path), 1422 .index = index, 1423 .needed = lib.needed, 1424 .alive = lib.needed, 1425 } }); 1426 try self.shared_objects.append(gpa, index); 1427 1428 const shared_object = self.file(index).?.shared_object; 1429 try shared_object.parse(self, handle); 1430 } 1431 1432 fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { 1433 const tracy = trace(@src()); 1434 defer tracy.end(); 1435 1436 const gpa = self.base.comp.gpa; 1437 const in_file = try fs.cwd().openFile(lib.path, .{}); 1438 defer in_file.close(); 1439 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); 1440 defer gpa.free(data); 1441 1442 var script = LdScript{ .path = lib.path }; 1443 defer script.deinit(gpa); 1444 try script.parse(data, self); 1445 1446 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 1447 defer arena_allocator.deinit(); 1448 const arena = arena_allocator.allocator(); 1449 1450 var test_path = std.ArrayList(u8).init(arena); 1451 var checked_paths = std.ArrayList([]const u8).init(arena); 1452 1453 for (script.args.items) |scr_obj| { 1454 checked_paths.clearRetainingCapacity(); 1455 1456 success: { 1457 if (mem.startsWith(u8, scr_obj.path, "-l")) { 1458 const lib_name = scr_obj.path["-l".len..]; 1459 1460 // TODO I think technically we should re-use the mechanism used by the frontend here. 1461 // Maybe we should hoist search-strategy all the way here? 1462 for (self.lib_dirs) |lib_dir| { 1463 if (!self.base.isStatic()) { 1464 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .dynamic)) 1465 break :success; 1466 } 1467 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .static)) 1468 break :success; 1469 } 1470 } else { 1471 var buffer: [fs.max_path_bytes]u8 = undefined; 1472 if (fs.realpath(scr_obj.path, &buffer)) |path| { 1473 test_path.clearRetainingCapacity(); 1474 try test_path.writer().writeAll(path); 1475 break :success; 1476 } else |_| {} 1477 1478 try checked_paths.append(try arena.dupe(u8, scr_obj.path)); 1479 for (self.lib_dirs) |lib_dir| { 1480 if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, scr_obj.path, null)) 1481 break :success; 1482 } 1483 } 1484 1485 try self.reportMissingLibraryError( 1486 checked_paths.items, 1487 "missing library dependency: GNU ld script '{s}' requires '{s}', but file not found", 1488 .{ 1489 lib.path, 1490 scr_obj.path, 1491 }, 1492 ); 1493 continue; 1494 } 1495 1496 const full_path = test_path.items; 1497 self.parseLibrary(.{ 1498 .needed = scr_obj.needed, 1499 .path = full_path, 1500 }, false) catch |err| switch (err) { 1501 error.LinkFailure => continue, // already reported 1502 else => |e| try self.addParseError( 1503 full_path, 1504 "unexpected error: parsing library failed with error {s}", 1505 .{@errorName(e)}, 1506 ), 1507 }; 1508 } 1509 } 1510 1511 pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Word) !void { 1512 if (self.first_eflags == null) { 1513 self.first_eflags = e_flags; 1514 return; // there isn't anything to conflict with yet 1515 } 1516 const self_eflags: *elf.Elf64_Word = &self.first_eflags.?; 1517 1518 switch (self.getTarget().cpu.arch) { 1519 .riscv64 => { 1520 if (e_flags != self_eflags.*) { 1521 const riscv_eflags: riscv.RiscvEflags = @bitCast(e_flags); 1522 const self_riscv_eflags: *riscv.RiscvEflags = @ptrCast(self_eflags); 1523 1524 self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc; 1525 self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso; 1526 1527 var any_errors: bool = false; 1528 if (self_riscv_eflags.fabi != riscv_eflags.fabi) { 1529 any_errors = true; 1530 try self.addFileError( 1531 file_index, 1532 "cannot link object files with different float-point ABIs", 1533 .{}, 1534 ); 1535 } 1536 if (self_riscv_eflags.rve != riscv_eflags.rve) { 1537 any_errors = true; 1538 try self.addFileError( 1539 file_index, 1540 "cannot link object files with different RVEs", 1541 .{}, 1542 ); 1543 } 1544 if (any_errors) return error.LinkFailure; 1545 } 1546 }, 1547 else => {}, 1548 } 1549 } 1550 1551 fn accessLibPath( 1552 self: *Elf, 1553 arena: Allocator, 1554 test_path: *std.ArrayList(u8), 1555 checked_paths: ?*std.ArrayList([]const u8), 1556 lib_dir_path: []const u8, 1557 lib_name: []const u8, 1558 link_mode: ?std.builtin.LinkMode, 1559 ) !bool { 1560 const sep = fs.path.sep_str; 1561 const target = self.getTarget(); 1562 test_path.clearRetainingCapacity(); 1563 const prefix = if (link_mode != null) "lib" else ""; 1564 const suffix = if (link_mode) |mode| switch (mode) { 1565 .static => target.staticLibSuffix(), 1566 .dynamic => target.dynamicLibSuffix(), 1567 } else ""; 1568 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{ 1569 lib_dir_path, 1570 prefix, 1571 lib_name, 1572 suffix, 1573 }); 1574 if (checked_paths) |cpaths| { 1575 try cpaths.append(try arena.dupe(u8, test_path.items)); 1576 } 1577 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { 1578 error.FileNotFound => return false, 1579 else => |e| return e, 1580 }; 1581 return true; 1582 } 1583 1584 /// When resolving symbols, we approach the problem similarly to `mold`. 1585 /// 1. Resolve symbols across all objects (including those preemptively extracted archives). 1586 /// 2. Resolve symbols across all shared objects. 1587 /// 3. Mark live objects (see `Elf.markLive`) 1588 /// 4. Reset state of all resolved globals since we will redo this bit on the pruned set. 1589 /// 5. Remove references to dead objects/shared objects 1590 /// 6. Re-run symbol resolution on pruned objects and shared objects sets. 1591 pub fn resolveSymbols(self: *Elf) !void { 1592 // Resolve symbols in the ZigObject. For now, we assume that it's always live. 1593 if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self); 1594 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). 1595 for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self); 1596 for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self); 1597 if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); 1598 1599 // Mark live objects. 1600 self.markLive(); 1601 1602 // Reset state of all globals after marking live objects. 1603 self.resolver.reset(); 1604 1605 // Prune dead objects and shared objects. 1606 var i: usize = 0; 1607 while (i < self.objects.items.len) { 1608 const index = self.objects.items[i]; 1609 if (!self.file(index).?.isAlive()) { 1610 _ = self.objects.orderedRemove(i); 1611 } else i += 1; 1612 } 1613 i = 0; 1614 while (i < self.shared_objects.items.len) { 1615 const index = self.shared_objects.items[i]; 1616 if (!self.file(index).?.isAlive()) { 1617 _ = self.shared_objects.orderedRemove(i); 1618 } else i += 1; 1619 } 1620 1621 { 1622 // Dedup comdat groups. 1623 var table = std.StringHashMap(Ref).init(self.base.comp.gpa); 1624 defer table.deinit(); 1625 1626 for (self.objects.items) |index| { 1627 try self.file(index).?.object.resolveComdatGroups(self, &table); 1628 } 1629 1630 for (self.objects.items) |index| { 1631 self.file(index).?.object.markComdatGroupsDead(self); 1632 } 1633 } 1634 1635 // Re-resolve the symbols. 1636 if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self); 1637 for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self); 1638 for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self); 1639 if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self); 1640 } 1641 1642 /// Traverses all objects and shared objects marking any object referenced by 1643 /// a live object/shared object as alive itself. 1644 /// This routine will prune unneeded objects extracted from archives and 1645 /// unneeded shared objects. 1646 fn markLive(self: *Elf) void { 1647 if (self.zigObjectPtr()) |zig_object| zig_object.asFile().markLive(self); 1648 for (self.objects.items) |index| { 1649 const file_ptr = self.file(index).?; 1650 if (file_ptr.isAlive()) file_ptr.markLive(self); 1651 } 1652 for (self.shared_objects.items) |index| { 1653 const file_ptr = self.file(index).?; 1654 if (file_ptr.isAlive()) file_ptr.markLive(self); 1655 } 1656 } 1657 1658 pub fn markEhFrameAtomsDead(self: *Elf) void { 1659 for (self.objects.items) |index| { 1660 const file_ptr = self.file(index).?; 1661 if (!file_ptr.isAlive()) continue; 1662 file_ptr.object.markEhFrameAtomsDead(self); 1663 } 1664 } 1665 1666 fn markImportsExports(self: *Elf) void { 1667 if (self.zigObjectPtr()) |zo| { 1668 zo.markImportsExports(self); 1669 } 1670 for (self.objects.items) |index| { 1671 self.file(index).?.object.markImportsExports(self); 1672 } 1673 if (!self.isEffectivelyDynLib()) { 1674 for (self.shared_objects.items) |index| { 1675 self.file(index).?.shared_object.markImportExports(self); 1676 } 1677 } 1678 } 1679 1680 fn claimUnresolved(self: *Elf) void { 1681 if (self.zigObjectPtr()) |zig_object| { 1682 zig_object.claimUnresolved(self); 1683 } 1684 for (self.objects.items) |index| { 1685 self.file(index).?.object.claimUnresolved(self); 1686 } 1687 } 1688 1689 /// In scanRelocs we will go over all live atoms and scan their relocs. 1690 /// This will help us work out what synthetics to emit, GOT indirection, etc. 1691 /// This is also the point where we will report undefined symbols for any 1692 /// alloc sections. 1693 fn scanRelocs(self: *Elf) !void { 1694 const gpa = self.base.comp.gpa; 1695 1696 var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); 1697 defer { 1698 for (undefs.values()) |*refs| { 1699 refs.deinit(); 1700 } 1701 undefs.deinit(); 1702 } 1703 1704 var has_reloc_errors = false; 1705 if (self.zigObjectPtr()) |zo| { 1706 zo.asFile().scanRelocs(self, &undefs) catch |err| switch (err) { 1707 error.RelaxFailure => unreachable, 1708 error.UnsupportedCpuArch => { 1709 try self.reportUnsupportedCpuArch(); 1710 return error.FlushFailure; 1711 }, 1712 error.RelocFailure => has_reloc_errors = true, 1713 else => |e| return e, 1714 }; 1715 } 1716 for (self.objects.items) |index| { 1717 self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) { 1718 error.RelaxFailure => unreachable, 1719 error.UnsupportedCpuArch => { 1720 try self.reportUnsupportedCpuArch(); 1721 return error.FlushFailure; 1722 }, 1723 error.RelocFailure => has_reloc_errors = true, 1724 else => |e| return e, 1725 }; 1726 } 1727 1728 try self.reportUndefinedSymbols(&undefs); 1729 1730 if (has_reloc_errors) return error.FlushFailure; 1731 1732 if (self.zigObjectPtr()) |zo| { 1733 try zo.asFile().createSymbolIndirection(self); 1734 } 1735 for (self.objects.items) |index| { 1736 try self.file(index).?.createSymbolIndirection(self); 1737 } 1738 for (self.shared_objects.items) |index| { 1739 try self.file(index).?.createSymbolIndirection(self); 1740 } 1741 if (self.linkerDefinedPtr()) |obj| { 1742 try obj.asFile().createSymbolIndirection(self); 1743 } 1744 if (self.got.flags.needs_tlsld) { 1745 log.debug("program needs TLSLD", .{}); 1746 try self.got.addTlsLdSymbol(self); 1747 } 1748 } 1749 1750 pub fn initOutputSection(self: *Elf, args: struct { 1751 name: [:0]const u8, 1752 flags: u64, 1753 type: u32, 1754 }) error{OutOfMemory}!u32 { 1755 const name = blk: { 1756 if (self.base.isRelocatable()) break :blk args.name; 1757 if (args.flags & elf.SHF_MERGE != 0) break :blk args.name; 1758 const name_prefixes: []const [:0]const u8 = &.{ 1759 ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", 1760 ".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors", 1761 ".dtors", ".gnu.warning", 1762 }; 1763 inline for (name_prefixes) |prefix| { 1764 if (std.mem.eql(u8, args.name, prefix) or std.mem.startsWith(u8, args.name, prefix ++ ".")) { 1765 break :blk prefix; 1766 } 1767 } 1768 break :blk args.name; 1769 }; 1770 const @"type" = tt: { 1771 if (self.getTarget().cpu.arch == .x86_64 and args.type == elf.SHT_X86_64_UNWIND) 1772 break :tt elf.SHT_PROGBITS; 1773 switch (args.type) { 1774 elf.SHT_NULL => unreachable, 1775 elf.SHT_PROGBITS => { 1776 if (std.mem.eql(u8, args.name, ".init_array") or std.mem.startsWith(u8, args.name, ".init_array.")) 1777 break :tt elf.SHT_INIT_ARRAY; 1778 if (std.mem.eql(u8, args.name, ".fini_array") or std.mem.startsWith(u8, args.name, ".fini_array.")) 1779 break :tt elf.SHT_FINI_ARRAY; 1780 break :tt args.type; 1781 }, 1782 else => break :tt args.type, 1783 } 1784 }; 1785 const flags = blk: { 1786 var flags = args.flags; 1787 if (!self.base.isRelocatable()) { 1788 flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); 1789 } 1790 break :blk switch (@"type") { 1791 elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE, 1792 else => flags, 1793 }; 1794 }; 1795 const out_shndx = self.sectionByName(name) orelse try self.addSection(.{ 1796 .type = @"type", 1797 .flags = flags, 1798 .name = try self.insertShString(name), 1799 }); 1800 return out_shndx; 1801 } 1802 1803 fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { 1804 dev.check(.lld_linker); 1805 1806 const tracy = trace(@src()); 1807 defer tracy.end(); 1808 1809 const comp = self.base.comp; 1810 const gpa = comp.gpa; 1811 1812 const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. 1813 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); 1814 1815 // If there is no Zig code to compile, then we should skip flushing the output file because it 1816 // will not be part of the linker line anyway. 1817 const module_obj_path: ?[]const u8 = if (comp.zcu != null) blk: { 1818 try self.flushModule(arena, tid, prog_node); 1819 1820 if (fs.path.dirname(full_out_path)) |dirname| { 1821 break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? }); 1822 } else { 1823 break :blk self.base.zcu_object_sub_path.?; 1824 } 1825 } else null; 1826 1827 const sub_prog_node = prog_node.start("LLD Link", 0); 1828 defer sub_prog_node.end(); 1829 1830 const output_mode = comp.config.output_mode; 1831 const is_obj = output_mode == .Obj; 1832 const is_lib = output_mode == .Lib; 1833 const link_mode = comp.config.link_mode; 1834 const is_dyn_lib = link_mode == .dynamic and is_lib; 1835 const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe; 1836 const have_dynamic_linker = comp.config.link_libc and 1837 link_mode == .dynamic and is_exe_or_dyn_lib; 1838 const target = self.getTarget(); 1839 const compiler_rt_path: ?[]const u8 = blk: { 1840 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; 1841 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; 1842 break :blk null; 1843 }; 1844 1845 // Here we want to determine whether we can save time by not invoking LLD when the 1846 // output is unchanged. None of the linker options or the object files that are being 1847 // linked are in the hash that namespaces the directory we are outputting to. Therefore, 1848 // we must hash those now, and the resulting digest will form the "id" of the linking 1849 // job we are about to perform. 1850 // After a successful link, we store the id in the metadata of a symlink named "lld.id" in 1851 // the artifact directory. So, now, we check if this symlink exists, and if it matches 1852 // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. 1853 const id_symlink_basename = "lld.id"; 1854 1855 var man: Cache.Manifest = undefined; 1856 defer if (!self.base.disable_lld_caching) man.deinit(); 1857 1858 var digest: [Cache.hex_digest_len]u8 = undefined; 1859 1860 if (!self.base.disable_lld_caching) { 1861 man = comp.cache_parent.obtain(); 1862 1863 // We are about to obtain this lock, so here we give other processes a chance first. 1864 self.base.releaseLock(); 1865 1866 comptime assert(Compilation.link_hash_implementation_version == 14); 1867 1868 try man.addOptionalFile(self.linker_script); 1869 try man.addOptionalFile(self.version_script); 1870 man.hash.add(self.allow_undefined_version); 1871 man.hash.addOptional(self.enable_new_dtags); 1872 for (comp.objects) |obj| { 1873 _ = try man.addFile(obj.path, null); 1874 man.hash.add(obj.must_link); 1875 man.hash.add(obj.loption); 1876 } 1877 for (comp.c_object_table.keys()) |key| { 1878 _ = try man.addFile(key.status.success.object_path, null); 1879 } 1880 try man.addOptionalFile(module_obj_path); 1881 try man.addOptionalFile(compiler_rt_path); 1882 try man.addOptionalFile(if (comp.tsan_lib) |l| l.full_object_path else null); 1883 try man.addOptionalFile(if (comp.fuzzer_lib) |l| l.full_object_path else null); 1884 1885 // We can skip hashing libc and libc++ components that we are in charge of building from Zig 1886 // installation sources because they are always a product of the compiler version + target information. 1887 man.hash.addOptionalBytes(self.entry_name); 1888 man.hash.add(self.image_base); 1889 man.hash.add(self.base.gc_sections); 1890 man.hash.addOptional(self.sort_section); 1891 man.hash.add(comp.link_eh_frame_hdr); 1892 man.hash.add(self.emit_relocs); 1893 man.hash.add(comp.config.rdynamic); 1894 man.hash.addListOfBytes(self.lib_dirs); 1895 man.hash.addListOfBytes(self.rpath_table.keys()); 1896 if (output_mode == .Exe) { 1897 man.hash.add(self.base.stack_size); 1898 man.hash.add(self.base.build_id); 1899 } 1900 man.hash.addListOfBytes(self.symbol_wrap_set.keys()); 1901 man.hash.add(comp.skip_linker_dependencies); 1902 man.hash.add(self.z_nodelete); 1903 man.hash.add(self.z_notext); 1904 man.hash.add(self.z_defs); 1905 man.hash.add(self.z_origin); 1906 man.hash.add(self.z_nocopyreloc); 1907 man.hash.add(self.z_now); 1908 man.hash.add(self.z_relro); 1909 man.hash.add(self.z_common_page_size orelse 0); 1910 man.hash.add(self.z_max_page_size orelse 0); 1911 man.hash.add(self.hash_style); 1912 // strip does not need to go into the linker hash because it is part of the hash namespace 1913 if (comp.config.link_libc) { 1914 man.hash.add(comp.libc_installation != null); 1915 if (comp.libc_installation) |libc_installation| { 1916 man.hash.addBytes(libc_installation.crt_dir.?); 1917 } 1918 if (have_dynamic_linker) { 1919 man.hash.addOptionalBytes(target.dynamic_linker.get()); 1920 } 1921 } 1922 man.hash.addOptionalBytes(self.soname); 1923 man.hash.addOptional(comp.version); 1924 try link.hashAddSystemLibs(&man, comp.system_libs); 1925 man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); 1926 man.hash.add(self.base.allow_shlib_undefined); 1927 man.hash.add(self.bind_global_refs_locally); 1928 man.hash.add(self.compress_debug_sections); 1929 man.hash.add(comp.config.any_sanitize_thread); 1930 man.hash.add(comp.config.any_fuzz); 1931 man.hash.addOptionalBytes(comp.sysroot); 1932 1933 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. 1934 _ = try man.hit(); 1935 digest = man.final(); 1936 1937 var prev_digest_buf: [digest.len]u8 = undefined; 1938 const prev_digest: []u8 = Cache.readSmallFile( 1939 directory.handle, 1940 id_symlink_basename, 1941 &prev_digest_buf, 1942 ) catch |err| blk: { 1943 log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); 1944 // Handle this as a cache miss. 1945 break :blk prev_digest_buf[0..0]; 1946 }; 1947 if (mem.eql(u8, prev_digest, &digest)) { 1948 log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); 1949 // Hot diggity dog! The output binary is already there. 1950 self.base.lock = man.toOwnedLock(); 1951 return; 1952 } 1953 log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); 1954 1955 // We are about to change the output file to be different, so we invalidate the build hash now. 1956 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { 1957 error.FileNotFound => {}, 1958 else => |e| return e, 1959 }; 1960 } 1961 1962 // Due to a deficiency in LLD, we need to special-case BPF to a simple file 1963 // copy when generating relocatables. Normally, we would expect `lld -r` to work. 1964 // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails 1965 // before even generating the relocatable. 1966 if (output_mode == .Obj and 1967 (comp.config.lto or target.isBpfFreestanding())) 1968 { 1969 // In this case we must do a simple file copy 1970 // here. TODO: think carefully about how we can avoid this redundant operation when doing 1971 // build-obj. See also the corresponding TODO in linkAsArchive. 1972 const the_object_path = blk: { 1973 if (comp.objects.len != 0) 1974 break :blk comp.objects[0].path; 1975 1976 if (comp.c_object_table.count() != 0) 1977 break :blk comp.c_object_table.keys()[0].status.success.object_path; 1978 1979 if (module_obj_path) |p| 1980 break :blk p; 1981 1982 // TODO I think this is unreachable. Audit this situation when solving the above TODO 1983 // regarding eliding redundant object -> object transformations. 1984 return error.NoObjectsToLink; 1985 }; 1986 // This can happen when using --enable-cache and using the stage1 backend. In this case 1987 // we can skip the file copy. 1988 if (!mem.eql(u8, the_object_path, full_out_path)) { 1989 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); 1990 } 1991 } else { 1992 // Create an LLD command line and invoke it. 1993 var argv = std.ArrayList([]const u8).init(gpa); 1994 defer argv.deinit(); 1995 // We will invoke ourselves as a child process to gain access to LLD. 1996 // This is necessary because LLD does not behave properly as a library - 1997 // it calls exit() and does not reset all global data between invocations. 1998 const linker_command = "ld.lld"; 1999 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); 2000 if (is_obj) { 2001 try argv.append("-r"); 2002 } 2003 2004 try argv.append("--error-limit=0"); 2005 2006 if (comp.sysroot) |sysroot| { 2007 try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot})); 2008 } 2009 2010 if (comp.config.lto) { 2011 switch (comp.root_mod.optimize_mode) { 2012 .Debug => {}, 2013 .ReleaseSmall => try argv.append("--lto-O2"), 2014 .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"), 2015 } 2016 } 2017 switch (comp.root_mod.optimize_mode) { 2018 .Debug => {}, 2019 .ReleaseSmall => try argv.append("-O2"), 2020 .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), 2021 } 2022 2023 if (self.entry_name) |name| { 2024 try argv.appendSlice(&.{ "--entry", name }); 2025 } 2026 2027 for (comp.force_undefined_symbols.keys()) |sym| { 2028 try argv.append("-u"); 2029 try argv.append(sym); 2030 } 2031 2032 switch (self.hash_style) { 2033 .gnu => try argv.append("--hash-style=gnu"), 2034 .sysv => try argv.append("--hash-style=sysv"), 2035 .both => {}, // this is the default 2036 } 2037 2038 if (output_mode == .Exe) { 2039 try argv.appendSlice(&.{ 2040 "-z", 2041 try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}), 2042 }); 2043 2044 switch (self.base.build_id) { 2045 .none => {}, 2046 .fast, .uuid, .sha1, .md5 => { 2047 try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ 2048 @tagName(self.base.build_id), 2049 })); 2050 }, 2051 .hexstring => |hs| { 2052 try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{ 2053 std.fmt.fmtSliceHexLower(hs.toSlice()), 2054 })); 2055 }, 2056 } 2057 } 2058 2059 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); 2060 2061 if (self.linker_script) |linker_script| { 2062 try argv.append("-T"); 2063 try argv.append(linker_script); 2064 } 2065 2066 if (self.sort_section) |how| { 2067 const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)}); 2068 try argv.append(arg); 2069 } 2070 2071 if (self.base.gc_sections) { 2072 try argv.append("--gc-sections"); 2073 } 2074 2075 if (self.base.print_gc_sections) { 2076 try argv.append("--print-gc-sections"); 2077 } 2078 2079 if (self.print_icf_sections) { 2080 try argv.append("--print-icf-sections"); 2081 } 2082 2083 if (self.print_map) { 2084 try argv.append("--print-map"); 2085 } 2086 2087 if (comp.link_eh_frame_hdr) { 2088 try argv.append("--eh-frame-hdr"); 2089 } 2090 2091 if (self.emit_relocs) { 2092 try argv.append("--emit-relocs"); 2093 } 2094 2095 if (comp.config.rdynamic) { 2096 try argv.append("--export-dynamic"); 2097 } 2098 2099 if (comp.config.debug_format == .strip) { 2100 try argv.append("-s"); 2101 } 2102 2103 if (self.z_nodelete) { 2104 try argv.append("-z"); 2105 try argv.append("nodelete"); 2106 } 2107 if (self.z_notext) { 2108 try argv.append("-z"); 2109 try argv.append("notext"); 2110 } 2111 if (self.z_defs) { 2112 try argv.append("-z"); 2113 try argv.append("defs"); 2114 } 2115 if (self.z_origin) { 2116 try argv.append("-z"); 2117 try argv.append("origin"); 2118 } 2119 if (self.z_nocopyreloc) { 2120 try argv.append("-z"); 2121 try argv.append("nocopyreloc"); 2122 } 2123 if (self.z_now) { 2124 // LLD defaults to -zlazy 2125 try argv.append("-znow"); 2126 } 2127 if (!self.z_relro) { 2128 // LLD defaults to -zrelro 2129 try argv.append("-znorelro"); 2130 } 2131 if (self.z_common_page_size) |size| { 2132 try argv.append("-z"); 2133 try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size})); 2134 } 2135 if (self.z_max_page_size) |size| { 2136 try argv.append("-z"); 2137 try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size})); 2138 } 2139 2140 if (getLDMOption(target)) |ldm| { 2141 try argv.append("-m"); 2142 try argv.append(ldm); 2143 } 2144 2145 if (link_mode == .static) { 2146 if (target.cpu.arch.isArmOrThumb()) { 2147 try argv.append("-Bstatic"); 2148 } else { 2149 try argv.append("-static"); 2150 } 2151 } else if (switch (target.os.tag) { 2152 else => is_dyn_lib, 2153 .haiku => is_exe_or_dyn_lib, 2154 }) { 2155 try argv.append("-shared"); 2156 } 2157 2158 if (comp.config.pie and output_mode == .Exe) { 2159 try argv.append("-pie"); 2160 } 2161 2162 if (is_exe_or_dyn_lib and target.os.tag == .netbsd) { 2163 // Add options to produce shared objects with only 2 PT_LOAD segments. 2164 // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise 2165 // ld.elf_so fails loading dynamic libraries with "not found" error. 2166 // See https://github.com/ziglang/zig/issues/9109 . 2167 try argv.append("--no-rosegment"); 2168 try argv.append("-znorelro"); 2169 } 2170 2171 try argv.append("-o"); 2172 try argv.append(full_out_path); 2173 2174 // csu prelude 2175 const csu = try CsuObjects.init(arena, comp); 2176 if (csu.crt0) |v| try argv.append(v); 2177 if (csu.crti) |v| try argv.append(v); 2178 if (csu.crtbegin) |v| try argv.append(v); 2179 2180 for (self.rpath_table.keys()) |rpath| { 2181 try argv.appendSlice(&.{ "-rpath", rpath }); 2182 } 2183 2184 for (self.symbol_wrap_set.keys()) |symbol_name| { 2185 try argv.appendSlice(&.{ "-wrap", symbol_name }); 2186 } 2187 2188 for (self.lib_dirs) |lib_dir| { 2189 try argv.append("-L"); 2190 try argv.append(lib_dir); 2191 } 2192 2193 if (comp.config.link_libc) { 2194 if (comp.libc_installation) |libc_installation| { 2195 try argv.append("-L"); 2196 try argv.append(libc_installation.crt_dir.?); 2197 } 2198 2199 if (have_dynamic_linker) { 2200 if (target.dynamic_linker.get()) |dynamic_linker| { 2201 try argv.append("-dynamic-linker"); 2202 try argv.append(dynamic_linker); 2203 } 2204 } 2205 } 2206 2207 if (is_dyn_lib) { 2208 if (self.soname) |soname| { 2209 try argv.append("-soname"); 2210 try argv.append(soname); 2211 } 2212 if (self.version_script) |version_script| { 2213 try argv.append("-version-script"); 2214 try argv.append(version_script); 2215 } 2216 if (self.allow_undefined_version) { 2217 try argv.append("--undefined-version"); 2218 } else { 2219 try argv.append("--no-undefined-version"); 2220 } 2221 if (self.enable_new_dtags) |enable_new_dtags| { 2222 if (enable_new_dtags) { 2223 try argv.append("--enable-new-dtags"); 2224 } else { 2225 try argv.append("--disable-new-dtags"); 2226 } 2227 } 2228 } 2229 2230 // Positional arguments to the linker such as object files. 2231 var whole_archive = false; 2232 for (comp.objects) |obj| { 2233 if (obj.must_link and !whole_archive) { 2234 try argv.append("-whole-archive"); 2235 whole_archive = true; 2236 } else if (!obj.must_link and whole_archive) { 2237 try argv.append("-no-whole-archive"); 2238 whole_archive = false; 2239 } 2240 2241 if (obj.loption) { 2242 assert(obj.path[0] == ':'); 2243 try argv.append("-l"); 2244 } 2245 try argv.append(obj.path); 2246 } 2247 if (whole_archive) { 2248 try argv.append("-no-whole-archive"); 2249 whole_archive = false; 2250 } 2251 2252 for (comp.c_object_table.keys()) |key| { 2253 try argv.append(key.status.success.object_path); 2254 } 2255 2256 if (module_obj_path) |p| { 2257 try argv.append(p); 2258 } 2259 2260 if (comp.tsan_lib) |lib| { 2261 assert(comp.config.any_sanitize_thread); 2262 try argv.append(lib.full_object_path); 2263 } 2264 2265 if (comp.fuzzer_lib) |lib| { 2266 assert(comp.config.any_fuzz); 2267 try argv.append(lib.full_object_path); 2268 } 2269 2270 // libc 2271 if (is_exe_or_dyn_lib and 2272 !comp.skip_linker_dependencies and 2273 !comp.config.link_libc) 2274 { 2275 if (comp.libc_static_lib) |lib| { 2276 try argv.append(lib.full_object_path); 2277 } 2278 } 2279 2280 // Shared libraries. 2281 if (is_exe_or_dyn_lib) { 2282 const system_libs = comp.system_libs.keys(); 2283 const system_libs_values = comp.system_libs.values(); 2284 2285 // Worst-case, we need an --as-needed argument for every lib, as well 2286 // as one before and one after. 2287 try argv.ensureUnusedCapacity(system_libs.len * 2 + 2); 2288 argv.appendAssumeCapacity("--as-needed"); 2289 var as_needed = true; 2290 2291 for (system_libs_values) |lib_info| { 2292 const lib_as_needed = !lib_info.needed; 2293 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { 2294 0b00, 0b11 => {}, 2295 0b01 => { 2296 argv.appendAssumeCapacity("--no-as-needed"); 2297 as_needed = false; 2298 }, 2299 0b10 => { 2300 argv.appendAssumeCapacity("--as-needed"); 2301 as_needed = true; 2302 }, 2303 } 2304 2305 // By this time, we depend on these libs being dynamically linked 2306 // libraries and not static libraries (the check for that needs to be earlier), 2307 // but they could be full paths to .so files, in which case we 2308 // want to avoid prepending "-l". 2309 argv.appendAssumeCapacity(lib_info.path.?); 2310 } 2311 2312 if (!as_needed) { 2313 argv.appendAssumeCapacity("--as-needed"); 2314 as_needed = true; 2315 } 2316 2317 // libc++ dep 2318 if (comp.config.link_libcpp) { 2319 try argv.append(comp.libcxxabi_static_lib.?.full_object_path); 2320 try argv.append(comp.libcxx_static_lib.?.full_object_path); 2321 } 2322 2323 // libunwind dep 2324 if (comp.config.link_libunwind) { 2325 try argv.append(comp.libunwind_static_lib.?.full_object_path); 2326 } 2327 2328 // libc dep 2329 comp.link_error_flags.missing_libc = false; 2330 if (comp.config.link_libc) { 2331 if (comp.libc_installation != null) { 2332 const needs_grouping = link_mode == .static; 2333 if (needs_grouping) try argv.append("--start-group"); 2334 try argv.appendSlice(target_util.libcFullLinkFlags(target)); 2335 if (needs_grouping) try argv.append("--end-group"); 2336 } else if (target.isGnuLibC()) { 2337 for (glibc.libs) |lib| { 2338 if (lib.removed_in) |rem_in| { 2339 if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; 2340 } 2341 2342 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ 2343 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, 2344 }); 2345 try argv.append(lib_path); 2346 } 2347 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); 2348 } else if (target.isMusl()) { 2349 try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { 2350 .static => "libc.a", 2351 .dynamic => "libc.so", 2352 })); 2353 } else { 2354 comp.link_error_flags.missing_libc = true; 2355 } 2356 } 2357 } 2358 2359 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs 2360 // to be after the shared libraries, so they are picked up from the shared 2361 // libraries, not libcompiler_rt. 2362 if (compiler_rt_path) |p| { 2363 try argv.append(p); 2364 } 2365 2366 // crt postlude 2367 if (csu.crtend) |v| try argv.append(v); 2368 if (csu.crtn) |v| try argv.append(v); 2369 2370 if (self.base.allow_shlib_undefined) { 2371 try argv.append("--allow-shlib-undefined"); 2372 } 2373 2374 switch (self.compress_debug_sections) { 2375 .none => {}, 2376 .zlib => try argv.append("--compress-debug-sections=zlib"), 2377 .zstd => try argv.append("--compress-debug-sections=zstd"), 2378 } 2379 2380 if (self.bind_global_refs_locally) { 2381 try argv.append("-Bsymbolic"); 2382 } 2383 2384 try link.spawnLld(comp, arena, argv.items); 2385 } 2386 2387 if (!self.base.disable_lld_caching) { 2388 // Update the file with the digest. If it fails we can continue; it only 2389 // means that the next invocation will have an unnecessary cache miss. 2390 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { 2391 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); 2392 }; 2393 // Again failure here only means an unnecessary cache miss. 2394 man.writeManifest() catch |err| { 2395 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); 2396 }; 2397 // We hang on to this lock so that the output file path can be used without 2398 // other processes clobbering it. 2399 self.base.lock = man.toOwnedLock(); 2400 } 2401 } 2402 2403 pub fn writeShdrTable(self: *Elf) !void { 2404 const gpa = self.base.comp.gpa; 2405 const target_endian = self.getTarget().cpu.arch.endian(); 2406 const foreign_endian = target_endian != builtin.cpu.arch.endian(); 2407 const shsize: u64 = switch (self.ptr_width) { 2408 .p32 => @sizeOf(elf.Elf32_Shdr), 2409 .p64 => @sizeOf(elf.Elf64_Shdr), 2410 }; 2411 const shalign: u16 = switch (self.ptr_width) { 2412 .p32 => @alignOf(elf.Elf32_Shdr), 2413 .p64 => @alignOf(elf.Elf64_Shdr), 2414 }; 2415 2416 const shoff = self.shdr_table_offset orelse 0; 2417 const needed_size = self.sections.items(.shdr).len * shsize; 2418 2419 if (needed_size > self.allocatedSize(shoff)) { 2420 self.shdr_table_offset = null; 2421 self.shdr_table_offset = try self.findFreeSpace(needed_size, shalign); 2422 } 2423 2424 log.debug("writing section headers from 0x{x} to 0x{x}", .{ 2425 self.shdr_table_offset.?, 2426 self.shdr_table_offset.? + needed_size, 2427 }); 2428 2429 switch (self.ptr_width) { 2430 .p32 => { 2431 const buf = try gpa.alloc(elf.Elf32_Shdr, self.sections.items(.shdr).len); 2432 defer gpa.free(buf); 2433 2434 for (buf, 0..) |*shdr, i| { 2435 assert(self.sections.items(.shdr)[i].sh_offset != math.maxInt(u64)); 2436 shdr.* = shdrTo32(self.sections.items(.shdr)[i]); 2437 if (foreign_endian) { 2438 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); 2439 } 2440 } 2441 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); 2442 }, 2443 .p64 => { 2444 const buf = try gpa.alloc(elf.Elf64_Shdr, self.sections.items(.shdr).len); 2445 defer gpa.free(buf); 2446 2447 for (buf, 0..) |*shdr, i| { 2448 assert(self.sections.items(.shdr)[i].sh_offset != math.maxInt(u64)); 2449 shdr.* = self.sections.items(.shdr)[i]; 2450 if (foreign_endian) { 2451 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); 2452 } 2453 } 2454 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); 2455 }, 2456 } 2457 } 2458 2459 fn writePhdrTable(self: *Elf) !void { 2460 const gpa = self.base.comp.gpa; 2461 const target_endian = self.getTarget().cpu.arch.endian(); 2462 const foreign_endian = target_endian != builtin.cpu.arch.endian(); 2463 const phdr_table = &self.phdrs.items[self.phdr_table_index.?]; 2464 2465 log.debug("writing program headers from 0x{x} to 0x{x}", .{ 2466 phdr_table.p_offset, 2467 phdr_table.p_offset + phdr_table.p_filesz, 2468 }); 2469 2470 switch (self.ptr_width) { 2471 .p32 => { 2472 const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len); 2473 defer gpa.free(buf); 2474 2475 for (buf, 0..) |*phdr, i| { 2476 phdr.* = phdrTo32(self.phdrs.items[i]); 2477 if (foreign_endian) { 2478 mem.byteSwapAllFields(elf.Elf32_Phdr, phdr); 2479 } 2480 } 2481 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); 2482 }, 2483 .p64 => { 2484 const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len); 2485 defer gpa.free(buf); 2486 2487 for (buf, 0..) |*phdr, i| { 2488 phdr.* = self.phdrs.items[i]; 2489 if (foreign_endian) { 2490 mem.byteSwapAllFields(elf.Elf64_Phdr, phdr); 2491 } 2492 } 2493 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset); 2494 }, 2495 } 2496 } 2497 2498 pub fn writeElfHeader(self: *Elf) !void { 2499 if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable 2500 2501 const comp = self.base.comp; 2502 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; 2503 2504 var index: usize = 0; 2505 hdr_buf[0..4].* = elf.MAGIC.*; 2506 index += 4; 2507 2508 hdr_buf[index] = switch (self.ptr_width) { 2509 .p32 => elf.ELFCLASS32, 2510 .p64 => elf.ELFCLASS64, 2511 }; 2512 index += 1; 2513 2514 const target = self.getTarget(); 2515 const endian = target.cpu.arch.endian(); 2516 hdr_buf[index] = switch (endian) { 2517 .little => elf.ELFDATA2LSB, 2518 .big => elf.ELFDATA2MSB, 2519 }; 2520 index += 1; 2521 2522 hdr_buf[index] = 1; // ELF version 2523 index += 1; 2524 2525 hdr_buf[index] = @intFromEnum(@as(elf.OSABI, switch (target.cpu.arch) { 2526 .amdgcn => switch (target.os.tag) { 2527 .amdhsa => .AMDGPU_HSA, 2528 .amdpal => .AMDGPU_PAL, 2529 .mesa3d => .AMDGPU_MESA3D, 2530 else => .NONE, 2531 }, 2532 .msp430 => .STANDALONE, 2533 else => switch (target.os.tag) { 2534 .freebsd, .ps4 => .FREEBSD, 2535 .hermit => .STANDALONE, 2536 .illumos, .solaris => .SOLARIS, 2537 .openbsd => .OPENBSD, 2538 else => .NONE, 2539 }, 2540 })); 2541 index += 1; 2542 2543 // ABI Version, possibly used by glibc but not by static executables 2544 // padding 2545 @memset(hdr_buf[index..][0..8], 0); 2546 index += 8; 2547 2548 assert(index == 16); 2549 2550 const output_mode = comp.config.output_mode; 2551 const link_mode = comp.config.link_mode; 2552 const elf_type: elf.ET = switch (output_mode) { 2553 .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC, 2554 .Obj => .REL, 2555 .Lib => switch (link_mode) { 2556 .static => @as(elf.ET, .REL), 2557 .dynamic => .DYN, 2558 }, 2559 }; 2560 mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(elf_type), endian); 2561 index += 2; 2562 2563 const machine = target.toElfMachine(); 2564 mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(machine), endian); 2565 index += 2; 2566 2567 // ELF Version, again 2568 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); 2569 index += 4; 2570 2571 const e_entry: u64 = if (self.linkerDefinedPtr()) |obj| blk: { 2572 const entry_sym = obj.entrySymbol(self) orelse break :blk 0; 2573 break :blk @intCast(entry_sym.address(.{}, self)); 2574 } else 0; 2575 const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0; 2576 switch (self.ptr_width) { 2577 .p32 => { 2578 mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian); 2579 index += 4; 2580 2581 // e_phoff 2582 mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(phdr_table_offset)), endian); 2583 index += 4; 2584 2585 // e_shoff 2586 mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(self.shdr_table_offset.?)), endian); 2587 index += 4; 2588 }, 2589 .p64 => { 2590 // e_entry 2591 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian); 2592 index += 8; 2593 2594 // e_phoff 2595 mem.writeInt(u64, hdr_buf[index..][0..8], phdr_table_offset, endian); 2596 index += 8; 2597 2598 // e_shoff 2599 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian); 2600 index += 8; 2601 }, 2602 } 2603 2604 const e_flags = 0; 2605 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian); 2606 index += 4; 2607 2608 const e_ehsize: u16 = switch (self.ptr_width) { 2609 .p32 => @sizeOf(elf.Elf32_Ehdr), 2610 .p64 => @sizeOf(elf.Elf64_Ehdr), 2611 }; 2612 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian); 2613 index += 2; 2614 2615 const e_phentsize: u16 = switch (self.ptr_width) { 2616 .p32 => @sizeOf(elf.Elf32_Phdr), 2617 .p64 => @sizeOf(elf.Elf64_Phdr), 2618 }; 2619 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian); 2620 index += 2; 2621 2622 const e_phnum = @as(u16, @intCast(self.phdrs.items.len)); 2623 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian); 2624 index += 2; 2625 2626 const e_shentsize: u16 = switch (self.ptr_width) { 2627 .p32 => @sizeOf(elf.Elf32_Shdr), 2628 .p64 => @sizeOf(elf.Elf64_Shdr), 2629 }; 2630 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian); 2631 index += 2; 2632 2633 const e_shnum = @as(u16, @intCast(self.sections.items(.shdr).len)); 2634 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); 2635 index += 2; 2636 2637 mem.writeInt(u16, hdr_buf[index..][0..2], @intCast(self.shstrtab_section_index.?), endian); 2638 index += 2; 2639 2640 assert(index == e_ehsize); 2641 2642 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); 2643 } 2644 2645 pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void { 2646 if (self.llvm_object) |llvm_object| return llvm_object.freeNav(nav); 2647 return self.zigObjectPtr().?.freeNav(self, nav); 2648 } 2649 2650 pub fn updateFunc(self: *Elf, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { 2651 if (build_options.skip_non_native and builtin.object_format != .elf) { 2652 @panic("Attempted to compile for object format that was disabled by build configuration"); 2653 } 2654 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness); 2655 return self.zigObjectPtr().?.updateFunc(self, pt, func_index, air, liveness); 2656 } 2657 2658 pub fn updateNav( 2659 self: *Elf, 2660 pt: Zcu.PerThread, 2661 nav: InternPool.Nav.Index, 2662 ) link.File.UpdateNavError!void { 2663 if (build_options.skip_non_native and builtin.object_format != .elf) { 2664 @panic("Attempted to compile for object format that was disabled by build configuration"); 2665 } 2666 if (self.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav); 2667 return self.zigObjectPtr().?.updateNav(self, pt, nav); 2668 } 2669 2670 pub fn updateContainerType( 2671 self: *Elf, 2672 pt: Zcu.PerThread, 2673 ty: InternPool.Index, 2674 ) link.File.UpdateNavError!void { 2675 if (build_options.skip_non_native and builtin.object_format != .elf) { 2676 @panic("Attempted to compile for object format that was disabled by build configuration"); 2677 } 2678 if (self.llvm_object) |_| return; 2679 return self.zigObjectPtr().?.updateContainerType(pt, ty); 2680 } 2681 2682 pub fn updateExports( 2683 self: *Elf, 2684 pt: Zcu.PerThread, 2685 exported: Zcu.Exported, 2686 export_indices: []const u32, 2687 ) link.File.UpdateExportsError!void { 2688 if (build_options.skip_non_native and builtin.object_format != .elf) { 2689 @panic("Attempted to compile for object format that was disabled by build configuration"); 2690 } 2691 if (self.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices); 2692 return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices); 2693 } 2694 2695 pub fn updateNavLineNumber(self: *Elf, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void { 2696 if (self.llvm_object) |_| return; 2697 return self.zigObjectPtr().?.updateNavLineNumber(pt, nav); 2698 } 2699 2700 pub fn deleteExport( 2701 self: *Elf, 2702 exported: Zcu.Exported, 2703 name: InternPool.NullTerminatedString, 2704 ) void { 2705 if (self.llvm_object) |_| return; 2706 return self.zigObjectPtr().?.deleteExport(self, exported, name); 2707 } 2708 2709 fn checkDuplicates(self: *Elf) !void { 2710 const gpa = self.base.comp.gpa; 2711 2712 var dupes = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)).init(gpa); 2713 defer { 2714 for (dupes.values()) |*list| { 2715 list.deinit(gpa); 2716 } 2717 dupes.deinit(); 2718 } 2719 2720 if (self.zigObjectPtr()) |zig_object| { 2721 try zig_object.checkDuplicates(&dupes, self); 2722 } 2723 for (self.objects.items) |index| { 2724 try self.file(index).?.object.checkDuplicates(&dupes, self); 2725 } 2726 2727 try self.reportDuplicates(dupes); 2728 } 2729 2730 pub fn addCommentString(self: *Elf) !void { 2731 const gpa = self.base.comp.gpa; 2732 if (self.comment_merge_section_index != null) return; 2733 const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS); 2734 const msec = self.mergeSection(msec_index); 2735 const res = try msec.insertZ(gpa, "zig " ++ builtin.zig_version_string); 2736 if (res.found_existing) return; 2737 const msub_index = try msec.addMergeSubsection(gpa); 2738 const msub = msec.mergeSubsection(msub_index); 2739 msub.merge_section_index = msec_index; 2740 msub.string_index = res.key.pos; 2741 msub.alignment = .@"1"; 2742 msub.size = res.key.len; 2743 msub.entsize = 1; 2744 msub.alive = true; 2745 res.sub.* = msub_index; 2746 self.comment_merge_section_index = msec_index; 2747 } 2748 2749 pub fn resolveMergeSections(self: *Elf) !void { 2750 const tracy = trace(@src()); 2751 defer tracy.end(); 2752 2753 var has_errors = false; 2754 for (self.objects.items) |index| { 2755 const object = self.file(index).?.object; 2756 if (!object.alive) continue; 2757 if (!object.dirty) continue; 2758 object.initInputMergeSections(self) catch |err| switch (err) { 2759 error.LinkFailure => has_errors = true, 2760 else => |e| return e, 2761 }; 2762 } 2763 2764 if (has_errors) return error.FlushFailure; 2765 2766 for (self.objects.items) |index| { 2767 const object = self.file(index).?.object; 2768 if (!object.alive) continue; 2769 if (!object.dirty) continue; 2770 try object.initOutputMergeSections(self); 2771 } 2772 2773 for (self.objects.items) |index| { 2774 const object = self.file(index).?.object; 2775 if (!object.alive) continue; 2776 if (!object.dirty) continue; 2777 object.resolveMergeSubsections(self) catch |err| switch (err) { 2778 error.LinkFailure => has_errors = true, 2779 else => |e| return e, 2780 }; 2781 } 2782 2783 if (has_errors) return error.LinkFailure; 2784 } 2785 2786 pub fn finalizeMergeSections(self: *Elf) !void { 2787 for (self.merge_sections.items) |*msec| { 2788 try msec.finalize(self.base.comp.gpa); 2789 } 2790 } 2791 2792 pub fn updateMergeSectionSizes(self: *Elf) !void { 2793 for (self.merge_sections.items) |*msec| { 2794 msec.updateSize(); 2795 } 2796 for (self.merge_sections.items) |*msec| { 2797 const shdr = &self.sections.items(.shdr)[msec.output_section_index]; 2798 const offset = msec.alignment.forward(shdr.sh_size); 2799 const padding = offset - shdr.sh_size; 2800 msec.value = @intCast(offset); 2801 shdr.sh_size += padding + msec.size; 2802 shdr.sh_addralign = @max(shdr.sh_addralign, msec.alignment.toByteUnits() orelse 1); 2803 shdr.sh_entsize = if (shdr.sh_entsize == 0) msec.entsize else @min(shdr.sh_entsize, msec.entsize); 2804 } 2805 } 2806 2807 pub fn writeMergeSections(self: *Elf) !void { 2808 const gpa = self.base.comp.gpa; 2809 var buffer = std.ArrayList(u8).init(gpa); 2810 defer buffer.deinit(); 2811 2812 for (self.merge_sections.items) |*msec| { 2813 const shdr = self.sections.items(.shdr)[msec.output_section_index]; 2814 const fileoff = math.cast(usize, msec.value + shdr.sh_offset) orelse return error.Overflow; 2815 const size = math.cast(usize, msec.size) orelse return error.Overflow; 2816 try buffer.ensureTotalCapacity(size); 2817 buffer.appendNTimesAssumeCapacity(0, size); 2818 2819 for (msec.finalized_subsections.items) |msub_index| { 2820 const msub = msec.mergeSubsection(msub_index); 2821 assert(msub.alive); 2822 const string = msub.getString(self); 2823 const off = math.cast(usize, msub.value) orelse return error.Overflow; 2824 @memcpy(buffer.items[off..][0..string.len], string); 2825 } 2826 2827 try self.base.file.?.pwriteAll(buffer.items, fileoff); 2828 buffer.clearRetainingCapacity(); 2829 } 2830 } 2831 2832 fn initOutputSections(self: *Elf) !void { 2833 for (self.objects.items) |index| { 2834 try self.file(index).?.object.initOutputSections(self); 2835 } 2836 for (self.merge_sections.items) |*msec| { 2837 if (msec.finalized_subsections.items.len == 0) continue; 2838 try msec.initOutputSection(self); 2839 } 2840 } 2841 2842 fn initSyntheticSections(self: *Elf) !void { 2843 const comp = self.base.comp; 2844 const target = self.getTarget(); 2845 const ptr_size = self.ptrWidthBytes(); 2846 2847 const needs_eh_frame = blk: { 2848 if (self.zigObjectPtr()) |zo| 2849 if (zo.eh_frame_index != null) break :blk true; 2850 break :blk for (self.objects.items) |index| { 2851 if (self.file(index).?.object.cies.items.len > 0) break true; 2852 } else false; 2853 }; 2854 if (needs_eh_frame) { 2855 if (self.eh_frame_section_index == null) { 2856 self.eh_frame_section_index = self.sectionByName(".eh_frame") orelse try self.addSection(.{ 2857 .name = try self.insertShString(".eh_frame"), 2858 .type = if (target.cpu.arch == .x86_64) 2859 elf.SHT_X86_64_UNWIND 2860 else 2861 elf.SHT_PROGBITS, 2862 .flags = elf.SHF_ALLOC, 2863 .addralign = ptr_size, 2864 .offset = std.math.maxInt(u64), 2865 }); 2866 } 2867 if (comp.link_eh_frame_hdr and self.eh_frame_hdr_section_index == null) { 2868 self.eh_frame_hdr_section_index = try self.addSection(.{ 2869 .name = try self.insertShString(".eh_frame_hdr"), 2870 .type = elf.SHT_PROGBITS, 2871 .flags = elf.SHF_ALLOC, 2872 .addralign = 4, 2873 .offset = std.math.maxInt(u64), 2874 }); 2875 } 2876 } 2877 2878 if (self.got.entries.items.len > 0 and self.got_section_index == null) { 2879 self.got_section_index = try self.addSection(.{ 2880 .name = try self.insertShString(".got"), 2881 .type = elf.SHT_PROGBITS, 2882 .flags = elf.SHF_ALLOC | elf.SHF_WRITE, 2883 .addralign = ptr_size, 2884 .offset = std.math.maxInt(u64), 2885 }); 2886 } 2887 2888 if (self.got_plt_section_index == null) { 2889 self.got_plt_section_index = try self.addSection(.{ 2890 .name = try self.insertShString(".got.plt"), 2891 .type = elf.SHT_PROGBITS, 2892 .flags = elf.SHF_ALLOC | elf.SHF_WRITE, 2893 .addralign = @alignOf(u64), 2894 .offset = std.math.maxInt(u64), 2895 }); 2896 } 2897 2898 const needs_rela_dyn = blk: { 2899 if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0) 2900 break :blk true; 2901 if (self.zigObjectPtr()) |zig_object| { 2902 if (zig_object.num_dynrelocs > 0) break :blk true; 2903 } 2904 for (self.objects.items) |index| { 2905 if (self.file(index).?.object.num_dynrelocs > 0) break :blk true; 2906 } 2907 break :blk false; 2908 }; 2909 if (needs_rela_dyn and self.rela_dyn_section_index == null) { 2910 self.rela_dyn_section_index = try self.addSection(.{ 2911 .name = try self.insertShString(".rela.dyn"), 2912 .type = elf.SHT_RELA, 2913 .flags = elf.SHF_ALLOC, 2914 .addralign = @alignOf(elf.Elf64_Rela), 2915 .entsize = @sizeOf(elf.Elf64_Rela), 2916 .offset = std.math.maxInt(u64), 2917 }); 2918 } 2919 2920 if (self.plt.symbols.items.len > 0) { 2921 if (self.plt_section_index == null) { 2922 self.plt_section_index = try self.addSection(.{ 2923 .name = try self.insertShString(".plt"), 2924 .type = elf.SHT_PROGBITS, 2925 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, 2926 .addralign = 16, 2927 .offset = std.math.maxInt(u64), 2928 }); 2929 } 2930 if (self.rela_plt_section_index == null) { 2931 self.rela_plt_section_index = try self.addSection(.{ 2932 .name = try self.insertShString(".rela.plt"), 2933 .type = elf.SHT_RELA, 2934 .flags = elf.SHF_ALLOC, 2935 .addralign = @alignOf(elf.Elf64_Rela), 2936 .entsize = @sizeOf(elf.Elf64_Rela), 2937 .offset = std.math.maxInt(u64), 2938 }); 2939 } 2940 } 2941 2942 if (self.plt_got.symbols.items.len > 0 and self.plt_got_section_index == null) { 2943 self.plt_got_section_index = try self.addSection(.{ 2944 .name = try self.insertShString(".plt.got"), 2945 .type = elf.SHT_PROGBITS, 2946 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, 2947 .addralign = 16, 2948 .offset = std.math.maxInt(u64), 2949 }); 2950 } 2951 2952 if (self.copy_rel.symbols.items.len > 0 and self.copy_rel_section_index == null) { 2953 self.copy_rel_section_index = try self.addSection(.{ 2954 .name = try self.insertShString(".copyrel"), 2955 .type = elf.SHT_NOBITS, 2956 .flags = elf.SHF_ALLOC | elf.SHF_WRITE, 2957 .offset = std.math.maxInt(u64), 2958 }); 2959 } 2960 2961 const needs_interp = blk: { 2962 // On Ubuntu with musl-gcc, we get a weird combo of options looking like this: 2963 // -dynamic-linker=<path> -static 2964 // In this case, if we do generate .interp section and segment, we will get 2965 // a segfault in the dynamic linker trying to load a binary that is static 2966 // and doesn't contain .dynamic section. 2967 if (self.base.isStatic() and !comp.config.pie) break :blk false; 2968 break :blk target.dynamic_linker.get() != null; 2969 }; 2970 if (needs_interp and self.interp_section_index == null) { 2971 self.interp_section_index = try self.addSection(.{ 2972 .name = try self.insertShString(".interp"), 2973 .type = elf.SHT_PROGBITS, 2974 .flags = elf.SHF_ALLOC, 2975 .addralign = 1, 2976 .offset = std.math.maxInt(u64), 2977 }); 2978 } 2979 2980 if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) { 2981 if (self.dynstrtab_section_index == null) { 2982 self.dynstrtab_section_index = try self.addSection(.{ 2983 .name = try self.insertShString(".dynstr"), 2984 .flags = elf.SHF_ALLOC, 2985 .type = elf.SHT_STRTAB, 2986 .entsize = 1, 2987 .addralign = 1, 2988 .offset = std.math.maxInt(u64), 2989 }); 2990 } 2991 if (self.dynamic_section_index == null) { 2992 self.dynamic_section_index = try self.addSection(.{ 2993 .name = try self.insertShString(".dynamic"), 2994 .flags = elf.SHF_ALLOC | elf.SHF_WRITE, 2995 .type = elf.SHT_DYNAMIC, 2996 .entsize = @sizeOf(elf.Elf64_Dyn), 2997 .addralign = @alignOf(elf.Elf64_Dyn), 2998 .offset = std.math.maxInt(u64), 2999 }); 3000 } 3001 if (self.dynsymtab_section_index == null) { 3002 self.dynsymtab_section_index = try self.addSection(.{ 3003 .name = try self.insertShString(".dynsym"), 3004 .flags = elf.SHF_ALLOC, 3005 .type = elf.SHT_DYNSYM, 3006 .addralign = @alignOf(elf.Elf64_Sym), 3007 .entsize = @sizeOf(elf.Elf64_Sym), 3008 .info = 1, 3009 .offset = std.math.maxInt(u64), 3010 }); 3011 } 3012 if (self.hash_section_index == null) { 3013 self.hash_section_index = try self.addSection(.{ 3014 .name = try self.insertShString(".hash"), 3015 .flags = elf.SHF_ALLOC, 3016 .type = elf.SHT_HASH, 3017 .addralign = 4, 3018 .entsize = 4, 3019 .offset = std.math.maxInt(u64), 3020 }); 3021 } 3022 if (self.gnu_hash_section_index == null) { 3023 self.gnu_hash_section_index = try self.addSection(.{ 3024 .name = try self.insertShString(".gnu.hash"), 3025 .flags = elf.SHF_ALLOC, 3026 .type = elf.SHT_GNU_HASH, 3027 .addralign = 8, 3028 .offset = std.math.maxInt(u64), 3029 }); 3030 } 3031 3032 const needs_versions = for (self.dynsym.entries.items) |entry| { 3033 const sym = self.symbol(entry.ref).?; 3034 if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true; 3035 } else false; 3036 if (needs_versions) { 3037 if (self.versym_section_index == null) { 3038 self.versym_section_index = try self.addSection(.{ 3039 .name = try self.insertShString(".gnu.version"), 3040 .flags = elf.SHF_ALLOC, 3041 .type = elf.SHT_GNU_VERSYM, 3042 .addralign = @alignOf(elf.Elf64_Versym), 3043 .entsize = @sizeOf(elf.Elf64_Versym), 3044 .offset = std.math.maxInt(u64), 3045 }); 3046 } 3047 if (self.verneed_section_index == null) { 3048 self.verneed_section_index = try self.addSection(.{ 3049 .name = try self.insertShString(".gnu.version_r"), 3050 .flags = elf.SHF_ALLOC, 3051 .type = elf.SHT_GNU_VERNEED, 3052 .addralign = @alignOf(elf.Elf64_Verneed), 3053 .offset = std.math.maxInt(u64), 3054 }); 3055 } 3056 } 3057 } 3058 3059 try self.initSymtab(); 3060 try self.initShStrtab(); 3061 } 3062 3063 pub fn initSymtab(self: *Elf) !void { 3064 const small_ptr = switch (self.ptr_width) { 3065 .p32 => true, 3066 .p64 => false, 3067 }; 3068 if (self.symtab_section_index == null) { 3069 self.symtab_section_index = try self.addSection(.{ 3070 .name = try self.insertShString(".symtab"), 3071 .type = elf.SHT_SYMTAB, 3072 .addralign = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym), 3073 .entsize = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym), 3074 .offset = std.math.maxInt(u64), 3075 }); 3076 } 3077 if (self.strtab_section_index == null) { 3078 self.strtab_section_index = try self.addSection(.{ 3079 .name = try self.insertShString(".strtab"), 3080 .type = elf.SHT_STRTAB, 3081 .entsize = 1, 3082 .addralign = 1, 3083 .offset = std.math.maxInt(u64), 3084 }); 3085 } 3086 } 3087 3088 pub fn initShStrtab(self: *Elf) !void { 3089 if (self.shstrtab_section_index == null) { 3090 self.shstrtab_section_index = try self.addSection(.{ 3091 .name = try self.insertShString(".shstrtab"), 3092 .type = elf.SHT_STRTAB, 3093 .entsize = 1, 3094 .addralign = 1, 3095 .offset = std.math.maxInt(u64), 3096 }); 3097 } 3098 } 3099 3100 fn initSpecialPhdrs(self: *Elf) !void { 3101 comptime assert(max_number_of_special_phdrs == 5); 3102 3103 if (self.interp_section_index != null and self.phdr_interp_index == null) { 3104 self.phdr_interp_index = try self.addPhdr(.{ 3105 .type = elf.PT_INTERP, 3106 .flags = elf.PF_R, 3107 .@"align" = 1, 3108 }); 3109 } 3110 if (self.dynamic_section_index != null and self.phdr_dynamic_index == null) { 3111 self.phdr_dynamic_index = try self.addPhdr(.{ 3112 .type = elf.PT_DYNAMIC, 3113 .flags = elf.PF_R | elf.PF_W, 3114 }); 3115 } 3116 if (self.eh_frame_hdr_section_index != null and self.phdr_gnu_eh_frame_index == null) { 3117 self.phdr_gnu_eh_frame_index = try self.addPhdr(.{ 3118 .type = elf.PT_GNU_EH_FRAME, 3119 .flags = elf.PF_R, 3120 }); 3121 } 3122 if (self.phdr_gnu_stack_index == null) { 3123 self.phdr_gnu_stack_index = try self.addPhdr(.{ 3124 .type = elf.PT_GNU_STACK, 3125 .flags = elf.PF_W | elf.PF_R, 3126 .memsz = self.base.stack_size, 3127 .@"align" = 1, 3128 }); 3129 } 3130 3131 const has_tls = for (self.sections.items(.shdr)) |shdr| { 3132 if (shdr.sh_flags & elf.SHF_TLS != 0) break true; 3133 } else false; 3134 if (has_tls and self.phdr_tls_index == null) { 3135 self.phdr_tls_index = try self.addPhdr(.{ 3136 .type = elf.PT_TLS, 3137 .flags = elf.PF_R, 3138 .@"align" = 1, 3139 }); 3140 } 3141 } 3142 3143 /// We need to sort constructors/destuctors in the following sections: 3144 /// * .init_array 3145 /// * .fini_array 3146 /// * .preinit_array 3147 /// * .ctors 3148 /// * .dtors 3149 /// The prority of inclusion is defined as part of the input section's name. For example, .init_array.10000. 3150 /// If no priority value has been specified, 3151 /// * for .init_array, .fini_array and .preinit_array, we automatically assign that section max value of maxInt(i32) 3152 /// and push it to the back of the queue, 3153 /// * for .ctors and .dtors, we automatically assign that section min value of -1 3154 /// and push it to the front of the queue, 3155 /// crtbegin and ctrend are assigned minInt(i32) and maxInt(i32) respectively. 3156 /// Ties are broken by the file prority which corresponds to the inclusion of input sections in this output section 3157 /// we are about to sort. 3158 fn sortInitFini(self: *Elf) !void { 3159 const gpa = self.base.comp.gpa; 3160 const slice = self.sections.slice(); 3161 3162 const Entry = struct { 3163 priority: i32, 3164 atom_ref: Ref, 3165 3166 pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool { 3167 if (lhs.priority == rhs.priority) { 3168 return ctx.atom(lhs.atom_ref).?.priority(ctx) < ctx.atom(rhs.atom_ref).?.priority(ctx); 3169 } 3170 return lhs.priority < rhs.priority; 3171 } 3172 }; 3173 3174 for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, *atom_list| { 3175 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; 3176 if (atom_list.atoms.keys().len == 0) continue; 3177 3178 var is_init_fini = false; 3179 var is_ctor_dtor = false; 3180 switch (shdr.sh_type) { 3181 elf.SHT_PREINIT_ARRAY, 3182 elf.SHT_INIT_ARRAY, 3183 elf.SHT_FINI_ARRAY, 3184 => is_init_fini = true, 3185 else => { 3186 const name = self.getShString(shdr.sh_name); 3187 is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null; 3188 }, 3189 } 3190 if (!is_init_fini and !is_ctor_dtor) continue; 3191 3192 var entries = std.ArrayList(Entry).init(gpa); 3193 try entries.ensureTotalCapacityPrecise(atom_list.atoms.keys().len); 3194 defer entries.deinit(); 3195 3196 for (atom_list.atoms.keys()) |ref| { 3197 const atom_ptr = self.atom(ref).?; 3198 const object = atom_ptr.file(self).?.object; 3199 const priority = blk: { 3200 if (is_ctor_dtor) { 3201 if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32); 3202 if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32); 3203 } 3204 const default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32); 3205 const name = atom_ptr.name(self); 3206 var it = mem.splitBackwardsScalar(u8, name, '.'); 3207 const priority = std.fmt.parseUnsigned(u16, it.first(), 10) catch default; 3208 break :blk priority; 3209 }; 3210 entries.appendAssumeCapacity(.{ .priority = priority, .atom_ref = ref }); 3211 } 3212 3213 mem.sort(Entry, entries.items, self, Entry.lessThan); 3214 3215 atom_list.atoms.clearRetainingCapacity(); 3216 for (entries.items) |entry| { 3217 _ = atom_list.atoms.getOrPutAssumeCapacity(entry.atom_ref); 3218 } 3219 } 3220 } 3221 3222 fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void { 3223 if (self.dynamic_section_index == null) return; 3224 3225 for (self.shared_objects.items) |index| { 3226 const shared_object = self.file(index).?.shared_object; 3227 if (!shared_object.alive) continue; 3228 try self.dynamic.addNeeded(shared_object, self); 3229 } 3230 3231 if (self.isEffectivelyDynLib()) { 3232 if (self.soname) |soname| { 3233 try self.dynamic.setSoname(soname, self); 3234 } 3235 } 3236 3237 try self.dynamic.setRpath(rpaths, self); 3238 } 3239 3240 fn sortDynamicSymtab(self: *Elf) void { 3241 if (self.gnu_hash_section_index == null) return; 3242 self.dynsym.sort(self); 3243 } 3244 3245 fn setVersionSymtab(self: *Elf) !void { 3246 const gpa = self.base.comp.gpa; 3247 if (self.versym_section_index == null) return; 3248 try self.versym.resize(gpa, self.dynsym.count()); 3249 self.versym.items[0] = elf.VER_NDX_LOCAL; 3250 for (self.dynsym.entries.items, 1..) |entry, i| { 3251 const sym = self.symbol(entry.ref).?; 3252 self.versym.items[i] = sym.version_index; 3253 } 3254 3255 if (self.verneed_section_index) |shndx| { 3256 try self.verneed.generate(self); 3257 const shdr = &self.sections.items(.shdr)[shndx]; 3258 shdr.sh_info = @as(u32, @intCast(self.verneed.verneed.items.len)); 3259 } 3260 } 3261 3262 fn setHashSections(self: *Elf) !void { 3263 if (self.hash_section_index != null) { 3264 try self.hash.generate(self); 3265 } 3266 if (self.gnu_hash_section_index != null) { 3267 try self.gnu_hash.calcSize(self); 3268 } 3269 } 3270 3271 fn phdrRank(phdr: elf.Elf64_Phdr) u8 { 3272 switch (phdr.p_type) { 3273 elf.PT_NULL => return 0, 3274 elf.PT_PHDR => return 1, 3275 elf.PT_INTERP => return 2, 3276 elf.PT_LOAD => return 3, 3277 elf.PT_DYNAMIC, elf.PT_TLS => return 4, 3278 elf.PT_GNU_EH_FRAME => return 5, 3279 elf.PT_GNU_STACK => return 6, 3280 else => return 7, 3281 } 3282 } 3283 3284 fn sortPhdrs(self: *Elf) error{OutOfMemory}!void { 3285 const Entry = struct { 3286 phndx: u16, 3287 3288 pub fn lessThan(elf_file: *Elf, lhs: @This(), rhs: @This()) bool { 3289 const lhs_phdr = elf_file.phdrs.items[lhs.phndx]; 3290 const rhs_phdr = elf_file.phdrs.items[rhs.phndx]; 3291 const lhs_rank = phdrRank(lhs_phdr); 3292 const rhs_rank = phdrRank(rhs_phdr); 3293 if (lhs_rank == rhs_rank) return lhs_phdr.p_vaddr < rhs_phdr.p_vaddr; 3294 return lhs_rank < rhs_rank; 3295 } 3296 }; 3297 3298 const gpa = self.base.comp.gpa; 3299 var entries = try std.ArrayList(Entry).initCapacity(gpa, self.phdrs.items.len); 3300 defer entries.deinit(); 3301 for (0..self.phdrs.items.len) |phndx| { 3302 entries.appendAssumeCapacity(.{ .phndx = @as(u16, @intCast(phndx)) }); 3303 } 3304 3305 mem.sort(Entry, entries.items, self, Entry.lessThan); 3306 3307 const backlinks = try gpa.alloc(u16, entries.items.len); 3308 defer gpa.free(backlinks); 3309 for (entries.items, 0..) |entry, i| { 3310 backlinks[entry.phndx] = @as(u16, @intCast(i)); 3311 } 3312 3313 const slice = try self.phdrs.toOwnedSlice(gpa); 3314 defer gpa.free(slice); 3315 3316 try self.phdrs.ensureTotalCapacityPrecise(gpa, slice.len); 3317 for (entries.items) |sorted| { 3318 self.phdrs.appendAssumeCapacity(slice[sorted.phndx]); 3319 } 3320 3321 for (&[_]*?u16{ 3322 &self.phdr_table_index, 3323 &self.phdr_table_load_index, 3324 &self.phdr_interp_index, 3325 &self.phdr_dynamic_index, 3326 &self.phdr_gnu_eh_frame_index, 3327 &self.phdr_tls_index, 3328 }) |maybe_index| { 3329 if (maybe_index.*) |*index| { 3330 index.* = backlinks[index.*]; 3331 } 3332 } 3333 3334 for (self.sections.items(.phndx)) |*maybe_phndx| { 3335 if (maybe_phndx.*) |*index| { 3336 index.* = backlinks[index.*]; 3337 } 3338 } 3339 } 3340 3341 fn shdrRank(self: *Elf, shndx: u32) u8 { 3342 const shdr = self.sections.items(.shdr)[shndx]; 3343 const name = self.getShString(shdr.sh_name); 3344 const flags = shdr.sh_flags; 3345 3346 switch (shdr.sh_type) { 3347 elf.SHT_NULL => return 0, 3348 elf.SHT_DYNSYM => return 2, 3349 elf.SHT_HASH => return 3, 3350 elf.SHT_GNU_HASH => return 3, 3351 elf.SHT_GNU_VERSYM => return 4, 3352 elf.SHT_GNU_VERDEF => return 4, 3353 elf.SHT_GNU_VERNEED => return 4, 3354 3355 elf.SHT_PREINIT_ARRAY, 3356 elf.SHT_INIT_ARRAY, 3357 elf.SHT_FINI_ARRAY, 3358 => return 0xf1, 3359 3360 elf.SHT_DYNAMIC => return 0xf2, 3361 3362 elf.SHT_RELA, elf.SHT_GROUP => return 0xf, 3363 3364 elf.SHT_PROGBITS => if (flags & elf.SHF_ALLOC != 0) { 3365 if (flags & elf.SHF_EXECINSTR != 0) { 3366 return 0xf0; 3367 } else if (flags & elf.SHF_WRITE != 0) { 3368 return if (flags & elf.SHF_TLS != 0) 0xf3 else 0xf5; 3369 } else if (mem.eql(u8, name, ".interp")) { 3370 return 1; 3371 } else if (mem.startsWith(u8, name, ".eh_frame")) { 3372 return 0xe1; 3373 } else { 3374 return 0xe0; 3375 } 3376 } else { 3377 if (mem.startsWith(u8, name, ".debug")) { 3378 return 0xf7; 3379 } else { 3380 return 0xf8; 3381 } 3382 }, 3383 elf.SHT_X86_64_UNWIND => return 0xe1, 3384 3385 elf.SHT_NOBITS => return if (flags & elf.SHF_TLS != 0) 0xf4 else 0xf6, 3386 elf.SHT_SYMTAB => return 0xf9, 3387 elf.SHT_STRTAB => return if (mem.eql(u8, name, ".dynstr")) 0x4 else 0xfa, 3388 else => return 0xff, 3389 } 3390 } 3391 3392 pub fn sortShdrs(self: *Elf) !void { 3393 const Entry = struct { 3394 shndx: u32, 3395 3396 pub fn lessThan(elf_file: *Elf, lhs: @This(), rhs: @This()) bool { 3397 return elf_file.shdrRank(lhs.shndx) < elf_file.shdrRank(rhs.shndx); 3398 } 3399 }; 3400 3401 const gpa = self.base.comp.gpa; 3402 var entries = try std.ArrayList(Entry).initCapacity(gpa, self.sections.items(.shdr).len); 3403 defer entries.deinit(); 3404 for (0..self.sections.items(.shdr).len) |shndx| { 3405 entries.appendAssumeCapacity(.{ .shndx = @intCast(shndx) }); 3406 } 3407 3408 mem.sort(Entry, entries.items, self, Entry.lessThan); 3409 3410 const backlinks = try gpa.alloc(u32, entries.items.len); 3411 defer gpa.free(backlinks); 3412 for (entries.items, 0..) |entry, i| { 3413 backlinks[entry.shndx] = @intCast(i); 3414 } 3415 3416 var slice = self.sections.toOwnedSlice(); 3417 defer slice.deinit(gpa); 3418 3419 try self.sections.ensureTotalCapacity(gpa, slice.len); 3420 for (entries.items) |sorted| { 3421 self.sections.appendAssumeCapacity(slice.get(sorted.shndx)); 3422 } 3423 3424 self.resetShdrIndexes(backlinks); 3425 } 3426 3427 fn resetShdrIndexes(self: *Elf, backlinks: []const u32) void { 3428 for (&[_]*?u32{ 3429 &self.eh_frame_section_index, 3430 &self.eh_frame_rela_section_index, 3431 &self.eh_frame_hdr_section_index, 3432 &self.got_section_index, 3433 &self.symtab_section_index, 3434 &self.strtab_section_index, 3435 &self.shstrtab_section_index, 3436 &self.interp_section_index, 3437 &self.dynamic_section_index, 3438 &self.dynsymtab_section_index, 3439 &self.dynstrtab_section_index, 3440 &self.hash_section_index, 3441 &self.gnu_hash_section_index, 3442 &self.plt_section_index, 3443 &self.got_plt_section_index, 3444 &self.plt_got_section_index, 3445 &self.rela_dyn_section_index, 3446 &self.rela_plt_section_index, 3447 &self.copy_rel_section_index, 3448 &self.versym_section_index, 3449 &self.verneed_section_index, 3450 }) |maybe_index| { 3451 if (maybe_index.*) |*index| { 3452 index.* = backlinks[index.*]; 3453 } 3454 } 3455 3456 for (self.merge_sections.items) |*msec| { 3457 msec.output_section_index = backlinks[msec.output_section_index]; 3458 } 3459 3460 const slice = self.sections.slice(); 3461 for (slice.items(.shdr), slice.items(.atom_list_2)) |*shdr, *atom_list| { 3462 atom_list.output_section_index = backlinks[atom_list.output_section_index]; 3463 for (atom_list.atoms.keys()) |ref| { 3464 self.atom(ref).?.output_section_index = atom_list.output_section_index; 3465 } 3466 if (shdr.sh_type == elf.SHT_RELA) { 3467 // FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections 3468 // to point at symtab 3469 // shdr.sh_link = backlinks[shdr.sh_link]; 3470 shdr.sh_link = self.symtab_section_index.?; 3471 shdr.sh_info = backlinks[shdr.sh_info]; 3472 } 3473 } 3474 3475 if (self.zigObjectPtr()) |zo| { 3476 for (zo.atoms_indexes.items) |atom_index| { 3477 const atom_ptr = zo.atom(atom_index) orelse continue; 3478 atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index]; 3479 } 3480 } 3481 3482 for (self.comdat_group_sections.items) |*cg| { 3483 cg.shndx = backlinks[cg.shndx]; 3484 } 3485 3486 if (self.symtab_section_index) |index| { 3487 const shdr = &slice.items(.shdr)[index]; 3488 shdr.sh_link = self.strtab_section_index.?; 3489 } 3490 3491 if (self.dynamic_section_index) |index| { 3492 const shdr = &slice.items(.shdr)[index]; 3493 shdr.sh_link = self.dynstrtab_section_index.?; 3494 } 3495 3496 if (self.dynsymtab_section_index) |index| { 3497 const shdr = &slice.items(.shdr)[index]; 3498 shdr.sh_link = self.dynstrtab_section_index.?; 3499 } 3500 3501 if (self.hash_section_index) |index| { 3502 const shdr = &slice.items(.shdr)[index]; 3503 shdr.sh_link = self.dynsymtab_section_index.?; 3504 } 3505 3506 if (self.gnu_hash_section_index) |index| { 3507 const shdr = &slice.items(.shdr)[index]; 3508 shdr.sh_link = self.dynsymtab_section_index.?; 3509 } 3510 3511 if (self.versym_section_index) |index| { 3512 const shdr = &slice.items(.shdr)[index]; 3513 shdr.sh_link = self.dynsymtab_section_index.?; 3514 } 3515 3516 if (self.verneed_section_index) |index| { 3517 const shdr = &slice.items(.shdr)[index]; 3518 shdr.sh_link = self.dynstrtab_section_index.?; 3519 } 3520 3521 if (self.rela_dyn_section_index) |index| { 3522 const shdr = &slice.items(.shdr)[index]; 3523 shdr.sh_link = self.dynsymtab_section_index orelse 0; 3524 } 3525 3526 if (self.rela_plt_section_index) |index| { 3527 const shdr = &slice.items(.shdr)[index]; 3528 shdr.sh_link = self.dynsymtab_section_index.?; 3529 shdr.sh_info = self.plt_section_index.?; 3530 } 3531 3532 if (self.eh_frame_rela_section_index) |index| { 3533 const shdr = &slice.items(.shdr)[index]; 3534 shdr.sh_link = self.symtab_section_index.?; 3535 shdr.sh_info = self.eh_frame_section_index.?; 3536 } 3537 } 3538 3539 fn updateSectionSizes(self: *Elf) !void { 3540 const slice = self.sections.slice(); 3541 for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, *atom_list| { 3542 if (atom_list.atoms.keys().len == 0) continue; 3543 if (!atom_list.dirty) continue; 3544 if (self.requiresThunks() and shdr.sh_flags & elf.SHF_EXECINSTR != 0) continue; 3545 atom_list.updateSize(self); 3546 try atom_list.allocate(self); 3547 atom_list.dirty = false; 3548 } 3549 3550 if (self.requiresThunks()) { 3551 for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, *atom_list| { 3552 if (shdr.sh_flags & elf.SHF_EXECINSTR == 0) continue; 3553 if (atom_list.atoms.keys().len == 0) continue; 3554 if (!atom_list.dirty) continue; 3555 3556 // Create jump/branch range extenders if needed. 3557 try self.createThunks(atom_list); 3558 try atom_list.allocate(self); 3559 atom_list.dirty = false; 3560 } 3561 3562 // FIXME:JK this will hopefully not be needed once we create a link from Atom/Thunk to AtomList. 3563 for (self.thunks.items) |*th| { 3564 th.value += slice.items(.atom_list_2)[th.output_section_index].value; 3565 } 3566 } 3567 3568 const shdrs = slice.items(.shdr); 3569 if (self.eh_frame_section_index) |index| { 3570 shdrs[index].sh_size = try eh_frame.calcEhFrameSize(self); 3571 } 3572 3573 if (self.eh_frame_hdr_section_index) |index| { 3574 shdrs[index].sh_size = eh_frame.calcEhFrameHdrSize(self); 3575 } 3576 3577 if (self.got_section_index) |index| { 3578 shdrs[index].sh_size = self.got.size(self); 3579 } 3580 3581 if (self.plt_section_index) |index| { 3582 shdrs[index].sh_size = self.plt.size(self); 3583 } 3584 3585 if (self.got_plt_section_index) |index| { 3586 shdrs[index].sh_size = self.got_plt.size(self); 3587 } 3588 3589 if (self.plt_got_section_index) |index| { 3590 shdrs[index].sh_size = self.plt_got.size(self); 3591 } 3592 3593 if (self.rela_dyn_section_index) |shndx| { 3594 var num = self.got.numRela(self) + self.copy_rel.numRela(); 3595 if (self.zigObjectPtr()) |zig_object| { 3596 num += zig_object.num_dynrelocs; 3597 } 3598 for (self.objects.items) |index| { 3599 num += self.file(index).?.object.num_dynrelocs; 3600 } 3601 shdrs[shndx].sh_size = num * @sizeOf(elf.Elf64_Rela); 3602 } 3603 3604 if (self.rela_plt_section_index) |index| { 3605 shdrs[index].sh_size = self.plt.numRela() * @sizeOf(elf.Elf64_Rela); 3606 } 3607 3608 if (self.copy_rel_section_index) |index| { 3609 try self.copy_rel.updateSectionSize(index, self); 3610 } 3611 3612 if (self.interp_section_index) |index| { 3613 shdrs[index].sh_size = self.getTarget().dynamic_linker.get().?.len + 1; 3614 } 3615 3616 if (self.hash_section_index) |index| { 3617 shdrs[index].sh_size = self.hash.size(); 3618 } 3619 3620 if (self.gnu_hash_section_index) |index| { 3621 shdrs[index].sh_size = self.gnu_hash.size(); 3622 } 3623 3624 if (self.dynamic_section_index) |index| { 3625 shdrs[index].sh_size = self.dynamic.size(self); 3626 } 3627 3628 if (self.dynsymtab_section_index) |index| { 3629 shdrs[index].sh_size = self.dynsym.size(); 3630 } 3631 3632 if (self.dynstrtab_section_index) |index| { 3633 shdrs[index].sh_size = self.dynstrtab.items.len; 3634 } 3635 3636 if (self.versym_section_index) |index| { 3637 shdrs[index].sh_size = self.versym.items.len * @sizeOf(elf.Elf64_Versym); 3638 } 3639 3640 if (self.verneed_section_index) |index| { 3641 shdrs[index].sh_size = self.verneed.size(); 3642 } 3643 3644 try self.updateSymtabSize(); 3645 self.updateShStrtabSize(); 3646 } 3647 3648 // FIXME:JK this is very much obsolete, remove! 3649 pub fn updateShStrtabSize(self: *Elf) void { 3650 if (self.shstrtab_section_index) |index| { 3651 self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len; 3652 } 3653 } 3654 3655 fn shdrToPhdrFlags(sh_flags: u64) u32 { 3656 const write = sh_flags & elf.SHF_WRITE != 0; 3657 const exec = sh_flags & elf.SHF_EXECINSTR != 0; 3658 var out_flags: u32 = elf.PF_R; 3659 if (write) out_flags |= elf.PF_W; 3660 if (exec) out_flags |= elf.PF_X; 3661 return out_flags; 3662 } 3663 3664 /// Returns maximum number of program headers that may be emitted by the linker. 3665 /// (This is an upper bound so that we can reserve enough space for the header and progam header 3666 /// table without running out of space and being forced to move things around.) 3667 fn getMaxNumberOfPhdrs() u64 { 3668 // The estimated maximum number of segments the linker can emit for input sections are: 3669 var num: u64 = max_number_of_object_segments; 3670 // Any other non-loadable program headers, including TLS, DYNAMIC, GNU_STACK, GNU_EH_FRAME, INTERP: 3671 num += max_number_of_special_phdrs; 3672 // PHDR program header and corresponding read-only load segment: 3673 num += 2; 3674 return num; 3675 } 3676 3677 fn addLoadPhdrs(self: *Elf) error{OutOfMemory}!void { 3678 for (self.sections.items(.shdr)) |shdr| { 3679 if (shdr.sh_type == elf.SHT_NULL) continue; 3680 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; 3681 const flags = shdrToPhdrFlags(shdr.sh_flags); 3682 if (self.getPhdr(.{ .flags = flags, .type = elf.PT_LOAD }) == null) { 3683 _ = try self.addPhdr(.{ .flags = flags, .type = elf.PT_LOAD }); 3684 } 3685 } 3686 } 3687 3688 /// Allocates PHDR table in virtual memory and in file. 3689 fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void { 3690 const phdr_table = &self.phdrs.items[self.phdr_table_index.?]; 3691 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?]; 3692 3693 const ehsize: u64 = switch (self.ptr_width) { 3694 .p32 => @sizeOf(elf.Elf32_Ehdr), 3695 .p64 => @sizeOf(elf.Elf64_Ehdr), 3696 }; 3697 const phsize: u64 = switch (self.ptr_width) { 3698 .p32 => @sizeOf(elf.Elf32_Phdr), 3699 .p64 => @sizeOf(elf.Elf64_Phdr), 3700 }; 3701 const needed_size = self.phdrs.items.len * phsize; 3702 const available_space = self.allocatedSize(phdr_table.p_offset); 3703 3704 if (needed_size > available_space) { 3705 // In this case, we have two options: 3706 // 1. increase the available padding for EHDR + PHDR table so that we don't overflow it 3707 // (revisit getMaxNumberOfPhdrs()) 3708 // 2. shift everything in file to free more space for EHDR + PHDR table 3709 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op 3710 var err = try self.base.addErrorWithNotes(1); 3711 try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{}); 3712 try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space }); 3713 } 3714 3715 phdr_table_load.p_filesz = needed_size + ehsize; 3716 phdr_table_load.p_memsz = needed_size + ehsize; 3717 phdr_table.p_filesz = needed_size; 3718 phdr_table.p_memsz = needed_size; 3719 } 3720 3721 /// Allocates alloc sections and creates load segments for sections 3722 /// extracted from input object files. 3723 pub fn allocateAllocSections(self: *Elf) !void { 3724 // We use this struct to track maximum alignment of all TLS sections. 3725 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold, 3726 // in-file offsets have to be aligned against the start of TLS program header. 3727 // If that's not ensured, then in a multi-threaded context, TLS variables across a shared object 3728 // boundary may not get correctly loaded at an aligned address. 3729 const Align = struct { 3730 tls_start_align: u64 = 1, 3731 first_tls_index: ?usize = null, 3732 3733 fn isFirstTlsShdr(this: @This(), other: usize) bool { 3734 if (this.first_tls_index) |index| return index == other; 3735 return false; 3736 } 3737 3738 fn @"align"(this: @This(), index: usize, sh_addralign: u64, addr: u64) u64 { 3739 const alignment = if (this.isFirstTlsShdr(index)) this.tls_start_align else sh_addralign; 3740 return mem.alignForward(u64, addr, alignment); 3741 } 3742 }; 3743 3744 const slice = self.sections.slice(); 3745 var alignment = Align{}; 3746 for (slice.items(.shdr), 0..) |shdr, i| { 3747 if (shdr.sh_type == elf.SHT_NULL) continue; 3748 if (shdr.sh_flags & elf.SHF_TLS == 0) continue; 3749 if (alignment.first_tls_index == null) alignment.first_tls_index = i; 3750 alignment.tls_start_align = @max(alignment.tls_start_align, shdr.sh_addralign); 3751 } 3752 3753 // Next, calculate segment covers by scanning all alloc sections. 3754 // If a section matches segment flags with the preceeding section, 3755 // we put it in the same segment. Otherwise, we create a new cover. 3756 // This algorithm is simple but suboptimal in terms of space re-use: 3757 // normally we would also take into account any gaps in allocated 3758 // virtual and file offsets. However, the simple one will do for one 3759 // as we are more interested in quick turnaround and compatibility 3760 // with `findFreeSpace` mechanics than anything else. 3761 const Cover = std.ArrayList(u32); 3762 const gpa = self.base.comp.gpa; 3763 var covers: [max_number_of_object_segments]Cover = undefined; 3764 for (&covers) |*cover| { 3765 cover.* = Cover.init(gpa); 3766 } 3767 defer for (&covers) |*cover| { 3768 cover.deinit(); 3769 }; 3770 3771 for (slice.items(.shdr), 0..) |shdr, shndx| { 3772 if (shdr.sh_type == elf.SHT_NULL) continue; 3773 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; 3774 const flags = shdrToPhdrFlags(shdr.sh_flags); 3775 try covers[flags - 1].append(@intCast(shndx)); 3776 } 3777 3778 // Now we can proceed with allocating the sections in virtual memory. 3779 // As the base address we take the end address of the PHDR table. 3780 // When allocating we first find the largest required alignment 3781 // of any section that is contained in a cover and use it to align 3782 // the start address of the segement (and first section). 3783 const phdr_table = &self.phdrs.items[self.phdr_table_load_index.?]; 3784 var addr = phdr_table.p_vaddr + phdr_table.p_memsz; 3785 3786 for (covers) |cover| { 3787 if (cover.items.len == 0) continue; 3788 3789 var @"align": u64 = self.page_size; 3790 for (cover.items) |shndx| { 3791 const shdr = slice.items(.shdr)[shndx]; 3792 if (shdr.sh_type == elf.SHT_NOBITS and shdr.sh_flags & elf.SHF_TLS != 0) continue; 3793 @"align" = @max(@"align", shdr.sh_addralign); 3794 } 3795 3796 addr = mem.alignForward(u64, addr, @"align"); 3797 3798 var memsz: u64 = 0; 3799 var filesz: u64 = 0; 3800 var i: usize = 0; 3801 while (i < cover.items.len) : (i += 1) { 3802 const shndx = cover.items[i]; 3803 const shdr = &slice.items(.shdr)[shndx]; 3804 if (shdr.sh_type == elf.SHT_NOBITS and shdr.sh_flags & elf.SHF_TLS != 0) { 3805 // .tbss is a little special as it's used only by the loader meaning it doesn't 3806 // need to be actually mmap'ed at runtime. We still need to correctly increment 3807 // the addresses of every TLS zerofill section tho. Thus, we hack it so that 3808 // we increment the start address like normal, however, after we are done, 3809 // the next ALLOC section will get its start address allocated within the same 3810 // range as the .tbss sections. We will get something like this: 3811 // 3812 // ... 3813 // .tbss 0x10 3814 // .tcommon 0x20 3815 // .data 0x10 3816 // ... 3817 var tbss_addr = addr; 3818 while (i < cover.items.len and 3819 slice.items(.shdr)[cover.items[i]].sh_type == elf.SHT_NOBITS and 3820 slice.items(.shdr)[cover.items[i]].sh_flags & elf.SHF_TLS != 0) : (i += 1) 3821 { 3822 const tbss_shndx = cover.items[i]; 3823 const tbss_shdr = &slice.items(.shdr)[tbss_shndx]; 3824 tbss_addr = alignment.@"align"(tbss_shndx, tbss_shdr.sh_addralign, tbss_addr); 3825 tbss_shdr.sh_addr = tbss_addr; 3826 tbss_addr += tbss_shdr.sh_size; 3827 } 3828 i -= 1; 3829 continue; 3830 } 3831 const next = alignment.@"align"(shndx, shdr.sh_addralign, addr); 3832 const padding = next - addr; 3833 addr = next; 3834 shdr.sh_addr = addr; 3835 if (shdr.sh_type != elf.SHT_NOBITS) { 3836 filesz += padding + shdr.sh_size; 3837 } 3838 memsz += padding + shdr.sh_size; 3839 addr += shdr.sh_size; 3840 } 3841 3842 const first = slice.items(.shdr)[cover.items[0]]; 3843 const phndx = self.getPhdr(.{ .type = elf.PT_LOAD, .flags = shdrToPhdrFlags(first.sh_flags) }).?; 3844 const phdr = &self.phdrs.items[phndx]; 3845 const allocated_size = self.allocatedSize(phdr.p_offset); 3846 if (filesz > allocated_size) { 3847 const old_offset = phdr.p_offset; 3848 phdr.p_offset = 0; 3849 var new_offset = try self.findFreeSpace(filesz, @"align"); 3850 phdr.p_offset = new_offset; 3851 3852 log.debug("moving phdr({d}) from 0x{x} to 0x{x}", .{ phndx, old_offset, new_offset }); 3853 3854 for (cover.items) |shndx| { 3855 const shdr = &slice.items(.shdr)[shndx]; 3856 slice.items(.phndx)[shndx] = phndx; 3857 if (shdr.sh_type == elf.SHT_NOBITS) { 3858 shdr.sh_offset = 0; 3859 continue; 3860 } 3861 new_offset = alignment.@"align"(shndx, shdr.sh_addralign, new_offset); 3862 3863 log.debug("moving {s} from 0x{x} to 0x{x}", .{ 3864 self.getShString(shdr.sh_name), 3865 shdr.sh_offset, 3866 new_offset, 3867 }); 3868 3869 if (shdr.sh_offset > 0) { 3870 // Get size actually commited to the output file. 3871 const existing_size = self.sectionSize(shndx); 3872 const amt = try self.base.file.?.copyRangeAll( 3873 shdr.sh_offset, 3874 self.base.file.?, 3875 new_offset, 3876 existing_size, 3877 ); 3878 if (amt != existing_size) return error.InputOutput; 3879 } 3880 3881 shdr.sh_offset = new_offset; 3882 new_offset += shdr.sh_size; 3883 } 3884 } 3885 3886 phdr.p_vaddr = first.sh_addr; 3887 phdr.p_paddr = first.sh_addr; 3888 phdr.p_memsz = memsz; 3889 phdr.p_filesz = filesz; 3890 phdr.p_align = @"align"; 3891 3892 addr = mem.alignForward(u64, addr, self.page_size); 3893 } 3894 } 3895 3896 /// Allocates non-alloc sections (debug info, symtabs, etc.). 3897 pub fn allocateNonAllocSections(self: *Elf) !void { 3898 for (self.sections.items(.shdr), 0..) |*shdr, shndx| { 3899 if (shdr.sh_type == elf.SHT_NULL) continue; 3900 if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue; 3901 const needed_size = shdr.sh_size; 3902 if (needed_size > self.allocatedSize(shdr.sh_offset)) { 3903 shdr.sh_size = 0; 3904 const new_offset = try self.findFreeSpace(needed_size, shdr.sh_addralign); 3905 3906 if (self.zigObjectPtr()) |zo| blk: { 3907 const existing_size = for ([_]?Symbol.Index{ 3908 zo.debug_info_index, 3909 zo.debug_abbrev_index, 3910 zo.debug_aranges_index, 3911 zo.debug_str_index, 3912 zo.debug_line_index, 3913 zo.debug_line_str_index, 3914 zo.debug_loclists_index, 3915 zo.debug_rnglists_index, 3916 }) |maybe_sym_index| { 3917 const sym_index = maybe_sym_index orelse continue; 3918 const sym = zo.symbol(sym_index); 3919 const atom_ptr = sym.atom(self).?; 3920 if (atom_ptr.output_section_index == shndx) break atom_ptr.size; 3921 } else break :blk; 3922 log.debug("moving {s} from 0x{x} to 0x{x}", .{ 3923 self.getShString(shdr.sh_name), 3924 shdr.sh_offset, 3925 new_offset, 3926 }); 3927 const amt = try self.base.file.?.copyRangeAll( 3928 shdr.sh_offset, 3929 self.base.file.?, 3930 new_offset, 3931 existing_size, 3932 ); 3933 if (amt != existing_size) return error.InputOutput; 3934 } 3935 3936 shdr.sh_offset = new_offset; 3937 shdr.sh_size = needed_size; 3938 } 3939 } 3940 } 3941 3942 fn allocateSpecialPhdrs(self: *Elf) void { 3943 const slice = self.sections.slice(); 3944 3945 for (&[_]struct { ?u16, ?u32 }{ 3946 .{ self.phdr_interp_index, self.interp_section_index }, 3947 .{ self.phdr_dynamic_index, self.dynamic_section_index }, 3948 .{ self.phdr_gnu_eh_frame_index, self.eh_frame_hdr_section_index }, 3949 }) |pair| { 3950 if (pair[0]) |index| { 3951 const shdr = slice.items(.shdr)[pair[1].?]; 3952 const phdr = &self.phdrs.items[index]; 3953 phdr.p_align = shdr.sh_addralign; 3954 phdr.p_offset = shdr.sh_offset; 3955 phdr.p_vaddr = shdr.sh_addr; 3956 phdr.p_paddr = shdr.sh_addr; 3957 phdr.p_filesz = shdr.sh_size; 3958 phdr.p_memsz = shdr.sh_size; 3959 } 3960 } 3961 3962 // Set the TLS segment boundaries. 3963 // We assume TLS sections are laid out contiguously and that there is 3964 // a single TLS segment. 3965 if (self.phdr_tls_index) |index| { 3966 const shdrs = slice.items(.shdr); 3967 const phdr = &self.phdrs.items[index]; 3968 var shndx: u32 = 0; 3969 while (shndx < shdrs.len) { 3970 const shdr = shdrs[shndx]; 3971 if (shdr.sh_flags & elf.SHF_TLS == 0) { 3972 shndx += 1; 3973 continue; 3974 } 3975 phdr.p_offset = shdr.sh_offset; 3976 phdr.p_vaddr = shdr.sh_addr; 3977 phdr.p_paddr = shdr.sh_addr; 3978 phdr.p_align = shdr.sh_addralign; 3979 shndx += 1; 3980 phdr.p_align = @max(phdr.p_align, shdr.sh_addralign); 3981 if (shdr.sh_type != elf.SHT_NOBITS) { 3982 phdr.p_filesz = shdr.sh_offset + shdr.sh_size - phdr.p_offset; 3983 } 3984 phdr.p_memsz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr; 3985 3986 while (shndx < shdrs.len) : (shndx += 1) { 3987 const next = shdrs[shndx]; 3988 if (next.sh_flags & elf.SHF_TLS == 0) break; 3989 phdr.p_align = @max(phdr.p_align, next.sh_addralign); 3990 if (next.sh_type != elf.SHT_NOBITS) { 3991 phdr.p_filesz = next.sh_offset + next.sh_size - phdr.p_offset; 3992 } 3993 phdr.p_memsz = next.sh_addr + next.sh_size - phdr.p_vaddr; 3994 } 3995 } 3996 } 3997 } 3998 3999 fn writeAtoms(self: *Elf) !void { 4000 const gpa = self.base.comp.gpa; 4001 4002 var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); 4003 defer { 4004 for (undefs.values()) |*refs| { 4005 refs.deinit(); 4006 } 4007 undefs.deinit(); 4008 } 4009 4010 var buffer = std.ArrayList(u8).init(gpa); 4011 defer buffer.deinit(); 4012 4013 const slice = self.sections.slice(); 4014 var has_reloc_errors = false; 4015 for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, atom_list| { 4016 if (shdr.sh_type == elf.SHT_NOBITS) continue; 4017 if (atom_list.atoms.keys().len == 0) continue; 4018 atom_list.write(&buffer, &undefs, self) catch |err| switch (err) { 4019 error.UnsupportedCpuArch => { 4020 try self.reportUnsupportedCpuArch(); 4021 return error.FlushFailure; 4022 }, 4023 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, 4024 else => |e| return e, 4025 }; 4026 } 4027 4028 try self.reportUndefinedSymbols(&undefs); 4029 if (has_reloc_errors) return error.FlushFailure; 4030 4031 if (self.requiresThunks()) { 4032 for (self.thunks.items) |th| { 4033 const thunk_size = th.size(self); 4034 try buffer.ensureUnusedCapacity(thunk_size); 4035 const shdr = slice.items(.shdr)[th.output_section_index]; 4036 const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset; 4037 try th.write(self, buffer.writer()); 4038 assert(buffer.items.len == thunk_size); 4039 try self.base.file.?.pwriteAll(buffer.items, offset); 4040 buffer.clearRetainingCapacity(); 4041 } 4042 } 4043 } 4044 4045 pub fn updateSymtabSize(self: *Elf) !void { 4046 var nlocals: u32 = 0; 4047 var nglobals: u32 = 0; 4048 var strsize: u32 = 0; 4049 4050 const gpa = self.base.comp.gpa; 4051 var files = std.ArrayList(File.Index).init(gpa); 4052 defer files.deinit(); 4053 try files.ensureTotalCapacityPrecise(self.objects.items.len + self.shared_objects.items.len + 2); 4054 4055 if (self.zig_object_index) |index| files.appendAssumeCapacity(index); 4056 for (self.objects.items) |index| files.appendAssumeCapacity(index); 4057 for (self.shared_objects.items) |index| files.appendAssumeCapacity(index); 4058 if (self.linker_defined_index) |index| files.appendAssumeCapacity(index); 4059 4060 // Section symbols 4061 nlocals += @intCast(self.sections.slice().len); 4062 4063 if (self.requiresThunks()) for (self.thunks.items) |*th| { 4064 th.output_symtab_ctx.reset(); 4065 th.output_symtab_ctx.ilocal = nlocals; 4066 th.calcSymtabSize(self); 4067 nlocals += th.output_symtab_ctx.nlocals; 4068 strsize += th.output_symtab_ctx.strsize; 4069 }; 4070 4071 for (files.items) |index| { 4072 const file_ptr = self.file(index).?; 4073 const ctx = switch (file_ptr) { 4074 inline else => |x| &x.output_symtab_ctx, 4075 }; 4076 ctx.reset(); 4077 ctx.ilocal = nlocals; 4078 ctx.iglobal = nglobals; 4079 try file_ptr.updateSymtabSize(self); 4080 nlocals += ctx.nlocals; 4081 nglobals += ctx.nglobals; 4082 strsize += ctx.strsize; 4083 } 4084 4085 if (self.got_section_index) |_| { 4086 self.got.output_symtab_ctx.reset(); 4087 self.got.output_symtab_ctx.ilocal = nlocals; 4088 self.got.updateSymtabSize(self); 4089 nlocals += self.got.output_symtab_ctx.nlocals; 4090 strsize += self.got.output_symtab_ctx.strsize; 4091 } 4092 4093 if (self.plt_section_index) |_| { 4094 self.plt.output_symtab_ctx.reset(); 4095 self.plt.output_symtab_ctx.ilocal = nlocals; 4096 self.plt.updateSymtabSize(self); 4097 nlocals += self.plt.output_symtab_ctx.nlocals; 4098 strsize += self.plt.output_symtab_ctx.strsize; 4099 } 4100 4101 if (self.plt_got_section_index) |_| { 4102 self.plt_got.output_symtab_ctx.reset(); 4103 self.plt_got.output_symtab_ctx.ilocal = nlocals; 4104 self.plt_got.updateSymtabSize(self); 4105 nlocals += self.plt_got.output_symtab_ctx.nlocals; 4106 strsize += self.plt_got.output_symtab_ctx.strsize; 4107 } 4108 4109 for (files.items) |index| { 4110 const file_ptr = self.file(index).?; 4111 const ctx = switch (file_ptr) { 4112 inline else => |x| &x.output_symtab_ctx, 4113 }; 4114 ctx.iglobal += nlocals; 4115 } 4116 4117 const slice = self.sections.slice(); 4118 const symtab_shdr = &slice.items(.shdr)[self.symtab_section_index.?]; 4119 symtab_shdr.sh_info = nlocals; 4120 symtab_shdr.sh_link = self.strtab_section_index.?; 4121 4122 const sym_size: u64 = switch (self.ptr_width) { 4123 .p32 => @sizeOf(elf.Elf32_Sym), 4124 .p64 => @sizeOf(elf.Elf64_Sym), 4125 }; 4126 const needed_size = (nlocals + nglobals) * sym_size; 4127 symtab_shdr.sh_size = needed_size; 4128 4129 const strtab = &slice.items(.shdr)[self.strtab_section_index.?]; 4130 strtab.sh_size = strsize + 1; 4131 } 4132 4133 fn writeSyntheticSections(self: *Elf) !void { 4134 const gpa = self.base.comp.gpa; 4135 const slice = self.sections.slice(); 4136 4137 if (self.interp_section_index) |shndx| { 4138 var buffer: [256]u8 = undefined; 4139 const interp = self.getTarget().dynamic_linker.get().?; 4140 @memcpy(buffer[0..interp.len], interp); 4141 buffer[interp.len] = 0; 4142 const contents = buffer[0 .. interp.len + 1]; 4143 const shdr = slice.items(.shdr)[shndx]; 4144 assert(shdr.sh_size == contents.len); 4145 try self.base.file.?.pwriteAll(contents, shdr.sh_offset); 4146 } 4147 4148 if (self.hash_section_index) |shndx| { 4149 const shdr = slice.items(.shdr)[shndx]; 4150 try self.base.file.?.pwriteAll(self.hash.buffer.items, shdr.sh_offset); 4151 } 4152 4153 if (self.gnu_hash_section_index) |shndx| { 4154 const shdr = slice.items(.shdr)[shndx]; 4155 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size()); 4156 defer buffer.deinit(); 4157 try self.gnu_hash.write(self, buffer.writer()); 4158 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4159 } 4160 4161 if (self.versym_section_index) |shndx| { 4162 const shdr = slice.items(.shdr)[shndx]; 4163 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset); 4164 } 4165 4166 if (self.verneed_section_index) |shndx| { 4167 const shdr = slice.items(.shdr)[shndx]; 4168 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size()); 4169 defer buffer.deinit(); 4170 try self.verneed.write(buffer.writer()); 4171 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4172 } 4173 4174 if (self.dynamic_section_index) |shndx| { 4175 const shdr = slice.items(.shdr)[shndx]; 4176 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self)); 4177 defer buffer.deinit(); 4178 try self.dynamic.write(self, buffer.writer()); 4179 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4180 } 4181 4182 if (self.dynsymtab_section_index) |shndx| { 4183 const shdr = slice.items(.shdr)[shndx]; 4184 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size()); 4185 defer buffer.deinit(); 4186 try self.dynsym.write(self, buffer.writer()); 4187 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4188 } 4189 4190 if (self.dynstrtab_section_index) |shndx| { 4191 const shdr = slice.items(.shdr)[shndx]; 4192 try self.base.file.?.pwriteAll(self.dynstrtab.items, shdr.sh_offset); 4193 } 4194 4195 if (self.eh_frame_section_index) |shndx| { 4196 const existing_size = existing_size: { 4197 const zo = self.zigObjectPtr() orelse break :existing_size 0; 4198 const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0); 4199 break :existing_size sym.atom(self).?.size; 4200 }; 4201 const shdr = slice.items(.shdr)[shndx]; 4202 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; 4203 var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size)); 4204 defer buffer.deinit(); 4205 try eh_frame.writeEhFrame(self, buffer.writer()); 4206 assert(buffer.items.len == sh_size - existing_size); 4207 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset + existing_size); 4208 } 4209 4210 if (self.eh_frame_hdr_section_index) |shndx| { 4211 const shdr = slice.items(.shdr)[shndx]; 4212 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; 4213 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size); 4214 defer buffer.deinit(); 4215 try eh_frame.writeEhFrameHdr(self, buffer.writer()); 4216 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4217 } 4218 4219 if (self.got_section_index) |index| { 4220 const shdr = slice.items(.shdr)[index]; 4221 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self)); 4222 defer buffer.deinit(); 4223 try self.got.write(self, buffer.writer()); 4224 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4225 } 4226 4227 if (self.rela_dyn_section_index) |shndx| { 4228 const shdr = slice.items(.shdr)[shndx]; 4229 try self.got.addRela(self); 4230 try self.copy_rel.addRela(self); 4231 self.sortRelaDyn(); 4232 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset); 4233 } 4234 4235 if (self.plt_section_index) |shndx| { 4236 const shdr = slice.items(.shdr)[shndx]; 4237 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self)); 4238 defer buffer.deinit(); 4239 try self.plt.write(self, buffer.writer()); 4240 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4241 } 4242 4243 if (self.got_plt_section_index) |shndx| { 4244 const shdr = slice.items(.shdr)[shndx]; 4245 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self)); 4246 defer buffer.deinit(); 4247 try self.got_plt.write(self, buffer.writer()); 4248 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4249 } 4250 4251 if (self.plt_got_section_index) |shndx| { 4252 const shdr = slice.items(.shdr)[shndx]; 4253 var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self)); 4254 defer buffer.deinit(); 4255 try self.plt_got.write(self, buffer.writer()); 4256 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); 4257 } 4258 4259 if (self.rela_plt_section_index) |shndx| { 4260 const shdr = slice.items(.shdr)[shndx]; 4261 try self.plt.addRela(self); 4262 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset); 4263 } 4264 4265 try self.writeSymtab(); 4266 try self.writeShStrtab(); 4267 } 4268 4269 // FIXME:JK again, why is this needed? 4270 pub fn writeShStrtab(self: *Elf) !void { 4271 if (self.shstrtab_section_index) |index| { 4272 const shdr = self.sections.items(.shdr)[index]; 4273 log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size }); 4274 try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset); 4275 } 4276 } 4277 4278 pub fn writeSymtab(self: *Elf) !void { 4279 const gpa = self.base.comp.gpa; 4280 const slice = self.sections.slice(); 4281 const symtab_shdr = slice.items(.shdr)[self.symtab_section_index.?]; 4282 const strtab_shdr = slice.items(.shdr)[self.strtab_section_index.?]; 4283 const sym_size: u64 = switch (self.ptr_width) { 4284 .p32 => @sizeOf(elf.Elf32_Sym), 4285 .p64 => @sizeOf(elf.Elf64_Sym), 4286 }; 4287 const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow; 4288 4289 log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{ 4290 nsyms, 4291 symtab_shdr.sh_offset, 4292 symtab_shdr.sh_offset + symtab_shdr.sh_size, 4293 }); 4294 log.debug("writing .strtab from 0x{x} to 0x{x}", .{ 4295 strtab_shdr.sh_offset, 4296 strtab_shdr.sh_offset + strtab_shdr.sh_size, 4297 }); 4298 4299 try self.symtab.resize(gpa, nsyms); 4300 const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; 4301 // TODO we could resize instead and in ZigObject/Object always access as slice 4302 self.strtab.clearRetainingCapacity(); 4303 self.strtab.appendAssumeCapacity(0); 4304 try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); 4305 4306 for (slice.items(.shdr), 0..) |shdr, shndx| { 4307 const out_sym = &self.symtab.items[shndx]; 4308 out_sym.* = .{ 4309 .st_name = 0, 4310 .st_value = shdr.sh_addr, 4311 .st_info = if (shdr.sh_type == elf.SHT_NULL) elf.STT_NOTYPE else elf.STT_SECTION, 4312 .st_shndx = @intCast(shndx), 4313 .st_size = 0, 4314 .st_other = 0, 4315 }; 4316 } 4317 4318 if (self.requiresThunks()) for (self.thunks.items) |th| { 4319 th.writeSymtab(self); 4320 }; 4321 4322 if (self.zigObjectPtr()) |zig_object| { 4323 zig_object.asFile().writeSymtab(self); 4324 } 4325 4326 for (self.objects.items) |index| { 4327 const file_ptr = self.file(index).?; 4328 file_ptr.writeSymtab(self); 4329 } 4330 4331 for (self.shared_objects.items) |index| { 4332 const file_ptr = self.file(index).?; 4333 file_ptr.writeSymtab(self); 4334 } 4335 4336 if (self.linkerDefinedPtr()) |obj| { 4337 obj.asFile().writeSymtab(self); 4338 } 4339 4340 if (self.got_section_index) |_| { 4341 self.got.writeSymtab(self); 4342 } 4343 4344 if (self.plt_section_index) |_| { 4345 self.plt.writeSymtab(self); 4346 } 4347 4348 if (self.plt_got_section_index) |_| { 4349 self.plt_got.writeSymtab(self); 4350 } 4351 4352 const foreign_endian = self.getTarget().cpu.arch.endian() != builtin.cpu.arch.endian(); 4353 switch (self.ptr_width) { 4354 .p32 => { 4355 const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len); 4356 defer gpa.free(buf); 4357 4358 for (buf, self.symtab.items) |*out, sym| { 4359 out.* = .{ 4360 .st_name = sym.st_name, 4361 .st_info = sym.st_info, 4362 .st_other = sym.st_other, 4363 .st_shndx = sym.st_shndx, 4364 .st_value = @as(u32, @intCast(sym.st_value)), 4365 .st_size = @as(u32, @intCast(sym.st_size)), 4366 }; 4367 if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); 4368 } 4369 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset); 4370 }, 4371 .p64 => { 4372 if (foreign_endian) { 4373 for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); 4374 } 4375 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset); 4376 }, 4377 } 4378 4379 try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); 4380 } 4381 4382 /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. 4383 pub fn ptrWidthBytes(self: Elf) u8 { 4384 return switch (self.ptr_width) { 4385 .p32 => 4, 4386 .p64 => 8, 4387 }; 4388 } 4389 4390 /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes 4391 /// in a 32-bit ELF file. 4392 pub fn archPtrWidthBytes(self: Elf) u8 { 4393 return @intCast(@divExact(self.getTarget().ptrBitWidth(), 8)); 4394 } 4395 4396 fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { 4397 return .{ 4398 .p_type = phdr.p_type, 4399 .p_flags = phdr.p_flags, 4400 .p_offset = @as(u32, @intCast(phdr.p_offset)), 4401 .p_vaddr = @as(u32, @intCast(phdr.p_vaddr)), 4402 .p_paddr = @as(u32, @intCast(phdr.p_paddr)), 4403 .p_filesz = @as(u32, @intCast(phdr.p_filesz)), 4404 .p_memsz = @as(u32, @intCast(phdr.p_memsz)), 4405 .p_align = @as(u32, @intCast(phdr.p_align)), 4406 }; 4407 } 4408 4409 fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { 4410 return .{ 4411 .sh_name = shdr.sh_name, 4412 .sh_type = shdr.sh_type, 4413 .sh_flags = @as(u32, @intCast(shdr.sh_flags)), 4414 .sh_addr = @as(u32, @intCast(shdr.sh_addr)), 4415 .sh_offset = @as(u32, @intCast(shdr.sh_offset)), 4416 .sh_size = @as(u32, @intCast(shdr.sh_size)), 4417 .sh_link = shdr.sh_link, 4418 .sh_info = shdr.sh_info, 4419 .sh_addralign = @as(u32, @intCast(shdr.sh_addralign)), 4420 .sh_entsize = @as(u32, @intCast(shdr.sh_entsize)), 4421 }; 4422 } 4423 4424 fn getLDMOption(target: std.Target) ?[]const u8 { 4425 // This should only return emulations understood by LLD's parseEmulation(). 4426 return switch (target.cpu.arch) { 4427 .aarch64 => switch (target.os.tag) { 4428 .linux => "aarch64linux", 4429 else => "aarch64elf", 4430 }, 4431 .aarch64_be => switch (target.os.tag) { 4432 .linux => "aarch64linuxb", 4433 else => "aarch64elfb", 4434 }, 4435 .amdgcn => "elf64_amdgpu", 4436 .arm, .thumb => switch (target.os.tag) { 4437 .linux => "armelf_linux_eabi", 4438 else => "armelf", 4439 }, 4440 .armeb, .thumbeb => switch (target.os.tag) { 4441 .linux => "armelfb_linux_eabi", 4442 else => "armelfb", 4443 }, 4444 .hexagon => "hexagonelf", 4445 .loongarch32 => "elf32loongarch", 4446 .loongarch64 => "elf64loongarch", 4447 .mips => switch (target.os.tag) { 4448 .freebsd => "elf32btsmip_fbsd", 4449 else => "elf32btsmip", 4450 }, 4451 .mipsel => switch (target.os.tag) { 4452 .freebsd => "elf32ltsmip_fbsd", 4453 else => "elf32ltsmip", 4454 }, 4455 .mips64 => switch (target.os.tag) { 4456 .freebsd => switch (target.abi) { 4457 .gnuabin32 => "elf32btsmipn32_fbsd", 4458 else => "elf64btsmip_fbsd", 4459 }, 4460 else => switch (target.abi) { 4461 .gnuabin32 => "elf32btsmipn32", 4462 else => "elf64btsmip", 4463 }, 4464 }, 4465 .mips64el => switch (target.os.tag) { 4466 .freebsd => switch (target.abi) { 4467 .gnuabin32 => "elf32ltsmipn32_fbsd", 4468 else => "elf64ltsmip_fbsd", 4469 }, 4470 else => switch (target.abi) { 4471 .gnuabin32 => "elf32ltsmipn32", 4472 else => "elf64ltsmip", 4473 }, 4474 }, 4475 .msp430 => "msp430elf", 4476 .powerpc => switch (target.os.tag) { 4477 .freebsd => "elf32ppc_fbsd", 4478 .linux => "elf32ppclinux", 4479 else => "elf32ppc", 4480 }, 4481 .powerpcle => switch (target.os.tag) { 4482 .linux => "elf32lppclinux", 4483 else => "elf32lppc", 4484 }, 4485 .powerpc64 => "elf64ppc", 4486 .powerpc64le => "elf64lppc", 4487 .riscv32 => "elf32lriscv", 4488 .riscv64 => "elf64lriscv", 4489 .s390x => "elf64_s390", 4490 .sparc64 => "elf64_sparc", 4491 .x86 => switch (target.os.tag) { 4492 .elfiamcu => "elf_iamcu", 4493 .freebsd => "elf_i386_fbsd", 4494 else => "elf_i386", 4495 }, 4496 .x86_64 => switch (target.abi) { 4497 .gnux32, .muslx32 => "elf32_x86_64", 4498 else => "elf_x86_64", 4499 }, 4500 else => null, 4501 }; 4502 } 4503 4504 pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { 4505 return actual_size +| (actual_size / ideal_factor); 4506 } 4507 4508 // Provide a blueprint of csu (c-runtime startup) objects for supported 4509 // link modes. 4510 // 4511 // This is for cross-mode targets only. For host-mode targets the system 4512 // compiler can be probed to produce a robust blueprint. 4513 // 4514 // Targets requiring a libc for which zig does not bundle a libc are 4515 // host-mode targets. Unfortunately, host-mode probes are not yet 4516 // implemented. For now the data is hard-coded here. Such targets are 4517 // { freebsd, netbsd, openbsd, dragonfly }. 4518 const CsuObjects = struct { 4519 crt0: ?[]const u8 = null, 4520 crti: ?[]const u8 = null, 4521 crtbegin: ?[]const u8 = null, 4522 crtend: ?[]const u8 = null, 4523 crtn: ?[]const u8 = null, 4524 4525 const InitArgs = struct {}; 4526 4527 fn init(arena: Allocator, comp: *const Compilation) !CsuObjects { 4528 // crt objects are only required for libc. 4529 if (!comp.config.link_libc) return .{}; 4530 4531 var result: CsuObjects = .{}; 4532 4533 // Flatten crt cases. 4534 const mode: enum { 4535 dynamic_lib, 4536 dynamic_exe, 4537 dynamic_pie, 4538 static_exe, 4539 static_pie, 4540 } = switch (comp.config.output_mode) { 4541 .Obj => return CsuObjects{}, 4542 .Lib => switch (comp.config.link_mode) { 4543 .dynamic => .dynamic_lib, 4544 .static => return CsuObjects{}, 4545 }, 4546 .Exe => switch (comp.config.link_mode) { 4547 .dynamic => if (comp.config.pie) .dynamic_pie else .dynamic_exe, 4548 .static => if (comp.config.pie) .static_pie else .static_exe, 4549 }, 4550 }; 4551 4552 const target = comp.root_mod.resolved_target.result; 4553 4554 if (target.isAndroid()) { 4555 switch (mode) { 4556 // zig fmt: off 4557 .dynamic_lib => result.set( null, null, "crtbegin_so.o", "crtend_so.o", null ), 4558 .dynamic_exe, 4559 .dynamic_pie => result.set( null, null, "crtbegin_dynamic.o", "crtend_android.o", null ), 4560 .static_exe, 4561 .static_pie => result.set( null, null, "crtbegin_static.o", "crtend_android.o", null ), 4562 // zig fmt: on 4563 } 4564 } else { 4565 switch (target.os.tag) { 4566 .linux => { 4567 switch (mode) { 4568 // zig fmt: off 4569 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4570 .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 4571 .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4572 .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), 4573 .static_pie => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4574 // zig fmt: on 4575 } 4576 if (comp.libc_installation) |_| { 4577 // hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs 4578 // and they are not known at comptime. For now null-out crtbegin/end objects; 4579 // there is no feature loss, zig has never linked those objects in before. 4580 result.crtbegin = null; 4581 result.crtend = null; 4582 } else { 4583 // Bundled glibc only has Scrt1.o . 4584 if (result.crt0 != null and target.isGnuLibC()) result.crt0 = "Scrt1.o"; 4585 } 4586 }, 4587 .dragonfly => switch (mode) { 4588 // zig fmt: off 4589 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4590 .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 4591 .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4592 .static_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 4593 .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4594 // zig fmt: on 4595 }, 4596 .freebsd => switch (mode) { 4597 // zig fmt: off 4598 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4599 .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 4600 .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4601 .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), 4602 .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4603 // zig fmt: on 4604 }, 4605 .netbsd => switch (mode) { 4606 // zig fmt: off 4607 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4608 .dynamic_exe => result.set( "crt0.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 4609 .dynamic_pie => result.set( "crt0.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4610 .static_exe => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), 4611 .static_pie => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtendS.o", "crtn.o" ), 4612 // zig fmt: on 4613 }, 4614 .openbsd => switch (mode) { 4615 // zig fmt: off 4616 .dynamic_lib => result.set( null, null, "crtbeginS.o", "crtendS.o", null ), 4617 .dynamic_exe, 4618 .dynamic_pie => result.set( "crt0.o", null, "crtbegin.o", "crtend.o", null ), 4619 .static_exe, 4620 .static_pie => result.set( "rcrt0.o", null, "crtbegin.o", "crtend.o", null ), 4621 // zig fmt: on 4622 }, 4623 .haiku => switch (mode) { 4624 // zig fmt: off 4625 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4626 .dynamic_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 4627 .dynamic_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4628 .static_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 4629 .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 4630 // zig fmt: on 4631 }, 4632 .solaris, .illumos => switch (mode) { 4633 // zig fmt: off 4634 .dynamic_lib => result.set( null, "crti.o", null, null, "crtn.o" ), 4635 .dynamic_exe, 4636 .dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ), 4637 .static_exe, 4638 .static_pie => result.set( null, null, null, null, null ), 4639 // zig fmt: on 4640 }, 4641 else => {}, 4642 } 4643 } 4644 4645 // Convert each object to a full pathname. 4646 if (comp.libc_installation) |lci| { 4647 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; 4648 switch (target.os.tag) { 4649 .dragonfly => { 4650 if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 4651 if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 4652 if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 4653 4654 var gccv: []const u8 = undefined; 4655 if (target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0 }) orelse true) { 4656 gccv = "gcc80"; 4657 } else { 4658 gccv = "gcc54"; 4659 } 4660 4661 if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); 4662 if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); 4663 }, 4664 .haiku => { 4665 const gcc_dir_path = lci.gcc_dir orelse return error.LibCInstallationMissingCRTDir; 4666 if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 4667 if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 4668 if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 4669 4670 if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); 4671 if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); 4672 }, 4673 else => { 4674 inline for (std.meta.fields(@TypeOf(result))) |f| { 4675 if (@field(result, f.name)) |*obj| { 4676 obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 4677 } 4678 } 4679 }, 4680 } 4681 } else { 4682 inline for (std.meta.fields(@TypeOf(result))) |f| { 4683 if (@field(result, f.name)) |*obj| { 4684 if (comp.crt_files.get(obj.*)) |crtf| { 4685 obj.* = crtf.full_object_path; 4686 } else { 4687 @field(result, f.name) = null; 4688 } 4689 } 4690 } 4691 } 4692 4693 return result; 4694 } 4695 4696 fn set( 4697 self: *CsuObjects, 4698 crt0: ?[]const u8, 4699 crti: ?[]const u8, 4700 crtbegin: ?[]const u8, 4701 crtend: ?[]const u8, 4702 crtn: ?[]const u8, 4703 ) void { 4704 self.crt0 = crt0; 4705 self.crti = crti; 4706 self.crtbegin = crtbegin; 4707 self.crtend = crtend; 4708 self.crtn = crtn; 4709 } 4710 }; 4711 4712 /// If a target compiles other output modes as dynamic libraries, 4713 /// this function returns true for those too. 4714 pub fn isEffectivelyDynLib(self: Elf) bool { 4715 if (self.base.isDynLib()) return true; 4716 return switch (self.getTarget().os.tag) { 4717 .haiku => self.base.isExe(), 4718 else => false, 4719 }; 4720 } 4721 4722 fn getPhdr(self: *Elf, opts: struct { 4723 type: u32 = 0, 4724 flags: u32 = 0, 4725 }) ?u16 { 4726 for (self.phdrs.items, 0..) |phdr, phndx| { 4727 if (self.phdr_table_load_index) |index| { 4728 if (phndx == index) continue; 4729 } 4730 if (phdr.p_type == opts.type and phdr.p_flags == opts.flags) return @intCast(phndx); 4731 } 4732 return null; 4733 } 4734 4735 fn addPhdr(self: *Elf, opts: struct { 4736 type: u32 = 0, 4737 flags: u32 = 0, 4738 @"align": u64 = 0, 4739 offset: u64 = 0, 4740 addr: u64 = 0, 4741 filesz: u64 = 0, 4742 memsz: u64 = 0, 4743 }) error{OutOfMemory}!u16 { 4744 const gpa = self.base.comp.gpa; 4745 const index = @as(u16, @intCast(self.phdrs.items.len)); 4746 try self.phdrs.append(gpa, .{ 4747 .p_type = opts.type, 4748 .p_flags = opts.flags, 4749 .p_offset = opts.offset, 4750 .p_vaddr = opts.addr, 4751 .p_paddr = opts.addr, 4752 .p_filesz = opts.filesz, 4753 .p_memsz = opts.memsz, 4754 .p_align = opts.@"align", 4755 }); 4756 return index; 4757 } 4758 4759 pub fn addRelaShdr(self: *Elf, name: u32, shndx: u32) !u32 { 4760 const entsize: u64 = switch (self.ptr_width) { 4761 .p32 => @sizeOf(elf.Elf32_Rela), 4762 .p64 => @sizeOf(elf.Elf64_Rela), 4763 }; 4764 const addralign: u64 = switch (self.ptr_width) { 4765 .p32 => @alignOf(elf.Elf32_Rela), 4766 .p64 => @alignOf(elf.Elf64_Rela), 4767 }; 4768 return self.addSection(.{ 4769 .name = name, 4770 .type = elf.SHT_RELA, 4771 .flags = elf.SHF_INFO_LINK, 4772 .entsize = entsize, 4773 .info = shndx, 4774 .addralign = addralign, 4775 .offset = std.math.maxInt(u64), 4776 }); 4777 } 4778 4779 pub const AddSectionOpts = struct { 4780 name: u32 = 0, 4781 type: u32 = elf.SHT_NULL, 4782 flags: u64 = 0, 4783 link: u32 = 0, 4784 info: u32 = 0, 4785 addralign: u64 = 0, 4786 entsize: u64 = 0, 4787 offset: u64 = 0, 4788 }; 4789 4790 pub fn addSection(self: *Elf, opts: AddSectionOpts) !u32 { 4791 const gpa = self.base.comp.gpa; 4792 const index: u32 = @intCast(try self.sections.addOne(gpa)); 4793 self.sections.set(index, .{ 4794 .shdr = .{ 4795 .sh_name = opts.name, 4796 .sh_type = opts.type, 4797 .sh_flags = opts.flags, 4798 .sh_addr = 0, 4799 .sh_offset = opts.offset, 4800 .sh_size = 0, 4801 .sh_link = opts.link, 4802 .sh_info = opts.info, 4803 .sh_addralign = opts.addralign, 4804 .sh_entsize = opts.entsize, 4805 }, 4806 }); 4807 return index; 4808 } 4809 4810 pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u32 { 4811 for (self.sections.items(.shdr), 0..) |*shdr, i| { 4812 const this_name = self.getShString(shdr.sh_name); 4813 if (mem.eql(u8, this_name, name)) return @intCast(i); 4814 } else return null; 4815 } 4816 4817 const RelaDyn = struct { 4818 offset: u64, 4819 sym: u64 = 0, 4820 type: u32, 4821 addend: i64 = 0, 4822 target: ?*const Symbol = null, 4823 }; 4824 4825 pub fn addRelaDyn(self: *Elf, opts: RelaDyn) !void { 4826 try self.rela_dyn.ensureUnusedCapacity(self.base.alloctor, 1); 4827 self.addRelaDynAssumeCapacity(opts); 4828 } 4829 4830 pub fn addRelaDynAssumeCapacity(self: *Elf, opts: RelaDyn) void { 4831 relocs_log.debug(" {s}: [{x} => {d}({s})] + {x}", .{ 4832 relocation.fmtRelocType(opts.type, self.getTarget().cpu.arch), 4833 opts.offset, 4834 opts.sym, 4835 if (opts.target) |sym| sym.name(self) else "", 4836 opts.addend, 4837 }); 4838 self.rela_dyn.appendAssumeCapacity(.{ 4839 .r_offset = opts.offset, 4840 .r_info = (opts.sym << 32) | opts.type, 4841 .r_addend = opts.addend, 4842 }); 4843 } 4844 4845 fn sortRelaDyn(self: *Elf) void { 4846 const Sort = struct { 4847 fn rank(rel: elf.Elf64_Rela, ctx: *Elf) u2 { 4848 const cpu_arch = ctx.getTarget().cpu.arch; 4849 const r_type = rel.r_type(); 4850 const r_kind = relocation.decode(r_type, cpu_arch).?; 4851 return switch (r_kind) { 4852 .rel => 0, 4853 .irel => 2, 4854 else => 1, 4855 }; 4856 } 4857 4858 pub fn lessThan(ctx: *Elf, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool { 4859 if (rank(lhs, ctx) == rank(rhs, ctx)) { 4860 if (lhs.r_sym() == rhs.r_sym()) return lhs.r_offset < rhs.r_offset; 4861 return lhs.r_sym() < rhs.r_sym(); 4862 } 4863 return rank(lhs, ctx) < rank(rhs, ctx); 4864 } 4865 }; 4866 mem.sort(elf.Elf64_Rela, self.rela_dyn.items, self, Sort.lessThan); 4867 } 4868 4869 pub fn calcNumIRelativeRelocs(self: *Elf) usize { 4870 var count: usize = self.num_ifunc_dynrelocs; 4871 4872 for (self.got.entries.items) |entry| { 4873 if (entry.tag != .got) continue; 4874 const sym = self.symbol(entry.ref).?; 4875 if (sym.isIFunc(self)) count += 1; 4876 } 4877 4878 return count; 4879 } 4880 4881 pub fn getStartStopBasename(self: Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { 4882 const name = self.getShString(shdr.sh_name); 4883 if (shdr.sh_flags & elf.SHF_ALLOC != 0 and name.len > 0) { 4884 if (Elf.isCIdentifier(name)) return name; 4885 } 4886 return null; 4887 } 4888 4889 pub fn isCIdentifier(name: []const u8) bool { 4890 if (name.len == 0) return false; 4891 const first_c = name[0]; 4892 if (!std.ascii.isAlphabetic(first_c) and first_c != '_') return false; 4893 for (name[1..]) |c| { 4894 if (!std.ascii.isAlphanumeric(c) and c != '_') return false; 4895 } 4896 return true; 4897 } 4898 4899 pub fn addThunk(self: *Elf) !Thunk.Index { 4900 const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); 4901 const th = try self.thunks.addOne(self.base.comp.gpa); 4902 th.* = .{}; 4903 return index; 4904 } 4905 4906 pub fn thunk(self: *Elf, index: Thunk.Index) *Thunk { 4907 assert(index < self.thunks.items.len); 4908 return &self.thunks.items[index]; 4909 } 4910 4911 pub fn file(self: *Elf, index: File.Index) ?File { 4912 const tag = self.files.items(.tags)[index]; 4913 return switch (tag) { 4914 .null => null, 4915 .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined }, 4916 .zig_object => .{ .zig_object = &self.files.items(.data)[index].zig_object }, 4917 .object => .{ .object = &self.files.items(.data)[index].object }, 4918 .shared_object => .{ .shared_object = &self.files.items(.data)[index].shared_object }, 4919 }; 4920 } 4921 4922 pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex { 4923 const gpa = self.base.comp.gpa; 4924 const index: File.HandleIndex = @intCast(self.file_handles.items.len); 4925 const fh = try self.file_handles.addOne(gpa); 4926 fh.* = handle; 4927 return index; 4928 } 4929 4930 pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle { 4931 assert(index < self.file_handles.items.len); 4932 return self.file_handles.items[index]; 4933 } 4934 4935 pub fn atom(self: *Elf, ref: Ref) ?*Atom { 4936 const file_ptr = self.file(ref.file) orelse return null; 4937 return file_ptr.atom(ref.index); 4938 } 4939 4940 pub fn comdatGroup(self: *Elf, ref: Ref) *ComdatGroup { 4941 return self.file(ref.file).?.comdatGroup(ref.index); 4942 } 4943 4944 pub fn symbol(self: *Elf, ref: Ref) ?*Symbol { 4945 const file_ptr = self.file(ref.file) orelse return null; 4946 return file_ptr.symbol(ref.index); 4947 } 4948 4949 pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { 4950 return self.zigObjectPtr().?.getGlobalSymbol(self, name, lib_name); 4951 } 4952 4953 pub fn zigObjectPtr(self: *Elf) ?*ZigObject { 4954 const index = self.zig_object_index orelse return null; 4955 return self.file(index).?.zig_object; 4956 } 4957 4958 pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined { 4959 const index = self.linker_defined_index orelse return null; 4960 return self.file(index).?.linker_defined; 4961 } 4962 4963 pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !MergeSection.Index { 4964 const gpa = self.base.comp.gpa; 4965 const out_name = name: { 4966 if (self.base.isRelocatable()) break :name name; 4967 if (mem.eql(u8, name, ".rodata") or mem.startsWith(u8, name, ".rodata")) 4968 break :name if (flags & elf.SHF_STRINGS != 0) ".rodata.str" else ".rodata.cst"; 4969 break :name name; 4970 }; 4971 for (self.merge_sections.items, 0..) |msec, index| { 4972 if (mem.eql(u8, msec.name(self), out_name)) return @intCast(index); 4973 } 4974 const out_off = try self.insertShString(out_name); 4975 const out_flags = flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP); 4976 const index = @as(MergeSection.Index, @intCast(self.merge_sections.items.len)); 4977 const msec = try self.merge_sections.addOne(gpa); 4978 msec.* = .{ 4979 .name_offset = out_off, 4980 .flags = out_flags, 4981 .type = @"type", 4982 }; 4983 return index; 4984 } 4985 4986 pub fn mergeSection(self: *Elf, index: MergeSection.Index) *MergeSection { 4987 assert(index < self.merge_sections.items.len); 4988 return &self.merge_sections.items[index]; 4989 } 4990 4991 pub fn gotAddress(self: *Elf) i64 { 4992 const shndx = blk: { 4993 if (self.getTarget().cpu.arch == .x86_64 and self.got_plt_section_index != null) 4994 break :blk self.got_plt_section_index.?; 4995 break :blk if (self.got_section_index) |shndx| shndx else null; 4996 }; 4997 return if (shndx) |index| @intCast(self.sections.items(.shdr)[index].sh_addr) else 0; 4998 } 4999 5000 pub fn tpAddress(self: *Elf) i64 { 5001 const index = self.phdr_tls_index orelse return 0; 5002 const phdr = self.phdrs.items[index]; 5003 const addr = switch (self.getTarget().cpu.arch) { 5004 .x86_64 => mem.alignForward(u64, phdr.p_vaddr + phdr.p_memsz, phdr.p_align), 5005 .aarch64 => mem.alignBackward(u64, phdr.p_vaddr - 16, phdr.p_align), 5006 .riscv64 => phdr.p_vaddr, 5007 else => |arch| std.debug.panic("TODO implement getTpAddress for {s}", .{@tagName(arch)}), 5008 }; 5009 return @intCast(addr); 5010 } 5011 5012 pub fn dtpAddress(self: *Elf) i64 { 5013 const index = self.phdr_tls_index orelse return 0; 5014 const phdr = self.phdrs.items[index]; 5015 return @intCast(phdr.p_vaddr); 5016 } 5017 5018 pub fn tlsAddress(self: *Elf) i64 { 5019 const index = self.phdr_tls_index orelse return 0; 5020 const phdr = self.phdrs.items[index]; 5021 return @intCast(phdr.p_vaddr); 5022 } 5023 5024 pub fn getShString(self: Elf, off: u32) [:0]const u8 { 5025 assert(off < self.shstrtab.items.len); 5026 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0); 5027 } 5028 5029 pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 { 5030 const gpa = self.base.comp.gpa; 5031 const off = @as(u32, @intCast(self.shstrtab.items.len)); 5032 try self.shstrtab.ensureUnusedCapacity(gpa, name.len + 1); 5033 self.shstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable; 5034 return off; 5035 } 5036 5037 pub fn getDynString(self: Elf, off: u32) [:0]const u8 { 5038 assert(off < self.dynstrtab.items.len); 5039 return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.dynstrtab.items.ptr + off)), 0); 5040 } 5041 5042 pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 { 5043 const gpa = self.base.comp.gpa; 5044 const off = @as(u32, @intCast(self.dynstrtab.items.len)); 5045 try self.dynstrtab.ensureUnusedCapacity(gpa, name.len + 1); 5046 self.dynstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable; 5047 return off; 5048 } 5049 5050 fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { 5051 const gpa = self.base.comp.gpa; 5052 const max_notes = 4; 5053 5054 try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count()); 5055 5056 for (undefs.keys(), undefs.values()) |key, refs| { 5057 const undef_sym = self.resolver.keys.items[key - 1]; 5058 const nrefs = @min(refs.items.len, max_notes); 5059 const nnotes = nrefs + @intFromBool(refs.items.len > max_notes); 5060 5061 var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes); 5062 try err.addMsg("undefined symbol: {s}", .{undef_sym.name(self)}); 5063 5064 for (refs.items[0..nrefs]) |ref| { 5065 const atom_ptr = self.atom(ref).?; 5066 const file_ptr = atom_ptr.file(self).?; 5067 try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) }); 5068 } 5069 5070 if (refs.items.len > max_notes) { 5071 const remaining = refs.items.len - max_notes; 5072 try err.addNote("referenced {d} more times", .{remaining}); 5073 } 5074 } 5075 } 5076 5077 fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void { 5078 if (dupes.keys().len == 0) return; // Nothing to do 5079 5080 const max_notes = 3; 5081 5082 for (dupes.keys(), dupes.values()) |key, notes| { 5083 const sym = self.resolver.keys.items[key - 1]; 5084 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); 5085 5086 var err = try self.base.addErrorWithNotes(nnotes + 1); 5087 try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)}); 5088 try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()}); 5089 5090 var inote: usize = 0; 5091 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { 5092 const file_ptr = self.file(notes.items[inote]).?; 5093 try err.addNote("defined by {}", .{file_ptr.fmtPath()}); 5094 } 5095 5096 if (notes.items.len > max_notes) { 5097 const remaining = notes.items.len - max_notes; 5098 try err.addNote("defined {d} more times", .{remaining}); 5099 } 5100 } 5101 5102 return error.HasDuplicates; 5103 } 5104 5105 fn reportMissingLibraryError( 5106 self: *Elf, 5107 checked_paths: []const []const u8, 5108 comptime format: []const u8, 5109 args: anytype, 5110 ) error{OutOfMemory}!void { 5111 var err = try self.base.addErrorWithNotes(checked_paths.len); 5112 try err.addMsg(format, args); 5113 for (checked_paths) |path| { 5114 try err.addNote("tried {s}", .{path}); 5115 } 5116 } 5117 5118 fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void { 5119 var err = try self.base.addErrorWithNotes(0); 5120 try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{ 5121 @tagName(self.getTarget().cpu.arch), 5122 }); 5123 } 5124 5125 pub fn addParseError( 5126 self: *Elf, 5127 path: []const u8, 5128 comptime format: []const u8, 5129 args: anytype, 5130 ) error{OutOfMemory}!void { 5131 var err = try self.base.addErrorWithNotes(1); 5132 try err.addMsg(format, args); 5133 try err.addNote("while parsing {s}", .{path}); 5134 } 5135 5136 pub fn addFileError( 5137 self: *Elf, 5138 file_index: File.Index, 5139 comptime format: []const u8, 5140 args: anytype, 5141 ) error{OutOfMemory}!void { 5142 var err = try self.base.addErrorWithNotes(1); 5143 try err.addMsg(format, args); 5144 try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()}); 5145 } 5146 5147 pub fn failFile( 5148 self: *Elf, 5149 file_index: File.Index, 5150 comptime format: []const u8, 5151 args: anytype, 5152 ) error{ OutOfMemory, LinkFailure } { 5153 try addFileError(self, file_index, format, args); 5154 return error.LinkFailure; 5155 } 5156 5157 pub fn failParse( 5158 self: *Elf, 5159 path: []const u8, 5160 comptime format: []const u8, 5161 args: anytype, 5162 ) error{ OutOfMemory, LinkFailure } { 5163 try addParseError(self, path, format, args); 5164 return error.LinkFailure; 5165 } 5166 5167 const FormatShdrCtx = struct { 5168 elf_file: *Elf, 5169 shdr: elf.Elf64_Shdr, 5170 }; 5171 5172 fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) { 5173 return .{ .data = .{ 5174 .shdr = shdr, 5175 .elf_file = self, 5176 } }; 5177 } 5178 5179 fn formatShdr( 5180 ctx: FormatShdrCtx, 5181 comptime unused_fmt_string: []const u8, 5182 options: std.fmt.FormatOptions, 5183 writer: anytype, 5184 ) !void { 5185 _ = options; 5186 _ = unused_fmt_string; 5187 const shdr = ctx.shdr; 5188 try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({})", .{ 5189 ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset, 5190 shdr.sh_addr, shdr.sh_addralign, 5191 shdr.sh_size, shdr.sh_entsize, 5192 fmtShdrFlags(shdr.sh_flags), 5193 }); 5194 } 5195 5196 pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) { 5197 return .{ .data = sh_flags }; 5198 } 5199 5200 fn formatShdrFlags( 5201 sh_flags: u64, 5202 comptime unused_fmt_string: []const u8, 5203 options: std.fmt.FormatOptions, 5204 writer: anytype, 5205 ) !void { 5206 _ = unused_fmt_string; 5207 _ = options; 5208 if (elf.SHF_WRITE & sh_flags != 0) { 5209 try writer.writeAll("W"); 5210 } 5211 if (elf.SHF_ALLOC & sh_flags != 0) { 5212 try writer.writeAll("A"); 5213 } 5214 if (elf.SHF_EXECINSTR & sh_flags != 0) { 5215 try writer.writeAll("X"); 5216 } 5217 if (elf.SHF_MERGE & sh_flags != 0) { 5218 try writer.writeAll("M"); 5219 } 5220 if (elf.SHF_STRINGS & sh_flags != 0) { 5221 try writer.writeAll("S"); 5222 } 5223 if (elf.SHF_INFO_LINK & sh_flags != 0) { 5224 try writer.writeAll("I"); 5225 } 5226 if (elf.SHF_LINK_ORDER & sh_flags != 0) { 5227 try writer.writeAll("L"); 5228 } 5229 if (elf.SHF_EXCLUDE & sh_flags != 0) { 5230 try writer.writeAll("E"); 5231 } 5232 if (elf.SHF_COMPRESSED & sh_flags != 0) { 5233 try writer.writeAll("C"); 5234 } 5235 if (elf.SHF_GROUP & sh_flags != 0) { 5236 try writer.writeAll("G"); 5237 } 5238 if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) { 5239 try writer.writeAll("O"); 5240 } 5241 if (elf.SHF_TLS & sh_flags != 0) { 5242 try writer.writeAll("T"); 5243 } 5244 if (elf.SHF_X86_64_LARGE & sh_flags != 0) { 5245 try writer.writeAll("l"); 5246 } 5247 if (elf.SHF_MIPS_ADDR & sh_flags != 0 or elf.SHF_ARM_PURECODE & sh_flags != 0) { 5248 try writer.writeAll("p"); 5249 } 5250 } 5251 5252 const FormatPhdrCtx = struct { 5253 elf_file: *Elf, 5254 phdr: elf.Elf64_Phdr, 5255 }; 5256 5257 fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) { 5258 return .{ .data = .{ 5259 .phdr = phdr, 5260 .elf_file = self, 5261 } }; 5262 } 5263 5264 fn formatPhdr( 5265 ctx: FormatPhdrCtx, 5266 comptime unused_fmt_string: []const u8, 5267 options: std.fmt.FormatOptions, 5268 writer: anytype, 5269 ) !void { 5270 _ = options; 5271 _ = unused_fmt_string; 5272 const phdr = ctx.phdr; 5273 const write = phdr.p_flags & elf.PF_W != 0; 5274 const read = phdr.p_flags & elf.PF_R != 0; 5275 const exec = phdr.p_flags & elf.PF_X != 0; 5276 var flags: [3]u8 = [_]u8{'_'} ** 3; 5277 if (exec) flags[0] = 'X'; 5278 if (write) flags[1] = 'W'; 5279 if (read) flags[2] = 'R'; 5280 const p_type = switch (phdr.p_type) { 5281 elf.PT_LOAD => "LOAD", 5282 elf.PT_TLS => "TLS", 5283 elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME", 5284 elf.PT_GNU_STACK => "GNU_STACK", 5285 elf.PT_DYNAMIC => "DYNAMIC", 5286 elf.PT_INTERP => "INTERP", 5287 elf.PT_NULL => "NULL", 5288 elf.PT_PHDR => "PHDR", 5289 elf.PT_NOTE => "NOTE", 5290 else => "UNKNOWN", 5291 }; 5292 try writer.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{ 5293 p_type, flags, phdr.p_offset, phdr.p_vaddr, 5294 phdr.p_align, phdr.p_filesz, phdr.p_memsz, 5295 }); 5296 } 5297 5298 pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { 5299 return .{ .data = self }; 5300 } 5301 5302 fn fmtDumpState( 5303 self: *Elf, 5304 comptime unused_fmt_string: []const u8, 5305 options: std.fmt.FormatOptions, 5306 writer: anytype, 5307 ) !void { 5308 _ = unused_fmt_string; 5309 _ = options; 5310 5311 if (self.zigObjectPtr()) |zig_object| { 5312 try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path }); 5313 try writer.print("{}{}", .{ 5314 zig_object.fmtAtoms(self), 5315 zig_object.fmtSymtab(self), 5316 }); 5317 try writer.writeByte('\n'); 5318 } 5319 5320 for (self.objects.items) |index| { 5321 const object = self.file(index).?.object; 5322 try writer.print("object({d}) : {}", .{ index, object.fmtPath() }); 5323 if (!object.alive) try writer.writeAll(" : [*]"); 5324 try writer.writeByte('\n'); 5325 try writer.print("{}{}{}{}{}\n", .{ 5326 object.fmtAtoms(self), 5327 object.fmtCies(self), 5328 object.fmtFdes(self), 5329 object.fmtSymtab(self), 5330 object.fmtComdatGroups(self), 5331 }); 5332 } 5333 5334 for (self.shared_objects.items) |index| { 5335 const shared_object = self.file(index).?.shared_object; 5336 try writer.print("shared_object({d}) : ", .{index}); 5337 try writer.print("{s}", .{shared_object.path}); 5338 try writer.print(" : needed({})", .{shared_object.needed}); 5339 if (!shared_object.alive) try writer.writeAll(" : [*]"); 5340 try writer.writeByte('\n'); 5341 try writer.print("{}\n", .{shared_object.fmtSymtab(self)}); 5342 } 5343 5344 if (self.linker_defined_index) |index| { 5345 const linker_defined = self.file(index).?.linker_defined; 5346 try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); 5347 try writer.print("{}\n", .{linker_defined.fmtSymtab(self)}); 5348 } 5349 5350 const slice = self.sections.slice(); 5351 { 5352 try writer.writeAll("atom lists\n"); 5353 for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| { 5354 try writer.print("shdr({d}) : {s} : {}\n", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) }); 5355 } 5356 } 5357 5358 if (self.requiresThunks()) { 5359 try writer.writeAll("thunks\n"); 5360 for (self.thunks.items, 0..) |th, index| { 5361 try writer.print("thunk({d}) : {}\n", .{ index, th.fmt(self) }); 5362 } 5363 } 5364 5365 try writer.print("{}\n", .{self.got.fmt(self)}); 5366 try writer.print("{}\n", .{self.plt.fmt(self)}); 5367 5368 try writer.writeAll("Output COMDAT groups\n"); 5369 for (self.comdat_group_sections.items) |cg| { 5370 try writer.print(" shdr({d}) : COMDAT({})\n", .{ cg.shndx, cg.cg_ref }); 5371 } 5372 5373 try writer.writeAll("\nOutput merge sections\n"); 5374 for (self.merge_sections.items) |msec| { 5375 try writer.print(" shdr({d}) : {}\n", .{ msec.output_section_index, msec.fmt(self) }); 5376 } 5377 5378 try writer.writeAll("\nOutput shdrs\n"); 5379 for (slice.items(.shdr), slice.items(.phndx), 0..) |shdr, phndx, shndx| { 5380 try writer.print(" shdr({d}) : phdr({?d}) : {}\n", .{ 5381 shndx, 5382 phndx, 5383 self.fmtShdr(shdr), 5384 }); 5385 } 5386 try writer.writeAll("\nOutput phdrs\n"); 5387 for (self.phdrs.items, 0..) |phdr, phndx| { 5388 try writer.print(" phdr({d}) : {}\n", .{ phndx, self.fmtPhdr(phdr) }); 5389 } 5390 } 5391 5392 /// Caller owns the memory. 5393 pub fn preadAllAlloc(allocator: Allocator, handle: fs.File, offset: u64, size: u64) ![]u8 { 5394 const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow); 5395 errdefer allocator.free(buffer); 5396 const amt = try handle.preadAll(buffer, offset); 5397 if (amt != size) return error.InputOutput; 5398 return buffer; 5399 } 5400 5401 /// Binary search 5402 pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { 5403 if (!@hasDecl(@TypeOf(predicate), "predicate")) 5404 @compileError("Predicate is required to define fn predicate(@This(), T) bool"); 5405 5406 var min: usize = 0; 5407 var max: usize = haystack.len; 5408 while (min < max) { 5409 const index = (min + max) / 2; 5410 const curr = haystack[index]; 5411 if (predicate.predicate(curr)) { 5412 min = index + 1; 5413 } else { 5414 max = index; 5415 } 5416 } 5417 return min; 5418 } 5419 5420 /// Linear search 5421 pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { 5422 if (!@hasDecl(@TypeOf(predicate), "predicate")) 5423 @compileError("Predicate is required to define fn predicate(@This(), T) bool"); 5424 5425 var i: usize = 0; 5426 while (i < haystack.len) : (i += 1) { 5427 if (predicate.predicate(haystack[i])) break; 5428 } 5429 return i; 5430 } 5431 5432 pub fn getTarget(self: Elf) std.Target { 5433 return self.base.comp.root_mod.resolved_target.result; 5434 } 5435 5436 fn requiresThunks(self: Elf) bool { 5437 return switch (self.getTarget().cpu.arch) { 5438 .aarch64 => true, 5439 .x86_64, .riscv64 => false, 5440 else => @panic("TODO unimplemented architecture"), 5441 }; 5442 } 5443 5444 /// The following three values are only observed at compile-time and used to emit a compile error 5445 /// to remind the programmer to update expected maximum numbers of different program header types 5446 /// so that we reserve enough space for the program header table up-front. 5447 /// Bump these numbers when adding or deleting a Zig specific pre-allocated segment, or adding 5448 /// more special-purpose program headers. 5449 const max_number_of_object_segments = 9; 5450 const max_number_of_special_phdrs = 5; 5451 5452 const default_entry_addr = 0x8000000; 5453 5454 pub const base_tag: link.File.Tag = .elf; 5455 5456 pub const ComdatGroup = struct { 5457 signature_off: u32, 5458 file_index: File.Index, 5459 shndx: u32, 5460 members_start: u32, 5461 members_len: u32, 5462 alive: bool = true, 5463 5464 pub fn file(cg: ComdatGroup, elf_file: *Elf) File { 5465 return elf_file.file(cg.file_index).?; 5466 } 5467 5468 pub fn signature(cg: ComdatGroup, elf_file: *Elf) [:0]const u8 { 5469 return cg.file(elf_file).object.getString(cg.signature_off); 5470 } 5471 5472 pub fn comdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 { 5473 const object = cg.file(elf_file).object; 5474 return object.comdat_group_data.items[cg.members_start..][0..cg.members_len]; 5475 } 5476 5477 pub const Index = u32; 5478 }; 5479 5480 pub const SymtabCtx = struct { 5481 ilocal: u32 = 0, 5482 iglobal: u32 = 0, 5483 nlocals: u32 = 0, 5484 nglobals: u32 = 0, 5485 strsize: u32 = 0, 5486 5487 pub fn reset(ctx: *SymtabCtx) void { 5488 ctx.ilocal = 0; 5489 ctx.iglobal = 0; 5490 ctx.nlocals = 0; 5491 ctx.nglobals = 0; 5492 ctx.strsize = 0; 5493 } 5494 }; 5495 5496 pub const null_sym = elf.Elf64_Sym{ 5497 .st_name = 0, 5498 .st_info = 0, 5499 .st_other = 0, 5500 .st_shndx = 0, 5501 .st_value = 0, 5502 .st_size = 0, 5503 }; 5504 5505 pub const null_shdr = elf.Elf64_Shdr{ 5506 .sh_name = 0, 5507 .sh_type = 0, 5508 .sh_flags = 0, 5509 .sh_addr = 0, 5510 .sh_offset = 0, 5511 .sh_size = 0, 5512 .sh_link = 0, 5513 .sh_info = 0, 5514 .sh_addralign = 0, 5515 .sh_entsize = 0, 5516 }; 5517 5518 pub const SystemLib = struct { 5519 needed: bool = false, 5520 path: []const u8, 5521 }; 5522 5523 pub const Ref = struct { 5524 index: u32 = 0, 5525 file: u32 = 0, 5526 5527 pub fn eql(ref: Ref, other: Ref) bool { 5528 return ref.index == other.index and ref.file == other.file; 5529 } 5530 5531 pub fn format( 5532 ref: Ref, 5533 comptime unused_fmt_string: []const u8, 5534 options: std.fmt.FormatOptions, 5535 writer: anytype, 5536 ) !void { 5537 _ = unused_fmt_string; 5538 _ = options; 5539 try writer.print("ref({},{})", .{ ref.index, ref.file }); 5540 } 5541 }; 5542 5543 pub const SymbolResolver = struct { 5544 keys: std.ArrayListUnmanaged(Key) = .empty, 5545 values: std.ArrayListUnmanaged(Ref) = .empty, 5546 table: std.AutoArrayHashMapUnmanaged(void, void) = .empty, 5547 5548 const Result = struct { 5549 found_existing: bool, 5550 index: Index, 5551 ref: *Ref, 5552 }; 5553 5554 pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void { 5555 resolver.keys.deinit(allocator); 5556 resolver.values.deinit(allocator); 5557 resolver.table.deinit(allocator); 5558 } 5559 5560 pub fn getOrPut( 5561 resolver: *SymbolResolver, 5562 allocator: Allocator, 5563 ref: Ref, 5564 elf_file: *Elf, 5565 ) !Result { 5566 const adapter = Adapter{ .keys = resolver.keys.items, .elf_file = elf_file }; 5567 const key = Key{ .index = ref.index, .file_index = ref.file }; 5568 const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter); 5569 if (!gop.found_existing) { 5570 try resolver.keys.append(allocator, key); 5571 _ = try resolver.values.addOne(allocator); 5572 } 5573 return .{ 5574 .found_existing = gop.found_existing, 5575 .index = @intCast(gop.index + 1), 5576 .ref = &resolver.values.items[gop.index], 5577 }; 5578 } 5579 5580 pub fn get(resolver: SymbolResolver, index: Index) ?Ref { 5581 if (index == 0) return null; 5582 return resolver.values.items[index - 1]; 5583 } 5584 5585 pub fn reset(resolver: *SymbolResolver) void { 5586 resolver.keys.clearRetainingCapacity(); 5587 resolver.values.clearRetainingCapacity(); 5588 resolver.table.clearRetainingCapacity(); 5589 } 5590 5591 const Key = struct { 5592 index: Symbol.Index, 5593 file_index: File.Index, 5594 5595 fn name(key: Key, elf_file: *Elf) [:0]const u8 { 5596 const ref = Ref{ .index = key.index, .file = key.file_index }; 5597 return elf_file.symbol(ref).?.name(elf_file); 5598 } 5599 5600 fn file(key: Key, elf_file: *Elf) ?File { 5601 return elf_file.file(key.file_index); 5602 } 5603 5604 fn eql(key: Key, other: Key, elf_file: *Elf) bool { 5605 const key_name = key.name(elf_file); 5606 const other_name = other.name(elf_file); 5607 return mem.eql(u8, key_name, other_name); 5608 } 5609 5610 fn hash(key: Key, elf_file: *Elf) u32 { 5611 return @truncate(Hash.hash(0, key.name(elf_file))); 5612 } 5613 }; 5614 5615 const Adapter = struct { 5616 keys: []const Key, 5617 elf_file: *Elf, 5618 5619 pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool { 5620 _ = b_void; 5621 const other = ctx.keys[b_map_index]; 5622 return key.eql(other, ctx.elf_file); 5623 } 5624 5625 pub fn hash(ctx: @This(), key: Key) u32 { 5626 return key.hash(ctx.elf_file); 5627 } 5628 }; 5629 5630 pub const Index = u32; 5631 }; 5632 5633 const Section = struct { 5634 /// Section header. 5635 shdr: elf.Elf64_Shdr, 5636 5637 /// Assigned program header index if any. 5638 phndx: ?u32 = null, 5639 5640 /// List of atoms contributing to this section. 5641 /// TODO currently this is only used for relocations tracking in relocatable mode 5642 /// but will be merged with atom_list_2. 5643 atom_list: std.ArrayListUnmanaged(Ref) = .empty, 5644 5645 /// List of atoms contributing to this section. 5646 /// This can be used by sections that require special handling such as init/fini array, etc. 5647 atom_list_2: AtomList = .{}, 5648 5649 /// Index of the last allocated atom in this section. 5650 last_atom: Ref = .{ .index = 0, .file = 0 }, 5651 5652 /// A list of atoms that have surplus capacity. This list can have false 5653 /// positives, as functions grow and shrink over time, only sometimes being added 5654 /// or removed from the freelist. 5655 /// 5656 /// An atom has surplus capacity when its overcapacity value is greater than 5657 /// padToIdeal(minimum_atom_size). That is, when it has so 5658 /// much extra capacity, that we could fit a small new symbol in it, itself with 5659 /// ideal_capacity or more. 5660 /// 5661 /// Ideal capacity is defined by size + (size / ideal_factor) 5662 /// 5663 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that 5664 /// overcapacity can be negative. A simple way to have negative overcapacity is to 5665 /// allocate a fresh text block, which will have ideal capacity, and then grow it 5666 /// by 1 byte. It will then have -1 overcapacity. 5667 free_list: std.ArrayListUnmanaged(Ref) = .empty, 5668 }; 5669 5670 pub fn sectionSize(self: *Elf, shndx: u32) u64 { 5671 const last_atom_ref = self.sections.items(.last_atom)[shndx]; 5672 const atom_ptr = self.atom(last_atom_ref) orelse return 0; 5673 return @as(u64, @intCast(atom_ptr.value)) + atom_ptr.size; 5674 } 5675 5676 fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 { 5677 return switch (cpu_arch) { 5678 .mips, .mipsel, .mips64, .mips64el => "__start", 5679 else => "_start", 5680 }; 5681 } 5682 5683 fn createThunks(elf_file: *Elf, atom_list: *AtomList) !void { 5684 const gpa = elf_file.base.comp.gpa; 5685 const cpu_arch = elf_file.getTarget().cpu.arch; 5686 5687 // A branch will need an extender if its target is larger than 5688 // `2^(jump_bits - 1) - margin` where margin is some arbitrary number. 5689 const max_distance = switch (cpu_arch) { 5690 .aarch64 => 0x500_000, 5691 .x86_64, .riscv64 => unreachable, 5692 else => @panic("unhandled arch"), 5693 }; 5694 5695 const advance = struct { 5696 fn advance(list: *AtomList, size: u64, alignment: Atom.Alignment) !i64 { 5697 const offset = alignment.forward(list.size); 5698 const padding = offset - list.size; 5699 list.size += padding + size; 5700 list.alignment = list.alignment.max(alignment); 5701 return @intCast(offset); 5702 } 5703 }.advance; 5704 5705 for (atom_list.atoms.keys()) |ref| { 5706 elf_file.atom(ref).?.value = -1; 5707 } 5708 5709 var i: usize = 0; 5710 while (i < atom_list.atoms.keys().len) { 5711 const start = i; 5712 const start_atom = elf_file.atom(atom_list.atoms.keys()[start]).?; 5713 assert(start_atom.alive); 5714 start_atom.value = try advance(atom_list, start_atom.size, start_atom.alignment); 5715 i += 1; 5716 5717 while (i < atom_list.atoms.keys().len) : (i += 1) { 5718 const atom_ptr = elf_file.atom(atom_list.atoms.keys()[i]).?; 5719 assert(atom_ptr.alive); 5720 if (@as(i64, @intCast(atom_ptr.alignment.forward(atom_list.size))) - start_atom.value >= max_distance) 5721 break; 5722 atom_ptr.value = try advance(atom_list, atom_ptr.size, atom_ptr.alignment); 5723 } 5724 5725 // Insert a thunk at the group end 5726 const thunk_index = try elf_file.addThunk(); 5727 const thunk_ptr = elf_file.thunk(thunk_index); 5728 thunk_ptr.output_section_index = atom_list.output_section_index; 5729 5730 // Scan relocs in the group and create trampolines for any unreachable callsite 5731 for (atom_list.atoms.keys()[start..i]) |ref| { 5732 const atom_ptr = elf_file.atom(ref).?; 5733 const file_ptr = atom_ptr.file(elf_file).?; 5734 log.debug("atom({}) {s}", .{ ref, atom_ptr.name(elf_file) }); 5735 for (atom_ptr.relocs(elf_file)) |rel| { 5736 const is_reachable = switch (cpu_arch) { 5737 .aarch64 => r: { 5738 const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); 5739 if (r_type != .CALL26 and r_type != .JUMP26) break :r true; 5740 const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file); 5741 const target = elf_file.symbol(target_ref).?; 5742 if (target.flags.has_plt) break :r false; 5743 if (atom_ptr.output_section_index != target.output_section_index) break :r false; 5744 const target_atom = target.atom(elf_file).?; 5745 if (target_atom.value == -1) break :r false; 5746 const saddr = atom_ptr.address(elf_file) + @as(i64, @intCast(rel.r_offset)); 5747 const taddr = target.address(.{}, elf_file); 5748 _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse break :r false; 5749 break :r true; 5750 }, 5751 .x86_64, .riscv64 => unreachable, 5752 else => @panic("unsupported arch"), 5753 }; 5754 if (is_reachable) continue; 5755 const target = file_ptr.resolveSymbol(rel.r_sym(), elf_file); 5756 try thunk_ptr.symbols.put(gpa, target, {}); 5757 } 5758 atom_ptr.addExtra(.{ .thunk = thunk_index }, elf_file); 5759 } 5760 5761 thunk_ptr.value = try advance(atom_list, thunk_ptr.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2)); 5762 5763 log.debug("thunk({d}) : {}", .{ thunk_index, thunk_ptr.fmt(elf_file) }); 5764 } 5765 } 5766 5767 const std = @import("std"); 5768 const build_options = @import("build_options"); 5769 const builtin = @import("builtin"); 5770 const assert = std.debug.assert; 5771 const elf = std.elf; 5772 const fs = std.fs; 5773 const log = std.log.scoped(.link); 5774 const relocs_log = std.log.scoped(.link_relocs); 5775 const state_log = std.log.scoped(.link_state); 5776 const math = std.math; 5777 const mem = std.mem; 5778 5779 const codegen = @import("../codegen.zig"); 5780 const dev = @import("../dev.zig"); 5781 const eh_frame = @import("Elf/eh_frame.zig"); 5782 const gc = @import("Elf/gc.zig"); 5783 const glibc = @import("../glibc.zig"); 5784 const link = @import("../link.zig"); 5785 const merge_section = @import("Elf/merge_section.zig"); 5786 const musl = @import("../musl.zig"); 5787 const relocatable = @import("Elf/relocatable.zig"); 5788 const relocation = @import("Elf/relocation.zig"); 5789 const target_util = @import("../target.zig"); 5790 const trace = @import("../tracy.zig").trace; 5791 const synthetic_sections = @import("Elf/synthetic_sections.zig"); 5792 5793 const Air = @import("../Air.zig"); 5794 const Allocator = std.mem.Allocator; 5795 const Archive = @import("Elf/Archive.zig"); 5796 pub const Atom = @import("Elf/Atom.zig"); 5797 const AtomList = @import("Elf/AtomList.zig"); 5798 const Cache = std.Build.Cache; 5799 const Path = Cache.Path; 5800 const Compilation = @import("../Compilation.zig"); 5801 const ComdatGroupSection = synthetic_sections.ComdatGroupSection; 5802 const CopyRelSection = synthetic_sections.CopyRelSection; 5803 const DynamicSection = synthetic_sections.DynamicSection; 5804 const DynsymSection = synthetic_sections.DynsymSection; 5805 const Dwarf = @import("Dwarf.zig"); 5806 const Elf = @This(); 5807 const File = @import("Elf/file.zig").File; 5808 const GnuHashSection = synthetic_sections.GnuHashSection; 5809 const GotSection = synthetic_sections.GotSection; 5810 const GotPltSection = synthetic_sections.GotPltSection; 5811 const Hash = std.hash.Wyhash; 5812 const HashSection = synthetic_sections.HashSection; 5813 const InputMergeSection = merge_section.InputMergeSection; 5814 const LdScript = @import("Elf/LdScript.zig"); 5815 const LinkerDefined = @import("Elf/LinkerDefined.zig"); 5816 const Liveness = @import("../Liveness.zig"); 5817 const LlvmObject = @import("../codegen/llvm.zig").Object; 5818 const MergeSection = merge_section.MergeSection; 5819 const MergeSubsection = merge_section.MergeSubsection; 5820 const Zcu = @import("../Zcu.zig"); 5821 const Object = @import("Elf/Object.zig"); 5822 const InternPool = @import("../InternPool.zig"); 5823 const PltSection = synthetic_sections.PltSection; 5824 const PltGotSection = synthetic_sections.PltGotSection; 5825 const SharedObject = @import("Elf/SharedObject.zig"); 5826 const Symbol = @import("Elf/Symbol.zig"); 5827 const StringTable = @import("StringTable.zig"); 5828 const Thunk = @import("Elf/Thunk.zig"); 5829 const Value = @import("../Value.zig"); 5830 const VerneedSection = synthetic_sections.VerneedSection; 5831 const ZigObject = @import("Elf/ZigObject.zig"); 5832 const riscv = @import("riscv.zig");