| ... | @@ -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 headers | 47 | /// Special program headers |
| 48 | /// PT_PHDR | 48 | /// PT_PHDR |
| 49 | phdr_table_index: ?u16 = null, | 49 | phdr_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. |
| | 53 | phdr_table_load_index: ?u16 = null, |
| 50 | /// PT_INTERP | 54 | /// PT_INTERP |
| 51 | phdr_interp_index: ?u16 = null, | 55 | phdr_interp_index: ?u16 = null, |
| 52 | /// PT_DYNAMIC | 56 | /// 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 | } |
| 296 | | 307 | |
| 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 | } |
| 572 | | 583 | |
| 573 | const AllocateSegmentOpts = struct { | 584 | const 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 | }; |
| 579 | | 590 | |
| 580 | pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 { | 591 | pub 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 largest | 593 | 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 all | 595 | .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 address | 599 | .@"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 ZigModule | 677 | /// TODO move to ZigModule |
| 688 | pub fn initMetadata(self: *Elf) !void { | 678 | pub 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; |
| 692 | | 683 | |
| 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; |
| 2826 | | 2823 | |
| | 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 { |
| 4387 | | 4391 | |
| 4388 | /// The assumed layout of PHDRs in file is as follows: | 4392 | /// The assumed layout of PHDRs in file is as follows: |
| 4389 | /// PT_PHDR | 4393 | /// PT_PHDR |
| | 4394 | /// PT_LOAD for PT_PHDR |
| 4390 | /// PT_INTERP | 4395 | /// PT_INTERP |
| 4391 | /// PT_DYNAMIC | 4396 | /// PT_DYNAMIC |
| 4392 | /// PT_GNU_EH_FRAME | 4397 | /// 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); |
| 4400 | | 4405 | |
| 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 | } |
| 4637 | | 4643 | |
| 4638 | fn allocateSections(self: *Elf) !void { | 4644 | fn 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 | } |
| 4675 | | 4681 | |
| 4676 | fn allocateAtoms(self: *Elf) void { | 4682 | fn 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, |