From 66d72b0104bcd49766ae38bf57810087edabf392 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Thu, 23 Mar 2023 03:43:48 -0400 Subject: [PATCH] support loading shared object --- .gitattributes | 3 +- src/apps/execve/Makefile | 3 ++ src/apps/fork/Makefile | 3 ++ src/apps/fork_c/Makefile | 3 ++ src/apps/hello_c/Makefile | 3 ++ src/apps/hello_pie/Makefile | 10 ++++ src/apps/hello_pie/hello | 3 ++ src/apps/hello_pie/hello.c | 6 +++ src/apps/hello_world/Makefile | 3 ++ src/apps/pthread/Makefile | 3 ++ src/ramdisk/Makefile | 10 +++- .../process/program_loader/elf/elf_file.rs | 18 ++++--- .../process/program_loader/elf/init_stack.rs | 46 ++++++++++------- .../process/program_loader/elf/load_elf.rs | 51 +++++++++++++++---- .../src/process/program_loader/mod.rs | 4 +- 15 files changed, 132 insertions(+), 37 deletions(-) create mode 100644 src/apps/hello_pie/Makefile create mode 100755 src/apps/hello_pie/hello create mode 100644 src/apps/hello_pie/hello.c diff --git a/.gitattributes b/.gitattributes index d19710b5c..5da868347 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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/signal_c/signal_test 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 \ No newline at end of file +src/apps/pthread/pthread_test filter=lfs diff=lfs merge=lfs -text +src/apps/hello_pie/hello filter=lfs diff=lfs merge=lfs -text diff --git a/src/apps/execve/Makefile b/src/apps/execve/Makefile index e16195e14..1a5184c03 100644 --- a/src/apps/execve/Makefile +++ b/src/apps/execve/Makefile @@ -1,9 +1,12 @@ .PHONY: build clean run + build: hello.c execve.c @gcc -static hello.c -o hello @gcc -static execve.c -o execve + clean: @rm hello @rm execve + run: build @./execve \ No newline at end of file diff --git a/src/apps/fork/Makefile b/src/apps/fork/Makefile index f4790cb82..f045feea0 100644 --- a/src/apps/fork/Makefile +++ b/src/apps/fork/Makefile @@ -1,7 +1,10 @@ .PHONY: build clean run + build: fork.s @gcc -static -nostdlib fork.s -o fork + clean: @rm fork + run: build @./fork \ No newline at end of file diff --git a/src/apps/fork_c/Makefile b/src/apps/fork_c/Makefile index 6a9a5e3ac..03d5e36b4 100644 --- a/src/apps/fork_c/Makefile +++ b/src/apps/fork_c/Makefile @@ -1,7 +1,10 @@ .PHONY: build clean run + build: fork.c @gcc -static fork.c -o fork + clean: @rm fork + run: build @./fork \ No newline at end of file diff --git a/src/apps/hello_c/Makefile b/src/apps/hello_c/Makefile index 4f7008ab0..f6dfdfe8b 100644 --- a/src/apps/hello_c/Makefile +++ b/src/apps/hello_c/Makefile @@ -1,7 +1,10 @@ .PHONY: build clean run + build: hello.c @gcc -static -mno-sse hello.c -o hello + clean: @rm hello + run: build @./hello \ No newline at end of file diff --git a/src/apps/hello_pie/Makefile b/src/apps/hello_pie/Makefile new file mode 100644 index 000000000..ae07ee0e6 --- /dev/null +++ b/src/apps/hello_pie/Makefile @@ -0,0 +1,10 @@ +.PHONY: build clean run + +build: hello.c + @gcc hello.c -o hello + +clean: + @rm hello + +run: build + @./hello \ No newline at end of file diff --git a/src/apps/hello_pie/hello b/src/apps/hello_pie/hello new file mode 100755 index 000000000..946abd00c --- /dev/null +++ b/src/apps/hello_pie/hello @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da31082cbaabc14fb9f6b62933844d61a73942dd23a28ac920d86c163126b530 +size 16696 diff --git a/src/apps/hello_pie/hello.c b/src/apps/hello_pie/hello.c new file mode 100644 index 000000000..d7f033f7e --- /dev/null +++ b/src/apps/hello_pie/hello.c @@ -0,0 +1,6 @@ +#include + +int main() { + printf("hello world from hello_c!\n"); + return 0; +} \ No newline at end of file diff --git a/src/apps/hello_world/Makefile b/src/apps/hello_world/Makefile index 69261d763..28bc7407e 100644 --- a/src/apps/hello_world/Makefile +++ b/src/apps/hello_world/Makefile @@ -1,7 +1,10 @@ .PHONY: build clean run + build: hello_world.s @gcc -static -nostdlib hello_world.s -o hello_world + clean: @rm hello_world + run: build @./hello_world \ No newline at end of file diff --git a/src/apps/pthread/Makefile b/src/apps/pthread/Makefile index c08b4ceaa..36e965ac0 100644 --- a/src/apps/pthread/Makefile +++ b/src/apps/pthread/Makefile @@ -1,7 +1,10 @@ .PHONY: build clean run + build: pthread_test.c @gcc -static pthread_test.c -lpthread -o pthread_test + clean: @rm pthread_test + run: build @./pthread_test \ No newline at end of file diff --git a/src/ramdisk/Makefile b/src/ramdisk/Makefile index e199fc8e4..76e531518 100644 --- a/src/ramdisk/Makefile +++ b/src/ramdisk/Makefile @@ -16,7 +16,7 @@ ifneq (, $(wildcard $(INITRAMFS)/. )) endif -.PHONY: all clean +.PHONY: all clean prepare_libs all: $(RAMDISK) @@ -25,7 +25,13 @@ $(INITRAMFS): $(APPS) $(APPS_DIRS) $(APPS_FILES) @cp -a $(APPS)/* $@ @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..." @rm -rf $(BUILD_DIR) && mkdir -p $(BUILD_DIR) @./mkinitramfs $(INITRAMFS) $@ diff --git a/src/services/libs/jinux-std/src/process/program_loader/elf/elf_file.rs b/src/services/libs/jinux-std/src/process/program_loader/elf/elf_file.rs index d3892f369..f7ff0dce4 100644 --- a/src/services/libs/jinux-std/src/process/program_loader/elf/elf_file.rs +++ b/src/services/libs/jinux-std/src/process/program_loader/elf/elf_file.rs @@ -16,8 +16,8 @@ impl Elf { // 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. debug_assert!(input.len() >= 128); - let header = - xmas_elf::header::parse_header(input).map_err(|_| Error::new(Errno::ENOEXEC))?; + let header = xmas_elf::header::parse_header(input) + .map_err(|_| Error::with_message(Errno::ENOEXEC, "parse elf header fails"))?; let elf_header = ElfHeader::parse_elf_header(header)?; check_elf_header(&elf_header)?; // than parse the program headers table @@ -31,7 +31,7 @@ impl Elf { let mut program_headers = Vec::with_capacity(ph_count as usize); for index in 0..ph_count { 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 { xmas_elf::program::ProgramHeader::Ph64(ph64) => ph64.clone(), xmas_elf::program::ProgramHeader::Ph32(_) => { @@ -81,6 +81,11 @@ impl 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 { @@ -167,9 +172,10 @@ fn check_elf_header(elf_header: &ElfHeader) -> Result<()> { if elf_header.pt2.machine.as_machine() != header::Machine::X86_64 { return_errno_with_message!(Errno::ENOEXEC, "Not x86_64 executable"); } - // Executable file - debug_assert_eq!(elf_header.pt2.type_.as_type(), header::Type::Executable); - if elf_header.pt2.type_.as_type() != header::Type::Executable { + // Executable file or shared object + let elf_type = elf_header.pt2.type_.as_type(); + 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"); } diff --git a/src/services/libs/jinux-std/src/process/program_loader/elf/init_stack.rs b/src/services/libs/jinux-std/src/process/program_loader/elf/init_stack.rs index 19449d7e1..91088dfcf 100644 --- a/src/services/libs/jinux-std/src/process/program_loader/elf/init_stack.rs +++ b/src/services/libs/jinux-std/src/process/program_loader/elf/init_stack.rs @@ -70,8 +70,6 @@ pub struct InitStack { argv: Vec, /// Environmental variables envp: Vec, - /// Auxiliary Vector - aux_vec: AuxVec, } impl InitStack { @@ -88,7 +86,6 @@ impl InitStack { pos: init_stack_top, argv, envp, - aux_vec: AuxVec::new(), } } @@ -112,9 +109,9 @@ impl InitStack { self.init_stack_top - self.init_stack_size } - pub fn init(&mut self, root_vmar: &Vmar, elf: &Elf) -> Result<()> { + pub fn init(&mut self, root_vmar: &Vmar, elf: &Elf, aux_vec: &mut AuxVec) -> Result<()> { 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); Ok(()) } @@ -139,10 +136,11 @@ impl InitStack { root_vmar: &Vmar, envp_pointers: &Vec, argv_pointers: &Vec, + aux_vec: &AuxVec, ) -> Result<()> { // ensure 8-byte alignment self.write_u64(0, root_vmar)?; - let auxvec_size = (self.aux_vec.table().len() + 1) * (mem::size_of::() * 2); + let auxvec_size = (aux_vec.table().len() + 1) * (mem::size_of::() * 2); let envp_pointers_size = (envp_pointers.len() + 1) * mem::size_of::(); let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::(); let argc_size = mem::size_of::(); @@ -153,7 +151,12 @@ impl InitStack { Ok(()) } - fn write_stack_content(&mut self, root_vmar: &Vmar, elf: &Elf) -> Result<()> { + fn write_stack_content( + &mut self, + root_vmar: &Vmar, + 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, // 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). @@ -169,13 +172,9 @@ impl InitStack { // write random value let random_value = generate_random_for_aux_vec(); let random_value_pointer = self.write_bytes(&random_value, root_vmar)?; - self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?; - self.aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?; - self.aux_vec.set(AuxKey::AT_PHDR, elf.ph_addr()? as u64)?; - 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)?; + aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?; + self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers, &aux_vec)?; + self.write_aux_vec(root_vmar, aux_vec)?; self.write_envp_pointers(root_vmar, envp_pointers)?; self.write_argv_pointers(root_vmar, argv_pointers)?; // write argc @@ -214,13 +213,12 @@ impl InitStack { Ok(argv_pointers) } - fn write_aux_vec(&mut self, root_vmar: &Vmar) -> Result<()> { + fn write_aux_vec(&mut self, root_vmar: &Vmar, aux_vec: &mut AuxVec) -> Result<()> { // Write NULL auxilary self.write_u64(0, root_vmar)?; self.write_u64(AuxKey::AT_NULL as u64, root_vmar)?; // Write Auxiliary vectors - let aux_vec: Vec<_> = self - .aux_vec + let aux_vec: Vec<_> = aux_vec .table() .iter() .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) -> Result { + 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]. /// FIXME: generate really random value. Now only return array with fixed values. fn generate_random_for_aux_vec() -> [u8; 16] { diff --git a/src/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs b/src/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs index 4f79bf086..e6c2304c0 100644 --- a/src/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs +++ b/src/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs @@ -2,7 +2,7 @@ //! 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::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::vmo::VmoRightsOp; use crate::{ @@ -32,10 +32,16 @@ pub fn load_elf_to_root_vmar( envp: Vec, ) -> Result { 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); - init_stack.init(root_vmar, &elf)?; - let elf_load_info = ElfLoadInfo::new(elf.entry_point(), init_stack.user_stack_top()); + init_stack.init(root_vmar, &elf, &mut aux_vec)?; + 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."); Ok(elf_load_info) } @@ -67,17 +73,26 @@ pub fn map_segment_vmos( elf: &Elf, root_vmar: &Vmar, elf_file: Arc, -) -> Result<()> { +) -> Result> { + let is_shared_object = elf.is_shared_object(); + let mut file_map_addr = None; for program_header in &elf.program_headers { let type_ = program_header .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 { 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 @@ -86,11 +101,29 @@ fn map_segment_vmo( vmo: Vmo, root_vmar: &Vmar, elf_file: Arc, + file_map_addr: &mut Option, + is_shared_object: bool, ) -> Result<()> { let perms = VmPerms::from(parse_segment_perm(program_header.flags)?); 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()?; + if is_shared_object && *file_map_addr == None { + *file_map_addr = Some(map_addr); + } Ok(()) } diff --git a/src/services/libs/jinux-std/src/process/program_loader/mod.rs b/src/services/libs/jinux-std/src/process/program_loader/mod.rs index d13dc7a34..c825a0249 100644 --- a/src/services/libs/jinux-std/src/process/program_loader/mod.rs +++ b/src/services/libs/jinux-std/src/process/program_loader/mod.rs @@ -59,6 +59,6 @@ pub fn load_program_to_root_vmar( } 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)?; - Ok((abs_path, elf_load_info)) + debug!("load executable, path = {}", executable_path); + load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp) }