1
1
mirror of https://github.com/rui314/mold.git synced 2024-09-11 21:17:28 +03:00

[ELF] Fix precedence with C++ version script rules.

Earlier entries in a version scripts always get the precedence. Don't let a
non-C++ symbol rule override a preceding C++ symbol rule.

MultiGlob is modified to return the necessary rule index information to the
caller. Since the rule index might not always be contiguous within a
MultiGlob instance (e.g. interleaving ordinary/C++ rules), the meaning of
MultiGlob's `val` is changed so that the minimum `val` itself is returned,
instead of the `val` with minimum index (the earliest added one).

MultiGlob is also used for the macOS backend, but since it does not care
about order yet the code is left as-is.

Signed-off-by: Tatsuyuki Ishi <ishitatsuyuki@gmail.com>
This commit is contained in:
Tatsuyuki Ishi 2022-11-09 14:31:37 +09:00
parent c9ff2a21c5
commit 9875150677
4 changed files with 49 additions and 19 deletions

View File

@ -1227,12 +1227,13 @@ void apply_version_script(Context<E> &ctx) {
MultiGlob matcher;
MultiGlob cpp_matcher;
for (VersionPattern &v : ctx.version_patterns) {
for (i64 i = 0; i < ctx.version_patterns.size(); i++) {
VersionPattern &v = ctx.version_patterns[i];
if (v.is_cpp) {
if (!cpp_matcher.add(v.pattern, v.ver_idx))
if (!cpp_matcher.add(v.pattern, i))
Fatal(ctx) << "invalid version pattern: " << v.pattern;
} else {
if (!matcher.add(v.pattern, v.ver_idx))
if (!matcher.add(v.pattern, i))
Fatal(ctx) << "invalid version pattern: " << v.pattern;
}
}
@ -1244,17 +1245,26 @@ void apply_version_script(Context<E> &ctx) {
std::string_view name = sym->name();
if (std::optional<u16> ver = matcher.find(name)) {
sym->ver_idx = *ver;
continue;
}
std::optional<u32> best_match = std::nullopt;
auto update_match = [&](u32 match) {
if (!best_match || match < *best_match)
best_match = match;
};
if (std::optional<u32> rule_idx = matcher.find(name))
update_match(*rule_idx);
// Match non-mangled symbols against the C++ pattern as well.
// Weird, but required to match other linkers' behavior.
if (!cpp_matcher.empty()) {
if (std::optional<std::string_view> s = cpp_demangle(name))
name = *s;
if (std::optional<u16> ver = cpp_matcher.find(name))
sym->ver_idx = *ver;
if (std::optional<u32> rule_idx = cpp_matcher.find(name))
update_match(*rule_idx);
}
if (best_match)
sym->ver_idx = ctx.version_patterns[*best_match].ver_idx;
}
});
}

1
mold.h
View File

@ -567,7 +567,6 @@ private:
std::vector<std::string> strings;
std::unique_ptr<TrieNode> root;
std::vector<std::pair<Glob, u32>> globs;
std::vector<u32> values;
std::once_flag once;
bool is_compiled = false;
};

View File

@ -26,7 +26,7 @@ namespace mold {
std::optional<u32> MultiGlob::find(std::string_view str) {
std::call_once(once, [&] { compile(); });
u32 idx = UINT32_MAX;
u32 val = UINT32_MAX;
if (root) {
// Match against simple glob patterns
@ -36,7 +36,7 @@ std::optional<u32> MultiGlob::find(std::string_view str) {
for (;;) {
if (node->children[c]) {
node = node->children[c].get();
idx = std::min(idx, node->value);
val = std::min(val, node->value);
return;
}
@ -55,11 +55,11 @@ std::optional<u32> MultiGlob::find(std::string_view str) {
// Match against complex glob patterns
for (std::pair<Glob, u32> &glob : globs)
if (glob.first.match(str))
idx = std::min(idx, glob.second);
val = std::min(val, glob.second);
if (idx == UINT32_MAX)
if (val == UINT32_MAX)
return {};
return values[idx];
return val;
}
static bool is_simple_pattern(std::string_view pat) {
@ -86,14 +86,12 @@ bool MultiGlob::add(std::string_view pat, u32 val) {
assert(!is_compiled);
assert(!pat.empty());
u32 idx = strings.size();
strings.push_back(std::string(pat));
values.push_back(val);
// Complex glob pattern
if (!is_simple_pattern(pat)) {
if (std::optional<Glob> glob = Glob::compile(pat)) {
globs.push_back({std::move(*glob), idx});
globs.push_back({std::move(*glob), val});
return true;
}
return false;
@ -110,7 +108,7 @@ bool MultiGlob::add(std::string_view pat, u32 val) {
node = node->children[c].get();
}
node->value = std::min(node->value, idx);
node->value = std::min(node->value, val);
return true;
}

23
test/elf/version-script18.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
. $(dirname $0)/common.inc
# Test version script precedence.
cat <<'EOF' > $t/a.ver
{ global: extern "C++" { *libalpha::*; }; local: *libbeta*; };
EOF
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc++ -
namespace libbeta {
struct Bar;
}
namespace libalpha {
template <typename T>
void foo() {}
template void foo<libbeta::Bar>();
}
EOF
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
readelf --wide --dyn-syms $t/c.so | grep libalpha | grep -q Bar