98 changes: 87 additions & 11 deletions lld/MachO/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"

using namespace llvm;
Expand Down Expand Up @@ -52,12 +53,16 @@ class Writer {
uint64_t fileOff = 0;
MachHeaderSection *headerSection = nullptr;
BindingSection *bindingSection = nullptr;
ExportSection *exportSection = nullptr;
StringTableSection *stringTableSection = nullptr;
SymtabSection *symtabSection = nullptr;
};

// LC_DYLD_INFO_ONLY stores the offsets of symbol import/export information.
class LCDyldInfo : public LoadCommand {
public:
LCDyldInfo(BindingSection *bindingSection) : bindingSection(bindingSection) {}
LCDyldInfo(BindingSection *bindingSection, ExportSection *exportSection)
: bindingSection(bindingSection), exportSection(exportSection) {}

uint32_t getSize() const override { return sizeof(dyld_info_command); }

Expand All @@ -69,13 +74,14 @@ class LCDyldInfo : public LoadCommand {
c->bind_off = bindingSection->getFileOffset();
c->bind_size = bindingSection->getFileSize();
}
c->export_off = exportOff;
c->export_size = exportSize;
if (exportSection->isNeeded()) {
c->export_off = exportSection->getFileOffset();
c->export_size = exportSection->getFileSize();
}
}

BindingSection *bindingSection;
uint64_t exportOff = 0;
uint64_t exportSize = 0;
ExportSection *exportSection;
};

