GitLab will continue to be upgraded from 11.4.5-ce.0 on November 25th 2019 at 4.00pm (AEDT) to 5.00pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available.

mod.rs 11.2 KB
Newer Older
1
// Copyright 2017 The Australian National University
qinsoon's avatar
qinsoon committed
2
//
3 4 5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
qinsoon's avatar
qinsoon committed
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
qinsoon's avatar
qinsoon committed
8
//
9 10 11 12 13 14
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

15
use utils;
16 17
use utils::Word;
use utils::Address;
18
use ast::ir::*;
19
use vm::VM;
20 21
use compiler::backend::RegGroup;

22 23
use libc::*;
use std;
24
use std::fmt;
25
use std::ffi::CString;
26 27
use std::ffi::CStr;
use std::sync::Arc;
28
use rodal;
29

30 31
use libc::c_void;

qinsoon's avatar
qinsoon committed
32 33
/// memory management: allocation, reclamation
/// (the actual code is in src/gc, which gets re-exported in mm module)
34
pub mod mm;
qinsoon's avatar
qinsoon committed
35
/// thread management: stack, thread
36
pub mod thread;
qinsoon's avatar
qinsoon committed
37
/// mathematics functions
38
pub mod math;
qinsoon's avatar
qinsoon committed
39 40
/// a list of all entrypoints used by compiler to generate calls into runtime
/// (where generated code entries the runtime)
41
pub mod entrypoints;
qinsoon's avatar
qinsoon committed
42
/// exception handling
43 44
pub mod exception;

45

qinsoon's avatar
qinsoon committed
46 47 48
/// returns name for a function address
// FIXME: this actually returns the name and address of the nearest symbol (of any type)
//        that starts before function_addr (instead we want the nearest function symbol)
49
pub fn get_function_info(function_addr: Address) -> (CName, Address) {
50
    // dladdr will initialise this for us
qinsoon's avatar
qinsoon committed
51
    let mut info = unsafe { std::mem::uninitialized::<Dl_info>() };
52

qinsoon's avatar
qinsoon committed
53
    unsafe { dladdr(function_addr.to_ptr_mut::<c_void>(), &mut info) };
54

qinsoon's avatar
qinsoon committed
55
    let error = unsafe { dlerror() };
56
    if !error.is_null() {
qinsoon's avatar
qinsoon committed
57
        let cstr = unsafe { CStr::from_ptr(error) };
58 59 60 61 62 63
        error!("cannot find function address: {}", function_addr);
        error!("{}", cstr.to_str().unwrap());

        panic!("failed to resolve function address");
    }
    if !info.dli_sname.is_null() {
qinsoon's avatar
qinsoon committed
64 65 66 67 68
        (
            unsafe { CStr::from_ptr(info.dli_sname) }
                .to_str()
                .unwrap()
                .to_string(),
69
            Address::from_ptr(info.dli_saddr)
qinsoon's avatar
qinsoon committed
70
        )
71 72 73 74 75 76
    } else {
        ("UNKOWN".to_string(), Address::from_ptr(info.dli_saddr))
    }

}

77

qinsoon's avatar
qinsoon committed
78
/// returns address for a given symbol, e.g. function name
79
#[cfg(not(feature = "sel4-rumprun-target-side"))]
qinsoon's avatar
qinsoon committed
80
pub fn resolve_symbol(symbol: MuName) -> Address {
81
    use std::ptr;
82

83
    let c_symbol = CString::new(mangle_name(symbol.clone())).unwrap();
84

qinsoon's avatar
qinsoon committed
85 86 87 88
    let rtld_default = unsafe { dlopen(ptr::null(), 0) };
    let ret = unsafe { dlsym(rtld_default, c_symbol.as_ptr()) };

    let error = unsafe { dlerror() };
89
    if !error.is_null() {
qinsoon's avatar
qinsoon committed
90 91 92 93 94 95
        let cstr = unsafe { CStr::from_ptr(error) };
        panic!(
            "failed to resolve symbol: {} ({})",
            symbol,
            cstr.to_str().unwrap()
        );
96
    }
qinsoon's avatar
qinsoon committed
97

98 99 100
    Address::from_ptr(ret)
}

101 102 103 104 105 106
use std::os::raw::c_char;
//use std::os::raw::c_void;
// This function is specific to sel4-rumprun platform
// it replaces the resolve_symbol function provided by Linux and Mac
// all other platforms (except sel4-rumprun) already provide this function
#[cfg(feature = "sel4-rumprun-target-side")]
107
#[link(name = "zebu_c_helpers")]
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
extern "C" {
    fn c_resolve_symbol(symbol: *const c_char) -> *const c_void;
}

// Although it is possible to directly \
// compile, call and check results of Mu test functions \
// in Linux and Mac, but in-order to unify testing styles \
// I will use this C function to check the correctness of results
// *************************************************
// #[link(name="runtime")]
// extern "C" {
//    fn c_check_result() -> c_long;
// }
//
// pub fn check_result() -> c_long {
//     let result = unsafe { c_check_result() };
//     result
// }
// *************************************************
// This code has been moved to thread.rs \
// due to the linkage with libruntime.a happenning there once

