diff --git a/elf/cmdline.cc b/elf/cmdline.cc index a0502a8ad2..c223ee7ea9 100644 --- a/elf/cmdline.cc +++ b/elf/cmdline.cc @@ -197,6 +197,8 @@ inline const char helpmsg[] = R"( -z nopack-relative-relocs -z sectionheader Do not omit section header (default) -z nosectionheader Omit section header + -z start_stop_visibility=[hidden,protected] + Specify symbol visibility for "__start_SECNAME" and "__stop_SECNAME" symbols -z separate-loadable-segments Separate all loadable segments onto different pages -z separate-code Separate code and data onto different pages @@ -937,9 +939,10 @@ std::vector parse_nonpositional_args(Context &ctx) { ctx.page_size = parse_number(ctx, "-z max-page-size", arg); if (!has_single_bit(ctx.page_size)) Fatal(ctx) << "-z max-page-size " << arg << ": value must be a power of 2"; - } else if (read_z_arg("start-stop-visibility")) { - if (arg != "hidden") - Fatal(ctx) << "-z start-stop-visibility: unsupported visibility: " << arg; + } else if (read_z_flag("start-stop-visibility=protected")) { + ctx.arg.z_start_stop_visibility_protected = true; + } else if (read_z_flag("start-stop-visibility=hidden")) { + ctx.arg.z_start_stop_visibility_protected = false; } else if (read_z_flag("noexecstack")) { ctx.arg.z_execstack = false; } else if (read_z_flag("relro")) { diff --git a/elf/mold.h b/elf/mold.h index 87922550a4..6ebd87b29f 100644 --- a/elf/mold.h +++ b/elf/mold.h @@ -1725,6 +1725,7 @@ struct Context { bool z_rewrite_endbr = false; bool z_sectionheader = true; bool z_shstk = false; + bool z_start_stop_visibility_protected = false; bool z_text = false; i64 filler = -1; i64 spare_dynamic_tags = 5; diff --git a/elf/passes.cc b/elf/passes.cc index 1a4166db6d..515f35b8de 100644 --- a/elf/passes.cc +++ b/elf/passes.cc @@ -830,14 +830,20 @@ void add_synthetic_symbols(Context &ctx) { if constexpr (is_ppc32) ctx.extra._SDA_BASE_ = add("_SDA_BASE_"); + auto add_start_stop = [&](std::string s) { + add(save_string(ctx, s)); + if (ctx.arg.z_start_stop_visibility_protected) + get_symbol(ctx, save_string(ctx, s))->is_exported = true; + }; + for (Chunk *chunk : ctx.chunks) { if (std::optional name = get_start_stop_name(ctx, *chunk)) { - add(save_string(ctx, "__start_" + *name)); - add(save_string(ctx, "__stop_" + *name)); + add_start_stop("__start_" + *name); + add_start_stop("__stop_" + *name); if (ctx.arg.physical_image_base) { - add(save_string(ctx, "__phys_start_" + *name)); - add(save_string(ctx, "__phys_stop_" + *name)); + add_start_stop("__phys_start_" + *name); + add_start_stop("__phys_stop_" + *name); } } } diff --git a/test/elf/z-start-stop-visibility.sh b/test/elf/z-start-stop-visibility.sh index 7efc94b53c..cf44248039 100755 --- a/test/elf/z-start-stop-visibility.sh +++ b/test/elf/z-start-stop-visibility.sh @@ -1,6 +1,28 @@ #!/bin/bash . $(dirname $0)/common.inc -./mold -z start-stop-visibility=hidden --version > /dev/null -! ./mold -z start-stop-visibility=protected --version 2> $t/log -grep -q 'unsupported visibility: protected' $t/log +cat < + +__attribute__((section("hello"))) +static char msg[] = "Hello world"; + +int main() { + printf("%s\n", msg); +} +EOF + +$CC -B. -o $t/exe1 $t/a.o +readelf -W --dyn-syms $t/exe1 > $t/log1 +! grep -q __start_hello $t/log1 || false +! grep -q __stop_hello $t/log1 || false + +$CC -B. -o $t/exe2 $t/a.o -Wl,-z,start-stop-visibility=hidden +readelf -W --dyn-syms $t/exe2 > $t/log2 +! grep -q __start_hello $t/log2 || false +! grep -q __stop_hello $t/log2 || false + +$CC -B. -o $t/exe3 $t/a.o -Wl,-z,start-stop-visibility=protected +readelf -W --dyn-syms $t/exe3 > $t/log3 +grep -q __start_hello $t/log3 +grep -q __stop_hello $t/log3