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_namespecifies what symbol to import for a given function inside anexternblock. 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_nameattribute 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_nameattributes 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!
in general. I'm weakly in favor of a separate attribute, because aliases are unsupported on some platforms (e.g. Darwin), so it's useful to distinguish them from the functionality that works on all platforms.