56 changes: 33 additions & 23 deletions lld/MachO/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,21 @@ void MachHeaderSection::writeTo(uint8_t *buf) const {
PageZeroSection::PageZeroSection()
: SyntheticSection(segment_names::pageZero, section_names::pageZero) {}

GotSection::GotSection()
: SyntheticSection(segment_names::dataConst, section_names::got) {
NonLazyPointerSectionBase::NonLazyPointerSectionBase(const char *segname,
const char *name)
: SyntheticSection(segname, name) {
align = 8;
flags = MachO::S_NON_LAZY_SYMBOL_POINTERS;

// TODO: section_64::reserved1 should be an index into the indirect symbol
// table, which we do not currently emit
}

void GotSection::addEntry(Symbol &sym) {
void NonLazyPointerSectionBase::addEntry(Symbol &sym) {
if (entries.insert(&sym)) {
assert(sym.gotIndex == UINT32_MAX);
sym.gotIndex = entries.size() - 1;
}
}

void GotSection::writeTo(uint8_t *buf) const {
void NonLazyPointerSectionBase::writeTo(uint8_t *buf) const {
for (size_t i = 0, n = entries.size(); i < n; ++i)
if (auto *defined = dyn_cast<Defined>(entries[i]))
write64le(&buf[i * WordSize], defined->getVA());
Expand All @@ -107,7 +106,8 @@ BindingSection::BindingSection()
: LinkEditSection(segment_names::linkEdit, section_names::binding) {}

bool BindingSection::isNeeded() const {
return bindings.size() != 0 || in.got->isNeeded();
return bindings.size() != 0 || in.got->isNeeded() ||
in.tlvPointers->isNeeded();
}

namespace {
Expand Down Expand Up @@ -138,7 +138,6 @@ static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
lastBinding.segment = seg;
lastBinding.offset = offset;
} else if (lastBinding.offset != offset) {
assert(lastBinding.offset <= offset);
os << static_cast<uint8_t>(BIND_OPCODE_ADD_ADDR_ULEB);
encodeULEB128(offset - lastBinding.offset, os);
lastBinding.offset = offset;
Expand All @@ -149,8 +148,8 @@ static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
os << static_cast<uint8_t>(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
dysym.file->ordinal);
} else {
error("TODO: Support larger dylib symbol ordinals");
return;
os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
encodeULEB128(dysym.file->ordinal, os);
}
lastBinding.ordinal = dysym.file->ordinal;
}
Expand All @@ -169,6 +168,22 @@ static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
lastBinding.offset += WordSize;
}

static bool encodeNonLazyPointerSection(NonLazyPointerSectionBase *osec,
Binding &lastBinding,
raw_svector_ostream &os) {
bool didEncode = false;
size_t idx = 0;
for (const Symbol *sym : osec->getEntries()) {
if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
didEncode = true;
encodeBinding(*dysym, osec, idx * WordSize, /*addend=*/0, lastBinding,
os);
}
++idx;
}
return didEncode;
}

// Emit bind opcodes, which are a stream of byte-sized opcodes that dyld
// interprets to update a record with the following fields:
// * segment index (of the segment to write the symbol addresses to, typically
Expand All @@ -185,15 +200,8 @@ static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
void BindingSection::finalizeContents() {
raw_svector_ostream os{contents};
Binding lastBinding;
bool didEncode = false;
size_t gotIdx = 0;
for (const Symbol *sym : in.got->getEntries()) {
if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
didEncode = true;
encodeBinding(*dysym, in.got, gotIdx * WordSize, 0, lastBinding, os);
}
++gotIdx;
}
bool didEncode = encodeNonLazyPointerSection(in.got, lastBinding, os);
didEncode |= encodeNonLazyPointerSection(in.tlvPointers, lastBinding, os);

// Sorting the relocations by segment and address allows us to encode them
// more compactly.
Expand Down Expand Up @@ -340,11 +348,13 @@ uint32_t LazyBindingSection::encode(const DylibSymbol &sym) {
uint64_t offset = in.lazyPointers->addr - dataSeg->firstSection()->addr +
sym.stubsIndex * WordSize;
encodeULEB128(offset, os);
if (sym.file->ordinal <= MachO::BIND_IMMEDIATE_MASK)
if (sym.file->ordinal <= MachO::BIND_IMMEDIATE_MASK) {
os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
sym.file->ordinal);
else
fatal("TODO: Support larger dylib symbol ordinals");
} else {
os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
encodeULEB128(sym.file->ordinal, os);
}

os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
<< sym.getName() << '\0'
Expand Down
30 changes: 26 additions & 4 deletions lld/MachO/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "ExportTrie.h"
#include "InputSection.h"
#include "OutputSection.h"
#include "OutputSegment.h"
#include "Target.h"

#include "llvm/ADT/SetVector.h"
Expand All @@ -31,6 +32,7 @@ constexpr const char export_[] = "__export";
constexpr const char symbolTable[] = "__symbol_table";
constexpr const char stringTable[] = "__string_table";
constexpr const char got[] = "__got";
constexpr const char threadPtrs[] = "__thread_ptrs";

} // namespace section_names

Expand Down Expand Up @@ -95,11 +97,13 @@ class PageZeroSection : public SyntheticSection {
void writeTo(uint8_t *buf) const override {}
};

// This section will be populated by dyld with addresses to non-lazily-loaded
// dylib symbols.
class GotSection : public SyntheticSection {
// This is the base class for the GOT and TLVPointer sections, which are nearly
// functionally identical -- they will both be populated by dyld with addresses
// to non-lazily-loaded dylib symbols. The main difference is that the
// TLVPointerSection stores references to thread-local variables.
class NonLazyPointerSectionBase : public SyntheticSection {
public:
GotSection();
NonLazyPointerSectionBase(const char *segname, const char *name);

const llvm::SetVector<const Symbol *> &getEntries() const { return entries; }

Expand All @@ -115,6 +119,23 @@ class GotSection : public SyntheticSection {
llvm::SetVector<const Symbol *> entries;
};

class GotSection : public NonLazyPointerSectionBase {
public:
GotSection()
: NonLazyPointerSectionBase(segment_names::dataConst,
section_names::got) {
// TODO: section_64::reserved1 should be an index into the indirect symbol
// table, which we do not currently emit
}
};

class TlvPointerSection : public NonLazyPointerSectionBase {
public:
TlvPointerSection()
: NonLazyPointerSectionBase(segment_names::data,
section_names::threadPtrs) {}
};

struct BindingEntry {
const DylibSymbol *dysym;
const InputSection *isec;
Expand Down Expand Up @@ -297,6 +318,7 @@ struct InStruct {
MachHeaderSection *header = nullptr;
BindingSection *binding = nullptr;
GotSection *got = nullptr;
TlvPointerSection *tlvPointers = nullptr;
LazyPointerSection *lazyPointers = nullptr;
StubsSection *stubs = nullptr;
StubHelperSection *stubHelper = nullptr;
Expand Down
27 changes: 27 additions & 0 deletions lld/MachO/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,30 @@ class LCLoadDylinker : public LoadCommand {
// different location.
const StringRef path = "/usr/lib/dyld";
};

class LCRPath : public LoadCommand {
public:
LCRPath(StringRef path) : path(path) {}

uint32_t getSize() const override {
return alignTo(sizeof(rpath_command) + path.size() + 1, WordSize);
}

void writeTo(uint8_t *buf) const override {
auto *c = reinterpret_cast<rpath_command *>(buf);
buf += sizeof(rpath_command);

c->cmd = LC_RPATH;
c->cmdsize = getSize();
c->path = sizeof(rpath_command);

memcpy(buf, path.data(), path.size());
buf[path.size()] = '\0';
}

private:
StringRef path;
};
} // namespace

void Writer::scanRelocations() {
Expand All @@ -268,6 +292,8 @@ void Writer::createLoadCommands() {
make<LCDyldInfo>(in.binding, lazyBindingSection, exportSection));
in.header->addLoadCommand(make<LCSymtab>(symtabSection, stringTableSection));
in.header->addLoadCommand(make<LCDysymtab>());
for (StringRef path : config->runtimePaths)
in.header->addLoadCommand(make<LCRPath>(path));

switch (config->outputType) {
case MH_EXECUTE:
Expand Down Expand Up @@ -540,6 +566,7 @@ void macho::createSyntheticSections() {
in.header = make<MachHeaderSection>();
in.binding = make<BindingSection>();
in.got = make<GotSection>();
in.tlvPointers = make<TlvPointerSection>();
in.lazyPointers = make<LazyPointerSection>();
in.stubs = make<StubsSection>();
in.stubHelper = make<StubHelperSection>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ install-name: '/System/Library/Frameworks/CoreFoundation.framework/CoreFound
current-version: 0001.001.1
compatibility-version: 150
exports:
- archs: [ 'x86_64' ]
symbols: [ '__CFBigNumGetInt128' ]
- archs: [ 'x86_64' ]
symbols: [ '__CFBigNumGetInt128' ]
objc-classes: [ NSObject ]
objc-ivars: [ NSConstantArray._count ]
objc-eh-types: [ NSException ]
19 changes: 19 additions & 0 deletions lld/test/MachO/force-load.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# REQUIRES: x86
# RUN: mkdir -p %t
# RUN: echo ".section __TEXT,archive; .globl _foo; .weak_definition _foo; _foo:" | llvm-mc -filetype=obj -triple=x86_64-apple-darwin -o %t/archive-foo.o
# RUN: rm -f %t/foo.a
# RUN: llvm-ar rcs %t/foo.a %t/archive-foo.o
# RUN: echo ".section __TEXT,obj; .globl _foo; .weak_definition _foo; _foo:" | llvm-mc -filetype=obj -triple=x86_64-apple-darwin -o %t/foo.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o

# RUN: lld -flavor darwinnew -force_load %t/foo.a %t/foo.o %t/test.o -o %t/test-force-load-first
# FORCE-LOAD-FIRST: __TEXT,archive _foo
# RUN: llvm-objdump --syms %t/test-force-load-first | FileCheck %s --check-prefix=FORCE-LOAD-FIRST

# RUN: lld -flavor darwinnew %t/foo.o -force_load %t/foo.a %t/test.o -o %t/test-force-load-second
# RUN: llvm-objdump --syms %t/test-force-load-second | FileCheck %s --check-prefix=FORCE-LOAD-SECOND
# FORCE-LOAD-SECOND: __TEXT,obj _foo

.globl _main
_main:
ret
1 change: 1 addition & 0 deletions lld/test/MachO/invalid/bad-archive.s
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o

# RUN: not lld -flavor darwinnew %t.o %t.a -o /dev/null 2>&1 | FileCheck -DFILE=%t.a %s
# RUN: not lld -flavor darwinnew %t.o -force_load %t.a -o /dev/null 2>&1 | FileCheck -DFILE=%t.a %s
# CHECK: error: [[FILE]]: failed to parse archive: truncated or malformed archive (remaining size of archive too small for next archive member header at offset 8)

.global _main
Expand Down
23 changes: 23 additions & 0 deletions lld/test/MachO/invalid/bad-got-to-dylib-tlv-reference.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# REQUIRES: x86
# RUN: split-file %s %t

# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/libtlv.s -o %t/libtlv.o
# RUN: lld -flavor darwinnew -dylib -install_name @executable_path/libtlv.dylib \
# RUN: -Z -L%S/../Inputs/MacOSX.sdk/usr/lib -lSystem -o %t/libtlv.dylib %t/libtlv.o

# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
# RUN: not lld -flavor darwinnew -Z -L%S/../Inputs/MacOSX.sdk/usr/lib -lSystem -L%t -ltlv -o /dev/null %t/test.o 2>&1 | FileCheck %s -DFILE=%t/test.o

# CHECK: error: found GOT relocation referencing thread-local variable in [[FILE]]:(__text)

#--- libtlv.s
.section __DATA,__thread_vars,thread_local_variables
.globl _foo
_foo:

#--- test.s
.text
.globl _main
_main:
movq _foo@GOTPCREL(%rip), %rax
ret
14 changes: 14 additions & 0 deletions lld/test/MachO/invalid/bad-got-to-tlv-reference.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
# RUN: not lld -flavor darwinnew -o /dev/null %t.o 2>&1 | FileCheck %s -DFILE=%t.o

# CHECK: error: found GOT relocation referencing thread-local variable in [[FILE]]:(__text)

.text
.globl _main
_main:
movq _foo@GOTPCREL(%rip), %rax
ret

.section __DATA,__thread_vars,thread_local_variables
_foo:
14 changes: 14 additions & 0 deletions lld/test/MachO/invalid/bad-tlv-relocation.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
# RUN: not lld -flavor darwinnew -o /dev/null %t.o 2>&1 | FileCheck %s -DFILE=%t.o

# CHECK: error: found X86_64_RELOC_TLV referencing a non-thread-local variable in [[FILE]]:(__text)

.text
.globl _main
_main:
leaq _foo@TLVP(%rip), %rax
ret

.data
_foo:
12 changes: 12 additions & 0 deletions lld/test/MachO/invalid/invalid-stub.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# REQUIRES: x86
# RUN: mkdir -p %t
# RUN: echo "--- !tapi-tbd-v3" > %t/libinvalidYAML.tbd
# RUN: echo "invalid YAML" >> %t/libinvalidYAML.tbd
# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %s -o %t/test.o
# RUN: not lld -flavor darwinnew -Z -L%t -linvalidYAML %t/test.o -o %t/test -Z 2>&1 | FileCheck %s -DDIR=%t

# CHECK: could not load TAPI file at [[DIR]]/libinvalidYAML.tbd: malformed file

.globl _main
_main:
ret
16 changes: 16 additions & 0 deletions lld/test/MachO/rpath.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
# RUN: lld -flavor darwinnew -o %t %t.o

## Check that -rpath generates LC_RPATH.
# RUN: lld -flavor darwinnew -o %t %t.o -rpath /some/rpath
# RUN: llvm-objdump --macho --all-headers %t | FileCheck %s
# CHECK: LC_RPATH
# CHECK-NEXT: cmdsize 24
# CHECK-NEXT: path /some/rpath

.text
.global _main
_main:
mov $0, %rax
ret
14 changes: 12 additions & 2 deletions lld/test/MachO/stub-link.s
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@
# RUN: mkdir -p %t
#
# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %s -o %t/test.o
# RUN: lld -flavor darwinnew -o %t/test -Z -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem %t/test.o
# RUN: lld -flavor darwinnew -o %t/test -syslibroot %S/Inputs/MacOSX.sdk -lSystem -framework CoreFoundation %t/test.o
#
# RUN: llvm-objdump --bind --no-show-raw-insn -d -r %t/test | FileCheck %s

# CHECK: Disassembly of section __TEXT,__text:
# CHECK: movq {{.*}} # [[ADDR:[0-9a-f]+]]

# CHECK: Bind table:
# CHECK: __DATA_CONST __got 0x[[ADDR]] pointer 0 libSystem ___nan
# CHECK-DAG: __DATA_CONST __got 0x[[ADDR]] pointer 0 libSystem ___nan
# CHECK-DAG: __DATA __data {{.*}} pointer 0 CoreFoundation _OBJC_CLASS_$_NSObject
# CHECK-DAG: __DATA __data {{.*}} pointer 0 CoreFoundation _OBJC_METACLASS_$_NSObject
# CHECK-DAG: __DATA __data {{.*}} pointer 0 CoreFoundation _OBJC_IVAR_$_NSConstantArray._count
# CHECK-DAG: __DATA __data {{.*}} pointer 0 CoreFoundation _OBJC_EHTYPE_$_NSException

.section __TEXT,__text
.global _main

_main:
movq ___nan@GOTPCREL(%rip), %rax
ret

.data
.quad _OBJC_CLASS_$_NSObject
.quad _OBJC_METACLASS_$_NSObject
.quad _OBJC_IVAR_$_NSConstantArray._count
.quad _OBJC_EHTYPE_$_NSException
40 changes: 40 additions & 0 deletions lld/test/MachO/tlv-dylib.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# REQUIRES: x86
# RUN: split-file %s %t

# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/libtlv.s -o %t/libtlv.o
# RUN: lld -flavor darwinnew -dylib -install_name @executable_path/libtlv.dylib \
# RUN: -Z -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem -o %t/libtlv.dylib %t/libtlv.o
# RUN: llvm-objdump --exports-trie -d --no-show-raw-insn %t/libtlv.dylib | FileCheck %s --check-prefix=DYLIB
# DYLIB-DAG: _foo [per-thread]
# DYLIB-DAG: _bar [per-thread]

# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
# RUN: lld -flavor darwinnew -Z -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem -L%t -ltlv %t/test.o -o %t/test
# RUN: llvm-objdump --bind -d --no-show-raw-insn %t/test | FileCheck %s

# CHECK: movq [[#]](%rip), %rax # [[#%x, FOO:]]
# CHECK-NEXT: movq [[#]](%rip), %rax # [[#%x, BAR:]]
# CHECK-NEXT: movq [[#]](%rip), %rax # [[#%x, BAZ:]]

# CHECK-LABEL: Bind table:
# CHECK-DAG: __DATA __thread_ptrs 0x{{0*}}[[#%x, FOO]] pointer 0 libtlv _foo
# CHECK-DAG: __DATA __thread_ptrs 0x{{0*}}[[#%x, BAR]] pointer 0 libtlv _bar
# CHECK-DAG: __DATA_CONST __got 0x{{0*}}[[#%x, BAZ]] pointer 0 libtlv _baz

#--- libtlv.s
.section __DATA,__thread_vars,thread_local_variables
.globl _foo, _bar, _baz
_foo:
_bar:

.text
_baz:

#--- test.s
.globl _main
_main:
mov _foo@TLVP(%rip), %rax
mov _bar@TLVP(%rip), %rax
## Add a GOT entry to make sure we don't mix it up with TLVs
mov _baz@GOTPCREL(%rip), %rax
ret