| ... | @@ -542,21 +542,23 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | ... | @@ -542,21 +542,23 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 542 | } | 542 | } |
| 543 | | 543 | |
| 544 | for (self.shdrs.items) |shdr| { | 544 | for (self.shdrs.items) |shdr| { |
| 545 | // SHT_NOBITS takes no physical space in the output file so set its size to 0. | 545 | if (shdr.sh_type == elf.SHT_NOBITS) continue; |
| 546 | const sh_size = if (shdr.sh_type == elf.SHT_NOBITS) 0 else shdr.sh_size; | 546 | const increased_size = padToIdeal(shdr.sh_size); |
| 547 | const increased_size = padToIdeal(sh_size); | | |
| 548 | const test_end = shdr.sh_offset + increased_size; | 547 | const test_end = shdr.sh_offset + increased_size; |
| 549 | if (end > shdr.sh_offset and start < test_end) { | 548 | if (end > shdr.sh_offset and start < test_end) { |
| 550 | return test_end; | 549 | return test_end; |
| 551 | } | 550 | } |
| 552 | } | 551 | } |
| | 552 | |
| 553 | for (self.phdrs.items) |phdr| { | 553 | for (self.phdrs.items) |phdr| { |
| | 554 | if (phdr.p_type != elf.PT_LOAD) continue; |
| 554 | const increased_size = padToIdeal(phdr.p_filesz); | 555 | const increased_size = padToIdeal(phdr.p_filesz); |
| 555 | const test_end = phdr.p_offset + increased_size; | 556 | const test_end = phdr.p_offset + increased_size; |
| 556 | if (end > phdr.p_offset and start < test_end) { | 557 | if (end > phdr.p_offset and start < test_end) { |
| 557 | return test_end; | 558 | return test_end; |
| 558 | } | 559 | } |
| 559 | } | 560 | } |
| | 561 | |
| 560 | return null; | 562 | return null; |
| 561 | } | 563 | } |
| 562 | | 564 | |
| ... | @@ -597,19 +599,20 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { | ... | @@ -597,19 +599,20 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { |
| 597 | | 599 | |
| 598 | const AllocateSegmentOpts = struct { | 600 | const AllocateSegmentOpts = struct { |
| 599 | addr: u64, | 601 | addr: u64, |
| 600 | size: u64, | 602 | memsz: u64, |
| | 603 | filesz: u64, |
| 601 | alignment: u64, | 604 | alignment: u64, |
| 602 | flags: u32 = elf.PF_R, | 605 | flags: u32 = elf.PF_R, |
| 603 | }; | 606 | }; |
| 604 | | 607 | |
| 605 | pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 { | 608 | pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory}!u16 { |
| 606 | const off = self.findFreeSpace(opts.size, opts.alignment); | 609 | const off = self.findFreeSpace(opts.filesz, opts.alignment); |
| 607 | const index = try self.addPhdr(.{ | 610 | const index = try self.addPhdr(.{ |
| 608 | .type = elf.PT_LOAD, | 611 | .type = elf.PT_LOAD, |
| 609 | .offset = off, | 612 | .offset = off, |
| 610 | .filesz = opts.size, | 613 | .filesz = opts.filesz, |
| 611 | .addr = opts.addr, | 614 | .addr = opts.addr, |
| 612 | .memsz = opts.size, | 615 | .memsz = opts.memsz, |
| 613 | .@"align" = opts.alignment, | 616 | .@"align" = opts.alignment, |
| 614 | .flags = opts.flags, | 617 | .flags = opts.flags, |
| 615 | }); | 618 | }); |
| ... | @@ -619,9 +622,9 @@ pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory} | ... | @@ -619,9 +622,9 @@ pub fn allocateSegment(self: *Elf, opts: AllocateSegmentOpts) error{OutOfMemory} |
| 619 | if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_', | 622 | if (opts.flags & elf.PF_W != 0) @as(u8, 'W') else '_', |
| 620 | if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_', | 623 | if (opts.flags & elf.PF_X != 0) @as(u8, 'X') else '_', |
| 621 | off, | 624 | off, |
| 622 | off + opts.size, | 625 | off + opts.filesz, |
| 623 | opts.addr, | 626 | opts.addr, |
| 624 | opts.addr + opts.size, | 627 | opts.addr + opts.memsz, |
| 625 | }); | 628 | }); |
| 626 | return index; | 629 | return index; |
| 627 | } | 630 | } |
| ... | @@ -698,7 +701,8 @@ pub fn initMetadata(self: *Elf) !void { | ... | @@ -698,7 +701,8 @@ pub fn initMetadata(self: *Elf) !void { |
| 698 | if (self.phdr_load_re_zig_index == null) { | 701 | if (self.phdr_load_re_zig_index == null) { |
| 699 | self.phdr_load_re_zig_index = try self.allocateSegment(.{ | 702 | self.phdr_load_re_zig_index = try self.allocateSegment(.{ |
| 700 | .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000, | 703 | .addr = if (ptr_bit_width >= 32) 0x8000000 else 0x8000, |
| 701 | .size = self.base.options.program_code_size_hint, | 704 | .memsz = self.base.options.program_code_size_hint, |
| | 705 | .filesz = self.base.options.program_code_size_hint, |
| 702 | .alignment = self.page_size, | 706 | .alignment = self.page_size, |
| 703 | .flags = elf.PF_X | elf.PF_R | elf.PF_W, | 707 | .flags = elf.PF_X | elf.PF_R | elf.PF_W, |
| 704 | }); | 708 | }); |
| ... | @@ -710,7 +714,8 @@ pub fn initMetadata(self: *Elf) !void { | ... | @@ -710,7 +714,8 @@ pub fn initMetadata(self: *Elf) !void { |
| 710 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); | 714 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); |
| 711 | self.phdr_got_zig_index = try self.allocateSegment(.{ | 715 | self.phdr_got_zig_index = try self.allocateSegment(.{ |
| 712 | .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000, | 716 | .addr = if (ptr_bit_width >= 32) 0x4000000 else 0x4000, |
| 713 | .size = @as(u64, ptr_size) * self.base.options.symbol_count_hint, | 717 | .memsz = @as(u64, ptr_size) * self.base.options.symbol_count_hint, |
| | 718 | .filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint, |
| 714 | .alignment = alignment, | 719 | .alignment = alignment, |
| 715 | .flags = elf.PF_R | elf.PF_W, | 720 | .flags = elf.PF_R | elf.PF_W, |
| 716 | }); | 721 | }); |
| ... | @@ -720,7 +725,8 @@ pub fn initMetadata(self: *Elf) !void { | ... | @@ -720,7 +725,8 @@ pub fn initMetadata(self: *Elf) !void { |
| 720 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); | 725 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); |
| 721 | self.phdr_load_ro_zig_index = try self.allocateSegment(.{ | 726 | self.phdr_load_ro_zig_index = try self.allocateSegment(.{ |
| 722 | .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000, | 727 | .addr = if (ptr_bit_width >= 32) 0xc000000 else 0xa000, |
| 723 | .size = 1024, | 728 | .memsz = 1024, |
| | 729 | .filesz = 1024, |
| 724 | .alignment = alignment, | 730 | .alignment = alignment, |
| 725 | .flags = elf.PF_R | elf.PF_W, | 731 | .flags = elf.PF_R | elf.PF_W, |
| 726 | }); | 732 | }); |
| ... | @@ -730,7 +736,8 @@ pub fn initMetadata(self: *Elf) !void { | ... | @@ -730,7 +736,8 @@ pub fn initMetadata(self: *Elf) !void { |
| 730 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); | 736 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); |
| 731 | self.phdr_load_rw_zig_index = try self.allocateSegment(.{ | 737 | self.phdr_load_rw_zig_index = try self.allocateSegment(.{ |
| 732 | .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000, | 738 | .addr = if (ptr_bit_width >= 32) 0x10000000 else 0xc000, |
| 733 | .size = 1024, | 739 | .memsz = 1024, |
| | 740 | .filesz = 1024, |
| 734 | .alignment = alignment, | 741 | .alignment = alignment, |
| 735 | .flags = elf.PF_R | elf.PF_W, | 742 | .flags = elf.PF_R | elf.PF_W, |
| 736 | }); | 743 | }); |
| ... | @@ -740,7 +747,8 @@ pub fn initMetadata(self: *Elf) !void { | ... | @@ -740,7 +747,8 @@ pub fn initMetadata(self: *Elf) !void { |
| 740 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); | 747 | const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); |
| 741 | self.phdr_load_zerofill_zig_index = try self.allocateSegment(.{ | 748 | self.phdr_load_zerofill_zig_index = try self.allocateSegment(.{ |
| 742 | .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000, | 749 | .addr = if (ptr_bit_width >= 32) 0x14000000 else 0xf000, |
| 743 | .size = 0, | 750 | .memsz = 0, |
| | 751 | .filesz = 0, |
| 744 | .alignment = alignment, | 752 | .alignment = alignment, |
| 745 | .flags = elf.PF_R | elf.PF_W, | 753 | .flags = elf.PF_R | elf.PF_W, |
| 746 | }); | 754 | }); |
| ... | @@ -1602,7 +1610,35 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1602,7 +1610,35 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1602 | try self.setVersionSymtab(); | 1610 | try self.setVersionSymtab(); |
| 1603 | try self.updateSectionSizes(); | 1611 | try self.updateSectionSizes(); |
| 1604 | | 1612 | |
| 1605 | try self.initAndAllocateSegments(); | 1613 | // Allocate PHDR table. |
| | 1614 | { |
| | 1615 | const new_load_segments = self.calcNumberOfSegments(); |
| | 1616 | const phdr_table = &self.phdrs.items[self.phdr_table_index.?]; |
| | 1617 | const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?]; |
| | 1618 | |
| | 1619 | const phsize: u64 = switch (self.ptr_width) { |
| | 1620 | .p32 => @sizeOf(elf.Elf32_Phdr), |
| | 1621 | .p64 => @sizeOf(elf.Elf64_Phdr), |
| | 1622 | }; |
| | 1623 | const needed_size = (self.phdrs.items.len + new_load_segments) * phsize; |
| | 1624 | |
| | 1625 | if (needed_size > self.allocatedSize(phdr_table.p_offset)) { |
| | 1626 | phdr_table.p_offset = 0; |
| | 1627 | phdr_table.p_offset = self.findFreeSpace(needed_size, phdr_table.p_align); |
| | 1628 | } |
| | 1629 | |
| | 1630 | phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align); |
| | 1631 | const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset; |
| | 1632 | phdr_table_load.p_filesz = load_align_offset + needed_size; |
| | 1633 | phdr_table_load.p_memsz = load_align_offset + needed_size; |
| | 1634 | |
| | 1635 | phdr_table.p_filesz = needed_size; |
| | 1636 | phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset; |
| | 1637 | phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset; |
| | 1638 | phdr_table.p_memsz = needed_size; |
| | 1639 | } |
| | 1640 | |
| | 1641 | try self.allocateAllocSections(); |
| 1606 | self.allocateNonAllocSections(); | 1642 | self.allocateNonAllocSections(); |
| 1607 | self.allocateSpecialPhdrs(); | 1643 | self.allocateSpecialPhdrs(); |
| 1608 | self.allocateAtoms(); | 1644 | self.allocateAtoms(); |
| ... | @@ -2832,26 +2868,11 @@ fn writePhdrTable(self: *Elf) !void { | ... | @@ -2832,26 +2868,11 @@ fn writePhdrTable(self: *Elf) !void { |
| 2832 | const gpa = self.base.allocator; | 2868 | const gpa = self.base.allocator; |
| 2833 | const target_endian = self.base.options.target.cpu.arch.endian(); | 2869 | const target_endian = self.base.options.target.cpu.arch.endian(); |
| 2834 | const foreign_endian = target_endian != builtin.cpu.arch.endian(); | 2870 | const foreign_endian = target_endian != builtin.cpu.arch.endian(); |
| 2835 | const phsize: u64 = switch (self.ptr_width) { | | |
| 2836 | .p32 => @sizeOf(elf.Elf32_Phdr), | | |
| 2837 | .p64 => @sizeOf(elf.Elf64_Phdr), | | |
| 2838 | }; | | |
| 2839 | const phdr_table = &self.phdrs.items[self.phdr_table_index.?]; | 2871 | const phdr_table = &self.phdrs.items[self.phdr_table_index.?]; |
| 2840 | const phdr_segment = &self.phdrs.items[self.phdr_table_load_index.?]; | | |
| 2841 | const needed_size = self.phdrs.items.len * phsize; | | |
| 2842 | phdr_table.p_filesz = needed_size; | | |
| 2843 | phdr_table.p_memsz = needed_size; | | |
| 2844 | | | |
| 2845 | const ehsize: u64 = switch (self.ptr_width) { | | |
| 2846 | .p32 => @sizeOf(elf.Elf32_Ehdr), | | |
| 2847 | .p64 => @sizeOf(elf.Elf64_Ehdr), | | |
| 2848 | }; | | |
| 2849 | phdr_segment.p_filesz = needed_size + ehsize; | | |
| 2850 | phdr_segment.p_memsz = needed_size + ehsize; | | |
| 2851 | | 2872 | |
| 2852 | log.debug("writing program headers from 0x{x} to 0x{x}", .{ | 2873 | log.debug("writing program headers from 0x{x} to 0x{x}", .{ |
| 2853 | phdr_table.p_offset, | 2874 | phdr_table.p_offset, |
| 2854 | phdr_table.p_offset + needed_size, | 2875 | phdr_table.p_offset + phdr_table.p_filesz, |
| 2855 | }); | 2876 | }); |
| 2856 | | 2877 | |
| 2857 | switch (self.ptr_width) { | 2878 | switch (self.ptr_width) { |
| ... | @@ -4454,7 +4475,6 @@ fn initSegments(self: *Elf) !void { | ... | @@ -4454,7 +4475,6 @@ fn initSegments(self: *Elf) !void { |
| 4454 | // Add LOAD phdrs | 4475 | // Add LOAD phdrs |
| 4455 | const gpa = self.base.allocator; | 4476 | const gpa = self.base.allocator; |
| 4456 | const slice = self.shdrs.items; | 4477 | const slice = self.shdrs.items; |
| 4457 | var is_first = true; | | |
| 4458 | var shndx: u16 = 0; | 4478 | var shndx: u16 = 0; |
| 4459 | while (shndx < slice.len) { | 4479 | while (shndx < slice.len) { |
| 4460 | const shdr = &slice[shndx]; | 4480 | const shdr = &slice[shndx]; |
| ... | @@ -4466,10 +4486,9 @@ fn initSegments(self: *Elf) !void { | ... | @@ -4466,10 +4486,9 @@ fn initSegments(self: *Elf) !void { |
| 4466 | .type = elf.PT_LOAD, | 4486 | .type = elf.PT_LOAD, |
| 4467 | .flags = shdrToPhdrFlags(shdr.sh_flags), | 4487 | .flags = shdrToPhdrFlags(shdr.sh_flags), |
| 4468 | .@"align" = @max(self.page_size, shdr.sh_addralign), | 4488 | .@"align" = @max(self.page_size, shdr.sh_addralign), |
| 4469 | .offset = if (is_first) 0 else shdr.sh_offset, | 4489 | .offset = shdr.sh_offset, |
| 4470 | .addr = if (is_first) self.calcImageBase() else shdr.sh_addr, | 4490 | .addr = shdr.sh_addr, |
| 4471 | }); | 4491 | }); |
| 4472 | is_first = false; | | |
| 4473 | const p_flags = self.phdrs.items[phndx].p_flags; | 4492 | const p_flags = self.phdrs.items[phndx].p_flags; |
| 4474 | self.addShdrToPhdr(shndx, phndx); | 4493 | self.addShdrToPhdr(shndx, phndx); |
| 4475 | try self.phdr_to_shdr_table.putNoClobber(gpa, shndx, phndx); | 4494 | try self.phdr_to_shdr_table.putNoClobber(gpa, shndx, phndx); |
| ... | @@ -4495,7 +4514,7 @@ fn addShdrToPhdr(self: *Elf, shdr_index: u16, phdr_index: u16) void { | ... | @@ -4495,7 +4514,7 @@ fn addShdrToPhdr(self: *Elf, shdr_index: u16, phdr_index: u16) void { |
| 4495 | const phdr = &self.phdrs.items[phdr_index]; | 4514 | const phdr = &self.phdrs.items[phdr_index]; |
| 4496 | phdr.p_align = @max(phdr.p_align, shdr.sh_addralign); | 4515 | phdr.p_align = @max(phdr.p_align, shdr.sh_addralign); |
| 4497 | if (shdr.sh_type != elf.SHT_NOBITS) { | 4516 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| 4498 | phdr.p_filesz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr; | 4517 | phdr.p_filesz = shdr.sh_offset + shdr.sh_size - phdr.p_offset; |
| 4499 | } | 4518 | } |
| 4500 | phdr.p_memsz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr; | 4519 | phdr.p_memsz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr; |
| 4501 | } | 4520 | } |
| ... | @@ -4529,6 +4548,18 @@ pub inline fn shdrIsTls(shdr: *const elf.Elf64_Shdr) bool { | ... | @@ -4529,6 +4548,18 @@ pub inline fn shdrIsTls(shdr: *const elf.Elf64_Shdr) bool { |
| 4529 | return shdr.sh_flags & elf.SHF_TLS != 0; | 4548 | return shdr.sh_flags & elf.SHF_TLS != 0; |
| 4530 | } | 4549 | } |
| 4531 | | 4550 | |
| | 4551 | fn calcNumberOfSegments(self: *Elf) usize { |
| | 4552 | var count: usize = 0; |
| | 4553 | var flags: u64 = 0; |
| | 4554 | for (self.shdrs.items) |shdr| { |
| | 4555 | if (shdr.sh_type == elf.SHT_NULL) continue; |
| | 4556 | if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; |
| | 4557 | if (flags != shdrToPhdrFlags(shdr.sh_flags)) count += 1; |
| | 4558 | flags = shdrToPhdrFlags(shdr.sh_flags); |
| | 4559 | } |
| | 4560 | return count; |
| | 4561 | } |
| | 4562 | |
| 4532 | fn allocateAllocSectionsInMemory(self: *Elf, base_addr: u64) void { | 4563 | fn allocateAllocSectionsInMemory(self: *Elf, base_addr: u64) void { |
| 4533 | // We use this struct to track maximum alignment of all TLS sections. | 4564 | // We use this struct to track maximum alignment of all TLS sections. |
| 4534 | // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold, | 4565 | // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold, |
| ... | @@ -4649,28 +4680,150 @@ fn allocateAllocSectionsInFile(self: *Elf, base_offset: u64) void { | ... | @@ -4649,28 +4680,150 @@ fn allocateAllocSectionsInFile(self: *Elf, base_offset: u64) void { |
| 4649 | /// As a base address we take space immediately after the PHDR table, and | 4680 | /// As a base address we take space immediately after the PHDR table, and |
| 4650 | /// aim for fitting between it and where the first incremental segment is | 4681 | /// aim for fitting between it and where the first incremental segment is |
| 4651 | /// allocated (see `initMetadata`). | 4682 | /// allocated (see `initMetadata`). |
| 4652 | fn initAndAllocateSegments(self: *Elf) !void { | 4683 | fn initAndAllocateSegments(self: *Elf, base_addr: u64, base_offset: u64) !void { |
| 4653 | const ehsize: u64 = switch (self.ptr_width) { | | |
| 4654 | .p32 => @sizeOf(elf.Elf32_Ehdr), | | |
| 4655 | .p64 => @sizeOf(elf.Elf64_Ehdr), | | |
| 4656 | }; | | |
| 4657 | const phsize: u64 = switch (self.ptr_width) { | | |
| 4658 | .p32 => @sizeOf(elf.Elf32_Phdr), | | |
| 4659 | .p64 => @sizeOf(elf.Elf64_Phdr), | | |
| 4660 | }; | | |
| 4661 | const image_base = self.calcImageBase(); | | |
| 4662 | while (true) { | 4684 | while (true) { |
| 4663 | const nphdrs = self.phdrs.items.len; | 4685 | const nphdrs = self.phdrs.items.len; |
| 4664 | const off = ehsize + nphdrs * phsize; | 4686 | self.allocateAllocSectionsInMemory(base_addr); |
| 4665 | const addr = image_base + off; | 4687 | self.allocateAllocSectionsInFile(base_offset); |
| 4666 | self.allocateAllocSectionsInMemory(addr); | | |
| 4667 | self.allocateAllocSectionsInFile(off); | | |
| 4668 | try self.resetPhdrs(); | 4688 | try self.resetPhdrs(); |
| 4669 | try self.initSegments(); | 4689 | try self.initSegments(); |
| 4670 | if (nphdrs == self.phdrs.items.len) break; | 4690 | if (nphdrs == self.phdrs.items.len) break; |
| 4671 | } | 4691 | } |
| 4672 | } | 4692 | } |
| 4673 | | 4693 | |
| | 4694 | fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void { |
| | 4695 | // We use this struct to track maximum alignment of all TLS sections. |
| | 4696 | // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold, |
| | 4697 | // in-file offsets have to be aligned against the start of TLS program header. |
| | 4698 | // If that's not ensured, then in a multi-threaded context, TLS variables across a shared object |
| | 4699 | // boundary may not get correctly loaded at an aligned address. |
| | 4700 | const Align = struct { |
| | 4701 | tls_start_align: u64 = 1, |
| | 4702 | first_tls_index: ?usize = null, |
| | 4703 | |
| | 4704 | fn isFirstTlsShdr(this: @This(), other: usize) bool { |
| | 4705 | if (this.first_tls_index) |index| return index == other; |
| | 4706 | return false; |
| | 4707 | } |
| | 4708 | |
| | 4709 | fn @"align"(this: @This(), index: usize, sh_addralign: u64, addr: u64) u64 { |
| | 4710 | const alignment = if (this.isFirstTlsShdr(index)) this.tls_start_align else sh_addralign; |
| | 4711 | return mem.alignForward(u64, addr, alignment); |
| | 4712 | } |
| | 4713 | }; |
| | 4714 | |
| | 4715 | var alignment = Align{}; |
| | 4716 | for (self.shdrs.items, 0..) |*shdr, i| { |
| | 4717 | if (shdr.sh_type == elf.SHT_NULL) continue; |
| | 4718 | if (!shdrIsTls(shdr)) continue; |
| | 4719 | if (alignment.first_tls_index == null) alignment.first_tls_index = i; |
| | 4720 | alignment.tls_start_align = @max(alignment.tls_start_align, shdr.sh_addralign); |
| | 4721 | } |
| | 4722 | |
| | 4723 | const gpa = self.base.allocator; |
| | 4724 | const nphdrs = self.calcNumberOfSegments(); |
| | 4725 | var cuts = try gpa.alloc(struct { start: u16, len: u16 }, nphdrs); |
| | 4726 | defer gpa.free(cuts); |
| | 4727 | |
| | 4728 | var shndx = for (self.shdrs.items, 0..) |shdr, in| { |
| | 4729 | if (shdr.sh_type == elf.SHT_NULL) continue; |
| | 4730 | if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; |
| | 4731 | break @as(u16, @intCast(in)); |
| | 4732 | } else @as(u16, @intCast(self.shdrs.items.len)); |
| | 4733 | |
| | 4734 | for (cuts) |*cut| { |
| | 4735 | cut.* = .{ .start = shndx, .len = 0 }; |
| | 4736 | var flags = shdrToPhdrFlags(self.shdrs.items[shndx].sh_flags); |
| | 4737 | |
| | 4738 | while (shndx < self.shdrs.items.len) : (shndx += 1) { |
| | 4739 | const shdr = &self.shdrs.items[shndx]; |
| | 4740 | if (!shdrIsAlloc(shdr)) break; |
| | 4741 | if (shdrToPhdrFlags(shdr.sh_flags) != flags) { |
| | 4742 | cut.len = shndx - cut.start; |
| | 4743 | break; |
| | 4744 | } |
| | 4745 | } |
| | 4746 | } |
| | 4747 | cuts[nphdrs - 1].len = shndx - cuts[nphdrs - 1].start; |
| | 4748 | |
| | 4749 | // Allocate in segments |
| | 4750 | const phdr_table = &self.phdrs.items[self.phdr_table_load_index.?]; |
| | 4751 | var addr = phdr_table.p_vaddr + phdr_table.p_memsz; |
| | 4752 | |
| | 4753 | for (cuts) |cut| { |
| | 4754 | const slice = self.shdrs.items[cut.start..][0..cut.len]; |
| | 4755 | |
| | 4756 | var @"align": u64 = self.page_size; |
| | 4757 | for (slice) |*shdr| { |
| | 4758 | if (shdrIsTbss(shdr)) continue; |
| | 4759 | @"align" = @max(@"align", shdr.sh_addralign); |
| | 4760 | } |
| | 4761 | |
| | 4762 | addr = mem.alignForward(u64, addr, @"align"); |
| | 4763 | |
| | 4764 | var memsz: u64 = 0; |
| | 4765 | var filesz: u64 = 0; |
| | 4766 | var i: usize = 0; |
| | 4767 | while (i < cut.len) : (i += 1) { |
| | 4768 | const shdr = &slice[i]; |
| | 4769 | if (shdrIsTbss(shdr)) { |
| | 4770 | // .tbss is a little special as it's used only by the loader meaning it doesn't |
| | 4771 | // need to be actually mmap'ed at runtime. We still need to correctly increment |
| | 4772 | // the addresses of every TLS zerofill section tho. Thus, we hack it so that |
| | 4773 | // we increment the start address like normal, however, after we are done, |
| | 4774 | // the next ALLOC section will get its start address allocated within the same |
| | 4775 | // range as the .tbss sections. We will get something like this: |
| | 4776 | // |
| | 4777 | // ... |
| | 4778 | // .tbss 0x10 |
| | 4779 | // .tcommon 0x20 |
| | 4780 | // .data 0x10 |
| | 4781 | // ... |
| | 4782 | var tbss_addr = addr; |
| | 4783 | while (i < cut.len and shdrIsTbss(&slice[i])) : (i += 1) { |
| | 4784 | const tbss_shdr = &slice[i]; |
| | 4785 | tbss_addr = alignment.@"align"(cut.start + i, tbss_shdr.sh_addralign, tbss_addr); |
| | 4786 | tbss_shdr.sh_addr = tbss_addr; |
| | 4787 | tbss_addr += tbss_shdr.sh_size; |
| | 4788 | } |
| | 4789 | i -= 1; |
| | 4790 | continue; |
| | 4791 | } |
| | 4792 | const next = alignment.@"align"(cut.start + i, shdr.sh_addralign, addr); |
| | 4793 | const padding = next - addr; |
| | 4794 | addr = next; |
| | 4795 | shdr.sh_addr = addr; |
| | 4796 | if (shdr.sh_type != elf.SHT_NOBITS) { |
| | 4797 | filesz += padding + shdr.sh_size; |
| | 4798 | } |
| | 4799 | memsz += padding + shdr.sh_size; |
| | 4800 | addr += shdr.sh_size; |
| | 4801 | } |
| | 4802 | |
| | 4803 | var off = self.findFreeSpace(filesz, @"align"); |
| | 4804 | const phndx = try self.addPhdr(.{ |
| | 4805 | .type = elf.PT_LOAD, |
| | 4806 | .offset = off, |
| | 4807 | .addr = slice[0].sh_addr, |
| | 4808 | .memsz = memsz, |
| | 4809 | .filesz = filesz, |
| | 4810 | .@"align" = @"align", |
| | 4811 | .flags = shdrToPhdrFlags(slice[0].sh_flags), |
| | 4812 | }); |
| | 4813 | |
| | 4814 | for (slice, 0..) |*shdr, ii| { |
| | 4815 | if (shdrIsZerofill(shdr)) continue; |
| | 4816 | off = alignment.@"align"(cut.start + ii, shdr.sh_addralign, off); |
| | 4817 | // off = mem.alignForward(u64, off, shdr.sh_addralign); |
| | 4818 | shdr.sh_offset = off; |
| | 4819 | off += shdr.sh_size; |
| | 4820 | try self.phdr_to_shdr_table.putNoClobber(gpa, @intCast(ii + cut.start), phndx); |
| | 4821 | } |
| | 4822 | |
| | 4823 | addr += self.page_size; |
| | 4824 | } |
| | 4825 | } |
| | 4826 | |
| 4674 | fn allocateNonAllocSections(self: *Elf) void { | 4827 | fn allocateNonAllocSections(self: *Elf) void { |
| 4675 | for (self.shdrs.items) |*shdr| { | 4828 | for (self.shdrs.items) |*shdr| { |
| 4676 | if (shdr.sh_type == elf.SHT_NULL) continue; | 4829 | if (shdr.sh_type == elf.SHT_NULL) continue; |
| ... | @@ -5817,65 +5970,78 @@ fn reportParseError( | ... | @@ -5817,65 +5970,78 @@ fn reportParseError( |
| 5817 | try err.addNote(self, "while parsing {s}", .{path}); | 5970 | try err.addNote(self, "while parsing {s}", .{path}); |
| 5818 | } | 5971 | } |
| 5819 | | 5972 | |
| 5820 | fn fmtShdrs(self: *Elf) std.fmt.Formatter(formatShdrs) { | 5973 | const FormatShdrCtx = struct { |
| 5821 | return .{ .data = self }; | 5974 | elf_file: *Elf, |
| | 5975 | shdr: elf.Elf64_Shdr, |
| | 5976 | }; |
| | 5977 | |
| | 5978 | fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) { |
| | 5979 | return .{ .data = .{ |
| | 5980 | .shdr = shdr, |
| | 5981 | .elf_file = self, |
| | 5982 | } }; |
| 5822 | } | 5983 | } |
| 5823 | | 5984 | |
| 5824 | fn formatShdrs( | 5985 | fn formatShdr( |
| 5825 | self: *Elf, | 5986 | ctx: FormatShdrCtx, |
| 5826 | comptime unused_fmt_string: []const u8, | 5987 | comptime unused_fmt_string: []const u8, |
| 5827 | options: std.fmt.FormatOptions, | 5988 | options: std.fmt.FormatOptions, |
| 5828 | writer: anytype, | 5989 | writer: anytype, |
| 5829 | ) !void { | 5990 | ) !void { |
| 5830 | _ = options; | 5991 | _ = options; |
| 5831 | _ = unused_fmt_string; | 5992 | _ = unused_fmt_string; |
| 5832 | for (self.shdrs.items, 0..) |shdr, i| { | 5993 | const shdr = ctx.shdr; |
| 5833 | try writer.print("shdr({d}) : phdr({?d}) : {s} : @{x} ({x}) : align({x}) : size({x})\n", .{ | 5994 | try writer.print("{s} : @{x} ({x}) : align({x}) : size({x})", .{ |
| 5834 | i, self.phdr_to_shdr_table.get(@intCast(i)), | 5995 | ctx.elf_file.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset, |
| 5835 | self.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset, | 5996 | shdr.sh_addr, shdr.sh_addralign, |
| 5836 | shdr.sh_addr, shdr.sh_addralign, | 5997 | shdr.sh_size, |
| 5837 | shdr.sh_size, | 5998 | }); |
| 5838 | }); | | |
| 5839 | } | | |
| 5840 | } | 5999 | } |
| 5841 | | 6000 | |
| 5842 | fn fmtPhdrs(self: *Elf) std.fmt.Formatter(formatPhdrs) { | 6001 | const FormatPhdrCtx = struct { |
| 5843 | return .{ .data = self }; | 6002 | elf_file: *Elf, |
| | 6003 | phdr: elf.Elf64_Phdr, |
| | 6004 | }; |
| | 6005 | |
| | 6006 | fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) { |
| | 6007 | return .{ .data = .{ |
| | 6008 | .phdr = phdr, |
| | 6009 | .elf_file = self, |
| | 6010 | } }; |
| 5844 | } | 6011 | } |
| 5845 | | 6012 | |
| 5846 | fn formatPhdrs( | 6013 | fn formatPhdr( |
| 5847 | self: *Elf, | 6014 | ctx: FormatPhdrCtx, |
| 5848 | comptime unused_fmt_string: []const u8, | 6015 | comptime unused_fmt_string: []const u8, |
| 5849 | options: std.fmt.FormatOptions, | 6016 | options: std.fmt.FormatOptions, |
| 5850 | writer: anytype, | 6017 | writer: anytype, |
| 5851 | ) !void { | 6018 | ) !void { |
| 5852 | _ = options; | 6019 | _ = options; |
| 5853 | _ = unused_fmt_string; | 6020 | _ = unused_fmt_string; |
| 5854 | for (self.phdrs.items, 0..) |phdr, i| { | 6021 | const phdr = ctx.phdr; |
| 5855 | const write = phdr.p_flags & elf.PF_W != 0; | 6022 | const write = phdr.p_flags & elf.PF_W != 0; |
| 5856 | const read = phdr.p_flags & elf.PF_R != 0; | 6023 | const read = phdr.p_flags & elf.PF_R != 0; |
| 5857 | const exec = phdr.p_flags & elf.PF_X != 0; | 6024 | const exec = phdr.p_flags & elf.PF_X != 0; |
| 5858 | var flags: [3]u8 = [_]u8{'_'} ** 3; | 6025 | var flags: [3]u8 = [_]u8{'_'} ** 3; |
| 5859 | if (exec) flags[0] = 'X'; | 6026 | if (exec) flags[0] = 'X'; |
| 5860 | if (write) flags[1] = 'W'; | 6027 | if (write) flags[1] = 'W'; |
| 5861 | if (read) flags[2] = 'R'; | 6028 | if (read) flags[2] = 'R'; |
| 5862 | const p_type = switch (phdr.p_type) { | 6029 | const p_type = switch (phdr.p_type) { |
| 5863 | elf.PT_LOAD => "LOAD", | 6030 | elf.PT_LOAD => "LOAD", |
| 5864 | elf.PT_TLS => "TLS", | 6031 | elf.PT_TLS => "TLS", |
| 5865 | elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME", | 6032 | elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME", |
| 5866 | elf.PT_GNU_STACK => "GNU_STACK", | 6033 | elf.PT_GNU_STACK => "GNU_STACK", |
| 5867 | elf.PT_DYNAMIC => "DYNAMIC", | 6034 | elf.PT_DYNAMIC => "DYNAMIC", |
| 5868 | elf.PT_INTERP => "INTERP", | 6035 | elf.PT_INTERP => "INTERP", |
| 5869 | elf.PT_NULL => "NULL", | 6036 | elf.PT_NULL => "NULL", |
| 5870 | elf.PT_PHDR => "PHDR", | 6037 | elf.PT_PHDR => "PHDR", |
| 5871 | elf.PT_NOTE => "NOTE", | 6038 | elf.PT_NOTE => "NOTE", |
| 5872 | else => "UNKNOWN", | 6039 | else => "UNKNOWN", |
| 5873 | }; | 6040 | }; |
| 5874 | try writer.print("phdr({d}) : {s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})\n", .{ | 6041 | try writer.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{ |
| 5875 | i, p_type, flags, phdr.p_offset, phdr.p_vaddr, | 6042 | p_type, flags, phdr.p_offset, phdr.p_vaddr, |
| 5876 | phdr.p_align, phdr.p_filesz, phdr.p_memsz, | 6043 | phdr.p_align, phdr.p_filesz, phdr.p_memsz, |
| 5877 | }); | 6044 | }); |
| 5878 | } | | |
| 5879 | } | 6045 | } |
| 5880 | | 6046 | |
| 5881 | fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { | 6047 | fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) { |
| ... | @@ -5928,9 +6094,17 @@ fn fmtDumpState( | ... | @@ -5928,9 +6094,17 @@ fn fmtDumpState( |
| 5928 | } | 6094 | } |
| 5929 | try writer.print("{}\n", .{self.got.fmt(self)}); | 6095 | try writer.print("{}\n", .{self.got.fmt(self)}); |
| 5930 | try writer.writeAll("Output shdrs\n"); | 6096 | try writer.writeAll("Output shdrs\n"); |
| 5931 | try writer.print("{}\n", .{self.fmtShdrs()}); | 6097 | for (self.shdrs.items, 0..) |shdr, shndx| { |
| | 6098 | try writer.print("shdr({d}) : phdr({?d}) : {}\n", .{ |
| | 6099 | shndx, |
| | 6100 | self.phdr_to_shdr_table.get(@intCast(shndx)), |
| | 6101 | self.fmtShdr(shdr), |
| | 6102 | }); |
| | 6103 | } |
| 5932 | try writer.writeAll("Output phdrs\n"); | 6104 | try writer.writeAll("Output phdrs\n"); |
| 5933 | try writer.print("{}\n", .{self.fmtPhdrs()}); | 6105 | for (self.phdrs.items, 0..) |phdr, phndx| { |
| | 6106 | try writer.print("phdr{d} : {}\n", .{ phndx, self.fmtPhdr(phdr) }); |
| | 6107 | } |
| 5934 | } | 6108 | } |
| 5935 | | 6109 | |
| 5936 | /// Binary search | 6110 | /// Binary search |