Merge branch 'auxv' into 'master'
Implement auxiliary vector See merge request redox-os/kernel!136
This commit is contained in:
@@ -196,10 +196,16 @@ impl Grant {
|
||||
pub fn start_address(&self) -> VirtualAddress {
|
||||
self.start
|
||||
}
|
||||
pub unsafe fn set_start_address(&mut self, start: VirtualAddress) {
|
||||
self.start = start;
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
pub unsafe fn set_size(&mut self, size: usize) {
|
||||
self.size = size;
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> EntryFlags {
|
||||
self.flags
|
||||
|
||||
@@ -73,6 +73,11 @@ impl<'a> Elf<'a> {
|
||||
pub fn entry(&self) -> usize {
|
||||
self.header.e_entry as usize
|
||||
}
|
||||
|
||||
/// Get the program header offset
|
||||
pub fn program_headers(&self) -> usize {
|
||||
self.header.e_phoff as usize
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ElfSections<'a> {
|
||||
|
||||
@@ -187,7 +187,7 @@ pub extern fn userspace_init() {
|
||||
}
|
||||
}
|
||||
|
||||
syscall::fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None).expect("failed to execute init");
|
||||
syscall::fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None, None).expect("failed to execute init");
|
||||
|
||||
panic!("init returned");
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use core::cmp;
|
||||
use crate::context;
|
||||
use crate::context::memory::Grant;
|
||||
use crate::memory::{free_frames, used_frames};
|
||||
use crate::paging::VirtualAddress;
|
||||
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
|
||||
use crate::paging::{ActivePageTable, Page, VirtualAddress};
|
||||
use crate::paging::entry::EntryFlags;
|
||||
use crate::syscall::data::{Map, StatVfs};
|
||||
use crate::syscall::data::{Map, Map2, StatVfs};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{PROT_EXEC, PROT_READ, PROT_WRITE};
|
||||
use crate::syscall::flag::{MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
|
||||
pub struct MemoryScheme;
|
||||
@@ -24,7 +25,7 @@ impl Scheme for MemoryScheme {
|
||||
let used = used_frames() as u64;
|
||||
let free = free_frames() as u64;
|
||||
|
||||
stat.f_bsize = 4096;
|
||||
stat.f_bsize = PAGE_SIZE as u32;
|
||||
stat.f_blocks = used + free;
|
||||
stat.f_bfree = free;
|
||||
stat.f_bavail = stat.f_bfree;
|
||||
@@ -32,7 +33,7 @@ impl Scheme for MemoryScheme {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fmap(&self, _id: usize, map: &Map) -> Result<usize> {
|
||||
fn fmap2(&self, _id: usize, map: &Map2) -> Result<usize> {
|
||||
//TODO: Abstract with other grant creation
|
||||
if map.size == 0 {
|
||||
Ok(0)
|
||||
@@ -41,10 +42,22 @@ impl Scheme for MemoryScheme {
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let fixed = map.flags.contains(MapFlags::MAP_FIXED);
|
||||
let fixed_noreplace = map.flags.contains(MapFlags::MAP_FIXED_NOREPLACE);
|
||||
|
||||
let mut grants = context.grants.lock();
|
||||
|
||||
let full_size = ((map.size + 4095)/4096) * 4096;
|
||||
let mut to_address = crate::USER_GRANT_OFFSET;
|
||||
let full_size = ((map.size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
|
||||
|
||||
let mut to_address = if map.address == 0 { crate::USER_GRANT_OFFSET } else {
|
||||
if
|
||||
map.address + full_size >= crate::PML4_SIZE * 256 // There are 256 PML4 entries reserved for userspace
|
||||
&& map.address % PAGE_SIZE != 0
|
||||
{
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
map.address
|
||||
};
|
||||
|
||||
let mut entry_flags = EntryFlags::PRESENT | EntryFlags::USER_ACCESSIBLE;
|
||||
if !map.flags.contains(PROT_EXEC) {
|
||||
@@ -58,27 +71,97 @@ impl Scheme for MemoryScheme {
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < grants.len() {
|
||||
let start = grants[i].start_address().get();
|
||||
if to_address + full_size < start {
|
||||
break;
|
||||
|
||||
while i < grants.len() {
|
||||
let grant = &mut grants[i];
|
||||
|
||||
let grant_start = grant.start_address().get();
|
||||
let grant_len = ((grant.size() + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
|
||||
let grant_end = grant_start + grant_len;
|
||||
|
||||
if to_address < grant_start || grant_end <= to_address {
|
||||
// grant has nothing to do with the memory to map, and thus we can safely just
|
||||
// go on to the next one.
|
||||
|
||||
if !fixed {
|
||||
// Use the default grant offset, or if we've already passed it, anything after that.
|
||||
to_address = cmp::max(
|
||||
cmp::max(crate::USER_GRANT_OFFSET, grant_end),
|
||||
to_address,
|
||||
);
|
||||
}
|
||||
i += 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let pages = (grants[i].size() + 4095) / 4096;
|
||||
let end = start + pages * 4096;
|
||||
to_address = end;
|
||||
i += 1;
|
||||
// check whether this grant overlaps with the memory range to use, by checking that
|
||||
// the start and end of the grant is not within the memory range to map
|
||||
if grant_start <= to_address && grant_end > to_address || grant_start <= to_address + full_size && grant_end > to_address + full_size {
|
||||
// the range overlaps, thus we'll have to continue to the next grant, or to
|
||||
// insert a new grant at the end (if not MapFlags::MAP_FIXED).
|
||||
|
||||
if fixed_noreplace {
|
||||
println!("grant: conflicts with: {:#x} - {:#x}", grant_start, grant_end);
|
||||
return Err(Error::new(EEXIST));
|
||||
} else if fixed {
|
||||
/*
|
||||
// shrink the grant, removing it if necessary. since the to_address isn't
|
||||
// changed at all when mapping to a fixed address, we can just continue to
|
||||
// the next grant and shrink or remove that one if it was also overlapping.
|
||||
if to_address + full_size > grant_start {
|
||||
let new_start = core::cmp::min(grant_end, to_address + full_size);
|
||||
|
||||
let new_size = grant.size() - (new_start - grant_start);
|
||||
unsafe { grant.set_size(new_size) };
|
||||
grant_len = ((new_size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
|
||||
|
||||
let new_start = VirtualAddress::new(new_start);
|
||||
unsafe { grant.set_start_address(new_start) };
|
||||
grant_start = new_start;
|
||||
|
||||
grant_end = grant_start + grant_len;
|
||||
}
|
||||
*/
|
||||
// TODO
|
||||
return Err(Error::new(EOPNOTSUPP));
|
||||
} else {
|
||||
to_address = grant_end;
|
||||
i += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
grants.insert(i, Grant::map(
|
||||
VirtualAddress::new(to_address),
|
||||
full_size,
|
||||
entry_flags
|
||||
));
|
||||
let start_address = VirtualAddress::new(to_address);
|
||||
let end_address = VirtualAddress::new(to_address + full_size);
|
||||
|
||||
// Make sure it's absolutely not mapped already
|
||||
let active_table = unsafe { ActivePageTable::new() };
|
||||
|
||||
for page in Page::range_inclusive(Page::containing_address(start_address), Page::containing_address(end_address)) {
|
||||
if active_table.translate_page(page).is_some() {
|
||||
return Err(Error::new(EEXIST))
|
||||
}
|
||||
}
|
||||
|
||||
grants.insert(i, Grant::map(start_address, full_size, entry_flags));
|
||||
|
||||
Ok(to_address)
|
||||
}
|
||||
}
|
||||
fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
|
||||
if map.flags.contains(MapFlags::MAP_FIXED) {
|
||||
// not supported for fmap, which lacks the address argument.
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
self.fmap2(id, &Map2 {
|
||||
offset: map.offset,
|
||||
size: map.size,
|
||||
flags: map.flags,
|
||||
address: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
|
||||
@@ -2,7 +2,7 @@ use core::{ascii, mem};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use super::data::{Map, Stat, TimeSpec};
|
||||
use super::data::{Map, Map2, Stat, TimeSpec};
|
||||
use super::flag::*;
|
||||
use super::number::*;
|
||||
use super::validate::*;
|
||||
@@ -114,6 +114,14 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
d/mem::size_of::<Map>()
|
||||
),
|
||||
),
|
||||
SYS_FMAP2 => format!(
|
||||
"fmap2({}, {:?})",
|
||||
b,
|
||||
validate_slice(
|
||||
c as *const Map2,
|
||||
d/mem::size_of::<Map2>()
|
||||
),
|
||||
),
|
||||
SYS_FUNMAP => format!(
|
||||
"funmap({:#X})",
|
||||
b
|
||||
|
||||
@@ -23,11 +23,11 @@ use crate::scheme::FileHandle;
|
||||
use crate::start::usermode;
|
||||
use crate::syscall::data::{SigAction, Stat};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{wifcontinued, wifstopped, CloneFlags, CLONE_FILES,
|
||||
CLONE_FS, CLONE_SIGHAND, CLONE_STACK, CLONE_VFORK, CLONE_VM, MapFlags,
|
||||
PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, PTRACE_STOP_EXIT,
|
||||
SigActionFlags, SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK, SIGCONT, SIGTERM,
|
||||
WaitFlags, WCONTINUED, WNOHANG,WUNTRACED};
|
||||
use crate::syscall::flag::{wifcontinued, wifstopped, AT_ENTRY, AT_NULL, AT_PHDR, CloneFlags,
|
||||
CLONE_FILES, CLONE_FS, CLONE_SIGHAND, CLONE_STACK, CLONE_VFORK, CLONE_VM,
|
||||
MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE,
|
||||
PTRACE_STOP_EXIT, SigActionFlags, SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK,
|
||||
SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED};
|
||||
use crate::syscall::ptrace_event;
|
||||
use crate::syscall::validate::{validate_slice, validate_slice_mut};
|
||||
|
||||
@@ -655,7 +655,8 @@ fn fexec_noreturn(
|
||||
name: Box<[u8]>,
|
||||
data: Box<[u8]>,
|
||||
args: Box<[Box<[u8]>]>,
|
||||
vars: Box<[Box<[u8]>]>
|
||||
vars: Box<[Box<[u8]>]>,
|
||||
auxv: Box<[usize]>,
|
||||
) -> ! {
|
||||
let entry;
|
||||
let singlestep;
|
||||
@@ -807,27 +808,40 @@ fn fexec_noreturn(
|
||||
context.tls = Some(tls);
|
||||
}
|
||||
|
||||
let mut push = |arg| {
|
||||
sp -= mem::size_of::<usize>();
|
||||
unsafe { *(sp as *mut usize) = arg; }
|
||||
};
|
||||
|
||||
// Push auxiliary vector
|
||||
push(AT_NULL);
|
||||
for &arg in auxv.iter().rev() {
|
||||
push(arg);
|
||||
}
|
||||
|
||||
drop(auxv); // no longer required
|
||||
|
||||
let mut arg_size = 0;
|
||||
|
||||
// Push arguments and variables
|
||||
// Push environment variables and arguments
|
||||
for iter in &[&vars, &args] {
|
||||
// Push null-terminator
|
||||
sp -= mem::size_of::<usize>();
|
||||
unsafe { *(sp as *mut usize) = 0; }
|
||||
push(0);
|
||||
|
||||
// Push content
|
||||
// Push pointer to content
|
||||
for arg in iter.iter().rev() {
|
||||
sp -= mem::size_of::<usize>();
|
||||
unsafe { *(sp as *mut usize) = crate::USER_ARG_OFFSET + arg_size; }
|
||||
|
||||
push(crate::USER_ARG_OFFSET + arg_size);
|
||||
arg_size += arg.len() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Push arguments length
|
||||
sp -= mem::size_of::<usize>();
|
||||
unsafe { *(sp as *mut usize) = args.len(); }
|
||||
// For some reason, Linux pushes the argument count here (in
|
||||
// addition to being null-terminated), but not the environment
|
||||
// variable count.
|
||||
// TODO: Push more counts? Less? Stop having null-termination?
|
||||
push(args.len());
|
||||
|
||||
// Write environment and argument pointers to USER_ARG_OFFSET
|
||||
if arg_size > 0 {
|
||||
let mut memory = context::memory::Memory::new(
|
||||
VirtualAddress::new(crate::USER_ARG_OFFSET),
|
||||
@@ -856,8 +870,9 @@ fn fexec_noreturn(
|
||||
context.image.push(memory.to_shared());
|
||||
}
|
||||
|
||||
// Args no longer required, can deallocate
|
||||
// Args and vars no longer required, can deallocate
|
||||
drop(args);
|
||||
drop(vars);
|
||||
|
||||
context.actions = Arc::new(Mutex::new(vec![(
|
||||
SigAction {
|
||||
@@ -906,7 +921,7 @@ fn fexec_noreturn(
|
||||
unsafe { usermode(entry, sp, 0, singlestep) }
|
||||
}
|
||||
|
||||
pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option<Box<[u8]>>) -> Result<usize> {
|
||||
pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option<Box<[u8]>>, auxv: Option<Vec<usize>>) -> Result<usize> {
|
||||
let (uid, gid) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
@@ -978,72 +993,92 @@ pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>
|
||||
return Err(Error::new(E2BIG));
|
||||
}
|
||||
|
||||
match elf::Elf::from(&data) {
|
||||
Ok(elf) => {
|
||||
// We check the validity of all loadable sections here
|
||||
for segment in elf.segments() {
|
||||
match segment.p_type {
|
||||
program_header::PT_INTERP => {
|
||||
//TODO: length restraint, parse interp earlier
|
||||
let mut interp = vec![0; segment.p_memsz as usize];
|
||||
unsafe {
|
||||
intrinsics::copy((elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8,
|
||||
interp.as_mut_ptr(),
|
||||
segment.p_filesz as usize);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < interp.len() {
|
||||
if interp[i] == 0 {
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
interp.truncate(i);
|
||||
|
||||
println!(" interpreter: {:?}", ::core::str::from_utf8(&interp));
|
||||
|
||||
let interp_fd = super::fs::open(&interp, super::flag::O_RDONLY | super::flag::O_CLOEXEC)?;
|
||||
|
||||
let mut args_vec = Vec::from(args);
|
||||
args_vec.insert(0, interp.into_boxed_slice());
|
||||
//TODO: pass file handle in auxv
|
||||
let name_override = name.into_boxed_slice();
|
||||
args_vec[1] = name_override.clone();
|
||||
|
||||
return fexec_kernel(
|
||||
interp_fd,
|
||||
args_vec.into_boxed_slice(),
|
||||
vars,
|
||||
Some(name_override),
|
||||
);
|
||||
},
|
||||
program_header::PT_LOAD => {
|
||||
let voff = segment.p_vaddr as usize % PAGE_SIZE;
|
||||
let vaddr = segment.p_vaddr as usize - voff;
|
||||
|
||||
// Due to the Userspace and kernel TLS bases being located right above 2GB,
|
||||
// limit any loadable sections to lower than that. Eventually we will need
|
||||
// to replace this with a more intelligent TLS address
|
||||
if vaddr >= 0x8000_0000 {
|
||||
println!("exec: invalid section address {:X}", segment.p_vaddr);
|
||||
return Err(Error::new(ENOEXEC));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
let elf = match elf::Elf::from(&data) {
|
||||
Ok(elf) => elf,
|
||||
Err(err) => {
|
||||
println!("fexec: failed to execute {}: {}", fd.into(), err);
|
||||
return Err(Error::new(ENOEXEC));
|
||||
}
|
||||
};
|
||||
|
||||
// `fexec_kernel` can recurse if an interpreter is found. We get the
|
||||
// auxiliary vector from the first invocation, which is passed via an
|
||||
// argument, or if this is the first one we create it.
|
||||
let auxv = if let Some(auxv) = auxv {
|
||||
auxv
|
||||
} else {
|
||||
let mut auxv = Vec::with_capacity(3);
|
||||
|
||||
auxv.push(AT_ENTRY);
|
||||
auxv.push(elf.entry());
|
||||
auxv.push(AT_PHDR);
|
||||
auxv.push(elf.program_headers());
|
||||
|
||||
auxv
|
||||
};
|
||||
|
||||
// We check the validity of all loadable sections here
|
||||
for segment in elf.segments() {
|
||||
match segment.p_type {
|
||||
program_header::PT_INTERP => {
|
||||
//TODO: length restraint, parse interp earlier
|
||||
let mut interp = vec![0; segment.p_memsz as usize];
|
||||
unsafe {
|
||||
intrinsics::copy((elf.data.as_ptr() as usize + segment.p_offset as usize) as *const u8,
|
||||
interp.as_mut_ptr(),
|
||||
segment.p_filesz as usize);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < interp.len() {
|
||||
if interp[i] == 0 {
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
interp.truncate(i);
|
||||
|
||||
println!(" interpreter: {:?}", ::core::str::from_utf8(&interp));
|
||||
|
||||
let interp_fd = super::fs::open(&interp, super::flag::O_RDONLY | super::flag::O_CLOEXEC)?;
|
||||
|
||||
let mut args_vec = Vec::from(args);
|
||||
//TODO: pass file handle in auxv
|
||||
let name_override = name.into_boxed_slice();
|
||||
args_vec[0] = name_override.clone();
|
||||
|
||||
// Drop variables, since fexec_kernel probably won't return
|
||||
drop(elf);
|
||||
drop(interp);
|
||||
|
||||
return fexec_kernel(
|
||||
interp_fd,
|
||||
args_vec.into_boxed_slice(),
|
||||
vars,
|
||||
Some(name_override),
|
||||
Some(auxv),
|
||||
);
|
||||
},
|
||||
program_header::PT_LOAD => {
|
||||
let voff = segment.p_vaddr as usize % PAGE_SIZE;
|
||||
let vaddr = segment.p_vaddr as usize - voff;
|
||||
|
||||
// Due to the Userspace and kernel TLS bases being located right above 2GB,
|
||||
// limit any loadable sections to lower than that. Eventually we will need
|
||||
// to replace this with a more intelligent TLS address
|
||||
if vaddr >= 0x8000_0000 {
|
||||
println!("exec: invalid section address {:X}", segment.p_vaddr);
|
||||
return Err(Error::new(ENOEXEC));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// This is the point of no return, quite literaly. Any checks for validity need
|
||||
// to be done before, and appropriate errors returned. Otherwise, we have nothing
|
||||
// to return to.
|
||||
fexec_noreturn(setuid, setgid, name.into_boxed_slice(), data.into_boxed_slice(), args, vars);
|
||||
fexec_noreturn(setuid, setgid, name.into_boxed_slice(), data.into_boxed_slice(), args, vars, auxv.into_boxed_slice());
|
||||
}
|
||||
|
||||
pub fn fexec(fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) -> Result<usize> {
|
||||
@@ -1064,7 +1099,7 @@ pub fn fexec(fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) -
|
||||
// Neither arg_ptrs nor var_ptrs should be used after this point, the kernel
|
||||
// now has owned copies in args and vars
|
||||
|
||||
fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None)
|
||||
fexec_kernel(fd, args.into_boxed_slice(), vars.into_boxed_slice(), None, None)
|
||||
}
|
||||
|
||||
pub fn exit(status: usize) -> ! {
|
||||
|
||||
2
syscall
2
syscall
Submodule syscall updated: 122878874d...6346fd671e
Reference in New Issue
Block a user