From db40e8f52b61f700dcdc8e522e30ec1c5f2bf7c1 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Fri, 5 Apr 2019 21:03:05 +0800 Subject: [PATCH] Enable Thread Local Storage (TLS) Add arch_prctl and pass necessary ELF info to libc via aux vector --- src/libos/src/process/arch_prctl.rs | 43 ++++++++++++++++++++ src/libos/src/process/mod.rs | 2 + src/libos/src/process/spawn/elf_helper.rs | 30 ++++++++------ src/libos/src/process/spawn/init_stack.rs | 1 - src/libos/src/process/spawn/init_vm.rs | 10 ++--- src/libos/src/process/spawn/mod.rs | 34 +++++++++++----- src/libos/src/syscall/mod.rs | 9 ++++ src/libos/src/syscall/syscall_entry_x86-64.S | 10 +++-- test/Makefile | 2 +- test/tls/Makefile | 5 +++ test/tls/main.c | 11 +++++ 11 files changed, 124 insertions(+), 33 deletions(-) create mode 100644 src/libos/src/process/arch_prctl.rs create mode 100644 test/tls/Makefile create mode 100644 test/tls/main.c diff --git a/src/libos/src/process/arch_prctl.rs b/src/libos/src/process/arch_prctl.rs new file mode 100644 index 00000000..c148e8ba --- /dev/null +++ b/src/libos/src/process/arch_prctl.rs @@ -0,0 +1,43 @@ +use super::*; + +#[allow(non_camel_case_types)] +#[derive(Debug)] +pub enum ArchPrctlCode { + ARCH_SET_GS = 0x1001, + ARCH_SET_FS = 0x1002, + ARCH_GET_FS = 0x1003, + ARCH_GET_GS = 0x1004, +} + +impl ArchPrctlCode { + pub fn from_u32(bits: u32) -> Result { + match bits { + 0x1001 => Ok(ArchPrctlCode::ARCH_SET_GS), + 0x1002 => Ok(ArchPrctlCode::ARCH_SET_FS), + 0x1003 => Ok(ArchPrctlCode::ARCH_GET_FS), + 0x1004 => Ok(ArchPrctlCode::ARCH_GET_GS), + _ => errno!(EINVAL, "Unknown code for arch_prctl"), + } + } +} + +pub fn do_arch_prctl(code: ArchPrctlCode, addr: *mut usize) -> Result<(), Error> { + info!("do_arch_prctl: code: {:?}, addr: {:#o}", code, addr as usize); + match code { + ArchPrctlCode::ARCH_SET_FS => { + let current_ref = get_current(); + let mut current = current_ref.lock().unwrap(); + let task = &mut current.task; + task.user_fsbase_addr = addr as usize; + }, + ArchPrctlCode::ARCH_GET_FS => { + let current_ref = get_current(); + let current = current_ref.lock().unwrap(); + let task = ¤t.task; + unsafe { *addr = task.user_fsbase_addr; } + }, + ArchPrctlCode::ARCH_SET_GS | ArchPrctlCode::ARCH_GET_GS + => return errno!(EINVAL, "GS cannot be accessed from the user space"), + } + Ok(()) +} diff --git a/src/libos/src/process/mod.rs b/src/libos/src/process/mod.rs index b048d97a..37539bc1 100644 --- a/src/libos/src/process/mod.rs +++ b/src/libos/src/process/mod.rs @@ -8,6 +8,7 @@ pub use self::spawn::{do_spawn, FileAction}; pub use self::wait::{WaitQueue, Waiter}; pub use self::thread::{do_clone, CloneFlags, ThreadGroup}; pub use self::futex::{FutexOp, FutexFlags, futex_op_and_flags_from_u32, futex_wake, futex_wait}; +pub use self::arch_prctl::{ArchPrctlCode, do_arch_prctl}; #[allow(non_camel_case_types)] pub type pid_t = u32; @@ -65,6 +66,7 @@ mod task; mod wait; mod thread; mod futex; +mod arch_prctl; use self::task::Task; use super::*; diff --git a/src/libos/src/process/spawn/elf_helper.rs b/src/libos/src/process/spawn/elf_helper.rs index 6fabff86..14c69214 100644 --- a/src/libos/src/process/spawn/elf_helper.rs +++ b/src/libos/src/process/spawn/elf_helper.rs @@ -6,6 +6,22 @@ use xmas_elf::symbol_table::Entry; use xmas_elf::symbol_table::{DynEntry64, Entry64}; use xmas_elf::{program, sections, ElfFile, P64}; +#[derive(Clone, Default, Copy, Debug)] +pub struct ProgramHeaderInfo { + pub addr: usize, + pub entry_size: usize, + pub entry_num: usize, +} + +pub fn get_program_header_info(elf_file: &ElfFile) -> Result { + let elf_header = &elf_file.header.pt2; + Ok(ProgramHeaderInfo { + addr: elf_header.ph_offset() as usize, + entry_size: elf_header.ph_entry_size() as usize, + entry_num: elf_header.ph_count() as usize, + }) +} + pub fn print_program_headers(elf_file: &ElfFile) -> Result<(), Error> { println!("Program headers:"); let ph_iter = elf_file.program_iter(); @@ -87,18 +103,8 @@ pub fn get_code_program_header<'b, 'a: 'b>( } pub fn get_start_address<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result { - let sym_entries = get_sym_entries(elf_file)?; - - for sym_entry in sym_entries { - let sym_str = sym_entry - .get_name(elf_file) - .map_err(|e| Error::new(Errno::ENOEXEC, "Failed to get the name of a symbol"))?; - if sym_str == "_start" { - return Ok(sym_entry.value() as usize); - } - } - - Err((Errno::ENOEXEC, "Failed to get the _start symbol").into()) + let elf_header = &elf_file.header.pt2; + Ok(elf_header.entry_point() as usize) } pub fn get_sym_entries<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<&'a [Entry64], Error> { diff --git a/src/libos/src/process/spawn/init_stack.rs b/src/libos/src/process/spawn/init_stack.rs index 22783488..b12af26a 100644 --- a/src/libos/src/process/spawn/init_stack.rs +++ b/src/libos/src/process/spawn/init_stack.rs @@ -58,7 +58,6 @@ pub fn do_init( let envp_cloned = clone_cstrings_on_stack(&stack_buf, envp)?; let argv_cloned = clone_cstrings_on_stack(&stack_buf, argv)?; dump_auxtbl_on_stack(&stack_buf, auxtbl)?; - dump_auxtbl_on_stack(&stack_buf, auxtbl)?; dump_cstrptrs_on_stack(&stack_buf, &envp_cloned); dump_cstrptrs_on_stack(&stack_buf, &argv_cloned); stack_buf.put(argv.len() as u64); diff --git a/src/libos/src/process/spawn/init_vm.rs b/src/libos/src/process/spawn/init_vm.rs index 32f7c21f..e01f7f3a 100644 --- a/src/libos/src/process/spawn/init_vm.rs +++ b/src/libos/src/process/spawn/init_vm.rs @@ -25,10 +25,10 @@ pub fn do_init(elf_file: &ElfFile, elf_buf: &[u8]) -> Result { // Calculate the "real" addresses let process_base_addr = process_vm.get_base_addr(); - let code_start = process_base_addr + code_start; - let code_end = process_base_addr + code_end; - let data_start = process_base_addr + data_start; - let data_end = process_base_addr + data_end; + let code_start = code_start + process_base_addr; + let code_end = code_end + process_base_addr; + let data_start = data_start + process_base_addr; + let data_end = data_end + process_base_addr; code_seg.set_runtime_info(process_base_addr, code_start, code_end); data_seg.set_runtime_info(process_base_addr, data_start, data_end); @@ -54,8 +54,8 @@ fn reloc_symbols(process_base_addr: usize, elf_file: &ElfFile) -> Result<(), Err rela_entry.get_addend()); */ - /* reloc type == R_X86_64_RELATIVE */ match rela_entry.get_type() { + // reloc type == R_X86_64_RELATIVE 8 if rela_entry.get_symbol_table_index() == 0 => { let rela_addr = process_base_addr + rela_entry.get_offset() as usize; let rela_val = process_base_addr + rela_entry.get_addend() as usize; diff --git a/src/libos/src/process/spawn/mod.rs b/src/libos/src/process/spawn/mod.rs index 0a6084af..c32a7e4c 100644 --- a/src/libos/src/process/spawn/mod.rs +++ b/src/libos/src/process/spawn/mod.rs @@ -54,16 +54,18 @@ pub fn do_spawn>( let (new_pid, new_process_ref) = { let cwd = parent_ref.lock().unwrap().get_cwd().to_owned(); let vm = init_vm::do_init(&elf_file, &elf_buf[..])?; + let base_addr = vm.get_base_addr(); + let program_entry = { + let program_entry = base_addr + elf_helper::get_start_address(&elf_file)?; + if !vm.get_code_vma().contains_obj(program_entry, 16) { + return Err(Error::new(Errno::EINVAL, "Invalid program entry")); + } + program_entry + }; + let auxtbl = init_auxtbl(base_addr, program_entry, &elf_file)?; let task = { - let program_entry = { - let program_entry = vm.get_base_addr() + elf_helper::get_start_address(&elf_file)?; - if !vm.get_code_vma().contains_obj(program_entry, 16) { - return Err(Error::new(Errno::EINVAL, "Invalid program entry")); - } - program_entry - }; let stack_top = vm.get_stack_top(); - init_task(program_entry, stack_top, argv, envp)? + init_task(program_entry, stack_top, argv, envp, &auxtbl)? }; let vm_ref = Arc::new(SgxMutex::new(vm)); let files_ref = { @@ -119,8 +121,9 @@ fn init_task( stack_top: usize, argv: &[CString], envp: &[CString], + auxtbl: &AuxTable, ) -> Result { - let user_stack = init_stack(stack_top, argv, envp)?; + let user_stack = init_stack::do_init(stack_top, 4096, argv, envp, auxtbl)?; Ok(Task { user_stack_addr: user_stack, user_entry_addr: user_entry, @@ -128,7 +131,7 @@ fn init_task( }) } -fn init_stack(stack_top: usize, argv: &[CString], envp: &[CString]) -> Result { +fn init_auxtbl(base_addr: usize, program_entry: usize, elf_file: &ElfFile) -> Result { let mut auxtbl = AuxTable::new(); auxtbl.set_val(AuxKey::AT_PAGESZ, 4096)?; auxtbl.set_val(AuxKey::AT_UID, 0)?; @@ -137,7 +140,16 @@ fn init_stack(stack_top: usize, argv: &[CString], envp: &[CString]) -> Result do_gettimeofday(arg0 as *mut timeval_t), + SYS_ARCH_PRCTL => do_arch_prctl(arg0 as u32, arg1 as *mut usize), + _ => do_unknown(num), }; debug!("syscall return: {:?}", ret); @@ -653,3 +655,10 @@ fn do_unlink(path: *const i8) -> Result { fs::do_unlink(&path)?; Ok(0) } + +fn do_arch_prctl(code: u32, addr: *mut usize) -> Result { + let code = process::ArchPrctlCode::from_u32(code)?; + check_mut_ptr(addr)?; + process::do_arch_prctl(code, addr).map(|_| 0) +} + diff --git a/src/libos/src/syscall/syscall_entry_x86-64.S b/src/libos/src/syscall/syscall_entry_x86-64.S index d578135d..d7b528ce 100644 --- a/src/libos/src/syscall/syscall_entry_x86-64.S +++ b/src/libos/src/syscall/syscall_entry_x86-64.S @@ -19,8 +19,10 @@ __occlum_syscall: bndcl %rsp, %bnd0 bndcu %rsp, %bnd0 - // Save the user stack + // Save the callee-saved registers pushq %rbp + pushq %r12 + // Save the user stack movq %rsp, %rbp // Get current task @@ -39,11 +41,13 @@ __occlum_syscall: // addq 0x08, %rsp // Use user fsbase - movq TASK_KERNEL_FSBASE_ADDR(%r12), %r11 + movq TASK_USER_FSBASE_ADDR(%r12), %r11 wrfsbase %r11 - // Restore the user stack + // Switch to the user stack movq %rbp, %rsp + // Restore callee-saved registers + popq %r12 popq %rbp // Check return target is a valid instruction (i.e., a cfi_label) diff --git a/test/Makefile b/test/Makefile index 07afb9f8..25cc2ab0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../) # Dependencies: need to be compiled but not to run by any Makefile target TEST_DEPS := dev_null # Tests: need to be compiled and run by test-% target -TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link clone +TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link clone tls # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput diff --git a/test/tls/Makefile b/test/tls/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/tls/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/tls/main.c b/test/tls/main.c new file mode 100644 index 00000000..a128aa99 --- /dev/null +++ b/test/tls/main.c @@ -0,0 +1,11 @@ +volatile int g_int = 0; +static void use_int(int* a) { + g_int += *a; +} + +__thread int tls_g_int = 0; + +int main(int argc, const char* argv[]) { + use_int(&tls_g_int); + return g_int; +}