Removes LLVM (#743)

This commit is contained in:
Putta Khunchalee 2024-03-17 02:01:57 +07:00 committed by GitHub
parent 469686edc1
commit ca98770a76
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 115 additions and 658 deletions

View file

@ -1,2 +0,0 @@
[env]
LLVM_SYS_170_PREFIX = { value = "lib/llvm", relative = true }

View file

@ -29,8 +29,7 @@ jobs:
run: |
flatpak install --noninteractive flathub \
org.kde.Platform//6.6 org.kde.Sdk//6.6 \
org.freedesktop.Sdk.Extension.rust-stable//23.08 \
org.freedesktop.Sdk.Extension.llvm17//23.08
org.freedesktop.Sdk.Extension.rust-stable//23.08
if: ${{ steps.flatpak-runtime.outputs.cache-hit != 'true' }}
- name: Restore build files
uses: actions/cache/restore@v4

View file

@ -4,50 +4,9 @@ on:
env:
CMAKE_BUILD_PARALLEL_LEVEL: '3'
jobs:
llvm:
name: LLVM for Mac
runs-on: macos-12
outputs:
dist-key: ${{ steps.cache-keys.outputs.dist }}
env:
LLVM_URL: https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.5/llvm-project-17.0.5.src.tar.xz
steps:
- name: Generate cache keys
run: |
hash=$(md5 -qs "$LLVM_URL")
echo "dist=${{ runner.os }}-llvm-$hash" >> $GITHUB_OUTPUT
id: cache-keys
- name: Check cached artifacts
uses: actions/cache/restore@v4
with:
path: lib/llvm
key: ${{ steps.cache-keys.outputs.dist }}
lookup-only: true
id: cache
- name: Download LLVM
run: |
curl -Lo llvm.tar.xz "$LLVM_URL"
tar -xJ --strip-components=1 -f llvm.tar.xz
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Run CMake
run: cmake --install-prefix ${{ github.workspace }}/lib/llvm -Wno-dev -DCMAKE_BUILD_TYPE:STRING=Release -DLLVM_ENABLE_ZSTD:BOOL=OFF -DLLVM_APPEND_VC_REV:BOOL=OFF -DLLVM_TARGETS_TO_BUILD:STRING=X86 -B build llvm
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Build
run: cmake --build build --config Release
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Export artifacts
run: cmake --install build --config Release
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Cache artifacts
uses: actions/cache/save@v4
with:
path: lib/llvm
key: ${{ steps.cache-keys.outputs.dist }}
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
build:
name: Mac
runs-on: macos-12
needs: llvm
env:
CMAKE_PREFIX_PATH: qt/6.6.0/macos
QT_URL_BASE: https://download.qt.io/online/qtsdkrepository/mac_x64/desktop/qt6_660/qt.qt6.660.clang_64/6.6.0-0-202310040910qtbase-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64.7z
@ -55,12 +14,6 @@ jobs:
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Download LLVM
uses: actions/cache/restore@v4
with:
path: lib/llvm
key: ${{ needs.llvm.outputs.dist-key }}
fail-on-cache-miss: true
- name: Generate cache keys
run: |
require Digest::MD5;
@ -104,7 +57,7 @@ jobs:
- name: Update Rust
run: rustup update stable
- name: Run CMake
run: cmake -DOB_BUILD_LLVM:BOOL=OFF --preset mac-release .
run: cmake --preset mac-release .
- name: Build
run: cmake --build --preset mac-release
- name: Run tests

View file

@ -2,65 +2,14 @@ name: CI (Windows)
on:
workflow_call:
env:
CMAKE_BUILD_PARALLEL_LEVEL: '2'
CMAKE_BUILD_PARALLEL_LEVEL: '4'
jobs:
llvm:
name: LLVM for Windows
runs-on: windows-2022
outputs:
dist-key: ${{ steps.cache-keys.outputs.dist }}
steps:
- name: Hash LLVM URL
run: |
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.5/llvm-project-17.0.5.src.tar.xz"
$urlhash = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($url))).Replace("-", "").ToLower()
echo "llvmurl=$url" >> $env:GITHUB_OUTPUT
echo "hash=$urlhash" >> $env:GITHUB_OUTPUT
id: download
- name: Generate cache keys
run: echo "dist=${{ runner.os }}-llvm-${{ steps.download.outputs.hash }}" >> $env:GITHUB_OUTPUT
id: cache-keys
- name: Check cached artifacts
uses: actions/cache/restore@v4
with:
path: lib/llvm
key: ${{ steps.cache-keys.outputs.dist }}
lookup-only: true
id: cache
- name: Download LLVM
run: |
Invoke-WebRequest -Uri ${{ steps.download.outputs.llvmurl }} -OutFile llvm.tar.xz
unxz llvm.tar.xz
tar -x --strip-components=1 -f llvm.tar
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Run CMake
run: cmake --install-prefix ${{ github.workspace }}/lib/llvm -Wno-dev -DLLVM_ENABLE_ZSTD:BOOL=OFF -DLLVM_APPEND_VC_REV:BOOL=OFF -DLLVM_TARGETS_TO_BUILD:STRING=X86 -B build llvm
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Build
run: cmake --build build --config Release
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Export artifacts
run: cmake --install build --config Release
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
- name: Cache artifacts
uses: actions/cache/save@v4
with:
path: lib/llvm
key: ${{ steps.cache-keys.outputs.dist }}
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
build:
name: Windows
runs-on: windows-2022
needs: llvm
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Download LLVM
uses: actions/cache/restore@v4
with:
path: lib/llvm
key: ${{ needs.llvm.outputs.dist-key }}
fail-on-cache-miss: true
- name: Generate cache keys
run: |
echo "cargo=${{ runner.os }}-cargo" >> $env:GITHUB_OUTPUT
@ -100,7 +49,7 @@ jobs:
- name: Update Rust
run: rustup update stable
- name: Run CMake
run: cmake -DOB_BUILD_LLVM:BOOL=OFF --preset windows-release .
run: cmake --preset windows-release .
- name: Build
run: cmake --build --preset windows-release
- name: Run tests

