Input C/C++ Header
int api_version(void);
int optional_api(int value) __attribute__((weak));
The first declaration makes sure the test still depends on the shared library
when optional_api is missing. The weak function is used in the usual way:
return optional_api ? optional_api(value) : value + 100;
Bindgen Invocation
$ bindgen weak_api.h \
--rust-target 1.75 \
--no-layout-tests \
--no-rustfmt-bindings \
--output bindings.rs \
-- -I.
Actual Results
The relevant output, reformatted for readability, is:
extern "C" {
pub fn api_version() -> ::std::os::raw::c_int;
}
extern "C" {
pub fn optional_api(
value: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
}
optional_api is represented like an ordinary required function. With bindgen
0.72.1 and rustc 1.75, the C and Rust callers lower to:
; C
declare extern_weak i32 @optional_api(i32 noundef)
; Rust, using bindings.rs unchanged
declare noundef i32 @optional_api(i32 noundef) unnamed_addr
A source build from current main at
9d26c6eddeff9192ddedb563192abe3128fc5aae produced the same ordinary Rust
declaration. In that run, the ELF symbol tables contain:
C caller: WEAK UND optional_api
Rust caller: GLOBAL UND optional_api
The full reproducer is
weak-function-linkage-repro.zip.
It builds an old library that exports only api_version, and a new library
that also exports optional_api:
$ unzip weak-function-linkage-repro.zip
$ cd repro
$ BINDGEN=/path/to/bindgen ./run.sh
Both the 0.72.1 and current-main runs gave these results:
C linked to old: version=1 fallback=105
C linked to new: version=2 optional=205
Rust linked to new: version=2 optional=205
Rust linked directly to old: link error, undefined reference to optional_api
C linked to new, run with old: version=1 fallback=105
Rust linked to new, run with old: exit 127, undefined symbol: optional_api
The C caller can run with the old library and take its fallback path. The Rust
caller using the unchanged generated declaration either fails to link or fails
during loader symbol resolution.
As a header-level example, JACK marks APIs such as
jack_set_latency_callback with JACK_WEAK_EXPORT so a caller can check
whether the function exists when using an older JACK library. Bindgen emits
that declaration as an ordinary extern function too.
--dynamic-loading WeakApi is a separate opt-in libloading workaround.
Without --dynamic-link-require-all, both api_version and optional_api
become fallible lookups. It does not preserve the default linker behavior or
infer optionality from the weak attribute.
$ bindgen --version
bindgen 0.72.1
$ clang --version
Ubuntu clang version 15.0.7
$ rustc --version
rustc 1.75.0
Release target: x86_64-unknown-linux-gnu.
The current-main run used rustc 1.82.0. Its bindgen CLI reported 0.72.0, so the
commit above identifies that build.
Expected Results
A binding that keeps optional_api optional, or an explicit diagnostic if the
selected output mode cannot preserve weak imports.
I am not prescribing a particular Rust representation. Silently emitting an
ordinary extern function changes the weak declaration into a required symbol
on ELF.
Input C/C++ Header
The first declaration makes sure the test still depends on the shared library
when
optional_apiis missing. The weak function is used in the usual way:Bindgen Invocation
Actual Results
The relevant output, reformatted for readability, is:
optional_apiis represented like an ordinary required function. With bindgen0.72.1 and rustc 1.75, the C and Rust callers lower to:
A source build from current main at
9d26c6eddeff9192ddedb563192abe3128fc5aaeproduced the same ordinary Rustdeclaration. In that run, the ELF symbol tables contain:
The full reproducer is
weak-function-linkage-repro.zip.
It builds an old library that exports only
api_version, and a new librarythat also exports
optional_api:Both the 0.72.1 and current-main runs gave these results:
The C caller can run with the old library and take its fallback path. The Rust
caller using the unchanged generated declaration either fails to link or fails
during loader symbol resolution.
As a header-level example, JACK marks APIs such as
jack_set_latency_callbackwithJACK_WEAK_EXPORTso a caller can checkwhether the function exists when using an older JACK library. Bindgen emits
that declaration as an ordinary extern function too.
--dynamic-loading WeakApiis a separate opt-inlibloadingworkaround.Without
--dynamic-link-require-all, bothapi_versionandoptional_apibecome fallible lookups. It does not preserve the default linker behavior or
infer optionality from the weak attribute.
Release target:
x86_64-unknown-linux-gnu.The current-main run used rustc 1.82.0. Its bindgen CLI reported 0.72.0, so the
commit above identifies that build.
Expected Results
A binding that keeps
optional_apioptional, or an explicit diagnostic if theselected output mode cannot preserve weak imports.
I am not prescribing a particular Rust representation. Silently emitting an
ordinary extern function changes the weak declaration into a required symbol
on ELF.