Sorry, this is a resurrection of a 4-year-old thread, since I wanted to ask a few follow-up questions and have some additional design discussions:
At the moment, one fairly large pain-point with using Rust to write C APIs intended for wider use (i.e., pure-Rust .sos) is the lack of proper symbol versioning support. At best, you can find a handful of tricks that are claimed to resolve the issue but usually do not work and fail with hard-to-understand linker errors. Maybe I'm just unlucky or missing something, but I have never managed to get these workarounds to work. Obviously some folks don't need or use symbol versioning but if you are building a library that is going to be used somewhat widely then symbol versioning becomes a necessity.
@zackw (sorry for pinging you) said that LLM does not support __attribute__((semver)). This does appear to be true, but LLVM does understand raw .semver ... ASM instructions in the context of LTO as far as I can see (i.e., it doesn't see them as "opaque ASM instructions"). For instance:
- There are quite a few code references to
symvergeneration (Code search results · GitHub). - ⚙ D30485 Perform symbol binding for .symver versioned symbols added support for
.symver name,foo@@versionbinding tonameas the default back in 2017. - different symbol versioning behaviour between LLD and GNU bfd ld · Issue #65017 · llvm/llvm-project · GitHub basically says that
.symver ...is supported, just that a particular corner case acts differently to GNU ld.
I couldn't find any real documentation about it, but it seems like .symver foo1,foo@A and .symver foo2,foo@@A are fully supported? Is this not sufficient for what we would need to have #[export_name] support symbol versioning?
I think that the API proposed in the above thread by @zackw looks reasonable, and I would be willing to work on it if there aren't any other issues I've missed. Here is the previously proposed API (using the new unsafe(...) wrapper):
#[unsafe(export_name = ("foo", version = "LIBX_1.0"))]
pub fn old_foo () {}
#[unsafe(export_name = ("foo", version = "LIBX_2.0", default_version))]
pub fn new_foo () {}
#[unsafe(export_name = [
("bar", version = "LIBX_1.0"),
("bar", version = "LIBX_2.0", default_version),
("foobar", version = "LIBY_0.5") // an obsolete fork
])]
pub fn i_am_many_bars () {}
EDIT: I think #[unsafe(export_name = (name = "foo", version = "LIBA_1.0"))] is maybe a little better, if a little verbose...