1
.gitignore vendored
View file

@ -2,6 +2,5 @@
/.flatpak-builder/
/.kernel-debug
/build/
/lib/
/src/target/
Cargo.lock

View file

@ -2,9 +2,6 @@ cmake_minimum_required(VERSION 3.21)
project(obliteration)
# Project options.
option(OB_BUILD_LLVM "Download and build the LLVM from source." ON)
# Set warning level to highest. This will propagate to sub-directories too.
if(WIN32)
add_compile_options(/W4)

View file

@ -28,12 +28,11 @@ We have a Discord server for discussion about Obliteration and its development.
- [x] Built-in PKG file supports for Fake PKG.
- [x] Game library.
- [x] Emulate system calls instead of user-space libraries.
- [ ] Supports AArch64 CPU.
## System requirements
- Windows 10, Linux or macOS 11+.
- x86-64 CPU.
- x86-64 CPU. We want to support non-x86 but currently we don't have any developers who are willing to work on this.
- A jailbroken PS4 with FTP server that supports SELF decryption.
### Windows-specific requirements
@ -53,8 +52,6 @@ Obliteration supports only 4KB/8KB/16KB pages. Most people should not have any p
- Rust on the latest stable channel
- CMake 3.21+
- Make sure you have `Add CMake to the system PATH` selected when installing
- Python 3.6+
- Make sure you have `Add python.exe to PATH` selected when installing
### Linux prerequisites

View file

@ -6,13 +6,10 @@ platform-extensions:
- org.freedesktop.Platform.GL.default
sdk: org.kde.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.llvm17
- org.freedesktop.Sdk.Extension.rust-stable
command: obliteration
build-options:
append-path: /usr/lib/sdk/rust-stable/bin
env:
LLVM_SYS_170_PREFIX: /usr/lib/sdk/llvm17
build-args:
- --share=network
- --device=kvm # required for running tests
@ -27,14 +24,12 @@ finish-args:
- --socket=pulseaudio
modules:
- name: obliteration
buildsystem: simple # cmake does not work somehow
buildsystem: simple
build-commands:
- cmake -DOB_BUILD_LLVM:BOOL=OFF --preset linux-release .
- cmake --preset linux-release .
- cmake --build --preset linux-release
- cargo test --manifest-path src/Cargo.toml --workspace --exclude core
- cmake --install build --prefix "$FLATPAK_DEST"
- mkdir -pv "$FLATPAK_DEST/lib"
- cp -v /usr/lib/sdk/llvm17/lib/libLLVM-17.so "$FLATPAK_DEST/lib"
sources:
- type: dir
path: .

View file

