Skip to content

Commit

Permalink
[ELF] Support --[no-]undefined-version
Browse files Browse the repository at this point in the history
Fixes #921
  • Loading branch information
rui314 committed Dec 21, 2022
1 parent 864f9a0 commit e2d7353
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 21 deletions.
10 changes: 9 additions & 1 deletion docs/mold.1
Expand Up @@ -950,6 +950,15 @@ Set target system root directory to
.It Fl -trace
Print name of each input file.
.Pp
.It Fl -undefined-version
.It Fl -no-undefined-version
By default,
.Nm
warns on a symbol specified by a version script or by
.Fl -export-dynamic-symbol
if it is not defined. You can silence the warning by
.Fl -undefined-version .
.Pp
.It Fl -unique Ns = Ns Ar pattern
Don't merge input sections that match
.Ar pattern .
Expand Down Expand Up @@ -1197,7 +1206,6 @@ Mark object to interpose all DSOs but executable.
.It Fl -no-allow-shlib-undefined
.It Fl -no-copy-dt-needed-entries
.It Fl -no-fatal-warnings
.It Fl -no-undefined-version
.It Fl -nostdlib
.It Fl -rpath-link Ns = Ns Ar dir
.It Fl -sort-common
Expand Down
7 changes: 6 additions & 1 deletion elf/cmdline.cc
Expand Up @@ -149,6 +149,8 @@ inline const char helpmsg[] = R"(
--threads Use multiple threads (default)
--no-threads
--trace Print name of each input file
--undefined-version Report version scripts that refer undefined symbols (default)
--no-undefined-version

This comment has been minimized.

Copy link
@justanotheranonymoususer

justanotheranonymoususer Dec 21, 2022

Contributor

From the code it looks like no-undefined-version is default, is message wrong? @rui314

--unique PATTERN Don't merge input sections that match a given pattern
--unresolved-symbols [report-all,ignore-all,ignore-in-object-files,ignore-in-shared-libs]
How to handle unresolved symbols
Expand Down Expand Up @@ -990,6 +992,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
Fatal(ctx) << "-R" << arg
<< ": -R as an alias for --just-symbols is not supported";
add_rpath(arg);
} else if (read_flag("undefined-version")) {
ctx.arg.undefined_version = true;
} else if (read_flag("no-undefined-version")) {
ctx.arg.undefined_version = false;
} else if (read_flag("build-id")) {
ctx.arg.build_id.kind = BuildId::HASH;
ctx.arg.build_id.hash_size = 20;
Expand Down Expand Up @@ -1046,7 +1052,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_flag("no-add-needed")) {
} else if (read_flag("no-call-graph-profile-sort")) {
} else if (read_flag("no-copy-dt-needed-entries")) {
} else if (read_flag("no-undefined-version")) {
} else if (read_arg("sort-section")) {
} else if (read_flag("sort-common")) {
} else if (read_flag("dc")) {
Expand Down
21 changes: 14 additions & 7 deletions elf/linker-script.cc
Expand Up @@ -278,7 +278,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,
u16 ver_idx, bool is_cpp) {
std::string_view ver_str, u16 ver_idx, bool is_cpp) {
bool is_global = true;

while (!tok.empty() && tok[0] != "}") {
Expand All @@ -298,11 +298,11 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
tok = skip(ctx, tok, "{");
read_version_script_commands( ctx, tok, ver_idx, false);
read_version_script_commands( ctx, tok, ver_str, ver_idx, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver_idx, true);
read_version_script_commands(ctx, tok, ver_str, ver_idx, true);
}

tok = skip(ctx, tok, "}");
Expand All @@ -314,9 +314,11 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
ctx.default_version = (is_global ? ver_idx : VER_NDX_LOCAL);
ctx.default_version_from_version_script = true;
} else if (is_global) {
ctx.version_patterns.push_back({unquote(tok[0]), ver_idx, is_cpp});
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
ver_str, ver_idx, is_cpp});
} else {
ctx.version_patterns.push_back({unquote(tok[0]), VER_NDX_LOCAL, is_cpp});
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
ver_str, VER_NDX_LOCAL, is_cpp});
}

tok = tok.subspan(1);
Expand All @@ -332,17 +334,21 @@ void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
u16 next_ver = VER_NDX_LAST_RESERVED + ctx.arg.version_definitions.size() + 1;

