authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-11 09:53:32+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-16 19:33:05+02:00
logbcce035636f14de6b2f39edea094d6eab3a321c9
tree10e570e6f130e634d31bd61347a9e39e1eb5e55c
parent43397bbdc48ec0d5484a0fc2290fc2feddec7905

elf: bring back PT_LOAD for PT_PHDR for incremental codepath


1 files changed, 52 insertions(+), 46 deletions(-)

src/link/Elf.zig+52-46
......@@ -47,6 +47,10 @@ phdr_load_zerofill_zig_index: ?u16 = null,
4747/// Special program headers
4848/// PT_PHDR
4949phdr_table_index: ?u16 = null,
50/// PT_LOAD for PHDR table
51/// We add this special load segment to ensure the PHDR table is always
52/// loaded into memory.
53phdr_table_load_index: ?u16 = null,
5054/// PT_INTERP
5155phdr_interp_index: ?u16 = null,
5256/// PT_DYNAMIC
......@@ -281,6 +285,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
281285 .p32 => @alignOf(elf.Elf32_Phdr),
282286 .p64 => @alignOf(elf.Elf64_Phdr),
283287 };
288 const image_base = self.calcImageBase();
284289 const offset: u64 = switch (self.ptr_width) {
285290 .p32 => @sizeOf(elf.Elf32_Ehdr),
286291 .p64 => @sizeOf(elf.Elf64_Ehdr),
......@@ -289,9 +294,15 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
289294 .type = elf.PT_PHDR,
290295 .flags = elf.PF_R,
291296 .@"align" = p_align,
292 .addr = self.calcImageBase() + offset,
297 .addr = image_base + offset,
293298 .offset = offset,
294299 });
300 self.phdr_table_load_index = try self.addPhdr(.{
301 .type = elf.PT_LOAD,
302 .flags = elf.PF_R,
303 .@"align" = self.page_size,
304 .addr = image_base,
305 });
295306 }
296307
297308 if (options.module != null and !options.use_llvm) {
......@@ -571,34 +582,23 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
571582}
572583
573584const AllocateSegmentOpts = struct {
585 addr: u64,
574586 size: u64,
575587 alignment: u64,
576 addr: ?u64 = null,
577588 flags: u32 = elf.PF_R,
578589};
579590
580591pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {
581 const gpa = self.base.allocator;
582 const index = @as(u16, @intCast(self.phdrs.items.len));
583 try self.phdrs.ensureUnusedCapacity(gpa, 1);
584592 const off = self.findFreeSpace(opts.size, opts.alignment);
585 // Currently, we automatically allocate memory in sequence by finding the largest
586 // allocated virtual address and going from there.
587 // TODO we want to keep machine code segment in the furthest memory range among all
588 // segments as it is most likely to grow.
589 const addr = opts.addr orelse blk: {
590 const reserved_capacity = self.calcImageBase() * 4;
591 // Calculate largest VM address
592 var addresses = std.ArrayList(u64).init(gpa);
593 defer addresses.deinit();
594 try addresses.ensureTotalCapacityPrecise(self.phdrs.items.len);
595 for (self.phdrs.items) |phdr| {
596 if (phdr.p_type != elf.PT_LOAD) continue;
597 addresses.appendAssumeCapacity(phdr.p_vaddr + reserved_capacity);
598 }
599 mem.sort(u64, addresses.items, {}, std.sort.asc(u64));
600 break :blk mem.alignForward(u64, addresses.pop(), opts.alignment);
601 };
593 const index = try self.addPhdr(.{
594 .type = elf.PT_LOAD,
595 .offset = off,
596 .filesz = opts.size,
597 .addr = opts.addr,
598 .memsz = opts.size,
599 .@"align" = opts.alignment,
600 .flags = opts.flags,
601 });
602602 log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
603603 index,
604604 if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',
......@@ -606,18 +606,8 @@ pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}
606606 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',
607607 off,
608608 off + opts.size,
609 addr,
610 addr + opts.size,
611 });
612 self.phdrs.appendAssumeCapacity(.{
613 .p_type = elf.PT_LOAD,
614 .p_offset = off,
615 .p_filesz = opts.size,
616 .p_vaddr = addr,
617 .p_paddr = addr,
618 .p_memsz = opts.size,
619 .p_align = opts.alignment,
620 .p_flags = opts.flags,
609 opts.addr,
610 opts.addr + opts.size,
621611 });
622612 return index;
623613}
......@@ -687,11 +677,13 @@ fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{
687677/// TODO move to ZigModule
688678pub fn initMetadata(self: *Elf) !void {
689679 const gpa = self.base.allocator;
690 const ptr_size: u8 = self.ptrWidthBytes();
680 const ptr_size = self.ptrWidthBytes();
681 const ptr_bit_width = self.base.options.target.ptrBitWidth();
691682 const is_linux = self.base.options.target.os.tag == .linux;
692683
693684 if (self.phdr_load_re_zig_index == null) {
694685 self.phdr_load_re_zig_index = try self.allocateSegment(.{
686 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
695687 .size = self.base.options.program_code_size_hint,
696688 .alignment = self.page_size,
697689 .flags = elf.PF_X | elf.PF_R | elf.PF_W,
......@@ -703,6 +695,7 @@ pub fn initMetadata(self: *Elf) !void {
703695 // page align.
704696 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
705697 self.phdr_got_zig_index = try self.allocateSegment(.{
698 .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
706699 .size = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
707700 .alignment = alignment,
708701 .flags = elf.PF_R | elf.PF_W,
......@@ -712,6 +705,7 @@ pub fn initMetadata(self: *Elf) !void {
712705 if (self.phdr_load_ro_zig_index == null) {
713706 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
714707 self.phdr_load_ro_zig_index = try self.allocateSegment(.{
708 .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
715709 .size = 1024,
716710 .alignment = alignment,
717711 .flags = elf.PF_R | elf.PF_W,
......@@ -721,6 +715,7 @@ pub fn initMetadata(self: *Elf) !void {
721715 if (self.phdr_load_rw_zig_index == null) {
722716 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
723717 self.phdr_load_rw_zig_index = try self.allocateSegment(.{
718 .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
724719 .size = 1024,
725720 .alignment = alignment,
726721 .flags = elf.PF_R | elf.PF_W,
......@@ -730,6 +725,7 @@ pub fn initMetadata(self: *Elf) !void {
730725 if (self.phdr_load_zerofill_zig_index == null) {
731726 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
732727 self.phdr_load_zerofill_zig_index = try self.allocateSegment(.{
728 .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
733729 .size = 0,
734730 .alignment = alignment,
735731 .flags = elf.PF_R | elf.PF_W,
......@@ -2820,10 +2816,18 @@ fn writePhdrTable(self: *Elf) !void {
28202816 .p64 => @sizeOf(elf.Elf64_Phdr),
28212817 };
28222818 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
2819 const phdr_segment = &self.phdrs.items[self.phdr_table_load_index.?];
28232820 const needed_size = self.phdrs.items.len * phsize;
28242821 phdr_table.p_filesz = needed_size;
28252822 phdr_table.p_memsz = needed_size;
28262823
2824 const ehsize: u64 = switch (self.ptr_width) {
2825 .p32 => @sizeOf(elf.Elf32_Ehdr),
2826 .p64 => @sizeOf(elf.Elf64_Ehdr),
2827 };
2828 phdr_segment.p_filesz = needed_size + ehsize;
2829 phdr_segment.p_memsz = needed_size + ehsize;
2830
28272831 log.debug("writing program headers from 0x{x} to 0x{x}", .{
28282832 phdr_table.p_offset,
28292833 phdr_table.p_offset + needed_size,
......@@ -4387,6 +4391,7 @@ fn updateSectionSizes(self: *Elf) !void {
43874391
43884392/// The assumed layout of PHDRs in file is as follows:
43894393/// PT_PHDR
4394/// PT_LOAD for PT_PHDR
43904395/// PT_INTERP
43914396/// PT_DYNAMIC
43924397/// PT_GNU_EH_FRAME
......@@ -4399,7 +4404,8 @@ fn resetPhdrs(self: *Elf) !void {
43994404 try self.phdrs.ensureUnusedCapacity(gpa, phdrs.len);
44004405
44014406 for (&[_]?u16{
4402 self.phdr_table_index.?,
4407 self.phdr_table_index,
4408 self.phdr_table_load_index,
44034409 self.phdr_interp_index,
44044410 self.phdr_dynamic_index,
44054411 self.phdr_gnu_eh_frame_index,
......@@ -4636,9 +4642,17 @@ fn allocateSectionsInFile(self: *Elf, base_offset: u64) void {
46364642}
46374643
46384644fn allocateSections(self: *Elf) !void {
4645 const ehsize: u64 = switch (self.ptr_width) {
4646 .p32 => @sizeOf(elf.Elf32_Ehdr),
4647 .p64 => @sizeOf(elf.Elf64_Ehdr),
4648 };
4649 const phsize: u64 = switch (self.ptr_width) {
4650 .p32 => @sizeOf(elf.Elf32_Phdr),
4651 .p64 => @sizeOf(elf.Elf64_Phdr),
4652 };
46394653 while (true) {
46404654 const nphdrs = self.phdrs.items.len;
4641 const base_offset: u64 = @sizeOf(elf.Elf64_Ehdr) + nphdrs * @sizeOf(elf.Elf64_Phdr);
4655 const base_offset: u64 = ehsize + nphdrs * phsize;
46424656 self.allocateSectionsInMemory(base_offset);
46434657 self.allocateSectionsInFile(base_offset);
46444658 try self.resetPhdrs();
......@@ -4663,14 +4677,6 @@ fn allocateSpecialPhdrs(self: *Elf) void {
46634677 phdr.p_memsz = shdr.sh_size;
46644678 }
46654679 }
4666
4667 {
4668 // Backpatch size of the PHDR phdr
4669 const phdr = &self.phdrs.items[self.phdr_table_index.?];
4670 const size = @sizeOf(elf.Elf64_Phdr) * self.phdrs.items.len;
4671 phdr.p_filesz = size;
4672 phdr.p_memsz = size;
4673 }
46744680}
46754681
46764682fn allocateAtoms(self: *Elf) void {
......@@ -5341,7 +5347,7 @@ fn addPhdr(self: *Elf, opts: struct {
53415347 addr: u64 = 0,
53425348 filesz: u64 = 0,
53435349 memsz: u64 = 0,
5344}) !u16 {
5350}) error{OutOfMemory}!u16 {
53455351 const index = @as(u16, @intCast(self.phdrs.items.len));
53465352 try self.phdrs.append(self.base.allocator, .{
53475353 .p_type = opts.type,