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);

Reply via email to