What is the current state of dynamic linking in Rust?

I was reading the news on redox-os.org and saw they have gotten dynamic linking into the project, I was wondering if this applies to or could apply to the greater rust language and just generally what the state of dynamic linking for rust is in 2025.

Unless I am mistaken two of the big features for rust that will enable it to see much greater adoption in systems programing is a stable rust ABI rather than depending solely on C ABI and stable dynamic linking for rust.

Below is the in-lined excerpt from Redox OS that refers to some work being done in the dynamic linking space:

Thanks to Anhad Singh for his amazing work on Dynamic Linking! In this southern-hemisphere-Redox-Summer-of-Code project, Anhad has implemented dynamic linking as the default build method for many recipes, and all new porting can use dynamic linking with relatively little effort.

This is a huge step forward for Redox, because relibc can now become a stable ABI. And having a stable ABI is one of the prerequisites for Redox to reach “Release 1.0”.

Other links:
The state of dynamically linking Rust binaries

what's wrong with the C ABI?

the term ABI has different meanings in different context. when it comes to dynamic linking, we need to formally specify at least two things: the symbol mangling algorithm, and data structure layout.

the mangling of symbols is tightly coupled with the type system. a stable mangling scheme would negatively impact the the freedom how the type system can be evolved (in a backward compatible way).

unspecified memory layout of data structures enables more aggresive optimization.

so, if we want to stablize the "ABI" while keeping the possibility to evolve the language in the future, we'll have to define some "subset" that can be guaranteed to be stable, at which point, we just reinvented the C ABI, more or less.

I don't think they are talking about the dynamic linking support of the rust language here. relibc is an implementation of, libc, after all. I'm not really into redox developoment, but I think they are mainly referring to the dynamic linker implementation improvements in this news post. redox people please correct me if I was wrong.

1 Like

I'm not aware of any active plans for a stable Rust ABI. There's a proposal for a system that can lower to the C ABI. But it would be distinct from the native repr and thus require a translation in many cases (albeit perhaps automatically supplied).

There's some discussion of why a stable Rust ABI is currently untenable in there and/or the linked PR.

Neither are merged and I can't tell if it's stalled out or not.

1 Like

All of that is over my head, but I will comment anyway.
I thought part of the "safety" of rust is that everything is static included in you binary so you can be sure that the binary does what it is supposed to do. If you have dynamic links then the dynamic libs could change and your code would be calling something that might not do what your original code thought it would do.
So my guess is part of the dynamic linking is verifying that the dynamic library does not change and gets hashed and verified when loaded.

So libraries can be "dynamic" in the sense that it is a shared library, but not "dynamic" that can be changed without recompiling the binaries that depend on it.

No. C ABI couldn't express things like Option<Foo> or Result<Bar, Baz>. Other rust-like language achieve that… but it's still huge cost and crazy amount of work.

Possible benefits are enormous. Possible drawbacks and implementation complexity are enormous, too.

True, but hardly relevant: with enough indirections and function thunking one may adopt these changes into the dynamic linking at the cost of efficiency – but then you don't normally expect that cross-DLL call would be as efficient as inlined in-library call anyway.

That concern is valid, but also limited. Would just mean that changes to internal ABI should be treated with about the same rigor as changed to API are treated. If Rust can support stability without stagnation in it's public API then why couldn't it do the same with ABI?

Sure, all kinds of all versions of ABIs would need to be supported, but, again many languages managed to do that (from Java and C# to Ada and Swift), why couldn't Rust do that?

So everything is certainly possible the question is whether it's feasible.

This would make most useful use-cases impossible. Because in practice dynamic linking desire, most often, comes not from the ability to use dynamic libraries to design you application, but more as the desire to make it extendable with plugins.

After all libraries that you deliver you can link into your app statically, the cost is only in mamory and SSD usage… but if plugin doesn't exist when you app is developed. then it's impossible to verify it.

You don't even need to have stable ABI for that. That's already supported.

The question is, of course, about libraries that don't exist when you build the binary.

P.S. I just hate that Rust plays a Buridan's ass for more than ten years now. It could have picked the TMP/comptime side and provided nice metaprogramming capabilities. Or it could have picked Ada/Java/Swift side, kept generics as generics and provided dynamic linking. But as it's developed now it combines worst sides of two approaches while giving developers none of the benefits. That's sad.

2 Likes

That is a safety feature, not a bug.

It's most definitely a bug. Because dynamic linking as you envision it already exists and is fully supported by Rust.

Adding your “safety feature” to it is not hard and can be done without any changes to the language.

Ergo: when people as for dynamic linking they want something else.

It would be pointless to ask “what's the current state of XXX” if XXX is already exist and is fully supported, isn't it?

Note that if you're defining Option and Result you can slap a #[repr(C)] on top of them to give them a stable ABI that's expressible in terms of C structs and unions. Type layout - The Rust Reference

What does duck typing have to do with dynamic linking?

And how can I then pass these to the bazillion libraries that expect core::option::Option or core::option::Result? Including the all-important ? operator?

What does duck typing have to do with dynamic linking?

They are two mutually incompatible choices.

If you base your language on templates and the ability to compile code at runtime then you end up with Julia, where there recently improved “hello, world” to “only” take 150MB instead of 900MB (and yes, these are megabytes, not kilobytes). That's as far from “no runtime” language as one may imagine.

If you base your language on generics (that are passed together with “type descriptors”) across the dynamic libraries borders then you have to restrict them to be able to have bounded, limited, set of functions that may, at tuntime give you descriptor for Foo<Bar, Baz> without bringing full-blown compiler with you. Assume that all three types (Foo, Bar, Baz) live in three different dynamic languages, think about implications.

Rust is neither here not there and supports neither of these (with no plans to go in one direction or the other AFAICS).

You convert them at the API boundaries. If you want to fix the layout of Option and Result then changing it later won't be backward compatible.

This seems to make a big assumption about how generics are compiled down (i.e. with "type descriptors"). Rust uses monomorphization which has its own advantages, like the ability to have (near) zero cost abstraction, but it doesn't play well with dynamic linking.

Then it's not really a Rust API, is it? Rust API works with Option and Result, Rust API supports ? and other common convention.

Sure, some idioms may not be supported when dynamic libraries are used, but most common and typical needs to be supported, or else you couldn't offer the same API as both static and dynamic library – and that choice is usually left to the end user, in most languages.

That's assumption for the dynamic library case. No one prevents you from compiling them differently in a static library, where all the information is available and can be processed statically.

That's not true and you know it. Rust uses “type descriptors” if you use dyn keyword. They are just incredibly restricted today. But nothing prevents them from being expanded to parity with impl. Of course to handle dynamic linking case it would have to be expanded… but that's desirable direction for programs that don't do dynamic linking, too: the desire to have clone in an “object safe” trait is common. “Foreign” types would also be “unknown sized”, but Swift did that, why Rust can't?

Basically: I don't see anything impossible there, everything is most definitely possible, while the question of how feasible would it be remains open. It's like a reflection: it's desirable and interesting but no one works on it, right now, after the well known story.