Use goblin as elf parser and only load segments on demand
Libos now Only read in headers and needed segments. Also reduce a memory copy.
This commit is contained in:
parent
c2aefcacf0
commit
1fab79681b
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -2,10 +2,6 @@
|
|||||||
path = deps/rust-sgx-sdk
|
path = deps/rust-sgx-sdk
|
||||||
url = https://github.com/occlum/incubator-teaclave-sgx-sdk.git
|
url = https://github.com/occlum/incubator-teaclave-sgx-sdk.git
|
||||||
branch = sgx_2.11_for_occlum
|
branch = sgx_2.11_for_occlum
|
||||||
[submodule "deps/xmas-elf"]
|
|
||||||
path = deps/xmas-elf
|
|
||||||
url = https://github.com/occlum/xmas-elf
|
|
||||||
branch = master
|
|
||||||
[submodule "deps/sefs"]
|
[submodule "deps/sefs"]
|
||||||
path = deps/sefs
|
path = deps/sefs
|
||||||
url = https://github.com/occlum/sefs.git
|
url = https://github.com/occlum/sefs.git
|
||||||
|
2
deps/sefs
vendored
2
deps/sefs
vendored
@ -1 +1 @@
|
|||||||
Subproject commit f9492a82f5453a4cfaeeebe0fbd20e65e96ef8f4
|
Subproject commit 733137cb6950b894848bc8d4f96637638dc42c94
|
1
deps/xmas-elf
vendored
1
deps/xmas-elf
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit 792105500ef80bdfe6f45ca6f64865121e4699c5
|
|
53
src/libos/Cargo.lock
generated
53
src/libos/Cargo.lock
generated
@ -9,6 +9,7 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"derive_builder",
|
"derive_builder",
|
||||||
|
"goblin",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"memoffset",
|
"memoffset",
|
||||||
@ -19,6 +20,7 @@ dependencies = [
|
|||||||
"rcore-fs-sefs",
|
"rcore-fs-sefs",
|
||||||
"rcore-fs-unionfs",
|
"rcore-fs-unionfs",
|
||||||
"ringbuf",
|
"ringbuf",
|
||||||
|
"scroll",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sgx_cov",
|
"sgx_cov",
|
||||||
@ -27,7 +29,6 @@ dependencies = [
|
|||||||
"sgx_tse",
|
"sgx_tse",
|
||||||
"sgx_tstd",
|
"sgx_tstd",
|
||||||
"sgx_types",
|
"sgx_types",
|
||||||
"xmas-elf",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -204,6 +205,17 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "goblin"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "669cdc3826f69a51d3f8fc3f86de81c2378110254f678b8407977736122057a4"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"plain",
|
||||||
|
"scroll",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown_tstd"
|
name = "hashbrown_tstd"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -254,6 +266,12 @@ dependencies = [
|
|||||||
"autocfg 1.0.1",
|
"autocfg 1.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plain"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.19"
|
version = "1.0.19"
|
||||||
@ -466,6 +484,26 @@ version = "1.0.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scroll"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec"
|
||||||
|
dependencies = [
|
||||||
|
"scroll_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scroll_derive"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b12bd20b94c7cdfda8c7ba9b92ad0d9a56e3fa018c25fca83b51aa664c9b4c0d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.104"
|
version = "1.0.104"
|
||||||
@ -675,16 +713,3 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xmas-elf"
|
|
||||||
version = "0.6.2"
|
|
||||||
dependencies = [
|
|
||||||
"zero",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zero"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5"
|
|
||||||
|
@ -25,6 +25,7 @@ rcore-fs-devfs = { path = "../../deps/sefs/rcore-fs-devfs" }
|
|||||||
serde = { path = "../../deps/serde-sgx/serde", features = ["derive"] }
|
serde = { path = "../../deps/serde-sgx/serde", features = ["derive"] }
|
||||||
serde_json = { path = "../../deps/serde-json-sgx" }
|
serde_json = { path = "../../deps/serde-json-sgx" }
|
||||||
memoffset = "0.6.1"
|
memoffset = "0.6.1"
|
||||||
|
scroll = { version = "0.10.2", default-features = false }
|
||||||
|
|
||||||
[patch.'https://github.com/apache/teaclave-sgx-sdk.git']
|
[patch.'https://github.com/apache/teaclave-sgx-sdk.git']
|
||||||
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd" }
|
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd" }
|
||||||
@ -39,10 +40,10 @@ dcap = [] # DCAP support. The compilation relies on DCAP package.
|
|||||||
cov = ["sgx_cov"] # Enable coverage colletcion.
|
cov = ["sgx_cov"] # Enable coverage colletcion.
|
||||||
|
|
||||||
[target.'cfg(not(target_env = "sgx"))'.dependencies]
|
[target.'cfg(not(target_env = "sgx"))'.dependencies]
|
||||||
xmas-elf = { path = "../../deps/xmas-elf" }
|
|
||||||
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
|
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
|
||||||
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace"] }
|
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace"] }
|
||||||
sgx_trts = { path = "../../deps/rust-sgx-sdk/sgx_trts" }
|
sgx_trts = { path = "../../deps/rust-sgx-sdk/sgx_trts" }
|
||||||
sgx_tse = { path = "../../deps/rust-sgx-sdk/sgx_tse" }
|
sgx_tse = { path = "../../deps/rust-sgx-sdk/sgx_tse" }
|
||||||
sgx_tcrypto = { path = "../../deps/rust-sgx-sdk/sgx_tcrypto" }
|
sgx_tcrypto = { path = "../../deps/rust-sgx-sdk/sgx_tcrypto" }
|
||||||
sgx_cov = { path = "../../deps/rust-sgx-sdk/sgx_cov", optional = true }
|
sgx_cov = { path = "../../deps/rust-sgx-sdk/sgx_cov", optional = true }
|
||||||
|
goblin = { version = "0.3.4", default-features = false, features = ["elf64", "elf32", "endian_fd"] }
|
||||||
|
@ -29,10 +29,11 @@ extern crate sgx_types;
|
|||||||
#[cfg(not(target_env = "sgx"))]
|
#[cfg(not(target_env = "sgx"))]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate sgx_tstd as std;
|
extern crate sgx_tstd as std;
|
||||||
|
extern crate goblin;
|
||||||
|
extern crate scroll;
|
||||||
extern crate sgx_tcrypto;
|
extern crate sgx_tcrypto;
|
||||||
extern crate sgx_trts;
|
extern crate sgx_trts;
|
||||||
extern crate sgx_tse;
|
extern crate sgx_tse;
|
||||||
extern crate xmas_elf;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
|
use super::super::elf_file::*;
|
||||||
use super::ThreadRef;
|
use super::ThreadRef;
|
||||||
use crate::fs::{FileMode, INodeExt};
|
use crate::fs::{FileMode, INodeExt};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use rcore_fs::vfs::INode;
|
use rcore_fs::vfs::INode;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
|
||||||
/// Load an ELF file itself or a script's interpreter into a vector.
|
/// Load an ELF file header or a script's interpreter header into a vector.
|
||||||
///
|
///
|
||||||
/// If the file is an executable binary, then just load this file.
|
/// If the file is an executable binary, then just load this file's header.
|
||||||
/// If the file is an script text, then parse the shebang and load
|
/// If the file is an script text, then parse the shebang and load
|
||||||
/// the interpreter.
|
/// the interpreter header.
|
||||||
pub fn load_exec_file_to_vec(
|
pub fn load_exec_file_hdr_to_vec(
|
||||||
file_path: &str,
|
file_path: &str,
|
||||||
current_ref: &ThreadRef,
|
current_ref: &ThreadRef,
|
||||||
) -> Result<(Option<String>, Vec<u8>)> {
|
) -> Result<(Option<String>, Arc<dyn INode>, Vec<u8>, ElfHeader)> {
|
||||||
let file_buf = load_file_to_vec(&file_path, current_ref)?;
|
let (inode, file_buf, elf_hdr) = load_file_hdr_to_vec(&file_path, current_ref)?;
|
||||||
let is_script = is_script_file(&file_buf);
|
if elf_hdr.is_some() {
|
||||||
if is_script {
|
Ok((None, inode, file_buf, elf_hdr.unwrap()))
|
||||||
|
} else {
|
||||||
|
// loaded file is not Elf format, try script file
|
||||||
|
if !is_script_file(&file_buf) {
|
||||||
|
return_errno!(ENOEXEC, "unknown executable file format");
|
||||||
|
}
|
||||||
// load interpreter
|
// load interpreter
|
||||||
let interpreter_path = parse_script_interpreter(&file_buf)?;
|
let interpreter_path = parse_script_interpreter(&file_buf)?;
|
||||||
if interpreter_path.starts_with("/host/") {
|
if interpreter_path.starts_with("/host/") {
|
||||||
@ -24,10 +30,14 @@ pub fn load_exec_file_to_vec(
|
|||||||
"libos doesn't support executing binaries from \"/host\" directory"
|
"libos doesn't support executing binaries from \"/host\" directory"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let elf_buf = load_file_to_vec(&interpreter_path, current_ref)?;
|
let (interp_inode, interp_buf, interp_hdr) =
|
||||||
Ok((Some(interpreter_path), elf_buf))
|
load_file_hdr_to_vec(&interpreter_path, current_ref)?;
|
||||||
|
let interp_hdr = if interp_hdr.is_none() {
|
||||||
|
return_errno!(ENOEXEC, "scrip interpreter is not ELF format");
|
||||||
} else {
|
} else {
|
||||||
Ok((None, file_buf))
|
interp_hdr.unwrap()
|
||||||
|
};
|
||||||
|
Ok((Some(interpreter_path), interp_inode, interp_buf, interp_hdr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +69,10 @@ fn parse_script_interpreter(file_buf: &Vec<u8>) -> Result<String> {
|
|||||||
Ok(interpreter.to_owned())
|
Ok(interpreter.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_file_to_vec(file_path: &str, current_ref: &ThreadRef) -> Result<Vec<u8>> {
|
pub fn load_file_hdr_to_vec(
|
||||||
|
file_path: &str,
|
||||||
|
current_ref: &ThreadRef,
|
||||||
|
) -> Result<(Arc<dyn INode>, Vec<u8>, Option<ElfHeader>)> {
|
||||||
let inode = current_ref
|
let inode = current_ref
|
||||||
.fs()
|
.fs()
|
||||||
.lock()
|
.lock()
|
||||||
@ -79,7 +92,16 @@ pub fn load_file_to_vec(file_path: &str, current_ref: &ThreadRef) -> Result<Vec<
|
|||||||
file_mode
|
file_mode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
inode
|
|
||||||
.read_as_vec()
|
// Try to read the file as ELF64
|
||||||
.map_err(|e| errno!(e.errno(), "failed to read the file"))
|
let mut file_buf = inode
|
||||||
|
.read_elf64_lazy_as_vec()
|
||||||
|
.map_err(|e| errno!(e.errno(), "failed to read the file"))?;
|
||||||
|
|
||||||
|
if let Ok(elf_header) = ElfFile::parse_elf_hdr(&inode, &mut file_buf) {
|
||||||
|
Ok((inode, file_buf, Some(elf_header)))
|
||||||
|
} else {
|
||||||
|
// this file is not ELF format
|
||||||
|
Ok((inode, file_buf, None))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use std::ffi::{CStr, CString};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use self::aux_vec::{AuxKey, AuxVec};
|
use self::aux_vec::{AuxKey, AuxVec};
|
||||||
use self::exec_loader::{load_exec_file_to_vec, load_file_to_vec};
|
use self::exec_loader::{load_exec_file_hdr_to_vec, load_file_hdr_to_vec};
|
||||||
use super::elf_file::{ElfFile, ElfHeader, ProgramHeader, ProgramHeaderExt, SegmentData};
|
use super::elf_file::{ElfFile, ElfHeader, ProgramHeaderExt};
|
||||||
use super::process::ProcessBuilder;
|
use super::process::ProcessBuilder;
|
||||||
use super::task::Task;
|
use super::task::Task;
|
||||||
use super::thread::ThreadName;
|
use super::thread::ThreadName;
|
||||||
@ -102,7 +102,8 @@ fn new_process(
|
|||||||
current_ref: &ThreadRef,
|
current_ref: &ThreadRef,
|
||||||
) -> Result<ProcessRef> {
|
) -> Result<ProcessRef> {
|
||||||
let mut argv = argv.clone().to_vec();
|
let mut argv = argv.clone().to_vec();
|
||||||
let (is_script, elf_buf) = load_exec_file_to_vec(file_path, current_ref)?;
|
let (is_script, elf_inode, mut elf_buf, elf_header) =
|
||||||
|
load_exec_file_hdr_to_vec(file_path, current_ref)?;
|
||||||
|
|
||||||
// elf_path might be different from file_path because file_path could lead to a script text file.
|
// elf_path might be different from file_path because file_path could lead to a script text file.
|
||||||
// And intepreter will be the loaded ELF.
|
// And intepreter will be the loaded ELF.
|
||||||
@ -117,30 +118,28 @@ fn new_process(
|
|||||||
file_path.to_string()
|
file_path.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let exec_elf_file =
|
let exec_elf_hdr = ElfFile::new(&elf_inode, &mut elf_buf, elf_header)
|
||||||
ElfFile::new(&elf_buf).cause_err(|e| errno!(e.errno(), "invalid executable"))?;
|
.cause_err(|e| errno!(e.errno(), "invalid executable"))?;
|
||||||
// Get the ldso_path of the executable
|
let ldso_path = exec_elf_hdr
|
||||||
let exec_interp_segment = exec_elf_file
|
.elf_interpreter()
|
||||||
.program_headers()
|
|
||||||
.find(|segment| segment.is_interpreter())
|
|
||||||
.ok_or_else(|| errno!(EINVAL, "cannot find the interpreter segment"))?;
|
.ok_or_else(|| errno!(EINVAL, "cannot find the interpreter segment"))?;
|
||||||
let ldso_path = match exec_interp_segment.get_content(&exec_elf_file) {
|
trace!("ldso_path = {:?}", ldso_path);
|
||||||
SegmentData::Undefined(bytes) => std::ffi::CStr::from_bytes_with_nul(bytes)
|
let (ldso_inode, mut ldso_elf_hdr_buf, ldso_elf_header) =
|
||||||
.unwrap()
|
load_file_hdr_to_vec(ldso_path, current_ref)
|
||||||
.to_str()
|
|
||||||
.unwrap(),
|
|
||||||
_ => return_errno!(EINVAL, "cannot get ldso_path from executable"),
|
|
||||||
};
|
|
||||||
let ldso_elf_buf = load_file_to_vec(ldso_path, current_ref)
|
|
||||||
.cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?;
|
.cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?;
|
||||||
let ldso_elf_file =
|
let ldso_elf_header = if ldso_elf_header.is_none() {
|
||||||
ElfFile::new(&ldso_elf_buf).cause_err(|e| errno!(e.errno(), "invalid ld.so"))?;
|
return_errno!(ENOEXEC, "ldso header is not ELF format");
|
||||||
|
} else {
|
||||||
|
ldso_elf_header.unwrap()
|
||||||
|
};
|
||||||
|
let ldso_elf_hdr = ElfFile::new(&ldso_inode, &mut ldso_elf_hdr_buf, ldso_elf_header)
|
||||||
|
.cause_err(|e| errno!(e.errno(), "invalid ld.so"))?;
|
||||||
|
|
||||||
let new_process_ref = {
|
let new_process_ref = {
|
||||||
let process_ref = current_ref.process().clone();
|
let process_ref = current_ref.process().clone();
|
||||||
|
|
||||||
let vm = init_vm::do_init(&exec_elf_file, &ldso_elf_file)?;
|
let vm = init_vm::do_init(&exec_elf_hdr, &ldso_elf_hdr)?;
|
||||||
let mut auxvec = init_auxvec(&vm, &exec_elf_file)?;
|
let mut auxvec = init_auxvec(&vm, &exec_elf_hdr)?;
|
||||||
|
|
||||||
// Notify debugger to load the symbols from elf file
|
// Notify debugger to load the symbols from elf file
|
||||||
let ldso_elf_base = vm.get_elf_ranges()[1].start() as u64;
|
let ldso_elf_base = vm.get_elf_ranges()[1].start() as u64;
|
||||||
@ -163,8 +162,7 @@ fn new_process(
|
|||||||
let task = {
|
let task = {
|
||||||
let ldso_entry = {
|
let ldso_entry = {
|
||||||
let ldso_range = vm.get_elf_ranges()[1];
|
let ldso_range = vm.get_elf_ranges()[1];
|
||||||
let ldso_entry =
|
let ldso_entry = ldso_range.start() + ldso_elf_hdr.elf_header().e_entry as usize;
|
||||||
ldso_range.start() + ldso_elf_file.elf_header().entry_point() as usize;
|
|
||||||
if !ldso_range.contains(ldso_entry) {
|
if !ldso_range.contains(ldso_entry) {
|
||||||
return_errno!(EINVAL, "Invalid program entry");
|
return_errno!(EINVAL, "Invalid program entry");
|
||||||
}
|
}
|
||||||
@ -309,13 +307,10 @@ fn init_auxvec(process_vm: &ProcessVM, exec_elf_file: &ElfFile) -> Result<AuxVec
|
|||||||
|
|
||||||
let exec_elf_base = process_vm.get_elf_ranges()[0].start() as u64;
|
let exec_elf_base = process_vm.get_elf_ranges()[0].start() as u64;
|
||||||
let exec_elf_header = exec_elf_file.elf_header();
|
let exec_elf_header = exec_elf_file.elf_header();
|
||||||
auxvec.set(AuxKey::AT_PHENT, exec_elf_header.ph_entry_size() as u64)?;
|
auxvec.set(AuxKey::AT_PHENT, exec_elf_header.e_phentsize as u64)?;
|
||||||
auxvec.set(AuxKey::AT_PHNUM, exec_elf_header.ph_count() as u64)?;
|
auxvec.set(AuxKey::AT_PHNUM, exec_elf_header.e_phnum as u64)?;
|
||||||
auxvec.set(AuxKey::AT_PHDR, exec_elf_base + exec_elf_header.ph_offset())?;
|
auxvec.set(AuxKey::AT_PHDR, exec_elf_base + exec_elf_header.e_phoff)?;
|
||||||
auxvec.set(
|
auxvec.set(AuxKey::AT_ENTRY, exec_elf_base + exec_elf_header.e_entry)?;
|
||||||
AuxKey::AT_ENTRY,
|
|
||||||
exec_elf_base + exec_elf_header.entry_point(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let ldso_elf_base = process_vm.get_elf_ranges()[1].start() as u64;
|
let ldso_elf_base = process_vm.get_elf_ranges()[1].start() as u64;
|
||||||
auxvec.set(AuxKey::AT_BASE, ldso_elf_base)?;
|
auxvec.set(AuxKey::AT_BASE, ldso_elf_base)?;
|
||||||
|
@ -1,51 +1,140 @@
|
|||||||
use xmas_elf::symbol_table::Entry;
|
|
||||||
use xmas_elf::{header, program, sections};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fmt;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
pub use xmas_elf::header::HeaderPt2 as ElfHeader;
|
use goblin::container::{Container, Ctx};
|
||||||
pub use xmas_elf::program::{ProgramHeader, ProgramIter, SegmentData};
|
pub use goblin::elf::header::Header as ElfHeader;
|
||||||
|
use goblin::elf::{program_header, Elf, ProgramHeader};
|
||||||
|
use goblin::elf64::header::ET_DYN;
|
||||||
|
use rcore_fs::vfs::INode;
|
||||||
|
use scroll::{self, ctx, Pread};
|
||||||
|
|
||||||
|
const ELF64_HDR_SIZE: usize = 64;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ElfFile<'a> {
|
pub struct ElfFile<'a> {
|
||||||
elf_buf: &'a [u8],
|
elf_buf: &'a [u8],
|
||||||
elf_inner: xmas_elf::ElfFile<'a>,
|
elf_inner: Elf<'a>,
|
||||||
|
file_inode: &'a Arc<dyn INode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Debug for ElfFile<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"ElfFile {{ inode: ???, elf_buf: {:?}, elf_inner: {:?} }}",
|
||||||
|
self.elf_buf, self.elf_inner,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ElfFile<'a> {
|
impl<'a> ElfFile<'a> {
|
||||||
pub fn new(elf_buf: &'a [u8]) -> Result<ElfFile> {
|
pub fn new(
|
||||||
let elf_inner =
|
file_inode: &'a Arc<dyn INode>,
|
||||||
xmas_elf::ElfFile::new(elf_buf).map_err(|e| errno!(ENOEXEC, "invalid ELF header"))?;
|
mut elf_buf: &'a mut [u8],
|
||||||
Self::validate(&elf_inner)?;
|
header: ElfHeader,
|
||||||
|
) -> Result<ElfFile<'a>> {
|
||||||
|
let ctx = Ctx {
|
||||||
|
le: scroll::Endian::Little,
|
||||||
|
container: Container::Big,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(ElfFile { elf_buf, elf_inner })
|
// Get a dummy Elf with only header. Fill needed parts later.
|
||||||
|
let mut elf_inner = goblin::elf::Elf::lazy_parse(header)
|
||||||
|
.map_err(|e| errno!(ENOEXEC, "invalid ELF format"))?;
|
||||||
|
|
||||||
|
let program_headers = ProgramHeader::parse(
|
||||||
|
elf_buf,
|
||||||
|
header.e_phoff as usize,
|
||||||
|
header.e_phnum as usize,
|
||||||
|
ctx,
|
||||||
|
)
|
||||||
|
.map_err(|e| errno!(ENOEXEC, "invalid program headers"))?;
|
||||||
|
|
||||||
|
// read interpreter path
|
||||||
|
let mut intepreter_count = 0;
|
||||||
|
let mut intepreter_offset = 0;
|
||||||
|
for ph in &program_headers {
|
||||||
|
ph.validate()?;
|
||||||
|
if ph.p_type == program_header::PT_INTERP && ph.p_filesz != 0 {
|
||||||
|
intepreter_count = ph.p_filesz as usize;
|
||||||
|
intepreter_offset = ph.p_offset as usize;
|
||||||
|
trace!(
|
||||||
|
"PT_INTERP offset = {}, count = {}",
|
||||||
|
intepreter_offset,
|
||||||
|
intepreter_count
|
||||||
|
);
|
||||||
|
file_inode.read_at(
|
||||||
|
intepreter_offset,
|
||||||
|
&mut elf_buf[intepreter_offset..intepreter_offset + intepreter_count],
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn program_headers<'b>(&'b self) -> ProgramIter<'b, 'a> {
|
let interpreter = if intepreter_count == 0 {
|
||||||
self.elf_inner.program_iter()
|
None
|
||||||
|
} else {
|
||||||
|
let cstr: &CStr = CStr::from_bytes_with_nul(
|
||||||
|
&elf_buf[intepreter_offset..intepreter_offset + intepreter_count],
|
||||||
|
)
|
||||||
|
.map_err(|e| errno!(ENOEXEC, "invalid interpreter path"))?;
|
||||||
|
cstr.to_str().ok()
|
||||||
|
};
|
||||||
|
trace!("interpreter = {:?}", interpreter);
|
||||||
|
elf_inner.program_headers = program_headers;
|
||||||
|
elf_inner.interpreter = interpreter;
|
||||||
|
Ok(ElfFile {
|
||||||
|
elf_buf,
|
||||||
|
elf_inner,
|
||||||
|
file_inode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program_headers<'b>(&'b self) -> impl Iterator<Item = &'b ProgramHeader> {
|
||||||
|
self.elf_inner.program_headers.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn elf_header(&self) -> &ElfHeader {
|
pub fn elf_header(&self) -> &ElfHeader {
|
||||||
&self.elf_inner.header.pt2
|
&self.elf_inner.header
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elf_interpreter(&self) -> Option<&'a str> {
|
||||||
|
self.elf_inner.interpreter
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
self.elf_buf
|
self.elf_buf
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(elf_inner: &xmas_elf::ElfFile) -> Result<()> {
|
pub fn file_inode(&self) -> &Arc<dyn INode> {
|
||||||
// Validate the ELF header
|
self.file_inode
|
||||||
xmas_elf::header::sanity_check(elf_inner)
|
}
|
||||||
.map_err(|e| errno!(ENOEXEC, "invalid ELF header"))?;
|
|
||||||
// Validate ELF type
|
pub fn parse_elf_hdr(inode: &Arc<dyn INode>, elf_buf: &mut Vec<u8>) -> Result<ElfHeader> {
|
||||||
if elf_inner.header.pt2.type_().as_type() != xmas_elf::header::Type::SharedObject {
|
// TODO: Sanity check the number of program headers..
|
||||||
|
let mut phdr_start = 0;
|
||||||
|
let mut phdr_end = 0;
|
||||||
|
|
||||||
|
let hdr_size = ELF64_HDR_SIZE;
|
||||||
|
let elf_hdr =
|
||||||
|
Elf::parse_header(&elf_buf).map_err(|e| errno!(ENOEXEC, "invalid ELF header"))?;
|
||||||
|
|
||||||
|
// executables built with -fPIE are type ET_DYN (shared object file)
|
||||||
|
if elf_hdr.e_type != ET_DYN {
|
||||||
return_errno!(ENOEXEC, "ELF is not position-independent");
|
return_errno!(ENOEXEC, "ELF is not position-independent");
|
||||||
}
|
}
|
||||||
// Validate the segments
|
|
||||||
for segment in elf_inner.program_iter() {
|
if elf_hdr.e_phnum == 0 {
|
||||||
segment.validate()?;
|
return_errno!(ENOEXEC, "ELF doesn't have any program segments");
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
let program_hdr_table_size = elf_hdr.e_phnum * elf_hdr.e_phentsize;
|
||||||
|
inode.read_at(
|
||||||
|
elf_hdr.e_phoff as usize,
|
||||||
|
&mut elf_buf[hdr_size..hdr_size + (program_hdr_table_size as usize)],
|
||||||
|
)?;
|
||||||
|
Ok(elf_hdr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,43 +142,33 @@ pub trait ProgramHeaderExt<'a> {
|
|||||||
fn loadable(&self) -> bool;
|
fn loadable(&self) -> bool;
|
||||||
fn is_interpreter(&self) -> bool;
|
fn is_interpreter(&self) -> bool;
|
||||||
fn validate(&self) -> Result<()>;
|
fn validate(&self) -> Result<()>;
|
||||||
fn get_content(&self, elf_file: &ElfFile<'a>) -> SegmentData<'a>;
|
fn get_content(&self, elf_file: &ElfFile<'a>) -> &'a [u8];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ProgramHeaderExt<'a> for ProgramHeader<'a> {
|
impl<'a> ProgramHeaderExt<'a> for ProgramHeader {
|
||||||
/// Is the segment loadable?
|
/// Is the segment loadable?
|
||||||
fn loadable(&self) -> bool {
|
fn loadable(&self) -> bool {
|
||||||
let type_ = self.get_type().unwrap();
|
let type_ = self.p_type;
|
||||||
type_ == xmas_elf::program::Type::Load
|
type_ == goblin::elf::program_header::PT_LOAD
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_interpreter(&self) -> bool {
|
fn is_interpreter(&self) -> bool {
|
||||||
let type_ = self.get_type().unwrap();
|
let type_ = self.p_type;
|
||||||
type_ == xmas_elf::program::Type::Interp
|
type_ == goblin::elf::program_header::PT_INTERP
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_content(&self, elf_file: &ElfFile<'a>) -> SegmentData<'a> {
|
fn get_content(&self, elf_file: &ElfFile<'a>) -> &'a [u8] {
|
||||||
self.get_data(&elf_file.elf_inner).unwrap()
|
let file_range = self.file_range();
|
||||||
|
&elf_file.elf_buf[file_range.start..file_range.end]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Do some basic sanity checks in case the ELF is corrupted somehow
|
/// Do some basic sanity checks in case the ELF is corrupted somehow
|
||||||
fn validate(&self) -> Result<()> {
|
fn validate(&self) -> Result<()> {
|
||||||
let ph64 = match self {
|
if !self.p_align.is_power_of_two() {
|
||||||
ProgramHeader::Ph32(ph) => {
|
|
||||||
return_errno!(ENOEXEC, "not support 32-bit ELF");
|
|
||||||
}
|
|
||||||
ProgramHeader::Ph64(ph64) => ph64,
|
|
||||||
};
|
|
||||||
if !ph64.align.is_power_of_two() {
|
|
||||||
return_errno!(EINVAL, "invalid memory alignment");
|
return_errno!(EINVAL, "invalid memory alignment");
|
||||||
}
|
}
|
||||||
if (ph64.offset % ph64.align) != (ph64.virtual_addr % ph64.align) {
|
|
||||||
return_errno!(
|
if self.p_memsz < self.p_filesz {
|
||||||
EINVAL,
|
|
||||||
"memory address and file offset is not equal, per modulo"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ph64.mem_size < ph64.file_size {
|
|
||||||
return_errno!(EINVAL, "memory size must be no less than file size");
|
return_errno!(EINVAL, "memory size must be no less than file size");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -65,8 +65,8 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
|
|||||||
elf.program_headers()
|
elf.program_headers()
|
||||||
.filter(|segment| segment.loadable())
|
.filter(|segment| segment.loadable())
|
||||||
.fold(VMLayout::new_empty(), |mut elf_layout, segment| {
|
.fold(VMLayout::new_empty(), |mut elf_layout, segment| {
|
||||||
let segment_size = (segment.virtual_addr() + segment.mem_size()) as usize;
|
let segment_size = (segment.p_vaddr + segment.p_memsz) as usize;
|
||||||
let segment_align = segment.align() as usize;
|
let segment_align = segment.p_align as usize;
|
||||||
let segment_layout = VMLayout::new(segment_size, segment_align).unwrap();
|
let segment_layout = VMLayout::new(segment_size, segment_align).unwrap();
|
||||||
elf_layout.extend(&segment_layout);
|
elf_layout.extend(&segment_layout);
|
||||||
elf_layout
|
elf_layout
|
||||||
@ -172,10 +172,10 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
|
|||||||
fn init_elf_memory(elf_range: &VMRange, elf_file: &ElfFile) -> Result<()> {
|
fn init_elf_memory(elf_range: &VMRange, elf_file: &ElfFile) -> Result<()> {
|
||||||
// Destination buffer: ELF appeared in the process
|
// Destination buffer: ELF appeared in the process
|
||||||
let elf_proc_buf = unsafe { elf_range.as_slice_mut() };
|
let elf_proc_buf = unsafe { elf_range.as_slice_mut() };
|
||||||
// Set zero for the buffer
|
let mut empty_offset_vec: Vec<(usize, usize)> = Vec::with_capacity(3); // usally two loadable segments
|
||||||
for b in &mut elf_proc_buf[..] {
|
let mut empty_start_offset = 0;
|
||||||
*b = 0;
|
let mut empty_end_offset = 0;
|
||||||
}
|
|
||||||
// Source buffer: ELF stored in the ELF file
|
// Source buffer: ELF stored in the ELF file
|
||||||
let elf_file_buf = elf_file.as_slice();
|
let elf_file_buf = elf_file.as_slice();
|
||||||
// Init all loadable segements
|
// Init all loadable segements
|
||||||
@ -183,17 +183,35 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
|
|||||||
.program_headers()
|
.program_headers()
|
||||||
.filter(|segment| segment.loadable())
|
.filter(|segment| segment.loadable())
|
||||||
.for_each(|segment| {
|
.for_each(|segment| {
|
||||||
let file_size = segment.file_size() as usize;
|
let file_size = segment.p_filesz as usize;
|
||||||
let file_offset = segment.offset() as usize;
|
let file_offset = segment.p_offset as usize;
|
||||||
let mem_addr = segment.virtual_addr() as usize;
|
let mem_addr = segment.p_vaddr as usize;
|
||||||
let mem_size = segment.mem_size() as usize;
|
let mem_size = segment.p_memsz as usize;
|
||||||
debug_assert!(file_size <= mem_size);
|
debug_assert!(file_size <= mem_size);
|
||||||
|
|
||||||
// The first file_size bytes are loaded from the ELF file,
|
// The first file_size bytes are loaded from the ELF file,
|
||||||
// the remaining (mem_size - file_size) bytes are zeros.
|
// the remaining (mem_size - file_size) bytes are zeros.
|
||||||
elf_proc_buf[mem_addr..mem_addr + file_size]
|
elf_file.file_inode().read_at(
|
||||||
.copy_from_slice(&elf_file_buf[file_offset..file_offset + file_size]);
|
file_offset,
|
||||||
|
&mut elf_proc_buf[mem_addr..mem_addr + file_size],
|
||||||
|
);
|
||||||
|
|
||||||
|
empty_end_offset = mem_addr;
|
||||||
|
empty_offset_vec.push((empty_start_offset, empty_end_offset));
|
||||||
|
empty_start_offset = empty_end_offset + file_size;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
empty_offset_vec.push((empty_start_offset, elf_proc_buf.len() - 1));
|
||||||
|
|
||||||
|
// Set zero for the remain part of the buffer
|
||||||
|
empty_offset_vec
|
||||||
|
.iter()
|
||||||
|
.for_each(|(start_offset, end_offset)| {
|
||||||
|
for b in &mut elf_proc_buf[*start_offset..*end_offset] {
|
||||||
|
*b = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user