rust-ctor icon indicating copy to clipboard operation
rust-ctor copied to clipboard

ctor not running for statically linked C library

Open vsbogd opened this issue 1 year ago • 7 comments

Very similar to #27. I am opening it to ask for help in understanding what exactly doesn't work here.

I have a project with the following structure:

  • Rust library which uses ctor;
  • static C API library, which is compiled by cargo and cbindgen and depends on the Rust library with ctor

When C API library is built in release mode and linked to the executable binary then ctor is successfully called on start. If library is build in debug mode then ctor function is not included into the final binary by the linker while it is still present in a static library itself.

I use absolutely same minimal gcc options to try both debug and release:

gcc ./main.c -o ./debug -I. -L./c/target/debug -l:libhyperonc.a -lm

I have tried to repeat the issue building a minimal example from scratch but in minimal example it works correctly.

Platform: Linux x86_64 ctor version 0.2.0 Rust version 1.69.0 GCC version 12.2.1 LD version 2.40

vsbogd avatar May 30 '23 11:05 vsbogd

@vsbogd Try with the new 0.2.1 and cargo +nightly --features=used_linker. It's possible this may help. If so, we just need to wait for that feature to stabilize.

mmastrac avatar May 30 '23 19:05 mmastrac

I receive an error when try using used_linker feature:

$ cat Cargo.toml | grep ctor
ctor = "0.2.1"
$ cargo +nightly build --features=used_linker
error: Package `hyperon v0.1.2` does not have the feature `used_linker`

Do I miss something?

vsbogd avatar May 30 '23 19:05 vsbogd

You'll need to add a feature to hyperon in its Cargo.toml that depends on the ctor feature:

[features]
used_linker = ["ctor/used_linker"]

mmastrac avatar May 30 '23 20:05 mmastrac

Thanks @mmastrac . I also needed to add used_linker into both my libraries: Rust and C API in order to compile C API.

Unfortunately it doesn't help. I still can see on_load which is marked as ctor in both static libraries:

$ objdump -t ./c/target/debug/libhyperonc.a | grep on_load 
0000000000000000 l    d  .text._ZN7hyperon7on_load17hdc39ff8fb07a0999E	0000000000000000 .text._ZN7hyperon7on_load17hdc39ff8fb07a0999E
0000000000000000 l     F .text._ZN7hyperon7on_load17hdc39ff8fb07a0999E	000000000000000b _ZN7hyperon7on_load17hdc39ff8fb07a0999E
0000000000000000 g     F .text.startup	0000000000000008 _ZN7hyperon26on_load___rust_ctor___ctor26on_load___rust_ctor___ctor17hc8a7af7b1d321ffcE
0000000000000000 g     O .init_array	0000000000000008 on_load___rust_ctor___ctor

$ objdump -t ./c/target/release/libhyperonc.a | grep on_load 
0000000000000000 l    d  .gcc_except_table._ZN7hyperon26on_load___rust_ctor___ctor26on_load___rust_ctor___ctor17hf96ede0c9e76c82eE	0000000000000000 .gcc_except_table._ZN7hyperon26on_load___rust_ctor___ctor26on_load___rust_ctor___ctor17hf96ede0c9e76c82eE
0000000000000000 l       .gcc_except_table._ZN7hyperon26on_load___rust_ctor___ctor26on_load___rust_ctor___ctor17hf96ede0c9e76c82eE	0000000000000000 GCC_except_table107
0000000000000000 g     F .text.startup	000000000000005c _ZN7hyperon26on_load___rust_ctor___ctor26on_load___rust_ctor___ctor17hf96ede0c9e76c82eE
0000000000000000 g     O .init_array	0000000000000008 on_load___rust_ctor___ctor

and cannot see it in executable linked with debug library:

$ objdump -t debug | grep on_load

$ objdump -t release | grep on_load
0000000000399500 g     O .init_array	0000000000000008              on_load___rust_ctor___ctor
0000000000066450 g     F .text	000000000000005c              _ZN7hyperon26on_load___rust_ctor___ctor26on_load___rust_ctor___ctor17hf96ede0c9e76c82eE

vsbogd avatar May 30 '23 20:05 vsbogd

This issue is still present today. At least in my case, the issue seems to be related to the codegen-units setting, which defaults to 256 for debug and 16 for release builds. Lowering this value for debug builds (i.e. for the dev profile) solves the issue, although leading to increased build times:

# Cargo.toml
# ...

[profile.dev]
codegen-units = 16

paukala avatar May 06 '24 14:05 paukala

Messing with codegen-units unfortunately doesn't seem to be a surefire way of solving this problem. I've built a simple test project to reproduce this issue because I encountered it in my own code: https://github.com/dbartussek/Rust-CRT-XCU-issue

The only way I've found to keep the initializer around was to have a static variable that Rust thinks might be touched by the code run from main.

dbartussek avatar May 13 '24 18:05 dbartussek

The only way I've found to keep the initializer around was to have a static variable that Rust thinks might be touched by the code run from main.

What pattern ended up working for you?

mmastrac avatar May 21 '24 14:05 mmastrac