-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Description
I was trying to make an implementation of the c standard library in rust and tried this code, while targeting x86_64-unknown-linux-none
:
#![no_std]
#![feature(c_size_t)]
use core::panic::PanicInfo;
pub use ccc::{write_string,strlen};
pub use syscalls::exit;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
exit(1);
}
mod ccc {
use core::ffi::{CStr, c_char, c_size_t};
/// # Safety
/// str has to be a non null pointer to a c string
#[unsafe(no_mangle)]
pub unsafe extern "C" fn strlen(str: *const core::ffi::c_char) -> c_size_t {
unsafe { CStr::from_ptr(str).count_bytes() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn write_string(str: *mut c_char) {
unsafe { *str = *c"hiii".as_ptr() }
}
}
mod syscalls {
use core::arch::asm;
#[unsafe(no_mangle)]
pub extern "C" fn exit(exit_code: u64) -> ! {
unsafe {
asm!(
"
mov rax, 60
mov rdi, {}
syscall
",
in(reg) exit_code,
options(noreturn)
);
}
}
}
With the following Cargo.toml:
cargo-features = ["per-package-target"]
[package]
name = "rlibc"
version = "0.1.0"
edition = "2024"
forced-target = "x86_64-unknown-linux-none"
[dependencies]
[lib]
crate-type = ["cdylib"]
test = false
doctest = false
bench = false
and compiled using cargo build -Zbuild-std --release --target x86_64-unknown-linux-none
I was expecting to see strlen
as an item in the resulting .so file, but only exit
and write_string
are present.
nm target/x86_64-unknown-linux-none/release/librlibc.so
0000000000002328 d _DYNAMIC
0000000000001310 T exit
0000000000001320 T write_string
Notably, if i apply this patch:
--- Cargo.toml2 2025-10-05 18:40:25.524604557 +0300
+++ Cargo.toml 2025-10-05 18:40:59.232435849 +0300
@@ -4,7 +4,6 @@
name = "rlibc"
version = "0.1.0"
edition = "2024"
-forced-target = "x86_64-unknown-linux-none"
[dependencies]
@@ -13,3 +12,6 @@
test = false
doctest = false
bench = false
+
+[profile.release]
+panic = "abort"
then strlen
does appear in the output for x86_64-linux-gnu
nm target/release/librlibc.so
0000000000003868 b completed.0
w __cxa_finalize
0000000000001580 t deregister_tm_clones
00000000000015f0 t __do_global_dtors_aux
0000000000002690 d __do_global_dtors_aux_fini_array_entry
0000000000003860 d __dso_handle
00000000000026a0 d _DYNAMIC
0000000000001640 T exit
000000000000156c t _fini
0000000000001630 t frame_dummy
0000000000002698 d __frame_dummy_init_array_entry
00000000000004f8 r __FRAME_END__
0000000000002840 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000001550 t _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
00000000000015b0 t register_tm_clones
0000000000001650 T strlen
0000000000003860 d __TMC_END__
0000000000003860 d __TMC_LIST__
0000000000001660 T write_string
Meta
rustc --version --verbose
:
rustc 1.92.0-nightly (595b9a498 2025-10-03)
binary: rustc
commit-hash: 595b9a498bc55fcd30111e430d8e4290ed833b4c
commit-date: 2025-10-03
host: x86_64-unknown-linux-gnu
release: 1.92.0-nightly
LLVM version: 21.1.2
Update: after disassemblying both libraries i've noticed that the code at the strlen
label for the x86_64-unknown-linux-gnu
was broken, e.g. was just calling itself in an infinite loop. I suppose that could be because the CStr::count_bytes
function is also labeled strlen
. However, after writing my own implementation of strlen
the linux-none
library still didn't have a strlen
item (even though the problem in linux-gnu
was solved)