Redefine a symbol

Hello! I am compiling a existing library from source to be linked as a native module to another program.
That program requires a specific symbol to be defined.
I need something equivalent to objcopy --redefine-sym Alice=Bob my_lib.a.
I tried

[build]
rustflags = []
rustflags = [
    "-C", "link-arg=-Wl,--verbose",
    "-C", "link-arg=-DAlice=Bob",
]

but nm -g my_lib.a | c++filt > symbols.txt doesn't show Alice

archives (aka static libraries) don't go through the linker.

did you try attributes like export_name and no_mangle?

Hi! Thanks for your answer.
I tried to repeat the same steps for .so and got the same result with no change in symbols.

I didn't try that. If possible I don't want to change sources. Is it possible to do that with configs?

the -D options is for the gcc front end, when invoke gcc as linker, the option will be parsed but has no effect.

unfortunately, I don't know if you can rename a symbol at link time, but I believe you can create aliases with a linker script, will that suffice your need?

for example, create a linker script to define the symbol Bob:

Bob = Alice;

then simply add the path of the linker script file to the linker command line as an input file:

rustflags = [ "-C", "link-arg=./bob.ld" ]

then a symbol named Bob should appear in the so file.

2 Likes

I created bob.ld in the root of the project and run cargo build again. I still can't find the new symbol.

To be more concrete I am trying to replace PyInit__pydantic_core with PyInit_anything when compiling GitHub - pydantic/pydantic-core: Core validation logic for pydantic written in rust with command cargo build --target=x86_64-unknown-linux-gnu -v. But no matter what I try the only symbol in lib_pydantic_core.so starting with PyInit is PyInit__pydantic_core.

it seems the exported symbol is generated by a procedural macro and the module name can be customized via an option, see doc and implementation

but nevertheless, aliases defined in linker script should work regardless.

if the linker didn't show any error messages, the symbol should be there, unless the linker script is not passed to the linker.

make sure the linker script is actually used by the linker. if you intentionally introduce errors, such as:

Bob = some_undefined_symbol;

then build again, did the linker complain about undefined symbol?

1 Like

Yes. It does complain with an error.

But when I compile with Bob = PyInit__pydantic_core; and try to check symbols with nm -g lib_pydantic_core.so | c++filt | grep Bob I see nothing.

Yeah, it works #[pyo3(name = "pydantic_core")]. But only with a valid identifier as a name. I need numbers after PyInit_ like PyInit_10something. In a sense that #[pyo3(name = "10something")] won't compile.

well, it seems the symbol defined in linker script is local, on my machine, I got:

00000000000072f0 g     F .text  000000000000002a              alice
00000000000072f0 l     F .text  0000000000000000              bob

which is different from the doc, to quote:

You may assign a value to a symbol in a linker script. This will define the symbol and place it into the symbol table with a global scope.

I'm at lost now. I don't know how to define a global symbol without modifying the source code. so I'd suggest just add a name option to the pymodule attribute macro, it is just easier than tinkering with the linker.

1 Like

Thank you. It is a valid suggestion.
But in my case I need a name that is not a valid Rust identifier. In the end it would be valid with a prefix PyInit_, but at a compile time it is not valid.

in that case, maybe you can just define an "proxy" function which forwards to the procedurally generated entry point, something like this:

// declare the exported entry point which will be resolved at link type
extern "C" {
	fn entry_point();
}
// define our "proxy" entry point
#[export_name = "another_entry_point"]
pub extern "C" fn proxy() {
	unsafe {
		entry_point()
	}
}
// pseudo code for the procedural generated entry point
mod procedurally_generated {
	#[export_name = "entry_point"]
	pub extern "C" fn init() {
		println!("hello, this is the entry point.");
	}
}

it's not perfect, but it should work. and if you enable LTO, the symbols should point to the same address.

[profile.release]
lto = true
1 Like