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

[ELF] Support extern "C++" in dynamic lists

Fixes https://github.com/rui314/mold/issues/149
This commit is contained in:
Rui Ueyama 2021-12-25 15:10:22 +09:00
parent 7b3c640472
commit 7aa5c393e4
5 changed files with 101 additions and 27 deletions

View File

@ -463,7 +463,8 @@ void parse_nonpositional_args(Context<E> &ctx,
} else if (read_arg(ctx, args, arg, "sysroot")) {
ctx.arg.sysroot = arg;
} else if (read_arg(ctx, args, arg, "unique")) {
ctx.arg.unique.reset(new std::regex(glob_to_regex(arg)));
auto flags = std::regex_constants::extended | std::regex_constants::optimize;
ctx.arg.unique.reset(new std::regex(glob_to_regex(arg), flags));
} else if (read_arg(ctx, args, arg, "unresolved-symbols")) {
if (arg == "report-all" || arg == "ignore-in-shared-libs")
ctx.arg.unresolved_symbols = UnresolvedKind::ERROR;

View File

@ -266,7 +266,7 @@ static bool read_label(std::span<std::string_view> &tok,
template <typename E>
static void
read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
VersionPattern &pat, bool is_extern_cpp) {
VersionPattern &pat, bool is_cpp) {
bool is_global = true;
while (!tok.empty() && tok[0] != "}") {
@ -283,17 +283,16 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
if (tok[0] == "extern") {
tok = tok.subspan(1);
bool is_cpp;
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
is_cpp = false;
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, pat, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
is_cpp = true;
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, pat, true);
}
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, pat, is_cpp);
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
continue;
@ -301,10 +300,10 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
if (tok[0] == "*")
ctx.arg.default_version = (is_global ? pat.ver_idx : VER_NDX_LOCAL);
else if (is_extern_cpp)
pat.cpp_patterns.push_back(tok[0]);
else if (is_cpp)
pat.cpp_patterns.push_back(unquote(tok[0]));
else
pat.patterns.push_back(tok[0]);
pat.patterns.push_back(unquote(tok[0]));
tok = tok.subspan(1);
@ -351,6 +350,41 @@ void parse_version_script(Context<E> &ctx, std::string path) {
SyntaxError(ctx, tok[0]) << "trailing garbage token";
}
template <typename E>
void read_dynamic_list_commands(Context<E> &ctx,
std::span<std::string_view> &tok,
VersionPattern &pat,
bool is_cpp) {
while (!tok.empty() && tok[0] != "}") {
if (tok[0] == "extern") {
tok = tok.subspan(1);
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, pat, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, pat, true);
}
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
continue;
}
if (tok[0] == "*")
ctx.arg.default_version = VER_NDX_GLOBAL;
else if (is_cpp)
pat.cpp_patterns.push_back(unquote(tok[0]));
else
pat.patterns.push_back(unquote(tok[0]));
tok = skip(ctx, tok.subspan(1), ";");
}
}
template <typename E>
void parse_dynamic_list(Context<E> &ctx, std::string path) {
current_file<E> = MappedFile<Context<E>>::must_open(ctx, path);
@ -360,18 +394,10 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
std::span<std::string_view> tok = vec;
tok = skip(ctx, tok, "{");
while (!tok.empty() && tok[0] != "}") {
if (tok[0] == "*") {
ctx.arg.default_version = VER_NDX_GLOBAL;
} else {
VersionPattern pat;
pat.ver_idx = VER_NDX_GLOBAL;
pat.patterns.push_back(tok[0]);
ctx.arg.version_patterns.push_back(pat);
}
tok = skip(ctx, tok.subspan(1), ";");
}
VersionPattern pat;
pat.ver_idx = VER_NDX_GLOBAL;
read_dynamic_list_commands(ctx, tok, pat, false);
ctx.arg.version_patterns.push_back(pat);
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");

View File

@ -18,11 +18,20 @@ namespace mold::elf {
std::string glob_to_regex(std::string_view pattern) {
std::stringstream ss;
for (u8 c : pattern) {
if (c == '*')
for (char c : pattern) {
switch (c) {
case '.': case '[': case ']': case '^':
case '$': case '\\': case '(': case ')':
case '+': case '?': case '|':
ss << "\\" << c;
break;
case '*':
ss << ".*";
else
ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (int)c;
break;
default:
ss << c;
break;
}
}
return ss.str();
}

View File

@ -622,7 +622,8 @@ void apply_version_script(Context<E> &ctx) {
if (vec.empty() && elem.cpp_patterns.empty())
continue;
auto flags = std::regex_constants::optimize | std::regex_constants::nosubs;
auto flags = std::regex_constants::extended | std::regex_constants::optimize |
std::regex_constants::nosubs;
std::regex re(to_regex(vec), flags);
std::regex cpp_re(to_regex(elem.cpp_patterns), flags);

37
test/elf/dynamic-list2.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash
export LANG=
set -e
cd $(dirname $0)
mold=`pwd`/../../mold
echo -n "Testing $(basename -s .sh $0) ... "
t=$(pwd)/../../out/test/elf/$(basename -s .sh $0)
mkdir -p $t
cat <<EOF | cc -o $t/a.o -c -xc -
void foo(int x) {}
void bar(int x) {}
EOF
cat <<EOF | c++ -o $t/b.o -c -xc++ -
void baz(int x) {}
int main() {}
EOF
clang++ -fuse-ld=$mold -o $t/exe $t/a.o $t/b.o
readelf --dyn-syms $t/exe > $t/log
! grep -q ' foo$' $t/log || false
! grep -q ' bar$' $t/log || false
cat <<EOF > $t/dyn
{ foo; extern "C++" { "baz(int)"; }; };
EOF
clang -fuse-ld=$mold -o $t/exe $t/a.o $t/b.o -Wl,-dynamic-list=$t/dyn
readelf --dyn-syms $t/exe > $t/log
grep -q ' foo$' $t/log
! grep -q ' bar$' $t/log || false
grep -q ' _Z3bazi$' $t/log
echo OK