Closed polarathene closed 12 months ago
Below is unrelated to issue addressed above (-musl
target support with eyra
).
Slightly off-topic, but might have some relevance. I noticed eyra
made it possible for the -gnu
target to successfully build statically linked that'd otherwise fail without eyra
(that or I'm just too inexperienced to know better).
eyra
helps enable no_std
static builds for -gnu
targetI have noticed that:
// Ref: https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html
// Ref: https://darkcoding.net/software/a-very-small-rust-binary-indeed/
#![no_std]
#![no_main]
// cargo add libc --no-default-features
extern crate libc;
#[no_mangle]
pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
// since we are passing a C string, the final null character is mandatory:
const HELLO: &'static str = "Hello, world!\n\0";
unsafe { libc::printf(HELLO.as_ptr() as *const _); }
0
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }
Will build for -gnu
target dynamically fine (with glibc), but fail attempting a static build. Some changes with eyra
fixes that (add extern + drop panic handler):
#![no_std]
#![no_main]
extern crate eyra;
extern crate libc;
#[no_mangle]
pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
// since we are passing a C string, the final null character is mandatory:
const HELLO: &'static str = "Hello, world!\n\0";
unsafe { libc::printf(HELLO.as_ptr() as *const _); }
0
}
// Must remove panic handler, as eyra provides one
# ...
# Ref: https://github.com/johnthagen/min-sized-rust
[profile.release]
codegen-units = 1
lto = true
opt-level = "z"
panic = "abort"
strip = true
# Ensure `--target` is specified when using `RUSTFLAGS`:
# https://msfjarvis.dev/posts/building-static-rust-binaries-for-linux/
$ 14K dynamically linked glibc (no_std):
$ RUSTFLAGS="-Zlocation-detail=none -C relocation-model=static" cargo +nightly build --target x86_64-unknown-linux-gnu --release
# 414K static linked with eyra (despite no_std)
$ RUSTFLAGS="-C link-arg=-nostartfiles -Zlocation-detail=none -C relocation-model=static -Ctarget-feature=+crt-static" cargo +nightly build --target x86_64-unknown-linux-gnu --release
# 49K (with -Z build-std):
$ RUSTFLAGS="-C link-arg=-nostartfiles -Zlocation-detail=none -C relocation-model=static -Ctarget-feature=+crt-static" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu --release
I found that a bit interesting since the standard cargo init
hello world example didn't have a problem (31K -gnu
dynamic vs 945K -gnu
static vs 37K eyra).
UPDATE: The above example will compile for -musl
target if removing libc
crate from Cargo.toml
and giving the extern
an attribute (compile error communicates this if not building with -Z build-std
):
#![feature(rustc_private)]
extern crate libc;
-musl
target) without libc
crateThat (Workaround provided in update above). Not sure if no_std
example above would fail to build on -musl
targets tooeyra
could likewise fix that, might have something to do with the libc
crate used? (possibly due to what is discussed here)
Here's a similar hello world no_std
example without reliance on the libc
crate. This builds fine with -musl
targets, but fails with -gnu
like the earlier example. Applying the same changes to build with eyra
also enabled -gnu
static builds to work š
// Ref: https://www.reddit.com/r/rust/comments/bf8l2b/comment/elbzd5h/
#![no_std]
#![no_main]
#[no_mangle]
pub extern "C" fn main() -> isize {
const HELLO: &'static str = "Hello, world!\n";
unsafe { write(1, HELLO.as_ptr() as *const i8, HELLO.len()) };
0
}
#[link(name = "c")]
extern "C" {
fn write(fd: i32, buf: *const i8, count: usize) -> isize;
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }
With -Z build-std
command shared earlier, this will output a 13.1K
binary for both -musl
and -gnu
(with eyra
), the eyra
build being slightly smaller.
Not sure if the issues with -gnu
static linking glibc were related to caveats like this:
Note the standard limitation of static
glibc
: If you do anything using NSS (notably domain name resolution),glibc
will require matching system NSS libraries.glibc
is working on improving that.
Yes, Eyra works by implementing the existing C ABIs. Currently it's only compatible with "-gnu" ABIs. There's no fundamental reason "-musl" ABIs couldn't be supported, it just hasn't been implemented yet. It's an interesting question whether that's worth implementing; Eyra doesn't use any of the glibc or musl libraries itself, so the choice of target doesn't matter that much to Eyra.
And yes, thanks for pointing out that that's not documented in the README.md; I've now posted #28.
I have noticed that: [...] Will build for -gnu target dynamically fine (with glibc), but fail attempting a static build. Some changes with eyra fixes that (add extern + drop panic handler):
I needed to add #![feature(rustc_private)]
, but otherwise, that example build for me without Eyra, with RUSTFLAGS="-C link-arg=-nostartfiles -Zlocation-detail=none -C relocation-model=static -Ctarget-feature=+crt-static" cargo +nightly build --target x86_64-unknown-linux-gnu --release
.
// Must remove panic handler, as eyra provides one
Eyra itself doesn't provide a panic handler; Eyra depends on libraries that depend on std
, which provides a panic handler.
Eyra previously didn't support no_std
applications. With https://github.com/sunfishcode/eyra/pull/29 I've now added support for no_std
. In this mode, there is no std, so there's no panic handler, though keep in mind that there's also no global allocator or eh_personality provided either. I've added a no-std example to show how to make these work.
That said, this no-std mode doesn't currently support printf
.
Not sure if the issues with -gnu static linking glibc were related to caveats like this:
Note the standard limitation of static glibc: If you do anything using NSS (notably domain name resolution), glibc will require matching system NSS libraries. glibc is working on improving that.
Eyra doesn't have this limitation. It can statically link without depending on system NSS libraries. It resolves NSS queries by invoking the getent
command, so it respects the system NSS config without needing to link to the libraries itself.
Thanks for the great response and PRs to address feedback! ā¤ļø
No pressure to read what follows and reply, it is mostly additional information to benefit myself and any future readers.
I needed to add
#![feature(rustc_private)]
Ah ok, so nightly either way. I had found the same fix last night but for the -musl
target to build that example, but forgot to submit the updated edit š
Good to know it works for -gnu
too!
-gnu
without Eyra, or 49K static build with Eyra (_after -Z build-std
, which I didn't think would matter with no_std
_).rustc_private
feature attribute is not compatible with -Z build-std
. To get the smaller 49K build with Eyra, that example still requires adding the libc
crate to Cargo.toml
.that example built for me without Eyra, with
RUSTFLAGS="-C link-arg=-nostartfiles -Zlocation-detail=none -C relocation-model=static -Ctarget-feature=+crt-static" cargo +nightly build --target x86_64-unknown-linux-gnu --release
.
Might have been a typo on your end, but -C link-arg=-nostartfiles
(without Eyra) builds a 4.4K binary that segfaults.
It will build a functional binary without that arg present though š
eyra
and panic handler (resolved: see next section)no_std
supportEyra previously didn't support
no_std
applications.
- With #29 I've now added support for
no_std
. In this mode, there is no std, so there's no panic handler, though keep in mind that there's also no global allocator oreh_personality
provided either.- I've added a no-std example to show how to make these work.
Oh... I should really read the full response before typing the above š
The following doesn't appear needed if using the -Z build-std
approach (which still produces same 13 304 bytes
/ 13K binary as before):
I guess compared to the prior example, the binary might differ by global allocator now? (that would have been provided from std
?)
rustix-dlmalloc
references the original C version which describes itself as widely used and default allocator on Linux? So perhaps they're roughly equivalent? š¤·āāļømimalloc
(no_std
compatible), which works with both -gnu
and -musl
targets (without Eyra involved), but presumably from the build errors is not compatible with Eyra yet.eh_personality
vs panic = "abort"
I was under the impression that eh_personality
was not necessary when panic = "abort"
is configured (even without related -Z build-std
args?), but I guess that's something Rust implicitly handled with no_std
on -gnu
/ -musl
targets when building without Eyra? (EDIT: Yep, as referenced from Rust unstable book on lang-items
)
This resource notes that panic = "abort"
in Cargo.toml
should disable unwinding and thus not require eh_personality
? I can see from cargo tree
that rustix
is using the unwinding
crate which provides a no_std
compatible pure rust alternative, so perhaps this opt-out behaviour belongs upstream there?
no_std
example (without -Z build-std
)Just adding this as reference to earlier examples shared. Adapted from your no_std
example.
[dependencies]
eyra = { version = "0.16.4", default-features = false }
rustix-dlmalloc = { version = "0.1.0", features = ["global"] }
[profile.release]
strip = true
panic = "abort"
lto = true
opt-level = "z"
codegen-units = 1
#![no_std]
#![no_main]
#![feature(lang_items)]
#![feature(rustc_private)]
extern crate libc;
extern crate eyra;
#[no_mangle]
pub extern "C" fn main() -> isize {
const HELLO: &'static str = "Hello, world!\n";
unsafe { write(1, HELLO.as_ptr() as *const i8, HELLO.len()) };
0
}
#[link(name = "c")]
extern "C" {
fn write(fd: i32, buf: *const i8, count: usize) -> isize;
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }
// Additionally required for building with Eyra:
#[global_allocator]
static GLOBAL_ALLOCATOR: rustix_dlmalloc::GlobalDlmalloc = rustix_dlmalloc::GlobalDlmalloc;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
$ RUSTFLAGS="-C link-arg=-nostartfiles -Z location-detail=none -C relocation-model=static -C target-feature=+crt-static" cargo +nightly build --target x86_64-unknown-linux-gnu --release
# 17K (17,400 bytes):
$ du -b target/x86_64-unknown-linux-gnu/release/hello_world_eyra_nostd
Very cool! š
There's no fundamental reason "-musl" ABIs couldn't be supported, it just hasn't been implemented yet. It's an interesting question whether that's worth implementing; Eyra doesn't use any of the glibc or musl libraries itself, so the choice of target doesn't matter that much to Eyra.
Not something I can comment much on, way beyond my expertise :) No point in implementing and maintaining support for a target if there is no other benefit. For users like myself just having that cleared up in the README is sufficient š
It didn't require the libc extern, so no rustc_private
feature needed either, although I don't know if that'd carry over with Eyra support.
I'm only aware of how musl differs with it's allocator performing poorly, and it having a history of issues with DNS / NSS and such. None of which would apply to a build with Eyra.
Eyra doesn't have this limitation. It can statically link without depending on system NSS libraries. It resolves NSS queries by invoking the
getent
command, so it respects the system NSS config without needing to link to the libraries itself.
Good to know, not having to be aware of such gotchas is another positive for building with Eyra šŖ
Whereas with the -musl
target, users may not be aware of drawbacks that can be encountered like the default allocator (which they can change once they're aware of an actual perf issue introduced by the target).
Thanks for the detailed report! The questions here seem answered, so I'll close this now, though feel free to reopen or file new issues if there's anything to add here.
UPDATE (7th Oct 2024):
no_std
examples_) a revised unified reference example can be found at: https://github.com/sunfishcode/eyra/pull/50no_std
additions specific to eyra forpanic = "abort"
usage via theunwinding
crate (_avoid theeh_personality
requirement_)libc
+no_std
: Unlike the staticno_std
example below, the revised variant instead avoids thelibc
crate calls - which removes the need for glibc/musl targets to include:Alternatively with
cargo add libc --no-default-features
instead you do not need those two lines insrc/main.rs
.With
libc
crate inCargo.toml
, now-Z build-std
will be compatible to build with (_redundant forno_std
?_), but doing so requires additionalRUSTFLAGS
depending on glibc or musl target (_just like the above linked revisedno_std
example_):--target x86_64-unknown-linux-gnu
will need:-C link-arg=/usr/lib64/libc.a -C link-arg=/usr/lib/gcc/x86_64-redhat-linux/14/libgcc_eh.a
. These locations for*.a
static libs are specific to Fedora 41 from theglibc-static
package.-L /usr/lib64 -L link-arg=/usr/lib/gcc/x86_64-redhat-linux/14 -C link-arg=-lgcc_eh
(-C link-arg=-lc
is implicit fromlibc
crate?). The-L
hints don't seem necessary in this case,-C link-arg=-lgcc_eh
alone works.--target x86_64-unknown-linux-musl
will need-C link-arg=-lc
which pairs with the implicit-C link-self-contained=yes
for this target (without=yes
that-lc
may unintentionally dynamically link the host libc library, update: may vary by environment/linker?).NOTE: Related to these caveats for
libc
usage, if considering the optimizations detailed in thesunfishcode/origin
tiny example README, the-N
/--omagic
link arg introduces a segfault with-musl
target and the implicit-C link-self-contained=yes
, but not when set to=no
explicitly (or using-Z build-std
which has roughly the same effect).Original message
I am used to building with the
*-musl
target, but noticed thateyra
is not compatible (default hello world example generated fromcargo init
).--target
:x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
I know the README clarifies the support constraints to linux, but doesn't highlight the
-gnu
vs-musl
target compatibility? I didn't think it would matter witheyra
?Might be worthwhile to convey that incompatibility in the README too?
Reproduction
Some error messages from
-musl
target attempt:Potentially related to?:
libc
crate musl compatibility (may be resolved forlibc 0.3
release)-C link-arg=-nostartfiles
on musl target