mirror of
https://github.com/rui314/mold.git
synced 2024-09-11 13:06:59 +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:
parent
3d9b4d5c05
commit
ea9864bbd5
@ -874,8 +874,8 @@ std::filesystem::path to_abs_path(std::filesystem::path path);
|
|||||||
// demangle.cc
|
// demangle.cc
|
||||||
//
|
//
|
||||||
|
|
||||||
std::string_view demangle(std::string_view name);
|
std::optional<std::string_view> demangle_cpp(std::string_view name);
|
||||||
std::optional<std::string_view> cpp_demangle(std::string_view name);
|
std::optional<std::string_view> demangle_rust(std::string_view name);
|
||||||
|
|
||||||
//
|
//
|
||||||
// compress.cc
|
// compress.cc
|
||||||
|
@ -10,24 +10,7 @@
|
|||||||
|
|
||||||
namespace mold {
|
namespace mold {
|
||||||
|
|
||||||
std::string_view demangle(std::string_view name) {
|
std::optional<std::string_view> demangle_cpp(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) {
|
|
||||||
static thread_local char *buf;
|
static thread_local char *buf;
|
||||||
static thread_local size_t buflen;
|
static thread_local size_t buflen;
|
||||||
|
|
||||||
@ -47,4 +30,13 @@ std::optional<std::string_view> cpp_demangle(std::string_view name) {
|
|||||||
return {};
|
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
|
} // namespace mold
|
||||||
|
@ -10,6 +10,31 @@
|
|||||||
|
|
||||||
namespace mold::elf {
|
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>
|
template <typename E>
|
||||||
InputFile<E>::InputFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
|
InputFile<E>::InputFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
|
||||||
: mf(mf), filename(mf->name) {
|
: mf(mf), filename(mf->name) {
|
||||||
@ -299,6 +324,10 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
|||||||
is_debug_section(shdr, name))
|
is_debug_section(shdr, name))
|
||||||
continue;
|
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.
|
// If an output file doesn't have a section header (i.e.
|
||||||
// --oformat=binary is given), we discard all non-memory-allocated
|
// --oformat=binary is given), we discard all non-memory-allocated
|
||||||
// sections. This is because without a section header, we can't find
|
// 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 InputFile<E>;
|
||||||
template class ObjectFile<E>;
|
template class ObjectFile<E>;
|
||||||
template class SharedFile<E>;
|
template class SharedFile<E>;
|
||||||
|
template std::string_view demangle(const Symbol<E> &);
|
||||||
template std::ostream &operator<<(std::ostream &, const InputFile<E> &);
|
template std::ostream &operator<<(std::ostream &, const InputFile<E> &);
|
||||||
|
|
||||||
} // namespace mold::elf
|
} // namespace mold::elf
|
||||||
|
@ -462,14 +462,15 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
|||||||
|
|
||||||
// Get the name of a function containin a given offset.
|
// Get the name of a function containin a given offset.
|
||||||
template <typename E>
|
template <typename E>
|
||||||
std::string_view InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) const {
|
std::string_view
|
||||||
for (const ElfSym<E> &esym : file.elf_syms) {
|
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 &&
|
if (esym.st_shndx == shndx && esym.st_type == STT_FUNC &&
|
||||||
esym.st_value <= offset && offset < esym.st_value + esym.st_size) {
|
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)
|
if (ctx.arg.demangle)
|
||||||
return demangle(name);
|
return demangle(*sym);
|
||||||
return name;
|
return sym->name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
@ -508,7 +509,7 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
|
|||||||
ss << ":(" << func << ")";
|
ss << ":(" << func << ")";
|
||||||
|
|
||||||
typename decltype(ctx.undef_errors)::accessor acc;
|
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());
|
acc->second.push_back(ss.str());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1188,6 +1188,7 @@ public:
|
|||||||
std::map<u32, u32> gnu_properties;
|
std::map<u32, u32> gnu_properties;
|
||||||
bool is_lto_obj = false;
|
bool is_lto_obj = false;
|
||||||
bool needs_executable_stack = false;
|
bool needs_executable_stack = false;
|
||||||
|
bool is_rust_obj = false;
|
||||||
|
|
||||||
u64 num_dynrel = 0;
|
u64 num_dynrel = 0;
|
||||||
u64 reldyn_offset = 0;
|
u64 reldyn_offset = 0;
|
||||||
@ -1806,7 +1807,7 @@ struct Context {
|
|||||||
Atomic<bool> has_textrel = false;
|
Atomic<bool> has_textrel = false;
|
||||||
Atomic<u32> num_ifunc_dynrels = 0;
|
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
|
// Output chunks
|
||||||
OutputEhdr<E> *ehdr = nullptr;
|
OutputEhdr<E> *ehdr = nullptr;
|
||||||
@ -2176,6 +2177,9 @@ public:
|
|||||||
[[no_unique_address]] SymbolExtras<E> extra;
|
[[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
|
// If we haven't seen the same `key` before, create a new instance
|
||||||
// of Symbol and returns it. Otherwise, returns the previously-
|
// of Symbol and returns it. Otherwise, returns the previously-
|
||||||
// instantiated object. `key` is usually the same as `name`.
|
// 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>
|
template <typename E>
|
||||||
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym) {
|
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym) {
|
||||||
if (opt_demangle)
|
if (opt_demangle)
|
||||||
out << demangle(sym.name());
|
out << demangle(sym);
|
||||||
else
|
else
|
||||||
out << sym.name();
|
out << sym.name();
|
||||||
return out;
|
return out;
|
||||||
|
@ -1492,14 +1492,13 @@ void report_undef_errors(Context<E> &ctx) {
|
|||||||
constexpr i64 max_errors = 3;
|
constexpr i64 max_errors = 3;
|
||||||
|
|
||||||
for (auto &pair : ctx.undef_errors) {
|
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;
|
std::span<std::string> errors = pair.second;
|
||||||
|
|
||||||
if (ctx.arg.demangle)
|
|
||||||
sym_name = demangle(sym_name);
|
|
||||||
|
|
||||||
std::stringstream ss;
|
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++)
|
for (i64 i = 0; i < errors.size() && i < max_errors; i++)
|
||||||
ss << 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.
|
// Match non-mangled symbols against the C++ pattern as well.
|
||||||
// Weird, but required to match other linkers' behavior.
|
// Weird, but required to match other linkers' behavior.
|
||||||
if (!cpp_matcher.empty()) {
|
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;
|
name = *s;
|
||||||
if (std::optional<u32> idx = cpp_matcher.find(name))
|
if (std::optional<u32> idx = cpp_matcher.find(name))
|
||||||
match = std::min<i64>(match, *idx);
|
match = std::min<i64>(match, *idx);
|
||||||
@ -1886,7 +1885,7 @@ void compute_import_export(Context<E> &ctx) {
|
|||||||
if (matcher.find(name)) {
|
if (matcher.find(name)) {
|
||||||
handle_match(sym);
|
handle_match(sym);
|
||||||
} else if (!cpp_matcher.empty()) {
|
} 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;
|
name = *s;
|
||||||
if (cpp_matcher.find(name))
|
if (cpp_matcher.find(name))
|
||||||
handle_match(sym);
|
handle_match(sym);
|
||||||
|
19
test/elf/demangle-cpp.sh
Executable file
19
test/elf/demangle-cpp.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user