From 5549a005f19227285a763e4342f50a767fcee48e Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Tue, 26 Oct 2021 16:57:20 +0900 Subject: [PATCH] [Mach-O] Create GOT entries for external references --- macho/input-sections.cc | 41 +++++++++++++++++++++++++++++------------ macho/main.cc | 13 +++++++++---- macho/mold.h | 8 +++++++- test/macho/hello3.sh | 21 +++++++++++++++++++++ test/macho/hello4.sh | 23 +++++++++++++++++++++++ 5 files changed, 89 insertions(+), 17 deletions(-) create mode 100755 test/macho/hello3.sh create mode 100755 test/macho/hello4.sh diff --git a/macho/input-sections.cc b/macho/input-sections.cc index 51fdc83f..a58d1273 100644 --- a/macho/input-sections.cc +++ b/macho/input-sections.cc @@ -43,8 +43,11 @@ static Relocation read_reloc(Context &ctx, ObjectFile &file, else unreachable(); + bool is_gotref = (r.type == X86_64_RELOC_GOT_LOAD || r.type == X86_64_RELOC_GOT); + if (r.is_extern) - return {r.offset, (bool)r.is_pcrel, addend, file.syms[r.idx], nullptr}; + return {r.offset, (bool)r.is_pcrel, is_gotref, addend, file.syms[r.idx], + nullptr}; u32 addr; if (r.is_pcrel) { @@ -58,7 +61,9 @@ static Relocation read_reloc(Context &ctx, ObjectFile &file, Subsection *target = file.sections[r.idx - 1]->find_subsection(ctx, addr); if (!target) Fatal(ctx) << file << ": bad relocation: " << r.offset; - return {r.offset, (bool)r.is_pcrel, addr - target->input_addr, nullptr, target}; + + return {r.offset, (bool)r.is_pcrel, is_gotref, addr - target->input_addr, + nullptr, target}; } void InputSection::parse_relocations(Context &ctx) { @@ -88,8 +93,12 @@ void InputSection::parse_relocations(Context &ctx) { void InputSection::scan_relocations(Context &ctx) { for (Relocation &rel : rels) { Symbol *sym = rel.sym; - if (sym && sym->file && sym->file->is_dylib) - sym->needs_stub = true; + if (sym && sym->file && sym->file->is_dylib) { + if (rel.is_gotref) + sym->flags |= NEEDS_GOT; + else + sym->flags |= NEEDS_STUB; + } } } @@ -97,15 +106,23 @@ void Subsection::apply_reloc(Context &ctx, u8 *buf) { for (const Relocation &rel : std::span(isec.rels).subspan(rel_offset, nrels)) { u32 *loc = (u32 *)(buf + rel.offset); - if (rel.sym) { - *loc = rel.sym->get_addr(ctx) + rel.addend; - } else { - *loc = rel.subsec->isec.osec->hdr.addr + rel.subsec->output_offset + - rel.addend; - } +#define S (rel.sym ? rel.sym->get_addr(ctx) : \ + rel.subsec->isec.osec->hdr.addr + rel.subsec->output_offset) +#define A rel.addend +#define P (isec.osec->hdr.addr + output_offset + rel.offset) +#define G rel.sym->get_got_addr(ctx) - if (rel.is_pcrel) - *loc = *loc - isec.osec->hdr.addr - output_offset - rel.offset - 4; + if (rel.is_gotref) + *loc = G - P - 4; + else if (rel.is_pcrel) + *loc = S + A - P - 4; + else + *loc = S + A; + +#undef S +#undef A +#undef P +#undef G } } diff --git a/macho/main.cc b/macho/main.cc index f748e779..00f7c0c8 100644 --- a/macho/main.cc +++ b/macho/main.cc @@ -90,10 +90,15 @@ static void create_synthetic_chunks(Context &ctx) { static void export_symbols(Context &ctx) { ctx.got.add(ctx, intern(ctx, "dyld_stub_binder")); - for (DylibFile *dylib : ctx.dylibs) - for (Symbol *sym : dylib->syms) - if (sym->file == dylib && sym->needs_stub) - ctx.stubs.add(ctx, sym); + for (DylibFile *dylib : ctx.dylibs) { + for (Symbol *sym : dylib->syms) { + if (sym->file == dylib) + if (sym->flags & NEEDS_STUB) + ctx.stubs.add(ctx, sym); + if (sym->flags & NEEDS_GOT) + ctx.got.add(ctx, sym); + } + } } static i64 assign_offsets(Context &ctx) { diff --git a/macho/mold.h b/macho/mold.h index 39f96f52..1e14a04d 100644 --- a/macho/mold.h +++ b/macho/mold.h @@ -29,6 +29,7 @@ struct Symbol; struct Relocation { u32 offset = 0; bool is_pcrel = false; + bool is_gotref = false; i64 addend = 0; Symbol *sym = nullptr; Subsection *subsec = nullptr; @@ -141,6 +142,11 @@ public: // Symbol // +enum { + NEEDS_GOT = 1 << 0, + NEEDS_STUB = 1 << 1, +}; + struct Symbol { Symbol() = default; Symbol(std::string_view name) : name(name) {} @@ -154,7 +160,7 @@ struct Symbol { i32 stub_idx = -1; i32 got_idx = -1; tbb::spin_mutex mu; - std::atomic_bool needs_stub = false; + std::atomic_uint8_t flags = 0; u8 is_extern : 1 = false; u8 referenced_dynamically : 1 = false; diff --git a/test/macho/hello3.sh b/test/macho/hello3.sh new file mode 100755 index 00000000..ac7987ec --- /dev/null +++ b/test/macho/hello3.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e +cd $(dirname $0) +mold=`pwd`/../../ld64.mold +echo -n "Testing $(basename -s .sh $0) ... " +t=$(pwd)/../../out/test/macho/$(basename -s .sh $0) +mkdir -p $t + +cat < + +int main() { + printf("Hello"); + fprintf(stdout, " world\n"); +} +EOF + +clang -fuse-ld=$mold -o $t/exe $t/a.o +$t/exe | grep -q 'Hello world' + +echo OK diff --git a/test/macho/hello4.sh b/test/macho/hello4.sh new file mode 100755 index 00000000..0fb36b65 --- /dev/null +++ b/test/macho/hello4.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e +cd $(dirname $0) +mold=`pwd`/../../ld64.mold +echo -n "Testing $(basename -s .sh $0) ... " +t=$(pwd)/../../out/test/macho/$(basename -s .sh $0) +mkdir -p $t + +cat < + +int main() { + printf("Hello"); + fprintf(stdout, " world\n"); + fprintf(stderr, "Hello stderr\n"); +} +EOF + +clang -fuse-ld=$mold -o $t/exe $t/a.o +$t/exe 2> /dev/null | grep -q 'Hello world' +$t/exe 2>&1 > /dev/null | grep -q 'Hello stderr' + +echo OK