// TODO
// resolve symbol is different from the one used for Linux and Mac
#[cfg(feature = "sel4-rumprun-target-side")]
pub fn resolve_symbol(symbol: String) -> Address {
    debug!("Going to resolve Symbol -{}-", symbol);
    let ret = unsafe { c_resolve_symbol(CString::new(symbol.clone()).unwrap().as_ptr()) };
    if ret.is_null() {
        panic!("failed to resolve symbol: {}", symbol.clone());
    }
    debug!("Symbol -{}- resolved", symbol);
    Address::from_ptr(ret)
}

qinsoon's avatar
qinsoon committed
143 144 145 146 147 148 149 150 151
/// ValueLocation represents the runtime location for a value.
/// The purpose of this data structure is to refer to a location in a unified way
/// for both compile time (usually talking about symbols) and run time (talking about addresses)
/// A ValueLocation could be:
/// * a register (the register holds the value)
/// * a Constant (the value itself)
/// * a relocatable symbol (a relocatable symbol emitted by AOT compiler, which resides the value)
/// * a direct memory address (the address contains the value)
/// * a indirect memory address (the address contains a pointer to the value)
qinsoon's avatar
qinsoon committed
152
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
153
pub enum ValueLocation {
154 155
    Register(RegGroup, MuID),
    Constant(RegGroup, Word),
qinsoon's avatar
qinsoon committed
156 157 158
    Relocatable(RegGroup, MuName), // TODO: This only works for mu entities (add a flag to indicate
    // if its native or have a different variant?)
    Direct(RegGroup, Address),   // Not dumped
159
    Indirect(RegGroup, Address)  // Not dumped
qinsoon's avatar
qinsoon committed
160 161
}

qinsoon's avatar
qinsoon committed
162 163
rodal_enum!(ValueLocation{(Register: group, id), (Constant: group, word),
    (Relocatable: group, name)});
164

165 166 167 168 169 170 171
impl fmt::Display for ValueLocation {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ValueLocation::Register(_, id) => write!(f, "VL_Reg: {}", id),
            &ValueLocation::Constant(_, val) => write!(f, "VL_Const: {}", val),
            &ValueLocation::Relocatable(_, ref name) => write!(f, "VL_Reloc: {}", name),
            &ValueLocation::Direct(_, addr) => write!(f, "VL_Direct: 0x{:x}", addr),
172
            &ValueLocation::Indirect(_, addr) => write!(f, "VL_Indirect: 0x{:x}", addr)
173 174 175 176
        }
    }
}

177
impl ValueLocation {
qinsoon's avatar
qinsoon committed
178
    /// loads value from a ValueLocation
179 180
    pub fn load_value(&self) -> (RegGroup, Word) {
        match self {
qinsoon's avatar
qinsoon committed
181 182 183 184 185 186 187
            &ValueLocation::Register(_, _) => unimplemented!(),
            &ValueLocation::Direct(group, addr) => (group, unsafe { addr.load::<Word>() }),
            &ValueLocation::Indirect(group, addr) => unsafe {
                let ptr = addr.load::<Address>();
                (group, ptr.load::<Word>())
            },
            &ValueLocation::Constant(group, word) => (group, word),
qinsoon's avatar
qinsoon committed
188 189
            &ValueLocation::Relocatable(group, ref symbol) => {
                let addr = resolve_symbol(symbol.clone());
qinsoon's avatar
qinsoon committed
190
                (group, unsafe { addr.load::<Word>() })
qinsoon's avatar
qinsoon committed
191
            }
192 193
        }
    }
qinsoon's avatar
qinsoon committed
194 195

    /// creates a ValueLocation from a constant, panics if impossible
qinsoon's avatar
qinsoon committed
196
    pub fn from_constant(c: Constant) -> ValueLocation {
197
        match c {
qinsoon's avatar
qinsoon committed
198 199 200 201 202 203 204 205 206
            Constant::Int(int_val) => {
                ValueLocation::Constant(RegGroup::GPR, utils::mem::u64_to_raw(int_val))
            }
            Constant::Float(f32_val) => {
                ValueLocation::Constant(RegGroup::FPR, utils::mem::f32_to_raw(f32_val))
            }
            Constant::Double(f64_val) => {
                ValueLocation::Constant(RegGroup::FPR, utils::mem::f64_to_raw(f64_val))
            }
207
            _ => unimplemented!()
208
        }
qinsoon's avatar
qinsoon committed
209
    }
qinsoon's avatar
qinsoon committed
210 211

