The Mach kernel & codesign on arm64 macOS has strict requirements for alignment and sequence of segments and sections. Dyld probably is just as picky, though kernel & codesign reject malformed Mach-O files before dyld ever has a chance.
I developed this diff by incrementally changing alignments & sequences to match the output of ld64. I stopped when my hello-world test program started working: `codesign --verify` succeded, and `execve(2)` didn't immediately fail with `errno == EBADMACHO` = `"Malformed Mach-O file"`.
Differential Revision: https://reviews.llvm.org/D94935
NonLazyPointerSectionBase::NonLazyPointerSectionBase(const char *segname,
const char *name)
: SyntheticSection(segname, name) {
- align = 8;
+ align = WordSize; // vector of pointers / mimic ld64
flags = MachO::S_NON_LAZY_SYMBOL_POINTERS;
}
StubsSection::StubsSection()
: SyntheticSection(segment_names::text, "__stubs") {
- flags = MachO::S_SYMBOL_STUBS;
+ flags = MachO::S_SYMBOL_STUBS | MachO::S_ATTR_SOME_INSTRUCTIONS |
+ MachO::S_ATTR_PURE_INSTRUCTIONS;
+ align = 4; // machine instructions / mimic ld64
reserved2 = target->stubSize;
}
}
StubHelperSection::StubHelperSection()
- : SyntheticSection(segment_names::text, "__stub_helper") {}
+ : SyntheticSection(segment_names::text, "__stub_helper") {
+ flags = MachO::S_ATTR_SOME_INSTRUCTIONS | MachO::S_ATTR_PURE_INSTRUCTIONS;
+ align = 4; // machine instructions / mimic ld64
+}
uint64_t StubHelperSection::getSize() const {
return target->stubHelperHeaderSize +
uint8_t *arr = bAlloc.Allocate<uint8_t>(WordSize);
memset(arr, 0, WordSize);
data = {arr, WordSize};
+ align = WordSize; // pointer / mimic ld64
}
LazyPointerSection::LazyPointerSection()
: SyntheticSection(segment_names::data, "__la_symbol_ptr") {
- align = 8;
+ align = WordSize; // vector of pointers / mimic ld64
flags = MachO::S_LAZY_SYMBOL_POINTERS;
}
// these are not synthetic, but in service of synthetic __unwind_info
constexpr const char compactUnwind[] = "__compact_unwind";
constexpr const char ehFrame[] = "__eh_frame";
+// these are not synthetic, but need to be sorted
+constexpr const char text[] = "__text";
+constexpr const char stubs[] = "__stubs";
+constexpr const char stubHelper[] = "__stub_helper";
+constexpr const char laSymbolPtr[] = "__la_symbol_ptr";
+constexpr const char data[] = "__data";
} // namespace section_names
public:
LinkEditSection(const char *segname, const char *name)
: SyntheticSection(segname, name) {
- align = WordSize;
+ align = WordSize; // mimic ld64
}
// Sections in __LINKEDIT are special: their offsets are recorded in the
UnwindInfoSection::UnwindInfoSection()
: SyntheticSection(segment_names::text, section_names::unwindInfo) {
- align = WordSize; // TODO(gkm): make this 4 KiB ?
+ align = 4; // mimic ld64
}
bool UnwindInfoSection::isNeeded() const {
}
void Writer::createLoadCommands() {
+ uint8_t segIndex = 0;
+ for (OutputSegment *seg : outputSegments) {
+ in.header->addLoadCommand(make<LCSegment>(seg->name, seg));
+ seg->index = segIndex++;
+ }
+
in.header->addLoadCommand(make<LCDyldInfo>(
in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports));
in.header->addLoadCommand(make<LCSymtab>(symtabSection, stringTableSection));
switch (config->outputType) {
case MH_EXECUTE:
- in.header->addLoadCommand(make<LCMain>());
in.header->addLoadCommand(make<LCLoadDylinker>());
+ in.header->addLoadCommand(make<LCMain>());
break;
case MH_DYLIB:
in.header->addLoadCommand(make<LCDylib>(LC_ID_DYLIB, config->installName,
llvm_unreachable("unhandled output file type");
}
- in.header->addLoadCommand(make<LCBuildVersion>(config->platform));
-
uuidCommand = make<LCUuid>();
in.header->addLoadCommand(uuidCommand);
- uint8_t segIndex = 0;
- for (OutputSegment *seg : outputSegments) {
- in.header->addLoadCommand(make<LCSegment>(seg->name, seg));
- seg->index = segIndex++;
- }
+ in.header->addLoadCommand(make<LCBuildVersion>(config->platform));
uint64_t dylibOrdinal = 1;
for (InputFile *file : inputFiles) {
static int segmentOrder(OutputSegment *seg) {
return StringSwitch<int>(seg->name)
- .Case(segment_names::pageZero, -2)
- .Case(segment_names::text, -1)
+ .Case(segment_names::pageZero, -4)
+ .Case(segment_names::text, -3)
+ .Case(segment_names::dataConst, -2)
+ .Case(segment_names::data, -1)
// Make sure __LINKEDIT is the last segment (i.e. all its hidden
// sections must be ordered after other sections).
.Case(segment_names::linkEdit, std::numeric_limits<int>::max())
// Sections are uniquely identified by their segment + section name.
if (segname == segment_names::text) {
return StringSwitch<int>(osec->name)
- .Case(section_names::header, -1)
+ .Case(section_names::header, -4)
+ .Case(section_names::text, -3)
+ .Case(section_names::stubs, -2)
+ .Case(section_names::stubHelper, -1)
.Case(section_names::unwindInfo, std::numeric_limits<int>::max() - 1)
.Case(section_names::ehFrame, std::numeric_limits<int>::max())
.Default(0);
- }
- if (segname == segment_names::data) {
+ } else if (segname == segment_names::data) {
// For each thread spawned, dyld will initialize its TLVs by copying the
// address range from the start of the first thread-local data section to
// the end of the last one. We therefore arrange these sections contiguously
case S_ZEROFILL:
return std::numeric_limits<int>::max();
default:
- return 0;
+ return StringSwitch<int>(osec->name)
+ .Case(section_names::laSymbolPtr, -2)
+ .Case(section_names::data, -1)
+ .Default(0);
}
- }
- if (segname == segment_names::linkEdit) {
+ } else if (segname == segment_names::linkEdit) {
return StringSwitch<int>(osec->name)
.Case(section_names::rebase, -8)
.Case(section_names::binding, -7)
# RUN: yaml2obj %t/fat.dylib.yaml > %t/fat.dylib
# RUN: %lld -lSystem %t/test.o %t/fat.dylib -o %t/test
# RUN: llvm-objdump --macho --lazy-bind %t/test | FileCheck %s
-# CHECK: __DATA __la_symbol_ptr 0x100001008 foo _foo
+# CHECK: __DATA __la_symbol_ptr 0x100002000 foo _foo
#--- test.s
.globl _main
# RUN: llvm-objdump --macho --all-headers --syms %t/not-main | FileCheck %s
# CHECK-LABEL: SYMBOL TABLE
# CHECK-NEXT: {{0*}}[[#%x, ENTRY_ADDR:]] {{.*}} __TEXT,__text _not_main
-# CHECK: cmd LC_MAIN
-# CHECK-NEXT: cmdsize 24
-# CHECK-NEXT: entryoff [[#ENTRYOFF:]]
# CHECK: sectname __text
# CHECK-NEXT: segname __TEXT
## Note: the following checks assume that the entry symbol is right at the
## beginning of __text.
# CHECK-NEXT: addr 0x{{0*}}[[#ENTRY_ADDR]]
# CHECK-NEXT: size
-# CHECK-NEXT: offset [[#ENTRYOFF]]
+# CHECK-NEXT: offset [[#ENTRYOFF:]]
+# CHECK: cmd LC_MAIN
+# CHECK-NEXT: cmdsize 24
+# CHECK-NEXT: entryoff [[#ENTRYOFF]]
# RUN: %lld -lSystem -o %t/dysym-main %t/not-main.o %t/libfoo.dylib -e _dysym_main
# RUN: llvm-objdump --macho --all-headers --indirect-symbols --lazy-bind %t/dysym-main | FileCheck %s --check-prefix=DYSYM
# CHECK-NEXT: address index name
# CHECK-NEXT: _bar_fn
# CHECK-NEXT: _foo_fn
-# CHECK-NEXT: Indirect symbols for (__DATA,__thread_ptrs) 2 entries
+# CHECK-NEXT: Indirect symbols for (__DATA_CONST,__got) 3 entries
# CHECK-NEXT: address index name
-# CHECK-NEXT: _bar_tlv
-# CHECK-NEXT: _foo_tlv
+# CHECK-NEXT: _bar
+# CHECK-NEXT: _foo
+# CHECK-NEXT: _stub_binder
# CHECK-NEXT: Indirect symbols for (__DATA,__la_symbol_ptr) 2 entries
# CHECK-NEXT: address index name
# CHECK-NEXT: _bar_fn
# CHECK-NEXT: _foo_fn
-# CHECK-NEXT: Indirect symbols for (__DATA_CONST,__got) 3 entries
+# CHECK-NEXT: Indirect symbols for (__DATA,__thread_ptrs) 2 entries
# CHECK-NEXT: address index name
-# CHECK-NEXT: _bar
-# CHECK-NEXT: _foo
-# CHECK-NEXT: _stub_binder
+# CHECK-NEXT: _bar_tlv
+# CHECK-NEXT: _foo_tlv
#--- libfoo.s
# CHECK-NEXT: ntools 1
# CHECK-NEXT: tool ld
# CHECK-NEXT: version {{[0-9\.]+}}
-# CHECK-NEXT: Load command [[#]]
.text
.global _main
--- /dev/null
+# REQUIRES: x86
+
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o
+# RUN: %lld -execute -o %t/exec %t/test.o -lSystem
+# RUN: %lld -dylib -o %t/dylib %t/test.o -lSystem
+# RUN: %lld -bundle -o %t/bundle %t/test.o -lSystem
+
+# RUN: llvm-objdump --macho --all-headers %t/exec | \
+# RUN: FileCheck %s --check-prefixes=EXEC,COMMON
+# RUN: llvm-objdump --macho --all-headers %t/dylib | \
+# RUN: FileCheck %s --check-prefixes=DYLIB,COMMON
+# RUN: llvm-objdump --macho --all-headers %t/bundle | \
+# RUN: FileCheck %s --check-prefixes=BUNDLE,COMMON
+
+## Check that load commands and sections within segments occur in the proper
+## sequence. On ARM64 kernel is especially picky about layout, and will
+## barf with errno=EBADMACHO when the sequence is wrong.
+
+# EXEC: cmd LC_SEGMENT_64
+# EXEC: segname __PAGEZERO
+
+# COMMON: cmd LC_SEGMENT_64
+# COMMON: segname __TEXT
+# COMMON: sectname __text
+# COMMON: segname __TEXT
+# COMMON: sectname __cstring
+# COMMON: segname __TEXT
+# COMMON: cmd LC_SEGMENT_64
+# COMMON: segname __DATA_CONST
+# COMMON: sectname __got
+# COMMON: segname __DATA_CONST
+# COMMON: cmd LC_SEGMENT_64
+# COMMON: segname __DATA
+# COMMON: sectname __data
+# COMMON: segname __DATA
+# COMMON: sectname __const
+# COMMON: segname __DATA
+# COMMON: cmd LC_SEGMENT_64
+# COMMON: segname __LINKEDIT
+# COMMON: cmd LC_DYLD_INFO_ONLY
+# COMMON: cmd LC_SYMTAB
+# COMMON: cmd LC_DYSYMTAB
+
+# EXEC: cmd LC_LOAD_DYLINKER
+# EXEC: cmd LC_MAIN
+# DYLIB: cmd LC_ID_DYLIB
+
+# COMMON: cmd LC_UUID
+# COMMON: cmd LC_BUILD_VERSION
+# COMMON: cmd LC_LOAD_DYLIB
+
+.section __TEXT,__cstring
+_str:
+ .asciz "Help me! I'm trapped in a test!\n"
+
+.globl _mutable
+.section __DATA,__data
+mutable:
+ .long 0x1234
+
+.globl _constant
+.section __DATA,__const
+constant:
+ .long 0x4567
+
+.text
+.global _main
+_main:
+ mov ___nan@GOTPCREL(%rip), %rax
+ ret
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: _local
# CHECK-NEXT: Type: Section (0xE)
-# CHECK-NEXT: Section: __data (0x4)
+# CHECK-NEXT: Section: __data
# CHECK-NEXT: RefType: UndefinedNonLazy (0x0)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: __dyld_private
# CHECK-NEXT: Type: Section (0xE)
-# CHECK-NEXT: Section: __data (0x4)
+# CHECK-NEXT: Section: __data
# CHECK-NEXT: RefType: UndefinedNonLazy (0x0)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Name: _external
# CHECK-NEXT: Extern
# CHECK-NEXT: Type: Section (0xE)
-# CHECK-NEXT: Section: __data (0x4)
+# CHECK-NEXT: Section: __data
# CHECK-NEXT: RefType: UndefinedNonLazy (0x0)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# RUN: llvm-objdump -d --no-show-raw-insn --bind --lazy-bind --weak-bind --full-contents %t/test | \
# RUN: FileCheck %s
-# CHECK: Contents of section __DATA,__la_symbol_ptr:
+# CHECK: Contents of section __DATA_CONST,__got:
## Check that this section contains a nonzero pointer. It should point to
-## _weak_external_fn, but we don't have a good way of testing the exact value as
-## the bytes here are in little-endian order.
+## _weak_external_for_gotpcrel.
# CHECK-NEXT: {{[0-9a-f]+}} {{[0-9a-f ]*[1-9a-f]+[0-9a-f ]*}}
-# CHECK: Contents of section __DATA_CONST,__got:
+# CHECK: Contents of section __DATA,__la_symbol_ptr:
## Check that this section contains a nonzero pointer. It should point to
-## _weak_external_for_gotpcrel.
+## _weak_external_fn, but we don't have a good way of testing the exact value as
+## the bytes here are in little-endian order.
# CHECK-NEXT: {{[0-9a-f]+}} {{[0-9a-f ]*[1-9a-f]+[0-9a-f ]*}}
# CHECK: <_main>:
# CHECK-NEXT: callq 0x{{[0-9a-f]*}}
# CHECK-LABEL: Bind table:
+# CHECK-DAG: __DATA_CONST __got 0x[[#WEAK_DY_GOT_ADDR]] pointer 0 libfoo _weak_dysym_for_gotpcrel
+# CHECK-DAG: __DATA __la_symbol_ptr 0x[[#%x,WEAK_DY_FN:]] pointer 0 libfoo _weak_dysym_fn
# CHECK-DAG: __DATA __data 0x[[#%x,WEAK_DY:]] pointer 0 libfoo _weak_dysym
# CHECK-DAG: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap
# CHECK-DAG: __DATA __thread_ptrs 0x[[#WEAK_DY_TLV_ADDR]] pointer 0 libfoo _weak_dysym_tlv
-# CHECK-DAG: __DATA_CONST __got 0x[[#WEAK_DY_GOT_ADDR]] pointer 0 libfoo _weak_dysym_for_gotpcrel
-# CHECK-DAG: __DATA __la_symbol_ptr 0x[[#%x,WEAK_DY_FN:]] pointer 0 libfoo _weak_dysym_fn
## Check that we don't have any other bindings
# CHECK-NOT: pointer
# YAML-LABEL: WeakBindOpcodes:
# YAML: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# YAML-NEXT: Imm: 0
-# YAML-NEXT: Symbol: _weak_foo
+# YAML-NEXT: Symbol: _weak_foo_fn
# YAML: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# YAML-NEXT: Imm: 0
-# YAML-NEXT: Symbol: _weak_foo_fn
+# YAML-NEXT: Symbol: _weak_foo
# YAML-LABEL: LazyBindOpcodes:
# YAML: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# YAML-NEXT: Imm: 1
# STRONG-YAML-LABEL: WeakBindOpcodes:
# STRONG-YAML: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# STRONG-YAML-NEXT: Imm: 0
-# STRONG-YAML-NEXT: Symbol: _weak_foo
+# STRONG-YAML-NEXT: Symbol: _weak_foo_fn
# STRONG-YAML: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# STRONG-YAML-NEXT: Imm: 0
# STRONG-YAML-NEXT: Symbol: _weak_foo
-# STRONG-YAML: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
-# STRONG-YAML-NEXT: Imm: 0
-# STRONG-YAML-NEXT: Symbol: _weak_foo_fn
# STRONG-YAML-LABEL: LazyBindOpcodes:
# STRONG-YAML: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
# STRONG-YAML-NEXT: Imm: 0