1
1
mirror of https://github.com/rui314/mold.git synced 2024-07-14 16:20:34 +03:00

Do not try to demangle C++ symbols as Rust ones

Previously, we always tried to demangle a symbol name as a Rust
symbol first and then attempted to demangle it as a C++ symbol.
This resulted in an incorrect demangled result, as the Rust legacy
mangling scheme is not distinguishable from C++.

This patch fix the issue by adding the "is_rust_obj" flag to the
ObjectFile. We try to demangle a symbol as a Rust one before as a C++
only if the flag is true.

Fixes https://github.com/rui314/mold/issues/1159
This commit is contained in:
Rui Ueyama 2023-11-30 13:40:01 +09:00
parent 3d9b4d5c05
commit ea9864bbd5
7 changed files with 80 additions and 35 deletions

View File

@ -874,8 +874,8 @@ std::filesystem::path to_abs_path(std::filesystem::path path);
// demangle.cc
//
std::string_view demangle(std::string_view name);
std::optional<std::string_view> cpp_demangle(std::string_view name);
std::optional<std::string_view> demangle_cpp(std::string_view name);
std::optional<std::string_view> demangle_rust(std::string_view name);
//
// compress.cc

View File

@ -10,24 +10,7 @@
namespace mold {
std::string_view demangle(std::string_view name) {
static thread_local char *buf;
free(buf);
// Try to demangle as a Rust symbol. Since legacy-style Rust symbols
// are also valid as a C++ mangled name, we need to call this before
// cpp_demangle.
buf = rust_demangle(std::string(name).c_str(), 0);
if (buf)
return buf;
// Try to demangle as a C++ symbol.
if (std::optional<std::string_view> s = cpp_demangle(name))
return *s;
return name;
}
std::optional<std::string_view> cpp_demangle(std::string_view name) {
std::optional<std::string_view> demangle_cpp(std::string_view name) {
static thread_local char *buf;
static thread_local size_t buflen;
@ -47,4 +30,13 @@ std::optional<std::string_view> cpp_demangle(std::string_view name) {
return {};
}
std::optional<std::string_view> demangle_rust(std::string_view name) {
static thread_local char *buf;
free(buf);
buf = rust_demangle(std::string(name).c_str(), 0);
if (buf)
return buf;
return {};
}
} // namespace mold

View File

@ -10,6 +10,31 @@
namespace mold::elf {
template <typename E>
static bool is_rust_symbol(const Symbol<E> &sym) {
// The legacy Rust mangling scheme is indistinguishtable from C++.
// We don't want to accidentally demangle C++ symbols as Rust ones.
// So, the legacy mangling scheme will be demangled only when we
// know the object file was created by rustc.
if (sym.file && !sym.file->is_dso && ((ObjectFile<E> *)sym.file)->is_rust_obj)
return true;
// "_R" is the prefix of the new Rust mangling scheme.
return sym.name().starts_with("_R");
}
template <typename E>
std::string_view demangle(const Symbol<E> &sym) {
if (is_rust_symbol(sym)) {
if (std::optional<std::string_view> s = demangle_rust(sym.name()))
return *s;
} else {
if (std::optional<std::string_view> s = demangle_cpp(sym.name()))
return *s;
}
return sym.name();
}
template <typename E>
InputFile<E>::InputFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
: mf(mf), filename(mf->name) {
@ -299,6 +324,10 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
is_debug_section(shdr, name))
continue;
if (name == ".comment" &&
this->get_string(ctx, shdr).starts_with("rustc "))
is_rust_obj = true;
// If an output file doesn't have a section header (i.e.
// --oformat=binary is given), we discard all non-memory-allocated
// sections. This is because without a section header, we can't find
@ -1512,6 +1541,7 @@ using E = MOLD_TARGET;
template class InputFile<E>;
template class ObjectFile<E>;
template class SharedFile<E>;
template std::string_view demangle(const Symbol<E> &);
template std::ostream &operator<<(std::ostream &, const InputFile<E> &);
} // namespace mold::elf

View File

@ -462,14 +462,15 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
// Get the name of a function containin a given offset.
template <typename E>
std::string_view InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) const {
for (const ElfSym<E> &esym : file.elf_syms) {
std::string_view
InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) const {
for (Symbol<E> *sym : file.symbols) {
const ElfSym<E> &esym = sym->esym();
if (esym.st_shndx == shndx && esym.st_type == STT_FUNC &&
esym.st_value <= offset && offset < esym.st_value + esym.st_size) {
std::string_view name = file.symbol_strtab.data() + esym.st_name;
if (ctx.arg.demangle)
return demangle(name);
return name;
return demangle(*sym);
return sym->name();
}
}
return "";
@ -508,7 +509,7 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
ss << ":(" << func << ")";
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
ctx.undef_errors.insert(acc, {&sym, {}});
acc->second.push_back(ss.str());
};

View File

@ -1188,6 +1188,7 @@ public:
std::map<u32, u32> gnu_properties;
bool is_lto_obj = false;
bool needs_executable_stack = false;
bool is_rust_obj = false;
u64 num_dynrel = 0;
u64 reldyn_offset = 0;
@ -1806,7 +1807,7 @@ struct Context {
Atomic<bool> has_textrel = false;
Atomic<u32> num_ifunc_dynrels = 0;
tbb::concurrent_hash_map<std::string_view, std::vector<std::string>> undef_errors;
tbb::concurrent_hash_map<Symbol<E> *, std::vector<std::string>> undef_errors;
// Output chunks
OutputEhdr<E> *ehdr = nullptr;
@ -2176,6 +2177,9 @@ public:
[[no_unique_address]] SymbolExtras<E> extra;
};
template <typename E>
std::string_view demangle(const Symbol<E> &sym);
// If we haven't seen the same `key` before, create a new instance
// of Symbol and returns it. Otherwise, returns the previously-
// instantiated object. `key` is usually the same as `name`.
@ -2195,7 +2199,7 @@ Symbol<E> *get_symbol(Context<E> &ctx, std::string_view name) {
template <typename E>
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym) {
if (opt_demangle)
out << demangle(sym.name());
out << demangle(sym);
else
out << sym.name();
return out;

View File

@ -1492,14 +1492,13 @@ void report_undef_errors(Context<E> &ctx) {
constexpr i64 max_errors = 3;
for (auto &pair : ctx.undef_errors) {
std::string_view sym_name = pair.first;
Symbol<E> *sym = pair.first;
std::span<std::string> errors = pair.second;
if (ctx.arg.demangle)
sym_name = demangle(sym_name);
std::stringstream ss;
ss << "undefined symbol: " << sym_name << "\n";
ss << "undefined symbol: "
<< (ctx.arg.demangle ? demangle(*sym) : sym->name())
<< "\n";
for (i64 i = 0; i < errors.size() && i < max_errors; i++)
ss << errors[i];
@ -1677,7 +1676,7 @@ void apply_version_script(Context<E> &ctx) {
// 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))
if (std::optional<std::string_view> s = demangle_cpp(name))
name = *s;
if (std::optional<u32> idx = cpp_matcher.find(name))
match = std::min<i64>(match, *idx);
@ -1886,7 +1885,7 @@ void compute_import_export(Context<E> &ctx) {
if (matcher.find(name)) {
handle_match(sym);
} else if (!cpp_matcher.empty()) {
if (std::optional<std::string_view> s = cpp_demangle(name))
if (std::optional<std::string_view> s = demangle_cpp(name))
name = *s;
if (cpp_matcher.find(name))
handle_match(sym);

19
test/elf/demangle-cpp.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<'EOF' | $CC -c -o $t/a.o -xc -
void _ZN2ns7versionEv();
int main() { _ZN2ns7versionEv(); }
EOF
! $CC -B. -o $t/exe1 $t/a.o 2> $t/log || false
grep -Fq 'ns::version()' $t/log
cat <<'EOF' | $CC -c -o $t/b.o -xc -
void _ZN2ns7versionEv();
int main() { _ZN2ns7versionEv(); }
__attribute__((section(".comment"))) char str[] = "rustc version x.y.z\n";
EOF
! $CC -B. -o $t/exe2 $t/b.o 2> $t/log || false
grep -Fq 'ns::versionv' $t/log