Enable Thread Local Storage (TLS)

Add arch_prctl and pass necessary ELF info to libc via aux vector
This commit is contained in:
Tate, Hongliang Tian 2019-04-05 21:03:05 +08:00 committed by Tate Tian
parent b2e626760b
commit db40e8f52b
11 changed files with 124 additions and 33 deletions

@ -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<ArchPrctlCode, Error> {
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 = &current.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(())
}

@ -8,6 +8,7 @@ pub use self::spawn::{do_spawn, FileAction};
pub use self::wait::{WaitQueue, Waiter}; pub use self::wait::{WaitQueue, Waiter};
pub use self::thread::{do_clone, CloneFlags, ThreadGroup}; 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::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)] #[allow(non_camel_case_types)]
pub type pid_t = u32; pub type pid_t = u32;
@ -65,6 +66,7 @@ mod task;
mod wait; mod wait;
mod thread; mod thread;
mod futex; mod futex;
mod arch_prctl;
use self::task::Task; use self::task::Task;
use super::*; use super::*;

@ -6,6 +6,22 @@ use xmas_elf::symbol_table::Entry;
use xmas_elf::symbol_table::{DynEntry64, Entry64}; use xmas_elf::symbol_table::{DynEntry64, Entry64};
use xmas_elf::{program, sections, ElfFile, P64}; 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<ProgramHeaderInfo, Error> {
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> { pub fn print_program_headers(elf_file: &ElfFile) -> Result<(), Error> {
println!("Program headers:"); println!("Program headers:");
let ph_iter = elf_file.program_iter(); 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<usize, Error> { pub fn get_start_address<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<usize, Error> {
let sym_entries = get_sym_entries(elf_file)?; let elf_header = &elf_file.header.pt2;
Ok(elf_header.entry_point() as usize)
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())
} }
pub fn get_sym_entries<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<&'a [Entry64], Error> { pub fn get_sym_entries<'b, 'a: 'b>(elf_file: &'b ElfFile<'a>) -> Result<&'a [Entry64], Error> {

@ -58,7 +58,6 @@ pub fn do_init(
let envp_cloned = clone_cstrings_on_stack(&stack_buf, envp)?; let envp_cloned = clone_cstrings_on_stack(&stack_buf, envp)?;
let argv_cloned = clone_cstrings_on_stack(&stack_buf, argv)?; 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_auxtbl_on_stack(&stack_buf, auxtbl)?;
dump_cstrptrs_on_stack(&stack_buf, &envp_cloned); dump_cstrptrs_on_stack(&stack_buf, &envp_cloned);
dump_cstrptrs_on_stack(&stack_buf, &argv_cloned); dump_cstrptrs_on_stack(&stack_buf, &argv_cloned);
stack_buf.put(argv.len() as u64); stack_buf.put(argv.len() as u64);

@ -25,10 +25,10 @@ pub fn do_init(elf_file: &ElfFile, elf_buf: &[u8]) -> Result<ProcessVM, Error> {
// Calculate the "real" addresses // Calculate the "real" addresses
let process_base_addr = process_vm.get_base_addr(); let process_base_addr = process_vm.get_base_addr();
let code_start = process_base_addr + code_start; let code_start = code_start + process_base_addr;
let code_end = process_base_addr + code_end; let code_end = code_end + process_base_addr;
let data_start = process_base_addr + data_start; let data_start = data_start + process_base_addr;
let data_end = process_base_addr + data_end; let data_end = data_end + process_base_addr;
code_seg.set_runtime_info(process_base_addr, code_start, code_end); code_seg.set_runtime_info(process_base_addr, code_start, code_end);
data_seg.set_runtime_info(process_base_addr, data_start, data_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()); rela_entry.get_addend());
*/ */
/* reloc type == R_X86_64_RELATIVE */
match rela_entry.get_type() { match rela_entry.get_type() {
// reloc type == R_X86_64_RELATIVE
8 if rela_entry.get_symbol_table_index() == 0 => { 8 if rela_entry.get_symbol_table_index() == 0 => {
let rela_addr = process_base_addr + rela_entry.get_offset() as usize; let rela_addr = process_base_addr + rela_entry.get_offset() as usize;
let rela_val = process_base_addr + rela_entry.get_addend() as usize; let rela_val = process_base_addr + rela_entry.get_addend() as usize;

@ -54,16 +54,18 @@ pub fn do_spawn<P: AsRef<Path>>(
let (new_pid, new_process_ref) = { let (new_pid, new_process_ref) = {
let cwd = parent_ref.lock().unwrap().get_cwd().to_owned(); let cwd = parent_ref.lock().unwrap().get_cwd().to_owned();
let vm = init_vm::do_init(&elf_file, &elf_buf[..])?; 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 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(); 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 vm_ref = Arc::new(SgxMutex::new(vm));
let files_ref = { let files_ref = {
@ -119,8 +121,9 @@ fn init_task(
stack_top: usize, stack_top: usize,
argv: &[CString], argv: &[CString],
envp: &[CString], envp: &[CString],
auxtbl: &AuxTable,
) -> Result<Task, Error> { ) -> Result<Task, Error> {
let user_stack = init_stack(stack_top, argv, envp)?; let user_stack = init_stack::do_init(stack_top, 4096, argv, envp, auxtbl)?;
Ok(Task { Ok(Task {
user_stack_addr: user_stack, user_stack_addr: user_stack,
user_entry_addr: user_entry, user_entry_addr: user_entry,
@ -128,7 +131,7 @@ fn init_task(
}) })
} }
fn init_stack(stack_top: usize, argv: &[CString], envp: &[CString]) -> Result<usize, Error> { fn init_auxtbl(base_addr: usize, program_entry: usize, elf_file: &ElfFile) -> Result<AuxTable, Error> {
let mut auxtbl = AuxTable::new(); let mut auxtbl = AuxTable::new();
auxtbl.set_val(AuxKey::AT_PAGESZ, 4096)?; auxtbl.set_val(AuxKey::AT_PAGESZ, 4096)?;
auxtbl.set_val(AuxKey::AT_UID, 0)?; auxtbl.set_val(AuxKey::AT_UID, 0)?;
@ -137,7 +140,16 @@ fn init_stack(stack_top: usize, argv: &[CString], envp: &[CString]) -> Result<us
auxtbl.set_val(AuxKey::AT_EGID, 0)?; auxtbl.set_val(AuxKey::AT_EGID, 0)?;
auxtbl.set_val(AuxKey::AT_SECURE, 0)?; auxtbl.set_val(AuxKey::AT_SECURE, 0)?;
init_stack::do_init(stack_top, 4096, argv, envp, &auxtbl) let ph = elf_helper::get_program_header_info(elf_file)?;
auxtbl.set_val(AuxKey::AT_PHDR, (base_addr + ph.addr) as u64)?;
auxtbl.set_val(AuxKey::AT_PHENT, ph.entry_size as u64)?;
auxtbl.set_val(AuxKey::AT_PHNUM, ph.entry_num as u64)?;
auxtbl.set_val(AuxKey::AT_ENTRY, program_entry as u64)?;
// TODO: init AT_EXECFN
// auxtbl.set_val(AuxKey::AT_EXECFN, "program_name")?;
Ok(auxtbl)
} }
fn parent_adopts_new_child(parent_ref: &ProcessRef, child_ref: &ProcessRef) { fn parent_adopts_new_child(parent_ref: &ProcessRef, child_ref: &ProcessRef) {

@ -122,6 +122,8 @@ pub extern "C" fn dispatch_syscall(
SYS_GETTIMEOFDAY => do_gettimeofday(arg0 as *mut timeval_t), SYS_GETTIMEOFDAY => do_gettimeofday(arg0 as *mut timeval_t),
SYS_ARCH_PRCTL => do_arch_prctl(arg0 as u32, arg1 as *mut usize),
_ => do_unknown(num), _ => do_unknown(num),
}; };
debug!("syscall return: {:?}", ret); debug!("syscall return: {:?}", ret);
@ -653,3 +655,10 @@ fn do_unlink(path: *const i8) -> Result<isize, Error> {
fs::do_unlink(&path)?; fs::do_unlink(&path)?;
Ok(0) Ok(0)
} }
fn do_arch_prctl(code: u32, addr: *mut usize) -> Result<isize, Error> {
let code = process::ArchPrctlCode::from_u32(code)?;
check_mut_ptr(addr)?;
process::do_arch_prctl(code, addr).map(|_| 0)
}

@ -19,8 +19,10 @@ __occlum_syscall:
bndcl %rsp, %bnd0 bndcl %rsp, %bnd0
bndcu %rsp, %bnd0 bndcu %rsp, %bnd0
// Save the user stack // Save the callee-saved registers
pushq %rbp pushq %rbp
pushq %r12
// Save the user stack
movq %rsp, %rbp movq %rsp, %rbp
// Get current task // Get current task
@ -39,11 +41,13 @@ __occlum_syscall:
// addq 0x08, %rsp // addq 0x08, %rsp
// Use user fsbase // Use user fsbase
movq TASK_KERNEL_FSBASE_ADDR(%r12), %r11 movq TASK_USER_FSBASE_ADDR(%r12), %r11
wrfsbase %r11 wrfsbase %r11
// Restore the user stack // Switch to the user stack
movq %rbp, %rsp movq %rbp, %rsp
// Restore callee-saved registers
popq %r12
popq %rbp popq %rbp
// Check return target is a valid instruction (i.e., a cfi_label) // Check return target is a valid instruction (i.e., a cfi_label)

@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../)
# Dependencies: need to be compiled but not to run by any Makefile target # Dependencies: need to be compiled but not to run by any Makefile target
TEST_DEPS := dev_null TEST_DEPS := dev_null
# Tests: need to be compiled and run by test-% target # 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 # Benchmarks: need to be compiled and run by bench-% target
BENCHES := spawn_and_exit_latency pipe_throughput BENCHES := spawn_and_exit_latency pipe_throughput

5
test/tls/Makefile Normal file

@ -0,0 +1,5 @@
include ../test_common.mk
EXTRA_C_FLAGS :=
EXTRA_LINK_FLAGS :=
BIN_ARGS :=

11
test/tls/main.c Normal file

@ -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;
}