class LCDysymtab : public LoadCommand {
Expand Down Expand Up @@ -163,13 +169,23 @@ class LCMain : public LoadCommand {

class LCSymtab : public LoadCommand {
public:
LCSymtab(SymtabSection *symtabSection, StringTableSection *stringTableSection)
: symtabSection(symtabSection), stringTableSection(stringTableSection) {}

uint32_t getSize() const override { return sizeof(symtab_command); }

void writeTo(uint8_t *buf) const override {
auto *c = reinterpret_cast<symtab_command *>(buf);
c->cmd = LC_SYMTAB;
c->cmdsize = getSize();
c->symoff = symtabSection->getFileOffset();
c->nsyms = symtabSection->getNumSymbols();
c->stroff = stringTableSection->getFileOffset();
c->strsize = stringTableSection->getFileSize();
}

SymtabSection *symtabSection = nullptr;
StringTableSection *stringTableSection = nullptr;
};

class LCLoadDylib : public LoadCommand {
Expand All @@ -196,6 +212,30 @@ class LCLoadDylib : public LoadCommand {
StringRef path;
};

class LCIdDylib : public LoadCommand {
public:
LCIdDylib(StringRef name) : name(name) {}

uint32_t getSize() const override {
return alignTo(sizeof(dylib_command) + name.size() + 1, 8);
}

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

c->cmd = LC_ID_DYLIB;
c->cmdsize = getSize();
c->dylib.name = sizeof(dylib_command);

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

private:
StringRef name;
};

class LCLoadDylinker : public LoadCommand {
public:
uint32_t getSize() const override {
Expand Down Expand Up @@ -238,7 +278,13 @@ class SectionComparator {
{defaultPosition, {}},
// Make sure __LINKEDIT is the last segment (i.e. all its hidden
// sections must be ordered after other sections).
{segment_names::linkEdit, {section_names::binding}},
{segment_names::linkEdit,
{
section_names::binding,
section_names::export_,
section_names::symbolTable,
section_names::stringTable,
}},
};

for (uint32_t i = 0, n = ordering.size(); i < n; ++i) {
Expand Down Expand Up @@ -292,11 +338,23 @@ void Writer::scanRelocations() {
}

void Writer::createLoadCommands() {
headerSection->addLoadCommand(make<LCDyldInfo>(bindingSection));
headerSection->addLoadCommand(make<LCLoadDylinker>());
headerSection->addLoadCommand(make<LCSymtab>());
headerSection->addLoadCommand(
make<LCDyldInfo>(bindingSection, exportSection));
headerSection->addLoadCommand(
make<LCSymtab>(symtabSection, stringTableSection));
headerSection->addLoadCommand(make<LCDysymtab>());
headerSection->addLoadCommand(make<LCMain>());

switch (config->outputType) {
case MH_EXECUTE:
headerSection->addLoadCommand(make<LCMain>());
headerSection->addLoadCommand(make<LCLoadDylinker>());
break;
case MH_DYLIB:
headerSection->addLoadCommand(make<LCIdDylib>(config->installName));
break;
default:
llvm_unreachable("unhandled output file type");
}

uint8_t segIndex = 0;
for (OutputSegment *seg : outputSegments) {
Expand All @@ -323,7 +381,19 @@ void Writer::createLoadCommands() {
void Writer::createHiddenSections() {
headerSection = createInputSection<MachHeaderSection>();
bindingSection = createInputSection<BindingSection>();
createInputSection<PageZeroSection>();
stringTableSection = createInputSection<StringTableSection>();
symtabSection = createInputSection<SymtabSection>(*stringTableSection);
exportSection = createInputSection<ExportSection>();

switch (config->outputType) {
case MH_EXECUTE:
createInputSection<PageZeroSection>();
break;
case MH_DYLIB:
break;
default:
llvm_unreachable("unhandled output file type");
}
}

void Writer::sortSections() {
Expand Down Expand Up @@ -351,6 +421,9 @@ void Writer::assignAddresses(OutputSegment *seg) {
ArrayRef<InputSection *> sections = p.second;
for (InputSection *isec : sections) {
addr = alignTo(addr, isec->align);
// We must align the file offsets too to avoid misaligned writes of
// structs.
fileOff = alignTo(fileOff, isec->align);
isec->addr = addr;
addr += isec->getSize();
fileOff += isec->getFileSize();
Expand All @@ -376,6 +449,7 @@ void Writer::writeSections() {
uint64_t fileOff = seg->fileOff;
for (auto &sect : seg->getSections()) {
for (InputSection *isec : sect.second) {
fileOff = alignTo(fileOff, isec->align);
isec->writeTo(buf + fileOff);
fileOff += isec->getFileSize();
}
Expand Down Expand Up @@ -405,6 +479,8 @@ void Writer::run() {

// Fill __LINKEDIT contents.
bindingSection->finalizeContents();
exportSection->finalizeContents();
symtabSection->finalizeContents();

// Now that __LINKEDIT is filled out, do a proper calculation of its
// addresses and offsets. We don't have to recalculate the other segments
Expand Down
175 changes: 0 additions & 175 deletions lld/test/MachO/Inputs/goodbye-dylib.yaml

This file was deleted.

169 changes: 0 additions & 169 deletions lld/test/MachO/Inputs/hello-dylib.yaml

This file was deleted.

5 changes: 5 additions & 0 deletions lld/test/MachO/Inputs/libgoodbye.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.section __TEXT,__cstring
.globl _goodbye_world

_goodbye_world:
.asciz "Goodbye world!\n"
5 changes: 5 additions & 0 deletions lld/test/MachO/Inputs/libhello.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.section __TEXT,__cstring
.globl _hello_world

_hello_world:
.asciz "Hello world!\n"
35 changes: 35 additions & 0 deletions lld/test/MachO/dylib.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o

# RUN: lld -flavor darwinnew -dylib -install_name @executable_path/libfoo.dylib \
# RUN: %t.o -o %t.dylib
# RUN: llvm-objdump --macho --dylib-id %t.dylib | FileCheck %s
# CHECK: @executable_path/libfoo.dylib

## If we are building a dylib, we shouldn't error out even if we are passed
## a flag for a missing entry symbol (since dylibs don't have entry symbols).
## Also check that we come up with the right install name if one isn't
## specified.
# RUN: lld -flavor darwinnew -dylib %t.o -o %t.defaultInstallName.dylib -e missing_entry
# RUN: obj2yaml %t.defaultInstallName.dylib | FileCheck %s -DOUTPUT=%t.defaultInstallName.dylib --check-prefix=DEFAULT-INSTALL-NAME
# DEFAULT-INSTALL-NAME: [[OUTPUT]]

## Check for the absence of load commands / segments that should not be in a
## dylib.
# RUN: llvm-objdump --macho --all-headers %t.dylib | FileCheck %s --check-prefix=NCHECK
# NCHECK-NOT: cmd LC_LOAD_DYLINKER
# NCHECK-NOT: cmd LC_MAIN
# NCHECK-NOT: segname __PAGEZERO

# RUN: llvm-objdump --syms --exports-trie %t.dylib | \
# RUN: FileCheck %s --check-prefix=EXPORTS
# EXPORTS-LABEL: SYMBOL TABLE:
# EXPORTS: [[#%x, HELLO_WORLD_ADDR:]] {{.*}} _hello_world
# EXPORTS-LABEL: Exports trie:
# EXPORTS: 0x{{0*}}[[#%X, HELLO_WORLD_ADDR]] _hello_world

.section __TEXT,__cstring
.globl _hello_world

_hello_world:
.asciz "Hello world!\n"
10 changes: 8 additions & 2 deletions lld/test/MachO/dylink.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# REQUIRES: x86
# RUN: mkdir -p %t
# RUN: yaml2obj %p/Inputs/hello-dylib.yaml -o %t/libhello.dylib
# RUN: yaml2obj %p/Inputs/goodbye-dylib.yaml -o %t/libgoodbye.dylib
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libhello.s \
# RUN: -o %t/libhello.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libgoodbye.s \
# RUN: -o %t/libgoodbye.o
# RUN: lld -flavor darwinnew -dylib -install_name \
# RUN: @executable_path/libhello.dylib %t/libhello.o -o %t/libhello.dylib
# RUN: lld -flavor darwinnew -dylib -install_name \
# RUN: @executable_path/libgoodbye.dylib %t/libgoodbye.o -o %t/libgoodbye.dylib
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/dylink.o
# RUN: lld -flavor darwinnew -o %t/dylink -Z -L%t -lhello -lgoodbye %t/dylink.o
# RUN: llvm-objdump --bind -d %t/dylink | FileCheck %s
Expand Down
17 changes: 11 additions & 6 deletions lld/test/MachO/load-commands.s
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
# RUN: lld -flavor darwinnew -o %t %t.o
# RUN: obj2yaml %t | FileCheck %s

# Check for the presence of a couple of load commands that are essential for
# a working binary.
## Check for the presence of load commands that are essential for a working
## executable.
# RUN: llvm-objdump --macho --all-headers %t | FileCheck %s
# CHECK-DAG: cmd LC_DYLD_INFO_ONLY
# CHECK-DAG: cmd LC_SYMTAB
# CHECK-DAG: cmd LC_DYSYMTAB
# CHECK-DAG: cmd LC_MAIN
# CHECK-DAG: cmd LC_LOAD_DYLINKER

# CHECK-DAG: cmd: LC_DYLD_INFO_ONLY
# CHECK-DAG: cmd: LC_SYMTAB
# CHECK-DAG: cmd: LC_DYSYMTAB
## Check for the absence of load commands that should not be in an executable.
# RUN: llvm-objdump --macho --all-headers %t | FileCheck %s --check-prefix=NCHECK
# NCHECK-NOT: cmd: LC_ID_DYLIB

.text
.global _main
Expand Down
23 changes: 23 additions & 0 deletions lld/test/MachO/symtab.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
# RUN: lld -flavor darwinnew -o %t %t.o
# RUN: llvm-readobj -symbols %t | FileCheck %s

# CHECK: Symbols [
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: _main
# CHECK-NEXT: Extern
# CHECK-NEXT: Type: Section (0xE)
# CHECK-NEXT: Section: __text (0x1)
# CHECK-NEXT: RefType:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Value:
# CHECK-NEXT: }
# CHECK-NEXT: ]

.global _main

_main:
mov $0, %rax
ret