    /// returns the address that contains the value
qinsoon's avatar
qinsoon committed
212 213
    pub fn to_address(&self) -> Address {
        match self {
qinsoon's avatar
qinsoon committed
214 215
            &ValueLocation::Direct(_, addr) => addr,
            &ValueLocation::Indirect(_, addr) => unsafe { addr.load::<Address>() },
qinsoon's avatar
qinsoon committed
216
            &ValueLocation::Relocatable(_, ref symbol) => resolve_symbol(symbol.clone()),
qinsoon's avatar
qinsoon committed
217 218 219
            &ValueLocation::Register(_, _) | &ValueLocation::Constant(_, _) => {
                panic!("a register/constant cannot be turned into address")
            }
qinsoon's avatar
qinsoon committed
220 221
        }
    }
222

qinsoon's avatar
qinsoon committed
223
    /// returns a relocatable symbol that contains the value, panics if impossible
224 225 226
    pub fn to_relocatable(&self) -> MuName {
        match self {
            &ValueLocation::Relocatable(_, ref name) => name.clone(),
227
            _ => panic!("expecting Relocatable location, found {}", self)
228 229
        }
    }
230 231
}

qinsoon's avatar
qinsoon committed
232 233 234 235 236
/// a C wrapper as main function for executable boot images"
/// The C wrapper does:
/// 1. loads the persisted VM
/// 2. invokes mu_main() to hand the control to Rust code
/// 3. returns the return value set by SetRetval
qinsoon's avatar
qinsoon committed
237
pub const PRIMORDIAL_ENTRY: &'static str = "src/runtime/main.c";
238

239 240
/// a C wrapper as main function for executable test boot images"
/// in addition to normal main.c, it checks the returned results of tests
241
pub const TEST_PRIMORDIAL_ENTRY: &'static str = "src/runtime/main_test.c";
242

qinsoon's avatar
qinsoon committed
243
/// starts trace level logging, this function will be called from C
244
#[no_mangle]
qinsoon's avatar
qinsoon committed
245
pub extern "C" fn mu_trace_level_log() {
qinsoon's avatar
qinsoon committed
246
    VM::start_logging_trace();
247 248
}

249 250 251
#[no_mangle]
pub static mut LAST_TIME: c_ulong = 0;

qinsoon's avatar
qinsoon committed
252
/// the main function for executable boot image, this function will be called from C
253
#[no_mangle]
qinsoon's avatar
qinsoon committed
254 255 256 257
pub extern "C" fn mu_main(
    edata: *const (),
    dumped_vm: *mut Arc<VM>,
    argc: c_int,
258
    argv: *const *const c_char
qinsoon's avatar
qinsoon committed
259
) {
260
    VM::start_logging_env();
261
    debug!("mu_main() started...");
262

qinsoon's avatar
qinsoon committed
263
    // load and resume the VM
qinsoon's avatar
qinsoon committed
264 265 266
    unsafe {
        rodal::load_asm_bounds(
            rodal::Address::from_ptr(dumped_vm),
267
            rodal::Address::from_ptr(edata)
qinsoon's avatar
qinsoon committed
268 269
        )
    };
270
    let vm = VM::resume_vm(dumped_vm);
qinsoon's avatar
qinsoon committed
271
    // find the primordial function as an entry
272
    let primordial = vm.primordial().read().unwrap();
273 274 275 276
    if primordial.is_none() {
        panic!("no primordial thread/stack/function. Client should provide an entry point");
    } else {
        let primordial = primordial.as_ref().unwrap();
qinsoon's avatar
qinsoon committed
277

278 279
        // create mu stack
        let stack = vm.new_stack(primordial.func_id);
280 281 282

        // if the primordial named some const arguments, we use the const args
        // otherwise we push 'argc' and 'argv' to new stack
qinsoon's avatar
qinsoon committed
283 284 285 286 287 288
        let args: Vec<ValueLocation> = if primordial.has_const_args {
            primordial
                .args
                .iter()
                .map(|arg| ValueLocation::from_constant(arg.clone()))
                .collect()
289 290 291 292 293 294 295 296 297 298 299
        } else {
            let mut args = vec![];

            // 1st arg: argc
            args.push(ValueLocation::from_constant(Constant::Int(argc as u64)));

            // 2nd arg: argv
            args.push(ValueLocation::from_constant(Constant::Int(argv as u64)));

            args
        };
qinsoon's avatar
qinsoon committed
300

qinsoon's avatar
qinsoon committed
301
        // FIXME: currently assumes no user defined thread local - See Issue #48
302
        thread::MuThread::new_thread_normal(
qinsoon's avatar
qinsoon committed
303 304 305
            stack,
            unsafe { Address::zero() },
            args,
306
            vm.clone()
qinsoon's avatar
qinsoon committed
307 308
        );

309 310 311 312 313 314 315 316 317
        loop {
            let thread = vm.pop_join_handle();
            if thread.is_none() {
                break;
            }
            thread.unwrap().join().unwrap();
        }

        trace!("All threads have exited, quiting...");
318
    }
319
}
320

qinsoon's avatar
qinsoon committed
321
/// runtime function to print a hex value (for PRINTHEX instruction for debugging use)
322
#[no_mangle]
qinsoon's avatar
qinsoon committed
323
pub extern "C" fn muentry_print_hex(x: u64) {
324
    println!("PRINTHEX: 0x{:x}", x);
325 326 327
}

#[no_mangle]
qinsoon's avatar
qinsoon committed
328
pub unsafe extern "C" fn muentry_mem_zero(dest: *mut u8, size: usize) {
329
    std::ptr::write_bytes(dest, 0, size);
qinsoon's avatar
qinsoon committed
330
}