Skip to content

[llvm-objcopy] Apply encryptable offset to first segment, not section #130517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 14, 2025

Conversation

drodriguez
Copy link
Contributor

Bug introduced #120995. The LLD code calculates the "size" of the Mach-O headers, and then uses that size to place the segments, but the __TEXT section stays at fileoff zero. When I wrote the code into llvm-objcopy I calculated the extra space into the initial offset, which moved all the sections back 1 page.

Besides the modified test checking for the right fileoff values of the sections and the segments, I also manually checked the generated binaries after llvm-objcopy using dyld_info, as the bug report suggested.

Fixes #130472

Bug introduced llvm#120995. The LLD code calculates the "size" of the Mach-O
headers, and then uses that size to place the segments, but the `__TEXT`
section stays at `fileoff` zero. When I wrote the code into llvm-objcopy
I calculated the extra space into the initial offset, which moved all
the sections back 1 page.

Besides the modified test checking for the right `fileoff` values of the
sections and the segments, I also checked the generated binaries after
`llvm-objcopy` using `dyld_info`, as the bug report suggested.

Fixes llvm#130472
@llvmbot
Copy link
Member

llvmbot commented Mar 9, 2025

@llvm/pr-subscribers-llvm-binary-utilities

Author: Daniel Rodríguez Troitiño (drodriguez)

Changes

Bug introduced #120995. The LLD code calculates the "size" of the Mach-O headers, and then uses that size to place the segments, but the __TEXT section stays at fileoff zero. When I wrote the code into llvm-objcopy I calculated the extra space into the initial offset, which moved all the sections back 1 page.

Besides the modified test checking for the right fileoff values of the sections and the segments, I also manually checked the generated binaries after llvm-objcopy using dyld_info, as the bug report suggested.

Fixes #130472


Full diff: https://github.com/llvm/llvm-project/pull/130517.diff

2 Files Affected:

  • (modified) llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp (+7-5)
  • (modified) llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test (+9-3)
diff --git a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp
index d4eb6a9b9fc0b..91f73b59db1bd 100644
--- a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp
+++ b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp
@@ -116,11 +116,9 @@ uint64_t MachOLayoutBuilder::layoutSegments() {
   const bool IsObjectFile =
       O.Header.FileType == MachO::HeaderFileType::MH_OBJECT;
   uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0;
-  if (O.EncryptionInfoCommandIndex) {
-    // If we are emitting an encryptable binary, our load commands must have a
-    // separate (non-encrypted) page to themselves.
-    Offset = alignToPowerOf2(HeaderSize + O.Header.SizeOfCmds, PageSize);
-  }
+  // If we are emitting an encryptable binary, our load commands must have a
+  // separate (non-encrypted) page to themselves.
+  bool FirstSectExtraEncryptableOffset = O.EncryptionInfoCommandIndex.has_value();
   for (LoadCommand &LC : O.LoadCommands) {
     auto &MLC = LC.MachOLoadCommand;
     StringRef Segname;
@@ -174,6 +172,10 @@ uint64_t MachOLayoutBuilder::layoutSegments() {
         if (!Sec->hasValidOffset()) {
           Sec->Offset = 0;
         } else {
+          if (FirstSectExtraEncryptableOffset) {
+            SectOffset =  alignToPowerOf2(SectOffset, PageSize);
+            FirstSectExtraEncryptableOffset = false;
+          }
           Sec->Offset = SegOffset + SectOffset;
           Sec->Size = Sec->Content.size();
           SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size);
diff --git a/llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test b/llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test
index 19b06b1ec02c8..2b2bd670613de 100644
--- a/llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test
+++ b/llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test
@@ -1,13 +1,19 @@
 # RUN: rm -rf %t && mkdir %t
 # RUN: yaml2obj %s -o %t/original
 # RUN: llvm-strip --strip-all %t/original -o %t/stripped
-# RUN: llvm-readobj --macho-segment %t/stripped | FileCheck %s
+# RUN: llvm-readobj --macho-segment --section-headers %t/stripped | FileCheck %s
+
+# CHECK-LABEL: Sections [
+# CHECK:      Index: 0
+# CHECK-NEXT: Name: __text
+# CHECK-NEXT: Segment: __TEXT
+# CHECK:      Offset: 16384
 
 # CHECK-LABEL: Name: __PAGEZERO
-# CHECK:       fileoff: 16384
+# CHECK:       fileoff: 0
 
 # CHECK-LABEL: Name: __TEXT
-# CHECK:       fileoff: 16384
+# CHECK:       fileoff: 0
 
 # The YAML below is the following code
 # int main(int argc, char **argv) { return 0; }

Copy link

github-actions bot commented Mar 9, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@jh7370
Copy link
Collaborator

jh7370 commented Mar 10, 2025

Unfortunately, this is outside my minimal knowledge of Mach-O, so I'll leave it to another reviewer to look into. If nobody else shows up, let me know and I'll try adding some other possible reviewers.

@drodriguez
Copy link
Contributor Author

Sorry James. I though you were the other commenter in the other thread. My bad.

@glandium do you mind checking if this was what was affecting your binary from #120995 ? For some reason the fileoff being a page into the binary was not crashing anything for me, but the dyld_info checker obviously find it incorrect.

@jh7370
Copy link
Collaborator

jh7370 commented Mar 10, 2025

Sorry James. I though you were the other commenter in the other thread. My bad.

Not a problem. I'm involved in pretty much every change in llvm-objcopy ELF and generic code, but have also been heavily involved in some aspects of the other file formats, so confusion is entirely understandable.

@glandium
Copy link
Contributor

Applying this patch fixes the problem I had indeed.

Copy link
Contributor

@alx32 alx32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@DataCorrupted DataCorrupted self-requested a review March 14, 2025 01:02
@drodriguez drodriguez merged commit 8413f4d into llvm:main Mar 14, 2025
11 checks passed
@drodriguez drodriguez deleted the encryption-info-bug-2 branch March 14, 2025 20:43
swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Mar 17, 2025
…llvm#130517)

Bug introduced llvm#120995. The LLD code calculates the "size" of the Mach-O
headers, and then uses that size to place the segments, but the `__TEXT`
section stays at `fileoff` zero. When I wrote the code into llvm-objcopy
I calculated the extra space into the initial offset, which moved all
the sections back 1 page.

Besides the modified test checking for the right `fileoff` values of the
sections and the segments, I also manually checked the generated
binaries after `llvm-objcopy` using `dyld_info`, as the bug report
suggested.

Fixes llvm#130472

(cherry picked from commit 8413f4d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

llvm-objcopy regression: Invalid iOS Mach-O: __TEXT segment fileoffset is not zero
6 participants