Skip to content

COMMON symbols left undefined in shared libraries instead of being allocated into BSS #1679

@alexandergunnarson

Description

@alexandergunnarson

Summary

When linking a shared library that contains object files with COMMON symbols (e.g. .gomp_critical_user_.var emitted by Clang for #pragma omp critical), wild leaves them as undefined (U) in the output instead of allocating them into BSS (B). Both lld and GNU ld correctly handle this case.

Version

wild 0.8.0 (d789d5d6716de3f665ff3da0c53d02dc65e49e3b)

Also verified the bug is present on current main (62a18be).

Reproduction

// test.c
#include <omp.h>
#include <stdio.h>

void do_work(int* data, int n) {
    int error_count = 0;
    #pragma omp parallel for reduction(+:error_count)
    for (int i = 0; i < n; i++) {
        if (data[i] < 0) {
            #pragma omp critical
            {
                fprintf(stderr, "Error at %d\n", i);
                error_count++;
            }
        }
    }
}
# Compile the object file
clang -c -fopenmp=libomp test.c -o test.o

# Confirm the COMMON symbols exist in the object file
$ nm test.o | grep gomp
0000000000000020 C .gomp_critical_user_.reduction.var
0000000000000020 C .gomp_critical_user_.var

# Link with lld — COMMON symbols correctly become BSS
$ clang -shared -fopenmp=libomp test.o -o test_lld.so -fuse-ld=lld
$ nm -D test_lld.so | grep gomp
0000000000003fb8 B .gomp_critical_user_.reduction.var
0000000000003f98 B .gomp_critical_user_.var

# Link with wild — COMMON symbols left undefined
$ clang -shared -fopenmp=libomp test.o -o test_wild.so -fuse-ld=wild
$ nm -D test_wild.so | grep gomp
                 U .gomp_critical_user_.reduction.var
                 U .gomp_critical_user_.var

Expected behavior

COMMON symbols (C) in input object files should be allocated into BSS and appear as defined (B) in the output shared library, matching lld and GNU ld behavior.

Actual behavior

COMMON symbols remain undefined (U) in the output shared library, causing dlopen to fail at runtime:

undefined symbol: .gomp_critical_user_.var

Impact

Any shared library built with wild that contains OpenMP #pragma omp critical or #pragma omp ... reduction(...) (compiled with Clang) will fail to load at runtime.

Analysis

I traced this to SymbolCopyInfo::new() in libwild/src/layout.rs (around line 4384):

if sym.as_common().is_some() && !symbol_state.has_resolution() {
    return None;
}

This skips COMMON symbols from the symtab when they don't have a resolution flag set. The load_symbol path in RegularLayoutState correctly calls common.allocate() for COMMON symbols, and load_non_hidden_symbols sets EXPORT_DYNAMIC. However, it appears the COMMON symbol either doesn't get its resolution flags set properly by the time allocate_symtab_space runs, or SymbolCopyInfo::new() is too aggressive in filtering them out.

Notes

  • GNU ld has a -d/-dc/-dp flag to "force common symbols to be defined", but lld doesn't need it (it always defines them). wild doesn't support this flag either.
  • A related fix for TLS COMMON symbols was made in commit 4f0688e (fix: put common TLS symbols into correct section #1310), but that addressed TLS symbols being placed in the wrong section, not the general case of non-TLS COMMON symbols being dropped from shared library output.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions