Skip to content

Commit

Permalink
[lld] Initial commit for new Mach-O backend
Browse files Browse the repository at this point in the history
Summary:
This is the first commit for the new Mach-O backend, designed to roughly
follow the architecture of the existing ELF and COFF backends, and
building off work that @RuiU and @pcc did in a branch a while back. Note
that this is a very stripped-down commit with the bare minimum of
functionality for ease of review. We'll be following up with more diffs
soon.

Currently, we're able to generate a simple "Hello World!" executable
that runs on OS X Catalina (and possibly on earlier OS X versions; I
haven't tested them). (This executable can be obtained by compiling
`test/MachO/relocations.s`.) We're mocking out a few load commands to
achieve this -- for example, we can't load dynamic libraries, but
Catalina requires binaries to be linked against `dyld`, so we hardcode
the emission of a `LC_LOAD_DYLIB` command. Other mocked out load
commands include LC_SYMTAB and LC_DYSYMTAB.

Differential Revision: https://reviews.llvm.org/D75382
  • Loading branch information
int3 authored and smeenai committed Mar 31, 2020
1 parent 6d20937 commit 03f43b3
Show file tree
Hide file tree
Showing 36 changed files with 1,669 additions and 4 deletions.
1 change: 1 addition & 0 deletions lld/CMakeLists.txt
Expand Up @@ -194,5 +194,6 @@ endif()
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
add_subdirectory(MachO)
add_subdirectory(MinGW)
add_subdirectory(wasm)
60 changes: 60 additions & 0 deletions lld/MachO/Arch/X86_64.cpp
@@ -0,0 +1,60 @@
//===- X86_64.cpp ---------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Support/Endian.h"

using namespace llvm::MachO;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::macho;

namespace {

struct X86_64 : TargetInfo {
X86_64();
uint64_t getImplicitAddend(const uint8_t *loc, uint8_t type) const override;
void relocateOne(uint8_t *loc, uint8_t type, uint64_t val) const override;
};

X86_64::X86_64() {
cpuType = CPU_TYPE_X86_64;
cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
}

uint64_t X86_64::getImplicitAddend(const uint8_t *loc, uint8_t type) const {
switch (type) {
case X86_64_RELOC_SIGNED:
return read32le(loc);
default:
error("TODO: Unhandled relocation type " + std::to_string(type));
return 0;
}
}

void X86_64::relocateOne(uint8_t *loc, uint8_t type, uint64_t val) const {
switch (type) {
case X86_64_RELOC_SIGNED:
// This type is only used for pc-relative relocations, so offset by 4 since
// the RIP has advanced by 4 at this point.
write32le(loc, val - 4);
break;
default:
llvm_unreachable(
"getImplicitAddend should have flagged all unhandled relocation types");
}
}

} // namespace

TargetInfo *macho::createX86_64TargetInfo() {
static X86_64 t;
return &t;
}
34 changes: 34 additions & 0 deletions lld/MachO/CMakeLists.txt
@@ -0,0 +1,34 @@
set(LLVM_TARGET_DEFINITIONS Options.td)
tablegen(LLVM Options.inc -gen-opt-parser-defs)
add_public_tablegen_target(MachOOptionsTableGen)

add_lld_library(lldMachO2
Arch/X86_64.cpp
Driver.cpp
InputFiles.cpp
InputSection.cpp
OutputSegment.cpp
SymbolTable.cpp
Symbols.cpp
Target.cpp
Writer.cpp

LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
BinaryFormat
Core
DebugInfoDWARF
LTO
MC
Object
Option
Support

LINK_LIBS
lldCommon
${LLVM_PTHREAD_LIB}

DEPENDS
MachOOptionsTableGen
${tablegen_deps}
)
29 changes: 29 additions & 0 deletions lld/MachO/Config.h
@@ -0,0 +1,29 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLD_MACHO_CONFIG_H
#define LLD_MACHO_CONFIG_H

#include "llvm/ADT/StringRef.h"

namespace lld {
namespace macho {

class Symbol;

struct Configuration {
llvm::StringRef outputFile;
Symbol *entry;
};

extern Configuration *config;

} // namespace macho
} // namespace lld