@ -5,39 +5,6 @@ include(ExternalProject)
find_package(Qt6 COMPONENTS Widgets REQUIRED)
find_package(Threads REQUIRED)
# Setup LLVM target.
set(LLVM_OPTS -DCMAKE_INSTALL_PREFIX:STRING=<INSTALL_DIR> -DLLVM_ENABLE_ZSTD:BOOL=OFF -DLLVM_APPEND_VC_REV:BOOL=OFF)
if(WIN32)
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64")
list(APPEND LLVM_OPTS -DLLVM_TARGETS_TO_BUILD:STRING=X86)
else()
message(FATAL_ERROR "Target CPU is not supported")
endif()
else()
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
list(APPEND LLVM_OPTS -DLLVM_TARGETS_TO_BUILD:STRING=X86)
elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")
list(APPEND LLVM_OPTS -DLLVM_TARGETS_TO_BUILD:STRING=AArch64)
else()
message(FATAL_ERROR "Target CPU is not supported")
endif()
list(APPEND LLVM_OPTS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
endif()
if(OB_BUILD_LLVM)
ExternalProject_Add(llvm
URL https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.5/llvm-project-17.0.5.src.tar.xz
URL_HASH SHA256=95d7eff82945cf05c16a1851d7b391fc2da726b87c1138125e3b6e4d300ab834
DOWNLOAD_NO_PROGRESS true
CMAKE_ARGS -Wno-dev
CMAKE_CACHE_ARGS ${LLVM_OPTS}
SOURCE_SUBDIR llvm
BUILD_ALWAYS ON
INSTALL_DIR ${CMAKE_SOURCE_DIR}/lib/llvm)
endif()
# Setup Rust target.
set(RUST_OUTPUTS $<IF:$<CONFIG:Debug>,${CMAKE_CURRENT_SOURCE_DIR}/target/debug,${CMAKE_CURRENT_SOURCE_DIR}/target/release>)
set(KERNEL ${RUST_OUTPUTS}/obkrnl${CMAKE_EXECUTABLE_SUFFIX})
@ -52,10 +19,6 @@ add_custom_target(core
COMMAND cargo build $<IF:$<CONFIG:Debug>,--profile=dev,--release>
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
if(OB_BUILD_LLVM)
add_dependencies(core llvm)
endif()
# Setup application target.
add_executable(obliteration WIN32 MACOSX_BUNDLE
ansi_escape.cpp

View file

@ -19,7 +19,6 @@ hv = { path = "../hv" }
iced-x86 = { version = "1.18", features = ["code_asm"] }
libc = "0.2"
llt = { path = "../llt" }
llvm-sys = { version = "170.0.0", features = ["strict-versioning", "prefer-static"] }
macros = { path = "../macros" }
param = { path = "../param" }
serde = { version = "1.0", features = ["derive"] }

View file

@ -1,21 +0,0 @@
use crate::llvm::module::LlvmModule;
use thiserror::Error;
/// Contains states for lifting a module.
pub(super) struct Codegen<'a> {
output: &'a mut LlvmModule,
}
impl<'a> Codegen<'a> {
pub fn new(output: &'a mut LlvmModule) -> Self {
Self { output }
}
pub fn lift(&mut self, _offset: usize) -> Result<(), LiftError> {
Ok(())
}
}
/// Represents an error for [`Codegen::lift()`].
#[derive(Debug, Error)]
pub enum LiftError {}

View file

@ -1,108 +0,0 @@
use self::codegen::Codegen;
use super::ExecutionEngine;
use crate::fs::VPathBuf;
use crate::llvm::Llvm;
use crate::rtld::Module;
use crate::syscalls::Syscalls;
use std::sync::Arc;
use thiserror::Error;
mod codegen;
/// An implementation of [`ExecutionEngine`] using JIT powered by LLVM IR.
#[derive(Debug)]
pub struct LlvmEngine {
llvm: Arc<Llvm>,
}
impl LlvmEngine {
pub fn new(llvm: &Arc<Llvm>) -> Arc<Self> {
Arc::new(Self { llvm: llvm.clone() })
}
fn lift(
&self,
module: &Module<Self>,
) -> Result<crate::llvm::module::ExecutionEngine, LiftError> {
// Get a list of public functions.
let path = module.path();
let targets = match module.entry() {
Some(v) => vec![v],
None => Vec::new(),
};
// Lift the public functions.
let mut lifting = self.llvm.create_module(path);
let mut codegen = Codegen::new(&mut lifting);
for &addr in &targets {
if let Err(e) = codegen.lift(addr) {
return Err(LiftError::LiftingFailed(path.to_owned(), addr, e));
}
}
drop(codegen);
// Create LLVM execution engine.
let lifted = match lifting.create_execution_engine() {
Ok(v) => v,
Err(e) => return Err(LiftError::CreateExecutionEngineFailed(path.to_owned(), e)),
};
Ok(lifted)
}
}
impl ExecutionEngine for LlvmEngine {
type RawFn = RawFn;
type SetupModuleErr = SetupModuleError;
type GetFunctionErr = GetFunctionError;
fn set_syscalls(&self, v: Syscalls) {
todo!()
}
fn setup_module(self: &Arc<Self>, md: &mut Module<Self>) -> Result<(), Self::SetupModuleErr> {
todo!()
}
unsafe fn get_function(
self: &Arc<Self>,
md: &Arc<Module<Self>>,
addr: usize,
) -> Result<Arc<Self::RawFn>, Self::GetFunctionErr> {
todo!()
}
}
/// An implementation of [`ExecutionEngine::RawFn`].
#[derive(Debug)]
pub struct RawFn {}
impl super::RawFn for RawFn {
fn addr(&self) -> usize {
todo!()
}
unsafe fn exec1<R, A>(&self, a: A) -> R {
todo!()
}
}
/// An implementation of [`ExecutionEngine::SetupModuleErr`].
#[derive(Debug, Error)]
pub enum SetupModuleError {}
/// An implementation of [`ExecutionEngine::GetFunctionErr`].
#[derive(Debug, Error)]
pub enum GetFunctionError {}
/// Represents an error when module lifting is failed.
#[derive(Debug, Error)]
enum LiftError {
#[error("cannot lift function {1:#018x} on {0}")]
LiftingFailed(VPathBuf, usize, #[source] self::codegen::LiftError),
#[error("cannot create LLVM execution engine for {0}")]
CreateExecutionEngineFailed(VPathBuf, #[source] crate::llvm::Error),
}

View file

@ -3,53 +3,20 @@ use crate::memory::MemoryManager;
use crate::process::ResourceType;
use crate::process::VProc;
use crate::rtld::Module;
use crate::syscalls::Syscalls;
use std::error::Error;
use std::ffi::CString;
use std::fmt::Debug;
use std::marker::PhantomPinned;
use std::mem::size_of_val;
use std::pin::Pin;
use std::sync::Arc;
pub mod llvm;
#[cfg(target_arch = "x86_64")]
pub mod native;
/// An object to execute the PS4 binary.
pub trait ExecutionEngine: Debug + Send + Sync + 'static {
type RawFn: RawFn;
type SetupModuleErr: Error;
type GetFunctionErr: Error;
/// # Panics
/// If this method called a second time.
fn set_syscalls(&self, v: Syscalls);
fn setup_module(self: &Arc<Self>, md: &mut Module<Self>) -> Result<(), Self::SetupModuleErr>;
unsafe fn get_function(
self: &Arc<Self>,
md: &Arc<Module<Self>>,
addr: usize,
) -> Result<Arc<Self::RawFn>, Self::GetFunctionErr>;
}
/// A function that was produced by [`ExecutionEngine`].
pub trait RawFn: Debug + Send + Sync + 'static {
/// Returns address of this function in the memory.
fn addr(&self) -> usize;
/// # Safety
/// The provided signature must be matched with the underlying function.
unsafe fn exec1<R, A>(&self, a: A) -> R;
}
/// Encapsulate an argument of the PS4 entry point.
pub struct EntryArg<E: ExecutionEngine> {
pub struct EntryArg {
vp: Arc<VProc>,
mm: Arc<MemoryManager>,
app: Arc<Module<E>>,
app: Arc<Module>,
name: CString,
path: CString,
canary: [u8; 64],
@ -58,8 +25,8 @@ pub struct EntryArg<E: ExecutionEngine> {
_pin: PhantomPinned,
}
impl<E: ExecutionEngine> EntryArg<E> {
pub fn new(vp: &Arc<VProc>, mm: &Arc<MemoryManager>, app: Arc<Module<E>>) -> Self {
impl EntryArg {
pub fn new(vp: &Arc<VProc>, mm: &Arc<MemoryManager>, app: Arc<Module>) -> Self {
let path = app.path();
let name = CString::new(path.file_name().unwrap()).unwrap();
let path = CString::new(path.as_str()).unwrap();

View file

@ -1,4 +1,3 @@
use super::ExecutionEngine;
use crate::fs::{VPath, VPathBuf};
use crate::memory::Protections;
use crate::process::VThread;
@ -38,10 +37,27 @@ impl NativeEngine {
})
}
fn patch_mod<E>(self: &Arc<Self>, module: &mut Module<E>) -> Result<usize, SetupModuleError>
where
E: ExecutionEngine,
{
pub fn set_syscalls(&self, v: Syscalls) {
self.syscalls.set(v).unwrap();
}
pub fn setup_module(self: &Arc<Self>, md: &mut Module) -> Result<(), SetupModuleError> {
self.patch_mod(md)?;
Ok(())
}
pub unsafe fn get_function(
self: &Arc<Self>,
md: &Arc<Module>,
addr: usize,
) -> Result<Arc<RawFn>, GetFunctionError> {
Ok(Arc::new(RawFn {
md: md.clone(),
addr,
}))
}
fn patch_mod(self: &Arc<Self>, module: &mut Module) -> Result<usize, SetupModuleError> {
let path = module.path();
// Patch all executable sections.
@ -648,33 +664,7 @@ impl NativeEngine {
}
}
impl ExecutionEngine for NativeEngine {
type RawFn = RawFn;
type SetupModuleErr = SetupModuleError;
type GetFunctionErr = GetFunctionError;
fn set_syscalls(&self, v: Syscalls) {
self.syscalls.set(v).unwrap();
}
fn setup_module(self: &Arc<Self>, md: &mut Module<Self>) -> Result<(), Self::SetupModuleErr> {
self.patch_mod(md)?;
Ok(())
}
unsafe fn get_function(
self: &Arc<Self>,
md: &Arc<Module<Self>>,
addr: usize,
) -> Result<Arc<Self::RawFn>, Self::GetFunctionErr> {
Ok(Arc::new(RawFn {
md: md.clone(),
addr,
}))
}
}
/// An implementation of [`super::RawFn`].
/// Raw executable function.
#[derive(Debug)]
pub struct RawFn {
#[allow(unused)]
@ -682,12 +672,12 @@ pub struct RawFn {
addr: usize,
}
impl super::RawFn for RawFn {
fn addr(&self) -> usize {
impl RawFn {
pub fn addr(&self) -> usize {
self.addr
}
unsafe fn exec1<R, A>(&self, a: A) -> R {
pub unsafe fn exec1<R, A>(&self, a: A) -> R {
let f: unsafe extern "sysv64" fn(A) -> R = transmute(self.addr);
f(a)
}

View file

@ -1,78 +0,0 @@
use self::module::LlvmModule;
use llvm_sys::core::{LLVMContextCreate, LLVMContextDispose, LLVMModuleCreateWithNameInContext};
use llvm_sys::prelude::LLVMContextRef;
use std::ffi::{c_char, CStr, CString};
use std::fmt::Display;
use std::sync::{Arc, Mutex};
pub mod module;
/// A LLVM wrapper for thread-safe.
#[derive(Debug)]
pub struct Llvm {
context: Mutex<LLVMContextRef>,
}
impl Llvm {
pub fn new() -> Arc<Self> {
let context = unsafe { LLVMContextCreate() };
Arc::new(Self {
context: Mutex::new(context),
})
}
pub fn create_module(self: &Arc<Self>, name: &str) -> LlvmModule {
let context = self.context.lock().unwrap();
let name = CString::new(name).unwrap();
let module = unsafe { LLVMModuleCreateWithNameInContext(name.as_ptr(), *context) };
LlvmModule::new(self, module)
}
fn with_context<F, R>(&self, f: F) -> R
where
F: FnOnce(LLVMContextRef) -> R,
{
f(*self.context.lock().unwrap())
}
}
impl Drop for Llvm {
fn drop(&mut self) {
unsafe { LLVMContextDispose(*self.context.get_mut().unwrap()) };
}
}
unsafe impl Send for Llvm {}
unsafe impl Sync for Llvm {}
/// A wrapper on LLVM error.
#[derive(Debug)]
pub struct Error {
message: String,
}
impl Error {
/// # Safety
/// `message` must be pointed to a null-terminated string allocated with `malloc` or a
/// compatible funtion because this method will free it with `free`.
unsafe fn new(message: *mut c_char) -> Self {
let owned = CStr::from_ptr(message)
.to_string_lossy()
.trim_end_matches('.')
.to_owned();
libc::free(message as _);
Self { message: owned }
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for Error {}

View file

@ -1,70 +0,0 @@
use super::{Error, Llvm};
use llvm_sys::core::LLVMDisposeModule;
use llvm_sys::execution_engine::{
LLVMCreateExecutionEngineForModule, LLVMDisposeExecutionEngine, LLVMExecutionEngineRef,
};
use llvm_sys::prelude::LLVMModuleRef;
use std::ffi::c_char;
use std::ptr::null_mut;
use std::sync::Arc;
/// A wrapper on LLVM module for thread-safe.
pub struct LlvmModule {
llvm: Arc<Llvm>,
module: LLVMModuleRef,
}
impl LlvmModule {
pub(super) fn new(llvm: &Arc<Llvm>, module: LLVMModuleRef) -> Self {
Self {
llvm: llvm.clone(),
module,
}
}
pub fn create_execution_engine(mut self) -> Result<ExecutionEngine, Error> {
let mut ee: LLVMExecutionEngineRef = null_mut();
let module = self.module;
let mut error: *mut c_char = null_mut();
self.module = null_mut();
if self.llvm.with_context(|_| unsafe {
LLVMCreateExecutionEngineForModule(&mut ee, module, &mut error)
}) != 0
{
return Err(unsafe { Error::new(error) });
}
Ok(ExecutionEngine {
llvm: self.llvm.clone(),
ee,
})
}
}
impl Drop for LlvmModule {
fn drop(&mut self) {
let m = self.module;
if !m.is_null() {
self.llvm.with_context(|_| unsafe { LLVMDisposeModule(m) });
}
}
}
/// A wrapper on LLVM Execution Engine for thread-safe.
///
/// # Safety
/// All JITed functions from this EE must not invoked once this EE has been droped.
pub struct ExecutionEngine {
llvm: Arc<Llvm>,
ee: LLVMExecutionEngineRef,
}
impl Drop for ExecutionEngine {
fn drop(&mut self) {
self.llvm
.with_context(|_| unsafe { LLVMDisposeExecutionEngine(self.ee) });
}
}

View file

@ -2,11 +2,11 @@ use crate::arch::MachDep;
use crate::budget::{Budget, BudgetManager, ProcType};
use crate::debug::{DebugManager, DebugManagerInitError};
use crate::dmem::DmemManager;
use crate::ee::{EntryArg, RawFn};
use crate::ee::native::NativeEngine;
use crate::ee::EntryArg;
use crate::errno::EEXIST;
use crate::fs::{Fs, FsInitError, MkdirError, MountError, MountFlags, MountOpts, VPathBuf};
use crate::kqueue::KernelQueueManager;
use crate::llvm::Llvm;
use crate::log::{print, LOGGER};
use crate::memory::{MemoryManager, MemoryManagerError};
use crate::namedobj::NamedObjManager;
@ -22,7 +22,7 @@ use crate::time::TimeManager;
use crate::tty::{TtyInitError, TtyManager};
use crate::ucred::{AuthAttrs, AuthCaps, AuthInfo, AuthPaid, Gid, Ucred, Uid};
use crate::umtx::UmtxManager;
use clap::{Parser, ValueEnum};
use clap::Parser;
use llt::{OsThread, SpawnError};
use macros::vpath;
use param::Param;
@ -47,7 +47,6 @@ mod errno;
mod fs;
mod idt;
mod kqueue;
mod llvm;
mod log;
mod memory;
mod namedobj;
@ -66,10 +65,10 @@ mod ucred;
mod umtx;
fn main() -> Exit {
start().into()
run().into()
}
fn start() -> Result<(), KernelError> {
fn run() -> Result<(), KernelError> {
// Begin logger.
log::init();
@ -189,7 +188,6 @@ fn start() -> Result<(), KernelError> {
));
// Initialize foundations.
let llvm = Llvm::new();
let mut syscalls = Syscalls::new();
let fs = Fs::new(args.system, &cred, &mut syscalls)?;
@ -329,47 +327,6 @@ fn start() -> Result<(), KernelError> {
print(log);
// Select execution engine.
match args.execution_engine.unwrap_or_default() {
#[cfg(target_arch = "x86_64")]
ExecutionEngine::Native => run(
args.debug_dump,
&param,
auth,
syscalls,
&fs,
&mm,
crate::ee::native::NativeEngine::new(),
root,
),
#[cfg(not(target_arch = "x86_64"))]
ExecutionEngine::Native => {
error!("Native execution engine cannot be used on your machine.");
Err(KernelError::NativeExecutionEngineNotSupported)
}
ExecutionEngine::Llvm => run(
args.debug_dump,
&param,
auth,
syscalls,
&fs,
&mm,
crate::ee::llvm::LlvmEngine::new(&llvm),
root,
),
}
}
fn run<E: crate::ee::ExecutionEngine>(
dump: Option<PathBuf>,
param: &Arc<Param>,
auth: AuthInfo,
mut syscalls: Syscalls,
fs: &Arc<Fs>,
mm: &Arc<MemoryManager>,
ee: Arc<E>,
root: VPathBuf,
) -> Result<(), KernelError> {
// Initialize TTY system.
#[allow(unused_variables)] // TODO: Remove this when someone use tty.
let tty = TtyManager::new()?;
@ -381,9 +338,9 @@ fn run<E: crate::ee::ExecutionEngine>(
let machdep = MachDep::new(&mut syscalls);
let budget = BudgetManager::new(&mut syscalls);
DmemManager::new(fs, &mut syscalls);
SharedMemoryManager::new(mm, &mut syscalls);
Sysctl::new(mm, &machdep, &mut syscalls);
DmemManager::new(&fs, &mut syscalls);
SharedMemoryManager::new(&mm, &mut syscalls);
Sysctl::new(&mm, &machdep, &mut syscalls);
TimeManager::new(&mut syscalls);
KernelQueueManager::new(&mut syscalls);
NetManager::new(&mut syscalls);
@ -408,7 +365,8 @@ fn run<E: crate::ee::ExecutionEngine>(
// Initialize runtime linker.
info!("Initializing runtime linker.");
let ld = RuntimeLinker::new(fs, mm, &ee, &mut syscalls, dump.as_deref())
let ee = NativeEngine::new();
let ld = RuntimeLinker::new(&fs, &mm, &ee, &mut syscalls, args.debug_dump.as_deref())
.map_err(|e| KernelError::RuntimeLinkerInitFailed(e.into()))?;
ee.set_syscalls(syscalls);
@ -462,7 +420,7 @@ fn run<E: crate::ee::ExecutionEngine>(
// Get entry point.
let boot = ld.kernel().unwrap();
let mut arg = Box::pin(EntryArg::<E>::new(&proc, mm, app.clone()));
let mut arg = Box::pin(EntryArg::new(&proc, &mm, app.clone()));
let entry = unsafe { boot.get_function(boot.entry().unwrap()) };
let entry = move || unsafe { entry.exec1(arg.as_mut().as_vec().as_ptr()) };
@ -482,7 +440,7 @@ fn run<E: crate::ee::ExecutionEngine>(
let main: OsThread = unsafe { main.start(stack.start(), stack.len(), entry) }?;
// Begin Discord Rich Presence before blocking current thread.
if let Err(e) = discord_presence(param) {
if let Err(e) = discord_presence(&param) {
warn!(e, "Failed to setup Discord rich presence");
}
@ -579,27 +537,6 @@ struct Args {
#[arg(long)]
#[serde(default)]
pro: bool,
#[arg(long, short)]
execution_engine: Option<ExecutionEngine>,
}
#[derive(Clone, ValueEnum, Deserialize)]
enum ExecutionEngine {
Native,
Llvm,
}
impl Default for ExecutionEngine {
#[cfg(target_arch = "x86_64")]
fn default() -> Self {
ExecutionEngine::Native
}
#[cfg(not(target_arch = "x86_64"))]
fn default() -> Self {
ExecutionEngine::Llvm
}
}
#[derive(Debug, Error)]
@ -643,10 +580,6 @@ enum KernelError {
#[error("memory manager initialization failed")]
MemoryManagerInitFailed(#[from] MemoryManagerError),
#[cfg(not(target_arch = "x86_64"))]
#[error("the native execution engine is only supported on x86_64")]
NativeExecutionEngineNotSupported,
#[error("tty initialization failed")]
TtyInitFailed(#[from] TtyInitError),

View file

@ -2,7 +2,7 @@ pub use self::mem::*;
pub use self::module::*;
use self::resolver::{ResolveFlags, SymbolResolver};
use crate::budget::ProcType;
use crate::ee::{ExecutionEngine, RawFn};
use crate::ee::native::{NativeEngine, SetupModuleError};
use crate::errno::{Errno, EINVAL, ENOENT, ENOEXEC, ENOMEM, EPERM, ESRCH};
use crate::fs::{Fs, OpenError, VPath, VPathBuf};
use crate::idt::Entry;
@ -33,21 +33,21 @@ mod resolver;
/// An implementation of
/// https://github.com/freebsd/freebsd-src/blob/release/9.1.0/libexec/rtld-elf/rtld.c.
#[derive(Debug)]
pub struct RuntimeLinker<E: ExecutionEngine> {
pub struct RuntimeLinker {
fs: Arc<Fs>,
mm: Arc<MemoryManager>,
ee: Arc<E>,
ee: Arc<NativeEngine>,
// TODO: Move all fields after this to proc.
list: Gutex<Vec<Arc<Module<E>>>>, // obj_list + obj_tail
app: Arc<Module<E>>, // obj_main
kernel: Gutex<Option<Arc<Module<E>>>>, // obj_kernel
mains: Gutex<Vec<Arc<Module<E>>>>, // list_main
globals: Gutex<Vec<Arc<Module<E>>>>, // list_global
list: Gutex<Vec<Arc<Module>>>, // obj_list + obj_tail
app: Arc<Module>, // obj_main
kernel: Gutex<Option<Arc<Module>>>, // obj_kernel
mains: Gutex<Vec<Arc<Module>>>, // list_main
globals: Gutex<Vec<Arc<Module>>>, // list_global
tls: Gutex<TlsAlloc>,
flags: LinkerFlags,
}
impl<E: ExecutionEngine> RuntimeLinker<E> {
impl RuntimeLinker {
const NID_CHARS: &'static [u8] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
const NID_SALT: [u8; 16] = [
@ -58,10 +58,10 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
pub fn new(
fs: &Arc<Fs>,
mm: &Arc<MemoryManager>,
ee: &Arc<E>,
ee: &Arc<NativeEngine>,
sys: &mut Syscalls,
dump: Option<&Path>,
) -> Result<Arc<Self>, RuntimeLinkerError<E>> {
) -> Result<Arc<Self>, RuntimeLinkerError> {
// Get eboot.bin.
let path = vpath!("/app0/eboot.bin");
let file = match fs.open(path, None) {
@ -159,15 +159,15 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
Ok(ld)
}
pub fn app(&self) -> &Arc<Module<E>> {
pub fn app(&self) -> &Arc<Module> {
&self.app
}
pub fn kernel(&self) -> Option<Arc<Module<E>>> {
pub fn kernel(&self) -> Option<Arc<Module>> {
self.kernel.read().clone()
}
pub fn set_kernel(&self, md: Arc<Module<E>>) {
pub fn set_kernel(&self, md: Arc<Module>) {
*self.kernel.write() = Some(md);
}
@ -180,7 +180,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
_: LoadFlags,
force: bool,
main: bool,
) -> Result<Arc<Module<E>>, LoadError<E>> {
) -> Result<Arc<Module>, LoadError> {
// Check if already loaded.
let name = path.file_name().unwrap().to_owned();
let mut list = self.list.write();
@ -276,7 +276,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
})?;
// Add to list.
let module = entry.data().clone().downcast::<Module<E>>().unwrap();
let module = entry.data().clone().downcast::<Module>().unwrap();
list.push(module.clone());
@ -288,7 +288,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
}
/// See `init_dag` on the PS4 for a reference.
fn init_dag(&self, md: &Arc<Module<E>>) {
fn init_dag(&self, md: &Arc<Module>) {
// Do nothing if already initializes.
let mut flags = md.flags_mut();
@ -307,7 +307,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
/// See `do_dlsym` on the PS4 for a reference.
fn resolve_symbol<'a>(
&self,
md: &'a Arc<Module<E>>,
md: &'a Arc<Module>,
mut name: Cow<'a, str>,
mut lib: Option<&'a str>,
flags: ResolveFlags,
@ -345,7 +345,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
None,
mname,
lib,
SymbolResolver::<E>::hash(Some(name.as_ref()), lib, mname),
SymbolResolver::hash(Some(name.as_ref()), lib, mname),
flags | ResolveFlags::UNK3 | ResolveFlags::UNK4,
&dags,
)
@ -673,9 +673,9 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
/// No other threads may access the memory of all loaded modules.
unsafe fn relocate(
&self,
md: &Arc<Module<E>>,
list: &[Arc<Module<E>>],
resolver: &SymbolResolver<E>,
md: &Arc<Module>,
list: &[Arc<Module>],
resolver: &SymbolResolver,
) -> Result<(), RelocateError> {
// TODO: Implement flags & 0x800.
self.relocate_single(md, resolver)?;
@ -698,8 +698,8 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
/// No other thread may access the module memory.
unsafe fn relocate_single<'b>(
&self,
md: &'b Arc<Module<E>>,
resolver: &SymbolResolver<'b, E>,
md: &'b Arc<Module>,
resolver: &SymbolResolver<'b>,
) -> Result<(), RelocateError> {
// Unprotect the memory.
let mut mem = match md.memory().unprotect() {
@ -722,10 +722,10 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
/// See `reloc_non_plt` on the PS4 kernel for a reference.
fn relocate_rela<'b>(
&self,
md: &'b Arc<Module<E>>,
md: &'b Arc<Module>,
mem: &mut [u8],
relocated: &mut [Option<Relocated<E>>],
resolver: &SymbolResolver<'b, E>,
relocated: &mut [Option<Relocated>],
resolver: &SymbolResolver<'b>,
) -> Result<(), RelocateError> {
let info = md.file_info().unwrap(); // Let it panic because the PS4 assume it is available.
let addr = mem.as_ptr() as usize;
@ -827,10 +827,10 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
/// See `reloc_jmplots` on the PS4 for a reference.
fn relocate_plt<'b>(
&self,
md: &'b Arc<Module<E>>,
md: &'b Arc<Module>,
mem: &mut [u8],
relocated: &mut [Option<Relocated<E>>],
resolver: &SymbolResolver<'b, E>,
relocated: &mut [Option<Relocated>],
resolver: &SymbolResolver<'b>,
) -> Result<(), RelocateError> {
// Do nothing if not a dynamic module.
let info = match md.file_info() {
@ -878,7 +878,7 @@ impl<E: ExecutionEngine> RuntimeLinker<E> {
Ok(())
}
fn get_relocated(md: Arc<Module<E>>, sym: usize) -> (Relocated<E>, usize) {
fn get_relocated(md: Arc<Module>, sym: usize) -> (Relocated, usize) {
let sym = md.symbol(sym).unwrap();
match sym.ty() {
@ -1110,7 +1110,7 @@ bitflags! {
/// Represents the error for [`RuntimeLinker`] initialization.
#[derive(Debug, Error)]
pub enum RuntimeLinkerError<E: ExecutionEngine> {
pub enum RuntimeLinkerError {
#[error("cannot open {0}")]
OpenExeFailed(VPathBuf, #[source] OpenError),
@ -1124,7 +1124,7 @@ pub enum RuntimeLinkerError<E: ExecutionEngine> {
MapExeFailed(VPathBuf, #[source] MapError),
#[error("cannot setup {0}")]
SetupExeFailed(VPathBuf, #[source] E::SetupModuleErr),
SetupExeFailed(VPathBuf, #[source] SetupModuleError),
}
/// Represents an error for (S)ELF mapping.
@ -1178,7 +1178,7 @@ pub enum MapError {
/// Represents an error for (S)ELF loading.
#[derive(Debug, Error)]
pub enum LoadError<E: ExecutionEngine> {
pub enum LoadError {
#[error("cannot open the specified file")]
OpenFileFailed(#[source] OpenError),
@ -1195,10 +1195,10 @@ pub enum LoadError<E: ExecutionEngine> {
ImpureText,
#[error("cannot setup the module")]
SetupFailed(#[source] E::SetupModuleErr),
SetupFailed(#[source] SetupModuleError),
}
impl<E: ExecutionEngine> Errno for LoadError<E> {
impl Errno for LoadError {
fn errno(&self) -> NonZeroI32 {
match self {
Self::OpenFileFailed(_) => ENOENT,

View file

@ -1,5 +1,5 @@
use super::{MapError, Memory};
use crate::ee::ExecutionEngine;
use crate::ee::native::{NativeEngine, RawFn};
use crate::fs::{VFile, VPath, VPathBuf};
use crate::log::{print, LogEntry};
use crate::memory::MemoryManager;
@ -19,8 +19,8 @@ use std::sync::Arc;
/// An implementation of
/// https://github.com/freebsd/freebsd-src/blob/release/9.1.0/libexec/rtld-elf/rtld.h#L147.
#[derive(Debug)]
pub struct Module<E: ExecutionEngine + ?Sized> {
ee: Arc<E>,
pub struct Module {
ee: Arc<NativeEngine>,
id: u32,
init: Option<usize>,
entry: Option<usize>,
@ -40,7 +40,7 @@ pub struct Module<E: ExecutionEngine + ?Sized> {
modules: Vec<ModuleInfo>,
libraries: Vec<LibraryInfo>,
memory: Memory,
relocated: Gutex<Vec<Option<Relocated<E>>>>,
relocated: Gutex<Vec<Option<Relocated>>>,
file_info: Option<FileInfo>,
path: VPathBuf,
is_self: bool,
@ -49,10 +49,10 @@ pub struct Module<E: ExecutionEngine + ?Sized> {
symbols: Vec<Symbol>,
}
impl<E: ExecutionEngine> Module<E> {
impl Module {
pub(super) fn map<N: Into<String>>(
mm: &Arc<MemoryManager>,
ee: &Arc<E>,
ee: &Arc<NativeEngine>,
mut image: Elf<VFile>,
base: usize,
mem_name: N,
@ -286,7 +286,7 @@ impl<E: ExecutionEngine> Module<E> {
&self.memory
}
pub fn relocated_mut(&self) -> GutexWriteGuard<'_, Vec<Option<Relocated<E>>>> {
pub fn relocated_mut(&self) -> GutexWriteGuard<'_, Vec<Option<Relocated>>> {
self.relocated.write()
}
@ -314,7 +314,7 @@ impl<E: ExecutionEngine> Module<E> {
/// # Safety
/// `off` must be a valid offset without base adjustment of a function in the memory of this
/// module.
pub unsafe fn get_function(self: &Arc<Self>, off: usize) -> Arc<E::RawFn> {
pub unsafe fn get_function(self: &Arc<Self>, off: usize) -> Arc<RawFn> {
self.ee
.get_function(self, self.memory.addr() + self.memory.base() + off)
.unwrap()
@ -758,8 +758,8 @@ pub struct NeededModule {
/// Indicated a type of value in the relocation entry.
#[derive(Debug)]
pub enum Relocated<E: ExecutionEngine + ?Sized> {
Executable(Arc<E::RawFn>),
Data((Arc<Module<E>>, usize)),
Tls((Arc<Module<E>>, usize)),
pub enum Relocated {
Executable(Arc<RawFn>),
Data((Arc<Module>, usize)),
Tls((Arc<Module>, usize)),
}

View file

@ -1,23 +1,18 @@
use super::Module;
use crate::ee::ExecutionEngine;
use bitflags::bitflags;
use elf::Symbol;
use std::borrow::Cow;
use std::sync::Arc;
/// An object to resolve a symbol from loaded (S)ELF.
pub struct SymbolResolver<'a, E: ExecutionEngine> {
mains: &'a [Arc<Module<E>>],
globals: &'a [Arc<Module<E>>],
pub struct SymbolResolver<'a> {
mains: &'a [Arc<Module>],
globals: &'a [Arc<Module>],
new_algorithm: bool,
}
impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
pub fn new(
mains: &'a [Arc<Module<E>>],
globals: &'a [Arc<Module<E>>],
new_algorithm: bool,
) -> Self {
impl<'a> SymbolResolver<'a> {
pub fn new(mains: &'a [Arc<Module>], globals: &'a [Arc<Module>], new_algorithm: bool) -> Self {
Self {
mains,
globals,
@ -28,10 +23,10 @@ impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
/// See `find_symdef` on the PS4 for a reference.
pub fn resolve_with_local(
&self,
md: &Arc<Module<E>>,
md: &Arc<Module>,
index: usize,
mut flags: ResolveFlags,
) -> Option<(Arc<Module<E>>, usize)> {
) -> Option<(Arc<Module>, usize)> {
// Check if symbol index is valid.
let sym = md.symbols().get(index)?;
let data = md.file_info().unwrap();
@ -102,14 +97,14 @@ impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
/// See `symlook_default` on the PS4 for a reference.
pub fn resolve(
&self,
refmod: &'a Arc<Module<E>>,
refmod: &'a Arc<Module>,
name: Option<&str>,
decoded_name: Option<&Cow<str>>,
symmod: Option<&str>,
symlib: Option<&str>,
hash: u64,
flags: ResolveFlags,
) -> Option<(Arc<Module<E>>, usize)> {
) -> Option<(Arc<Module>, usize)> {
// TODO: Resolve from DAGs.
self.resolve_from_global(refmod, name, decoded_name, symmod, symlib, hash, flags)
}
@ -117,14 +112,14 @@ impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
/// See `symlook_global` on the PS4 for a reference.
pub fn resolve_from_global(
&self,
refmod: &'a Arc<Module<E>>,
refmod: &'a Arc<Module>,
name: Option<&str>,
decoded_name: Option<&Cow<str>>,
symmod: Option<&str>,
symlib: Option<&str>,
hash: u64,
flags: ResolveFlags,
) -> Option<(Arc<Module<E>>, usize)> {
) -> Option<(Arc<Module>, usize)> {
// Resolve from list_main.
let mut result = None;
@ -171,15 +166,15 @@ impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
/// See `symlook_list` on the PS4 for a reference.
pub fn resolve_from_list(
&self,
refmod: &'a Arc<Module<E>>,
refmod: &'a Arc<Module>,
name: Option<&str>,
decoded_name: Option<&Cow<str>>,
symmod: Option<&str>,
symlib: Option<&str>,
hash: u64,
flags: ResolveFlags,
list: &'a [Arc<Module<E>>],
) -> Option<(Arc<Module<E>>, usize)> {
list: &'a [Arc<Module>],
) -> Option<(Arc<Module>, usize)> {
// Get module name.
let symmod = if !flags.contains(ResolveFlags::UNK2) {
symmod
@ -242,15 +237,15 @@ impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
/// See `symlook_obj` on the PS4 for a reference.
pub fn resolve_from_module(
&self,
_: &'a Arc<Module<E>>,
_: &'a Arc<Module>,
name: Option<&str>,
decoded_name: Option<&str>,
symmod: Option<&str>,
symlib: Option<&str>,
hash: u64,
flags: ResolveFlags,
md: &Arc<Module<E>>,
) -> Option<(Arc<Module<E>>, usize)> {
md: &Arc<Module>,
) -> Option<(Arc<Module>, usize)> {
let info = md.file_info().unwrap();
let buckets = info.buckets();
let hash: usize = hash.try_into().unwrap();
@ -414,7 +409,7 @@ impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
symlib: Option<&str>,
sym: &Symbol,
flags: ResolveFlags,
md: &'a Arc<Module<E>>,
md: &'a Arc<Module>,
) -> bool {
// Check type.
let ty = sym.ty();
@ -499,7 +494,7 @@ impl<'a, E: ExecutionEngine> SymbolResolver<'a, E> {
}
/// See `convert_mangled_name_to_long` on the PS4 for a reference.
fn decode_legacy(md: &Module<E>, name: &str) -> Option<String> {
fn decode_legacy(md: &Module, name: &str) -> Option<String> {
// Split the name.
let mut p = name.splitn(3, '#');
let n = p.next()?;