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,...@@ -47,6 +47,10 @@ phdr_load_zerofill_zig_index: ?u16 = null,
47/// Special program headers47/// Special program headers
48/// PT_PHDR48/// PT_PHDR
49phdr_table_index: ?u16 = null,49phdr_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,
50/// PT_INTERP54/// PT_INTERP
51phdr_interp_index: ?u16 = null,55phdr_interp_index: ?u16 = null,
52/// PT_DYNAMIC56/// PT_DYNAMIC
...@@ -281,6 +285,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -281,6 +285,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
281 .p32 => @alignOf(elf.Elf32_Phdr),285 .p32 => @alignOf(elf.Elf32_Phdr),
282 .p64 => @alignOf(elf.Elf64_Phdr),286 .p64 => @alignOf(elf.Elf64_Phdr),
283 };287 };
288 const image_base = self.calcImageBase();
284 const offset: u64 = switch (self.ptr_width) {289 const offset: u64 = switch (self.ptr_width) {
285 .p32 => @sizeOf(elf.Elf32_Ehdr),290 .p32 => @sizeOf(elf.Elf32_Ehdr),
286 .p64 => @sizeOf(elf.Elf64_Ehdr),291 .p64 => @sizeOf(elf.Elf64_Ehdr),
...@@ -289,9 +294,15 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -289,9 +294,15 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
289 .type = elf.PT_PHDR,294 .type = elf.PT_PHDR,
290 .flags = elf.PF_R,295 .flags = elf.PF_R,
291 .@"align" = p_align,296 .@"align" = p_align,
292 .addr = self.calcImageBase() + offset,297 .addr = image_base + offset,
293 .offset = offset,298 .offset = offset,
294 });299 });
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 });
295 }306 }
296307
297 if (options.module != null and !options.use_llvm) {308 if (options.module != null and !options.use_llvm) {
...@@ -571,34 +582,23 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {...@@ -571,34 +582,23 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
571}582}
572583
573const AllocateSegmentOpts = struct {584const AllocateSegmentOpts = struct {
585 addr: u64,
574 size: u64,586 size: u64,
575 alignment: u64,587 alignment: u64,
576 addr: ?u64 = null,
577 flags: u32 = elf.PF_R,588 flags: u32 = elf.PF_R,
578};589};
579590
580pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 {591pub 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);
584 const off = self.findFreeSpace(opts.size, opts.alignment);592 const off = self.findFreeSpace(opts.size, opts.alignment);
585 // Currently, we automatically allocate memory in sequence by finding the largest593 const index = try self.addPhdr(.{
586 // allocated virtual address and going from there.594 .type = elf.PT_LOAD,
587 // TODO we want to keep machine code segment in the furthest memory range among all595 .offset = off,
588 // segments as it is most likely to grow.596 .filesz = opts.size,
589 const addr = opts.addr orelse blk: {597 .addr = opts.addr,
590 const reserved_capacity = self.calcImageBase() * 4;598 .memsz = opts.size,
591 // Calculate largest VM address599 .@"align" = opts.alignment,
592 var addresses = std.ArrayList(u64).init(gpa);600 .flags = opts.flags,
593 defer addresses.deinit();601 });
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 };
602 log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{602 log.debug("allocating phdr({d})({c}{c}{c}) from 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
603 index,603 index,
604 if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',604 if (opts.flags & elf.PF_R != 0) @as(u8, 'R') else '_',
...@@ -606,18 +606,8 @@ pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}...@@ -606,18 +606,8 @@ pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}
606 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',606 if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_',
607 off,607 off,
608 off + opts.size,608 off + opts.size,
609 addr,609 opts.addr,
610 addr + opts.size,610 opts.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,
621 });611 });
622 return index;612 return index;
623}613}
...@@ -687,11 +677,13 @@ fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{...@@ -687,11 +677,13 @@ fn allocateNonAllocSection(self: *Elf, opts: AllocateNonAllocSectionOpts) error{
687/// TODO move to ZigModule677/// TODO move to ZigModule
688pub fn initMetadata(self: *Elf) !void {678pub fn initMetadata(self: *Elf) !void {
689 const gpa = self.base.allocator;679 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();
691 const is_linux = self.base.options.target.os.tag == .linux;682 const is_linux = self.base.options.target.os.tag == .linux;
692683
693 if (self.phdr_load_re_zig_index == null) {684 if (self.phdr_load_re_zig_index == null) {
694 self.phdr_load_re_zig_index = try self.allocateSegment(.{685 self.phdr_load_re_zig_index = try self.allocateSegment(.{
686 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
695 .size = self.base.options.program_code_size_hint,687 .size = self.base.options.program_code_size_hint,
696 .alignment = self.page_size,688 .alignment = self.page_size,
697 .flags = elf.PF_X | elf.PF_R | elf.PF_W,689 .flags = elf.PF_X | elf.PF_R | elf.PF_W,
...@@ -703,6 +695,7 @@ pub fn initMetadata(self: *Elf) !void {...@@ -703,6 +695,7 @@ pub fn initMetadata(self: *Elf) !void {
703 // page align.695 // page align.
704 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);696 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
705 self.phdr_got_zig_index = try self.allocateSegment(.{697 self.phdr_got_zig_index = try self.allocateSegment(.{
698 .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000,
706 .size = @as(u64, ptr_size) * self.base.options.symbol_count_hint,699 .size = @as(u64, ptr_size) * self.base.options.symbol_count_hint,
707 .alignment = alignment,700 .alignment = alignment,
708 .flags = elf.PF_R | elf.PF_W,701 .flags = elf.PF_R | elf.PF_W,
...@@ -712,6 +705,7 @@ pub fn initMetadata(self: *Elf) !void {...@@ -712,6 +705,7 @@ pub fn initMetadata(self: *Elf) !void {
712 if (self.phdr_load_ro_zig_index == null) {705 if (self.phdr_load_ro_zig_index == null) {
713 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);706 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
714 self.phdr_load_ro_zig_index = try self.allocateSegment(.{707 self.phdr_load_ro_zig_index = try self.allocateSegment(.{
708 .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000,
715 .size = 1024,709 .size = 1024,
716 .alignment = alignment,710 .alignment = alignment,
717 .flags = elf.PF_R | elf.PF_W,711 .flags = elf.PF_R | elf.PF_W,
...@@ -721,6 +715,7 @@ pub fn initMetadata(self: *Elf) !void {...@@ -721,6 +715,7 @@ pub fn initMetadata(self: *Elf) !void {
721 if (self.phdr_load_rw_zig_index == null) {715 if (self.phdr_load_rw_zig_index == null) {
722 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);716 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
723 self.phdr_load_rw_zig_index = try self.allocateSegment(.{717 self.phdr_load_rw_zig_index = try self.allocateSegment(.{
718 .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000,
724 .size = 1024,719 .size = 1024,
725 .alignment = alignment,720 .alignment = alignment,
726 .flags = elf.PF_R | elf.PF_W,721 .flags = elf.PF_R | elf.PF_W,
...@@ -730,6 +725,7 @@ pub fn initMetadata(self: *Elf) !void {...@@ -730,6 +725,7 @@ pub fn initMetadata(self: *Elf) !void {
730 if (self.phdr_load_zerofill_zig_index == null) {725 if (self.phdr_load_zerofill_zig_index == null) {
731 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);726 const alignment = if (is_linux) self.page_size else @as(u16, ptr_size);
732 self.phdr_load_zerofill_zig_index = try self.allocateSegment(.{727 self.phdr_load_zerofill_zig_index = try self.allocateSegment(.{
728 .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000,
733 .size = 0,729 .size = 0,
734 .alignment = alignment,730 .alignment = alignment,
735 .flags = elf.PF_R | elf.PF_W,731 .flags = elf.PF_R | elf.PF_W,
...@@ -2820,10 +2816,18 @@ fn writePhdrTable(self: *Elf) !void {...@@ -2820,10 +2816,18 @@ fn writePhdrTable(self: *Elf) !void {
2820 .p64 => @sizeOf(elf.Elf64_Phdr),2816 .p64 => @sizeOf(elf.Elf64_Phdr),
2821 };2817 };
2822 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];2818 const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
2819 const phdr_segment = &self.phdrs.items[self.phdr_table_load_index.?];
2823 const needed_size = self.phdrs.items.len * phsize;2820 const needed_size = self.phdrs.items.len * phsize;
2824 phdr_table.p_filesz = needed_size;2821 phdr_table.p_filesz = needed_size;
2825 phdr_table.p_memsz = needed_size;2822 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
2827 log.debug("writing program headers from 0x{x} to 0x{x}", .{2831 log.debug("writing program headers from 0x{x} to 0x{x}", .{
2828 phdr_table.p_offset,2832 phdr_table.p_offset,
2829 phdr_table.p_offset + needed_size,2833 phdr_table.p_offset + needed_size,
...@@ -4387,6 +4391,7 @@ fn updateSectionSizes(self: *Elf) !void {...@@ -4387,6 +4391,7 @@ fn updateSectionSizes(self: *Elf) !void {
43874391
4388/// The assumed layout of PHDRs in file is as follows:4392/// The assumed layout of PHDRs in file is as follows:
4389/// PT_PHDR4393/// PT_PHDR
4394/// PT_LOAD for PT_PHDR
4390/// PT_INTERP4395/// PT_INTERP
4391/// PT_DYNAMIC4396/// PT_DYNAMIC
4392/// PT_GNU_EH_FRAME4397/// PT_GNU_EH_FRAME
...@@ -4399,7 +4404,8 @@ fn resetPhdrs(self: *Elf) !void {...@@ -4399,7 +4404,8 @@ fn resetPhdrs(self: *Elf) !void {
4399 try self.phdrs.ensureUnusedCapacity(gpa, phdrs.len);4404 try self.phdrs.ensureUnusedCapacity(gpa, phdrs.len);
44004405
4401 for (&[_]?u16{4406 for (&[_]?u16{
4402 self.phdr_table_index.?,4407 self.phdr_table_index,
4408 self.phdr_table_load_index,
4403 self.phdr_interp_index,4409 self.phdr_interp_index,
4404 self.phdr_dynamic_index,4410 self.phdr_dynamic_index,
4405 self.phdr_gnu_eh_frame_index,4411 self.phdr_gnu_eh_frame_index,
...@@ -4636,9 +4642,17 @@ fn allocateSectionsInFile(self: *Elf, base_offset: u64) void {...@@ -4636,9 +4642,17 @@ fn allocateSectionsInFile(self: *Elf, base_offset: u64) void {
4636}4642}
46374643
4638fn allocateSections(self: *Elf) !void {4644fn 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 };
4639 while (true) {4653 while (true) {
4640 const nphdrs = self.phdrs.items.len;4654 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;
4642 self.allocateSectionsInMemory(base_offset);4656 self.allocateSectionsInMemory(base_offset);
4643 self.allocateSectionsInFile(base_offset);4657 self.allocateSectionsInFile(base_offset);
4644 try self.resetPhdrs();4658 try self.resetPhdrs();
...@@ -4663,14 +4677,6 @@ fn allocateSpecialPhdrs(self: *Elf) void {...@@ -4663,14 +4677,6 @@ fn allocateSpecialPhdrs(self: *Elf) void {
4663 phdr.p_memsz = shdr.sh_size;4677 phdr.p_memsz = shdr.sh_size;
4664 }4678 }
4665 }4679 }
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 }
4674}4680}
46754681
4676fn allocateAtoms(self: *Elf) void {4682fn allocateAtoms(self: *Elf) void {
...@@ -5341,7 +5347,7 @@ fn addPhdr(self: *Elf, opts: struct {...@@ -5341,7 +5347,7 @@ fn addPhdr(self: *Elf, opts: struct {
5341 addr: u64 = 0,5347 addr: u64 = 0,
5342 filesz: u64 = 0,5348 filesz: u64 = 0,
5343 memsz: u64 = 0,5349 memsz: u64 = 0,
5344}) !u16 {5350}) error{OutOfMemory}!u16 {
5345 const index = @as(u16, @intCast(self.phdrs.items.len));5351 const index = @as(u16, @intCast(self.phdrs.items.len));
5346 try self.phdrs.append(self.base.allocator, .{5352 try self.phdrs.append(self.base.allocator, .{
5347 .p_type = opts.type,5353 .p_type = opts.type,