#endif
150 changes: 150 additions & 0 deletions lld/MachO/Driver.cpp
@@ -0,0 +1,150 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "Driver.h"
#include "Config.h"
#include "InputFiles.h"
#include "OutputSegment.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
#include "Writer.h"

#include "lld/Common/Args.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/MemoryBuffer.h"

using namespace llvm;
using namespace llvm::MachO;
using namespace llvm::sys;
using namespace lld;
using namespace lld::macho;

Configuration *lld::macho::config;

// Create prefix string literals used in Options.td
#define PREFIX(NAME, VALUE) const char *NAME[] = VALUE;
#include "Options.inc"
#undef PREFIX

// Create table mapping all options defined in Options.td
static const opt::OptTable::Info optInfo[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
#include "Options.inc"
#undef OPTION
};

MachOOptTable::MachOOptTable() : OptTable(optInfo) {}

opt::InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) {
// Make InputArgList from string vectors.
unsigned missingIndex;
unsigned missingCount;
SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());

opt::InputArgList args = ParseArgs(vec, missingIndex, missingCount);

if (missingCount)
error(Twine(args.getArgString(missingIndex)) + ": missing argument");

for (opt::Arg *arg : args.filtered(OPT_UNKNOWN))
error("unknown argument: " + arg->getSpelling());
return args;
}

static TargetInfo *createTargetInfo(opt::InputArgList &args) {
StringRef s = args.getLastArgValue(OPT_arch, "x86_64");
if (s != "x86_64")
error("missing or unsupported -arch " + s);
return createX86_64TargetInfo();
}

static void addFile(StringRef path) {
Optional<MemoryBufferRef> buffer = readFile(path);
if (!buffer)
return;
MemoryBufferRef mbref = *buffer;

switch (identify_magic(mbref.getBuffer())) {
case file_magic::macho_object:
inputFiles.push_back(make<ObjFile>(mbref));
break;
default:
error(path + ": unhandled file type");
}
}

bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
raw_ostream &stdoutOS, raw_ostream &stderrOS) {
lld::stdoutOS = &stdoutOS;
lld::stderrOS = &stderrOS;

MachOOptTable parser;
opt::InputArgList args = parser.parse(argsArr.slice(1));

if (args.hasArg(OPT_v)) {
message(getLLDVersion());
freeArena();
return !errorCount();
}

config = make<Configuration>();
symtab = make<SymbolTable>();
target = createTargetInfo(args);

config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"));
config->outputFile = args.getLastArgValue(OPT_o, "a.out");

getOrCreateOutputSegment("__TEXT", VM_PROT_READ | VM_PROT_EXECUTE);
getOrCreateOutputSegment("__DATA", VM_PROT_READ | VM_PROT_WRITE);

for (opt::Arg *arg : args) {
switch (arg->getOption().getID()) {
case OPT_INPUT:
addFile(arg->getValue());
break;
}
}

if (!isa<Defined>(config->entry)) {
error("undefined symbol: " + config->entry->getName());
return false;
}

// Initialize InputSections.
for (InputFile *file : inputFiles)
for (InputSection *sec : file->sections)
inputSections.push_back(sec);

// Add input sections to output segments.
for (InputSection *isec : inputSections) {
OutputSegment *os =
getOrCreateOutputSegment(isec->segname, VM_PROT_READ | VM_PROT_WRITE);
os->sections[isec->name].push_back(isec);
}

// Write to an output file.
writeResult();

if (canExitEarly)
exitLld(errorCount() ? 1 : 0);

freeArena();
return !errorCount();
}
35 changes: 35 additions & 0 deletions lld/MachO/Driver.h
@@ -0,0 +1,35 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLD_MACHO_DRIVER_H
#define LLD_MACHO_DRIVER_H

#include "lld/Common/LLVM.h"
#include "llvm/Option/OptTable.h"

namespace lld {
namespace macho {

class MachOOptTable : public llvm::opt::OptTable {
public:
MachOOptTable();
llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
};

// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
#include "Options.inc"
#undef OPTION
};

} // namespace macho
} // namespace lld

#endif

0 comments on commit 03f43b3

Please sign in to comment.