From 16921c78eaacb259bd9a3f20123db55b71a7b0ff Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Thu, 2 Dec 2021 11:35:48 +0900 Subject: [PATCH] [Mach-O] Add ARM64 stub --- macho/arch-arm64.cc | 22 ++++++ macho/arch-x86-64.cc | 165 ++++++++++++++++++++++++++++++++++++++ macho/cmdline.cc | 7 +- macho/dead-strip.cc | 1 + macho/input-sections.cc | 170 +--------------------------------------- macho/macho.h | 22 +++++- macho/main.cc | 7 +- macho/mapfile.cc | 1 + macho/mold.h | 12 +++ macho/object-file.cc | 1 + macho/output-chunks.cc | 1 + macho/output-file.cc | 1 + macho/tapi.cc | 1 + 13 files changed, 236 insertions(+), 175 deletions(-) create mode 100644 macho/arch-arm64.cc create mode 100644 macho/arch-x86-64.cc diff --git a/macho/arch-arm64.cc b/macho/arch-arm64.cc new file mode 100644 index 00000000..c41ccb3e --- /dev/null +++ b/macho/arch-arm64.cc @@ -0,0 +1,22 @@ +#include "mold.h" + +#include + +namespace mold::macho { + +template <> +Relocation +read_reloc(Context &ctx, ObjectFile &file, + const MachSection &hdr, MachRel r) { + return {}; +} + +template <> +void Subsection::scan_relocations(Context &ctx) { +} + +template <> +void Subsection::apply_reloc(Context &ctx, u8 *buf) { +} + +} // namespace mold::macho diff --git a/macho/arch-x86-64.cc b/macho/arch-x86-64.cc new file mode 100644 index 00000000..08cf2b1f --- /dev/null +++ b/macho/arch-x86-64.cc @@ -0,0 +1,165 @@ +#include "mold.h" + +#include + +namespace mold::macho { + +static i64 get_reloc_addend(u32 type) { + switch (type) { + case X86_64_RELOC_SIGNED_1: + return 1; + case X86_64_RELOC_SIGNED_2: + return 2; + case X86_64_RELOC_SIGNED_4: + return 4; + default: + return 0; + } +} + +static i64 read_addend(u8 *buf, const MachRel &r) { + switch (r.p2size) { + case 2: + return *(i32 *)(buf + r.offset) + get_reloc_addend(r.type); + case 3: + return *(i64 *)(buf + r.offset) + get_reloc_addend(r.type); + default: + unreachable(); + } +} + +template <> +Relocation +read_reloc(Context &ctx, ObjectFile &file, + const MachSection &hdr, MachRel r) { + if (r.p2size != 2 && r.p2size != 3) + Fatal(ctx) << file << ": invalid r.p2size: " << (u32)r.p2size; + + if (r.is_pcrel) { + if (r.p2size != 2) + Fatal(ctx) << file << ": invalid PC-relative reloc: " << r.offset; + } else { + if (r.p2size != 3) + Fatal(ctx) << file << ": invalid non-PC-relative reloc: " << r.offset; + } + + u8 *buf = (u8 *)file.mf->data + hdr.offset; + Relocation rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel}; + i64 addend = read_addend(buf, r); + + if (r.is_extern) { + rel.sym = file.syms[r.idx]; + rel.addend = addend; + return rel; + } + + u32 addr; + if (r.is_pcrel) + addr = hdr.addr + r.offset + 4 + addend; + else + addr = addend; + + Subsection *target = file.find_subsection(ctx, addr); + if (!target) + Fatal(ctx) << file << ": bad relocation: " << r.offset; + + rel.subsec = target; + rel.addend = addr - target->input_addr; + return rel; +} + +template <> +void Subsection::scan_relocations(Context &ctx) { + for (Relocation &rel : get_rels()) { + Symbol *sym = rel.sym; + if (!sym) + continue; + + switch (rel.type) { + case X86_64_RELOC_GOT_LOAD: + if (sym->file && sym->file->is_dylib) + sym->flags |= NEEDS_GOT; + break; + case X86_64_RELOC_GOT: + sym->flags |= NEEDS_GOT; + break; + case X86_64_RELOC_TLV: + if (sym->file && sym->file->is_dylib) + sym->flags |= NEEDS_THREAD_PTR; + break; + } + + if (sym->file && sym->file->is_dylib) { + sym->flags |= NEEDS_STUB; + ((DylibFile *)sym->file)->is_needed = true; + } + } +} + +template <> +void Subsection::apply_reloc(Context &ctx, u8 *buf) { + for (const Relocation &rel : get_rels()) { + if (rel.sym && !rel.sym->file) { + Error(ctx) << "undefined symbol: " << isec.file << ": " << *rel.sym; + continue; + } + + u64 val = 0; + + switch (rel.type) { + case X86_64_RELOC_UNSIGNED: + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_BRANCH: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + val = rel.sym ? rel.sym->get_addr(ctx) : rel.subsec->get_addr(ctx); + break; + case X86_64_RELOC_GOT_LOAD: + if (rel.sym->got_idx != -1) { + val = rel.sym->get_got_addr(ctx); + } else { + // Relax MOVQ into LEAQ + if (buf[rel.offset - 2] != 0x8b) + Error(ctx) << isec << ": invalid GOT_LOAD relocation"; + buf[rel.offset - 2] = 0x8d; + val = rel.sym->get_addr(ctx); + } + break; + case X86_64_RELOC_GOT: + val = rel.sym->get_got_addr(ctx); + break; + case X86_64_RELOC_TLV: + if (rel.sym->tlv_idx != -1) { + val = rel.sym->get_tlv_addr(ctx); + } else { + // Relax MOVQ into LEAQ + if (buf[rel.offset - 2] != 0x8b) + Error(ctx) << isec << ": invalid TLV relocation"; + buf[rel.offset - 2] = 0x8d; + val = rel.sym->get_addr(ctx); + } + break; + default: + Fatal(ctx) << isec << ": unknown reloc: " << (int)rel.type; + } + + val += rel.addend; + + if (rel.is_pcrel) + val -= get_addr(ctx) + rel.offset + 4 + get_reloc_addend(rel.type); + + switch (rel.p2size) { + case 2: + *(u32 *)(buf + rel.offset) = val; + break; + case 3: + *(u64 *)(buf + rel.offset) = val; + break; + default: + unreachable(); + }; + } +} + +} // namespace mold::macho diff --git a/macho/cmdline.cc b/macho/cmdline.cc index 52dc6298..c96d9ad2 100644 --- a/macho/cmdline.cc +++ b/macho/cmdline.cc @@ -179,7 +179,11 @@ void parse_nonpositional_args(Context &ctx, } else if (read_flag("-no_adhoc_codesign")) { ctx.arg.adhoc_codesign = false; } else if (read_arg("-arch")) { - if (arg != "x86_64") + if (arg == "x86_64") + ctx.arg.arch = CPU_TYPE_X86_64; + else if (arg == "arm64") + ctx.arg.arch = CPU_TYPE_ARM64; + else Fatal(ctx) << "unknown -arch: " << arg; } else if (read_flag("-color-diagnostics") || read_flag("--color-diagnostics")) { @@ -297,6 +301,7 @@ void parse_nonpositional_args(Context &ctx, #define INSTANTIATE(E) \ template void parse_nonpositional_args(Context &, std::vector &) +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho diff --git a/macho/dead-strip.cc b/macho/dead-strip.cc index b1462480..19aed4d3 100644 --- a/macho/dead-strip.cc +++ b/macho/dead-strip.cc @@ -105,6 +105,7 @@ void dead_strip(Context &ctx) { #define INSTANTIATE(E) \ template void dead_strip(Context &) +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho diff --git a/macho/input-sections.cc b/macho/input-sections.cc index b2314d81..d39ee19c 100644 --- a/macho/input-sections.cc +++ b/macho/input-sections.cc @@ -1,16 +1,7 @@ #include "mold.h" -#include - namespace mold::macho { -template -std::ostream &operator<<(std::ostream &out, const InputSection &sec) { - out << sec.file << "(" << sec.hdr.get_segname() << "," - << sec.hdr.get_sectname() << ")"; - return out; -} - template InputSection::InputSection(Context &ctx, ObjectFile &file, const MachSection &hdr) @@ -21,69 +12,6 @@ InputSection::InputSection(Context &ctx, ObjectFile &file, contents = file.mf->get_contents().substr(hdr.offset, hdr.size); } -static i64 get_reloc_addend(u32 type) { - switch (type) { - case X86_64_RELOC_SIGNED_1: - return 1; - case X86_64_RELOC_SIGNED_2: - return 2; - case X86_64_RELOC_SIGNED_4: - return 4; - default: - return 0; - } -} - -static i64 read_addend(u8 *buf, const MachRel &r) { - switch (r.p2size) { - case 2: - return *(i32 *)(buf + r.offset) + get_reloc_addend(r.type); - case 3: - return *(i64 *)(buf + r.offset) + get_reloc_addend(r.type); - default: - unreachable(); - } -} - -template -static Relocation read_reloc(Context &ctx, ObjectFile &file, - const MachSection &hdr, MachRel r) { - if (r.p2size != 2 && r.p2size != 3) - Fatal(ctx) << file << ": invalid r.p2size: " << (u32)r.p2size; - - if (r.is_pcrel) { - if (r.p2size != 2) - Fatal(ctx) << file << ": invalid PC-relative reloc: " << r.offset; - } else { - if (r.p2size != 3) - Fatal(ctx) << file << ": invalid non-PC-relative reloc: " << r.offset; - } - - u8 *buf = (u8 *)file.mf->data + hdr.offset; - Relocation rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel}; - i64 addend = read_addend(buf, r); - - if (r.is_extern) { - rel.sym = file.syms[r.idx]; - rel.addend = addend; - return rel; - } - - u32 addr; - if (r.is_pcrel) - addr = hdr.addr + r.offset + 4 + addend; - else - addr = addend; - - Subsection *target = file.find_subsection(ctx, addr); - if (!target) - Fatal(ctx) << file << ": bad relocation: " << r.offset; - - rel.subsec = target; - rel.addend = addr - target->input_addr; - return rel; -} - template void InputSection::parse_relocations(Context &ctx) { rels.reserve(hdr.nreloc); @@ -125,104 +53,10 @@ void InputSection::parse_relocations(Context &ctx) { } } -template -void Subsection::scan_relocations(Context &ctx) { - for (Relocation &rel : get_rels()) { - Symbol *sym = rel.sym; - if (!sym) - continue; - - switch (rel.type) { - case X86_64_RELOC_GOT_LOAD: - if (sym->file && sym->file->is_dylib) - sym->flags |= NEEDS_GOT; - break; - case X86_64_RELOC_GOT: - sym->flags |= NEEDS_GOT; - break; - case X86_64_RELOC_TLV: - if (sym->file && sym->file->is_dylib) - sym->flags |= NEEDS_THREAD_PTR; - break; - } - - if (sym->file && sym->file->is_dylib) { - sym->flags |= NEEDS_STUB; - ((DylibFile *)sym->file)->is_needed = true; - } - } -} - -template -void Subsection::apply_reloc(Context &ctx, u8 *buf) { - for (const Relocation &rel : get_rels()) { - if (rel.sym && !rel.sym->file) { - Error(ctx) << "undefined symbol: " << isec.file << ": " << *rel.sym; - continue; - } - - u64 val = 0; - - switch (rel.type) { - case X86_64_RELOC_UNSIGNED: - case X86_64_RELOC_SIGNED: - case X86_64_RELOC_BRANCH: - case X86_64_RELOC_SIGNED_1: - case X86_64_RELOC_SIGNED_2: - case X86_64_RELOC_SIGNED_4: - val = rel.sym ? rel.sym->get_addr(ctx) : rel.subsec->get_addr(ctx); - break; - case X86_64_RELOC_GOT_LOAD: - if (rel.sym->got_idx != -1) { - val = rel.sym->get_got_addr(ctx); - } else { - // Relax MOVQ into LEAQ - if (buf[rel.offset - 2] != 0x8b) - Error(ctx) << isec << ": invalid GOT_LOAD relocation"; - buf[rel.offset - 2] = 0x8d; - val = rel.sym->get_addr(ctx); - } - break; - case X86_64_RELOC_GOT: - val = rel.sym->get_got_addr(ctx); - break; - case X86_64_RELOC_TLV: - if (rel.sym->tlv_idx != -1) { - val = rel.sym->get_tlv_addr(ctx); - } else { - // Relax MOVQ into LEAQ - if (buf[rel.offset - 2] != 0x8b) - Error(ctx) << isec << ": invalid TLV relocation"; - buf[rel.offset - 2] = 0x8d; - val = rel.sym->get_addr(ctx); - } - break; - default: - Fatal(ctx) << isec << ": unknown reloc: " << (int)rel.type; - } - - val += rel.addend; - - if (rel.is_pcrel) - val -= get_addr(ctx) + rel.offset + 4 + get_reloc_addend(rel.type); - - switch (rel.p2size) { - case 2: - *(u32 *)(buf + rel.offset) = val; - break; - case 3: - *(u64 *)(buf + rel.offset) = val; - break; - default: - unreachable(); - }; - } -} - #define INSTANTIATE(E) \ - template class InputSection; \ - template class Subsection + template class InputSection +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho diff --git a/macho/macho.h b/macho/macho.h index 6fc33c83..f1adce42 100644 --- a/macho/macho.h +++ b/macho/macho.h @@ -168,7 +168,7 @@ static constexpr u32 S_ATTR_NO_TOC = 0x400000; static constexpr u32 S_ATTR_PURE_INSTRUCTIONS = 0x800000; static constexpr u32 CPU_TYPE_X86_64 = 0x1000007; -static constexpr u32 CPU_TYPE_ARM64 = 0x1000012; +static constexpr u32 CPU_TYPE_ARM64 = 0x100000c; static constexpr u32 CPU_SUBTYPE_X86_64_ALL = 3; static constexpr u32 CPU_SUBTYPE_ARM64_ALL = 0; @@ -306,6 +306,18 @@ static constexpr u32 TOOL_CLANG = 1; static constexpr u32 TOOL_SWIFT = 2; static constexpr u32 TOOL_LD = 3; +static constexpr u32 ARM64_RELOC_UNSIGNED = 0; +static constexpr u32 ARM64_RELOC_SUBTRACTOR = 1; +static constexpr u32 ARM64_RELOC_BRANCH26 = 2; +static constexpr u32 ARM64_RELOC_PAGE21 = 3; +static constexpr u32 ARM64_RELOC_PAGEOFF12 = 4; +static constexpr u32 ARM64_RELOC_GOT_LOAD_PAGE21 = 5; +static constexpr u32 ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6; +static constexpr u32 ARM64_RELOC_POINTER_TO_GOT = 7; +static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGE21 = 8; +static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9; +static constexpr u32 ARM64_RELOC_ADDEND = 10; + static constexpr u32 X86_64_RELOC_UNSIGNED = 0; static constexpr u32 X86_64_RELOC_SIGNED = 1; static constexpr u32 X86_64_RELOC_BRANCH = 2; @@ -657,12 +669,14 @@ struct CodeSignatureDirectory { ubig64 exec_seg_flags; }; +struct ARM64 { + static constexpr u32 cputype = CPU_TYPE_ARM64; + static constexpr u32 cpusubtype = CPU_SUBTYPE_ARM64_ALL; +}; + struct X86_64 { static constexpr u32 cputype = CPU_TYPE_X86_64; static constexpr u32 cpusubtype = CPU_SUBTYPE_X86_64_ALL; }; -struct AARCH64 { -}; - } // namespace mold::macho diff --git a/macho/main.cc b/macho/main.cc index d2d95413..cb173705 100644 --- a/macho/main.cc +++ b/macho/main.cc @@ -254,7 +254,7 @@ strip_universal_header(Context &ctx, MappedFile> *mf) { FatArch *arch = (FatArch *)(mf->data + sizeof(hdr)); for (i64 i = 0; i < hdr.nfat_arch; i++) - if (arch[i].cputype == CPU_TYPE_X86_64) + if (arch[i].cputype == E::cputype) return mf->slice(ctx, mf->name, arch[i].offset, arch[i].size); Fatal(ctx) << mf->name << ": fat file contains no matching file"; } @@ -358,6 +358,9 @@ static int do_main(int argc, char **argv) { std::vector file_args; parse_nonpositional_args(ctx, file_args); + if (ctx.arg.arch == CPU_TYPE_X86_64) + return do_main(argc, argv); + read_input_files(ctx, file_args); i64 priority = 1; @@ -453,7 +456,7 @@ static int do_main(int argc, char **argv) { } int main(int argc, char **argv) { - return do_main(argc, argv); + return do_main(argc, argv); } } diff --git a/macho/mapfile.cc b/macho/mapfile.cc index 9abed894..ba7e6416 100644 --- a/macho/mapfile.cc +++ b/macho/mapfile.cc @@ -74,6 +74,7 @@ void print_map(Context &ctx) { #define INSTANTIATE(E) \ template void print_map(Context &) +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho diff --git a/macho/mold.h b/macho/mold.h index 271f85d5..cf7a1886 100644 --- a/macho/mold.h +++ b/macho/mold.h @@ -191,6 +191,10 @@ public: std::atomic_bool is_alive = false; }; +template +Relocation read_reloc(Context &ctx, ObjectFile &file, + const MachSection &hdr, MachRel r); + // // Symbol // @@ -781,6 +785,7 @@ struct Context { bool dynamic = true; bool fatal_warnings = false; bool trace = false; + i64 arch = CPU_TYPE_ARM64; i64 headerpad = 256; i64 pagezero_size = 0; i64 platform = PLATFORM_MACOS; @@ -857,6 +862,13 @@ int main(int argc, char **argv); // Inline functions // +template +std::ostream &operator<<(std::ostream &out, const InputSection &sec) { + out << sec.file << "(" << sec.hdr.get_segname() << "," + << sec.hdr.get_sectname() << ")"; + return out; +} + template u64 Subsection::get_addr(Context &ctx) const { return ctx.arg.pagezero_size + raddr; diff --git a/macho/object-file.cc b/macho/object-file.cc index d430444a..1267ea56 100644 --- a/macho/object-file.cc +++ b/macho/object-file.cc @@ -602,6 +602,7 @@ void DylibFile::resolve_symbols(Context &ctx) { template class ObjectFile; \ template class DylibFile +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho diff --git a/macho/output-chunks.cc b/macho/output-chunks.cc index 0d78cc1e..20a2fe36 100644 --- a/macho/output-chunks.cc +++ b/macho/output-chunks.cc @@ -1342,6 +1342,7 @@ void ThreadPtrsSection::copy_buf(Context &ctx) { template class LazySymbolPtrSection; \ template class ThreadPtrsSection +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho diff --git a/macho/output-file.cc b/macho/output-file.cc index 051644a9..18ee76bb 100644 --- a/macho/output-file.cc +++ b/macho/output-file.cc @@ -114,6 +114,7 @@ OutputFile::open(Context &ctx, std::string path, i64 filesize, i64 perm) { #define INSTANTIATE(E) \ template class OutputFile +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho diff --git a/macho/tapi.cc b/macho/tapi.cc index 306e26e4..42adf6b2 100644 --- a/macho/tapi.cc +++ b/macho/tapi.cc @@ -131,6 +131,7 @@ TextDylib parse_tbd(Context &ctx, MappedFile> *mf) { #define INSTANTIATE(E) \ template TextDylib parse_tbd(Context &, MappedFile> *); +INSTANTIATE(ARM64); INSTANTIATE(X86_64); } // namespace mold::macho