| ... | ... | @@ -66,6 +66,8 @@ shndx: struct { |
| 66 | 66 | debug_rnglists: Section.Index, |
| 67 | 67 | debug_str: Section.Index, |
| 68 | 68 | debug_str_offsets: Section.Index, |
| 69 | bss: Section.Index, |
| 70 | tbss: Section.Index, |
| 69 | 71 | // These sections are created only as needed, and are initially `.UNDEF`. |
| 70 | 72 | init_array: Section.Index, |
| 71 | 73 | fini_array: Section.Index, |
| ... | ... | @@ -135,6 +137,10 @@ node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, Symbol.Globa |
| 135 | 137 | /// See the definition of `DsoGlobals`. |
| 136 | 138 | dso_globals: DsoGlobals, |
| 137 | 139 | |
| 140 | // These are needed to find local symbols when a nodeless section moves. |
| 141 | bss_inputs: std.ArrayList(Node.InputIndex), |
| 142 | tbss_inputs: std.ArrayList(Node.InputIndex), |
| 143 | |
| 138 | 144 | shstrtab: StringTable, |
| 139 | 145 | strtab: StringTable, |
| 140 | 146 | dynstr: StringTable, |
| ... | ... | @@ -491,7 +497,8 @@ const Archive = struct { |
| 491 | 497 | }; |
| 492 | 498 | |
| 493 | 499 | const Section = struct { |
| 494 | | /// The node corresponding to this section. |
| 500 | /// File-backed node corresponding to this section. |
| 501 | /// This is `.none` for `SHT_NOBITS` sections. |
| 495 | 502 | ni: MappedFile.Node.Index.Optional, |
| 496 | 503 | /// A symbol which is exactly at the start of this section. |
| 497 | 504 | /// |
| ... | ... | @@ -655,14 +662,30 @@ const Section = struct { |
| 655 | 662 | elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits())); |
| 656 | 663 | }, |
| 657 | 664 | } |
| 658 | | const ni = shndx.node(elf); |
| 659 | | if (min_align.compare(.gt, ni.alignment(&elf.mf))) { |
| 660 | | try ni.realign(elf.base.comp.gpa, &elf.mf, min_align); |
| 661 | | } |
| 662 | | switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { |
| 663 | | .elf => {}, |
| 664 | | .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align), |
| 665 | | else => unreachable, |
| 665 | |
| 666 | if (shndx.get(elf).ni.unwrap()) |ni| { |
| 667 | if (min_align.compare(.gt, ni.alignment(&elf.mf))) { |
| 668 | try ni.realign(elf.base.comp.gpa, &elf.mf, min_align); |
| 669 | } |
| 670 | switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) { |
| 671 | .elf => {}, |
| 672 | .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align), |
| 673 | else => unreachable, |
| 674 | } |
| 675 | } else { |
| 676 | // For sections without nodes (NOBITS), propagate alignment to the parent. |
| 677 | assert(switch (elf.shdrPtr(shndx)) { |
| 678 | inline else => |shdr| elf.targetLoad(&shdr.type) == .NOBITS, |
| 679 | }); |
| 680 | |
| 681 | if (elf.ehdrType() == .REL) return; |
| 682 | |
| 683 | const segment_ni = if (shndx.flags(elf).TLS) |
| 684 | elf.ni.tls.unwrap().? |
| 685 | else |
| 686 | elf.ni.data; |
| 687 | |
| 688 | try elf.ensureSegmentAligned(elf.getNode(segment_ni).segment, min_align); |
| 666 | 689 | } |
| 667 | 690 | } |
| 668 | 691 | |
| ... | ... | @@ -4718,6 +4741,8 @@ fn create( |
| 4718 | 4741 | .init_array = .UNDEF, |
| 4719 | 4742 | .fini_array = .UNDEF, |
| 4720 | 4743 | .preinit_array = .UNDEF, |
| 4744 | .bss = .UNDEF, |
| 4745 | .tbss = .UNDEF, |
| 4721 | 4746 | }, |
| 4722 | 4747 | .dynamic = .{ |
| 4723 | 4748 | .flags = 0, |
| ... | ... | @@ -4745,6 +4770,8 @@ fn create( |
| 4745 | 4770 | .symbols = .empty, |
| 4746 | 4771 | .default_sym_vers = .empty, |
| 4747 | 4772 | }, |
| 4773 | .bss_inputs = .empty, |
| 4774 | .tbss_inputs = .empty, |
| 4748 | 4775 | .shstrtab = .{ .map = .empty }, |
| 4749 | 4776 | .strtab = .{ .map = .empty }, |
| 4750 | 4777 | .dynstr = .{ .map = .empty }, |
| ... | ... | @@ -4827,6 +4854,8 @@ pub fn deinit(elf: *Elf) void { |
| 4827 | 4854 | elf.dso_globals.string_bytes.deinit(gpa); |
| 4828 | 4855 | elf.dso_globals.symbols.deinit(gpa); |
| 4829 | 4856 | elf.dso_globals.default_sym_vers.deinit(gpa); |
| 4857 | elf.bss_inputs.deinit(gpa); |
| 4858 | elf.tbss_inputs.deinit(gpa); |
| 4830 | 4859 | elf.shstrtab.map.deinit(gpa); |
| 4831 | 4860 | elf.strtab.map.deinit(gpa); |
| 4832 | 4861 | elf.dynstr.map.deinit(gpa); |
| ... | ... | @@ -4912,7 +4941,9 @@ fn initHeaders( |
| 4912 | 4941 | shnum += 1; // .rodata |
| 4913 | 4942 | shnum += 1; // .text |
| 4914 | 4943 | shnum += 1; // .data |
| 4944 | shnum += 1; // .bss |
| 4915 | 4945 | shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata |
| 4946 | shnum += @intFromBool(comp.config.any_non_single_threaded); // .tbss |
| 4916 | 4947 | shnum += 1; // .data.rel.ro |
| 4917 | 4948 | if (have_dynamic) { |
| 4918 | 4949 | shnum += 1; // .dynamic |
| ... | ... | @@ -5029,9 +5060,13 @@ fn initHeaders( |
| 5029 | 5060 | }; |
| 5030 | 5061 | }; |
| 5031 | 5062 | |
| 5063 | // NOBITS sections don't contribute nodes. |
| 5064 | const nobits_shnum: u32 = |
| 5065 | @as(u32, 1) + @intFromBool(comp.config.any_non_single_threaded); |
| 5066 | |
| 5032 | 5067 | const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header |
| 5033 | 5068 | 3 + // `.elf`, `.ehdr`, and `.shdr` nodes |
| 5034 | | (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node |
| 5069 | (shnum - 1 - nobits_shnum) + // -1 because the SHN_UNDEF shdr does not have a `.section` node |
| 5035 | 5070 | (phnum -| 1) + // -1 because the GNU_STACK phdr does not have a `.segment` node |
| 5036 | 5071 | @intFromBool(have_eh_frame and @"type" != .REL); // eh_frame_footer |
| 5037 | 5072 | |
| ... | ... | @@ -5521,6 +5556,13 @@ fn initHeaders( |
| 5521 | 5556 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 5522 | 5557 | .node_align = node_block_align, |
| 5523 | 5558 | })); |
| 5559 | |
| 5560 | elf.shndx.bss = try elf.addSection(elf.ni.data, .{ |
| 5561 | .name = ".bss", |
| 5562 | .type = .NOBITS, |
| 5563 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 5564 | }); |
| 5565 | |
| 5524 | 5566 | if (@"type" != .REL) { |
| 5525 | 5567 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ |
| 5526 | 5568 | .name = ".got", |
| ... | ... | @@ -6119,11 +6161,19 @@ fn initHeaders( |
| 6119 | 6161 | .manual_size = true, |
| 6120 | 6162 | }); |
| 6121 | 6163 | } |
| 6122 | | if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{ |
| 6123 | | .name = ".tdata", |
| 6124 | | .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, |
| 6125 | | .node_align = node_block_align, |
| 6126 | | }); |
| 6164 | if (elf.ni.tls.unwrap()) |tls_segment_ni| { |
| 6165 | elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{ |
| 6166 | .name = ".tdata", |
| 6167 | .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, |
| 6168 | .node_align = node_block_align, |
| 6169 | }); |
| 6170 | |
| 6171 | elf.shndx.tbss = try elf.addSection(tls_segment_ni, .{ |
| 6172 | .name = ".tbss", |
| 6173 | .type = .NOBITS, |
| 6174 | .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, |
| 6175 | }); |
| 6176 | } |
| 6127 | 6177 | switch (comp.config.debug_format) { |
| 6128 | 6178 | .strip => {}, |
| 6129 | 6179 | .dwarf => { |
| ... | ... | @@ -6330,6 +6380,23 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6330 | 6380 | => elf.computeNodeVAddr(ni), |
| 6331 | 6381 | }; |
| 6332 | 6382 | } |
| 6383 | |
| 6384 | fn tbssSegmentOffset(elf: *Elf) u64 { |
| 6385 | const tbss_addr = elf.shndx.tbss.vaddr(elf); |
| 6386 | |
| 6387 | // Hasn't been laid out yet. |
| 6388 | if (tbss_addr == 0) return 0; |
| 6389 | |
| 6390 | const tls_ni = elf.ni.tls.unwrap().?; |
| 6391 | const tls_phndx = elf.getNode(tls_ni).segment; |
| 6392 | |
| 6393 | const tls_vaddr = switch (elf.phdrSlice()) { |
| 6394 | inline else => |phdr| elf.targetLoad(&phdr[tls_phndx].vaddr), |
| 6395 | }; |
| 6396 | |
| 6397 | return tbss_addr - tls_vaddr; |
| 6398 | } |
| 6399 | |
| 6333 | 6400 | fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 { |
| 6334 | 6401 | const parent_ni = ni.parent(&elf.mf).unwrap().?; |
| 6335 | 6402 | const parent_vaddr = parent_vaddr: switch (elf.getNode(parent_ni)) { |
| ... | ... | @@ -6942,6 +7009,7 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 6942 | 7009 | name: []const u8, |
| 6943 | 7010 | flags: std.elf.SHF, |
| 6944 | 7011 | entsize: std.elf.Xword, |
| 7012 | type: std.elf.SHT = .PROGBITS, |
| 6945 | 7013 | }) (Error || error{ |
| 6946 | 7014 | UnsupportedSectionFlags, |
| 6947 | 7015 | TlsSectionUnavailable, |
| ... | ... | @@ -6979,12 +7047,10 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 6979 | 7047 | if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data"; |
| 6980 | 7048 | if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata"; |
| 6981 | 7049 | if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table"; |
| 6982 | | // TODO: actually generate a bss section! |
| 6983 | | if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data"; |
| 6984 | | if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data"; |
| 6985 | | // TODO: actually generate a tbss section! |
| 6986 | | if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata"; |
| 6987 | | if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata"; |
| 7050 | if (std.mem.eql(u8, opts.name, ".bss")) break :name ".bss"; |
| 7051 | if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".bss"; |
| 7052 | if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tbss"; |
| 7053 | if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tbss"; |
| 6988 | 7054 | break :name opts.name; |
| 6989 | 7055 | }, |
| 6990 | 7056 | }; |
| ... | ... | @@ -7005,7 +7071,11 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 7005 | 7071 | assert(gop.index == elf.shdrs.items.len); |
| 7006 | 7072 | return elf.addSection(parent_node, .{ |
| 7007 | 7073 | .name = name, |
| 7008 | | .type = .NULL, // because initial size is 0 |
| 7074 | .type = switch (opts.type) { |
| 7075 | .PROGBITS => .NULL, // size is 0 |
| 7076 | .NOBITS => .NOBITS, |
| 7077 | else => unreachable, |
| 7078 | }, |
| 7009 | 7079 | .flags = flags: { |
| 7010 | 7080 | // We need to decompress the section for linking. |
| 7011 | 7081 | var flags = opts.flags; |
| ... | ... | @@ -7026,9 +7096,20 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 7026 | 7096 | return error.SectionFlagsConflict; |
| 7027 | 7097 | } |
| 7028 | 7098 | |
| 7029 | | switch (elf.targetLoad(&shdr.type)) { |
| 7030 | | .NULL, .PROGBITS, .X86_64_UNWIND => {}, |
| 7031 | | else => return error.SectionTypeConflict, |
| 7099 | const existing_type = elf.targetLoad(&shdr.type); |
| 7100 | |
| 7101 | switch (opts.type) { |
| 7102 | .PROGBITS => switch (existing_type) { |
| 7103 | .NULL, .PROGBITS, .X86_64_UNWIND => {}, |
| 7104 | else => return error.SectionTypeConflict, |
| 7105 | }, |
| 7106 | |
| 7107 | .NOBITS => switch (existing_type) { |
| 7108 | .NOBITS => {}, |
| 7109 | else => return error.SectionTypeConflict, |
| 7110 | }, |
| 7111 | |
| 7112 | else => unreachable, |
| 7032 | 7113 | } |
| 7033 | 7114 | |
| 7034 | 7115 | // All okay, combine the section flags |
| ... | ... | @@ -7447,6 +7528,9 @@ fn loadObject( |
| 7447 | 7528 | .type = .FILE, |
| 7448 | 7529 | .shndx = .ABS, |
| 7449 | 7530 | }) }; |
| 7531 | |
| 7532 | var input_has_bss = false; |
| 7533 | var input_has_tbss = false; |
| 7450 | 7534 | const target_endian = elf.targetEndian(); |
| 7451 | 7535 | switch (elf.identClass()) { |
| 7452 | 7536 | .NONE, _ => unreachable, |
| ... | ... | @@ -7461,13 +7545,28 @@ fn loadObject( |
| 7461 | 7545 | return diags.failParse(path, "bad section header location", .{}); |
| 7462 | 7546 | if (ehdr.shentsize < @sizeOf(ElfN.Shdr)) |
| 7463 | 7547 | return diags.failParse(path, "unsupported shentsize", .{}); |
| 7464 | | const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, isi: ?InputSection.Index }, ehdr.shnum); |
| 7548 | |
| 7549 | const sections = try gpa.alloc(struct { |
| 7550 | shdr: ElfN.Shdr, |
| 7551 | placement: ?union(enum) { |
| 7552 | progbits: InputSection.Index, |
| 7553 | bss: struct { |
| 7554 | shndx: Section.Index, |
| 7555 | offset: u64, |
| 7556 | }, |
| 7557 | tbss: struct { |
| 7558 | shndx: Section.Index, |
| 7559 | offset: u64, |
| 7560 | }, |
| 7561 | }, |
| 7562 | }, ehdr.shnum); |
| 7563 | |
| 7465 | 7564 | defer gpa.free(sections); |
| 7466 | 7565 | try fr.seekTo(fl.offset + ehdr.shoff); |
| 7467 | 7566 | for (sections) |*section| { |
| 7468 | 7567 | section.* = .{ |
| 7469 | 7568 | .shdr = try r.peekStruct(ElfN.Shdr, target_endian), |
| 7470 | | .isi = null, |
| 7569 | .placement = null, |
| 7471 | 7570 | }; |
| 7472 | 7571 | try r.discardAll(ehdr.shentsize); |
| 7473 | 7572 | switch (section.shdr.type) { |
| ... | ... | @@ -7494,16 +7593,22 @@ fn loadObject( |
| 7494 | 7593 | if (section.shdr.name >= shstrtab.len) continue; |
| 7495 | 7594 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 7496 | 7595 | if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; |
| 7596 | const sect_type = section.shdr.type; |
| 7597 | const is_bss = (std.mem.startsWith(u8, name, ".bss.") or |
| 7598 | std.mem.eql(u8, name, ".bss")) and sect_type == .NOBITS; |
| 7599 | const is_tbss = (std.mem.startsWith(u8, name, ".tbss.") or |
| 7600 | std.mem.eql(u8, name, ".tbss")) and sect_type == .NOBITS; |
| 7497 | 7601 | const opts: struct { |
| 7498 | 7602 | shndx: Section.Index, |
| 7499 | 7603 | node_fixed: bool, |
| 7500 | | } = switch (section.shdr.type) { |
| 7604 | } = switch (sect_type) { |
| 7501 | 7605 | else => continue, |
| 7502 | 7606 | .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: { |
| 7503 | 7607 | const shndx = elf.mapInputSection(.{ |
| 7504 | 7608 | .name = name, |
| 7505 | 7609 | .flags = section.shdr.flags.shf, |
| 7506 | 7610 | .entsize = section.shdr.entsize, |
| 7611 | .type = if (is_bss or is_tbss) .NOBITS else .PROGBITS, |
| 7507 | 7612 | }) catch |err| switch (err) { |
| 7508 | 7613 | else => |e| return e, |
| 7509 | 7614 | error.StripSection => continue, |
| ... | ... | @@ -7559,7 +7664,8 @@ fn loadObject( |
| 7559 | 7664 | shndx != .rodata and |
| 7560 | 7665 | shndx != .data and |
| 7561 | 7666 | shndx != .data_rel_ro and |
| 7562 | | shndx != elf.shndx.tdata, |
| 7667 | shndx != elf.shndx.tdata and |
| 7668 | shndx != elf.shndx.tbss, |
| 7563 | 7669 | }; |
| 7564 | 7670 | }, |
| 7565 | 7671 | inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{ |
| ... | ... | @@ -7602,7 +7708,43 @@ fn loadObject( |
| 7602 | 7708 | const need_align: Alignment = .fromByteUnits( |
| 7603 | 7709 | std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))), |
| 7604 | 7710 | ); |
| 7711 | |
| 7605 | 7712 | try opts.shndx.ensureAligned(elf, need_align); |
| 7713 | |
| 7714 | if (is_bss or is_tbss) { |
| 7715 | const offset: u64 = offset: { |
| 7716 | switch (elf.shdrPtr(opts.shndx)) { |
| 7717 | inline else => |shdr| { |
| 7718 | // Bump our BSS output section size. |
| 7719 | const old_size: u64 = elf.targetLoad(&shdr.size); |
| 7720 | const offset = need_align.forward(old_size); |
| 7721 | const new_size = offset + section.shdr.size; |
| 7722 | elf.targetStore(&shdr.size, @intCast(new_size)); |
| 7723 | break :offset offset; |
| 7724 | }, |
| 7725 | } |
| 7726 | }; |
| 7727 | |
| 7728 | if (is_bss) { |
| 7729 | section.placement = .{ .bss = .{ |
| 7730 | .shndx = opts.shndx, |
| 7731 | .offset = offset, |
| 7732 | } }; |
| 7733 | input_has_bss = true; |
| 7734 | continue; |
| 7735 | } |
| 7736 | |
| 7737 | if (is_tbss) { |
| 7738 | section.placement = .{ .tbss = .{ |
| 7739 | .shndx = opts.shndx, |
| 7740 | .offset = offset, |
| 7741 | } }; |
| 7742 | input_has_tbss = true; |
| 7743 | } |
| 7744 | |
| 7745 | continue; |
| 7746 | } |
| 7747 | |
| 7606 | 7748 | const add_node_opts: MappedFile.Node.AddOptions = .{ |
| 7607 | 7749 | .size = need_align.forward(section.shdr.size), |
| 7608 | 7750 | .alignment = need_align, |
| ... | ... | @@ -7622,7 +7764,7 @@ fn loadObject( |
| 7622 | 7764 | } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts), |
| 7623 | 7765 | .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) }, |
| 7624 | 7766 | ); |
| 7625 | | section.isi = @fromBackingInt(@intCast(elf.input_sections.items.len)); |
| 7767 | section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) }; |
| 7626 | 7768 | elf.input_sections.addOneAssumeCapacity().* = .{ |
| 7627 | 7769 | .input = input_index, |
| 7628 | 7770 | .file_location = .{ |
| ... | ... | @@ -7719,7 +7861,31 @@ fn loadObject( |
| 7719 | 7861 | }, |
| 7720 | 7862 | }; |
| 7721 | 7863 | |
| 7722 | | const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf); |
| 7864 | const placement = |
| 7865 | sections[input_sym.shndx].placement orelse continue; |
| 7866 | |
| 7867 | const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx, const symbol_value = switch (placement) { |
| 7868 | .progbits => |isi| .{ |
| 7869 | .wrap(isi.node(elf)), |
| 7870 | elf.getNodeShndx(isi.node(elf)), |
| 7871 | input_sym.value, |
| 7872 | }, |
| 7873 | .bss => |bss| .{ |
| 7874 | bss.shndx.get(elf).ni, |
| 7875 | bss.shndx, |
| 7876 | bss.shndx.vaddr(elf) + bss.offset + input_sym.value, |
| 7877 | }, |
| 7878 | .tbss => |tbss| .{ |
| 7879 | tbss.shndx.get(elf).ni, |
| 7880 | tbss.shndx, |
| 7881 | switch (elf.ehdrType()) { |
| 7882 | .REL => tbss.offset + input_sym.value, |
| 7883 | .EXEC, .DYN => elf.tbssSegmentOffset() + |
| 7884 | tbss.offset + |
| 7885 | input_sym.value, |
| 7886 | }, |
| 7887 | }, |
| 7888 | }; |
| 7723 | 7889 | |
| 7724 | 7890 | switch (input_sym.info.bind) { |
| 7725 | 7891 | else => |bind| return diags.failParse( |
| ... | ... | @@ -7729,20 +7895,20 @@ fn loadObject( |
| 7729 | 7895 | ), |
| 7730 | 7896 | .LOCAL => { |
| 7731 | 7897 | const lsi = try elf.addLocalSymbol(.{ |
| 7732 | | .node = .wrap(input_section_node), |
| 7898 | .node = symbol_oni, |
| 7733 | 7899 | .name = name, |
| 7734 | | .value = input_sym.value, |
| 7900 | .value = symbol_value, |
| 7735 | 7901 | .size = input_sym.size, |
| 7736 | 7902 | .type = sym_type, |
| 7737 | | .shndx = elf.getNodeShndx(input_section_node), |
| 7903 | .shndx = output_shndx, |
| 7738 | 7904 | }); |
| 7739 | 7905 | si.* = .local(lsi); |
| 7740 | 7906 | }, |
| 7741 | 7907 | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 7742 | 7908 | si.* = .global(elf.addGlobalSymbol(.{ |
| 7743 | | .node = .wrap(input_section_node), |
| 7909 | .node = symbol_oni, |
| 7744 | 7910 | .name = name, |
| 7745 | | .value = input_sym.value, |
| 7911 | .value = symbol_value, |
| 7746 | 7912 | .size = input_sym.size, |
| 7747 | 7913 | .type = sym_type, |
| 7748 | 7914 | .bind = switch (bind) { |
| ... | ... | @@ -7751,7 +7917,7 @@ fn loadObject( |
| 7751 | 7917 | else => unreachable, |
| 7752 | 7918 | }, |
| 7753 | 7919 | .visibility = input_sym.other.visibility, |
| 7754 | | .shndx = elf.getNodeShndx(input_section_node), |
| 7920 | .shndx = output_shndx, |
| 7755 | 7921 | }) catch |err| switch (err) { |
| 7756 | 7922 | error.MultipleDefinitions => return diags.failParse( |
| 7757 | 7923 | path, |
| ... | ... | @@ -7783,7 +7949,15 @@ fn loadObject( |
| 7783 | 7949 | return diags.failParse(path, "unsupported rel entsize", .{}); |
| 7784 | 7950 | |
| 7785 | 7951 | const loc_sec = &sections[rel_sec.shdr.info]; |
| 7786 | | const loc_node = (loc_sec.isi orelse continue).node(elf); |
| 7952 | const loc_node = switch (loc_sec.placement orelse continue) { |
| 7953 | .progbits => |isi| isi.node(elf), |
| 7954 | .bss, .tbss => return diags.failParse( |
| 7955 | path, |
| 7956 | "relocations applying to BSS (SHT_NOBITS) sections are unsupported", |
| 7957 | .{}, |
| 7958 | ), |
| 7959 | }; |
| 7960 | |
| 7787 | 7961 | elf.resetNodeRelocs(loc_node); |
| 7788 | 7962 | |
| 7789 | 7963 | const relnum = std.math.divExact( |
| ... | ... | @@ -7850,6 +8024,16 @@ fn loadObject( |
| 7850 | 8024 | }; |
| 7851 | 8025 | }, |
| 7852 | 8026 | }; |
| 8027 | |
| 8028 | if (input_has_bss) { |
| 8029 | try elf.bss_inputs.append(gpa, input_index); |
| 8030 | try elf.ni.data.resized(gpa, &elf.mf); |
| 8031 | } |
| 8032 | |
| 8033 | if (input_has_tbss) { |
| 8034 | try elf.tbss_inputs.append(gpa, input_index); |
| 8035 | try elf.ni.tls.unwrap().?.resized(gpa, &elf.mf); |
| 8036 | } |
| 7853 | 8037 | }, |
| 7854 | 8038 | } |
| 7855 | 8039 | } |
| ... | ... | @@ -8843,7 +9027,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 8843 | 9027 | try elf.ensureSegmentAligned(phndx, opts.addralign); |
| 8844 | 9028 | } |
| 8845 | 9029 | const gpa = elf.base.comp.gpa; |
| 8846 | | try elf.nodes.ensureUnusedCapacity(gpa, 1); |
| 9030 | if (opts.type != .NOBITS) { |
| 9031 | try elf.nodes.ensureUnusedCapacity(gpa, 1); |
| 9032 | } |
| 8847 | 9033 | try elf.shdrs.ensureUnusedCapacity(gpa, 1); |
| 8848 | 9034 | const want_symbol = opts.flags.ALLOC or switch (opts.type) { |
| 8849 | 9035 | .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL, |
| ... | ... | @@ -8884,7 +9070,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 8884 | 9070 | .EXEC, .DYN => segment_ni, |
| 8885 | 9071 | }; |
| 8886 | 9072 | assert(opts.addralign.check(opts.size)); |
| 8887 | | const ni = elf.addNodeAssumeCapacity(try parent_ni.addFloatingChild(gpa, &elf.mf, .{ |
| 9073 | const oni: MappedFile.Node.Index.Optional = if (opts.type == .NOBITS) .none else .wrap(elf.addNodeAssumeCapacity(try parent_ni.addFloatingChild(gpa, &elf.mf, .{ |
| 8888 | 9074 | .size = opts.node_align.forward(opts.size), |
| 8889 | 9075 | .alignment = opts.addralign.max(opts.node_align), |
| 8890 | 9076 | .resized = opts.size > 0, |
| ... | ... | @@ -8892,18 +9078,18 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 8892 | 9078 | }), switch (opts.manual_size) { |
| 8893 | 9079 | false => .{ .section = shndx }, |
| 8894 | 9080 | true => .{ .section_manual_size = shndx }, |
| 8895 | | }); |
| 8896 | | const addr = elf.computeNodeVAddr(ni); |
| 9081 | })); |
| 9082 | const addr = if (oni.unwrap()) |ni| elf.computeNodeVAddr(ni) else 0; |
| 8897 | 9083 | elf.shdrs.appendAssumeCapacity(.{ |
| 8898 | 9084 | .lsi = if (want_symbol) try elf.addLocalSymbol(.{ |
| 8899 | | .node = ni.toOptional(), |
| 9085 | .node = oni, |
| 8900 | 9086 | .name = "", |
| 8901 | 9087 | .value = addr, |
| 8902 | 9088 | .size = 0, |
| 8903 | 9089 | .type = .SECTION, |
| 8904 | 9090 | .shndx = shndx, |
| 8905 | 9091 | }) else .null, |
| 8906 | | .ni = .wrap(ni), |
| 9092 | .ni = oni, |
| 8907 | 9093 | .rela = switch (opts.type) { |
| 8908 | 9094 | .REL => unreachable, |
| 8909 | 9095 | .RELA => .{ .free_head = .none }, |
| ... | ... | @@ -8917,7 +9103,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct { |
| 8917 | 9103 | .type = opts.type, |
| 8918 | 9104 | .flags = .{ .shf = opts.flags }, |
| 8919 | 9105 | .addr = @intCast(addr), |
| 8920 | | .offset = @intCast(elf.computeNodeElfOffset(ni)), |
| 9106 | .offset = if (oni.unwrap()) |ni| @intCast(elf.computeNodeElfOffset(ni)) else 0, |
| 8921 | 9107 | .size = @intCast(opts.size), |
| 8922 | 9108 | .link = opts.link, |
| 8923 | 9109 | .info = opts.info, |
| ... | ... | @@ -11034,7 +11220,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11034 | 11220 | .DYNAMIC, |
| 11035 | 11221 | .INTERP, |
| 11036 | 11222 | .PHDR, |
| 11037 | | .TLS, |
| 11038 | 11223 | .GNU_EH_FRAME, |
| 11039 | 11224 | .GNU_RELRO, |
| 11040 | 11225 | => { |
| ... | ... | @@ -11042,6 +11227,28 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11042 | 11227 | elf.targetStore(&ph.vaddr, @intCast(new_vaddr)); |
| 11043 | 11228 | elf.targetStore(&ph.paddr, @intCast(new_vaddr)); |
| 11044 | 11229 | }, |
| 11230 | |
| 11231 | .TLS => { |
| 11232 | const tbss_base = elf.tbssSegmentOffset(); |
| 11233 | const new_vaddr = elf.computeNodeVAddr(ni); |
| 11234 | const tls_offset = elf.targetLoad(&ph.offset); |
| 11235 | const tls_filesz = elf.targetLoad(&ph.filesz); |
| 11236 | |
| 11237 | elf.targetStore(&ph.vaddr, @intCast(new_vaddr)); |
| 11238 | elf.targetStore(&ph.paddr, @intCast(new_vaddr)); |
| 11239 | |
| 11240 | // This may seem like a no-op since we're moving to and fro the same base, |
| 11241 | // but we also need to update the section STT_SECTION symbol, which this |
| 11242 | // function will conveniently do for us. |
| 11243 | elf.updateNobitsAddress( |
| 11244 | elf.shndx.tbss, |
| 11245 | new_vaddr + tbss_base, |
| 11246 | tls_offset + tls_filesz, |
| 11247 | tbss_base, |
| 11248 | tbss_base, |
| 11249 | elf.tbss_inputs.items, |
| 11250 | ); |
| 11251 | }, |
| 11045 | 11252 | } |
| 11046 | 11253 | }, |
| 11047 | 11254 | } |
| ... | ... | @@ -11372,6 +11579,78 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11372 | 11579 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11373 | 11580 | } |
| 11374 | 11581 | |
| 11582 | /// Update the position of a `NOBITS` section. |
| 11583 | /// BSS needs an absolute symbol base, and TBSS needs TLS-segment-relative base. |
| 11584 | fn updateNobitsAddress( |
| 11585 | elf: *Elf, |
| 11586 | shndx: Section.Index, |
| 11587 | new_section_addr: u64, |
| 11588 | new_file_offset: u64, |
| 11589 | old_symbol_base: u64, |
| 11590 | new_symbol_base: u64, |
| 11591 | inputs: []const Node.InputIndex, |
| 11592 | ) void { |
| 11593 | assert(shndx != .UNDEF); |
| 11594 | |
| 11595 | switch (elf.shdrPtr(shndx)) { |
| 11596 | inline else => |shdr| { |
| 11597 | elf.targetStore(&shdr.addr, @intCast(new_section_addr)); |
| 11598 | elf.targetStore(&shdr.offset, @intCast(new_file_offset)); |
| 11599 | }, |
| 11600 | } |
| 11601 | |
| 11602 | const expected_shndx = shndx.toSection().?; |
| 11603 | |
| 11604 | // Update local symbols. |
| 11605 | for (inputs) |ii| { |
| 11606 | var lsi, const end_lsi = ii.localSymbolRange(elf); |
| 11607 | |
| 11608 | while (lsi != end_lsi) : (lsi = @fromBackingInt( |
| 11609 | @backingInt(lsi) + 1, |
| 11610 | )) { |
| 11611 | const symbol_shndx, const visibility = switch (elf.symPtr(lsi.index())) { |
| 11612 | inline else => |sym| .{ |
| 11613 | elf.targetLoad(&sym.shndx), |
| 11614 | elf.targetLoad(&sym.other).visibility, |
| 11615 | }, |
| 11616 | }; |
| 11617 | |
| 11618 | if (symbol_shndx != expected_shndx) continue; |
| 11619 | |
| 11620 | switch (visibility) { |
| 11621 | // These are globals demoted to STB_LOCAL and are updated below. |
| 11622 | .HIDDEN, .INTERNAL => continue, |
| 11623 | .PROTECTED => unreachable, |
| 11624 | .DEFAULT => {}, |
| 11625 | } |
| 11626 | |
| 11627 | const symbol: Symbol.Id = .local(lsi); |
| 11628 | symbol.flushMoved( |
| 11629 | elf, |
| 11630 | symbol.value(elf) - old_symbol_base + new_symbol_base, |
| 11631 | ); |
| 11632 | } |
| 11633 | } |
| 11634 | |
| 11635 | // Update globals. |
| 11636 | // Since nobits sections have no nodes, we can't just iterate over `node_global_symbols`, |
| 11637 | // so instead filter manually. |
| 11638 | for (elf.globals.items, 0..) |global, global_index| { |
| 11639 | if (global.symtab_index.ptr(elf).node != .none) continue; |
| 11640 | const symbol: Symbol.Id = .global(@fromBackingInt(@intCast(global_index))); |
| 11641 | const symbol_shndx = switch (elf.symPtr(global.symtab_index)) { |
| 11642 | inline else => |sym| elf.targetLoad(&sym.shndx), |
| 11643 | }; |
| 11644 | if (symbol_shndx != expected_shndx) continue; |
| 11645 | symbol.flushMoved(elf, symbol.value(elf) - old_symbol_base + new_symbol_base); |
| 11646 | } |
| 11647 | |
| 11648 | Symbol.Id.local(shndx.get(elf).lsi).flushMoved( |
| 11649 | elf, |
| 11650 | new_section_addr, |
| 11651 | ); |
| 11652 | } |
| 11653 | |
| 11375 | 11654 | /// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz` |
| 11376 | 11655 | /// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and |
| 11377 | 11656 | /// `memsz` fields of the segment, in order to place it at a valid virtual address. |
| ... | ... | @@ -11400,22 +11679,21 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11400 | 11679 | switch (elf.phdrSlice()) { |
| 11401 | 11680 | inline else => |phdr| { |
| 11402 | 11681 | const offset = elf.targetLoad(&phdr[orig_phndx].offset); |
| 11403 | | const size = elf.targetLoad(&phdr[orig_phndx].filesz); |
| 11404 | | |
| 11405 | | if (size == 0) { |
| 11406 | | assert(elf.targetLoad(&phdr[orig_phndx].type) == .NULL); |
| 11407 | | } else { |
| 11408 | | assert(elf.targetLoad(&phdr[orig_phndx].type) == .LOAD); |
| 11409 | | } |
| 11410 | | |
| 11411 | | elf.targetStore(&phdr[orig_phndx].memsz, size); |
| 11412 | | elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits())); |
| 11682 | const filesz = elf.targetLoad(&phdr[orig_phndx].filesz); |
| 11413 | 11683 | |
| 11414 | 11684 | const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr); |
| 11415 | 11685 | assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr); |
| 11416 | 11686 | |
| 11687 | const bss_size = if (segment_ni == elf.ni.data) |
| 11688 | elf.shndx.bss.size(elf) |
| 11689 | else |
| 11690 | 0; |
| 11691 | const memsz = filesz + bss_size; |
| 11692 | |
| 11417 | 11693 | var vaddr: u64 = orig_vaddr; |
| 11418 | 11694 | |
| 11695 | elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits())); |
| 11696 | |
| 11419 | 11697 | // First, we will shift the virtual address as needed in order to maintain the required |
| 11420 | 11698 | // property that vaddr is congruent to offset modulo the phdr alignment. |
| 11421 | 11699 | { |
| ... | ... | @@ -11459,7 +11737,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11459 | 11737 | // the segment is already known to be moving---making it easier to grow in-place is |
| 11460 | 11738 | // the whole point of the multiplier! |
| 11461 | 11739 | { |
| 11462 | | const target_size = if (vaddr == orig_vaddr) size else size * reserve_size_multiplier; |
| 11740 | const target_size = if (vaddr == orig_vaddr) memsz else memsz * reserve_size_multiplier; |
| 11463 | 11741 | if (vaddr + target_size <= next_page_vaddr) { |
| 11464 | 11742 | break; // hooray, we fit here! |
| 11465 | 11743 | } |
| ... | ... | @@ -11481,7 +11759,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11481 | 11759 | else => {}, |
| 11482 | 11760 | }; |
| 11483 | 11761 | |
| 11484 | | const free_vaddr = vaddr + size * reserve_size_multiplier; |
| 11762 | const free_vaddr = vaddr + memsz * reserve_size_multiplier; |
| 11485 | 11763 | |
| 11486 | 11764 | const next_align = page_align.max(next_ni.alignment(&elf.mf)); |
| 11487 | 11765 | const next_offset = elf.targetLoad(&next_ph.offset); |
| ... | ... | @@ -11491,6 +11769,21 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11491 | 11769 | // will update the vaddr of `phndx` when we break from the loop. |
| 11492 | 11770 | elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr)); |
| 11493 | 11771 | elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr)); |
| 11772 | |
| 11773 | const bss_shndx = elf.shndx.bss; |
| 11774 | const next_filesz = elf.targetLoad(&next_ph.filesz); |
| 11775 | const old_bss_addr = bss_shndx.vaddr(elf); |
| 11776 | const new_bss_addr = next_new_vaddr + next_filesz; |
| 11777 | |
| 11778 | elf.updateNobitsAddress( |
| 11779 | bss_shndx, |
| 11780 | new_bss_addr, |
| 11781 | next_offset + next_filesz, |
| 11782 | old_bss_addr, |
| 11783 | new_bss_addr, |
| 11784 | elf.bss_inputs.items, |
| 11785 | ); |
| 11786 | |
| 11494 | 11787 | try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11495 | 11788 | |
| 11496 | 11789 | break; |
| ... | ... | @@ -11518,6 +11811,27 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro |
| 11518 | 11811 | elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr)); |
| 11519 | 11812 | try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 11520 | 11813 | } |
| 11814 | |
| 11815 | const ph = &phdr[phndx]; |
| 11816 | |
| 11817 | elf.targetStore(&ph.memsz, @intCast(memsz)); |
| 11818 | elf.targetStore(&ph.type, if (memsz > 0) .LOAD else .NULL); |
| 11819 | |
| 11820 | if (segment_ni == elf.ni.data and elf.shndx.bss != .UNDEF) { |
| 11821 | const bss_shndx = elf.shndx.bss; |
| 11822 | |
| 11823 | const old_bss_addr = bss_shndx.vaddr(elf); |
| 11824 | const new_bss_addr = vaddr + filesz; |
| 11825 | |
| 11826 | elf.updateNobitsAddress( |
| 11827 | bss_shndx, |
| 11828 | new_bss_addr, |
| 11829 | offset + filesz, |
| 11830 | old_bss_addr, |
| 11831 | new_bss_addr, |
| 11832 | elf.bss_inputs.items, |
| 11833 | ); |
| 11834 | } |
| 11521 | 11835 | }, |
| 11522 | 11836 | } |
| 11523 | 11837 | } |
| ... | ... | @@ -11560,7 +11874,25 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 11560 | 11874 | elf.targetStore(&ph.memsz, @intCast(size)); |
| 11561 | 11875 | }, |
| 11562 | 11876 | .TLS => { |
| 11563 | | elf.targetStore(&ph.memsz, @intCast(size)); |
| 11877 | const tls_filesz: u64 = size; |
| 11878 | |
| 11879 | const old_tbss_base = elf.tbssSegmentOffset(); |
| 11880 | const new_tbss_base = tls_filesz; |
| 11881 | const tbss_size = elf.shndx.tbss.size(elf); |
| 11882 | const tls_vaddr = elf.targetLoad(&ph.vaddr); |
| 11883 | const tls_offset = elf.targetLoad(&ph.offset); |
| 11884 | |
| 11885 | elf.targetStore(&ph.memsz, @intCast(tls_filesz + tbss_size)); |
| 11886 | |
| 11887 | elf.updateNobitsAddress( |
| 11888 | elf.shndx.tbss, |
| 11889 | tls_vaddr + new_tbss_base, |
| 11890 | tls_offset + new_tbss_base, |
| 11891 | old_tbss_base, |
| 11892 | new_tbss_base, |
| 11893 | elf.tbss_inputs.items, |
| 11894 | ); |
| 11895 | |
| 11564 | 11896 | // TPOFF relocations care about the size of the TLS segment. Re-apply |
| 11565 | 11897 | // those, and also update any GOT entries from GOTTPOFF relocations. |
| 11566 | 11898 | for (elf.tls_size_symbol_relocs.keys()) |reloc| { |