On Wed, Feb 15, 2023 at 11:30:34AM -0800, Philip Guenther wrote: > llvm-strip is somehow ignoring the alignment requirements of the segments. > If you look at the "readelf -l" output instead: > > Good: > Program Headers: > Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align > PHDR 0x000034 0x00000034 0x00000034 0x001e0 0x001e0 R E 0x4 > INTERP 0x001000 0x20000000 0x20000000 0x00013 0x00013 R 0x1 > [Requesting program interpreter: /usr/libexec/ld.so] > LOAD 0x000000 0x00000000 0x00000000 0x0058d 0x0058d R E 0x1000 > LOAD 0x001000 0x20000000 0x20000000 0x003e8 0x003e8 R 0x1000 > ... > > Note: offset == virtaddr mod alignment > > > Bad: > Program Headers: > Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align > PHDR 0x000034 0x00000034 0x00000034 0x001e0 0x001e0 R E 0x4 > INTERP 0x00058d 0x20000000 0x20000000 0x00013 0x00013 R 0x1 > [Requesting program interpreter: /usr/libexec/ld.so] > LOAD 0x000000 0x00000000 0x00000000 0x0058d 0x0058d R E 0x1000 > LOAD 0x00058d 0x20000000 0x20000000 0x003e8 0x003e8 R 0x1000 > > Boom, that second LOAD does not have offset == virtaddr mod alignment. > > Now, the sections that go into that segment have a max alignment of 4 and > llvm-strip's changes abides by that, but *IF* it's not going to keep the > segments page-aligned then it should be adjusting the virtaddr field of the > LOAD segment to keep the offset aligned with the virtaddr (and adjusting > the sizes so the LOAD continues to cover the total data). > > Or it shouldn't be screwing with the packing like that.
Below is not the fix you suggested, but it works for me. In particular, strip modified this way produces a working gcc package on i386 with llvm-15. The way I understand the issue is that a parent's alignment requirement can be strictly laxer than a child's (maybe that doesn't happen with ld.lld), so after changing the parent's offset, the child can become misaligned if we preserve the original offset difference. So fix the newly calculated offset of children up if necessary. This diff is for llvm 15 (since that's what my only i386 machine has), but the same code is in llvm/llvm/tools/llvm-objcopy/ELF/Object.cpp. It went through a full snapshot build on arm64 and is currently building i386. All binaries I tested still work. Here's sthen's example stripped with the modified strip. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x00000034 0x00000034 0x001e0 0x001e0 R E 0x4 INTERP 0x00055d 0x20000000 0x20000000 0x00013 0x00013 R 0x1 [Requesting program interpreter: /usr/libexec/ld.so] LOAD 0x000000 0x00000000 0x00000000 0x0055d 0x0055d R E 0x1000 LOAD 0x001000 0x20000000 0x20000000 0x003e8 0x003e8 R 0x1000 LOAD 0x001f18 0x20001f18 0x20001f18 0x000e8 0x00118 RW 0x1000 DYNAMIC 0x001f20 0x20001f20 0x20001f20 0x000a8 0x000a8 RW 0x4 NOTE 0x001014 0x20000014 0x20000014 0x00018 0x00018 R 0x4 GNU_EH_FRAME 0x0012e8 0x200002e8 0x200002e8 0x00034 0x00034 R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4 OPENBSD_RANDOM 0x001f18 0x20001f18 0x20001f18 0x00004 0x00004 RW 0x4 GNU_RELRO 0x001f18 0x20001f18 0x20001f18 0x000e8 0x000e8 R 0x1 Here's the context for the diff: https://github.com/llvm/llvm-project/blob/66749ce92707b578a17b36cb479ae44cdc314640/llvm/lib/ObjCopy/ELF/ELFObject.cpp#L2257-L2276 --- llvm/llvm/lib/ObjCopy/ELF/ELFObject.cpp.jca Wed Feb 15 20:56:46 2023 +++ llvm/llvm/lib/ObjCopy/ELF/ELFObject.cpp Thu Feb 16 14:04:13 2023 @@ -2245,7 +2245,7 @@ if (Seg->ParentSegment != nullptr) { Segment *Parent = Seg->ParentSegment; Seg->Offset = - Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset; + alignTo(Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset, std::max<uint64_t>(Seg->Align, 1), Seg->VAddr); } else { Seg->Offset = alignTo(Offset, std::max<uint64_t>(Seg->Align, 1), Seg->VAddr);