From 6d67614004daf7d3134d1c1d6d06ff5c4c829a38 Mon Sep 17 00:00:00 2001 From: Tapan Thaker Date: Tue, 17 May 2022 20:16:27 -0700 Subject: [PATCH] [Mach-O] Adds support for objc and weak symbols in TBD files Signed-off-by: Tapan Thaker --- macho/input-files.cc | 22 ++++++++++++ macho/mold.h | 10 ++++++ macho/tapi.cc | 15 +++++++- test/macho/tbd.sh | 85 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100755 test/macho/tbd.sh diff --git a/macho/input-files.cc b/macho/input-files.cc index 13392ede..4423a15a 100644 --- a/macho/input-files.cc +++ b/macho/input-files.cc @@ -679,8 +679,30 @@ void DylibFile::parse(Context &ctx) { switch (get_file_type(this->mf)) { case FileType::TAPI: { TextDylib tbd = parse_tbd(ctx, this->mf); + auto add_symbol_with_prefix = [&](std::string_view prefix, + std::string_view sym_name) { + std::string symbol_name = std::string(prefix); + symbol_name.append(sym_name); + Symbol *sym = get_symbol(ctx, save_string(ctx, symbol_name)); + this->syms.push_back(sym); + }; + for (std::string_view sym : tbd.exports) this->syms.push_back(get_symbol(ctx, sym)); + for (std::string_view weak_sym : tbd.weak_exports) { + Symbol *sym = get_symbol(ctx, weak_sym); + sym->is_weak_def = true; + this->syms.push_back(sym); + } + for (std::string_view objc_class : tbd.objc_classes) { + add_symbol_with_prefix(OBJC2_CLASS_NAME_PREFIX, objc_class); + add_symbol_with_prefix(OBJC2_METACLASS_NAME_PREFIX, objc_class); + } + for (std::string_view eh_type : tbd.objc_eh_types) + add_symbol_with_prefix(OBJC2_EHTYPE_PREFIX, eh_type); + for (std::string_view ivar : tbd.objc_ivars) + add_symbol_with_prefix(OBJC2_IVAR_PREFIX, ivar); + install_name = tbd.install_name; break; } diff --git a/macho/mold.h b/macho/mold.h index bad733d5..e6ad89c5 100644 --- a/macho/mold.h +++ b/macho/mold.h @@ -702,6 +702,12 @@ parse_yaml(std::string_view str); // tapi.cc // +static const std::string_view OBJC2_CLASS_NAME_PREFIX = "_OBJC_CLASS_$_"; +static const std::string_view OBJC2_METACLASS_NAME_PREFIX = + "_OBJC_METACLASS_$_"; +static const std::string_view OBJC2_EHTYPE_PREFIX = "_OBJC_EHTYPE_$_"; +static const std::string_view OBJC2_IVAR_PREFIX = "_OBJC_IVAR_$_"; + struct TextDylib { std::string_view uuid; std::string_view install_name; @@ -709,6 +715,10 @@ struct TextDylib { std::string_view parent_umbrella; std::vector reexported_libs; std::vector exports; + std::vector weak_exports; + std::vector objc_classes; + std::vector objc_eh_types; + std::vector objc_ivars; }; template diff --git a/macho/tapi.cc b/macho/tapi.cc index cd162676..a5c44466 100644 --- a/macho/tapi.cc +++ b/macho/tapi.cc @@ -72,10 +72,23 @@ static std::optional to_tbd(YamlNode &node, std::string_view arch) { for (std::string_view key : {"exports", "reexports"}) for (YamlNode &mem : get_vector(node, key)) - if (contains(get_vector(mem, "targets"), arch)) + if (contains(get_vector(mem, "targets"), arch)) { for (YamlNode &mem : get_vector(mem, "symbols")) if (auto *sym = std::get_if(&mem.data)) tbd.exports.push_back(*sym); + for (YamlNode &mem : get_vector(mem, "weak-symbols")) + if (auto *sym = std::get_if(&mem.data)) + tbd.weak_exports.push_back(*sym); + for (YamlNode &mem : get_vector(mem, "objc-classes")) + if (auto *clazz = std::get_if(&mem.data)) + tbd.objc_classes.push_back(*clazz); + for (YamlNode &mem : get_vector(mem, "objc-eh-types")) + if (auto *eh_type = std::get_if(&mem.data)) + tbd.objc_eh_types.push_back(*eh_type); + for (YamlNode &mem : get_vector(mem, "objc-ivars")) + if (auto *ivar = std::get_if(&mem.data)) + tbd.objc_ivars.push_back(*ivar); + } return tbd; } diff --git a/test/macho/tbd.sh b/test/macho/tbd.sh new file mode 100755 index 00000000..633ccbcf --- /dev/null +++ b/test/macho/tbd.sh @@ -0,0 +1,85 @@ +#!/bin/bash +export LC_ALL=C +set -e +CC="${TEST_CC:-cc}" +CXX="${TEST_CXX:-c++}" +GCC="${TEST_GCC:-gcc}" +GXX="${TEST_GXX:-g++}" +OBJDUMP="${OBJDUMP:-objdump}" +MACHINE="${MACHINE:-$(uname -m)}" +testname=$(basename "$0" .sh) +echo -n "Testing $testname ... " +cd "$(dirname "$0")"/../.. +t=out/test/macho/$testname +mkdir -p $t + +some_fw=$t/SF/SomeFramework.framework/ +mkdir -p $some_fw + +cat > $some_fw/SomeFramework.tbd < +#import + +// Interface Declaration for SomeFramework.framework + +@interface SomeObjectiveC: NSObject + +@end + +void some_framework_print(char*); +void weak_some_framework_print(char*) __attribute__((weak));; + +// End Interface Declaration for SomeFramework.framework + +@interface TestTBDFiles: SomeObjectiveC +@end + +@implementation TestTBDFiles + +-(void) helloWorld { + some_framework_print("Hello World"); + weak_some_framework_print("Hello World"); +} + +@end + +int main(int argc, const char * argv[]) { + @autoreleasepool { + TestTBDFiles *tbd = [[TestTBDFiles alloc]init]; + [tbd helloWorld]; + } + return 0; +} +EOF + + +clang --ld-path=./ld64 -F$t/SF/ -Wl,-framework,SomeFramework \ + -Wl,-framework,CoreFoundation -lobjc -o $t/exe $t/TestTBDFiles.o +otool -L $t/exe > $t/install_paths.log +grep -q '/usr/frameworks/SomeFramework.framework/SomeFramework' $t/install_paths.log + +echo OK