Problem
C supports aliasing of function names. To be precise, GCC defines function attributes that can be used to define function aliases with different linkage types. Rust does not, to my knowledge, provide a similar feature.
While translating libxml2 with the C2Rust transpiler, I came across the following pattern relying on function aliases:
int sum(int a, int b);
extern __typeof (sum) sum__internal_alias __attribute((visibility("hidden")));
#define sum sum__internal_alias
// define sum__internal_alias
int sum(int a, int b) {
return a + b;
}
int (*const int_ptr)(int, int) = ∑
int call_sum__internal_alias(void) {
return sum(2, 2);
}
#undef sum
extern __typeof (sum) sum __attribute((alias("sum__internal_alias")));
int (*const ext_ptr)(int, int) = ∑
int call_sum(void) {
return sum(2, 2);
}
int return_one(void) {
return int_ptr == ext_ptr;
}
The above is condensed version of the pattern used in libxml2
to optimize calls when building a shared library. This lets internal calls (i.e., calls that do not cross a module boundary) avoid indirection through the global offset table to increase performance. See https://github.com/GNOME/libxml2/blob/mainline/elfgcchack.h and its uses for details.
My colleagues and I attempted two approaches to automatically translate this pattern into Rust:
First Attempt: Function Wrappers
For the purposes of translating C to Rust, we considered emitting wrapper functions for each alias. While this adds a level of indirection when calling a function alias, it has the advantage of letting us control the visibility of each alias. Unfortunately, the function wrapping approach would not fully match the semantics of aliased functions in GNU C which requires that function pointers are equal for aliases.
Second Attempt: Function Attributes
Rust includes two possibly useful function attributes:
-
link_name
specifies what symbol to import for a given function inside anextern
block. This lets us rename an extern function locally (i.e., inside a single module). Unfortunately, what we want here is a way to assign two names to the same function globally. - The symbol names in the compiled output of a crate are derived from the names used in the source code by default. The
export_name
attribute exports a function under a different name in the compiled output. While it is possible to specify multiple exported names, it does not seem possible to export a function under different names as only one of theexport_name
attributes has any effect.
First Solution: Extending the export_name
Attribute
Since the export_name
attribute comes close to the functionality we seek, we could extend it to allow multiple uses of the attribute to take effect thus exporting a function under multiple aliases like so:
#[no_mangle]
#[export_name("sum__internal_alias"] // use visibility of fn definition by default
#[export_name("sum", visibility = "default")] // explicit visibility (default|protected|hidden)
pub(crate) unsafe extern "C" fn sum__internal_alias(a: i32, b: i32) -> i32 {
a + b
}
Having to specify the exported name twice is clunky so we could optionally change the semantics of export_name
to also export the function symbol under is source-level name by default. Since export_name
does not create a source-level alias, this solution would require the C2Rust transpiler to track and resolve calls to aliased functions inside the same translation unit.
Second Solution: Adding an alias
Attribute
Adding a brand new alias
attribute to introduce aliases at the source level is another possibility. In that case, we could simply emit the following Rust to create an alias:
#[no_mangle]
#[alias("sum")]
pub(crate) unsafe extern "C" fn sum__internal_alias(a: i32, b: i32) -> i32 {
a + b
}
In this case, one would be able to write Rust code that calls sum
from anywhere else in the same crate. This approach also minimizes the additional complexity of the C2Rust transpiler and refactoring tool. The alias
attribute should support an optional visibility
key in this case as well.
We are seeking input on these suggestions; if there is some other way of accomplishing our goals, please let us know. In case there is consensus around a particular solution, we're happy to contribute a patch. Looking forward to hear everybody's thoughts and suggestions!