authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-12 17:42:58+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-16 19:33:05+02:00
log716a45a209d4f42ddc9937dc552c6f6d89c8b808
tree7cfd7deef68940ca1685779fe5a5c4aef57705ff
parent5ff12003eeb88ae0d02318f6147cccd09a3149ea

elf: use findFreeSpace mechanics to allocate object-extracted segments


2 files changed, 271 insertions(+), 97 deletions(-)

src/link/Elf.zig+267-93
...@@ -542,21 +542,23 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -542,21 +542,23 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
542 }542 }
543543
544 for (self.shdrs.items) |shdr| {544 for (self.shdrs.items) |shdr| {
545 // SHT_NOBITS takes no physical space in the output file so set its size to 0.545 if (shdr.sh_type == elf.SHT_NOBITS) continue;
546 const sh_size = if (shdr.sh_type == elf.SHT_NOBITS) 0 else shdr.sh_size;546 const increased_size = padToIdeal(shdr.sh_size);
547 const increased_size = padToIdeal(sh_size);
548 const test_end = shdr.sh_offset + increased_size;547 const test_end = shdr.sh_offset + increased_size;
549 if (end > shdr.sh_offset and start < test_end) {548 if (end > shdr.sh_offset and start < test_end) {
550 return test_end;549 return test_end;
551 }550 }
552 }551 }
552
553 for (self.phdrs.items) |phdr| {553 for (self.phdrs.items) |phdr| {
554 if (phdr.p_type != elf.PT_LOAD) continue;
554 const increased_size = padToIdeal(phdr.p_filesz);555 const increased_size = padToIdeal(phdr.p_filesz);
555 const test_end = phdr.p_offset + increased_size;556 const test_end = phdr.p_offset + increased_size;
556 if (end > phdr.p_offset and start < test_end) {557 if (end > phdr.p_offset and start < test_end) {
557 return test_end;558 return test_end;
558 }559 }
559 }560 }
561
560 return null;562 return null;
561}563}
562564
...@@ -597,19 +599,20 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {...@@ -597,19 +599,20 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
597599
598const AllocateSegmentOpts = struct {600const AllocateSegmentOpts = struct {
599 addr: u64,601 addr: u64,
600 size: u64,602 memsz: u64,
603 filesz: u64,
601 alignment: u64,604 alignment: u64,
602 flags: u32 = elf.PF_R,605 flags: u32 = elf.PF_R,
603};606};
604607
605pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {608pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {
606 const off = self.findFreeSpace(opts.size, opts.alignment);609 const off = self.findFreeSpace(opts.filesz, opts.alignment);
607 const index = try self.addPhdr(.{610 const index = try self.addPhdr(.{
608 .type = elf.PT_LOAD,611 .type = elf.PT_LOAD,
609 .offset = off,612 .offset = off,
610 .filesz = opts.size,613 .filesz = opts.filesz,
611 .addr = opts.addr,614 .addr = opts.addr,
612 .memsz = opts.size,615 .memsz = opts.memsz,
613 .@"align" = opts.alignment,616 .@"align" = opts.alignment,
614 .flags = opts.flags,617 .flags = opts.flags,
615 });618 });
...@@ -619,9 +622,9 @@ pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}...@@ -619,9 +622,9 @@ pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}
619 if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_',622 if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_',
620 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',623 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',
621 off,624 off,
622 off + opts.size,625 off + opts.filesz,
623 opts.addr,626 opts.addr,
624 opts.addr + opts.size,627 opts.addr + opts.memsz,
625 });628 });
626 return index;629 return index;
627}630}
...@@ -698,7 +701,8 @@ pub fn initMetadata(self: *Elf) !void {...@@ -698,7 +701,8 @@ pub fn initMetadata(self: *Elf) !void {
698 if (self.phdr_load_re_zig_index == null) {701 if (self.phdr_load_re_zig_index == null) {
699 self.phdr_load_re_zig_index = try self.allocateSegment(.{702 self.phdr_load_re_zig_index = try self.allocateSegment(.{
700 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,703 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
701 .size = self.base.options.program_code_size_hint,704 .memsz = self.base.options.program_code_size_hint,
705 .filesz = self.base.options.program_code_size_hint,
702 .alignment = self.page_size,706 .alignment = self.page_size,
703 .flags = elf.PF_X | elf.PF_R | elf.PF_W,707 .flags = elf.PF_X | elf.PF_R | elf.PF_W,
704 });708 });
...@@ -710,7 +714,8 @@ pub fn initMetadata(self: *Elf) !void {...@@ -710,7 +714,8 @@ pub fn initMetadata(self: *Elf) !void {
710 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);714 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
711 self.phdr_got_zig_index = try self.allocateSegment(.{715 self.phdr_got_zig_index = try self.allocateSegment(.{
712 .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,716 .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
713 .size = @as(u64, ptr_size) * self.base.options.symbol_count_hint,717 .memsz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
718 .filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
714 .alignment = alignment,719 .alignment = alignment,
715 .flags = elf.PF_R | elf.PF_W,720 .flags = elf.PF_R | elf.PF_W,
716 });721 });
...@@ -720,7 +725,8 @@ pub fn initMetadata(self: *Elf) !void {...@@ -720,7 +725,8 @@ pub fn initMetadata(self: *Elf) !void {
720 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);725 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
721 self.phdr_load_ro_zig_index = try self.allocateSegment(.{726 self.phdr_load_ro_zig_index = try self.allocateSegment(.{
722 .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,727 .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
723 .size = 1024,728 .memsz = 1024,
729 .filesz = 1024,
724 .alignment = alignment,730 .alignment = alignment,
725 .flags = elf.PF_R | elf.PF_W,731 .flags = elf.PF_R | elf.PF_W,
726 });732 });
...@@ -730,7 +736,8 @@ pub fn initMetadata(self: *Elf) !void {...@@ -730,7 +736,8 @@ pub fn initMetadata(self: *Elf) !void {
730 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);736 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
731 self.phdr_load_rw_zig_index = try self.allocateSegment(.{737 self.phdr_load_rw_zig_index = try self.allocateSegment(.{
732 .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,738 .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
733 .size = 1024,739 .memsz = 1024,
740 .filesz = 1024,
734 .alignment = alignment,741 .alignment = alignment,
735 .flags = elf.PF_R | elf.PF_W,742 .flags = elf.PF_R | elf.PF_W,
736 });743 });
...@@ -740,7 +747,8 @@ pub fn initMetadata(self: *Elf) !void {...@@ -740,7 +747,8 @@ pub fn initMetadata(self: *Elf) !void {
740 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);747 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
741 self.phdr_load_zerofill_zig_index = try self.allocateSegment(.{748 self.phdr_load_zerofill_zig_index = try self.allocateSegment(.{
742 .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,749 .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
743 .size = 0,750 .memsz = 0,
751 .filesz = 0,
744 .alignment = alignment,752 .alignment = alignment,
745 .flags = elf.PF_R | elf.PF_W,753 .flags = elf.PF_R | elf.PF_W,
746 });754 });
...@@ -1602,7 +1610,35 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1602,7 +1610,35 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1602 try self.setVersionSymtab();1610 try self.setVersionSymtab();
1603 try self.updateSectionSizes();1611 try self.updateSectionSizes();
16041612
1605 try self.initAndAllocateSegments();1613 // Allocate PHDR table.
1614 {
1615 const new_load_segments = self.calcNumberOfSegments();
1616 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
1617 const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
1618
1619 const phsize: u64 = switch (self.ptr_width) {
1620 .p32 => @sizeOf(elf.Elf32_Phdr),
1621 .p64 => @sizeOf(elf.Elf64_Phdr),
1622 };
1623 const needed_size = (self.phdrs.items.len + new_load_segments) * phsize;
1624
1625 if (needed_size > self.allocatedSize(phdr_table.p_offset)) {
1626 phdr_table.p_offset = 0;
1627 phdr_table.p_offset = self.findFreeSpace(needed_size, phdr_table.p_align);
1628 }
1629
1630 phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align);
1631 const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
1632 phdr_table_load.p_filesz = load_align_offset + needed_size;
1633 phdr_table_load.p_memsz = load_align_offset + needed_size;
1634
1635 phdr_table.p_filesz = needed_size;
1636 phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
1637 phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
1638 phdr_table.p_memsz = needed_size;
1639 }
1640
1641 try self.allocateAllocSections();
1606 self.allocateNonAllocSections();1642 self.allocateNonAllocSections();
1607 self.allocateSpecialPhdrs();1643 self.allocateSpecialPhdrs();
1608 self.allocateAtoms();1644 self.allocateAtoms();
...@@ -2832,26 +2868,11 @@ fn writePhdrTable(self: *Elf) !void {...@@ -2832,26 +2868,11 @@ fn writePhdrTable(self: *Elf) !void {
2832 const gpa = self.base.allocator;2868 const gpa = self.base.allocator;
2833 const target_endian = self.base.options.target.cpu.arch.endian();2869 const target_endian = self.base.options.target.cpu.arch.endian();
2834 const foreign_endian = target_endian != builtin.cpu.arch.endian();2870 const foreign_endian = target_endian != builtin.cpu.arch.endian();
2835 const phsize: u64 = switch (self.ptr_width) {
2836 .p32 => @sizeOf(elf.Elf32_Phdr),
2837 .p64 => @sizeOf(elf.Elf64_Phdr),
2838 };
2839 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];2871 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
2840 const phdr_segment = &self.phdrs.items[self.phdr_table_load_index.?];
2841 const needed_size = self.phdrs.items.len * phsize;
2842 phdr_table.p_filesz = needed_size;
2843 phdr_table.p_memsz = needed_size;
2844
2845 const ehsize: u64 = switch (self.ptr_width) {
2846 .p32 => @sizeOf(elf.Elf32_Ehdr),
2847 .p64 => @sizeOf(elf.Elf64_Ehdr),
2848 };
2849 phdr_segment.p_filesz = needed_size + ehsize;
2850 phdr_segment.p_memsz = needed_size + ehsize;
28512872
2852 log.debug("writing program headers from 0x{x} to 0x{x}", .{2873 log.debug("writing program headers from 0x{x} to 0x{x}", .{
2853 phdr_table.p_offset,2874 phdr_table.p_offset,
2854 phdr_table.p_offset + needed_size,2875 phdr_table.p_offset + phdr_table.p_filesz,
2855 });2876 });
28562877
2857 switch (self.ptr_width) {2878 switch (self.ptr_width) {
...@@ -4454,7 +4475,6 @@ fn initSegments(self: *Elf) !void {...@@ -4454,7 +4475,6 @@ fn initSegments(self: *Elf) !void {
4454 // Add LOAD phdrs4475 // Add LOAD phdrs
4455 const gpa = self.base.allocator;4476 const gpa = self.base.allocator;
4456 const slice = self.shdrs.items;4477 const slice = self.shdrs.items;
4457 var is_first = true;
4458 var shndx: u16 = 0;4478 var shndx: u16 = 0;
4459 while (shndx < slice.len) {4479 while (shndx < slice.len) {
4460 const shdr = &slice[shndx];4480 const shdr = &slice[shndx];
...@@ -4466,10 +4486,9 @@ fn initSegments(self: *Elf) !void {...@@ -4466,10 +4486,9 @@ fn initSegments(self: *Elf) !void {
4466 .type = elf.PT_LOAD,4486 .type = elf.PT_LOAD,
4467 .flags = shdrToPhdrFlags(shdr.sh_flags),4487 .flags = shdrToPhdrFlags(shdr.sh_flags),
4468 .@"align" = @max(self.page_size, shdr.sh_addralign),4488 .@"align" = @max(self.page_size, shdr.sh_addralign),
4469 .offset = if (is_first) 0 else shdr.sh_offset,4489 .offset = shdr.sh_offset,
4470 .addr = if (is_first) self.calcImageBase() else shdr.sh_addr,4490 .addr = shdr.sh_addr,
4471 });4491 });
4472 is_first = false;
4473 const p_flags = self.phdrs.items[phndx].p_flags;4492 const p_flags = self.phdrs.items[phndx].p_flags;
4474 self.addShdrToPhdr(shndx, phndx);4493 self.addShdrToPhdr(shndx, phndx);
4475 try self.phdr_to_shdr_table.putNoClobber(gpa, shndx, phndx);4494 try self.phdr_to_shdr_table.putNoClobber(gpa, shndx, phndx);
...@@ -4495,7 +4514,7 @@ fn addShdrToPhdr(self: *Elf, shdr_index: u16, phdr_index: u16) void {...@@ -4495,7 +4514,7 @@ fn addShdrToPhdr(self: *Elf, shdr_index: u16, phdr_index: u16) void {
4495 const phdr = &self.phdrs.items[phdr_index];4514 const phdr = &self.phdrs.items[phdr_index];
4496 phdr.p_align = @max(phdr.p_align, shdr.sh_addralign);4515 phdr.p_align = @max(phdr.p_align, shdr.sh_addralign);
4497 if (shdr.sh_type != elf.SHT_NOBITS) {4516 if (shdr.sh_type != elf.SHT_NOBITS) {
4498 phdr.p_filesz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr;4517 phdr.p_filesz = shdr.sh_offset + shdr.sh_size - phdr.p_offset;
4499 }4518 }
4500 phdr.p_memsz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr;4519 phdr.p_memsz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr;
4501}4520}
...@@ -4529,6 +4548,18 @@ pub inline fn shdrIsTls(shdr: *const elf.Elf64_Shdr) bool {...@@ -4529,6 +4548,18 @@ pub inline fn shdrIsTls(shdr: *const elf.Elf64_Shdr) bool {
4529 return shdr.sh_flags & elf.SHF_TLS != 0;4548 return shdr.sh_flags & elf.SHF_TLS != 0;
4530}4549}
45314550
4551fn calcNumberOfSegments(self: *Elf) usize {
4552 var count: usize = 0;
4553 var flags: u64 = 0;
4554 for (self.shdrs.items) |shdr| {
4555 if (shdr.sh_type == elf.SHT_NULL) continue;
4556 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
4557 if (flags != shdrToPhdrFlags(shdr.sh_flags)) count += 1;
4558 flags = shdrToPhdrFlags(shdr.sh_flags);
4559 }
4560 return count;
4561}
4562
4532fn allocateAllocSectionsInMemory(self: *Elf, base_addr: u64) void {4563fn allocateAllocSectionsInMemory(self: *Elf, base_addr: u64) void {
4533 // We use this struct to track maximum alignment of all TLS sections.4564 // We use this struct to track maximum alignment of all TLS sections.
4534 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,4565 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
...@@ -4649,28 +4680,150 @@ fn allocateAllocSectionsInFile(self: *Elf, base_offset: u64) void {...@@ -4649,28 +4680,150 @@ fn allocateAllocSectionsInFile(self: *Elf, base_offset: u64) void {
4649/// As a base address we take space immediately after the PHDR table, and4680/// As a base address we take space immediately after the PHDR table, and
4650/// aim for fitting between it and where the first incremental segment is4681/// aim for fitting between it and where the first incremental segment is
4651/// allocated (see `initMetadata`).4682/// allocated (see `initMetadata`).
4652fn initAndAllocateSegments(self: *Elf) !void {4683fn initAndAllocateSegments(self: *Elf, base_addr: u64, base_offset: u64) !void {
4653 const ehsize: u64 = switch (self.ptr_width) {
4654 .p32 => @sizeOf(elf.Elf32_Ehdr),
4655 .p64 => @sizeOf(elf.Elf64_Ehdr),
4656 };
4657 const phsize: u64 = switch (self.ptr_width) {
4658 .p32 => @sizeOf(elf.Elf32_Phdr),
4659 .p64 => @sizeOf(elf.Elf64_Phdr),
4660 };
4661 const image_base = self.calcImageBase();
4662 while (true) {4684 while (true) {
4663 const nphdrs = self.phdrs.items.len;4685 const nphdrs = self.phdrs.items.len;
4664 const off = ehsize + nphdrs * phsize;4686 self.allocateAllocSectionsInMemory(base_addr);
4665 const addr = image_base + off;4687 self.allocateAllocSectionsInFile(base_offset);
4666 self.allocateAllocSectionsInMemory(addr);
4667 self.allocateAllocSectionsInFile(off);
4668 try self.resetPhdrs();4688 try self.resetPhdrs();
4669 try self.initSegments();4689 try self.initSegments();
4670 if (nphdrs == self.phdrs.items.len) break;4690 if (nphdrs == self.phdrs.items.len) break;
4671 }4691 }
4672}4692}
46734693
4694fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
4695 // We use this struct to track maximum alignment of all TLS sections.
4696 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
4697 // in-file offsets have to be aligned against the start of TLS program header.
4698 // If that's not ensured, then in a multi-threaded context, TLS variables across a shared object
4699 // boundary may not get correctly loaded at an aligned address.
4700 const Align = struct {
4701 tls_start_align: u64 = 1,
4702 first_tls_index: ?usize = null,
4703
4704 fn isFirstTlsShdr(this: @This(), other: usize) bool {
4705 if (this.first_tls_index) |index| return index == other;
4706 return false;
4707 }
4708
4709 fn @"align"(this: @This(), index: usize, sh_addralign: u64, addr: u64) u64 {
4710 const alignment = if (this.isFirstTlsShdr(index)) this.tls_start_align else sh_addralign;
4711 return mem.alignForward(u64, addr, alignment);
4712 }
4713 };
4714
4715 var alignment = Align{};
4716 for (self.shdrs.items, 0..) |*shdr, i| {
4717 if (shdr.sh_type == elf.SHT_NULL) continue;
4718 if (!shdrIsTls(shdr)) continue;
4719 if (alignment.first_tls_index == null) alignment.first_tls_index = i;
4720 alignment.tls_start_align = @max(alignment.tls_start_align, shdr.sh_addralign);
4721 }
4722
4723 const gpa = self.base.allocator;
4724 const nphdrs = self.calcNumberOfSegments();
4725 var cuts = try gpa.alloc(struct { start: u16, len: u16 }, nphdrs);
4726 defer gpa.free(cuts);
4727
4728 var shndx = for (self.shdrs.items, 0..) |shdr, in| {
4729 if (shdr.sh_type == elf.SHT_NULL) continue;
4730 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
4731 break @as(u16, @intCast(in));
4732 } else @as(u16, @intCast(self.shdrs.items.len));
4733
4734 for (cuts) |*cut| {
4735 cut.* = .{ .start = shndx, .len = 0 };
4736 var flags = shdrToPhdrFlags(self.shdrs.items[shndx].sh_flags);
4737
4738 while (shndx < self.shdrs.items.len) : (shndx += 1) {
4739 const shdr = &self.shdrs.items[shndx];
4740 if (!shdrIsAlloc(shdr)) break;
4741 if (shdrToPhdrFlags(shdr.sh_flags) != flags) {
4742 cut.len = shndx - cut.start;
4743 break;
4744 }
4745 }
4746 }
4747 cuts[nphdrs - 1].len = shndx - cuts[nphdrs - 1].start;
4748
4749 // Allocate in segments
4750 const phdr_table = &self.phdrs.items[self.phdr_table_load_index.?];
4751 var addr = phdr_table.p_vaddr + phdr_table.p_memsz;
4752
4753 for (cuts) |cut| {
4754 const slice = self.shdrs.items[cut.start..][0..cut.len];
4755
4756 var @"align": u64 = self.page_size;
4757 for (slice) |*shdr| {
4758 if (shdrIsTbss(shdr)) continue;
4759 @"align" = @max(@"align", shdr.sh_addralign);
4760 }
4761
4762 addr = mem.alignForward(u64, addr, @"align");
4763
4764 var memsz: u64 = 0;
4765 var filesz: u64 = 0;
4766 var i: usize = 0;
4767 while (i < cut.len) : (i += 1) {
4768 const shdr = &slice[i];
4769 if (shdrIsTbss(shdr)) {
4770 // .tbss is a little special as it's used only by the loader meaning it doesn't
4771 // need to be actually mmap'ed at runtime. We still need to correctly increment
4772 // the addresses of every TLS zerofill section tho. Thus, we hack it so that
4773 // we increment the start address like normal, however, after we are done,
4774 // the next ALLOC section will get its start address allocated within the same
4775 // range as the .tbss sections. We will get something like this:
4776 //
4777 // ...
4778 // .tbss 0x10
4779 // .tcommon 0x20
4780 // .data 0x10
4781 // ...
4782 var tbss_addr = addr;
4783 while (i < cut.len and shdrIsTbss(&slice[i])) : (i += 1) {
4784 const tbss_shdr = &slice[i];
4785 tbss_addr = alignment.@"align"(cut.start + i, tbss_shdr.sh_addralign, tbss_addr);
4786 tbss_shdr.sh_addr = tbss_addr;
4787 tbss_addr += tbss_shdr.sh_size;
4788 }
4789 i -= 1;
4790 continue;
4791 }
4792 const next = alignment.@"align"(cut.start + i, shdr.sh_addralign, addr);
4793 const padding = next - addr;
4794 addr = next;
4795 shdr.sh_addr = addr;
4796 if (shdr.sh_type != elf.SHT_NOBITS) {
4797 filesz += padding + shdr.sh_size;
4798 }
4799 memsz += padding + shdr.sh_size;
4800 addr += shdr.sh_size;
4801 }
4802
4803 var off = self.findFreeSpace(filesz, @"align");
4804 const phndx = try self.addPhdr(.{
4805 .type = elf.PT_LOAD,
4806 .offset = off,
4807 .addr = slice[0].sh_addr,
4808 .memsz = memsz,
4809 .filesz = filesz,
4810 .@"align" = @"align",
4811 .flags = shdrToPhdrFlags(slice[0].sh_flags),
4812 });
4813
4814 for (slice, 0..) |*shdr, ii| {
4815 if (shdrIsZerofill(shdr)) continue;
4816 off = alignment.@"align"(cut.start + ii, shdr.sh_addralign, off);
4817 // off = mem.alignForward(u64, off, shdr.sh_addralign);
4818 shdr.sh_offset = off;
4819 off += shdr.sh_size;
4820 try self.phdr_to_shdr_table.putNoClobber(gpa, @intCast(ii + cut.start), phndx);
4821 }
4822
4823 addr += self.page_size;
4824 }
4825}
4826
4674fn allocateNonAllocSections(self: *Elf) void {4827fn allocateNonAllocSections(self: *Elf) void {
4675 for (self.shdrs.items) |*shdr| {4828 for (self.shdrs.items) |*shdr| {
4676 if (shdr.sh_type == elf.SHT_NULL) continue;4829 if (shdr.sh_type == elf.SHT_NULL) continue;
...@@ -5817,65 +5970,78 @@ fn reportParseError(...@@ -5817,65 +5970,78 @@ fn reportParseError(
5817 try err.addNote(self, "while parsing {s}", .{path});5970 try err.addNote(self, "while parsing {s}", .{path});
5818}5971}
58195972
5820fn fmtShdrs(self: *Elf) std.fmt.Formatter(formatShdrs) {5973const FormatShdrCtx = struct {
5821 return .{ .data = self };5974 elf_file: *Elf,
5975 shdr: elf.Elf64_Shdr,
5976};
5977
5978fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) {
5979 return .{ .data = .{
5980 .shdr = shdr,
5981 .elf_file = self,
5982 } };
5822}5983}
58235984
5824fn formatShdrs(5985fn formatShdr(
5825 self: *Elf,5986 ctx: FormatShdrCtx,
5826 comptime unused_fmt_string: []const u8,5987 comptime unused_fmt_string: []const u8,
5827 options: std.fmt.FormatOptions,5988 options: std.fmt.FormatOptions,
5828 writer: anytype,5989 writer: anytype,
5829) !void {5990) !void {
5830 _ = options;5991 _ = options;
5831 _ = unused_fmt_string;5992 _ = unused_fmt_string;
5832 for (self.shdrs.items, 0..) |shdr, i| {5993 const shdr = ctx.shdr;
5833 try writer.print("shdr({d}) : phdr({?d}) : {s} : @{x} ({x}) : align({x}) : size({x})\n", .{5994 try writer.print("{s} : @{x} ({x}) : align({x}) : size({x})", .{
5834 i, self.phdr_to_shdr_table.get(@intCast(i)),5995 ctx.elf_file.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset,
5835 self.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset,5996 shdr.sh_addr, shdr.sh_addralign,
5836 shdr.sh_addr, shdr.sh_addralign,5997 shdr.sh_size,
5837 shdr.sh_size,5998 });
5838 });
5839 }
5840}5999}
58416000
5842fn fmtPhdrs(self: *Elf) std.fmt.Formatter(formatPhdrs) {6001const FormatPhdrCtx = struct {
5843 return .{ .data = self };6002 elf_file: *Elf,
6003 phdr: elf.Elf64_Phdr,
6004};
6005
6006fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) {
6007 return .{ .data = .{
6008 .phdr = phdr,
6009 .elf_file = self,
6010 } };
5844}6011}
58456012
5846fn formatPhdrs(6013fn formatPhdr(
5847 self: *Elf,6014 ctx: FormatPhdrCtx,
5848 comptime unused_fmt_string: []const u8,6015 comptime unused_fmt_string: []const u8,
5849 options: std.fmt.FormatOptions,6016 options: std.fmt.FormatOptions,
5850 writer: anytype,6017 writer: anytype,
5851) !void {6018) !void {
5852 _ = options;6019 _ = options;
5853 _ = unused_fmt_string;6020 _ = unused_fmt_string;
5854 for (self.phdrs.items, 0..) |phdr, i| {6021 const phdr = ctx.phdr;
5855 const write = phdr.p_flags & elf.PF_W != 0;6022 const write = phdr.p_flags & elf.PF_W != 0;
5856 const read = phdr.p_flags & elf.PF_R != 0;6023 const read = phdr.p_flags & elf.PF_R != 0;
5857 const exec = phdr.p_flags & elf.PF_X != 0;6024 const exec = phdr.p_flags & elf.PF_X != 0;
5858 var flags: [3]u8 = [_]u8{'_'} ** 3;6025 var flags: [3]u8 = [_]u8{'_'} ** 3;
5859 if (exec) flags[0] = 'X';6026 if (exec) flags[0] = 'X';
5860 if (write) flags[1] = 'W';6027 if (write) flags[1] = 'W';
5861 if (read) flags[2] = 'R';6028 if (read) flags[2] = 'R';
5862 const p_type = switch (phdr.p_type) {6029 const p_type = switch (phdr.p_type) {
5863 elf.PT_LOAD => "LOAD",6030 elf.PT_LOAD => "LOAD",
5864 elf.PT_TLS => "TLS",6031 elf.PT_TLS => "TLS",
5865 elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME",6032 elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME",
5866 elf.PT_GNU_STACK => "GNU_STACK",6033 elf.PT_GNU_STACK => "GNU_STACK",
5867 elf.PT_DYNAMIC => "DYNAMIC",6034 elf.PT_DYNAMIC => "DYNAMIC",
5868 elf.PT_INTERP => "INTERP",6035 elf.PT_INTERP => "INTERP",
5869 elf.PT_NULL => "NULL",6036 elf.PT_NULL => "NULL",
5870 elf.PT_PHDR => "PHDR",6037 elf.PT_PHDR => "PHDR",
5871 elf.PT_NOTE => "NOTE",6038 elf.PT_NOTE => "NOTE",
5872 else => "UNKNOWN",6039 else => "UNKNOWN",
5873 };6040 };
5874 try writer.print("phdr({d}) : {s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})\n", .{6041 try writer.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{
5875 i, p_type, flags, phdr.p_offset, phdr.p_vaddr,6042 p_type, flags, phdr.p_offset, phdr.p_vaddr,
5876 phdr.p_align, phdr.p_filesz, phdr.p_memsz,6043 phdr.p_align, phdr.p_filesz, phdr.p_memsz,
5877 });6044 });
5878 }
5879}6045}
58806046
5881fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {6047fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
...@@ -5928,9 +6094,17 @@ fn fmtDumpState(...@@ -5928,9 +6094,17 @@ fn fmtDumpState(
5928 }6094 }
5929 try writer.print("{}\n", .{self.got.fmt(self)});6095 try writer.print("{}\n", .{self.got.fmt(self)});
5930 try writer.writeAll("Output shdrs\n");6096 try writer.writeAll("Output shdrs\n");
5931 try writer.print("{}\n", .{self.fmtShdrs()});6097 for (self.shdrs.items, 0..) |shdr, shndx| {
6098 try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{
6099 shndx,
6100 self.phdr_to_shdr_table.get(@intCast(shndx)),
6101 self.fmtShdr(shdr),
6102 });
6103 }
5932 try writer.writeAll("Output phdrs\n");6104 try writer.writeAll("Output phdrs\n");
5933 try writer.print("{}\n", .{self.fmtPhdrs()});6105 for (self.phdrs.items, 0..) |phdr, phndx| {
6106 try writer.print("phdr{d} : {}\n", .{ phndx, self.fmtPhdr(phdr) });
6107 }
5934}6108}
59356109
5936/// Binary search6110/// Binary search
test/link/elf.zig+4-4
...@@ -1374,10 +1374,6 @@ fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {...@@ -1374,10 +1374,6 @@ fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
1374 exe.link_function_sections = true;1374 exe.link_function_sections = true;
1375 exe.linkLibC();1375 exe.linkLibC();
13761376
1377 const run = addRunArtifact(exe);
1378 run.expectStdOutEqual("Hello world");
1379 test_step.dependOn(&run.step);
1380
1381 const check = exe.checkObject();1377 const check = exe.checkObject();
1382 check.checkInSymtab();1378 check.checkInSymtab();
1383 check.checkExtract("{addr1} {size1} {shndx1} FUNC LOCAL DEFAULT hello");1379 check.checkExtract("{addr1} {size1} {shndx1} FUNC LOCAL DEFAULT hello");
...@@ -1387,6 +1383,10 @@ fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {...@@ -1387,6 +1383,10 @@ fn testLargeAlignmentExe(b: *Build, opts: Options) *Step {
1387 check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });1383 check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } });
1388 test_step.dependOn(&check.step);1384 test_step.dependOn(&check.step);
13891385
1386 const run = addRunArtifact(exe);
1387 run.expectStdOutEqual("Hello world");
1388 test_step.dependOn(&run.step);
1389
1390 return test_step;1390 return test_step;
1391}1391}
13921392