blob fd615bc1 (148932B) - Raw
1 const Elf = @This(); 2 3 const std = @import("std"); 4 const builtin = @import("builtin"); 5 const mem = std.mem; 6 const assert = std.debug.assert; 7 const Allocator = std.mem.Allocator; 8 const fs = std.fs; 9 const elf = std.elf; 10 const log = std.log.scoped(.link); 11 const DW = std.dwarf; 12 const leb128 = std.leb; 13 14 const Module = @import("../Module.zig"); 15 const Compilation = @import("../Compilation.zig"); 16 const codegen = @import("../codegen.zig"); 17 const trace = @import("../tracy.zig").trace; 18 const Package = @import("../Package.zig"); 19 const Value = @import("../value.zig").Value; 20 const Type = @import("../type.zig").Type; 21 const link = @import("../link.zig"); 22 const File = link.File; 23 const build_options = @import("build_options"); 24 const target_util = @import("../target.zig"); 25 const glibc = @import("../glibc.zig"); 26 const musl = @import("../musl.zig"); 27 const Cache = @import("../Cache.zig"); 28 const Air = @import("../Air.zig"); 29 const Liveness = @import("../Liveness.zig"); 30 const LlvmObject = @import("../codegen/llvm.zig").Object; 31 32 const default_entry_addr = 0x8000000; 33 34 pub const base_tag: File.Tag = .elf; 35 36 base: File, 37 38 ptr_width: PtrWidth, 39 40 /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. 41 llvm_object: ?*LlvmObject = null, 42 43 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. 44 /// Same order as in the file. 45 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){}, 46 shdr_table_offset: ?u64 = null, 47 48 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. 49 /// Same order as in the file. 50 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){}, 51 phdr_table_offset: ?u64 = null, 52 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags 53 phdr_load_re_index: ?u16 = null, 54 /// The index into the program headers of the global offset table. 55 /// It needs PT_LOAD and Read flags. 56 phdr_got_index: ?u16 = null, 57 entry_addr: ?u64 = null, 58 59 debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, 60 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, 61 shstrtab_index: ?u16 = null, 62 63 text_section_index: ?u16 = null, 64 symtab_section_index: ?u16 = null, 65 got_section_index: ?u16 = null, 66 debug_info_section_index: ?u16 = null, 67 debug_abbrev_section_index: ?u16 = null, 68 debug_str_section_index: ?u16 = null, 69 debug_aranges_section_index: ?u16 = null, 70 debug_line_section_index: ?u16 = null, 71 72 debug_abbrev_table_offset: ?u64 = null, 73 74 /// The same order as in the file. ELF requires global symbols to all be after the 75 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and 76 /// write them at the end. These are only the local symbols. The length of this array 77 /// is the value used for sh_info in the .symtab section. 78 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, 79 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, 80 81 local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, 82 global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{}, 83 offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, 84 85 /// Same order as in the file. The value is the absolute vaddr value. 86 /// If the vaddr of the executable program header changes, the entire 87 /// offset table needs to be rewritten. 88 offset_table: std.ArrayListUnmanaged(u64) = .{}, 89 90 phdr_table_dirty: bool = false, 91 shdr_table_dirty: bool = false, 92 shstrtab_dirty: bool = false, 93 debug_strtab_dirty: bool = false, 94 offset_table_count_dirty: bool = false, 95 debug_abbrev_section_dirty: bool = false, 96 debug_aranges_section_dirty: bool = false, 97 98 debug_info_header_dirty: bool = false, 99 debug_line_header_dirty: bool = false, 100 101 error_flags: File.ErrorFlags = File.ErrorFlags{}, 102 103 /// A list of text blocks that have surplus capacity. This list can have false 104 /// positives, as functions grow and shrink over time, only sometimes being added 105 /// or removed from the freelist. 106 /// 107 /// A text block has surplus capacity when its overcapacity value is greater than 108 /// padToIdeal(minimum_text_block_size). That is, when it has so 109 /// much extra capacity, that we could fit a small new symbol in it, itself with 110 /// ideal_capacity or more. 111 /// 112 /// Ideal capacity is defined by size + (size / ideal_factor) 113 /// 114 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that 115 /// overcapacity can be negative. A simple way to have negative overcapacity is to 116 /// allocate a fresh text block, which will have ideal capacity, and then grow it 117 /// by 1 byte. It will then have -1 overcapacity. 118 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, 119 last_text_block: ?*TextBlock = null, 120 121 /// A list of `SrcFn` whose Line Number Programs have surplus capacity. 122 /// This is the same concept as `text_block_free_list`; see those doc comments. 123 dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, 124 dbg_line_fn_first: ?*SrcFn = null, 125 dbg_line_fn_last: ?*SrcFn = null, 126 127 /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity. 128 /// This is the same concept as `text_block_free_list`; see those doc comments. 129 dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{}, 130 dbg_info_decl_first: ?*TextBlock = null, 131 dbg_info_decl_last: ?*TextBlock = null, 132 133 /// When allocating, the ideal_capacity is calculated by 134 /// actual_capacity + (actual_capacity / ideal_factor) 135 const ideal_factor = 3; 136 137 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at 138 /// it as a possible place to put new symbols, it must have enough room for this many bytes 139 /// (plus extra for reserved capacity). 140 const minimum_text_block_size = 64; 141 const min_text_capacity = padToIdeal(minimum_text_block_size); 142 143 pub const PtrWidth = enum { p32, p64 }; 144 145 pub const TextBlock = struct { 146 /// Each decl always gets a local symbol with the fully qualified name. 147 /// The vaddr and size are found here directly. 148 /// The file offset is found by computing the vaddr offset from the section vaddr 149 /// the symbol references, and adding that to the file offset of the section. 150 /// If this field is 0, it means the codegen size = 0 and there is no symbol or 151 /// offset table entry. 152 local_sym_index: u32, 153 /// This field is undefined for symbols with size = 0. 154 offset_table_index: u32, 155 /// Points to the previous and next neighbors, based on the `text_offset`. 156 /// This can be used to find, for example, the capacity of this `TextBlock`. 157 prev: ?*TextBlock, 158 next: ?*TextBlock, 159 160 /// Previous/next linked list pointers. 161 /// This is the linked list node for this Decl's corresponding .debug_info tag. 162 dbg_info_prev: ?*TextBlock, 163 dbg_info_next: ?*TextBlock, 164 /// Offset into .debug_info pointing to the tag for this Decl. 165 dbg_info_off: u32, 166 /// Size of the .debug_info tag for this Decl, not including padding. 167 dbg_info_len: u32, 168 169 pub const empty = TextBlock{ 170 .local_sym_index = 0, 171 .offset_table_index = undefined, 172 .prev = null, 173 .next = null, 174 .dbg_info_prev = null, 175 .dbg_info_next = null, 176 .dbg_info_off = undefined, 177 .dbg_info_len = undefined, 178 }; 179 180 /// Returns how much room there is to grow in virtual address space. 181 /// File offset relocation happens transparently, so it is not included in 182 /// this calculation. 183 fn capacity(self: TextBlock, elf_file: Elf) u64 { 184 const self_sym = elf_file.local_symbols.items[self.local_sym_index]; 185 if (self.next) |next| { 186 const next_sym = elf_file.local_symbols.items[next.local_sym_index]; 187 return next_sym.st_value - self_sym.st_value; 188 } else { 189 // We are the last block. The capacity is limited only by virtual address space. 190 return std.math.maxInt(u32) - self_sym.st_value; 191 } 192 } 193 194 fn freeListEligible(self: TextBlock, elf_file: Elf) bool { 195 // No need to keep a free list node for the last block. 196 const next = self.next orelse return false; 197 const self_sym = elf_file.local_symbols.items[self.local_sym_index]; 198 const next_sym = elf_file.local_symbols.items[next.local_sym_index]; 199 const cap = next_sym.st_value - self_sym.st_value; 200 const ideal_cap = padToIdeal(self_sym.st_size); 201 if (cap <= ideal_cap) return false; 202 const surplus = cap - ideal_cap; 203 return surplus >= min_text_capacity; 204 } 205 }; 206 207 pub const Export = struct { 208 sym_index: ?u32 = null, 209 }; 210 211 pub const SrcFn = struct { 212 /// Offset from the beginning of the Debug Line Program header that contains this function. 213 off: u32, 214 /// Size of the line number program component belonging to this function, not 215 /// including padding. 216 len: u32, 217 218 /// Points to the previous and next neighbors, based on the offset from .debug_line. 219 /// This can be used to find, for example, the capacity of this `SrcFn`. 220 prev: ?*SrcFn, 221 next: ?*SrcFn, 222 223 pub const empty: SrcFn = .{ 224 .off = 0, 225 .len = 0, 226 .prev = null, 227 .next = null, 228 }; 229 }; 230 231 pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf { 232 assert(options.object_format == .elf); 233 234 if (build_options.have_llvm and options.use_llvm) { 235 const self = try createEmpty(allocator, options); 236 errdefer self.base.destroy(); 237 238 self.llvm_object = try LlvmObject.create(allocator, sub_path, options); 239 return self; 240 } 241 242 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ 243 .truncate = false, 244 .read = true, 245 .mode = link.determineMode(options), 246 }); 247 errdefer file.close(); 248 249 const self = try createEmpty(allocator, options); 250 errdefer self.base.destroy(); 251 252 self.base.file = file; 253 self.shdr_table_dirty = true; 254 255 // Index 0 is always a null symbol. 256 try self.local_symbols.append(allocator, .{ 257 .st_name = 0, 258 .st_info = 0, 259 .st_other = 0, 260 .st_shndx = 0, 261 .st_value = 0, 262 .st_size = 0, 263 }); 264 265 // There must always be a null section in index 0 266 try self.sections.append(allocator, .{ 267 .sh_name = 0, 268 .sh_type = elf.SHT_NULL, 269 .sh_flags = 0, 270 .sh_addr = 0, 271 .sh_offset = 0, 272 .sh_size = 0, 273 .sh_link = 0, 274 .sh_info = 0, 275 .sh_addralign = 0, 276 .sh_entsize = 0, 277 }); 278 279 try self.populateMissingMetadata(); 280 281 return self; 282 } 283 284 pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { 285 const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { 286 0...32 => .p32, 287 33...64 => .p64, 288 else => return error.UnsupportedELFArchitecture, 289 }; 290 const self = try gpa.create(Elf); 291 self.* = .{ 292 .base = .{ 293 .tag = .elf, 294 .options = options, 295 .allocator = gpa, 296 .file = null, 297 }, 298 .ptr_width = ptr_width, 299 }; 300 return self; 301 } 302 303 pub fn deinit(self: *Elf) void { 304 if (build_options.have_llvm) { 305 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); 306 } 307 308 self.sections.deinit(self.base.allocator); 309 self.program_headers.deinit(self.base.allocator); 310 self.shstrtab.deinit(self.base.allocator); 311 self.debug_strtab.deinit(self.base.allocator); 312 self.local_symbols.deinit(self.base.allocator); 313 self.global_symbols.deinit(self.base.allocator); 314 self.global_symbol_free_list.deinit(self.base.allocator); 315 self.local_symbol_free_list.deinit(self.base.allocator); 316 self.offset_table_free_list.deinit(self.base.allocator); 317 self.text_block_free_list.deinit(self.base.allocator); 318 self.dbg_line_fn_free_list.deinit(self.base.allocator); 319 self.dbg_info_decl_free_list.deinit(self.base.allocator); 320 self.offset_table.deinit(self.base.allocator); 321 } 322 323 pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 { 324 assert(self.llvm_object == null); 325 assert(decl.link.elf.local_sym_index != 0); 326 return self.local_symbols.items[decl.link.elf.local_sym_index].st_value; 327 } 328 329 fn getDebugLineProgramOff(self: Elf) u32 { 330 return self.dbg_line_fn_first.?.off; 331 } 332 333 fn getDebugLineProgramEnd(self: Elf) u32 { 334 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len; 335 } 336 337 /// Returns end pos of collision, if any. 338 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { 339 const small_ptr = self.ptr_width == .p32; 340 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); 341 if (start < ehdr_size) 342 return ehdr_size; 343 344 const end = start + padToIdeal(size); 345 346 if (self.shdr_table_offset) |off| { 347 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr); 348 const tight_size = self.sections.items.len * shdr_size; 349 const increased_size = padToIdeal(tight_size); 350 const test_end = off + increased_size; 351 if (end > off and start < test_end) { 352 return test_end; 353 } 354 } 355 356 if (self.phdr_table_offset) |off| { 357 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr); 358 const tight_size = self.sections.items.len * phdr_size; 359 const increased_size = padToIdeal(tight_size); 360 const test_end = off + increased_size; 361 if (end > off and start < test_end) { 362 return test_end; 363 } 364 } 365 366 for (self.sections.items) |section| { 367 const increased_size = padToIdeal(section.sh_size); 368 const test_end = section.sh_offset + increased_size; 369 if (end > section.sh_offset and start < test_end) { 370 return test_end; 371 } 372 } 373 for (self.program_headers.items) |program_header| { 374 const increased_size = padToIdeal(program_header.p_filesz); 375 const test_end = program_header.p_offset + increased_size; 376 if (end > program_header.p_offset and start < test_end) { 377 return test_end; 378 } 379 } 380 return null; 381 } 382 383 fn allocatedSize(self: *Elf, start: u64) u64 { 384 if (start == 0) 385 return 0; 386 var min_pos: u64 = std.math.maxInt(u64); 387 if (self.shdr_table_offset) |off| { 388 if (off > start and off < min_pos) min_pos = off; 389 } 390 if (self.phdr_table_offset) |off| { 391 if (off > start and off < min_pos) min_pos = off; 392 } 393 for (self.sections.items) |section| { 394 if (section.sh_offset <= start) continue; 395 if (section.sh_offset < min_pos) min_pos = section.sh_offset; 396 } 397 for (self.program_headers.items) |program_header| { 398 if (program_header.p_offset <= start) continue; 399 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset; 400 } 401 return min_pos - start; 402 } 403 404 fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { 405 var start: u64 = 0; 406 while (self.detectAllocCollision(start, object_size)) |item_end| { 407 start = mem.alignForwardGeneric(u64, item_end, min_alignment); 408 } 409 return start; 410 } 411 412 /// TODO Improve this to use a table. 413 fn makeString(self: *Elf, bytes: []const u8) !u32 { 414 try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); 415 const result = self.shstrtab.items.len; 416 self.shstrtab.appendSliceAssumeCapacity(bytes); 417 self.shstrtab.appendAssumeCapacity(0); 418 return @intCast(u32, result); 419 } 420 421 /// TODO Improve this to use a table. 422 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 { 423 try self.debug_strtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); 424 const result = self.debug_strtab.items.len; 425 self.debug_strtab.appendSliceAssumeCapacity(bytes); 426 self.debug_strtab.appendAssumeCapacity(0); 427 return @intCast(u32, result); 428 } 429 430 fn getString(self: *Elf, str_off: u32) []const u8 { 431 assert(str_off < self.shstrtab.items.len); 432 return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0); 433 } 434 435 fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 { 436 const existing_name = self.getString(old_str_off); 437 if (mem.eql(u8, existing_name, new_name)) { 438 return old_str_off; 439 } 440 return self.makeString(new_name); 441 } 442 443 pub fn populateMissingMetadata(self: *Elf) !void { 444 assert(self.llvm_object == null); 445 446 const small_ptr = switch (self.ptr_width) { 447 .p32 => true, 448 .p64 => false, 449 }; 450 const ptr_size: u8 = self.ptrWidthBytes(); 451 if (self.phdr_load_re_index == null) { 452 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); 453 const file_size = self.base.options.program_code_size_hint; 454 const p_align = 0x1000; 455 const off = self.findFreeSpace(file_size, p_align); 456 log.debug("found PT_LOAD free space 0x{x} to 0x{x}", .{ off, off + file_size }); 457 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; 458 try self.program_headers.append(self.base.allocator, .{ 459 .p_type = elf.PT_LOAD, 460 .p_offset = off, 461 .p_filesz = file_size, 462 .p_vaddr = entry_addr, 463 .p_paddr = entry_addr, 464 .p_memsz = file_size, 465 .p_align = p_align, 466 .p_flags = elf.PF_X | elf.PF_R, 467 }); 468 self.entry_addr = null; 469 self.phdr_table_dirty = true; 470 } 471 if (self.phdr_got_index == null) { 472 self.phdr_got_index = @intCast(u16, self.program_headers.items.len); 473 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint; 474 // We really only need ptr alignment but since we are using PROGBITS, linux requires 475 // page align. 476 const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size); 477 const off = self.findFreeSpace(file_size, p_align); 478 log.debug("found PT_LOAD free space 0x{x} to 0x{x}", .{ off, off + file_size }); 479 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. 480 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something 481 // else in virtual memory. 482 const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000; 483 try self.program_headers.append(self.base.allocator, .{ 484 .p_type = elf.PT_LOAD, 485 .p_offset = off, 486 .p_filesz = file_size, 487 .p_vaddr = got_addr, 488 .p_paddr = got_addr, 489 .p_memsz = file_size, 490 .p_align = p_align, 491 .p_flags = elf.PF_R, 492 }); 493 self.phdr_table_dirty = true; 494 } 495 if (self.shstrtab_index == null) { 496 self.shstrtab_index = @intCast(u16, self.sections.items.len); 497 assert(self.shstrtab.items.len == 0); 498 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0 499 const off = self.findFreeSpace(self.shstrtab.items.len, 1); 500 log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len }); 501 try self.sections.append(self.base.allocator, .{ 502 .sh_name = try self.makeString(".shstrtab"), 503 .sh_type = elf.SHT_STRTAB, 504 .sh_flags = 0, 505 .sh_addr = 0, 506 .sh_offset = off, 507 .sh_size = self.shstrtab.items.len, 508 .sh_link = 0, 509 .sh_info = 0, 510 .sh_addralign = 1, 511 .sh_entsize = 0, 512 }); 513 self.shstrtab_dirty = true; 514 self.shdr_table_dirty = true; 515 } 516 if (self.text_section_index == null) { 517 self.text_section_index = @intCast(u16, self.sections.items.len); 518 const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; 519 520 try self.sections.append(self.base.allocator, .{ 521 .sh_name = try self.makeString(".text"), 522 .sh_type = elf.SHT_PROGBITS, 523 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, 524 .sh_addr = phdr.p_vaddr, 525 .sh_offset = phdr.p_offset, 526 .sh_size = phdr.p_filesz, 527 .sh_link = 0, 528 .sh_info = 0, 529 .sh_addralign = phdr.p_align, 530 .sh_entsize = 0, 531 }); 532 self.shdr_table_dirty = true; 533 } 534 if (self.got_section_index == null) { 535 self.got_section_index = @intCast(u16, self.sections.items.len); 536 const phdr = &self.program_headers.items[self.phdr_got_index.?]; 537 538 try self.sections.append(self.base.allocator, .{ 539 .sh_name = try self.makeString(".got"), 540 .sh_type = elf.SHT_PROGBITS, 541 .sh_flags = elf.SHF_ALLOC, 542 .sh_addr = phdr.p_vaddr, 543 .sh_offset = phdr.p_offset, 544 .sh_size = phdr.p_filesz, 545 .sh_link = 0, 546 .sh_info = 0, 547 .sh_addralign = phdr.p_align, 548 .sh_entsize = 0, 549 }); 550 self.shdr_table_dirty = true; 551 } 552 if (self.symtab_section_index == null) { 553 self.symtab_section_index = @intCast(u16, self.sections.items.len); 554 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); 555 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); 556 const file_size = self.base.options.symbol_count_hint * each_size; 557 const off = self.findFreeSpace(file_size, min_align); 558 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size }); 559 560 try self.sections.append(self.base.allocator, .{ 561 .sh_name = try self.makeString(".symtab"), 562 .sh_type = elf.SHT_SYMTAB, 563 .sh_flags = 0, 564 .sh_addr = 0, 565 .sh_offset = off, 566 .sh_size = file_size, 567 // The section header index of the associated string table. 568 .sh_link = self.shstrtab_index.?, 569 .sh_info = @intCast(u32, self.local_symbols.items.len), 570 .sh_addralign = min_align, 571 .sh_entsize = each_size, 572 }); 573 self.shdr_table_dirty = true; 574 try self.writeSymbol(0); 575 } 576 if (self.debug_str_section_index == null) { 577 self.debug_str_section_index = @intCast(u16, self.sections.items.len); 578 assert(self.debug_strtab.items.len == 0); 579 try self.sections.append(self.base.allocator, .{ 580 .sh_name = try self.makeString(".debug_str"), 581 .sh_type = elf.SHT_PROGBITS, 582 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, 583 .sh_addr = 0, 584 .sh_offset = 0, 585 .sh_size = self.debug_strtab.items.len, 586 .sh_link = 0, 587 .sh_info = 0, 588 .sh_addralign = 1, 589 .sh_entsize = 1, 590 }); 591 self.debug_strtab_dirty = true; 592 self.shdr_table_dirty = true; 593 } 594 if (self.debug_info_section_index == null) { 595 self.debug_info_section_index = @intCast(u16, self.sections.items.len); 596 597 const file_size_hint = 200; 598 const p_align = 1; 599 const off = self.findFreeSpace(file_size_hint, p_align); 600 log.debug("found .debug_info free space 0x{x} to 0x{x}", .{ 601 off, 602 off + file_size_hint, 603 }); 604 try self.sections.append(self.base.allocator, .{ 605 .sh_name = try self.makeString(".debug_info"), 606 .sh_type = elf.SHT_PROGBITS, 607 .sh_flags = 0, 608 .sh_addr = 0, 609 .sh_offset = off, 610 .sh_size = file_size_hint, 611 .sh_link = 0, 612 .sh_info = 0, 613 .sh_addralign = p_align, 614 .sh_entsize = 0, 615 }); 616 self.shdr_table_dirty = true; 617 self.debug_info_header_dirty = true; 618 } 619 if (self.debug_abbrev_section_index == null) { 620 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); 621 622 const file_size_hint = 128; 623 const p_align = 1; 624 const off = self.findFreeSpace(file_size_hint, p_align); 625 log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{ 626 off, 627 off + file_size_hint, 628 }); 629 try self.sections.append(self.base.allocator, .{ 630 .sh_name = try self.makeString(".debug_abbrev"), 631 .sh_type = elf.SHT_PROGBITS, 632 .sh_flags = 0, 633 .sh_addr = 0, 634 .sh_offset = off, 635 .sh_size = file_size_hint, 636 .sh_link = 0, 637 .sh_info = 0, 638 .sh_addralign = p_align, 639 .sh_entsize = 0, 640 }); 641 self.shdr_table_dirty = true; 642 self.debug_abbrev_section_dirty = true; 643 } 644 if (self.debug_aranges_section_index == null) { 645 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); 646 647 const file_size_hint = 160; 648 const p_align = 16; 649 const off = self.findFreeSpace(file_size_hint, p_align); 650 log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{ 651 off, 652 off + file_size_hint, 653 }); 654 try self.sections.append(self.base.allocator, .{ 655 .sh_name = try self.makeString(".debug_aranges"), 656 .sh_type = elf.SHT_PROGBITS, 657 .sh_flags = 0, 658 .sh_addr = 0, 659 .sh_offset = off, 660 .sh_size = file_size_hint, 661 .sh_link = 0, 662 .sh_info = 0, 663 .sh_addralign = p_align, 664 .sh_entsize = 0, 665 }); 666 self.shdr_table_dirty = true; 667 self.debug_aranges_section_dirty = true; 668 } 669 if (self.debug_line_section_index == null) { 670 self.debug_line_section_index = @intCast(u16, self.sections.items.len); 671 672 const file_size_hint = 250; 673 const p_align = 1; 674 const off = self.findFreeSpace(file_size_hint, p_align); 675 log.debug("found .debug_line free space 0x{x} to 0x{x}", .{ 676 off, 677 off + file_size_hint, 678 }); 679 try self.sections.append(self.base.allocator, .{ 680 .sh_name = try self.makeString(".debug_line"), 681 .sh_type = elf.SHT_PROGBITS, 682 .sh_flags = 0, 683 .sh_addr = 0, 684 .sh_offset = off, 685 .sh_size = file_size_hint, 686 .sh_link = 0, 687 .sh_info = 0, 688 .sh_addralign = p_align, 689 .sh_entsize = 0, 690 }); 691 self.shdr_table_dirty = true; 692 self.debug_line_header_dirty = true; 693 } 694 const shsize: u64 = switch (self.ptr_width) { 695 .p32 => @sizeOf(elf.Elf32_Shdr), 696 .p64 => @sizeOf(elf.Elf64_Shdr), 697 }; 698 const shalign: u16 = switch (self.ptr_width) { 699 .p32 => @alignOf(elf.Elf32_Shdr), 700 .p64 => @alignOf(elf.Elf64_Shdr), 701 }; 702 if (self.shdr_table_offset == null) { 703 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); 704 self.shdr_table_dirty = true; 705 } 706 const phsize: u64 = switch (self.ptr_width) { 707 .p32 => @sizeOf(elf.Elf32_Phdr), 708 .p64 => @sizeOf(elf.Elf64_Phdr), 709 }; 710 const phalign: u16 = switch (self.ptr_width) { 711 .p32 => @alignOf(elf.Elf32_Phdr), 712 .p64 => @alignOf(elf.Elf64_Phdr), 713 }; 714 if (self.phdr_table_offset == null) { 715 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign); 716 self.phdr_table_dirty = true; 717 } 718 { 719 // Iterate over symbols, populating free_list and last_text_block. 720 if (self.local_symbols.items.len != 1) { 721 @panic("TODO implement setting up free_list and last_text_block from existing ELF file"); 722 } 723 // We are starting with an empty file. The default values are correct, null and empty list. 724 } 725 } 726 727 pub const abbrev_compile_unit = 1; 728 pub const abbrev_subprogram = 2; 729 pub const abbrev_subprogram_retvoid = 3; 730 pub const abbrev_base_type = 4; 731 pub const abbrev_pad1 = 5; 732 pub const abbrev_parameter = 6; 733 734 pub fn flush(self: *Elf, comp: *Compilation) !void { 735 if (build_options.have_llvm and self.base.options.use_lld) { 736 return self.linkWithLLD(comp); 737 } else { 738 switch (self.base.options.effectiveOutputMode()) { 739 .Exe, .Obj => {}, 740 .Lib => return error.TODOImplementWritingLibFiles, 741 } 742 return self.flushModule(comp); 743 } 744 } 745 746 pub fn flushModule(self: *Elf, comp: *Compilation) !void { 747 const tracy = trace(@src()); 748 defer tracy.end(); 749 750 if (build_options.have_llvm) 751 if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp); 752 753 // TODO This linker code currently assumes there is only 1 compilation unit and it 754 // corresponds to the Zig source code. 755 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; 756 757 const target_endian = self.base.options.target.cpu.arch.endian(); 758 const foreign_endian = target_endian != builtin.cpu.arch.endian(); 759 const ptr_width_bytes: u8 = self.ptrWidthBytes(); 760 const init_len_size: usize = switch (self.ptr_width) { 761 .p32 => 4, 762 .p64 => 12, 763 }; 764 765 // Unfortunately these have to be buffered and done at the end because ELF does not allow 766 // mixing local and global symbols within a symbol table. 767 try self.writeAllGlobalSymbols(); 768 769 if (self.debug_abbrev_section_dirty) { 770 const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?]; 771 772 // These are LEB encoded but since the values are all less than 127 773 // we can simply append these bytes. 774 const abbrev_buf = [_]u8{ 775 abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header 776 DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, 777 DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, 778 DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, 779 DW.FORM.strp, DW.AT.producer, DW.FORM.strp, 780 DW.AT.language, DW.FORM.data2, 0, 781 0, // table sentinel 782 abbrev_subprogram, 783 DW.TAG.subprogram, 784 DW.CHILDREN.yes, // header 785 DW.AT.low_pc, 786 DW.FORM.addr, 787 DW.AT.high_pc, 788 DW.FORM.data4, 789 DW.AT.type, 790 DW.FORM.ref4, 791 DW.AT.name, 792 DW.FORM.string, 793 0, 0, // table sentinel 794 abbrev_subprogram_retvoid, 795 DW.TAG.subprogram, DW.CHILDREN.yes, // header 796 DW.AT.low_pc, DW.FORM.addr, 797 DW.AT.high_pc, DW.FORM.data4, 798 DW.AT.name, DW.FORM.string, 799 0, 800 0, // table sentinel 801 abbrev_base_type, 802 DW.TAG.base_type, 803 DW.CHILDREN.no, // header 804 DW.AT.encoding, 805 DW.FORM.data1, 806 DW.AT.byte_size, 807 DW.FORM.data1, 808 DW.AT.name, 809 DW.FORM.string, 0, 0, // table sentinel 810 abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header 811 0, 0, // table sentinel 812 abbrev_parameter, 813 DW.TAG.formal_parameter, DW.CHILDREN.no, // header 814 DW.AT.location, DW.FORM.exprloc, 815 DW.AT.type, DW.FORM.ref4, 816 DW.AT.name, DW.FORM.string, 817 0, 818 0, // table sentinel 819 0, 820 0, 821 0, // section sentinel 822 }; 823 824 const needed_size = abbrev_buf.len; 825 const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset); 826 if (needed_size > allocated_size) { 827 debug_abbrev_sect.sh_size = 0; // free the space 828 debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1); 829 } 830 debug_abbrev_sect.sh_size = needed_size; 831 log.debug(".debug_abbrev start=0x{x} end=0x{x}", .{ 832 debug_abbrev_sect.sh_offset, 833 debug_abbrev_sect.sh_offset + needed_size, 834 }); 835 836 const abbrev_offset = 0; 837 self.debug_abbrev_table_offset = abbrev_offset; 838 try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset); 839 if (!self.shdr_table_dirty) { 840 // Then it won't get written with the others and we need to do it. 841 try self.writeSectHeader(self.debug_abbrev_section_index.?); 842 } 843 844 self.debug_abbrev_section_dirty = false; 845 } 846 847 if (self.debug_info_header_dirty) debug_info: { 848 // If this value is null it means there is an error in the module; 849 // leave debug_info_header_dirty=true. 850 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info; 851 const last_dbg_info_decl = self.dbg_info_decl_last.?; 852 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; 853 854 // We have a function to compute the upper bound size, because it's needed 855 // for determining where to put the offset of the first `LinkBlock`. 856 const needed_bytes = self.dbgInfoNeededHeaderBytes(); 857 var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes); 858 defer di_buf.deinit(); 859 860 // initial length - length of the .debug_info contribution for this compilation unit, 861 // not including the initial length itself. 862 // We have to come back and write it later after we know the size. 863 const after_init_len = di_buf.items.len + init_len_size; 864 // +1 for the final 0 that ends the compilation unit children. 865 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1; 866 const init_len = dbg_info_end - after_init_len; 867 switch (self.ptr_width) { 868 .p32 => { 869 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); 870 }, 871 .p64 => { 872 di_buf.appendNTimesAssumeCapacity(0xff, 4); 873 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); 874 }, 875 } 876 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version 877 const abbrev_offset = self.debug_abbrev_table_offset.?; 878 switch (self.ptr_width) { 879 .p32 => { 880 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian); 881 di_buf.appendAssumeCapacity(4); // address size 882 }, 883 .p64 => { 884 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian); 885 di_buf.appendAssumeCapacity(8); // address size 886 }, 887 } 888 // Write the form for the compile unit, which must match the abbrev table above. 889 const name_strp = try self.makeDebugString(module.root_pkg.root_src_path); 890 const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse "."); 891 const producer_strp = try self.makeDebugString(link.producer_string); 892 // Currently only one compilation unit is supported, so the address range is simply 893 // identical to the main program header virtual address and memory size. 894 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; 895 const low_pc = text_phdr.p_vaddr; 896 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; 897 898 di_buf.appendAssumeCapacity(abbrev_compile_unit); 899 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset 900 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); 901 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); 902 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp); 903 self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp); 904 self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp); 905 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: 906 // http://dwarfstd.org/ShowIssue.php?issue=171115.1 907 // Until then we say it is C99. 908 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian); 909 910 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) { 911 // Move the first N decls to the end to make more padding for the header. 912 @panic("TODO: handle .debug_info header exceeding its padding"); 913 } 914 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len; 915 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset); 916 self.debug_info_header_dirty = false; 917 } 918 919 if (self.debug_aranges_section_dirty) { 920 const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?]; 921 922 // Enough for all the data without resizing. When support for more compilation units 923 // is added, the size of this section will become more variable. 924 var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, 100); 925 defer di_buf.deinit(); 926 927 // initial length - length of the .debug_aranges contribution for this compilation unit, 928 // not including the initial length itself. 929 // We have to come back and write it later after we know the size. 930 const init_len_index = di_buf.items.len; 931 di_buf.items.len += init_len_size; 932 const after_init_len = di_buf.items.len; 933 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version 934 // When more than one compilation unit is supported, this will be the offset to it. 935 // For now it is always at offset 0 in .debug_info. 936 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset 937 di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size 938 di_buf.appendAssumeCapacity(0); // segment_selector_size 939 940 const end_header_offset = di_buf.items.len; 941 const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2); 942 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset); 943 944 // Currently only one compilation unit is supported, so the address range is simply 945 // identical to the main program header virtual address and memory size. 946 const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; 947 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr); 948 self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz); 949 950 // Sentinel. 951 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); 952 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); 953 954 // Go back and populate the initial length. 955 const init_len = di_buf.items.len - after_init_len; 956 switch (self.ptr_width) { 957 .p32 => { 958 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian); 959 }, 960 .p64 => { 961 // initial length - length of the .debug_aranges contribution for this compilation unit, 962 // not including the initial length itself. 963 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff }; 964 mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian); 965 }, 966 } 967 968 const needed_size = di_buf.items.len; 969 const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset); 970 if (needed_size > allocated_size) { 971 debug_aranges_sect.sh_size = 0; // free the space 972 debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16); 973 } 974 debug_aranges_sect.sh_size = needed_size; 975 log.debug(".debug_aranges start=0x{x} end=0x{x}", .{ 976 debug_aranges_sect.sh_offset, 977 debug_aranges_sect.sh_offset + needed_size, 978 }); 979 980 try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset); 981 if (!self.shdr_table_dirty) { 982 // Then it won't get written with the others and we need to do it. 983 try self.writeSectHeader(self.debug_aranges_section_index.?); 984 } 985 986 self.debug_aranges_section_dirty = false; 987 } 988 if (self.debug_line_header_dirty) debug_line: { 989 if (self.dbg_line_fn_first == null) { 990 break :debug_line; // Error in module; leave debug_line_header_dirty=true. 991 } 992 const dbg_line_prg_off = self.getDebugLineProgramOff(); 993 const dbg_line_prg_end = self.getDebugLineProgramEnd(); 994 assert(dbg_line_prg_end != 0); 995 996 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; 997 998 // The size of this header is variable, depending on the number of directories, 999 // files, and padding. We have a function to compute the upper bound size, however, 1000 // because it's needed for determining where to put the offset of the first `SrcFn`. 1001 const needed_bytes = self.dbgLineNeededHeaderBytes(); 1002 var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes); 1003 defer di_buf.deinit(); 1004 1005 // initial length - length of the .debug_line contribution for this compilation unit, 1006 // not including the initial length itself. 1007 const after_init_len = di_buf.items.len + init_len_size; 1008 const init_len = dbg_line_prg_end - after_init_len; 1009 switch (self.ptr_width) { 1010 .p32 => { 1011 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian); 1012 }, 1013 .p64 => { 1014 di_buf.appendNTimesAssumeCapacity(0xff, 4); 1015 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian); 1016 }, 1017 } 1018 1019 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version 1020 1021 // Empirically, debug info consumers do not respect this field, or otherwise 1022 // consider it to be an error when it does not point exactly to the end of the header. 1023 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for 1024 // padding rather than this field. 1025 const before_header_len = di_buf.items.len; 1026 di_buf.items.len += ptr_width_bytes; // We will come back and write this. 1027 const after_header_len = di_buf.items.len; 1028 1029 const opcode_base = DW.LNS.set_isa + 1; 1030 di_buf.appendSliceAssumeCapacity(&[_]u8{ 1031 1, // minimum_instruction_length 1032 1, // maximum_operations_per_instruction 1033 1, // default_is_stmt 1034 1, // line_base (signed) 1035 1, // line_range 1036 opcode_base, 1037 1038 // Standard opcode lengths. The number of items here is based on `opcode_base`. 1039 // The value is the number of LEB128 operands the instruction takes. 1040 0, // `DW.LNS.copy` 1041 1, // `DW.LNS.advance_pc` 1042 1, // `DW.LNS.advance_line` 1043 1, // `DW.LNS.set_file` 1044 1, // `DW.LNS.set_column` 1045 0, // `DW.LNS.negate_stmt` 1046 0, // `DW.LNS.set_basic_block` 1047 0, // `DW.LNS.const_add_pc` 1048 1, // `DW.LNS.fixed_advance_pc` 1049 0, // `DW.LNS.set_prologue_end` 1050 0, // `DW.LNS.set_epilogue_begin` 1051 1, // `DW.LNS.set_isa` 1052 0, // include_directories (none except the compilation unit cwd) 1053 }); 1054 // file_names[0] 1055 di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name 1056 di_buf.appendSliceAssumeCapacity(&[_]u8{ 1057 0, // null byte for the relative path name 1058 0, // directory_index 1059 0, // mtime (TODO supply this) 1060 0, // file size bytes (TODO supply this) 1061 0, // file_names sentinel 1062 }); 1063 1064 const header_len = di_buf.items.len - after_header_len; 1065 switch (self.ptr_width) { 1066 .p32 => { 1067 mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian); 1068 }, 1069 .p64 => { 1070 mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian); 1071 }, 1072 } 1073 1074 // We use NOPs because consumers empirically do not respect the header length field. 1075 if (di_buf.items.len > dbg_line_prg_off) { 1076 // Move the first N files to the end to make more padding for the header. 1077 @panic("TODO: handle .debug_line header exceeding its padding"); 1078 } 1079 const jmp_amt = dbg_line_prg_off - di_buf.items.len; 1080 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset); 1081 self.debug_line_header_dirty = false; 1082 } 1083 1084 if (self.phdr_table_dirty) { 1085 const phsize: u64 = switch (self.ptr_width) { 1086 .p32 => @sizeOf(elf.Elf32_Phdr), 1087 .p64 => @sizeOf(elf.Elf64_Phdr), 1088 }; 1089 const phalign: u16 = switch (self.ptr_width) { 1090 .p32 => @alignOf(elf.Elf32_Phdr), 1091 .p64 => @alignOf(elf.Elf64_Phdr), 1092 }; 1093 const allocated_size = self.allocatedSize(self.phdr_table_offset.?); 1094 const needed_size = self.program_headers.items.len * phsize; 1095 1096 if (needed_size > allocated_size) { 1097 self.phdr_table_offset = null; // free the space 1098 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign); 1099 } 1100 1101 switch (self.ptr_width) { 1102 .p32 => { 1103 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len); 1104 defer self.base.allocator.free(buf); 1105 1106 for (buf) |*phdr, i| { 1107 phdr.* = progHeaderTo32(self.program_headers.items[i]); 1108 if (foreign_endian) { 1109 mem.bswapAllFields(elf.Elf32_Phdr, phdr); 1110 } 1111 } 1112 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); 1113 }, 1114 .p64 => { 1115 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len); 1116 defer self.base.allocator.free(buf); 1117 1118 for (buf) |*phdr, i| { 1119 phdr.* = self.program_headers.items[i]; 1120 if (foreign_endian) { 1121 mem.bswapAllFields(elf.Elf64_Phdr, phdr); 1122 } 1123 } 1124 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); 1125 }, 1126 } 1127 self.phdr_table_dirty = false; 1128 } 1129 1130 { 1131 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?]; 1132 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) { 1133 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset); 1134 const needed_size = self.shstrtab.items.len; 1135 1136 if (needed_size > allocated_size) { 1137 shstrtab_sect.sh_size = 0; // free the space 1138 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1); 1139 } 1140 shstrtab_sect.sh_size = needed_size; 1141 log.debug("writing shstrtab start=0x{x} end=0x{x}", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size }); 1142 1143 try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); 1144 if (!self.shdr_table_dirty) { 1145 // Then it won't get written with the others and we need to do it. 1146 try self.writeSectHeader(self.shstrtab_index.?); 1147 } 1148 self.shstrtab_dirty = false; 1149 } 1150 } 1151 { 1152 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?]; 1153 if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) { 1154 const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset); 1155 const needed_size = self.debug_strtab.items.len; 1156 1157 if (needed_size > allocated_size) { 1158 debug_strtab_sect.sh_size = 0; // free the space 1159 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1); 1160 } 1161 debug_strtab_sect.sh_size = needed_size; 1162 log.debug("debug_strtab start=0x{x} end=0x{x}", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size }); 1163 1164 try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset); 1165 if (!self.shdr_table_dirty) { 1166 // Then it won't get written with the others and we need to do it. 1167 try self.writeSectHeader(self.debug_str_section_index.?); 1168 } 1169 self.debug_strtab_dirty = false; 1170 } 1171 } 1172 if (self.shdr_table_dirty) { 1173 const shsize: u64 = switch (self.ptr_width) { 1174 .p32 => @sizeOf(elf.Elf32_Shdr), 1175 .p64 => @sizeOf(elf.Elf64_Shdr), 1176 }; 1177 const shalign: u16 = switch (self.ptr_width) { 1178 .p32 => @alignOf(elf.Elf32_Shdr), 1179 .p64 => @alignOf(elf.Elf64_Shdr), 1180 }; 1181 const allocated_size = self.allocatedSize(self.shdr_table_offset.?); 1182 const needed_size = self.sections.items.len * shsize; 1183 1184 if (needed_size > allocated_size) { 1185 self.shdr_table_offset = null; // free the space 1186 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign); 1187 } 1188 1189 switch (self.ptr_width) { 1190 .p32 => { 1191 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); 1192 defer self.base.allocator.free(buf); 1193 1194 for (buf) |*shdr, i| { 1195 shdr.* = sectHeaderTo32(self.sections.items[i]); 1196 log.debug("writing section {}", .{shdr.*}); 1197 if (foreign_endian) { 1198 mem.bswapAllFields(elf.Elf32_Shdr, shdr); 1199 } 1200 } 1201 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); 1202 }, 1203 .p64 => { 1204 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); 1205 defer self.base.allocator.free(buf); 1206 1207 for (buf) |*shdr, i| { 1208 shdr.* = self.sections.items[i]; 1209 log.debug("writing section {}", .{shdr.*}); 1210 if (foreign_endian) { 1211 mem.bswapAllFields(elf.Elf64_Shdr, shdr); 1212 } 1213 } 1214 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); 1215 }, 1216 } 1217 self.shdr_table_dirty = false; 1218 } 1219 if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { 1220 log.debug("flushing. no_entry_point_found = true", .{}); 1221 self.error_flags.no_entry_point_found = true; 1222 } else { 1223 log.debug("flushing. no_entry_point_found = false", .{}); 1224 self.error_flags.no_entry_point_found = false; 1225 try self.writeElfHeader(); 1226 } 1227 1228 // The point of flush() is to commit changes, so in theory, nothing should 1229 // be dirty after this. However, it is possible for some things to remain 1230 // dirty because they fail to be written in the event of compile errors, 1231 // such as debug_line_header_dirty and debug_info_header_dirty. 1232 assert(!self.debug_abbrev_section_dirty); 1233 assert(!self.debug_aranges_section_dirty); 1234 assert(!self.phdr_table_dirty); 1235 assert(!self.shdr_table_dirty); 1236 assert(!self.shstrtab_dirty); 1237 assert(!self.debug_strtab_dirty); 1238 } 1239 1240 fn linkWithLLD(self: *Elf, comp: *Compilation) !void { 1241 const tracy = trace(@src()); 1242 defer tracy.end(); 1243 1244 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); 1245 defer arena_allocator.deinit(); 1246 const arena = &arena_allocator.allocator; 1247 1248 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. 1249 1250 // If there is no Zig code to compile, then we should skip flushing the output file because it 1251 // will not be part of the linker line anyway. 1252 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { 1253 // stage1 puts the object file in the cache directory. 1254 if (self.base.options.use_stage1) { 1255 const obj_basename = try std.zig.binNameAlloc(arena, .{ 1256 .root_name = self.base.options.root_name, 1257 .target = self.base.options.target, 1258 .output_mode = .Obj, 1259 }); 1260 const o_directory = module.zig_cache_artifact_directory; 1261 const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); 1262 break :blk full_obj_path; 1263 } 1264 1265 try self.flushModule(comp); 1266 const obj_basename = self.base.intermediary_basename.?; 1267 const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); 1268 break :blk full_obj_path; 1269 } else null; 1270 1271 const is_obj = self.base.options.output_mode == .Obj; 1272 const is_lib = self.base.options.output_mode == .Lib; 1273 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; 1274 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; 1275 const have_dynamic_linker = self.base.options.link_libc and 1276 self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib; 1277 const target = self.base.options.target; 1278 const gc_sections = self.base.options.gc_sections orelse !is_obj; 1279 const stack_size = self.base.options.stack_size_override orelse 16777216; 1280 const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; 1281 const compiler_rt_path: ?[]const u8 = blk: { 1282 if (comp.compiler_rt_static_lib) |x| break :blk x.full_object_path; 1283 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; 1284 break :blk null; 1285 }; 1286 1287 // Here we want to determine whether we can save time by not invoking LLD when the 1288 // output is unchanged. None of the linker options or the object files that are being 1289 // linked are in the hash that namespaces the directory we are outputting to. Therefore, 1290 // we must hash those now, and the resulting digest will form the "id" of the linking 1291 // job we are about to perform. 1292 // After a successful link, we store the id in the metadata of a symlink named "id.txt" in 1293 // the artifact directory. So, now, we check if this symlink exists, and if it matches 1294 // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. 1295 const id_symlink_basename = "lld.id"; 1296 1297 var man: Cache.Manifest = undefined; 1298 defer if (!self.base.options.disable_lld_caching) man.deinit(); 1299 1300 var digest: [Cache.hex_digest_len]u8 = undefined; 1301 1302 if (!self.base.options.disable_lld_caching) { 1303 man = comp.cache_parent.obtain(); 1304 1305 // We are about to obtain this lock, so here we give other processes a chance first. 1306 self.base.releaseLock(); 1307 1308 try man.addOptionalFile(self.base.options.linker_script); 1309 try man.addOptionalFile(self.base.options.version_script); 1310 try man.addListOfFiles(self.base.options.objects); 1311 for (comp.c_object_table.keys()) |key| { 1312 _ = try man.addFile(key.status.success.object_path, null); 1313 } 1314 try man.addOptionalFile(module_obj_path); 1315 try man.addOptionalFile(compiler_rt_path); 1316 1317 // We can skip hashing libc and libc++ components that we are in charge of building from Zig 1318 // installation sources because they are always a product of the compiler version + target information. 1319 man.hash.add(stack_size); 1320 man.hash.addOptional(self.base.options.image_base_override); 1321 man.hash.add(gc_sections); 1322 man.hash.add(self.base.options.eh_frame_hdr); 1323 man.hash.add(self.base.options.emit_relocs); 1324 man.hash.add(self.base.options.rdynamic); 1325 man.hash.addListOfBytes(self.base.options.lib_dirs); 1326 man.hash.addListOfBytes(self.base.options.rpath_list); 1327 man.hash.add(self.base.options.each_lib_rpath); 1328 man.hash.add(self.base.options.skip_linker_dependencies); 1329 man.hash.add(self.base.options.z_nodelete); 1330 man.hash.add(self.base.options.z_notext); 1331 man.hash.add(self.base.options.z_defs); 1332 man.hash.add(self.base.options.z_origin); 1333 man.hash.add(self.base.options.z_noexecstack); 1334 man.hash.add(self.base.options.z_now); 1335 man.hash.add(self.base.options.z_relro); 1336 if (self.base.options.link_libc) { 1337 man.hash.add(self.base.options.libc_installation != null); 1338 if (self.base.options.libc_installation) |libc_installation| { 1339 man.hash.addBytes(libc_installation.crt_dir.?); 1340 } 1341 if (have_dynamic_linker) { 1342 man.hash.addOptionalBytes(self.base.options.dynamic_linker); 1343 } 1344 } 1345 man.hash.addOptionalBytes(self.base.options.soname); 1346 man.hash.addOptional(self.base.options.version); 1347 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); 1348 man.hash.add(allow_shlib_undefined); 1349 man.hash.add(self.base.options.bind_global_refs_locally); 1350 man.hash.add(self.base.options.tsan); 1351 man.hash.addOptionalBytes(self.base.options.sysroot); 1352 man.hash.add(self.base.options.linker_optimization); 1353 1354 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. 1355 _ = try man.hit(); 1356 digest = man.final(); 1357 1358 var prev_digest_buf: [digest.len]u8 = undefined; 1359 const prev_digest: []u8 = Cache.readSmallFile( 1360 directory.handle, 1361 id_symlink_basename, 1362 &prev_digest_buf, 1363 ) catch |err| blk: { 1364 log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); 1365 // Handle this as a cache miss. 1366 break :blk prev_digest_buf[0..0]; 1367 }; 1368 if (mem.eql(u8, prev_digest, &digest)) { 1369 log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); 1370 // Hot diggity dog! The output binary is already there. 1371 self.base.lock = man.toOwnedLock(); 1372 return; 1373 } 1374 log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); 1375 1376 // We are about to change the output file to be different, so we invalidate the build hash now. 1377 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { 1378 error.FileNotFound => {}, 1379 else => |e| return e, 1380 }; 1381 } 1382 1383 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); 1384 1385 // Due to a deficiency in LLD, we need to special-case BPF to a simple file copy when generating 1386 // relocatables. Normally, we would expect `lld -r` to work. However, because LLD wants to resolve 1387 // BPF relocations which it shouldn't, it fails before even generating the relocatable. 1388 if (self.base.options.output_mode == .Obj and (self.base.options.lto or target.isBpfFreestanding())) { 1389 // In this case we must do a simple file copy 1390 // here. TODO: think carefully about how we can avoid this redundant operation when doing 1391 // build-obj. See also the corresponding TODO in linkAsArchive. 1392 const the_object_path = blk: { 1393 if (self.base.options.objects.len != 0) 1394 break :blk self.base.options.objects[0]; 1395 1396 if (comp.c_object_table.count() != 0) 1397 break :blk comp.c_object_table.keys()[0].status.success.object_path; 1398 1399 if (module_obj_path) |p| 1400 break :blk p; 1401 1402 // TODO I think this is unreachable. Audit this situation when solving the above TODO 1403 // regarding eliding redundant object -> object transformations. 1404 return error.NoObjectsToLink; 1405 }; 1406 // This can happen when using --enable-cache and using the stage1 backend. In this case 1407 // we can skip the file copy. 1408 if (!mem.eql(u8, the_object_path, full_out_path)) { 1409 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); 1410 } 1411 } else { 1412 1413 // Create an LLD command line and invoke it. 1414 var argv = std.ArrayList([]const u8).init(self.base.allocator); 1415 defer argv.deinit(); 1416 // We will invoke ourselves as a child process to gain access to LLD. 1417 // This is necessary because LLD does not behave properly as a library - 1418 // it calls exit() and does not reset all global data between invocations. 1419 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" }); 1420 if (is_obj) { 1421 try argv.append("-r"); 1422 } 1423 1424 try argv.append("-error-limit=0"); 1425 1426 if (self.base.options.sysroot) |sysroot| { 1427 try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot})); 1428 } 1429 1430 if (self.base.options.lto) { 1431 switch (self.base.options.optimize_mode) { 1432 .Debug => {}, 1433 .ReleaseSmall => try argv.append("--lto-O2"), 1434 .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"), 1435 } 1436 } 1437 try argv.append(try std.fmt.allocPrint(arena, "-O{d}", .{ 1438 self.base.options.linker_optimization, 1439 })); 1440 1441 if (self.base.options.output_mode == .Exe) { 1442 try argv.append("-z"); 1443 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size})); 1444 } 1445 1446 if (self.base.options.image_base_override) |image_base| { 1447 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base})); 1448 } 1449 1450 if (self.base.options.linker_script) |linker_script| { 1451 try argv.append("-T"); 1452 try argv.append(linker_script); 1453 } 1454 1455 if (gc_sections) { 1456 try argv.append("--gc-sections"); 1457 } 1458 1459 if (self.base.options.eh_frame_hdr) { 1460 try argv.append("--eh-frame-hdr"); 1461 } 1462 1463 if (self.base.options.emit_relocs) { 1464 try argv.append("--emit-relocs"); 1465 } 1466 1467 if (self.base.options.rdynamic) { 1468 try argv.append("--export-dynamic"); 1469 } 1470 1471 if (self.base.options.z_nodelete) { 1472 try argv.append("-z"); 1473 try argv.append("nodelete"); 1474 } 1475 if (self.base.options.z_notext) { 1476 try argv.append("-z"); 1477 try argv.append("notext"); 1478 } 1479 if (self.base.options.z_defs) { 1480 try argv.append("-z"); 1481 try argv.append("defs"); 1482 } 1483 if (self.base.options.z_origin) { 1484 try argv.append("-z"); 1485 try argv.append("origin"); 1486 } 1487 if (self.base.options.z_noexecstack) { 1488 try argv.append("-z"); 1489 try argv.append("noexecstack"); 1490 } 1491 if (self.base.options.z_now) { 1492 try argv.append("-z"); 1493 try argv.append("now"); 1494 } 1495 if (self.base.options.z_relro) { 1496 try argv.append("-z"); 1497 try argv.append("relro"); 1498 } 1499 1500 if (getLDMOption(target)) |ldm| { 1501 // Any target ELF will use the freebsd osabi if suffixed with "_fbsd". 1502 const arg = if (target.os.tag == .freebsd) 1503 try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm}) 1504 else 1505 ldm; 1506 try argv.append("-m"); 1507 try argv.append(arg); 1508 } 1509 1510 if (self.base.options.link_mode == .Static) { 1511 if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) { 1512 try argv.append("-Bstatic"); 1513 } else { 1514 try argv.append("-static"); 1515 } 1516 } else if (is_dyn_lib) { 1517 try argv.append("-shared"); 1518 } 1519 1520 if (self.base.options.pie and self.base.options.output_mode == .Exe) { 1521 try argv.append("-pie"); 1522 } 1523 1524 if (self.base.options.link_mode == .Dynamic and target.os.tag == .netbsd) { 1525 // Add options to produce shared objects with only 2 PT_LOAD segments. 1526 // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise 1527 // ld.elf_so fails to load, emitting a general "not found" error. 1528 // See https://github.com/ziglang/zig/issues/9109 . 1529 try argv.append("--no-rosegment"); 1530 try argv.append("-znorelro"); 1531 } 1532 1533 try argv.append("-o"); 1534 try argv.append(full_out_path); 1535 1536 // csu prelude 1537 var csu = try CsuObjects.init(arena, self.base.options, comp); 1538 if (csu.crt0) |v| try argv.append(v); 1539 if (csu.crti) |v| try argv.append(v); 1540 if (csu.crtbegin) |v| try argv.append(v); 1541 1542 // rpaths 1543 var rpath_table = std.StringHashMap(void).init(self.base.allocator); 1544 defer rpath_table.deinit(); 1545 for (self.base.options.rpath_list) |rpath| { 1546 if ((try rpath_table.fetchPut(rpath, {})) == null) { 1547 try argv.append("-rpath"); 1548 try argv.append(rpath); 1549 } 1550 } 1551 if (self.base.options.each_lib_rpath) { 1552 var test_path = std.ArrayList(u8).init(self.base.allocator); 1553 defer test_path.deinit(); 1554 for (self.base.options.lib_dirs) |lib_dir_path| { 1555 for (self.base.options.system_libs.keys()) |link_lib| { 1556 test_path.clearRetainingCapacity(); 1557 const sep = fs.path.sep_str; 1558 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ 1559 lib_dir_path, link_lib, 1560 }); 1561 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { 1562 error.FileNotFound => continue, 1563 else => |e| return e, 1564 }; 1565 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) { 1566 try argv.append("-rpath"); 1567 try argv.append(lib_dir_path); 1568 } 1569 } 1570 } 1571 } 1572 1573 for (self.base.options.lib_dirs) |lib_dir| { 1574 try argv.append("-L"); 1575 try argv.append(lib_dir); 1576 } 1577 1578 if (self.base.options.link_libc) { 1579 if (self.base.options.libc_installation) |libc_installation| { 1580 try argv.append("-L"); 1581 try argv.append(libc_installation.crt_dir.?); 1582 } 1583 1584 if (have_dynamic_linker) { 1585 if (self.base.options.dynamic_linker) |dynamic_linker| { 1586 try argv.append("-dynamic-linker"); 1587 try argv.append(dynamic_linker); 1588 } 1589 } 1590 } 1591 1592 if (is_dyn_lib) { 1593 if (self.base.options.soname) |soname| { 1594 try argv.append("-soname"); 1595 try argv.append(soname); 1596 } 1597 if (self.base.options.version_script) |version_script| { 1598 try argv.append("-version-script"); 1599 try argv.append(version_script); 1600 } 1601 } 1602 1603 // Positional arguments to the linker such as object files. 1604 try argv.appendSlice(self.base.options.objects); 1605 1606 for (comp.c_object_table.keys()) |key| { 1607 try argv.append(key.status.success.object_path); 1608 } 1609 1610 if (module_obj_path) |p| { 1611 try argv.append(p); 1612 } 1613 1614 // TSAN 1615 if (self.base.options.tsan) { 1616 try argv.append(comp.tsan_static_lib.?.full_object_path); 1617 } 1618 1619 // libc 1620 if (is_exe_or_dyn_lib and 1621 !self.base.options.skip_linker_dependencies and 1622 !self.base.options.link_libc) 1623 { 1624 if (comp.libc_static_lib) |lib| { 1625 try argv.append(lib.full_object_path); 1626 } 1627 } 1628 1629 // compiler-rt 1630 if (compiler_rt_path) |p| { 1631 try argv.append(p); 1632 } 1633 1634 // Shared libraries. 1635 if (is_exe_or_dyn_lib) { 1636 const system_libs = self.base.options.system_libs.keys(); 1637 const system_libs_values = self.base.options.system_libs.values(); 1638 1639 // Worst-case, we need an --as-needed argument for every lib, as well 1640 // as one before and one after. 1641 try argv.ensureUnusedCapacity(system_libs.len * 2 + 2); 1642 argv.appendAssumeCapacity("--as-needed"); 1643 var as_needed = true; 1644 1645 for (system_libs) |link_lib, i| { 1646 const lib_as_needed = !system_libs_values[i].needed; 1647 switch ((@as(u2, @boolToInt(lib_as_needed)) << 1) | @boolToInt(as_needed)) { 1648 0b00, 0b11 => {}, 1649 0b01 => { 1650 argv.appendAssumeCapacity("--no-as-needed"); 1651 as_needed = false; 1652 }, 1653 0b10 => { 1654 argv.appendAssumeCapacity("--as-needed"); 1655 as_needed = true; 1656 }, 1657 } 1658 1659 // By this time, we depend on these libs being dynamically linked 1660 // libraries and not static libraries (the check for that needs to be earlier), 1661 // but they could be full paths to .so files, in which case we 1662 // want to avoid prepending "-l". 1663 const ext = Compilation.classifyFileExt(link_lib); 1664 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib}); 1665 argv.appendAssumeCapacity(arg); 1666 } 1667 1668 if (!as_needed) { 1669 argv.appendAssumeCapacity("--as-needed"); 1670 as_needed = true; 1671 } 1672 1673 // libc++ dep 1674 if (self.base.options.link_libcpp) { 1675 try argv.append(comp.libcxxabi_static_lib.?.full_object_path); 1676 try argv.append(comp.libcxx_static_lib.?.full_object_path); 1677 } 1678 1679 // libunwind dep 1680 if (self.base.options.link_libunwind) { 1681 try argv.append(comp.libunwind_static_lib.?.full_object_path); 1682 } 1683 1684 // libc dep 1685 if (self.base.options.link_libc) { 1686 if (self.base.options.libc_installation != null) { 1687 const needs_grouping = self.base.options.link_mode == .Static; 1688 if (needs_grouping) try argv.append("--start-group"); 1689 try argv.appendSlice(target_util.libcFullLinkFlags(target)); 1690 if (needs_grouping) try argv.append("--end-group"); 1691 } else if (target.isGnuLibC()) { 1692 for (glibc.libs) |lib| { 1693 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ 1694 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, 1695 }); 1696 try argv.append(lib_path); 1697 } 1698 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); 1699 } else if (target.isMusl()) { 1700 try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) { 1701 .Static => "libc.a", 1702 .Dynamic => "libc.so", 1703 })); 1704 } else { 1705 unreachable; // Compiler was supposed to emit an error for not being able to provide libc. 1706 } 1707 } 1708 } 1709 1710 // crt postlude 1711 if (csu.crtend) |v| try argv.append(v); 1712 if (csu.crtn) |v| try argv.append(v); 1713 1714 if (allow_shlib_undefined) { 1715 try argv.append("--allow-shlib-undefined"); 1716 } 1717 1718 if (self.base.options.bind_global_refs_locally) { 1719 try argv.append("-Bsymbolic"); 1720 } 1721 1722 if (self.base.options.verbose_link) { 1723 // Skip over our own name so that the LLD linker name is the first argv item. 1724 Compilation.dump_argv(argv.items[1..]); 1725 } 1726 1727 // Sadly, we must run LLD as a child process because it does not behave 1728 // properly as a library. 1729 const child = try std.ChildProcess.init(argv.items, arena); 1730 defer child.deinit(); 1731 1732 if (comp.clang_passthrough_mode) { 1733 child.stdin_behavior = .Inherit; 1734 child.stdout_behavior = .Inherit; 1735 child.stderr_behavior = .Inherit; 1736 1737 const term = child.spawnAndWait() catch |err| { 1738 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); 1739 return error.UnableToSpawnSelf; 1740 }; 1741 switch (term) { 1742 .Exited => |code| { 1743 if (code != 0) { 1744 // TODO https://github.com/ziglang/zig/issues/6342 1745 std.process.exit(1); 1746 } 1747 }, 1748 else => std.process.abort(), 1749 } 1750 } else { 1751 child.stdin_behavior = .Ignore; 1752 child.stdout_behavior = .Ignore; 1753 child.stderr_behavior = .Pipe; 1754 1755 try child.spawn(); 1756 1757 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); 1758 1759 const term = child.wait() catch |err| { 1760 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); 1761 return error.UnableToSpawnSelf; 1762 }; 1763 1764 switch (term) { 1765 .Exited => |code| { 1766 if (code != 0) { 1767 // TODO parse this output and surface with the Compilation API rather than 1768 // directly outputting to stderr here. 1769 std.debug.print("{s}", .{stderr}); 1770 return error.LLDReportedFailure; 1771 } 1772 }, 1773 else => { 1774 log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); 1775 return error.LLDCrashed; 1776 }, 1777 } 1778 1779 if (stderr.len != 0) { 1780 log.warn("unexpected LLD stderr:\n{s}", .{stderr}); 1781 } 1782 } 1783 } 1784 1785 if (!self.base.options.disable_lld_caching) { 1786 // Update the file with the digest. If it fails we can continue; it only 1787 // means that the next invocation will have an unnecessary cache miss. 1788 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { 1789 log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); 1790 }; 1791 // Again failure here only means an unnecessary cache miss. 1792 man.writeManifest() catch |err| { 1793 log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); 1794 }; 1795 // We hang on to this lock so that the output file path can be used without 1796 // other processes clobbering it. 1797 self.base.lock = man.toOwnedLock(); 1798 } 1799 } 1800 1801 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { 1802 const target_endian = self.base.options.target.cpu.arch.endian(); 1803 switch (self.ptr_width) { 1804 .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian), 1805 .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian), 1806 } 1807 } 1808 1809 fn writeElfHeader(self: *Elf) !void { 1810 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; 1811 1812 var index: usize = 0; 1813 hdr_buf[0..4].* = "\x7fELF".*; 1814 index += 4; 1815 1816 hdr_buf[index] = switch (self.ptr_width) { 1817 .p32 => elf.ELFCLASS32, 1818 .p64 => elf.ELFCLASS64, 1819 }; 1820 index += 1; 1821 1822 const endian = self.base.options.target.cpu.arch.endian(); 1823 hdr_buf[index] = switch (endian) { 1824 .Little => elf.ELFDATA2LSB, 1825 .Big => elf.ELFDATA2MSB, 1826 }; 1827 index += 1; 1828 1829 hdr_buf[index] = 1; // ELF version 1830 index += 1; 1831 1832 // OS ABI, often set to 0 regardless of target platform 1833 // ABI Version, possibly used by glibc but not by static executables 1834 // padding 1835 mem.set(u8, hdr_buf[index..][0..9], 0); 1836 index += 9; 1837 1838 assert(index == 16); 1839 1840 const elf_type = switch (self.base.options.effectiveOutputMode()) { 1841 .Exe => elf.ET.EXEC, 1842 .Obj => elf.ET.REL, 1843 .Lib => switch (self.base.options.link_mode) { 1844 .Static => elf.ET.REL, 1845 .Dynamic => elf.ET.DYN, 1846 }, 1847 }; 1848 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian); 1849 index += 2; 1850 1851 const machine = self.base.options.target.cpu.arch.toElfMachine(); 1852 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian); 1853 index += 2; 1854 1855 // ELF Version, again 1856 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); 1857 index += 4; 1858 1859 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; 1860 1861 switch (self.ptr_width) { 1862 .p32 => { 1863 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian); 1864 index += 4; 1865 1866 // e_phoff 1867 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian); 1868 index += 4; 1869 1870 // e_shoff 1871 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian); 1872 index += 4; 1873 }, 1874 .p64 => { 1875 // e_entry 1876 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian); 1877 index += 8; 1878 1879 // e_phoff 1880 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian); 1881 index += 8; 1882 1883 // e_shoff 1884 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian); 1885 index += 8; 1886 }, 1887 } 1888 1889 const e_flags = 0; 1890 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian); 1891 index += 4; 1892 1893 const e_ehsize: u16 = switch (self.ptr_width) { 1894 .p32 => @sizeOf(elf.Elf32_Ehdr), 1895 .p64 => @sizeOf(elf.Elf64_Ehdr), 1896 }; 1897 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian); 1898 index += 2; 1899 1900 const e_phentsize: u16 = switch (self.ptr_width) { 1901 .p32 => @sizeOf(elf.Elf32_Phdr), 1902 .p64 => @sizeOf(elf.Elf64_Phdr), 1903 }; 1904 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian); 1905 index += 2; 1906 1907 const e_phnum = @intCast(u16, self.program_headers.items.len); 1908 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian); 1909 index += 2; 1910 1911 const e_shentsize: u16 = switch (self.ptr_width) { 1912 .p32 => @sizeOf(elf.Elf32_Shdr), 1913 .p64 => @sizeOf(elf.Elf64_Shdr), 1914 }; 1915 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian); 1916 index += 2; 1917 1918 const e_shnum = @intCast(u16, self.sections.items.len); 1919 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); 1920 index += 2; 1921 1922 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian); 1923 index += 2; 1924 1925 assert(index == e_ehsize); 1926 1927 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); 1928 } 1929 1930 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void { 1931 var already_have_free_list_node = false; 1932 { 1933 var i: usize = 0; 1934 // TODO turn text_block_free_list into a hash map 1935 while (i < self.text_block_free_list.items.len) { 1936 if (self.text_block_free_list.items[i] == text_block) { 1937 _ = self.text_block_free_list.swapRemove(i); 1938 continue; 1939 } 1940 if (self.text_block_free_list.items[i] == text_block.prev) { 1941 already_have_free_list_node = true; 1942 } 1943 i += 1; 1944 } 1945 } 1946 // TODO process free list for dbg info just like we do above for vaddrs 1947 1948 if (self.last_text_block == text_block) { 1949 // TODO shrink the .text section size here 1950 self.last_text_block = text_block.prev; 1951 } 1952 if (self.dbg_info_decl_first == text_block) { 1953 self.dbg_info_decl_first = text_block.dbg_info_next; 1954 } 1955 if (self.dbg_info_decl_last == text_block) { 1956 // TODO shrink the .debug_info section size here 1957 self.dbg_info_decl_last = text_block.dbg_info_prev; 1958 } 1959 1960 if (text_block.prev) |prev| { 1961 prev.next = text_block.next; 1962 1963 if (!already_have_free_list_node and prev.freeListEligible(self.*)) { 1964 // The free list is heuristics, it doesn't have to be perfect, so we can 1965 // ignore the OOM here. 1966 self.text_block_free_list.append(self.base.allocator, prev) catch {}; 1967 } 1968 } else { 1969 text_block.prev = null; 1970 } 1971 1972 if (text_block.next) |next| { 1973 next.prev = text_block.prev; 1974 } else { 1975 text_block.next = null; 1976 } 1977 1978 if (text_block.dbg_info_prev) |prev| { 1979 prev.dbg_info_next = text_block.dbg_info_next; 1980 1981 // TODO the free list logic like we do for text blocks above 1982 } else { 1983 text_block.dbg_info_prev = null; 1984 } 1985 1986 if (text_block.dbg_info_next) |next| { 1987 next.dbg_info_prev = text_block.dbg_info_prev; 1988 } else { 1989 text_block.dbg_info_next = null; 1990 } 1991 } 1992 1993 fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void { 1994 _ = self; 1995 _ = text_block; 1996 _ = new_block_size; 1997 // TODO check the new capacity, and if it crosses the size threshold into a big enough 1998 // capacity, insert a free list node for it. 1999 } 2000 2001 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { 2002 const sym = self.local_symbols.items[text_block.local_sym_index]; 2003 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value; 2004 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); 2005 if (!need_realloc) return sym.st_value; 2006 return self.allocateTextBlock(text_block, new_block_size, alignment); 2007 } 2008 2009 fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { 2010 const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; 2011 const shdr = &self.sections.items[self.text_section_index.?]; 2012 const new_block_ideal_capacity = padToIdeal(new_block_size); 2013 2014 // We use these to indicate our intention to update metadata, placing the new block, 2015 // and possibly removing a free list node. 2016 // It would be simpler to do it inside the for loop below, but that would cause a 2017 // problem if an error was returned later in the function. So this action 2018 // is actually carried out at the end of the function, when errors are no longer possible. 2019 var block_placement: ?*TextBlock = null; 2020 var free_list_removal: ?usize = null; 2021 2022 // First we look for an appropriately sized free list node. 2023 // The list is unordered. We'll just take the first thing that works. 2024 const vaddr = blk: { 2025 var i: usize = 0; 2026 while (i < self.text_block_free_list.items.len) { 2027 const big_block = self.text_block_free_list.items[i]; 2028 // We now have a pointer to a live text block that has too much capacity. 2029 // Is it enough that we could fit this new text block? 2030 const sym = self.local_symbols.items[big_block.local_sym_index]; 2031 const capacity = big_block.capacity(self.*); 2032 const ideal_capacity = padToIdeal(capacity); 2033 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity; 2034 const capacity_end_vaddr = sym.st_value + capacity; 2035 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; 2036 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); 2037 if (new_start_vaddr < ideal_capacity_end_vaddr) { 2038 // Additional bookkeeping here to notice if this free list node 2039 // should be deleted because the block that it points to has grown to take up 2040 // more of the extra capacity. 2041 if (!big_block.freeListEligible(self.*)) { 2042 _ = self.text_block_free_list.swapRemove(i); 2043 } else { 2044 i += 1; 2045 } 2046 continue; 2047 } 2048 // At this point we know that we will place the new block here. But the 2049 // remaining question is whether there is still yet enough capacity left 2050 // over for there to still be a free list node. 2051 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; 2052 const keep_free_list_node = remaining_capacity >= min_text_capacity; 2053 2054 // Set up the metadata to be updated, after errors are no longer possible. 2055 block_placement = big_block; 2056 if (!keep_free_list_node) { 2057 free_list_removal = i; 2058 } 2059 break :blk new_start_vaddr; 2060 } else if (self.last_text_block) |last| { 2061 const sym = self.local_symbols.items[last.local_sym_index]; 2062 const ideal_capacity = padToIdeal(sym.st_size); 2063 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity; 2064 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); 2065 // Set up the metadata to be updated, after errors are no longer possible. 2066 block_placement = last; 2067 break :blk new_start_vaddr; 2068 } else { 2069 break :blk phdr.p_vaddr; 2070 } 2071 }; 2072 2073 const expand_text_section = block_placement == null or block_placement.?.next == null; 2074 if (expand_text_section) { 2075 const text_capacity = self.allocatedSize(shdr.sh_offset); 2076 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; 2077 if (needed_size > text_capacity) { 2078 // Must move the entire text section. 2079 const new_offset = self.findFreeSpace(needed_size, 0x1000); 2080 const text_size = if (self.last_text_block) |last| blk: { 2081 const sym = self.local_symbols.items[last.local_sym_index]; 2082 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; 2083 } else 0; 2084 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size); 2085 if (amt != text_size) return error.InputOutput; 2086 shdr.sh_offset = new_offset; 2087 phdr.p_offset = new_offset; 2088 } 2089 self.last_text_block = text_block; 2090 2091 shdr.sh_size = needed_size; 2092 phdr.p_memsz = needed_size; 2093 phdr.p_filesz = needed_size; 2094 2095 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address 2096 // range of the compilation unit. When we expand the text section, this range changes, 2097 // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. 2098 self.debug_info_header_dirty = true; 2099 // This becomes dirty for the same reason. We could potentially make this more 2100 // fine-grained with the addition of support for more compilation units. It is planned to 2101 // model each package as a different compilation unit. 2102 self.debug_aranges_section_dirty = true; 2103 2104 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty 2105 self.shdr_table_dirty = true; // TODO look into making only the one section dirty 2106 } 2107 2108 // This function can also reallocate a text block. 2109 // In this case we need to "unplug" it from its previous location before 2110 // plugging it in to its new location. 2111 if (text_block.prev) |prev| { 2112 prev.next = text_block.next; 2113 } 2114 if (text_block.next) |next| { 2115 next.prev = text_block.prev; 2116 } 2117 2118 if (block_placement) |big_block| { 2119 text_block.prev = big_block; 2120 text_block.next = big_block.next; 2121 big_block.next = text_block; 2122 } else { 2123 text_block.prev = null; 2124 text_block.next = null; 2125 } 2126 if (free_list_removal) |i| { 2127 _ = self.text_block_free_list.swapRemove(i); 2128 } 2129 return vaddr; 2130 } 2131 2132 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { 2133 if (self.llvm_object) |_| return; 2134 2135 if (decl.link.elf.local_sym_index != 0) return; 2136 2137 try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); 2138 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); 2139 2140 if (self.local_symbol_free_list.popOrNull()) |i| { 2141 log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); 2142 decl.link.elf.local_sym_index = i; 2143 } else { 2144 log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name }); 2145 decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len); 2146 _ = self.local_symbols.addOneAssumeCapacity(); 2147 } 2148 2149 if (self.offset_table_free_list.popOrNull()) |i| { 2150 decl.link.elf.offset_table_index = i; 2151 } else { 2152 decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len); 2153 _ = self.offset_table.addOneAssumeCapacity(); 2154 self.offset_table_count_dirty = true; 2155 } 2156 2157 const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; 2158 2159 self.local_symbols.items[decl.link.elf.local_sym_index] = .{ 2160 .st_name = 0, 2161 .st_info = 0, 2162 .st_other = 0, 2163 .st_shndx = 0, 2164 .st_value = phdr.p_vaddr, 2165 .st_size = 0, 2166 }; 2167 self.offset_table.items[decl.link.elf.offset_table_index] = 0; 2168 } 2169 2170 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { 2171 if (build_options.have_llvm) { 2172 if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); 2173 } 2174 2175 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. 2176 self.freeTextBlock(&decl.link.elf); 2177 if (decl.link.elf.local_sym_index != 0) { 2178 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {}; 2179 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; 2180 2181 self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0; 2182 2183 decl.link.elf.local_sym_index = 0; 2184 } 2185 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing 2186 // is desired for both. 2187 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf); 2188 if (decl.fn_link.elf.prev) |prev| { 2189 self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; 2190 prev.next = decl.fn_link.elf.next; 2191 if (decl.fn_link.elf.next) |next| { 2192 next.prev = prev; 2193 } else { 2194 self.dbg_line_fn_last = prev; 2195 } 2196 } else if (decl.fn_link.elf.next) |next| { 2197 self.dbg_line_fn_first = next; 2198 next.prev = null; 2199 } 2200 if (self.dbg_line_fn_first == &decl.fn_link.elf) { 2201 self.dbg_line_fn_first = decl.fn_link.elf.next; 2202 } 2203 if (self.dbg_line_fn_last == &decl.fn_link.elf) { 2204 self.dbg_line_fn_last = decl.fn_link.elf.prev; 2205 } 2206 } 2207 2208 fn deinitRelocs(gpa: *Allocator, table: *File.DbgInfoTypeRelocsTable) void { 2209 var it = table.valueIterator(); 2210 while (it.next()) |value| { 2211 value.relocs.deinit(gpa); 2212 } 2213 table.deinit(gpa); 2214 } 2215 2216 fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { 2217 const required_alignment = decl.ty.abiAlignment(self.base.options.target); 2218 2219 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes() 2220 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index]; 2221 if (local_sym.st_size != 0) { 2222 const capacity = decl.link.elf.capacity(self.*); 2223 const need_realloc = code.len > capacity or 2224 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); 2225 if (need_realloc) { 2226 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment); 2227 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, local_sym.st_value, vaddr }); 2228 if (vaddr != local_sym.st_value) { 2229 local_sym.st_value = vaddr; 2230 2231 log.debug(" (writing new offset table entry)", .{}); 2232 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr; 2233 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); 2234 } 2235 } else if (code.len < local_sym.st_size) { 2236 self.shrinkTextBlock(&decl.link.elf, code.len); 2237 } 2238 local_sym.st_size = code.len; 2239 local_sym.st_name = try self.updateString(local_sym.st_name, mem.sliceTo(decl.name, 0)); 2240 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; 2241 local_sym.st_other = 0; 2242 local_sym.st_shndx = self.text_section_index.?; 2243 // TODO this write could be avoided if no fields of the symbol were changed. 2244 try self.writeSymbol(decl.link.elf.local_sym_index); 2245 } else { 2246 const decl_name = mem.sliceTo(decl.name, 0); 2247 const name_str_index = try self.makeString(decl_name); 2248 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment); 2249 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); 2250 errdefer self.freeTextBlock(&decl.link.elf); 2251 2252 local_sym.* = .{ 2253 .st_name = name_str_index, 2254 .st_info = (elf.STB_LOCAL << 4) | stt_bits, 2255 .st_other = 0, 2256 .st_shndx = self.text_section_index.?, 2257 .st_value = vaddr, 2258 .st_size = code.len, 2259 }; 2260 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr; 2261 2262 try self.writeSymbol(decl.link.elf.local_sym_index); 2263 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); 2264 } 2265 2266 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr; 2267 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset; 2268 try self.base.file.?.pwriteAll(code, file_offset); 2269 2270 return local_sym; 2271 } 2272 2273 fn finishUpdateDecl( 2274 self: *Elf, 2275 module: *Module, 2276 decl: *Module.Decl, 2277 dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, 2278 dbg_info_buffer: *std.ArrayList(u8), 2279 ) !void { 2280 // Now we emit the .debug_info types of the Decl. These will count towards the size of 2281 // the buffer, so we have to do it before computing the offset, and we can't perform the actual 2282 // relocations yet. 2283 { 2284 var it = dbg_info_type_relocs.iterator(); 2285 while (it.next()) |entry| { 2286 entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); 2287 try self.addDbgInfoType(entry.key_ptr.*, dbg_info_buffer); 2288 } 2289 } 2290 2291 const text_block = &decl.link.elf; 2292 try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len)); 2293 2294 const target_endian = self.base.options.target.cpu.arch.endian(); 2295 2296 { 2297 // Now that we have the offset assigned we can finally perform type relocations. 2298 var it = dbg_info_type_relocs.valueIterator(); 2299 while (it.next()) |value| { 2300 for (value.relocs.items) |off| { 2301 mem.writeInt( 2302 u32, 2303 dbg_info_buffer.items[off..][0..4], 2304 text_block.dbg_info_off + value.off, 2305 target_endian, 2306 ); 2307 } 2308 } 2309 } 2310 2311 try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items); 2312 2313 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. 2314 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; 2315 return self.updateDeclExports(module, decl, decl_exports); 2316 } 2317 2318 pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { 2319 if (build_options.skip_non_native and builtin.object_format != .elf) { 2320 @panic("Attempted to compile for object format that was disabled by build configuration"); 2321 } 2322 if (build_options.have_llvm) { 2323 if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness); 2324 } 2325 2326 const tracy = trace(@src()); 2327 defer tracy.end(); 2328 2329 var code_buffer = std.ArrayList(u8).init(self.base.allocator); 2330 defer code_buffer.deinit(); 2331 2332 // For functions we need to add a prologue to the debug line program. 2333 var dbg_line_buffer = try std.ArrayList(u8).initCapacity(self.base.allocator, 26); 2334 defer dbg_line_buffer.deinit(); 2335 2336 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); 2337 defer dbg_info_buffer.deinit(); 2338 2339 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; 2340 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); 2341 2342 const decl = func.owner_decl; 2343 const line_off = @intCast(u28, decl.src_line + func.lbrace_line); 2344 2345 const ptr_width_bytes = self.ptrWidthBytes(); 2346 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ 2347 DW.LNS.extended_op, 2348 ptr_width_bytes + 1, 2349 DW.LNE.set_address, 2350 }); 2351 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. 2352 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); 2353 dbg_line_buffer.items.len += ptr_width_bytes; 2354 2355 dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); 2356 // This is the "relocatable" relative line offset from the previous function's end curly 2357 // to this function's begin curly. 2358 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); 2359 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. 2360 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off); 2361 2362 dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); 2363 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len); 2364 // Once we support more than one source file, this will have the ability to be more 2365 // than one possible value. 2366 const file_index = 1; 2367 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index); 2368 2369 // Emit a line for the begin curly with prologue_end=false. The codegen will 2370 // do the work of setting prologue_end=true and epilogue_begin=true. 2371 dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); 2372 2373 // .debug_info subprogram 2374 const decl_name_with_null = decl.name[0 .. mem.sliceTo(decl.name, 0).len + 1]; 2375 try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); 2376 2377 const fn_ret_type = decl.ty.fnReturnType(); 2378 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits(); 2379 if (fn_ret_has_bits) { 2380 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); 2381 } else { 2382 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); 2383 } 2384 // These get overwritten after generating the machine code. These values are 2385 // "relocations" and have to be in this fixed place so that functions can be 2386 // moved in virtual address space. 2387 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); 2388 dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr 2389 assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); 2390 dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 2391 if (fn_ret_has_bits) { 2392 const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type); 2393 if (!gop.found_existing) { 2394 gop.value_ptr.* = .{ 2395 .off = undefined, 2396 .relocs = .{}, 2397 }; 2398 } 2399 try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len)); 2400 dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 2401 } 2402 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string 2403 2404 const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ 2405 .dwarf = .{ 2406 .dbg_line = &dbg_line_buffer, 2407 .dbg_info = &dbg_info_buffer, 2408 .dbg_info_type_relocs = &dbg_info_type_relocs, 2409 }, 2410 }); 2411 const code = switch (res) { 2412 .appended => code_buffer.items, 2413 .fail => |em| { 2414 decl.analysis = .codegen_failure; 2415 try module.failed_decls.put(module.gpa, decl, em); 2416 return; 2417 }, 2418 }; 2419 2420 const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); 2421 2422 const target_endian = self.base.options.target.cpu.arch.endian(); 2423 2424 // Since the Decl is a function, we need to update the .debug_line program. 2425 // Perform the relocations based on vaddr. 2426 switch (self.ptr_width) { 2427 .p32 => { 2428 { 2429 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4]; 2430 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); 2431 } 2432 { 2433 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4]; 2434 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian); 2435 } 2436 }, 2437 .p64 => { 2438 { 2439 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8]; 2440 mem.writeInt(u64, ptr, local_sym.st_value, target_endian); 2441 } 2442 { 2443 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8]; 2444 mem.writeInt(u64, ptr, local_sym.st_value, target_endian); 2445 } 2446 }, 2447 } 2448 { 2449 const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4]; 2450 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian); 2451 } 2452 2453 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); 2454 2455 // Now we have the full contents and may allocate a region to store it. 2456 2457 // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for 2458 // `TextBlock` and the .debug_info. If you are editing this logic, you 2459 // probably need to edit that logic too. 2460 2461 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?]; 2462 const src_fn = &decl.fn_link.elf; 2463 src_fn.len = @intCast(u32, dbg_line_buffer.items.len); 2464 if (self.dbg_line_fn_last) |last| not_first: { 2465 if (src_fn.next) |next| { 2466 // Update existing function - non-last item. 2467 if (src_fn.off + src_fn.len + min_nop_size > next.off) { 2468 // It grew too big, so we move it to a new location. 2469 if (src_fn.prev) |prev| { 2470 self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; 2471 prev.next = src_fn.next; 2472 } 2473 assert(src_fn.prev != next); 2474 next.prev = src_fn.prev; 2475 src_fn.next = null; 2476 // Populate where it used to be with NOPs. 2477 const file_pos = debug_line_sect.sh_offset + src_fn.off; 2478 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos); 2479 // TODO Look at the free list before appending at the end. 2480 src_fn.prev = last; 2481 last.next = src_fn; 2482 self.dbg_line_fn_last = src_fn; 2483 2484 src_fn.off = last.off + padToIdeal(last.len); 2485 } 2486 } else if (src_fn.prev == null) { 2487 if (src_fn == last) { 2488 // Special case: there is only 1 function and it is being updated. 2489 // In this case there is nothing to do. The function's length has 2490 // already been updated, and the logic below takes care of 2491 // resizing the .debug_line section. 2492 break :not_first; 2493 } 2494 // Append new function. 2495 // TODO Look at the free list before appending at the end. 2496 src_fn.prev = last; 2497 last.next = src_fn; 2498 self.dbg_line_fn_last = src_fn; 2499 2500 src_fn.off = last.off + padToIdeal(last.len); 2501 } 2502 } else { 2503 // This is the first function of the Line Number Program. 2504 self.dbg_line_fn_first = src_fn; 2505 self.dbg_line_fn_last = src_fn; 2506 2507 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes()); 2508 } 2509 2510 const last_src_fn = self.dbg_line_fn_last.?; 2511 const needed_size = last_src_fn.off + last_src_fn.len; 2512 if (needed_size != debug_line_sect.sh_size) { 2513 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) { 2514 const new_offset = self.findFreeSpace(needed_size, 1); 2515 const existing_size = last_src_fn.off; 2516 log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{ 2517 existing_size, 2518 debug_line_sect.sh_offset, 2519 new_offset, 2520 }); 2521 const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size); 2522 if (amt != existing_size) return error.InputOutput; 2523 debug_line_sect.sh_offset = new_offset; 2524 } 2525 debug_line_sect.sh_size = needed_size; 2526 self.shdr_table_dirty = true; // TODO look into making only the one section dirty 2527 self.debug_line_header_dirty = true; 2528 } 2529 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0; 2530 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; 2531 2532 // We only have support for one compilation unit so far, so the offsets are directly 2533 // from the .debug_line section. 2534 const file_pos = debug_line_sect.sh_offset + src_fn.off; 2535 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); 2536 2537 // .debug_info - End the TAG.subprogram children. 2538 try dbg_info_buffer.append(0); 2539 2540 return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); 2541 } 2542 2543 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { 2544 if (build_options.skip_non_native and builtin.object_format != .elf) { 2545 @panic("Attempted to compile for object format that was disabled by build configuration"); 2546 } 2547 if (build_options.have_llvm) { 2548 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl); 2549 } 2550 2551 const tracy = trace(@src()); 2552 defer tracy.end(); 2553 2554 if (decl.val.tag() == .extern_fn) { 2555 return; // TODO Should we do more when front-end analyzed extern decl? 2556 } 2557 if (decl.val.castTag(.variable)) |payload| { 2558 const variable = payload.data; 2559 if (variable.is_extern) { 2560 return; // TODO Should we do more when front-end analyzed extern decl? 2561 } 2562 } 2563 2564 var code_buffer = std.ArrayList(u8).init(self.base.allocator); 2565 defer code_buffer.deinit(); 2566 2567 var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator); 2568 defer dbg_line_buffer.deinit(); 2569 2570 var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator); 2571 defer dbg_info_buffer.deinit(); 2572 2573 var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; 2574 defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); 2575 2576 // TODO implement .debug_info for global variables 2577 const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; 2578 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ 2579 .ty = decl.ty, 2580 .val = decl_val, 2581 }, &code_buffer, .{ 2582 .dwarf = .{ 2583 .dbg_line = &dbg_line_buffer, 2584 .dbg_info = &dbg_info_buffer, 2585 .dbg_info_type_relocs = &dbg_info_type_relocs, 2586 }, 2587 }); 2588 const code = switch (res) { 2589 .externally_managed => |x| x, 2590 .appended => code_buffer.items, 2591 .fail => |em| { 2592 decl.analysis = .codegen_failure; 2593 try module.failed_decls.put(module.gpa, decl, em); 2594 return; 2595 }, 2596 }; 2597 2598 _ = try self.updateDeclCode(decl, code, elf.STT_OBJECT); 2599 return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); 2600 } 2601 2602 /// Asserts the type has codegen bits. 2603 fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void { 2604 switch (ty.zigTypeTag()) { 2605 .Void => unreachable, 2606 .NoReturn => unreachable, 2607 .Bool => { 2608 try dbg_info_buffer.appendSlice(&[_]u8{ 2609 abbrev_base_type, 2610 DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 2611 1, // DW.AT.byte_size, DW.FORM.data1 2612 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string 2613 }); 2614 }, 2615 .Int => { 2616 const info = ty.intInfo(self.base.options.target); 2617 try dbg_info_buffer.ensureUnusedCapacity(12); 2618 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); 2619 // DW.AT.encoding, DW.FORM.data1 2620 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { 2621 .signed => DW.ATE.signed, 2622 .unsigned => DW.ATE.unsigned, 2623 }); 2624 // DW.AT.byte_size, DW.FORM.data1 2625 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target))); 2626 // DW.AT.name, DW.FORM.string 2627 try dbg_info_buffer.writer().print("{}\x00", .{ty}); 2628 }, 2629 .Optional => { 2630 if (ty.isPtrLikeOptional()) { 2631 try dbg_info_buffer.ensureUnusedCapacity(12); 2632 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); 2633 // DW.AT.encoding, DW.FORM.data1 2634 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); 2635 // DW.AT.byte_size, DW.FORM.data1 2636 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target))); 2637 // DW.AT.name, DW.FORM.string 2638 try dbg_info_buffer.writer().print("{}\x00", .{ty}); 2639 } else { 2640 log.debug("TODO implement .debug_info for type '{}'", .{ty}); 2641 try dbg_info_buffer.append(abbrev_pad1); 2642 } 2643 }, 2644 else => { 2645 log.debug("TODO implement .debug_info for type '{}'", .{ty}); 2646 try dbg_info_buffer.append(abbrev_pad1); 2647 }, 2648 } 2649 } 2650 2651 fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void { 2652 const tracy = trace(@src()); 2653 defer tracy.end(); 2654 2655 // This logic is nearly identical to the logic above in `updateDecl` for 2656 // `SrcFn` and the line number programs. If you are editing this logic, you 2657 // probably need to edit that logic too. 2658 2659 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; 2660 text_block.dbg_info_len = len; 2661 if (self.dbg_info_decl_last) |last| not_first: { 2662 if (text_block.dbg_info_next) |next| { 2663 // Update existing Decl - non-last item. 2664 if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) { 2665 // It grew too big, so we move it to a new location. 2666 if (text_block.dbg_info_prev) |prev| { 2667 self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {}; 2668 prev.dbg_info_next = text_block.dbg_info_next; 2669 } 2670 next.dbg_info_prev = text_block.dbg_info_prev; 2671 text_block.dbg_info_next = null; 2672 // Populate where it used to be with NOPs. 2673 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; 2674 try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos); 2675 // TODO Look at the free list before appending at the end. 2676 text_block.dbg_info_prev = last; 2677 last.dbg_info_next = text_block; 2678 self.dbg_info_decl_last = text_block; 2679 2680 text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); 2681 } 2682 } else if (text_block.dbg_info_prev == null) { 2683 if (text_block == last) { 2684 // Special case: there is only 1 .debug_info block and it is being updated. 2685 // In this case there is nothing to do. The block's length has 2686 // already been updated, and logic in writeDeclDebugInfo takes care of 2687 // resizing the .debug_info section. 2688 break :not_first; 2689 } 2690 // Append new Decl. 2691 // TODO Look at the free list before appending at the end. 2692 text_block.dbg_info_prev = last; 2693 last.dbg_info_next = text_block; 2694 self.dbg_info_decl_last = text_block; 2695 2696 text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len); 2697 } 2698 } else { 2699 // This is the first Decl of the .debug_info 2700 self.dbg_info_decl_first = text_block; 2701 self.dbg_info_decl_last = text_block; 2702 2703 text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes()); 2704 } 2705 } 2706 2707 fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void { 2708 const tracy = trace(@src()); 2709 defer tracy.end(); 2710 2711 // This logic is nearly identical to the logic above in `updateDecl` for 2712 // `SrcFn` and the line number programs. If you are editing this logic, you 2713 // probably need to edit that logic too. 2714 2715 const debug_info_sect = &self.sections.items[self.debug_info_section_index.?]; 2716 2717 const last_decl = self.dbg_info_decl_last.?; 2718 // +1 for a trailing zero to end the children of the decl tag. 2719 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1; 2720 if (needed_size != debug_info_sect.sh_size) { 2721 if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) { 2722 const new_offset = self.findFreeSpace(needed_size, 1); 2723 const existing_size = last_decl.dbg_info_off; 2724 log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}", .{ 2725 existing_size, 2726 debug_info_sect.sh_offset, 2727 new_offset, 2728 }); 2729 const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size); 2730 if (amt != existing_size) return error.InputOutput; 2731 debug_info_sect.sh_offset = new_offset; 2732 } 2733 debug_info_sect.sh_size = needed_size; 2734 self.shdr_table_dirty = true; // TODO look into making only the one section dirty 2735 self.debug_info_header_dirty = true; 2736 } 2737 const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev| 2738 text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len) 2739 else 2740 0; 2741 const next_padding_size: u32 = if (text_block.dbg_info_next) |next| 2742 next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len) 2743 else 2744 0; 2745 2746 // To end the children of the decl tag. 2747 const trailing_zero = text_block.dbg_info_next == null; 2748 2749 // We only have support for one compilation unit so far, so the offsets are directly 2750 // from the .debug_info section. 2751 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off; 2752 try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos); 2753 } 2754 2755 pub fn updateDeclExports( 2756 self: *Elf, 2757 module: *Module, 2758 decl: *Module.Decl, 2759 exports: []const *Module.Export, 2760 ) !void { 2761 if (build_options.skip_non_native and builtin.object_format != .elf) { 2762 @panic("Attempted to compile for object format that was disabled by build configuration"); 2763 } 2764 if (build_options.have_llvm) { 2765 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); 2766 } 2767 2768 const tracy = trace(@src()); 2769 defer tracy.end(); 2770 2771 try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len); 2772 if (decl.link.elf.local_sym_index == 0) return; 2773 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index]; 2774 2775 for (exports) |exp| { 2776 if (exp.options.section) |section_name| { 2777 if (!mem.eql(u8, section_name, ".text")) { 2778 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); 2779 module.failed_exports.putAssumeCapacityNoClobber( 2780 exp, 2781 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), 2782 ); 2783 continue; 2784 } 2785 } 2786 const stb_bits: u8 = switch (exp.options.linkage) { 2787 .Internal => elf.STB_LOCAL, 2788 .Strong => blk: { 2789 if (mem.eql(u8, exp.options.name, "_start")) { 2790 self.entry_addr = decl_sym.st_value; 2791 } 2792 break :blk elf.STB_GLOBAL; 2793 }, 2794 .Weak => elf.STB_WEAK, 2795 .LinkOnce => { 2796 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); 2797 module.failed_exports.putAssumeCapacityNoClobber( 2798 exp, 2799 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}), 2800 ); 2801 continue; 2802 }, 2803 }; 2804 const stt_bits: u8 = @truncate(u4, decl_sym.st_info); 2805 if (exp.link.elf.sym_index) |i| { 2806 const sym = &self.global_symbols.items[i]; 2807 sym.* = .{ 2808 .st_name = try self.updateString(sym.st_name, exp.options.name), 2809 .st_info = (stb_bits << 4) | stt_bits, 2810 .st_other = 0, 2811 .st_shndx = self.text_section_index.?, 2812 .st_value = decl_sym.st_value, 2813 .st_size = decl_sym.st_size, 2814 }; 2815 } else { 2816 const name = try self.makeString(exp.options.name); 2817 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { 2818 _ = self.global_symbols.addOneAssumeCapacity(); 2819 break :blk self.global_symbols.items.len - 1; 2820 }; 2821 self.global_symbols.items[i] = .{ 2822 .st_name = name, 2823 .st_info = (stb_bits << 4) | stt_bits, 2824 .st_other = 0, 2825 .st_shndx = self.text_section_index.?, 2826 .st_value = decl_sym.st_value, 2827 .st_size = decl_sym.st_size, 2828 }; 2829 2830 exp.link.elf.sym_index = @intCast(u32, i); 2831 } 2832 } 2833 } 2834 2835 /// Must be called only after a successful call to `updateDecl`. 2836 pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void { 2837 _ = module; 2838 const tracy = trace(@src()); 2839 defer tracy.end(); 2840 2841 if (self.llvm_object) |_| return; 2842 2843 const func = decl.val.castTag(.function).?.data; 2844 const casted_line_off = @intCast(u28, decl.src_line + func.lbrace_line); 2845 2846 const shdr = &self.sections.items[self.debug_line_section_index.?]; 2847 const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff(); 2848 var data: [4]u8 = undefined; 2849 leb128.writeUnsignedFixed(4, &data, casted_line_off); 2850 try self.base.file.?.pwriteAll(&data, file_pos); 2851 } 2852 2853 pub fn deleteExport(self: *Elf, exp: Export) void { 2854 if (self.llvm_object) |_| return; 2855 2856 const sym_index = exp.sym_index orelse return; 2857 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {}; 2858 self.global_symbols.items[sym_index].st_info = 0; 2859 } 2860 2861 fn writeProgHeader(self: *Elf, index: usize) !void { 2862 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); 2863 const offset = self.program_headers.items[index].p_offset; 2864 switch (self.ptr_width) { 2865 .p32 => { 2866 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; 2867 if (foreign_endian) { 2868 mem.bswapAllFields(elf.Elf32_Phdr, &phdr[0]); 2869 } 2870 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); 2871 }, 2872 .p64 => { 2873 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; 2874 if (foreign_endian) { 2875 mem.bswapAllFields(elf.Elf64_Phdr, &phdr[0]); 2876 } 2877 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); 2878 }, 2879 } 2880 } 2881 2882 fn writeSectHeader(self: *Elf, index: usize) !void { 2883 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); 2884 switch (self.ptr_width) { 2885 .p32 => { 2886 var shdr: [1]elf.Elf32_Shdr = undefined; 2887 shdr[0] = sectHeaderTo32(self.sections.items[index]); 2888 if (foreign_endian) { 2889 mem.bswapAllFields(elf.Elf32_Shdr, &shdr[0]); 2890 } 2891 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); 2892 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); 2893 }, 2894 .p64 => { 2895 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; 2896 if (foreign_endian) { 2897 mem.bswapAllFields(elf.Elf64_Shdr, &shdr[0]); 2898 } 2899 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); 2900 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); 2901 }, 2902 } 2903 } 2904 2905 fn writeOffsetTableEntry(self: *Elf, index: usize) !void { 2906 const shdr = &self.sections.items[self.got_section_index.?]; 2907 const phdr = &self.program_headers.items[self.phdr_got_index.?]; 2908 const entry_size: u16 = self.archPtrWidthBytes(); 2909 if (self.offset_table_count_dirty) { 2910 // TODO Also detect virtual address collisions. 2911 const allocated_size = self.allocatedSize(shdr.sh_offset); 2912 const needed_size = self.offset_table.items.len * entry_size; 2913 if (needed_size > allocated_size) { 2914 // Must move the entire got section. 2915 const new_offset = self.findFreeSpace(needed_size, entry_size); 2916 const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size); 2917 if (amt != shdr.sh_size) return error.InputOutput; 2918 shdr.sh_offset = new_offset; 2919 phdr.p_offset = new_offset; 2920 } 2921 shdr.sh_size = needed_size; 2922 phdr.p_memsz = needed_size; 2923 phdr.p_filesz = needed_size; 2924 2925 self.shdr_table_dirty = true; // TODO look into making only the one section dirty 2926 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty 2927 2928 self.offset_table_count_dirty = false; 2929 } 2930 const endian = self.base.options.target.cpu.arch.endian(); 2931 const off = shdr.sh_offset + @as(u64, entry_size) * index; 2932 switch (entry_size) { 2933 2 => { 2934 var buf: [2]u8 = undefined; 2935 mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian); 2936 try self.base.file.?.pwriteAll(&buf, off); 2937 }, 2938 4 => { 2939 var buf: [4]u8 = undefined; 2940 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); 2941 try self.base.file.?.pwriteAll(&buf, off); 2942 }, 2943 8 => { 2944 var buf: [8]u8 = undefined; 2945 mem.writeInt(u64, &buf, self.offset_table.items[index], endian); 2946 try self.base.file.?.pwriteAll(&buf, off); 2947 }, 2948 else => unreachable, 2949 } 2950 } 2951 2952 fn writeSymbol(self: *Elf, index: usize) !void { 2953 const tracy = trace(@src()); 2954 defer tracy.end(); 2955 2956 const syms_sect = &self.sections.items[self.symtab_section_index.?]; 2957 // Make sure we are not pointlessly writing symbol data that will have to get relocated 2958 // due to running out of space. 2959 if (self.local_symbols.items.len != syms_sect.sh_info) { 2960 const sym_size: u64 = switch (self.ptr_width) { 2961 .p32 => @sizeOf(elf.Elf32_Sym), 2962 .p64 => @sizeOf(elf.Elf64_Sym), 2963 }; 2964 const sym_align: u16 = switch (self.ptr_width) { 2965 .p32 => @alignOf(elf.Elf32_Sym), 2966 .p64 => @alignOf(elf.Elf64_Sym), 2967 }; 2968 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; 2969 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { 2970 // Move all the symbols to a new file location. 2971 const new_offset = self.findFreeSpace(needed_size, sym_align); 2972 const existing_size = @as(u64, syms_sect.sh_info) * sym_size; 2973 const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size); 2974 if (amt != existing_size) return error.InputOutput; 2975 syms_sect.sh_offset = new_offset; 2976 } 2977 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len); 2978 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later 2979 self.shdr_table_dirty = true; // TODO look into only writing one section 2980 } 2981 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); 2982 switch (self.ptr_width) { 2983 .p32 => { 2984 var sym = [1]elf.Elf32_Sym{ 2985 .{ 2986 .st_name = self.local_symbols.items[index].st_name, 2987 .st_value = @intCast(u32, self.local_symbols.items[index].st_value), 2988 .st_size = @intCast(u32, self.local_symbols.items[index].st_size), 2989 .st_info = self.local_symbols.items[index].st_info, 2990 .st_other = self.local_symbols.items[index].st_other, 2991 .st_shndx = self.local_symbols.items[index].st_shndx, 2992 }, 2993 }; 2994 if (foreign_endian) { 2995 mem.bswapAllFields(elf.Elf32_Sym, &sym[0]); 2996 } 2997 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index; 2998 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); 2999 }, 3000 .p64 => { 3001 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]}; 3002 if (foreign_endian) { 3003 mem.bswapAllFields(elf.Elf64_Sym, &sym[0]); 3004 } 3005 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index; 3006 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); 3007 }, 3008 } 3009 } 3010 3011 fn writeAllGlobalSymbols(self: *Elf) !void { 3012 const syms_sect = &self.sections.items[self.symtab_section_index.?]; 3013 const sym_size: u64 = switch (self.ptr_width) { 3014 .p32 => @sizeOf(elf.Elf32_Sym), 3015 .p64 => @sizeOf(elf.Elf64_Sym), 3016 }; 3017 const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); 3018 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; 3019 switch (self.ptr_width) { 3020 .p32 => { 3021 const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len); 3022 defer self.base.allocator.free(buf); 3023 3024 for (buf) |*sym, i| { 3025 sym.* = .{ 3026 .st_name = self.global_symbols.items[i].st_name, 3027 .st_value = @intCast(u32, self.global_symbols.items[i].st_value), 3028 .st_size = @intCast(u32, self.global_symbols.items[i].st_size), 3029 .st_info = self.global_symbols.items[i].st_info, 3030 .st_other = self.global_symbols.items[i].st_other, 3031 .st_shndx = self.global_symbols.items[i].st_shndx, 3032 }; 3033 if (foreign_endian) { 3034 mem.bswapAllFields(elf.Elf32_Sym, sym); 3035 } 3036 } 3037 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); 3038 }, 3039 .p64 => { 3040 const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len); 3041 defer self.base.allocator.free(buf); 3042 3043 for (buf) |*sym, i| { 3044 sym.* = .{ 3045 .st_name = self.global_symbols.items[i].st_name, 3046 .st_value = self.global_symbols.items[i].st_value, 3047 .st_size = self.global_symbols.items[i].st_size, 3048 .st_info = self.global_symbols.items[i].st_info, 3049 .st_other = self.global_symbols.items[i].st_other, 3050 .st_shndx = self.global_symbols.items[i].st_shndx, 3051 }; 3052 if (foreign_endian) { 3053 mem.bswapAllFields(elf.Elf64_Sym, sym); 3054 } 3055 } 3056 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off); 3057 }, 3058 } 3059 } 3060 3061 /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. 3062 fn ptrWidthBytes(self: Elf) u8 { 3063 return switch (self.ptr_width) { 3064 .p32 => 4, 3065 .p64 => 8, 3066 }; 3067 } 3068 3069 /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes 3070 /// in a 32-bit ELF file. 3071 fn archPtrWidthBytes(self: Elf) u8 { 3072 return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8); 3073 } 3074 3075 /// The reloc offset for the virtual address of a function in its Line Number Program. 3076 /// Size is a virtual address integer. 3077 const dbg_line_vaddr_reloc_index = 3; 3078 /// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. 3079 /// Size is a virtual address integer. 3080 const dbg_info_low_pc_reloc_index = 1; 3081 3082 /// The reloc offset for the line offset of a function from the previous function's line. 3083 /// It's a fixed-size 4-byte ULEB128. 3084 fn getRelocDbgLineOff(self: Elf) usize { 3085 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1; 3086 } 3087 3088 fn getRelocDbgFileIndex(self: Elf) usize { 3089 return self.getRelocDbgLineOff() + 5; 3090 } 3091 3092 fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 { 3093 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); 3094 } 3095 3096 fn dbgLineNeededHeaderBytes(self: Elf) u32 { 3097 const directory_entry_format_count = 1; 3098 const file_name_entry_format_count = 1; 3099 const directory_count = 1; 3100 const file_name_count = 1; 3101 const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." 3102 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + 3103 directory_count * 8 + file_name_count * 8 + 3104 // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like 3105 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. 3106 root_src_dir_path_len + 3107 self.base.options.module.?.root_pkg.root_src_path.len); 3108 } 3109 3110 fn dbgInfoNeededHeaderBytes(self: Elf) u32 { 3111 _ = self; 3112 return 120; 3113 } 3114 3115 const min_nop_size = 2; 3116 3117 /// Writes to the file a buffer, prefixed and suffixed by the specified number of 3118 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes 3119 /// are less than 1044480 bytes (if this limit is ever reached, this function can be 3120 /// improved to make more than one pwritev call, or the limit can be raised by a fixed 3121 /// amount by increasing the length of `vecs`). 3122 fn pwriteDbgLineNops( 3123 self: *Elf, 3124 prev_padding_size: usize, 3125 buf: []const u8, 3126 next_padding_size: usize, 3127 offset: u64, 3128 ) !void { 3129 const tracy = trace(@src()); 3130 defer tracy.end(); 3131 3132 const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; 3133 const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; 3134 var vecs: [512]std.os.iovec_const = undefined; 3135 var vec_index: usize = 0; 3136 { 3137 var padding_left = prev_padding_size; 3138 if (padding_left % 2 != 0) { 3139 vecs[vec_index] = .{ 3140 .iov_base = &three_byte_nop, 3141 .iov_len = three_byte_nop.len, 3142 }; 3143 vec_index += 1; 3144 padding_left -= three_byte_nop.len; 3145 } 3146 while (padding_left > page_of_nops.len) { 3147 vecs[vec_index] = .{ 3148 .iov_base = &page_of_nops, 3149 .iov_len = page_of_nops.len, 3150 }; 3151 vec_index += 1; 3152 padding_left -= page_of_nops.len; 3153 } 3154 if (padding_left > 0) { 3155 vecs[vec_index] = .{ 3156 .iov_base = &page_of_nops, 3157 .iov_len = padding_left, 3158 }; 3159 vec_index += 1; 3160 } 3161 } 3162 3163 vecs[vec_index] = .{ 3164 .iov_base = buf.ptr, 3165 .iov_len = buf.len, 3166 }; 3167 vec_index += 1; 3168 3169 { 3170 var padding_left = next_padding_size; 3171 if (padding_left % 2 != 0) { 3172 vecs[vec_index] = .{ 3173 .iov_base = &three_byte_nop, 3174 .iov_len = three_byte_nop.len, 3175 }; 3176 vec_index += 1; 3177 padding_left -= three_byte_nop.len; 3178 } 3179 while (padding_left > page_of_nops.len) { 3180 vecs[vec_index] = .{ 3181 .iov_base = &page_of_nops, 3182 .iov_len = page_of_nops.len, 3183 }; 3184 vec_index += 1; 3185 padding_left -= page_of_nops.len; 3186 } 3187 if (padding_left > 0) { 3188 vecs[vec_index] = .{ 3189 .iov_base = &page_of_nops, 3190 .iov_len = padding_left, 3191 }; 3192 vec_index += 1; 3193 } 3194 } 3195 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); 3196 } 3197 3198 /// Writes to the file a buffer, prefixed and suffixed by the specified number of 3199 /// bytes of padding. 3200 fn pwriteDbgInfoNops( 3201 self: *Elf, 3202 prev_padding_size: usize, 3203 buf: []const u8, 3204 next_padding_size: usize, 3205 trailing_zero: bool, 3206 offset: u64, 3207 ) !void { 3208 const tracy = trace(@src()); 3209 defer tracy.end(); 3210 3211 const page_of_nops = [1]u8{abbrev_pad1} ** 4096; 3212 var vecs: [32]std.os.iovec_const = undefined; 3213 var vec_index: usize = 0; 3214 { 3215 var padding_left = prev_padding_size; 3216 while (padding_left > page_of_nops.len) { 3217 vecs[vec_index] = .{ 3218 .iov_base = &page_of_nops, 3219 .iov_len = page_of_nops.len, 3220 }; 3221 vec_index += 1; 3222 padding_left -= page_of_nops.len; 3223 } 3224 if (padding_left > 0) { 3225 vecs[vec_index] = .{ 3226 .iov_base = &page_of_nops, 3227 .iov_len = padding_left, 3228 }; 3229 vec_index += 1; 3230 } 3231 } 3232 3233 vecs[vec_index] = .{ 3234 .iov_base = buf.ptr, 3235 .iov_len = buf.len, 3236 }; 3237 vec_index += 1; 3238 3239 { 3240 var padding_left = next_padding_size; 3241 while (padding_left > page_of_nops.len) { 3242 vecs[vec_index] = .{ 3243 .iov_base = &page_of_nops, 3244 .iov_len = page_of_nops.len, 3245 }; 3246 vec_index += 1; 3247 padding_left -= page_of_nops.len; 3248 } 3249 if (padding_left > 0) { 3250 vecs[vec_index] = .{ 3251 .iov_base = &page_of_nops, 3252 .iov_len = padding_left, 3253 }; 3254 vec_index += 1; 3255 } 3256 } 3257 3258 if (trailing_zero) { 3259 var zbuf = [1]u8{0}; 3260 vecs[vec_index] = .{ 3261 .iov_base = &zbuf, 3262 .iov_len = zbuf.len, 3263 }; 3264 vec_index += 1; 3265 } 3266 3267 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); 3268 } 3269 3270 fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr { 3271 return .{ 3272 .p_type = phdr.p_type, 3273 .p_flags = phdr.p_flags, 3274 .p_offset = @intCast(u32, phdr.p_offset), 3275 .p_vaddr = @intCast(u32, phdr.p_vaddr), 3276 .p_paddr = @intCast(u32, phdr.p_paddr), 3277 .p_filesz = @intCast(u32, phdr.p_filesz), 3278 .p_memsz = @intCast(u32, phdr.p_memsz), 3279 .p_align = @intCast(u32, phdr.p_align), 3280 }; 3281 } 3282 3283 fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { 3284 return .{ 3285 .sh_name = shdr.sh_name, 3286 .sh_type = shdr.sh_type, 3287 .sh_flags = @intCast(u32, shdr.sh_flags), 3288 .sh_addr = @intCast(u32, shdr.sh_addr), 3289 .sh_offset = @intCast(u32, shdr.sh_offset), 3290 .sh_size = @intCast(u32, shdr.sh_size), 3291 .sh_link = shdr.sh_link, 3292 .sh_info = shdr.sh_info, 3293 .sh_addralign = @intCast(u32, shdr.sh_addralign), 3294 .sh_entsize = @intCast(u32, shdr.sh_entsize), 3295 }; 3296 } 3297 3298 fn getLDMOption(target: std.Target) ?[]const u8 { 3299 switch (target.cpu.arch) { 3300 .i386 => return "elf_i386", 3301 .aarch64 => return "aarch64linux", 3302 .aarch64_be => return "aarch64_be_linux", 3303 .arm, .thumb => return "armelf_linux_eabi", 3304 .armeb, .thumbeb => return "armebelf_linux_eabi", 3305 .powerpc => return "elf32ppclinux", 3306 .powerpc64 => return "elf64ppc", 3307 .powerpc64le => return "elf64lppc", 3308 .sparc, .sparcel => return "elf32_sparc", 3309 .sparcv9 => return "elf64_sparc", 3310 .mips => return "elf32btsmip", 3311 .mipsel => return "elf32ltsmip", 3312 .mips64 => { 3313 if (target.abi == .gnuabin32) { 3314 return "elf32btsmipn32"; 3315 } else { 3316 return "elf64btsmip"; 3317 } 3318 }, 3319 .mips64el => { 3320 if (target.abi == .gnuabin32) { 3321 return "elf32ltsmipn32"; 3322 } else { 3323 return "elf64ltsmip"; 3324 } 3325 }, 3326 .s390x => return "elf64_s390", 3327 .x86_64 => { 3328 if (target.abi == .gnux32) { 3329 return "elf32_x86_64"; 3330 } else { 3331 return "elf_x86_64"; 3332 } 3333 }, 3334 .riscv32 => return "elf32lriscv", 3335 .riscv64 => return "elf64lriscv", 3336 else => return null, 3337 } 3338 } 3339 3340 fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { 3341 // TODO https://github.com/ziglang/zig/issues/1284 3342 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch 3343 std.math.maxInt(@TypeOf(actual_size)); 3344 } 3345 3346 // Provide a blueprint of csu (c-runtime startup) objects for supported 3347 // link modes. 3348 // 3349 // This is for cross-mode targets only. For host-mode targets the system 3350 // compiler can be probed to produce a robust blueprint. 3351 // 3352 // Targets requiring a libc for which zig does not bundle a libc are 3353 // host-mode targets. Unfortunately, host-mode probes are not yet 3354 // implemented. For now the data is hard-coded here. Such targets are 3355 // { freebsd, netbsd, openbsd, dragonfly }. 3356 const CsuObjects = struct { 3357 crt0: ?[]const u8 = null, 3358 crti: ?[]const u8 = null, 3359 crtbegin: ?[]const u8 = null, 3360 crtend: ?[]const u8 = null, 3361 crtn: ?[]const u8 = null, 3362 3363 fn init(arena: *mem.Allocator, link_options: link.Options, comp: *const Compilation) !CsuObjects { 3364 // crt objects are only required for libc. 3365 if (!link_options.link_libc) return CsuObjects{}; 3366 3367 var result: CsuObjects = .{}; 3368 3369 // TODO: https://github.com/ziglang/zig/issues/4629 3370 // - use inline enum type 3371 // - reduce to enum-literals for values 3372 const Mode = enum { 3373 dynamic_lib, 3374 dynamic_exe, 3375 dynamic_pie, 3376 static_exe, 3377 static_pie, 3378 }; 3379 3380 // Flatten crt case types. 3381 const mode: Mode = switch (link_options.output_mode) { 3382 .Obj => return CsuObjects{}, 3383 .Lib => switch (link_options.link_mode) { 3384 .Dynamic => Mode.dynamic_lib, 3385 .Static => return CsuObjects{}, 3386 }, 3387 .Exe => switch (link_options.link_mode) { 3388 .Dynamic => if (link_options.pie) Mode.dynamic_pie else Mode.dynamic_exe, 3389 .Static => if (link_options.pie) Mode.static_pie else Mode.static_exe, 3390 }, 3391 }; 3392 3393 if (link_options.target.isAndroid()) { 3394 switch (mode) { 3395 // zig fmt: off 3396 .dynamic_lib => result.set( null, null, "crtbegin_so.o", "crtend_so.o", null ), 3397 .dynamic_exe, 3398 .dynamic_pie => result.set( null, null, "crtbegin_dynamic.o", "crtend_android.o", null ), 3399 .static_exe, 3400 .static_pie => result.set( null, null, "crtbegin_static.o", "crtend_android.o", null ), 3401 // zig fmt: on 3402 } 3403 } else { 3404 switch (link_options.target.os.tag) { 3405 .linux => { 3406 switch (mode) { 3407 // zig fmt: off 3408 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3409 .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 3410 .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3411 .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), 3412 .static_pie => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3413 // zig fmt: on 3414 } 3415 if (link_options.libc_installation) |_| { 3416 // hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs 3417 // and they are not known at comptime. For now null-out crtbegin/end objects; 3418 // there is no feature loss, zig has never linked those objects in before. 3419 // TODO: probe for paths, ie. `cc -print-file-name` 3420 result.crtbegin = null; 3421 result.crtend = null; 3422 } else { 3423 // Bundled glibc only has Scrt1.o . 3424 if (result.crt0 != null and link_options.target.isGnuLibC()) result.crt0 = "Scrt1.o"; 3425 } 3426 }, 3427 .dragonfly => switch (mode) { 3428 // zig fmt: off 3429 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3430 .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 3431 .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3432 .static_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 3433 .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3434 // zig fmt: on 3435 }, 3436 .freebsd => switch (mode) { 3437 // zig fmt: off 3438 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3439 .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 3440 .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3441 .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), 3442 .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3443 // zig fmt: on 3444 }, 3445 .netbsd => switch (mode) { 3446 // zig fmt: off 3447 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3448 .dynamic_exe => result.set( "crt0.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 3449 .dynamic_pie => result.set( "crt0.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3450 .static_exe => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), 3451 .static_pie => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtendS.o", "crtn.o" ), 3452 // zig fmt: on 3453 }, 3454 .openbsd => switch (mode) { 3455 // zig fmt: off 3456 .dynamic_lib => result.set( null, null, "crtbeginS.o", "crtendS.o", null ), 3457 .dynamic_exe, 3458 .dynamic_pie => result.set( "crt0.o", null, "crtbegin.o", "crtend.o", null ), 3459 .static_exe, 3460 .static_pie => result.set( "rcrt0.o", null, "crtbegin.o", "crtend.o", null ), 3461 // zig fmt: on 3462 }, 3463 .haiku => switch (mode) { 3464 // zig fmt: off 3465 .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3466 .dynamic_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 3467 .dynamic_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3468 .static_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), 3469 .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), 3470 // zig fmt: on 3471 }, 3472 .solaris => switch (mode) { 3473 // zig fmt: off 3474 .dynamic_lib => result.set( null, "crti.o", null, null, "crtn.o" ), 3475 .dynamic_exe, 3476 .dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ), 3477 .static_exe, 3478 .static_pie => result.set( null, null, null, null, null ), 3479 // zig fmt: on 3480 }, 3481 else => {}, 3482 } 3483 } 3484 3485 // Convert each object to a full pathname. 3486 if (link_options.libc_installation) |lci| { 3487 const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; 3488 switch (link_options.target.os.tag) { 3489 .dragonfly => { 3490 if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 3491 if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 3492 if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 3493 3494 var gccv: []const u8 = undefined; 3495 if (link_options.target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4 }) orelse true) { 3496 gccv = "gcc80"; 3497 } else { 3498 gccv = "gcc54"; 3499 } 3500 3501 if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); 3502 if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); 3503 }, 3504 .haiku => { 3505 const gcc_dir_path = lci.gcc_dir orelse return error.LibCInstallationMissingCRTDir; 3506 if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 3507 if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 3508 if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 3509 3510 if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); 3511 if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); 3512 }, 3513 else => { 3514 inline for (std.meta.fields(@TypeOf(result))) |f| { 3515 if (@field(result, f.name)) |*obj| { 3516 obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); 3517 } 3518 } 3519 }, 3520 } 3521 } else { 3522 inline for (std.meta.fields(@TypeOf(result))) |f| { 3523 if (@field(result, f.name)) |*obj| { 3524 if (comp.crt_files.get(obj.*)) |crtf| { 3525 obj.* = crtf.full_object_path; 3526 } else { 3527 @field(result, f.name) = null; 3528 } 3529 } 3530 } 3531 } 3532 3533 return result; 3534 } 3535 3536 fn set( 3537 self: *CsuObjects, 3538 crt0: ?[]const u8, 3539 crti: ?[]const u8, 3540 crtbegin: ?[]const u8, 3541 crtend: ?[]const u8, 3542 crtn: ?[]const u8, 3543 ) void { 3544 self.crt0 = crt0; 3545 self.crti = crti; 3546 self.crtbegin = crtbegin; 3547 self.crtend = crtend; 3548 self.crtn = crtn; 3549 } 3550 };