From 6758c39f9e416358d9d701b2ac95dbe76b57b4e4 Mon Sep 17 00:00:00 2001 From: Sungbin Jo Date: Sun, 21 Jun 2026 04:51:09 +0900 Subject: [PATCH 1/2] [llvm-objcopy][MachO] Align __LINKEDIT entries to pointer size (#203680) Align Mach-O __LINKEDIT entries to the target pointer size when building the tail layout. This matches the behavior of ld64 and lld-macho. dyld on macOS 27 rejects loading dylibs with misaligned __LINKEDIT entries. See #203678 for details and the motivation of this fix. AI Tool Use Disclosure: Regarding the PR and the linked issue, I have personally wrote every single part of the PR by myself, and have/ran/verified every single part of the issue report as well without any AI tool usage. I have used LLM-based coding agents only for debugging purposes, e.g. to figure out why the dylib was not loading (from the original bug report), and figuring out how to build, run, and test my local `llvm-objcopy`. (cherry picked from commit 18c1cbce6874a7341f357014befb66d4c11a04a9) --- llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp | 85 ++-- llvm/lib/ObjCopy/MachO/MachOWriter.cpp | 42 +- .../MachO/linkedit-alignment.test | 366 ++++++++++++++++++ .../llvm-objcopy/MachO/linkedit-order-1.test | 3 +- .../llvm-objcopy/MachO/linkedit-order-2.test | 3 +- .../llvm-objcopy/MachO/symbol-table.test | 4 +- 6 files changed, 453 insertions(+), 50 deletions(-) create mode 100644 llvm/test/tools/llvm-objcopy/MachO/linkedit-alignment.test diff --git a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp index 8660c903c617d..aa4c0654eabed 100644 --- a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp @@ -235,24 +235,27 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { "Incorrect tail offset"); Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds); + const uint64_t LinkEditAlign = Is64Bit ? 8 : 4; + Offset = alignToPowerOf2(Offset, LinkEditAlign); + // The exports trie can be in either LC_DYLD_INFO or in // LC_DYLD_EXPORTS_TRIE, but not both. - size_t DyldInfoExportsTrieSize = 0; - size_t DyldExportsTrieSize = 0; + uint64_t DyldInfoExportsTrieRawSize = 0; + uint64_t DyldExportsTrieRawSize = 0; for (const auto &LC : O.LoadCommands) { switch (LC.MachOLoadCommand.load_command_data.cmd) { case MachO::LC_DYLD_INFO: case MachO::LC_DYLD_INFO_ONLY: - DyldInfoExportsTrieSize = O.Exports.Trie.size(); + DyldInfoExportsTrieRawSize = O.Exports.Trie.size(); break; case MachO::LC_DYLD_EXPORTS_TRIE: - DyldExportsTrieSize = O.Exports.Trie.size(); + DyldExportsTrieRawSize = O.Exports.Trie.size(); break; default: break; } } - assert((DyldInfoExportsTrieSize == 0 || DyldExportsTrieSize == 0) && + assert((DyldInfoExportsTrieRawSize == 0 || DyldExportsTrieRawSize == 0) && "Export trie in both LCs"); uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); @@ -263,28 +266,43 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { // trie, chained fixups, dyld exports trie, function starts, data-in-code, // symbol table, indirect symbol table, symbol table strings, // dylib codesign drs, and code signature. - auto updateOffset = [&Offset](size_t Size) { + auto updateOffset = [&Offset, LinkEditAlign](uint64_t Size) { uint64_t PreviousOffset = Offset; - Offset += Size; - return PreviousOffset; + // Match ld64 and lld-macho behavior by aligning all LINKEDIT entries to + // pointer size. This is safe because zero padding is valid for all entries. + uint64_t PaddedSize = alignToPowerOf2(Size, LinkEditAlign); + Offset += PaddedSize; + return std::make_pair(PreviousOffset, PaddedSize); }; - uint64_t StartOfRebaseInfo = updateOffset(O.Rebases.Opcodes.size()); - uint64_t StartOfBindingInfo = updateOffset(O.Binds.Opcodes.size()); - uint64_t StartOfWeakBindingInfo = updateOffset(O.WeakBinds.Opcodes.size()); - uint64_t StartOfLazyBindingInfo = updateOffset(O.LazyBinds.Opcodes.size()); - uint64_t StartOfExportTrie = updateOffset(DyldInfoExportsTrieSize); - uint64_t StartOfChainedFixups = updateOffset(O.ChainedFixups.Data.size()); - uint64_t StartOfDyldExportsTrie = updateOffset(DyldExportsTrieSize); - uint64_t StartOfFunctionStarts = updateOffset(O.FunctionStarts.Data.size()); - uint64_t StartOfDataInCode = updateOffset(O.DataInCode.Data.size()); - uint64_t StartOfLinkerOptimizationHint = + auto [StartOfRebaseInfo, RebaseInfoSize] = + updateOffset(O.Rebases.Opcodes.size()); + auto [StartOfBindingInfo, BindingInfoSize] = + updateOffset(O.Binds.Opcodes.size()); + auto [StartOfWeakBindingInfo, WeakBindingInfoSize] = + updateOffset(O.WeakBinds.Opcodes.size()); + auto [StartOfLazyBindingInfo, LazyBindingInfoSize] = + updateOffset(O.LazyBinds.Opcodes.size()); + auto [StartOfExportTrie, ExportTrieSize] = + updateOffset(DyldInfoExportsTrieRawSize); + auto [StartOfChainedFixups, ChainedFixupsSize] = + updateOffset(O.ChainedFixups.Data.size()); + auto [StartOfDyldExportsTrie, DyldExportsTrieSize] = + updateOffset(DyldExportsTrieRawSize); + auto [StartOfFunctionStarts, FunctionStartsSize] = + updateOffset(O.FunctionStarts.Data.size()); + auto [StartOfDataInCode, DataInCodeSize] = + updateOffset(O.DataInCode.Data.size()); + auto [StartOfLinkerOptimizationHint, LinkerOptimizationHintSize] = updateOffset(O.LinkerOptimizationHint.Data.size()); - uint64_t StartOfSymbols = updateOffset(NListSize * O.SymTable.Symbols.size()); + uint64_t StartOfSymbols = + updateOffset(NListSize * O.SymTable.Symbols.size()).first; uint64_t StartOfIndirectSymbols = - updateOffset(sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()); - uint64_t StartOfSymbolStrings = updateOffset(StrTableBuilder.getSize()); - uint64_t StartOfDylibCodeSignDRs = updateOffset(O.DylibCodeSignDRs.Data.size()); + updateOffset(sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()).first; + auto [StartOfSymbolStrings, SymbolStringsSize] = + updateOffset(StrTableBuilder.getSize()); + auto [StartOfDylibCodeSignDRs, DylibCodeSignDRsSize] = + updateOffset(O.DylibCodeSignDRs.Data.size()); uint64_t StartOfCodeSignature = Offset; uint32_t CodeSignatureSize = 0; @@ -343,13 +361,13 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { break; case MachO::LC_DYLIB_CODE_SIGN_DRS: MLC.linkedit_data_command_data.dataoff = StartOfDylibCodeSignDRs; - MLC.linkedit_data_command_data.datasize = O.DylibCodeSignDRs.Data.size(); + MLC.linkedit_data_command_data.datasize = DylibCodeSignDRsSize; break; case MachO::LC_SYMTAB: MLC.symtab_command_data.symoff = StartOfSymbols; MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); MLC.symtab_command_data.stroff = StartOfSymbolStrings; - MLC.symtab_command_data.strsize = StrTableBuilder.getSize(); + MLC.symtab_command_data.strsize = SymbolStringsSize; break; case MachO::LC_DYSYMTAB: { if (MLC.dysymtab_command_data.ntoc != 0 || @@ -368,20 +386,19 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { } case MachO::LC_DATA_IN_CODE: MLC.linkedit_data_command_data.dataoff = StartOfDataInCode; - MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size(); + MLC.linkedit_data_command_data.datasize = DataInCodeSize; break; case MachO::LC_LINKER_OPTIMIZATION_HINT: MLC.linkedit_data_command_data.dataoff = StartOfLinkerOptimizationHint; - MLC.linkedit_data_command_data.datasize = - O.LinkerOptimizationHint.Data.size(); + MLC.linkedit_data_command_data.datasize = LinkerOptimizationHintSize; break; case MachO::LC_FUNCTION_STARTS: MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts; - MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size(); + MLC.linkedit_data_command_data.datasize = FunctionStartsSize; break; case MachO::LC_DYLD_CHAINED_FIXUPS: MLC.linkedit_data_command_data.dataoff = StartOfChainedFixups; - MLC.linkedit_data_command_data.datasize = O.ChainedFixups.Data.size(); + MLC.linkedit_data_command_data.datasize = ChainedFixupsSize; break; case MachO::LC_DYLD_EXPORTS_TRIE: MLC.linkedit_data_command_data.dataoff = StartOfDyldExportsTrie; @@ -391,19 +408,19 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { case MachO::LC_DYLD_INFO_ONLY: MLC.dyld_info_command_data.rebase_off = O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo; - MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size(); + MLC.dyld_info_command_data.rebase_size = RebaseInfoSize; MLC.dyld_info_command_data.bind_off = O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo; - MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size(); + MLC.dyld_info_command_data.bind_size = BindingInfoSize; MLC.dyld_info_command_data.weak_bind_off = O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo; - MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size(); + MLC.dyld_info_command_data.weak_bind_size = WeakBindingInfoSize; MLC.dyld_info_command_data.lazy_bind_off = O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo; - MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size(); + MLC.dyld_info_command_data.lazy_bind_size = LazyBindingInfoSize; MLC.dyld_info_command_data.export_off = O.Exports.Trie.empty() ? 0 : StartOfExportTrie; - MLC.dyld_info_command_data.export_size = DyldInfoExportsTrieSize; + MLC.dyld_info_command_data.export_size = ExportTrieSize; break; // Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in // is not an offset in the binary file, instead, it is a diff --git a/llvm/lib/ObjCopy/MachO/MachOWriter.cpp b/llvm/lib/ObjCopy/MachO/MachOWriter.cpp index 07514dd2f8d6a..d83a82cee73ae 100644 --- a/llvm/lib/ObjCopy/MachO/MachOWriter.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOWriter.cpp @@ -24,6 +24,12 @@ using namespace llvm; using namespace llvm::objcopy::macho; using namespace llvm::support::endian; +#ifndef NDEBUG +static uint64_t paddedLinkEditEntrySize(uint64_t Size, bool Is64Bit) { + return alignToPowerOf2(Size, Is64Bit ? 8 : 4); +} +#endif + size_t MachOWriter::headerSize() const { return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); } @@ -55,29 +61,34 @@ size_t MachOWriter::totalSize() const { O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; if (DyLdInfoCommand.rebase_off) { - assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && + assert((DyLdInfoCommand.rebase_size == + paddedLinkEditEntrySize(O.Rebases.Opcodes.size(), Is64Bit)) && "Incorrect rebase opcodes size"); Ends.push_back(DyLdInfoCommand.rebase_off + DyLdInfoCommand.rebase_size); } if (DyLdInfoCommand.bind_off) { - assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && + assert((DyLdInfoCommand.bind_size == + paddedLinkEditEntrySize(O.Binds.Opcodes.size(), Is64Bit)) && "Incorrect bind opcodes size"); Ends.push_back(DyLdInfoCommand.bind_off + DyLdInfoCommand.bind_size); } if (DyLdInfoCommand.weak_bind_off) { - assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && + assert((DyLdInfoCommand.weak_bind_size == + paddedLinkEditEntrySize(O.WeakBinds.Opcodes.size(), Is64Bit)) && "Incorrect weak bind opcodes size"); Ends.push_back(DyLdInfoCommand.weak_bind_off + DyLdInfoCommand.weak_bind_size); } if (DyLdInfoCommand.lazy_bind_off) { - assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && + assert((DyLdInfoCommand.lazy_bind_size == + paddedLinkEditEntrySize(O.LazyBinds.Opcodes.size(), Is64Bit)) && "Incorrect lazy bind opcodes size"); Ends.push_back(DyLdInfoCommand.lazy_bind_off + DyLdInfoCommand.lazy_bind_size); } if (DyLdInfoCommand.export_off) { - assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && + assert((DyLdInfoCommand.export_size == + paddedLinkEditEntrySize(O.Exports.Trie.size(), Is64Bit)) && "Incorrect trie size"); Ends.push_back(DyLdInfoCommand.export_off + DyLdInfoCommand.export_size); } @@ -320,7 +331,8 @@ void MachOWriter::writeRebaseInfo() { O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; char *Out = Buf->getBufferStart() + DyLdInfoCommand.rebase_off; - assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && + assert((DyLdInfoCommand.rebase_size == + paddedLinkEditEntrySize(O.Rebases.Opcodes.size(), Is64Bit)) && "Incorrect rebase opcodes size"); memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size()); } @@ -332,7 +344,8 @@ void MachOWriter::writeBindInfo() { O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; char *Out = Buf->getBufferStart() + DyLdInfoCommand.bind_off; - assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && + assert((DyLdInfoCommand.bind_size == + paddedLinkEditEntrySize(O.Binds.Opcodes.size(), Is64Bit)) && "Incorrect bind opcodes size"); memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size()); } @@ -344,7 +357,8 @@ void MachOWriter::writeWeakBindInfo() { O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; char *Out = Buf->getBufferStart() + DyLdInfoCommand.weak_bind_off; - assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && + assert((DyLdInfoCommand.weak_bind_size == + paddedLinkEditEntrySize(O.WeakBinds.Opcodes.size(), Is64Bit)) && "Incorrect weak bind opcodes size"); memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size()); } @@ -356,7 +370,8 @@ void MachOWriter::writeLazyBindInfo() { O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; char *Out = Buf->getBufferStart() + DyLdInfoCommand.lazy_bind_off; - assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && + assert((DyLdInfoCommand.lazy_bind_size == + paddedLinkEditEntrySize(O.LazyBinds.Opcodes.size(), Is64Bit)) && "Incorrect lazy bind opcodes size"); memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size()); } @@ -368,7 +383,8 @@ void MachOWriter::writeExportInfo() { O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; char *Out = Buf->getBufferStart() + DyLdInfoCommand.export_off; - assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && + assert((DyLdInfoCommand.export_size == + paddedLinkEditEntrySize(O.Exports.Trie.size(), Is64Bit)) && "Incorrect export trie size"); memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); } @@ -398,7 +414,8 @@ void MachOWriter::writeLinkData(std::optional LCIndex, const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; char *Out = Buf->getBufferStart() + LinkEditDataCommand.dataoff; - assert((LinkEditDataCommand.datasize == LD.Data.size()) && + assert((LinkEditDataCommand.datasize == + paddedLinkEditEntrySize(LD.Data.size(), Is64Bit)) && "Incorrect data size"); memcpy(Out, LD.Data.data(), LD.Data.size()); } @@ -575,7 +592,8 @@ void MachOWriter::writeExportsTrieData() { O.LoadCommands[*O.ExportsTrieCommandIndex] .MachOLoadCommand.linkedit_data_command_data; char *Out = Buf->getBufferStart() + ExportsTrieCmd.dataoff; - assert((ExportsTrieCmd.datasize == O.Exports.Trie.size()) && + assert((ExportsTrieCmd.datasize == + paddedLinkEditEntrySize(O.Exports.Trie.size(), Is64Bit)) && "Incorrect export trie size"); memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); } diff --git a/llvm/test/tools/llvm-objcopy/MachO/linkedit-alignment.test b/llvm/test/tools/llvm-objcopy/MachO/linkedit-alignment.test new file mode 100644 index 0000000000000..86f826968cbb7 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/linkedit-alignment.test @@ -0,0 +1,366 @@ +## This test verifies that all LINKEDIT entry offset and sizes are +## aligned to the target word size. + +# RUN: yaml2obj --docnum=1 %s -o %t.arm64 +# RUN: llvm-objcopy %t.arm64 %t.arm64.copy +# RUN: obj2yaml %t.arm64.copy > %t.arm64.yaml +## Print the result twice: first to capture the LINKEDIT offsets and sizes, +## then to check that the captured values are aligned. +# RUN: cat %t.arm64.yaml %t.arm64.yaml | FileCheck %s --check-prefix=ARM64 + +# ARM64: cmd: LC_SEGMENT_64 +# ARM64: segname: __LINKEDIT +# ARM64: fileoff: [[#ARM64_LINKEDIT_FILEOFF:]] +# ARM64: filesize: [[#ARM64_LINKEDIT_FILESIZE:]] + +# ARM64: cmd: LC_DYLD_INFO_ONLY +# ARM64: rebase_off: [[#ARM64_REBASE_OFF:]] +# ARM64: rebase_size: [[#ARM64_REBASE_SIZE:]] +# ARM64: bind_off: [[#ARM64_BIND_OFF:]] +# ARM64: bind_size: [[#ARM64_BIND_SIZE:]] +# ARM64: weak_bind_off: [[#ARM64_WEAK_BIND_OFF:]] +# ARM64: weak_bind_size: [[#ARM64_WEAK_BIND_SIZE:]] +# ARM64: lazy_bind_off: [[#ARM64_LAZY_BIND_OFF:]] +# ARM64: lazy_bind_size: [[#ARM64_LAZY_BIND_SIZE:]] +# ARM64: export_off: [[#ARM64_EXPORT_OFF:]] +# ARM64: export_size: [[#ARM64_EXPORT_SIZE:]] + +# ARM64: cmd: LC_SYMTAB +# ARM64: symoff: [[#ARM64_SYMOFF:]] +# ARM64: stroff: [[#ARM64_STROFF:]] +# ARM64: strsize: [[#ARM64_STRSIZE:]] + +# ARM64: cmd: LC_DYSYMTAB +# ARM64: indirectsymoff: [[#ARM64_INDIRECTSYMOFF:]] + +# ARM64: cmd: LC_FUNCTION_STARTS +# ARM64: dataoff: [[#ARM64_FUNCTION_STARTS_OFF:]] +# ARM64: datasize: [[#ARM64_FUNCTION_STARTS_SIZE:]] + +# ARM64: cmd: LC_DATA_IN_CODE +# ARM64: dataoff: [[#ARM64_DATA_IN_CODE_OFF:]] +# ARM64: datasize: [[#ARM64_DATA_IN_CODE_SIZE:]] + +# ARM64: --- !mach-o + +# ARM64: cmd: LC_SEGMENT_64 +# ARM64: segname: __LINKEDIT +# ARM64: fileoff: [[#mul(div(ARM64_LINKEDIT_FILEOFF, 8), 8)]] +# ARM64: filesize: [[#mul(div(ARM64_LINKEDIT_FILESIZE, 8), 8)]] + +# ARM64: cmd: LC_DYLD_INFO_ONLY +# ARM64: rebase_off: [[#mul(div(ARM64_REBASE_OFF, 8), 8)]] +# ARM64: rebase_size: [[#mul(div(ARM64_REBASE_SIZE, 8), 8)]] +# ARM64: bind_off: [[#mul(div(ARM64_BIND_OFF, 8), 8)]] +# ARM64: bind_size: [[#mul(div(ARM64_BIND_SIZE, 8), 8)]] +# ARM64: weak_bind_off: [[#mul(div(ARM64_WEAK_BIND_OFF, 8), 8)]] +# ARM64: weak_bind_size: [[#mul(div(ARM64_WEAK_BIND_SIZE, 8), 8)]] +# ARM64: lazy_bind_off: [[#mul(div(ARM64_LAZY_BIND_OFF, 8), 8)]] +# ARM64: lazy_bind_size: [[#mul(div(ARM64_LAZY_BIND_SIZE, 8), 8)]] +# ARM64: export_off: [[#mul(div(ARM64_EXPORT_OFF, 8), 8)]] +# ARM64: export_size: [[#mul(div(ARM64_EXPORT_SIZE, 8), 8)]] + +# ARM64: cmd: LC_SYMTAB +# ARM64: symoff: [[#mul(div(ARM64_SYMOFF, 8), 8)]] +# ARM64: stroff: [[#mul(div(ARM64_STROFF, 8), 8)]] +# ARM64: strsize: [[#mul(div(ARM64_STRSIZE, 8), 8)]] + +# ARM64: cmd: LC_DYSYMTAB +# ARM64: indirectsymoff: [[#mul(div(ARM64_INDIRECTSYMOFF, 8), 8)]] + +# ARM64: cmd: LC_FUNCTION_STARTS +# ARM64: dataoff: [[#mul(div(ARM64_FUNCTION_STARTS_OFF, 8), 8)]] +# ARM64: datasize: [[#mul(div(ARM64_FUNCTION_STARTS_SIZE, 8), 8)]] + +# ARM64: cmd: LC_DATA_IN_CODE +# ARM64: dataoff: [[#mul(div(ARM64_DATA_IN_CODE_OFF, 8), 8)]] +# ARM64: datasize: [[#mul(div(ARM64_DATA_IN_CODE_SIZE, 8), 8)]] + +# RUN: yaml2obj --docnum=2 %s -o %t.armv7 +# RUN: llvm-objcopy %t.armv7 %t.armv7.copy +# RUN: obj2yaml %t.armv7.copy > %t.armv7.yaml +# RUN: cat %t.armv7.yaml %t.armv7.yaml | FileCheck %s --check-prefix=ARMV7 + +# ARMV7: cmd: LC_SEGMENT +# ARMV7: segname: __LINKEDIT +# ARMV7: fileoff: [[#ARMV7_LINKEDIT_FILEOFF:]] +# ARMV7: filesize: [[#ARMV7_LINKEDIT_FILESIZE:]] + +# ARMV7: cmd: LC_DYLD_INFO_ONLY +# ARMV7: rebase_off: [[#ARMV7_REBASE_OFF:]] +# ARMV7: rebase_size: [[#ARMV7_REBASE_SIZE:]] +# ARMV7: bind_off: [[#ARMV7_BIND_OFF:]] +# ARMV7: bind_size: [[#ARMV7_BIND_SIZE:]] +# ARMV7: weak_bind_off: [[#ARMV7_WEAK_BIND_OFF:]] +# ARMV7: weak_bind_size: [[#ARMV7_WEAK_BIND_SIZE:]] +# ARMV7: lazy_bind_off: [[#ARMV7_LAZY_BIND_OFF:]] +# ARMV7: lazy_bind_size: [[#ARMV7_LAZY_BIND_SIZE:]] +# ARMV7: export_off: [[#ARMV7_EXPORT_OFF:]] +# ARMV7: export_size: [[#ARMV7_EXPORT_SIZE:]] + +# ARMV7: cmd: LC_SYMTAB +# ARMV7: symoff: [[#ARMV7_SYMOFF:]] +# ARMV7: stroff: [[#ARMV7_STROFF:]] +# ARMV7: strsize: [[#ARMV7_STRSIZE:]] + +# ARMV7: cmd: LC_DYSYMTAB +# ARMV7: indirectsymoff: [[#ARMV7_INDIRECTSYMOFF:]] + +# ARMV7: cmd: LC_FUNCTION_STARTS +# ARMV7: dataoff: [[#ARMV7_FUNCTION_STARTS_OFF:]] +# ARMV7: datasize: [[#ARMV7_FUNCTION_STARTS_SIZE:]] + +# ARMV7: cmd: LC_DATA_IN_CODE +# ARMV7: dataoff: [[#ARMV7_DATA_IN_CODE_OFF:]] +# ARMV7: datasize: [[#ARMV7_DATA_IN_CODE_SIZE:]] + +# ARMV7: --- !mach-o + +# ARMV7: cmd: LC_SEGMENT +# ARMV7: segname: __LINKEDIT +# ARMV7: fileoff: [[#mul(div(ARMV7_LINKEDIT_FILEOFF, 4), 4)]] +# ARMV7: filesize: [[#mul(div(ARMV7_LINKEDIT_FILESIZE, 4), 4)]] + +# ARMV7: cmd: LC_DYLD_INFO_ONLY +# ARMV7: rebase_off: [[#mul(div(ARMV7_REBASE_OFF, 4), 4)]] +# ARMV7: rebase_size: [[#mul(div(ARMV7_REBASE_SIZE, 4), 4)]] +# ARMV7: bind_off: [[#mul(div(ARMV7_BIND_OFF, 4), 4)]] +# ARMV7: bind_size: [[#mul(div(ARMV7_BIND_SIZE, 4), 4)]] +# ARMV7: weak_bind_off: [[#mul(div(ARMV7_WEAK_BIND_OFF, 4), 4)]] +# ARMV7: weak_bind_size: [[#mul(div(ARMV7_WEAK_BIND_SIZE, 4), 4)]] +# ARMV7: lazy_bind_off: [[#mul(div(ARMV7_LAZY_BIND_OFF, 4), 4)]] +# ARMV7: lazy_bind_size: [[#mul(div(ARMV7_LAZY_BIND_SIZE, 4), 4)]] +# ARMV7: export_off: [[#mul(div(ARMV7_EXPORT_OFF, 4), 4)]] +# ARMV7: export_size: [[#mul(div(ARMV7_EXPORT_SIZE, 4), 4)]] + +# ARMV7: cmd: LC_SYMTAB +# ARMV7: symoff: [[#mul(div(ARMV7_SYMOFF, 4), 4)]] +# ARMV7: stroff: [[#mul(div(ARMV7_STROFF, 4), 4)]] +# ARMV7: strsize: [[#mul(div(ARMV7_STRSIZE, 4), 4)]] + +# ARMV7: cmd: LC_DYSYMTAB +# ARMV7: indirectsymoff: [[#mul(div(ARMV7_INDIRECTSYMOFF, 4), 4)]] + +# ARMV7: cmd: LC_FUNCTION_STARTS +# ARMV7: dataoff: [[#mul(div(ARMV7_FUNCTION_STARTS_OFF, 4), 4)]] +# ARMV7: datasize: [[#mul(div(ARMV7_FUNCTION_STARTS_SIZE, 4), 4)]] + +# ARMV7: cmd: LC_DATA_IN_CODE +# ARMV7: dataoff: [[#mul(div(ARMV7_DATA_IN_CODE_OFF, 4), 4)]] +# ARMV7: datasize: [[#mul(div(ARMV7_DATA_IN_CODE_SIZE, 4), 4)]] + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x0100000C + cpusubtype: 0x00000000 + filetype: 0x00000002 + ncmds: 6 + sizeofcmds: 256 + flags: 0x00000085 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 288 + vmsize: 256 + fileoff: 288 + filesize: 256 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 288 + rebase_size: 1 + bind_off: 289 + bind_size: 1 + weak_bind_off: 290 + weak_bind_size: 1 + lazy_bind_off: 291 + lazy_bind_size: 1 + export_off: 0 + export_size: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 304 + nsyms: 2 + stroff: 340 + strsize: 24 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 0 + iundefsym: 0 + nundefsym: 2 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 336 + nindirectsyms: 1 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 292 + datasize: 1 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 293 + datasize: 8 +LinkEditData: + RebaseOpcodes: + - Opcode: REBASE_OPCODE_DONE + Imm: 0 + BindOpcodes: + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + WeakBindOpcodes: + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + LazyBindOpcodes: + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + NameList: + - n_strx: 1 + n_type: 0x01 + n_sect: 0 + n_desc: 0 + n_value: 0 + - n_strx: 6 + n_type: 0x01 + n_sect: 0 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _foo + - _bar + - '' + IndirectSymbols: [ 0x1 ] + FunctionStarts: [ 0x0 ] + DataInCode: + - Offset: 0x0 + Length: 4 + Kind: 0x4 +... + +--- !mach-o +IsLittleEndian: true +FileHeader: + magic: 0xFEEDFACE + cputype: 0x0000000C + cpusubtype: 0x00000009 + filetype: 0x00000002 + ncmds: 6 + sizeofcmds: 240 + flags: 0x00000085 +LoadCommands: + - cmd: LC_SEGMENT + cmdsize: 56 + segname: __LINKEDIT + vmaddr: 268 + vmsize: 128 + fileoff: 268 + filesize: 128 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 268 + rebase_size: 1 + bind_off: 269 + bind_size: 1 + weak_bind_off: 270 + weak_bind_size: 1 + lazy_bind_off: 271 + lazy_bind_size: 1 + export_off: 0 + export_size: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 284 + nsyms: 2 + stroff: 312 + strsize: 16 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 0 + iundefsym: 0 + nundefsym: 2 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 308 + nindirectsyms: 1 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 272 + datasize: 4 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 276 + datasize: 8 +LinkEditData: + RebaseOpcodes: + - Opcode: REBASE_OPCODE_DONE + Imm: 0 + BindOpcodes: + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + WeakBindOpcodes: + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + LazyBindOpcodes: + - Opcode: BIND_OPCODE_DONE + Imm: 0 + Symbol: '' + NameList: + - n_strx: 1 + n_type: 0x01 + n_sect: 0 + n_desc: 0 + n_value: 0 + - n_strx: 6 + n_type: 0x01 + n_sect: 0 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _foo + - _bar + - '' + IndirectSymbols: [ 0x1 ] + FunctionStarts: [ 0x0 ] + DataInCode: + - Offset: 0x0 + Length: 4 + Kind: 0x4 +... diff --git a/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-1.test b/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-1.test index 4915f344c1e7e..b7ee56ed47dba 100644 --- a/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-1.test +++ b/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-1.test @@ -51,7 +51,8 @@ # CHECK: --- !mach-o # CHECK: cmd: LC_SYMTAB -# CHECK: stroff: [[#SYMTAB_STROFF: DYSYMTAB_INDIRECTSYMOFF + mul(DYSYMTAB_NINDIRECTSYMS, 4)]] +## LINKEDIT entries are aligned to the target word size. +# CHECK: stroff: [[#SYMTAB_STROFF: DYSYMTAB_INDIRECTSYMOFF + mul(div(mul(DYSYMTAB_NINDIRECTSYMS, 4) + 4, 8), 8)]] # CHECK: strsize: [[#SYMTAB_STRSIZE:]] # CHECK: cmd: LC_CODE_SIGNATURE diff --git a/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-2.test b/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-2.test index 0c5521ece0e44..dfbb7277a4fa1 100644 --- a/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-2.test +++ b/llvm/test/tools/llvm-objcopy/MachO/linkedit-order-2.test @@ -50,7 +50,8 @@ # CHECK: --- !mach-o # CHECK: cmd: LC_SYMTAB -# CHECK: stroff: [[#SYMTAB_STROFF: DYSYMTAB_INDIRECTSYMOFF + mul(DYSYMTAB_NINDIRECTSYMS, 4)]] +## LINKEDIT entries are aligned to the target word size. +# CHECK: stroff: [[#SYMTAB_STROFF: DYSYMTAB_INDIRECTSYMOFF + mul(div(mul(DYSYMTAB_NINDIRECTSYMS, 4) + 4, 8), 8)]] # CHECK: strsize: [[#SYMTAB_STRSIZE:]] # CHECK: cmd: LC_CODE_SIGNATURE diff --git a/llvm/test/tools/llvm-objcopy/MachO/symbol-table.test b/llvm/test/tools/llvm-objcopy/MachO/symbol-table.test index 7e0e1421c0646..a95c2ac7a358b 100644 --- a/llvm/test/tools/llvm-objcopy/MachO/symbol-table.test +++ b/llvm/test/tools/llvm-objcopy/MachO/symbol-table.test @@ -301,7 +301,7 @@ LoadCommands: vmaddr: 4294975488 vmsize: 4096 fileoff: 8192 - filesize: 508 + filesize: 512 maxprot: 1 initprot: 1 nsects: 0 @@ -310,7 +310,7 @@ LoadCommands: cmdsize: 24 symoff: 8192 nsyms: 18 - stroff: 8484 + stroff: 8488 strsize: 216 - cmd: LC_DYSYMTAB cmdsize: 80 From 3c3f13025bf9f99bb2a757eb37dad4f09e7d6c36 Mon Sep 17 00:00:00 2001 From: Sungbin Jo Date: Tue, 23 Jun 2026 01:58:09 +0900 Subject: [PATCH 2/2] [yaml2obj][MachO] Fix byte order of the indirect symbol table (#205044) This is a follow-up of PR #203680 that added the test case `linkedit-alignment.test`, which currently fails on big-endian buildbots (see: https://lab.llvm.org/buildbot/#/builders/98/builds/3084 and https://lab.llvm.org/buildbot/#/builders/114/builds/906). The failure seems to be on `yaml2obj`, where `writeDynamicSymbolTable` emits an indirect symbol table in host byte order rather than the specified object's byte order (i.e. the `IsLittleEndian` field value). This PR adds the missing swap and a regression test that round-trips all endian-sensitive fields with both endianness values. (cherry picked from commit 6e562169fd026e26124fe4e2e435d3a4c522ffe2) --- llvm/lib/ObjectYAML/MachOEmitter.cpp | 9 +- llvm/test/ObjectYAML/MachO/endianness.yaml | 207 +++++++++++++++++++++ 2 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 llvm/test/ObjectYAML/MachO/endianness.yaml diff --git a/llvm/lib/ObjectYAML/MachOEmitter.cpp b/llvm/lib/ObjectYAML/MachOEmitter.cpp index a4d17dfe1e320..cf7202c7da949 100644 --- a/llvm/lib/ObjectYAML/MachOEmitter.cpp +++ b/llvm/lib/ObjectYAML/MachOEmitter.cpp @@ -625,9 +625,12 @@ void MachOWriter::writeStringTable(raw_ostream &OS) { } void MachOWriter::writeDynamicSymbolTable(raw_ostream &OS) { - for (auto Data : Obj.LinkEdit.IndirectSymbols) - OS.write(reinterpret_cast(&Data), - sizeof(yaml::Hex32::BaseType)); + for (auto Data : Obj.LinkEdit.IndirectSymbols) { + uint32_t Value = Data; + if (Obj.IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(Value); + OS.write(reinterpret_cast(&Value), sizeof(uint32_t)); + } } void MachOWriter::writeFunctionStarts(raw_ostream &OS) { diff --git a/llvm/test/ObjectYAML/MachO/endianness.yaml b/llvm/test/ObjectYAML/MachO/endianness.yaml new file mode 100644 index 0000000000000..a0880e4642274 --- /dev/null +++ b/llvm/test/ObjectYAML/MachO/endianness.yaml @@ -0,0 +1,207 @@ +# RUN: yaml2obj -DENDIAN=false %s | obj2yaml | FileCheck %s +# RUN: yaml2obj -DENDIAN=true %s | obj2yaml | FileCheck %s + +## Check that yaml2obj writes all endian-sensitive Mach-O fields in object +## endianness rather than host endianness. + +# CHECK: FileHeader: +# CHECK: magic: 0xFEEDFACF +# CHECK: cputype: 0x1000012 +# CHECK: cpusubtype: 0x34 +# CHECK: filetype: 0x1 +# CHECK: ncmds: 5 +# CHECK: sizeofcmds: 304 +# CHECK: flags: 0x1020304 + +# CHECK: LoadCommands: +# CHECK: - cmd: LC_SEGMENT_64 +# CHECK: cmdsize: 152 +# CHECK: segname: __TEXT +# CHECK: vmaddr: 2387509390608836384 +# CHECK: vmsize: 256 +# CHECK: fileoff: 336 +# CHECK: filesize: 4 +# CHECK: maxprot: 16909060 +# CHECK: initprot: 84281096 +# CHECK: nsects: 1 +# CHECK: flags: 151653132 +# CHECK: Sections: +# CHECK: - sectname: __text +# CHECK: segname: __TEXT +# CHECK: addr: 0x2122232425262728 +# CHECK: size: 4 +# CHECK: offset: 0x150 +# CHECK: align: 2 +# CHECK: reloff: 0x154 +# CHECK: nreloc: 1 +# CHECK: flags: 0x80000400 +# CHECK: reserved1: 0x1020304 +# CHECK: reserved2: 0x5060708 +# CHECK: reserved3: 0x90A0B0C +# CHECK: content: DEADBEEF +# CHECK: relocations: +# CHECK: - address: 0x1020304 +# CHECK: symbolnum: 1 +# CHECK: pcrel: false +# CHECK: length: 3 +# CHECK: extern: true +# CHECK: type: 2 +# CHECK: scattered: false +# CHECK: value: 0 +# CHECK: - cmd: LC_SYMTAB +# CHECK: cmdsize: 24 +# CHECK: symoff: 356 +# CHECK: nsyms: 2 +# CHECK: stroff: 396 +# CHECK: strsize: 8 +# CHECK: - cmd: LC_DYSYMTAB +# CHECK: cmdsize: 80 +# CHECK: ilocalsym: 0 +# CHECK: nlocalsym: 1 +# CHECK: iextdefsym: 1 +# CHECK: nextdefsym: 0 +# CHECK: iundefsym: 1 +# CHECK: nundefsym: 1 +# CHECK: indirectsymoff: 388 +# CHECK: nindirectsyms: 2 +# CHECK: - cmd: LC_DATA_IN_CODE +# CHECK: cmdsize: 16 +# CHECK: dataoff: 348 +# CHECK: datasize: 8 +# CHECK: - cmd: LC_BUILD_VERSION +# CHECK: cmdsize: 32 +# CHECK: platform: 16909060 +# CHECK: minos: 84281096 +# CHECK: sdk: 151653132 +# CHECK: ntools: 1 +# CHECK: Tools: +# CHECK: - tool: 219025168 +# CHECK: version: 286397204 + +# CHECK: LinkEditData: +# CHECK: NameList: +# CHECK: - n_strx: 1 +# CHECK: n_type: 0x1 +# CHECK: n_sect: 1 +# CHECK: n_desc: 4660 +# CHECK: n_value: 72623859790382856 +# CHECK: - n_strx: 4 +# CHECK: n_type: 0x1 +# CHECK: n_sect: 0 +# CHECK: n_desc: 22136 +# CHECK: n_value: 1230066625199609624 +# CHECK: StringTable: +# CHECK: - '' +# CHECK: - _a +# CHECK: - _b +# CHECK: - '' +# CHECK: IndirectSymbols: [ 0x1, 0x40000000 ] +# CHECK: DataInCode: +# CHECK: - Offset: 0x1020304 +# CHECK: Length: 1286 +# CHECK: Kind: 0x708 + +--- !mach-o +IsLittleEndian: [[ENDIAN]] +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000012 + cpusubtype: 0x00000034 + filetype: 0x00000001 + ncmds: 5 + sizeofcmds: 304 + flags: 0x01020304 + reserved: 0x05060708 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __TEXT + vmaddr: 0x2122232425262720 + vmsize: 0x0000000000000100 + fileoff: 336 + filesize: 4 + maxprot: 0x01020304 + initprot: 0x05060708 + nsects: 1 + flags: 0x090A0B0C + Sections: + - sectname: __text + segname: __TEXT + addr: 0x2122232425262728 + size: 4 + offset: 336 + align: 2 + reloff: 340 + nreloc: 1 + flags: 0x80000400 + reserved1: 0x01020304 + reserved2: 0x05060708 + reserved3: 0x090A0B0C + content: DEADBEEF + relocations: + - address: 0x01020304 + symbolnum: 1 + pcrel: false + length: 3 + extern: true + type: 2 + scattered: false + value: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 356 + nsyms: 2 + stroff: 396 + strsize: 8 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 1 + iextdefsym: 1 + nextdefsym: 0 + iundefsym: 1 + nundefsym: 1 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 388 + nindirectsyms: 2 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 348 + datasize: 8 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 0x01020304 + minos: 0x05060708 + sdk: 0x090A0B0C + ntools: 1 + Tools: + - tool: 0x0D0E0F10 + version: 0x11121314 +LinkEditData: + NameList: + - n_strx: 1 + n_type: 0x01 + n_sect: 1 + n_desc: 0x1234 + n_value: 0x0102030405060708 + - n_strx: 4 + n_type: 0x01 + n_sect: 0 + n_desc: 0x5678 + n_value: 0x1112131415161718 + StringTable: [ '', _a, _b, '' ] + IndirectSymbols: [ 0x1, 0x40000000 ] + DataInCode: + - Offset: 0x01020304 + Length: 0x0506 + Kind: 0x0708 +...