| ... | @@ -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 | } |
| 316 | | 321 | |
| ... | @@ -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; |
| 716 | | 721 | |
| | 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 | } |
| 4054 | | 4061 | |
| 4055 | fn initSpecialPhdrs(self: *Elf) !void { | 4062 | fn 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 | } |
| 4643 | | 4652 | |
| | 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.) |
| | 4656 | fn 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 required | 4668 | /// 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); |
| 4679 | | 4703 | |
| 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 it | 4706 | // 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 table | 4708 | // 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 | } |
| 6267 | | 6292 | |
| | 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. |
| | 6298 | const number_of_zig_segments = 5; |
| | 6299 | const max_number_of_object_segments = 9; |
| | 6300 | const max_number_of_special_phdrs = 5; |
| | 6301 | |
| 6268 | const default_entry_addr = 0x8000000; | 6302 | const default_entry_addr = 0x8000000; |
| 6269 | | 6303 | |
| 6270 | pub const base_tag: link.File.Tag = .elf; | 6304 | pub const base_tag: link.File.Tag = .elf; |