#[cfg(any(
feature = "take-charge",
all(not(feature = "unwinding"), feature = "panic-handler-trap")
))]
use core::arch::asm;
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
use core::ptr::without_provenance_mut;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::elf::Elf_Ehdr;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
#[cfg(feature = "take-charge")]
#[cfg(feature = "signal")]
use linux_raw_sys::general::{__NR_rt_sigreturn, __NR_sigreturn};
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
use {
core::ffi::c_void,
linux_raw_sys::general::{__NR_clone, __NR_exit, __NR_munmap},
rustix::thread::RawPid,
};
#[cfg(feature = "origin-start")]
naked_fn!(
"
The program entry point.
# Safety
This function must never be called explicitly. It is the first thing
executed in the program, and it assumes that memory is laid out according
to the operating system convention for starting a new program.
";
pub(super) fn _start() -> !;
"mov eax, esp", "push ebp", "push ebp", "push ebp", "push eax", "push ebp", "jmp {entry}"; entry = sym super::program::entry
);
#[cfg(any(
feature = "take-charge",
all(not(feature = "unwinding"), feature = "panic-handler-trap")
))]
pub(super) fn trap() -> ! {
unsafe {
asm!("ud2", options(noreturn, nostack));
}
}
#[cfg(any(
all(feature = "thread", feature = "take-charge"),
all(feature = "experimental-relocate", feature = "origin-start")
))]
#[allow(dead_code)]
pub(super) fn dynamic_table_addr() -> *const linux_raw_sys::elf::Elf_Dyn {
let addr;
unsafe {
asm!(
".weak _DYNAMIC",
".hidden _DYNAMIC",
"call 2f",
"2:",
"pop {0}", "add {0}, offset _GLOBAL_OFFSET_TABLE_+1",
"lea {0}, [{0} + _DYNAMIC@GOTOFF]",
out(reg) addr
)
}
addr
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
let addr;
unsafe {
asm!(
"call 2f",
".cfi_adjust_cfa_offset 4",
"2:",
"pop {0}",
".cfi_adjust_cfa_offset -4",
"3:",
".ifne (3b-2b)-1",
".error \"The pop opcode is expected to be 1 byte long.\"",
".endif",
"add {0}, offset _GLOBAL_OFFSET_TABLE_+1",
"lea {0}, [{0} + __ehdr_start@GOTOFF]",
out(reg) addr
)
}
addr
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
#[inline]
pub(super) unsafe fn relocation_load(ptr: usize) -> usize {
unsafe {
let r0;
asm!(
"mov {}, [{}]",
out(reg) r0,
in(reg) ptr,
options(nostack, preserves_flags),
);
r0
}
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
#[inline]
pub(super) unsafe fn relocation_store(ptr: usize, value: usize) {
unsafe {
asm!(
"mov [{}], {}",
in(reg) ptr,
in(reg) value,
options(nostack, preserves_flags),
);
}
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
#[inline]
pub(super) unsafe fn relocation_mprotect_readonly(ptr: usize, len: usize) {
unsafe {
let r0: usize;
asm!(
"int 0x80",
inlateout("eax") __NR_mprotect as usize => r0,
in("ebx") ptr,
in("ecx") len,
in("edx") PROT_READ,
options(nostack, preserves_flags),
);
if r0 != 0 {
trap();
}
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
pub(super) const STACK_ALIGNMENT: usize = 16;
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) unsafe fn clone(
flags: u32,
child_stack: *mut c_void,
parent_tid: *mut RawPid,
child_tid: *mut RawPid,
newtls: *mut c_void,
fn_: extern "C" fn(),
num_args: usize,
) -> isize {
unsafe {
let mut gs: u32 = 0;
asm!("mov {0:x}, gs", inout(reg) gs);
let entry_number = gs >> 3;
let mut user_desc = rustix::runtime::UserDesc {
entry_number,
base_addr: newtls.addr() as u32,
limit: 0xfffff,
_bitfield_align_1: [],
_bitfield_1: Default::default(),
__bindgen_padding_0: [0_u8; 3_usize],
};
user_desc.set_seg_32bit(1);
user_desc.set_contents(0);
user_desc.set_read_exec_only(0);
user_desc.set_limit_in_pages(1);
user_desc.set_seg_not_present(0);
user_desc.set_useable(1);
let newtls: *const _ = &user_desc;
let child_stack = child_stack.cast::<*mut c_void>().sub(1);
child_stack.write(fn_ as _);
let r0;
asm!(
"push esi", "push ebp",
"mov ebp, [eax+8]",
"mov esi, [eax+0]", "mov eax, [eax+4]",
"int 0x80", "test eax, eax", "jnz 2f",
"pop edi", "mov esi, esp", "push eax", "push ebp", "push esi", "push edi", "xor ebp, ebp", "push eax", "jmp {entry}",
"2:",
"pop ebp", "pop esi",
entry = sym super::thread::entry,
inout("eax") &[
newtls.cast::<c_void>().cast_mut(),
without_provenance_mut(__NR_clone as usize),
without_provenance_mut(num_args)
] => r0,
in("ebx") flags,
in("ecx") child_stack,
in("edx") parent_tid,
in("edi") child_tid,
);
r0
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) unsafe fn set_thread_pointer(ptr: *mut c_void) {
unsafe {
let mut user_desc = rustix::runtime::UserDesc {
entry_number: !0u32,
base_addr: ptr.addr() as u32,
limit: 0xfffff,
_bitfield_align_1: [],
_bitfield_1: Default::default(),
__bindgen_padding_0: [0_u8; 3_usize],
};
user_desc.set_seg_32bit(1);
user_desc.set_contents(0);
user_desc.set_read_exec_only(0);
user_desc.set_limit_in_pages(1);
user_desc.set_seg_not_present(0);
user_desc.set_useable(1);
let res = rustix::runtime::set_thread_area(&mut user_desc);
debug_assert_eq!(res, Ok(()));
debug_assert_ne!(user_desc.entry_number, !0);
asm!("mov gs, {0:x}", in(reg) ((user_desc.entry_number << 3) | 3) as u16);
debug_assert_eq!(*ptr.cast::<*const c_void>(), ptr);
debug_assert_eq!(thread_pointer(), ptr);
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) fn thread_pointer() -> *mut c_void {
let ptr;
unsafe {
asm!("mov {}, gs:0", out(reg) ptr, options(nostack, preserves_flags, readonly));
}
ptr
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
pub(super) const TLS_OFFSET: usize = 0;
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) unsafe fn munmap_and_exit_thread(map_addr: *mut c_void, map_len: usize) -> ! {
unsafe {
asm!(
"int 0x80",
"xor ebx, ebx",
"mov eax, {__NR_exit}",
"int 0x80",
"ud2",
__NR_exit = const __NR_exit,
in("eax") __NR_munmap,
in("ebx") map_addr,
in("ecx") map_len,
options(noreturn, nostack)
);
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "signal")]
naked_fn!(
"
Invoke the `__NR_rt_sigreturn` system call to return control from a signal
handler.
# Safety
This function must never be called other than by the `sa_restorer`
mechanism.
";
pub(super) fn return_from_signal_handler() -> ();
"mov eax, {__NR_rt_sigreturn}",
"int 0x80",
"ud2";
__NR_rt_sigreturn = const __NR_rt_sigreturn
);
#[cfg(feature = "take-charge")]
#[cfg(feature = "signal")]
naked_fn!(
"
Invoke the appropriate system call to return control from a signal
handler that does not use `SA_SIGINFO`.
# Safety
This function must never be called other than by the `sa_restorer`
mechanism.
";
pub(super) fn return_from_signal_handler_noinfo() -> ();
"pop eax",
"mov eax, {__NR_sigreturn}",
"int 0x80",
"ud2";
__NR_sigreturn = const __NR_sigreturn
);