authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-24 18:22:58+02:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-25 04:27:44-04:00
log030da45c8e8437d31e7443077f8f2493d334a7d1
tree7a3636129f1cebe1c92a99bc2c66c1027b45c4a1
parente1248b693ff53266d20d6fc88a9c70a13432322f

elf: estimate max number of phdrs that can be emitted


1 files changed, 44 insertions(+), 10 deletions(-)

src/link/Elf.zig+44-10
...@@ -289,17 +289,22 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -289,17 +289,22 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
289 .p64 => @alignOf(elf.Elf64_Phdr),289 .p64 => @alignOf(elf.Elf64_Phdr),
290 };290 };
291 const image_base = self.calcImageBase();291 const image_base = self.calcImageBase();
292 const ehdr_size: u64 = switch (self.ptr_width) {292 const ehsize: u64 = switch (self.ptr_width) {
293 .p32 => @sizeOf(elf.Elf32_Ehdr),293 .p32 => @sizeOf(elf.Elf32_Ehdr),
294 .p64 => @sizeOf(elf.Elf64_Ehdr),294 .p64 => @sizeOf(elf.Elf64_Ehdr),
295 };295 };
296 const reserved: u64 = 2 * self.page_size;296 const phsize: u64 = switch (self.ptr_width) {
297 .p32 => @sizeOf(elf.Elf32_Phdr),
298 .p64 => @sizeOf(elf.Elf64_Phdr),
299 };
300 const max_nphdrs = comptime getMaxNumberOfPhdrs();
301 const reserved: u64 = mem.alignForward(u64, padToIdeal(max_nphdrs * phsize), self.page_size);
297 self.phdr_table_index = try self.addPhdr(.{302 self.phdr_table_index = try self.addPhdr(.{
298 .type = elf.PT_PHDR,303 .type = elf.PT_PHDR,
299 .flags = elf.PF_R,304 .flags = elf.PF_R,
300 .@"align" = p_align,305 .@"align" = p_align,
301 .addr = image_base + ehdr_size,306 .addr = image_base + ehsize,
302 .offset = ehdr_size,307 .offset = ehsize,
303 .filesz = reserved,308 .filesz = reserved,
304 .memsz = reserved,309 .memsz = reserved,
305 });310 });
...@@ -309,8 +314,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -309,8 +314,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
309 .@"align" = self.page_size,314 .@"align" = self.page_size,
310 .addr = image_base,315 .addr = image_base,
311 .offset = 0,316 .offset = 0,
312 .filesz = reserved + ehdr_size,317 .filesz = reserved + ehsize,
313 .memsz = reserved + ehdr_size,318 .memsz = reserved + ehsize,
314 });319 });
315 }320 }
316321
...@@ -714,6 +719,8 @@ pub fn initMetadata(self: *Elf) !void {...@@ -714,6 +719,8 @@ pub fn initMetadata(self: *Elf) !void {
714 const ptr_bit_width = self.base.options.target.ptrBitWidth();719 const ptr_bit_width = self.base.options.target.ptrBitWidth();
715 const is_linux = self.base.options.target.os.tag == .linux;720 const is_linux = self.base.options.target.os.tag == .linux;
716721
722 comptime assert(number_of_zig_segments == 5);
723
717 if (self.phdr_zig_load_re_index == null) {724 if (self.phdr_zig_load_re_index == null) {
718 self.phdr_zig_load_re_index = try self.allocateSegment(.{725 self.phdr_zig_load_re_index = try self.allocateSegment(.{
719 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,726 .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000,
...@@ -4053,6 +4060,8 @@ fn initSections(self: *Elf) !void {...@@ -4053,6 +4060,8 @@ fn initSections(self: *Elf) !void {
4053}4060}
40544061
4055fn initSpecialPhdrs(self: *Elf) !void {4062fn initSpecialPhdrs(self: *Elf) !void {
4063 comptime assert(max_number_of_special_phdrs == 5);
4064
4056 if (self.interp_section_index != null) {4065 if (self.interp_section_index != null) {
4057 self.phdr_interp_index = try self.addPhdr(.{4066 self.phdr_interp_index = try self.addPhdr(.{
4058 .type = elf.PT_INTERP,4067 .type = elf.PT_INTERP,
...@@ -4641,6 +4650,21 @@ fn shdrToPhdrFlags(sh_flags: u64) u32 {...@@ -4641,6 +4650,21 @@ fn shdrToPhdrFlags(sh_flags: u64) u32 {
4641 return out_flags;4650 return out_flags;
4642}4651}
46434652
4653/// Returns maximum number of program headers that may be emitted by the linker.
4654/// (This is an upper bound so that we can reserve enough space for the header and progam header
4655/// table without running out of space and being forced to move things around.)
4656fn getMaxNumberOfPhdrs() u64 {
4657 // First, assume we compile Zig's source incrementally, this gives us:
4658 var num: u64 = number_of_zig_segments;
4659 // Next, the estimated maximum number of segments the linker can emit for input sections are:
4660 num += max_number_of_object_segments;
4661 // Next, any other non-loadable program headers, including TLS, DYNAMIC, GNU_STACK, GNU_EH_FRAME, INTERP:
4662 num += max_number_of_special_phdrs;
4663 // Finally, PHDR program header and corresponding read-only load segment:
4664 num += 2;
4665 return num;
4666}
4667
4644/// Calculates how many segments (PT_LOAD progam headers) are required4668/// Calculates how many segments (PT_LOAD progam headers) are required
4645/// to cover the set of sections.4669/// to cover the set of sections.
4646/// We permit a maximum of 3**2 number of segments.4670/// We permit a maximum of 3**2 number of segments.
...@@ -4677,11 +4701,12 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {...@@ -4677,11 +4701,12 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
4677 const needed_size = (self.phdrs.items.len + new_load_segments) * phsize;4701 const needed_size = (self.phdrs.items.len + new_load_segments) * phsize;
4678 const available_space = self.allocatedSize(phdr_table.p_offset);4702 const available_space = self.allocatedSize(phdr_table.p_offset);
46794703
4680 if (needed_size > self.allocatedSize(phdr_table.p_offset)) {4704 if (needed_size > available_space) {
4681 // TODO in this case, we have two options:4705 // In this case, we have two options:
4682 // 1. increase the available padding for EHDR + PHDR table so that we don't overflow it4706 // 1. increase the available padding for EHDR + PHDR table so that we don't overflow it
4683 // (I think we are in good position to estimate required size without running out of space)4707 // (revisit getMaxNumberOfPhdrs())
4684 // 2. shift everything in file to free more space for EHDR + PHDR table4708 // 2. shift everything in file to free more space for EHDR + PHDR table
4709 // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
4685 var err = try self.addErrorWithNotes(1);4710 var err = try self.addErrorWithNotes(1);
4686 try err.addMsg(self, "fatal linker error: not enough space reserved for EHDR and PHDR table", .{});4711 try err.addMsg(self, "fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
4687 try err.addNote(self, "required 0x{x}, available 0x{x}", .{ needed_size, available_space });4712 try err.addNote(self, "required 0x{x}, available 0x{x}", .{ needed_size, available_space });
...@@ -4734,7 +4759,7 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {...@@ -4734,7 +4759,7 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
4734 // with `findFreeSpace` mechanics than anything else.4759 // with `findFreeSpace` mechanics than anything else.
4735 const Cover = std.ArrayList(u16);4760 const Cover = std.ArrayList(u16);
4736 const gpa = self.base.allocator;4761 const gpa = self.base.allocator;
4737 var covers: [9]Cover = undefined;4762 var covers: [max_number_of_object_segments]Cover = undefined;
4738 for (&covers) |*cover| {4763 for (&covers) |*cover| {
4739 cover.* = Cover.init(gpa);4764 cover.* = Cover.init(gpa);
4740 }4765 }
...@@ -6265,6 +6290,15 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp...@@ -6265,6 +6290,15 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp
6265 return i;6290 return i;
6266}6291}
62676292
6293/// The following three values are only observed at compile-time and used to emit a compile error
6294/// to remind the programmer to update expected maximum numbers of different program header types
6295/// so that we reserve enough space for the program header table up-front.
6296/// Bump these numbers when adding or deleting a Zig specific pre-allocated segment, or adding
6297/// more special-purpose program headers.
6298const number_of_zig_segments = 5;
6299const max_number_of_object_segments = 9;
6300const max_number_of_special_phdrs = 5;
6301
6268const default_entry_addr = 0x8000000;6302const default_entry_addr = 0x8000000;
62696303
6270pub const base_tag: link.File.Tag = .elf;6304pub const base_tag: link.File.Tag = .elf;