From 880204ebe57c5033f3cd0e9278ff6181fc22505d Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Mon, 6 Jun 2022 20:18:44 +0800 Subject: [PATCH] [Mach-O] Handle $ld$hide$ symbols --- macho/tapi.cc | 29 ++++++++++++++++++++------- test/macho/tbd-hide.sh | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) create mode 100755 test/macho/tbd-hide.sh diff --git a/macho/tapi.cc b/macho/tapi.cc index 2174f0f4..ac51ba69 100644 --- a/macho/tapi.cc +++ b/macho/tapi.cc @@ -13,6 +13,7 @@ #include #include +#include namespace mold::macho { @@ -114,11 +115,11 @@ static void interpret_ld_symbols(Context &ctx, TextDylib &tbd) { std::vector syms; syms.reserve(tbd.exports.size()); + std::unordered_set hidden_syms; + for (std::string_view s : tbd.exports) { - if (!s.starts_with("$ld$")) { - syms.push_back(s); + if (!s.starts_with("$ld$")) continue; - } std::string name{s}; std::smatch m; @@ -126,11 +127,11 @@ static void interpret_ld_symbols(Context &ctx, TextDylib &tbd) { // $ld$previous$ symbol replaces the default install name with a // specified one if the platform OS version is in a specified range. - static std::regex re_previous( + static std::regex previous_re( R"(\$ld\$previous\$([^$]+)\$([\d.]*)\$(\d+)\$([\d.]+)\$([\d.]+)\$(.*)\$)", flags); - if (std::regex_match(name, m, re_previous)) { + if (std::regex_match(name, m, previous_re)) { std::string install_name = m[1]; i64 platform = std::stoi(m[3]); i64 min_version = parse_version(m[4]); @@ -155,15 +156,29 @@ static void interpret_ld_symbols(Context &ctx, TextDylib &tbd) { // $ld$add$os_version$symbol adds a symbol if the given OS version // matches. - static std::regex re_add(R"(\$ld\$add\$os([\d.]+)\$(.+))", flags); + static std::regex add_re(R"(\$ld\$add\$os([\d.]+)\$(.+))", flags); - if (std::regex_match(name, m, re_add)) { + if (std::regex_match(name, m, add_re)) { if (ctx.arg.platform_min_version == parse_version(m[1])) syms.push_back(save_string(ctx, m[2])); continue; } + + // $ld$hide$os_version$symbol hides a symbol if the given OS version + // matches. + static std::regex hidden_re(R"(\$ld\$hide\$os([\d.]+)\$(.+))", flags); + + if (std::regex_match(name, m, hidden_re)) { + if (ctx.arg.platform_min_version == parse_version(m[1])) + hidden_syms.insert(m[2]); + continue; + } } + for (std::string_view s : tbd.exports) + if (!s.starts_with("$ld$") && !hidden_syms.contains(std::string(s))) + syms.push_back(s); + std::erase_if(syms, [](std::string_view s) { return s.starts_with("$ld$"); }); tbd.exports = syms; } diff --git a/test/macho/tbd-hide.sh b/test/macho/tbd-hide.sh new file mode 100755 index 00000000..49d5c1b8 --- /dev/null +++ b/test/macho/tbd-hide.sh @@ -0,0 +1,45 @@ +#!/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 + +cat > $t/libfoo.tbd <<'EOF' +--- !tapi-tbd +tbd-version: 4 +targets: [ x86_64-macos, arm64-macos ] +uuids: + - target: x86_64-macos + value: 00000000-0000-0000-0000-000000000000 + - target: arm64-macos + value: 00000000-0000-0000-0000-000000000000 +install-name: '/foo' +current-version: 0 +compatibility-version: 0 +exports: + - targets: [ x86_64-macos, arm64-macos ] + symbols: [ '$ld$hide$os25.0$_foo', _foo ] +... +EOF + +cat <& /dev/null + +! clang --ld-path=./ld64 -o $t/exe $t/libfoo.tbd $t/a.o \ + -Wl,-platform_version,macos,25.0,21.0 >& /dev/null || false + +echo OK