Create .dynamic for static executables that export symbols

A static executable had no .dynamic, so -E, --dynamic-list and the
other options that export symbols were silently ignored. Such an
executable now has .dynamic and .dynsym with the exported symbols, as
the options ask. It still has no .interp and runs as before.

-static -shared output, which also lacked .dynamic, now has one.
This commit is contained in:
Rui Ueyama committed 2026-10-19 09:27:55 +09:00
1 parent c522789231
commit c12b4144fd
3 files changed
+29 -16

No files matched your search

-3
View File
@@ -265,9 +265,6 @@ fn for_each_entry<E: Target>(ctx: &Context<E>, mut define: impl FnMut(u32, u64))
}
pub fn update_shdr<E: Target>(ctx: &mut Context<E>) {
if ctx.args.is_static && !ctx.args.pie {
return;
}
let mut n = 0;
for_each_entry(ctx, |_, _| n += 1);
let size = (n * size_of::<ElfDyn<E>>()) as u64;
+11 -4
View File
@@ -160,7 +160,15 @@ pub fn create_synthetic_sections<E: Target>(ctx: &mut Context<E>) {
chunks.push(ChunkId::GnuDebuglink);
}
if ctx.args.shared || !ctx.dsos.is_empty() || ctx.args.pie {
// An executable that exports symbols has .dynamic even if it is
// statically linked.
if ctx.args.shared
|| !ctx.dsos.is_empty()
|| ctx.args.pie
|| ctx.args.export_dynamic
|| ctx.args.dynamic_list_data
|| !ctx.dynamic_list_patterns.is_empty()
{
ctx.dynamic = Some(chunks::dynamic::new_header::<E>(&ctx.args));
chunks.push(ChunkId::Dynamic);
// If .dynamic exists, .dynsym and .dynstr must exist as well
@@ -2605,7 +2613,7 @@ pub fn scan_relocations<E: Target>(ctx: &mut Context<E>) {
};
// An executable without .dynamic has no dynamic symbol table even
// if --export-dynamic or --dynamic-list exports symbols.
// if a version script exports symbols.
if (is_imported || is_exported) && ctx.dynamic.is_some() {
ctx.dynsym.add_symbol(&mut ctx.symbols, id);
}
@@ -3907,8 +3915,7 @@ pub fn fix_synthetic_symbols<E: Target>(ctx: &mut Context<E>) {
// __rel_iplt_start and __rel_iplt_end. These symbols need to be
// defined in a statically-linked non-relocatable executable because
// such an executable lacks the .dynamic section and thus there's no way
// to find ifunc relocations other than these symbols.
// libc's startup code finds ifunc relocations by these symbols.
if ctx.chunks.contains(&ChunkId::RelDyn) && ctx.args.is_static && !ctx.args.pie {
let n = num_irelative_relocs(ctx) as i64 * size_of::<ElfRel<E>>() as i64;
stop(ctx, ctx.syms.rel_iplt_start, Some(ChunkId::RelDyn), -n);
+18 -9
View File
@@ -1,17 +1,26 @@
#!/usr/bin/env bash
. $(dirname $0)/common.inc
# An executable that doesn't link against any DSO has no .dynamic, so it
# must not have .dynsym or .dynstr either, even if symbols are exported.
cat <<EOF | $CC -c -o $t/a.o -xc -
# A statically-linked executable that exports symbols has .dynamic and
# .dynsym.
test_cflags -static || skip
cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
void foo() {}
void _start() {}
int main() {
printf("Hello world\n");
}
EOF
$CC -B. -o $t/exe1 $t/a.o -static -Wl,--export-dynamic
readelf -W --dyn-syms $t/exe1 | grep -w foo
$QEMU $t/exe1 | grep 'Hello world'
echo '{ foo; };' > $t/dyn
$CC -B. -o $t/exe2 $t/a.o -static -Wl,--dynamic-list=$t/dyn
readelf -W --dyn-syms $t/exe2 | grep -w foo
$QEMU $t/exe2 | grep 'Hello world'
./mold -o $t/exe1 $t/a.o --dynamic-list=$t/dyn
readelf -SW $t/exe1 | not grep -E '\.dyn(sym|str)'
./mold -o $t/exe2 $t/a.o --export-dynamic
readelf -SW $t/exe2 | not grep -E '\.dyn(sym|str)'
$CC -B. -o $t/exe3 $t/a.o -static
readelf -WS $t/exe3 | not grep -F .dynamic