while (!tok.empty() && tok[0] != "}") {
std::string_view ver_str;
u16 ver_idx;

if (tok[0] == "{") {
ver_str = "global";
ver_idx = VER_NDX_GLOBAL;
} else {
ver_str = tok[0];
ver_idx = next_ver++;
ctx.arg.version_definitions.push_back(std::string(tok[0]));
tok = tok.subspan(1);
}

tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver_idx, false);
read_version_script_commands(ctx, tok, ver_str, ver_idx, false);
tok = skip(ctx, tok, "}");
if (!tok.empty() && tok[0] != ";")
tok = tok.subspan(1);
Expand Down Expand Up @@ -385,7 +391,8 @@ void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &to
if (tok[0] == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({unquote(tok[0]), VER_NDX_GLOBAL, is_cpp});
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
"global", VER_NDX_GLOBAL, is_cpp});

tok = skip(ctx, tok.subspan(1), ";");
}
Expand Down
3 changes: 2 additions & 1 deletion elf/main.cc
Expand Up @@ -295,7 +295,8 @@ static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
if (arg == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({arg, VER_NDX_GLOBAL, false});
ctx.version_patterns.push_back({arg, "--export-dynamic-symbol",
"global", VER_NDX_GLOBAL, false});
} else if (remove_prefix(arg, "--export-dynamic-symbol-list=")) {
MappedFile<Context<E>> *mf = find_from_search_paths(ctx, std::string(arg));
if (!mf)
Expand Down
3 changes: 3 additions & 0 deletions elf/mold.h
Expand Up @@ -1560,6 +1560,8 @@ typedef enum {

struct VersionPattern {
std::string_view pattern;
std::string_view source;
std::string_view ver_str;
u16 ver_idx = -1;
bool is_cpp = false;
};
Expand Down Expand Up @@ -1641,6 +1643,7 @@ struct Context {
bool strip_all = false;
bool strip_debug = false;
bool trace = false;
bool undefined_version = false;
bool warn_common = false;
bool warn_once = false;
bool warn_textrel = false;
Expand Down
12 changes: 9 additions & 3 deletions elf/passes.cc
Expand Up @@ -1375,10 +1375,16 @@ void apply_version_script(Context<E> &ctx) {
};

if (is_simple()) {
for (VersionPattern &v : ctx.version_patterns)
if (Symbol<E> *sym = get_symbol(ctx, v.pattern);
sym->file && !sym->file->is_dso)
for (VersionPattern &v : ctx.version_patterns) {
Symbol<E> *sym = get_symbol(ctx, v.pattern);

if (!sym->file && !ctx.arg.undefined_version)
Warn(ctx) << v.source << ": cannot assign version `" << v.ver_str
<< "` to symbol `" << *sym << "`: symbol not found";

if (sym->file && !sym->file->is_dso)
sym->ver_idx = v.ver_idx;
}
return;
}

Expand Down
11 changes: 11 additions & 0 deletions test/elf/no-undefined-version.sh
@@ -0,0 +1,11 @@
#!/bin/bash
. $(dirname $0)/common.inc

echo 'ver_x { global: foo; };' > $t/a.ver

cat <<EOF | $CC -o $t/b.o -c -xc -
int main() {}
EOF

$CC -B. -o $t/exe -Wl,--version-script,$t/a.ver $t/b.o 2> $t/log
grep -Fq 'a.ver: cannot assign version `ver_x` to symbol `foo`: symbol not found' $t/log
6 changes: 1 addition & 5 deletions test/elf/symbol-version2.sh
Expand Up @@ -10,11 +10,7 @@ __asm__(".symver bar1, bar@TEST");
EOF

cat <<EOF > $t/b.version
TEST {
global:
foo;
bar;
};
TEST { global: foo; };
EOF

$CC -B. -o $t/c.so -shared $t/a.o -Wl,--version-script=$t/b.version
Expand Down
2 changes: 1 addition & 1 deletion test/elf/version-script17.sh
Expand Up @@ -19,7 +19,7 @@ cat <<'EOF' > $t/c.ver
{ local: *; global: xyz; };
EOF

$CC -B. -o $t/exe2 $t/a.o $t/b.so -Wl,--version-script=$t/c.ver
$CC -B. -o $t/exe2 $t/a.o $t/b.so -Wl,--version-script=$t/c.ver -Wl,--undefined-version
nm -g $t/exe2 > $t/log2
! grep -q foo $t/log2 || false

Expand Down
3 changes: 1 addition & 2 deletions test/elf/version-script6.sh
Expand Up @@ -26,8 +26,7 @@ extern int bar;
int baz() { return foo + bar; }
EOF

$CC -B. -shared -Wl,-version-script,$t/d.ver \
-o $t/f.so $t/e.o $t/c.so
$CC -B. -shared -Wl,-version-script,$t/d.ver -o $t/f.so $t/e.o $t/c.so -Wl,--undefined-version

readelf --dyn-syms $t/f.so > $t/log
grep -q 'foo@VER_X1' $t/log
Expand Down

0 comments on commit e2d7353

Please sign in to comment.