support loading shared object

This commit is contained in:
Jianfeng Jiang
2023-03-23 03:43:48 -04:00
committed by Tate, Hongliang Tian
parent 5a2f3c94b0
commit 66d72b0104
15 changed files with 132 additions and 37 deletions

3
.gitattributes vendored
View File

@ -6,4 +6,5 @@ src/apps/execve/hello filter=lfs diff=lfs merge=lfs -text
src/apps/fork_c/fork filter=lfs diff=lfs merge=lfs -text src/apps/fork_c/fork filter=lfs diff=lfs merge=lfs -text
src/apps/signal_c/signal_test filter=lfs diff=lfs merge=lfs -text src/apps/signal_c/signal_test filter=lfs diff=lfs merge=lfs -text
src/apps/busybox/busybox filter=lfs diff=lfs merge=lfs -text src/apps/busybox/busybox filter=lfs diff=lfs merge=lfs -text
src/apps/pthread/pthread_test filter=lfs diff=lfs merge=lfs -text src/apps/pthread/pthread_test filter=lfs diff=lfs merge=lfs -text
src/apps/hello_pie/hello filter=lfs diff=lfs merge=lfs -text

View File

@ -1,9 +1,12 @@
.PHONY: build clean run .PHONY: build clean run
build: hello.c execve.c build: hello.c execve.c
@gcc -static hello.c -o hello @gcc -static hello.c -o hello
@gcc -static execve.c -o execve @gcc -static execve.c -o execve
clean: clean:
@rm hello @rm hello
@rm execve @rm execve
run: build run: build
@./execve @./execve

View File

@ -1,7 +1,10 @@
.PHONY: build clean run .PHONY: build clean run
build: fork.s build: fork.s
@gcc -static -nostdlib fork.s -o fork @gcc -static -nostdlib fork.s -o fork
clean: clean:
@rm fork @rm fork
run: build run: build
@./fork @./fork

View File

@ -1,7 +1,10 @@
.PHONY: build clean run .PHONY: build clean run
build: fork.c build: fork.c
@gcc -static fork.c -o fork @gcc -static fork.c -o fork
clean: clean:
@rm fork @rm fork
run: build run: build
@./fork @./fork

View File

@ -1,7 +1,10 @@
.PHONY: build clean run .PHONY: build clean run
build: hello.c build: hello.c
@gcc -static -mno-sse hello.c -o hello @gcc -static -mno-sse hello.c -o hello
clean: clean:
@rm hello @rm hello
run: build run: build
@./hello @./hello

View File

@ -0,0 +1,10 @@
.PHONY: build clean run
build: hello.c
@gcc hello.c -o hello
clean:
@rm hello
run: build
@./hello

3
src/apps/hello_pie/hello Executable file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:da31082cbaabc14fb9f6b62933844d61a73942dd23a28ac920d86c163126b530
size 16696

View File

@ -0,0 +1,6 @@
#include <stdio.h>
int main() {
printf("hello world from hello_c!\n");
return 0;
}

View File

@ -1,7 +1,10 @@
.PHONY: build clean run .PHONY: build clean run
build: hello_world.s build: hello_world.s
@gcc -static -nostdlib hello_world.s -o hello_world @gcc -static -nostdlib hello_world.s -o hello_world
clean: clean:
@rm hello_world @rm hello_world
run: build run: build
@./hello_world @./hello_world

View File

@ -1,7 +1,10 @@
.PHONY: build clean run .PHONY: build clean run
build: pthread_test.c build: pthread_test.c
@gcc -static pthread_test.c -lpthread -o pthread_test @gcc -static pthread_test.c -lpthread -o pthread_test
clean: clean:
@rm pthread_test @rm pthread_test
run: build run: build
@./pthread_test @./pthread_test

View File

