| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| anyhow::{anyhow, Context, Error}, |
| fidl::endpoints::{ClientEnd, ServerEnd}, |
| fidl_fuchsia_component_runner::{ |
| self as fcrunner, ComponentControllerMarker, ComponentStartInfo, |
| }, |
| fidl_fuchsia_io as fio, fuchsia_async as fasync, |
| fuchsia_component::server::ServiceFs, |
| fuchsia_zircon::{ |
| self as zx, sys::zx_exception_info_t, sys::zx_thread_state_general_regs_t, |
| sys::ZX_EXCEPTION_STATE_HANDLED, sys::ZX_EXCEPTION_STATE_TRY_NEXT, |
| sys::ZX_EXCP_POLICY_CODE_BAD_SYSCALL, |
| AsHandleRef, |
| }, |
| futures::{StreamExt, TryStreamExt}, |
| io_util::directory, |
| log::info, |
| std::ffi::CString, |
| std::mem, |
| std::sync::Arc, |
| }; |
| |
| mod executive; |
| mod fs; |
| mod loader; |
| mod syscall_table; |
| mod syscalls; |
| mod types; |
| |
| use executive::*; |
| use loader::*; |
| use syscall_table::dispatch_syscall; |
| use types::*; |
| |
| // TODO: Should we move this code into fuchsia_zircon? It seems like part of a better abstraction |
| // for exception channels. |
| fn as_exception_info(buffer: &zx::MessageBuf) -> zx_exception_info_t { |
| let mut tmp = [0; mem::size_of::<zx_exception_info_t>()]; |
| tmp.clone_from_slice(buffer.bytes()); |
| unsafe { mem::transmute(tmp) } |
| } |
| |
| fn read_channel_sync(chan: &zx::Channel, buf: &mut zx::MessageBuf) -> Result<(), zx::Status> { |
| let res = chan.read(buf); |
| if let Err(zx::Status::SHOULD_WAIT) = res { |
| chan.wait_handle(zx::Signals::CHANNEL_READABLE | zx::Signals::CHANNEL_PEER_CLOSED, zx::Time::INFINITE)?; |
| chan.read(buf) |
| } else { |
| res |
| } |
| } |
| |
| fn run_process(process: Arc<ProcessContext>) -> Result<(), Error> { |
| let exceptions = &process.exceptions; |
| let mut buffer = zx::MessageBuf::new(); |
| while read_channel_sync(exceptions, &mut buffer).is_ok() { |
| let info = as_exception_info(&buffer); |
| assert!(buffer.n_handles() == 1); |
| let exception = zx::Exception::from(buffer.take_handle(0).unwrap()); |
| |
| if info.type_ != 0x8208 { |
| // ZX_EXCP_POLICY_ERROR |
| info!("exception type: 0x{:x}", info.type_); |
| exception.set_exception_state(&ZX_EXCEPTION_STATE_TRY_NEXT)?; |
| continue; |
| } |
| |
| let mut ctx = ThreadContext { |
| handle: exception.get_thread()?, |
| process: Arc::clone(&process), |
| registers: zx_thread_state_general_regs_t::default(), |
| }; |
| |
| let report = ctx.handle.get_exception_report()?; |
| |
| if report.context.synth_code != ZX_EXCP_POLICY_CODE_BAD_SYSCALL { |
| info!("exception synth_code: {}", report.context.synth_code); |
| exception.set_exception_state(&ZX_EXCEPTION_STATE_TRY_NEXT)?; |
| continue; |
| } |
| |
| let syscall_number = report.context.synth_data as syscall_number_t; |
| ctx.registers = ctx.handle.read_state_general_regs()?; |
| let regs = &ctx.registers; |
| let args = (regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9); |
| // TODO(tbodt): Print the name of the syscall instead of its number (using a proc macro or |
| // something) |
| info!(target: "strace", "{}({:#x}, {:#x}, {:#x}, {:#x}, {:#x}, {:#x})", syscall_number, args.0, args.1, args.2, args.3, args.4, args.5); |
| match dispatch_syscall(&mut ctx, syscall_number, args) { |
| Ok(rv) => { |
| info!(target: "strace", "-> {:#x}", rv.value()); |
| ctx.registers.rax = rv.value(); |
| } |
| Err(errno) => { |
| info!(target: "strace", "!-> {}", errno); |
| ctx.registers.rax = (-errno.value()) as u64; |
| } |
| } |
| ctx.handle.write_state_general_regs(ctx.registers)?; |
| |
| exception.set_exception_state(&ZX_EXCEPTION_STATE_HANDLED)?; |
| } |
| |
| Ok(()) |
| } |
| |
| async fn start_component( |
| start_info: ComponentStartInfo, |
| _controller: ServerEnd<ComponentControllerMarker>, |
| ) -> Result<(), Error> { |
| info!( |
| "start_component: {}", |
| start_info.resolved_url.clone().unwrap_or("<unknown>".to_string()) |
| ); |
| |
| let root_path = runner::get_program_string(&start_info, "root") |
| .ok_or_else(|| anyhow!("No root in component manifest"))? |
| .to_owned(); |
| let binary_path = runner::get_program_binary(&start_info)?; |
| let ns = start_info.ns.ok_or_else(|| anyhow!("Missing namespace"))?; |
| |
| let pkg_proxy = ns |
| .into_iter() |
| .find(|entry| entry.path == Some("/pkg".to_string())) |
| .ok_or_else(|| anyhow!("Missing /pkg entry in namespace"))? |
| .directory |
| .ok_or_else(|| anyhow!("Missing directory handlee in pkg namespace entry"))? |
| .into_proxy() |
| .context("failed to open /pkg")?; |
| let root_proxy = directory::open_directory( |
| &pkg_proxy, |
| &root_path, |
| fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_EXECUTABLE, |
| ) |
| .await |
| .context("failed to open root")?; |
| |
| let executable_vmo = library_loader::load_vmo(&root_proxy, &binary_path) |
| .await |
| .context("failed to load executable")?; |
| let (ldsvc_client, ldsvc_server) = zx::Channel::create()?; |
| library_loader::start( |
| directory::clone_no_describe( |
| &root_proxy, |
| Some(fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_EXECUTABLE), |
| )?, |
| ldsvc_server, |
| ); |
| let ldsvc_client = ClientEnd::new(ldsvc_client); |
| |
| let parent_job = fuchsia_runtime::job_default(); |
| let job = parent_job.create_child_job()?; |
| |
| let params = ProcessParameters { |
| name: CString::new(binary_path.clone())?, |
| argv: vec![CString::new(binary_path.clone())?], |
| environ: vec![], |
| }; |
| |
| std::thread::spawn(move || { |
| let err = (|| -> Result<(), Error> { |
| let proc = block_on(load_executable(&job, executable_vmo, ldsvc_client, ¶ms, root_proxy))?; |
| run_process(Arc::new(proc)) |
| })(); |
| err.unwrap(); |
| }); |
| Ok(()) |
| } |
| |
| async fn start_runner( |
| mut request_stream: fcrunner::ComponentRunnerRequestStream, |
| ) -> Result<(), Error> { |
| while let Some(event) = request_stream.try_next().await? { |
| match event { |
| fcrunner::ComponentRunnerRequest::Start { start_info, controller, .. } => { |
| fasync::Task::local(async move { |
| start_component(start_info, controller) |
| .await |
| .expect("failed to start component") |
| }) |
| .detach(); |
| } |
| } |
| } |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded] |
| async fn main() -> Result<(), Error> { |
| fuchsia_syslog::init_with_tags(&["starnix"]).expect("failed to initialize logger"); |
| info!("main"); |
| |
| let mut fs = ServiceFs::new_local(); |
| fs.dir("svc").add_fidl_service(move |stream| { |
| fasync::Task::local( |
| async move { start_runner(stream).await.expect("failed to start runner.") }, |
| ) |
| .detach(); |
| }); |
| fs.take_and_serve_directory_handle()?; |
| fs.collect::<()>().await; |
| Ok(()) |
| } |
| |
| pub fn block_on<F: std::future::Future>(fut: F) -> F::Output { |
| info!("awen lili"); |
| fuchsia_async::Executor::new().unwrap().run_singlethreaded(fut) |
| } |