@ -16,7 +16,7 @@ ifneq (, $(wildcard $(INITRAMFS)/. ))
endif endif
.PHONY: all clean .PHONY: all clean prepare_libs
all: $(RAMDISK) all: $(RAMDISK)
@ -25,7 +25,13 @@ $(INITRAMFS): $(APPS) $(APPS_DIRS) $(APPS_FILES)
@cp -a $(APPS)/* $@ @cp -a $(APPS)/* $@
@cd $@ && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete @cd $@ && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete
$(RAMDISK): $(INITRAMFS) $(INITRAMFS_DIRS) $(INITRAMFS_FILES) prepare_libs: $(INITRAMFS)
@mkdir -p $(INITRAMFS)/lib64
@cp -L /lib64/ld-linux-x86-64.so.2 $(INITRAMFS)/lib64
@mkdir -p $(INITRAMFS)/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libc.so.6 $(INITRAMFS)/lib/x86_64-linux-gnu
$(RAMDISK): $(INITRAMFS) $(INITRAMFS_DIRS) $(INITRAMFS_FILES) prepare_libs
@echo "Generating the ramdisk image..." @echo "Generating the ramdisk image..."
@rm -rf $(BUILD_DIR) && mkdir -p $(BUILD_DIR) @rm -rf $(BUILD_DIR) && mkdir -p $(BUILD_DIR)
@./mkinitramfs $(INITRAMFS) $@ @./mkinitramfs $(INITRAMFS) $@

View File

@ -16,8 +16,8 @@ impl Elf {
// The elf header is usually 64 bytes. pt1 is 16bytes and pt2 is 48 bytes. // The elf header is usually 64 bytes. pt1 is 16bytes and pt2 is 48 bytes.
// We require 128 bytes here is to keep consistency with linux implementations. // We require 128 bytes here is to keep consistency with linux implementations.
debug_assert!(input.len() >= 128); debug_assert!(input.len() >= 128);
let header = let header = xmas_elf::header::parse_header(input)
xmas_elf::header::parse_header(input).map_err(|_| Error::new(Errno::ENOEXEC))?; .map_err(|_| Error::with_message(Errno::ENOEXEC, "parse elf header fails"))?;
let elf_header = ElfHeader::parse_elf_header(header)?; let elf_header = ElfHeader::parse_elf_header(header)?;
check_elf_header(&elf_header)?; check_elf_header(&elf_header)?;
// than parse the program headers table // than parse the program headers table
@ -31,7 +31,7 @@ impl Elf {
let mut program_headers = Vec::with_capacity(ph_count as usize); let mut program_headers = Vec::with_capacity(ph_count as usize);
for index in 0..ph_count { for index in 0..ph_count {
let program_header = xmas_elf::program::parse_program_header(input, header, index) let program_header = xmas_elf::program::parse_program_header(input, header, index)
.map_err(|_| Error::new(Errno::ENOEXEC))?; .map_err(|_| Error::with_message(Errno::ENOEXEC, "parse program header fails"))?;
let ph64 = match program_header { let ph64 = match program_header {
xmas_elf::program::ProgramHeader::Ph64(ph64) => ph64.clone(), xmas_elf::program::ProgramHeader::Ph64(ph64) => ph64.clone(),
xmas_elf::program::ProgramHeader::Ph32(_) => { xmas_elf::program::ProgramHeader::Ph32(_) => {
@ -81,6 +81,11 @@ impl Elf {
"can not find program header table address in elf" "can not find program header table address in elf"
); );
} }
/// whether the elf is a shared object
pub fn is_shared_object(&self) -> bool {
self.elf_header.pt2.type_.as_type() == header::Type::SharedObject
}
} }
pub struct ElfHeader { pub struct ElfHeader {
@ -167,9 +172,10 @@ fn check_elf_header(elf_header: &ElfHeader) -> Result<()> {
if elf_header.pt2.machine.as_machine() != header::Machine::X86_64 { if elf_header.pt2.machine.as_machine() != header::Machine::X86_64 {
return_errno_with_message!(Errno::ENOEXEC, "Not x86_64 executable"); return_errno_with_message!(Errno::ENOEXEC, "Not x86_64 executable");
} }
// Executable file // Executable file or shared object
debug_assert_eq!(elf_header.pt2.type_.as_type(), header::Type::Executable); let elf_type = elf_header.pt2.type_.as_type();
if elf_header.pt2.type_.as_type() != header::Type::Executable { debug_assert!(elf_type == header::Type::Executable || elf_type == header::Type::SharedObject);
if elf_type != header::Type::Executable && elf_type != header::Type::SharedObject {
return_errno_with_message!(Errno::ENOEXEC, "Not executable file"); return_errno_with_message!(Errno::ENOEXEC, "Not executable file");
} }

View File

@ -70,8 +70,6 @@ pub struct InitStack {
argv: Vec<CString>, argv: Vec<CString>,
/// Environmental variables /// Environmental variables
envp: Vec<CString>, envp: Vec<CString>,
/// Auxiliary Vector
aux_vec: AuxVec,
} }
impl InitStack { impl InitStack {
@ -88,7 +86,6 @@ impl InitStack {
pos: init_stack_top, pos: init_stack_top,
argv, argv,
envp, envp,
aux_vec: AuxVec::new(),
} }
} }
@ -112,9 +109,9 @@ impl InitStack {
self.init_stack_top - self.init_stack_size self.init_stack_top - self.init_stack_size
} }
pub fn init(&mut self, root_vmar: &Vmar<Full>, elf: &Elf) -> Result<()> { pub fn init(&mut self, root_vmar: &Vmar<Full>, elf: &Elf, aux_vec: &mut AuxVec) -> Result<()> {
self.map_and_zeroed(root_vmar)?; self.map_and_zeroed(root_vmar)?;
self.write_stack_content(root_vmar, elf)?; self.write_stack_content(root_vmar, elf, aux_vec)?;
self.debug_print_stack_content(root_vmar); self.debug_print_stack_content(root_vmar);
Ok(()) Ok(())
} }
@ -139,10 +136,11 @@ impl InitStack {
root_vmar: &Vmar<Full>, root_vmar: &Vmar<Full>,
envp_pointers: &Vec<u64>, envp_pointers: &Vec<u64>,
argv_pointers: &Vec<u64>, argv_pointers: &Vec<u64>,
aux_vec: &AuxVec,
) -> Result<()> { ) -> Result<()> {
// ensure 8-byte alignment // ensure 8-byte alignment
self.write_u64(0, root_vmar)?; self.write_u64(0, root_vmar)?;
let auxvec_size = (self.aux_vec.table().len() + 1) * (mem::size_of::<u64>() * 2); let auxvec_size = (aux_vec.table().len() + 1) * (mem::size_of::<u64>() * 2);
let envp_pointers_size = (envp_pointers.len() + 1) * mem::size_of::<u64>(); let envp_pointers_size = (envp_pointers.len() + 1) * mem::size_of::<u64>();
let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::<u64>(); let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::<u64>();
let argc_size = mem::size_of::<u64>(); let argc_size = mem::size_of::<u64>();
@ -153,7 +151,12 @@ impl InitStack {
Ok(()) Ok(())
} }
fn write_stack_content(&mut self, root_vmar: &Vmar<Full>, elf: &Elf) -> Result<()> { fn write_stack_content(
&mut self,
root_vmar: &Vmar<Full>,
elf: &Elf,
aux_vec: &mut AuxVec,
) -> Result<()> {
// write a zero page. When a user program tries to read a cstring(like argv) from init stack, // write a zero page. When a user program tries to read a cstring(like argv) from init stack,
// it will typically read 4096 bytes and then find the first '\0' in the buffer // it will typically read 4096 bytes and then find the first '\0' in the buffer
// (we now read 128 bytes, which is set by MAX_FILENAME_LEN). // (we now read 128 bytes, which is set by MAX_FILENAME_LEN).
@ -169,13 +172,9 @@ impl InitStack {
// write random value // write random value
let random_value = generate_random_for_aux_vec(); let random_value = generate_random_for_aux_vec();
let random_value_pointer = self.write_bytes(&random_value, root_vmar)?; let random_value_pointer = self.write_bytes(&random_value, root_vmar)?;
self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?; aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?;
self.aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?; self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers, &aux_vec)?;
self.aux_vec.set(AuxKey::AT_PHDR, elf.ph_addr()? as u64)?; self.write_aux_vec(root_vmar, aux_vec)?;
self.aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u64)?;
self.aux_vec.set(AuxKey::AT_PHENT, elf.ph_ent() as u64)?;
self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers)?;
self.write_aux_vec(root_vmar)?;
self.write_envp_pointers(root_vmar, envp_pointers)?; self.write_envp_pointers(root_vmar, envp_pointers)?;
self.write_argv_pointers(root_vmar, argv_pointers)?; self.write_argv_pointers(root_vmar, argv_pointers)?;
// write argc // write argc
@ -214,13 +213,12 @@ impl InitStack {
Ok(argv_pointers) Ok(argv_pointers)
} }
fn write_aux_vec(&mut self, root_vmar: &Vmar<Full>) -> Result<()> { fn write_aux_vec(&mut self, root_vmar: &Vmar<Full>, aux_vec: &mut AuxVec) -> Result<()> {
// Write NULL auxilary // Write NULL auxilary
self.write_u64(0, root_vmar)?; self.write_u64(0, root_vmar)?;
self.write_u64(AuxKey::AT_NULL as u64, root_vmar)?; self.write_u64(AuxKey::AT_NULL as u64, root_vmar)?;
// Write Auxiliary vectors // Write Auxiliary vectors
let aux_vec: Vec<_> = self let aux_vec: Vec<_> = aux_vec
.aux_vec
.table() .table()
.iter() .iter()
.map(|(aux_key, aux_value)| (*aux_key, *aux_value)) .map(|(aux_key, aux_value)| (*aux_key, *aux_value))
@ -322,6 +320,20 @@ impl InitStack {
} }
} }
pub fn init_aux_vec(elf: &Elf, elf_map_addr: Option<Vaddr>) -> Result<AuxVec> {
let mut aux_vec = AuxVec::new();
aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?;
let ph_addr = if elf.is_shared_object() {
elf.ph_addr()? + elf_map_addr.unwrap()
} else {
elf.ph_addr()?
};
aux_vec.set(AuxKey::AT_PHDR, ph_addr as u64)?;
aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u64)?;
aux_vec.set(AuxKey::AT_PHENT, elf.ph_ent() as u64)?;
Ok(aux_vec)
}
/// generate random [u8; 16]. /// generate random [u8; 16].
/// FIXME: generate really random value. Now only return array with fixed values. /// FIXME: generate really random value. Now only return array with fixed values.
fn generate_random_for_aux_vec() -> [u8; 16] { fn generate_random_for_aux_vec() -> [u8; 16] {

View File

@ -2,7 +2,7 @@
//! When create a process from elf file, we will use the elf_load_info to construct the VmSpace //! When create a process from elf file, we will use the elf_load_info to construct the VmSpace
use crate::fs::file_handle::FileHandle; use crate::fs::file_handle::FileHandle;
use crate::process::program_loader::elf::init_stack::InitStack; use crate::process::program_loader::elf::init_stack::{init_aux_vec, InitStack};
use crate::vm::perms::VmPerms; use crate::vm::perms::VmPerms;
use crate::vm::vmo::VmoRightsOp; use crate::vm::vmo::VmoRightsOp;
use crate::{ use crate::{
@ -32,10 +32,16 @@ pub fn load_elf_to_root_vmar(
envp: Vec<CString>, envp: Vec<CString>,
) -> Result<ElfLoadInfo> { ) -> Result<ElfLoadInfo> {
let elf = Elf::parse_elf(file_header)?; let elf = Elf::parse_elf(file_header)?;
map_segment_vmos(&elf, root_vmar, elf_file)?; let map_addr = map_segment_vmos(&elf, root_vmar, elf_file)?;
let mut aux_vec = init_aux_vec(&elf, map_addr)?;
let mut init_stack = InitStack::new_default_config(argv, envp); let mut init_stack = InitStack::new_default_config(argv, envp);
init_stack.init(root_vmar, &elf)?; init_stack.init(root_vmar, &elf, &mut aux_vec)?;
let elf_load_info = ElfLoadInfo::new(elf.entry_point(), init_stack.user_stack_top()); let entry_point = if elf.is_shared_object() {
elf.entry_point() + map_addr.unwrap()
} else {
elf.entry_point()
};
let elf_load_info = ElfLoadInfo::new(entry_point, init_stack.user_stack_top());
debug!("load elf succeeds."); debug!("load elf succeeds.");
Ok(elf_load_info) Ok(elf_load_info)
} }
@ -67,17 +73,26 @@ pub fn map_segment_vmos(
elf: &Elf, elf: &Elf,
root_vmar: &Vmar<Full>, root_vmar: &Vmar<Full>,
elf_file: Arc<FileHandle>, elf_file: Arc<FileHandle>,
) -> Result<()> { ) -> Result<Option<Vaddr>> {
let is_shared_object = elf.is_shared_object();
let mut file_map_addr = None;
for program_header in &elf.program_headers { for program_header in &elf.program_headers {
let type_ = program_header let type_ = program_header
.get_type() .get_type()
.map_err(|_| Error::new(Errno::ENOEXEC))?; .map_err(|_| Error::with_message(Errno::ENOEXEC, "parse program header type fails"))?;
if type_ == program::Type::Load { if type_ == program::Type::Load {
let vmo = init_segment_vmo(program_header, elf_file.clone())?; let vmo = init_segment_vmo(program_header, elf_file.clone())?;
map_segment_vmo(program_header, vmo, root_vmar, elf_file.clone())?; map_segment_vmo(
program_header,
vmo,
root_vmar,
elf_file.clone(),
&mut file_map_addr,
is_shared_object,
)?;
} }
} }
Ok(()) Ok(file_map_addr)
} }
/// map the segment vmo to root_vmar /// map the segment vmo to root_vmar
@ -86,11 +101,29 @@ fn map_segment_vmo(
vmo: Vmo, vmo: Vmo,
root_vmar: &Vmar<Full>, root_vmar: &Vmar<Full>,
elf_file: Arc<FileHandle>, elf_file: Arc<FileHandle>,
file_map_addr: &mut Option<Vaddr>,
is_shared_object: bool,
) -> Result<()> { ) -> Result<()> {
let perms = VmPerms::from(parse_segment_perm(program_header.flags)?); let perms = VmPerms::from(parse_segment_perm(program_header.flags)?);
let offset = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE); let offset = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE);
let vm_map_options = root_vmar.new_map(vmo, perms)?.offset(offset); debug!(
"map segment vmo: offset = 0x{:x}, virtual_addr = 0x{:x}",
offset, program_header.virtual_addr
);
let mut vm_map_options = root_vmar.new_map(vmo, perms)?;
// offset = 0 means the vmo can be put at any address
if is_shared_object {
if let Some(file_init_addr) = *file_map_addr {
let offset = file_init_addr + offset;
vm_map_options = vm_map_options.offset(offset);
}
} else {
vm_map_options = vm_map_options.offset(offset);
}
let map_addr = vm_map_options.build()?; let map_addr = vm_map_options.build()?;
if is_shared_object && *file_map_addr == None {
*file_map_addr = Some(map_addr);
}
Ok(()) Ok(())
} }

View File

@ -59,6 +59,6 @@ pub fn load_program_to_root_vmar(
} }
let elf_file = Arc::new(FileHandle::new_inode_handle(file)); let elf_file = Arc::new(FileHandle::new_inode_handle(file));
let elf_load_info = load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp)?; debug!("load executable, path = {}", executable_path);
Ok((abs_path, elf_load_info)) load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp)
} }