diff --git a/CMakeLists.txt b/CMakeLists.txt index c7838a81ee8a992fc9578ac2cf9dc2836cf611b0..1fdbe0cd3b868c7d77093b5f36b1639979233a29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,11 +4,21 @@ cmake_minimum_required(VERSION 3.14) project(syscare) +# Arch +EXECUTE_PROCESS(COMMAND uname -m + OUTPUT_VARIABLE ARCH + OUTPUT_STRIP_TRAILING_WHITESPACE) + # Includes include(GNUInstallDirs) find_package(Git QUIET) -# Version +# Build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo) +endif() + +# Build version if(NOT DEFINED BUILD_VERSION) execute_process( COMMAND sh -c "cat syscare/Cargo.toml | grep -F 'version' | head -n 1 | awk -F '\"' '{print $2}'" @@ -32,7 +42,70 @@ else() set(BUILD_VERSION "${BUILD_VERSION}") endif() -# Set install directories +# Build configurations +if(ENABLE_ASAN) + set(BUILD_VERSION "${BUILD_VERSION}-asan") + list(APPEND BUILD_FLAGS_C -fsanitize=address -fno-omit-frame-pointer) + list(APPEND LINK_LIBRARIES_C asan) +endif() + +if(ENABLE_GCOV) + set(BUILD_VERSION "${BUILD_VERSION}-gcov") + list(APPEND BUILD_FLAGS_C -ftest-coverage -fprofile-arcs) + list(APPEND BUILD_FLAGS_RUST -C instrument-coverage) + list(APPEND LINK_LIBRARIES_C gcov) +endif() + +# Build flags +list(APPEND BUILD_FLAGS_C + -std=gnu99 -O2 -Wall -Wextra -Werror + -DBUILD_VERSION="${BUILD_VERSION}" -D_FORTIFY_SOURCE=2 + -Wtrampolines -Wformat=2 -Wstrict-prototypes -Wdate-time + -Wstack-usage=8192 -Wfloat-equal -Wswitch-default + -Wshadow -Wconversion -Wcast-qual -Wunused -Wundef + -funsigned-char -fstack-protector-all -fpic -fpie -ftrapv + -fstack-check -freg-struct-return -fno-canonical-system-headers + -fno-common -pipe -fdebug-prefix-map=old=new +) + +# The -Werror=cast-align compiler flag causes issues on riscv64 GCC, +# while the same operations do not error on aarch64. This appears to be +# a compiler-specific problem. Temporarily disable this option as a +# workaround since applying fixes would require intrusive code changes +# across multiple files. +if(NOT ARCH STREQUAL "riscv64") + list(APPEND BUILD_FLAGS_C + -Wcast-align + ) +endif() + +list(APPEND BUILD_FLAGS_RUST + --cfg unsound_local_offset + -D warnings + -C link-arg=-s + -C strip=symbols + -C overflow_checks=yes + -C relocation_model=pic + -C force-frame-pointers=yes + -W rust_2021_incompatible_closure_captures +) + +# Link flags +list(APPEND LINK_FLAGS_C + -pie + -Wl,-z,relro,-z,now + -Wl,-z,noexecstack -rdynamic + -Wl,-Bsymbolic + -Wl,-no-undefined +) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + list(APPEND BUILD_FLAGS_C -g) +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + list(APPEND LINK_FLAGS_C -s) +endif() + +# Install directories set(SYSCARE_BINARY_DIR "${CMAKE_INSTALL_FULL_BINDIR}") set(SYSCARE_LIBEXEC_DIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/syscare") set(SYSCARE_SERVICE_DIR "${CMAKE_INSTALL_PREFIX}/lib/systemd/system") @@ -46,41 +119,55 @@ message("╚════██║ ╚██╔╝ ╚════██║█ message("███████║ ██║ ███████║╚██████╗██║ ██║██║ ██║███████╗") message("╚══════╝ ╚═╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝") message("---------------------------------------------------------") -message("-- Verion: ${BUILD_VERSION}") +message("-- Build type: ${CMAKE_BUILD_TYPE}") +message("-- Build version: ${BUILD_VERSION}") +message("-- Rust flags: ${BUILD_FLAGS_RUST}") +message("-- Build flags: ${BUILD_FLAGS_C}") +message("-- Link flags: ${LINK_FLAGS_C}") +message("-- Link libraries: ${LINK_LIBRARIES_C}") message("-- Binary directory: ${SYSCARE_BINARY_DIR}") message("-- Libexec directory: ${SYSCARE_LIBEXEC_DIR}") message("-- Service directory: ${SYSCARE_SERVICE_DIR}") message("---------------------------------------------------------") -# Compile options -add_compile_options(-DBUILD_VERSION="${BUILD_VERSION}") -add_compile_options(-g -Wall -O2 -fPIE) - -# Subdirectories -add_subdirectory(upatch-diff) -add_subdirectory(upatch-manage) -add_subdirectory(upatch-hijacker) -add_subdirectory(misc) +# Apply all flags +add_compile_options(${BUILD_FLAGS_C}) +add_link_options(${LINK_FLAGS_C}) +link_libraries(${LINK_LIBRARIES_C}) # Build rust executables -add_custom_target(rust-executables ALL - COMMENT "Building rust executables..." +set(RUST_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/target") +set(RUST_OUTPUT_DIR "${RUST_TARGET_DIR}/release") +foreach(CURR_FLAG IN LISTS BUILD_FLAGS_RUST) + set(RUST_FLAGS "${RUST_FLAGS} ${CURR_FLAG}") +endforeach() + +add_custom_target(syscare ALL COMMAND ${CMAKE_COMMAND} -E env "BUILD_VERSION=${BUILD_VERSION}" - "RUSTFLAGS=--cfg unsound_local_offset" - cargo build --release --target-dir ${CMAKE_CURRENT_BINARY_DIR} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + "RUSTFLAGS=${RUST_FLAGS}" + cargo build --release --target-dir "${RUST_TARGET_DIR}" + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} +) + +set_target_properties(syscare PROPERTIES + ADDITIONAL_CLEAN_FILES "${RUST_TARGET_DIR}" +) + +add_custom_target(upatch-helpers ALL + COMMAND ln -sf upatch-helper upatch-cc + COMMAND ln -sf upatch-helper upatch-c++ + DEPENDS syscare + WORKING_DIRECTORY ${RUST_OUTPUT_DIR} ) # Install rust binaries install( PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/release/upatchd - ${CMAKE_CURRENT_BINARY_DIR}/release/syscared - ${CMAKE_CURRENT_BINARY_DIR}/release/syscare + ${RUST_OUTPUT_DIR}/syscare PERMISSIONS - OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION ${SYSCARE_BINARY_DIR} @@ -88,12 +175,30 @@ install( install( PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/release/upatch-build - ${CMAKE_CURRENT_BINARY_DIR}/release/syscare-build + ${RUST_OUTPUT_DIR}/syscared PERMISSIONS - OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + DESTINATION + ${SYSCARE_BINARY_DIR} +) + +install( + PROGRAMS + ${RUST_OUTPUT_DIR}/syscare-build + ${RUST_OUTPUT_DIR}/upatch-build + ${RUST_OUTPUT_DIR}/upatch-helper + ${RUST_OUTPUT_DIR}/upatch-cc + ${RUST_OUTPUT_DIR}/upatch-c++ + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION ${SYSCARE_LIBEXEC_DIR} ) + +# Build other components +add_subdirectory(upatch-diff) +add_subdirectory(upatch-manage) +add_subdirectory(misc) diff --git a/Cargo.lock b/Cargo.lock index e3074a61facbf8984b54cb0d6282ae566265d00d..38324d716d66074dbc0e79d7f76661cf07c3e1f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,16 +210,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctrlc" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d778600249295e82b6ab12e291ed9029407efee0cfb7baf67157edc65964df" -dependencies = [ - "nix", - "windows-sys", -] - [[package]] name = "daemonize" version = "0.5.0" @@ -1112,7 +1102,7 @@ dependencies = [ [[package]] name = "syscare" -version = "1.2.1" +version = "1.2.2" dependencies = [ "anyhow", "clap", @@ -1127,7 +1117,7 @@ dependencies = [ [[package]] name = "syscare-abi" -version = "1.2.1" +version = "1.2.2" dependencies = [ "serde", "uuid", @@ -1135,12 +1125,11 @@ dependencies = [ [[package]] name = "syscare-build" -version = "1.2.1" +version = "1.2.2" dependencies = [ "anyhow", "chrono", "clap", - "ctrlc", "flexi_logger", "lazy_static", "log", @@ -1154,13 +1143,14 @@ dependencies = [ [[package]] name = "syscare-common" -version = "1.2.1" +version = "1.2.2" dependencies = [ "anyhow", - "lazy_static", "log", "memmap2", "nix", + "num_cpus", + "object", "regex", "serde", "serde_cbor", @@ -1169,7 +1159,7 @@ dependencies = [ [[package]] name = "syscared" -version = "1.2.1" +version = "1.2.2" dependencies = [ "anyhow", "clap", @@ -1313,21 +1303,21 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "upatch-build" -version = "1.2.1" +version = "1.2.2" dependencies = [ "anyhow", "clap", - "ctrlc", "flexi_logger", "function_name", "gimli", "indexmap", - "jsonrpc", "lazy_static", "log", "memmap2", "memoffset", "object", + "once_cell", + "regex", "serde", "syscare-common", "typed-arena", @@ -1335,24 +1325,11 @@ dependencies = [ ] [[package]] -name = "upatchd" -version = "1.2.1" +name = "upatch-helper" +version = "1.2.2" dependencies = [ "anyhow", - "clap", - "daemonize", - "flexi_logger", - "indexmap", - "jsonrpc-core", - "jsonrpc-derive", - "jsonrpc-ipc-server", - "log", - "nix", - "object", - "serde", - "serde_yaml", - "signal-hook", - "syscare-common", + "uuid", ] [[package]] @@ -1488,15 +1465,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-targets" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index ecc67d2230dc1372df7ae28f146e9eb8d574a803..b22335f74597ac37958b1f69c906b670685f2ec3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,12 +6,10 @@ members = [ "syscare-build", "syscared", "upatch-build", - "upatchd" + "upatch-helper" ] [profile.release] -opt-level = 's' +opt-level = 3 debug = true rpath = false -debug-assertions = false -overflow-checks = false diff --git a/README.en.md b/README.en.md index d004fa8fa76c08c5f720e648cf7370cb28d7b60c..66734a5f7b5284ddba87216a5e28d9f7a657a666 100644 --- a/README.en.md +++ b/README.en.md @@ -72,7 +72,7 @@ $ syscare build \ --patch ./0001-test.patch ``` -For detailed instructions on patch production, please see builder/README.md +For detailed instructions on patch production, please see syscare-build/README.md ### Patch management diff --git a/README.md b/README.md index 138c05926eea6861095de0997ed3c4377c787892..b740ace3bd5f945c14ad4aa22323aac35fa456f4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## 软件架构 -​ 可以利用系统组件源代码与相应的patch问题,制作出相应组件补丁的RPM(包含补丁文件、依赖信息与配置信息等). 制作的补丁RPM,可以上传到相应的补丁仓库中,集群的系统demon定时去查询补丁仓库, 对系统中运行的CVE与软件错误进行热修复,保证系统安全、稳定、高效运行。 +​ 可以利用系统组件源代码与相应的patch问题,制作出相应组件补丁的RPM(包含补丁文件、依赖信息与配置信息等). 制作的补丁RPM,可以上传到相应的补丁仓库中,集群的系统daemon定时去查询补丁仓库, 对系统中运行的CVE与软件错误进行热修复,保证系统安全、稳定、高效运行。 @@ -33,6 +33,8 @@ * 编译并安装 + PS: 直接编译在应用补丁的时候会显示缺少依赖,建议通过rpm包安装 + ```bash git clone https://gitee.com/openeuler/syscare.git cd syscare @@ -41,6 +43,11 @@ cmake -DCMAKE_INSTALL_PREFIX=/usr -DKERNEL_VERSION=$(uname -r) .. make make install + + mkdir -p /usr/lib/syscare/patches + systemctl daemon-reload + systemctl enable syscare + systemctl start syscare ``` * 离线编译 @@ -63,7 +70,11 @@ ```bash rpm -ivh syscare-*.rpm ``` +或者 +``` +dnf install syscare-*.rpm +``` ## 使用说明 @@ -93,7 +104,7 @@ $ syscare build \ --patch ./0001-test.patch ``` -补丁制作详细使用说明请见[builder/README.md](https://gitee.com/openeuler/syscare/blob/master/builder/README.md) +补丁制作详细使用说明请见[syscare-build/README.md](https://gitee.com/openeuler/syscare/blob/master/syscare-build/README.md) @@ -113,7 +124,7 @@ $ sudo syscare active redis-6.2.5-1/HP001 3. 补丁去激活 ```bash -$ sudo syscarae deactive redis-6.2.5-1/HP001 +$ sudo syscare deactive redis-6.2.5-1/HP001 ``` 4. 补丁卸载/移除 diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt index 7450e2e4f90c13817ab7ffba3f7fe5123c83428b..eb21cb726c60af5bc7813460296b05add18d1d2a 100644 --- a/misc/CMakeLists.txt +++ b/misc/CMakeLists.txt @@ -1,7 +1,6 @@ # Install service install( FILES - ${CMAKE_CURRENT_LIST_DIR}/upatch.service ${CMAKE_CURRENT_LIST_DIR}/syscare.service PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ diff --git a/misc/upatch.service b/misc/upatch.service deleted file mode 100644 index 6b2e0b88516c9a02d90942417ed3aac596c95011..0000000000000000000000000000000000000000 --- a/misc/upatch.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Syscare user patch build daemon -Requires=local-fs.target -After=network-online.target - -[Service] -Type=forking -ExecStart=/usr/bin/upatchd -d -ExecStop=/usr/bin/kill -s SIGTERM $MAINPID -User=root -Group=root - -[Install] -WantedBy=multi-user.target diff --git a/syscare-abi/Cargo.toml b/syscare-abi/Cargo.toml index f086850762335c54353bb1d380cfc5c25d6b1ed2..d90ab02c441739e89a833f26ad77e188f9eeb509 100644 --- a/syscare-abi/Cargo.toml +++ b/syscare-abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syscare-abi" -version = "1.2.1" +version = "1.2.2" authors = ["renoseven "] description = "Syscare binary interface" license = "MulanPSL-2.0" @@ -10,4 +10,4 @@ build = "build.rs" [dependencies] serde = { version = "1.0", features = ["derive"] } -uuid = { version = "0.8", features = ["v4"] } +uuid = { version = "0.8", features = ["v4", "serde"] } diff --git a/syscare-abi/build.rs b/syscare-abi/build.rs index 8a86f6385d02aea9150f1a1b35724af9e2c1eb52..ed83c439cfc8bf1a19c3b59ad6a9cea22b350d2d 100644 --- a/syscare-abi/build.rs +++ b/syscare-abi/build.rs @@ -12,26 +12,25 @@ * See the Mulan PSL v2 for more details. */ -use std::{env, process::Command}; +use std::{env, ffi::OsStr, os::unix::ffi::OsStrExt, process::Command}; fn rewrite_version() { - const ENV_VERSION_NAME: &str = "BUILD_VERSION"; - const PKG_VERSION_NAME: &str = "CARGO_PKG_VERSION"; + const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + const ENV_VERSION: Option<&str> = option_env!("BUILD_VERSION"); - let version = env::var(ENV_VERSION_NAME).unwrap_or_else(|_| { - let pkg_version = env::var(PKG_VERSION_NAME).expect("Failed to fetch package version"); - let git_output = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|output| String::from_utf8(output.stdout).expect("Failed to fetch git version")); - - match git_output { - Ok(git_version) => format!("{}-g{}", pkg_version, git_version), - Err(_) => pkg_version, - } - }); - - println!("cargo:rustc-env={}={}", PKG_VERSION_NAME, version); + println!( + "cargo:rustc-env=CARGO_PKG_VERSION={}", + ENV_VERSION.map(String::from).unwrap_or_else(|| { + Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|output| { + let git_version = OsStr::from_bytes(&output.stdout).to_string_lossy(); + format!("{}-g{}", PKG_VERSION, git_version) + }) + .unwrap_or_else(|_| PKG_VERSION.to_string()) + }) + ); } fn main() { diff --git a/syscare-abi/src/lib.rs b/syscare-abi/src/lib.rs index 6600099697af04b80dfb0782ce8d485c55fe5c2d..881587c9593a8dd306f54670b4be8fb5ce838a13 100644 --- a/syscare-abi/src/lib.rs +++ b/syscare-abi/src/lib.rs @@ -12,6 +12,8 @@ * See the Mulan PSL v2 for more details. */ +pub use uuid::Uuid; + mod package_info; mod patch_info; mod patch_status; diff --git a/syscare-abi/src/patch_info.rs b/syscare-abi/src/patch_info.rs index 08cc2c7ecde08f9bb94e82b71e5d0d5a347c6412..97a2f07866664b8883882558fdaa4b4d37b88f23 100644 --- a/syscare-abi/src/patch_info.rs +++ b/syscare-abi/src/patch_info.rs @@ -21,6 +21,7 @@ use uuid::Uuid; use super::package_info::PackageInfo; pub const PATCH_INFO_MAGIC: &str = "112574B6EDEE4BA4A05F"; +const MAX_CHECKSUM_LEN: usize = 32; #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum PatchType { @@ -42,6 +43,15 @@ pub struct PatchEntity { pub checksum: String, } +impl std::fmt::Display for PatchEntity { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "uuid: {}", self.uuid)?; + writeln!(f, "target: {}", self.patch_target.display())?; + writeln!(f, "patch: {}", self.patch_name.to_string_lossy())?; + write!(f, "checksum: {}", &self.checksum[0..MAX_CHECKSUM_LEN]) + } +} + #[derive(Debug, Serialize, Deserialize, Clone, Eq, Ord, PartialEq, PartialOrd)] pub struct PatchFile { pub name: OsString, @@ -49,6 +59,13 @@ pub struct PatchFile { pub digest: String, } +impl std::fmt::Display for PatchFile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "name: {}", self.name.to_string_lossy())?; + write!(f, "checksum: {}", &self.digest[0..MAX_CHECKSUM_LEN]) + } +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct PatchInfo { pub uuid: Uuid, @@ -71,8 +88,6 @@ impl PatchInfo { impl std::fmt::Display for PatchInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - const LIST_DISPLAY_LIMIT: usize = 9; - writeln!(f, "name: {}", self.name)?; writeln!(f, "version: {}", self.version)?; writeln!(f, "release: {}", self.release)?; @@ -81,37 +96,17 @@ impl std::fmt::Display for PatchInfo { writeln!(f, "target: {}", self.target.short_name())?; writeln!(f, "license: {}", self.target.license)?; writeln!(f, "description: {}", self.description)?; - if !self.entities.is_empty() { - writeln!(f, "entities:")?; - for (entity_idx, entity) in self.entities.iter().enumerate() { - if entity_idx >= LIST_DISPLAY_LIMIT { - writeln!(f, "* ......")?; - break; - } - writeln!(f, "* {}", entity.patch_name.to_string_lossy())?; - } + writeln!(f, "entities:")?; + for entity in &self.entities { + writeln!(f, "* {}", entity.patch_name.to_string_lossy())?; } - - if !self.patches.is_empty() { - writeln!(f, "patches:")?; - let last_idx = self.patches.len() - 1; - for (patch_idx, patch_file) in self.patches.iter().enumerate() { - match patch_idx == last_idx { - false => { - if patch_idx >= LIST_DISPLAY_LIMIT { - writeln!(f, "* ......")?; - break; - } - writeln!(f, "* {}", patch_file.name.to_string_lossy())? - } - true => { - if patch_idx >= LIST_DISPLAY_LIMIT { - write!(f, "* ......")?; - break; - } - write!(f, "* {}", patch_file.name.to_string_lossy())? - } - } + writeln!(f, "patches:")?; + let mut patches_iter = self.patches.iter().peekable(); + while let Some(patch) = patches_iter.next() { + if patches_iter.peek().is_none() { + write!(f, "* {}", patch.name.to_string_lossy())? + } else { + writeln!(f, "* {}", patch.name.to_string_lossy())? } } diff --git a/syscare-build/Cargo.toml b/syscare-build/Cargo.toml index 9b531c0b34b4d03242742632311a3db948527706..ba4d1c8b230c63e8aa0b777c66732ca829f0a526 100644 --- a/syscare-build/Cargo.toml +++ b/syscare-build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syscare-build" -version = "1.2.1" +version = "1.2.2" authors = ["renoseven "] description = "Syscare patch build utility" license = "MulanPSL-2.0" @@ -14,7 +14,6 @@ syscare-common = { package = "syscare-common", path = "../syscare-common" } anyhow = { version = "1.0" } clap = { version = "3.2", features = ["derive"] } chrono = { version = "0.4" } -ctrlc = { version = "3.3" } flexi_logger = { version = "0.24" } lazy_static = { version = "1.0" } log = { version = "0.4" } diff --git a/syscare-build/README.md b/syscare-build/README.md index e0cf66e432fc39c155efc0b7bbd6f5bf5a2f0712..1f05c7439f22ef02e86eb4886d63cc6af4db4d9b 100644 --- a/syscare-build/README.md +++ b/syscare-build/README.md @@ -9,34 +9,29 @@ syscare build为纯CLI工具,提供从RPM包生成热补丁包的功能,补 ## 命令行参数 ```bash -Usage: syscare build [OPTIONS] --patch-name --source --debuginfo ... - -Arguments: - ... Patch file(s) - -Options: - -n, --patch-name Patch name - --patch-arch Patch architecture [default: x86_64] - --patch-version Patch version [default: 1] - --patch-release Patch release [default: 1] - --patch-description Patch description [default: (none)] - -s, --source Source package - -d, --debuginfo Debuginfo package - --workdir Working directory [default: .] - -o, --output Generated patch output directory [default: .] - --jobs Parallel build jobs [default: 96] - --skip-compiler-check Skip compiler version check (not recommended) - --skip-cleanup Skip post-build cleanup - -v, --verbose Provide more detailed info - -h, --help Print help information - -V, --version Print version information +USAGE: + syscare build [OPTIONS] --patch-name --source ... --debuginfo ... --patch ... + +OPTIONS: + -n, --patch-name Patch name + --patch-arch Patch architecture [default: x86_64] + --patch-version Patch version [default: 1] + --patch-release Patch release [default: 1] + --patch-description Patch description [default: (none)] + --patch-requires ... Patch requirements + -s, --source ... Source package(s) + -d, --debuginfo ... Debuginfo package(s) + -p, --patch ... Patch file(s) + --build-root Build directory [default: .] + -o, --output Output directory [default: .] + -j, --jobs Parallel build jobs [default: 20] + --skip-compiler-check Skip compiler version check (not recommended) + --skip-cleanup Skip post-build cleanup + -v, --verbose Provide more detailed info + -h, --help Print help information + -V, --version Print version information ``` -### 参数 -|名称|描述|类型|备注| -| ---- | ---- | ---- | ---- | -| ``````... |补丁文件路径|字符串|必选参数,可指定多个,需为合法路径| - ### 选项 |名称|描述|类型|备注| | ---- | ---- | ---- | ---- | @@ -45,9 +40,11 @@ Options: |--patch-version ``````|补丁版本号|字符串|默认值为1,需符合RPM命名规范| |--patch-release ``````|补丁release|数字|默认值为1,需符合RPM命名规范| |--patch-description ``````|补丁描述|字符串|默认为(none)| +|--patch-requires ``````|补丁依赖|字符串|默认为(none)| |-s, --source ``````|目标软件src.rpm源码包路径|字符串|必选参数,需为合法路径| |-d, --debuginfo ``````|目标软件debuginfo包路径|字符串|必选参数,需为合法路径| -|--workdir ``````|临时文件夹路径|字符串|默认为当前执行目录,需为合法路径| +|-p, --patch ``````|目标软件debuginfo包路径|字符串|必选参数,需为合法路径| +|--build-root ``````|编译临时目录|字符串|默认为当前执行目录| |-o, --output ``````|补丁输出文件夹|字符串|默认为当前执行目录,需为合法路径| |-j, --jobs ``````|并行编译线程数|数字|默认为cpu线程数| |--skip-compiler-check|跳过编译器检查|标识|-| @@ -103,17 +100,19 @@ syscare build \ | arch | 补丁架构 | | type | 补丁类型 | | target | 目标软件名 | -| target_elf | 目标软件可执行文件名称 | -| digest | 补丁指纹 | | license | 目标软件许可证 | | description | 补丁描述 | -| patch| 补丁文件列表 | +| entities | 补丁实体列表 | +| patch | 补丁文件列表 | 示例: ```bash -dev@openeuler-dev:[output]$ syscare info redis-6.2.5-1/HP001 +dev@dev-x86:[output]$ syscare info redis-6.2.5-1/HP001-1-1 +--------------------------------------------------- +Patch: redis-6.2.5-1/HP001-1-1 +--------------------------------------------------- uuid: ec503257-aa75-4abc-9045-c4afdd7ae0f2 name: HP001 version: 1 @@ -121,12 +120,15 @@ release: 1 arch: x86_64 type: UserPatch target: redis-6.2.5-1 -target_elf: redis-cli, redis-server, redis-benchmark -digest: 31fc7544 license: BSD and MIT description: CVE-2021-32675 - When parsing an incoming Redis Standard Protocol (RESP) request, Redis allocates memory according to user-specified values which determine the number of elements (in the multi-bulk header) and size of each element (in the bulk header). An attacker delivering specially crafted requests over multiple connections can cause the server to allocate significant amount of memory. Because the same parsing mechanism is used to handle authentication requests, this vulnerability can also be exploited by unauthenticated users. +entities: +* redis-server +* redis-cli +* redis-benchmark patch: -31fc7544 0001-Prevent-unauthenticated-client-from-easily-consuming.patch +* 0001-Prevent-unauthenticated-client-from-easily-consuming.patch +--------------------------------------------------- ``` @@ -156,7 +158,7 @@ patch: 示例: ```bash - syscare-build \ + syscare build \ --patch-name HP001 \ --source kernel-5.10.0-60.66.0.91.oe2203.src.rpm \ --debuginfo kernel-debuginfo-5.10.0-60.66.0.91.oe2203.x86_64.rpm \ @@ -164,12 +166,10 @@ patch: 001-kernel-patch-test.patch ``` - 补丁制作过程将会在由`--workdir`参数所指定的目录中(默认为当前目录)创建以```syscare-build```开头的临时文件夹,用于存放临时文件及编译日志。 - 示例: ```bash - dev@openeuler-dev:[kernel_patch]$ ls -l syscare-build.111602/ + dev@dev-x86:[kernel_patch]$ ls -l syscare-build.111602/ total 100 -rw-r--r--. 1 dev dev 92303 Nov 12 00:00 build.log drwxr-xr-x. 6 dev dev 4096 Nov 12 00:00 package @@ -177,7 +177,7 @@ patch: ``` 编译日志将会生成在临时文件夹中,名称为```build.log``` ```bash - dev@openeuler-dev:[kernel_patch]$ cat syscare-build.111602/build.log | less + dev@dev-x86:[kernel_patch]$ cat syscare-build.111602/build.log | less ... ``` 若补丁制作成功,将不会保留该临时文件夹。 @@ -187,7 +187,7 @@ patch: 示例: ```bash - dev@openeuler-dev:[output]$ ll + dev@dev-x86:[output]$ ll total 372M -rw-r--r--. 1 dev dev 186M Nov 12 00:00 kernel-5.10.0-60.80.0.104.oe2203-HP001-1-1.x86_64.src.rpm -rw-r--r--. 1 dev dev 11K Nov 12 00:00 patch-kernel-5.10.0-60.80.0.104.oe2203-HP001-1-1.x86_64.rpm diff --git a/syscare-build/build.rs b/syscare-build/build.rs index b3de09318f3f125076dd04feb64f314502971324..1ca960939ec715fd585ad55c4dc1a59002288c97 100644 --- a/syscare-build/build.rs +++ b/syscare-build/build.rs @@ -12,26 +12,25 @@ * See the Mulan PSL v2 for more details. */ -use std::{env, process::Command}; +use std::{env, ffi::OsStr, os::unix::ffi::OsStrExt, process::Command}; fn rewrite_version() { - const ENV_VERSION_NAME: &str = "BUILD_VERSION"; - const PKG_VERSION_NAME: &str = "CARGO_PKG_VERSION"; + const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + const ENV_VERSION: Option<&str> = option_env!("BUILD_VERSION"); - let version = env::var(ENV_VERSION_NAME).unwrap_or_else(|_| { - let pkg_version = env::var(PKG_VERSION_NAME).expect("Failed to fetch package version"); - let git_output = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|output| String::from_utf8(output.stdout).expect("Failed to fetch git version")); - - match git_output { - Ok(git_version) => format!("{}-g{}", pkg_version, git_version), - Err(_) => pkg_version, - } - }); - - println!("cargo:rustc-env={}={}", PKG_VERSION_NAME, version); + println!( + "cargo:rustc-env=CARGO_PKG_VERSION={}", + ENV_VERSION.map(String::from).unwrap_or_else(|| { + Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|output| { + let git_version = OsStr::from_bytes(&output.stdout).to_string_lossy(); + format!("{}-g{}", PKG_VERSION, git_version) + }) + .unwrap_or_else(|_| PKG_VERSION.to_string()) + }) + ); } fn main() { diff --git a/syscare-build/src/args.rs b/syscare-build/src/args.rs index 3b5fef9e314773c729b84d512032c4c4642898ef..cba93f5607a8eaadd41e44fda410bb5ef1951fa6 100644 --- a/syscare-build/src/args.rs +++ b/syscare-build/src/args.rs @@ -12,7 +12,7 @@ * See the Mulan PSL v2 for more details. */ -use std::path::PathBuf; +use std::{ffi::OsString, path::PathBuf}; use anyhow::{ensure, Result}; use clap::{AppSettings, ColorChoice, Parser}; @@ -25,7 +25,7 @@ use super::{CLI_ABOUT, CLI_NAME, CLI_VERSION}; const DEFAULT_PATCH_VERSION: &str = "1"; const DEFAULT_PATCH_RELEASE: &str = "1"; const DEFAULT_PATCH_DESCRIPTION: &str = "(none)"; -const DEFAULT_WORK_DIR: &str = "/var/run/syscare"; +const DEFAULT_KERNEL_CONFIG: &str = "openeuler_defconfig"; const DEFAULT_BUILD_ROOT: &str = "."; const DEFAULT_OUTPUT_DIR: &str = "."; @@ -77,15 +77,15 @@ pub struct Arguments { #[clap(short, long, multiple = true, required = true)] pub debuginfo: Vec, + /// Kernel config name or path (kernel patch only) + #[clap(short, long, default_value = DEFAULT_KERNEL_CONFIG, hide_default_value = true)] + pub kconfig: OsString, + /// Patch file(s) #[clap(short, long, multiple = true, required = true)] pub patch: Vec, - /// Working directory - #[clap(long, default_value = DEFAULT_WORK_DIR)] - pub work_dir: PathBuf, - - /// Build temporary directory + /// Build directory #[clap(long, default_value = DEFAULT_BUILD_ROOT)] pub build_root: PathBuf, @@ -97,6 +97,10 @@ pub struct Arguments { #[clap(short, long, default_value = &DEFAULT_BUILD_JOBS)] pub jobs: usize, + /// Override line macros to a fixed value (userspace patch only) + #[clap(long)] + pub override_line_macros: bool, + /// Skip compiler version check (not recommended) #[clap(long)] pub skip_compiler_check: bool, @@ -112,16 +116,21 @@ pub struct Arguments { impl Arguments { pub fn new() -> Result { - let mut args = Self::parse().normalize_path().and_then(Self::check)?; + let mut args = Self::parse(); + args.setup_build_root().normalize()?.check()?; + + Ok(args) + } - args.build_root = args + fn setup_build_root(&mut self) -> &mut Self { + self.build_root = self .build_root .join(format!("syscare-build.{}", os::process::id())); - Ok(args) + self } - fn normalize_path(mut self) -> Result { + fn normalize(&mut self) -> Result<&mut Self> { for source in &mut self.source { *source = fs::normalize(&source)?; } @@ -131,14 +140,13 @@ impl Arguments { for patch in &mut self.patch { *patch = fs::normalize(&patch)?; } - self.work_dir = fs::normalize(&self.work_dir)?; self.build_root = fs::normalize(&self.build_root)?; self.output = fs::normalize(&self.output)?; Ok(self) } - fn check(self) -> Result { + fn check(&self) -> Result<()> { for source in &self.source { ensure!( source.is_file(), @@ -172,14 +180,9 @@ impl Arguments { ) } - ensure!( - self.work_dir.is_dir(), - format!("Cannot find directory {}", self.work_dir.display()) - ); - ensure!(self.jobs != 0, "Parallel build job number cannot be zero"); - Ok(self) + Ok(()) } } diff --git a/syscare-build/src/build_params.rs b/syscare-build/src/build_params.rs index 0fa3ae5dd11b5249289b277df56d4b388bff305c..40d17dc66d97c55e8309b977da01f9e2e368e26f 100644 --- a/syscare-build/src/build_params.rs +++ b/syscare-build/src/build_params.rs @@ -12,7 +12,7 @@ * See the Mulan PSL v2 for more details. */ -use std::path::PathBuf; +use std::{ffi::OsString, path::PathBuf}; use syscare_abi::{PackageInfo, PatchFile, PatchType}; @@ -26,11 +26,11 @@ pub struct BuildEntry { } pub struct BuildParameters { - pub work_dir: PathBuf, pub build_root: BuildRoot, pub pkg_build_root: PackageBuildRoot, pub build_entry: BuildEntry, pub kernel_build_entry: Option, + pub kernel_config: OsString, pub patch_name: String, pub patch_type: PatchType, pub patch_version: String, @@ -39,6 +39,7 @@ pub struct BuildParameters { pub patch_description: String, pub patch_files: Vec, pub jobs: usize, + pub override_line_macros: bool, pub skip_compiler_check: bool, pub skip_cleanup: bool, pub verbose: bool, @@ -62,6 +63,11 @@ impl std::fmt::Display for BuildParameters { if let Some(k) = &self.kernel_build_entry { writeln!(f, "kernel_source: {}", k.build_source.display())?; writeln!(f, "kernel_spec: {}", k.build_spec.display())?; + writeln!( + f, + "kernel_config: {}", + self.kernel_config.to_string_lossy() + )?; } writeln!(f, "jobs: {}", self.jobs)?; writeln!(f, "skip_compiler_check: {}", self.skip_compiler_check)?; diff --git a/syscare-build/src/build_root/mod.rs b/syscare-build/src/build_root/mod.rs index 81de6b2295e73d2196c6e53b8a48d115e601ba69..395feadb825a1b05d8f61d92bf71c812b1131e27 100644 --- a/syscare-build/src/build_root/mod.rs +++ b/syscare-build/src/build_root/mod.rs @@ -12,11 +12,7 @@ * See the Mulan PSL v2 for more details. */ -use std::{ - ffi::OsStr, - ops::Deref, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use anyhow::Result; use syscare_common::fs; @@ -40,8 +36,8 @@ pub struct BuildRoot { } impl BuildRoot { - pub fn new>(path: P) -> Result { - let path = path.as_ref().to_path_buf(); + pub fn new>(directory: P) -> Result { + let path = directory.as_ref().to_path_buf(); let package = PackageRoot::new(path.join(PACKAGE_ROOT_NAME))?; let patch = PatchRoot::new(path.join(PATCH_ROOT_NAME))?; let log_file = path.join(BUILD_LOG_NAME); @@ -61,17 +57,3 @@ impl BuildRoot { Ok(()) } } - -impl Deref for BuildRoot { - type Target = Path; - - fn deref(&self) -> &Self::Target { - &self.path - } -} - -impl AsRef for BuildRoot { - fn as_ref(&self) -> &OsStr { - self.as_os_str() - } -} diff --git a/syscare-build/src/build_root/package_root.rs b/syscare-build/src/build_root/package_root.rs index abb7a86537bbfe303ee2f4e2cdf15750e2e8adc8..eaac34954b38fe1db842386a09f448635a67817e 100644 --- a/syscare-build/src/build_root/package_root.rs +++ b/syscare-build/src/build_root/package_root.rs @@ -25,34 +25,26 @@ const BUILD_ROOT_DIR_NAME: &str = "patch"; #[derive(Debug, Clone)] pub struct PackageRoot { - pub path: PathBuf, pub source: PathBuf, pub debuginfo: PathBuf, pub build_root: PackageBuildRoot, } impl PackageRoot { - pub fn new>(path: P) -> Result { - let path = path.as_ref().to_path_buf(); + pub fn new>(directory: P) -> Result { + let path = directory.as_ref(); let source = path.join(SOURCE_DIR_NAME); let debuginfo = path.join(DEBUGINFO_DIR_NAME); let build_root = PackageBuildRoot::new(path.join(BUILD_ROOT_DIR_NAME))?; - fs::create_dir_all(&path)?; + fs::create_dir_all(path)?; fs::create_dir_all(&source)?; fs::create_dir_all(&debuginfo)?; Ok(Self { - path, source, debuginfo, build_root, }) } } - -impl AsRef for PackageRoot { - fn as_ref(&self) -> &Path { - &self.path - } -} diff --git a/syscare-build/src/build_root/patch_root.rs b/syscare-build/src/build_root/patch_root.rs index b780e3249f6e9d92fcc9b7322423c37a6b7ab190..0aa0a1c0a0f794081e7cb1bc8073f1f9bae99fed 100644 --- a/syscare-build/src/build_root/patch_root.rs +++ b/syscare-build/src/build_root/patch_root.rs @@ -22,31 +22,20 @@ const OUTPUT_DIR_NAME: &str = "output"; #[derive(Debug, Clone)] pub struct PatchRoot { - pub path: PathBuf, pub build: PathBuf, pub output: PathBuf, } impl PatchRoot { - pub fn new>(base_dir: P) -> Result { - let path = base_dir.as_ref().to_path_buf(); + pub fn new>(directory: P) -> Result { + let path = directory.as_ref(); let build = path.join(BUILD_DIR_NAME); let output = path.join(OUTPUT_DIR_NAME); - fs::create_dir_all(&path)?; + fs::create_dir_all(path)?; fs::create_dir_all(&build)?; fs::create_dir_all(&output)?; - Ok(Self { - path, - build, - output, - }) - } -} - -impl AsRef for PatchRoot { - fn as_ref(&self) -> &Path { - &self.path + Ok(Self { build, output }) } } diff --git a/syscare-build/src/main.rs b/syscare-build/src/main.rs index 8928218297cafb8bec4d49b24c073a3171a7e12b..df38a17951e6fe54cc1f0937b85029943906aeb5 100644 --- a/syscare-build/src/main.rs +++ b/syscare-build/src/main.rs @@ -12,7 +12,7 @@ * See the Mulan PSL v2 for more details. */ -use std::{process, sync::Arc}; +use std::{env, process, sync::Arc}; use anyhow::{bail, ensure, Context, Result}; use flexi_logger::{ @@ -22,7 +22,7 @@ use lazy_static::lazy_static; use log::{error, info, LevelFilter, Record}; use syscare_abi::{PackageInfo, PackageType, PatchInfo, PatchType}; -use syscare_common::{fs, os}; +use syscare_common::{concat_os, fs, os}; mod args; mod build_params; @@ -37,7 +37,7 @@ use package::{ PackageBuildRoot, PackageBuilderFactory, PackageFormat, PackageImpl, PackageSpecBuilderFactory, PackageSpecWriterFactory, }; -use patch::{PatchBuilderFactory, PatchHelper, PatchMetadata, PATCH_FILE_EXT}; +use patch::{PatchBuilderFactory, PatchHelper, PatchMetadata}; const CLI_NAME: &str = "syscare build"; const CLI_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -47,6 +47,9 @@ const CLI_UMASK: u32 = 0o022; const LOG_FILE_NAME: &str = "build"; const KERNEL_PKG_NAME: &str = "kernel"; +const PATH_ENV: &str = "PATH"; +const BINARY_INSTALL_PATH: &str = "/usr/libexec/syscare"; + lazy_static! { static ref PKG_IMPL: Arc = Arc::new(PackageImpl::new(PackageFormat::RpmPackage)); } @@ -68,9 +71,13 @@ impl SyscareBuild { } fn new() -> Result { - // Initialize arguments & prepare environments + // Setup environment varialbe & umask + let path_env = env::var_os(PATH_ENV) + .with_context(|| format!("Cannot read environment variable {}", PATH_ENV))?; + env::set_var(PATH_ENV, concat_os!(BINARY_INSTALL_PATH, ":", path_env)); os::umask::set_umask(CLI_UMASK); + // Parse arguments let args = Arguments::new()?; let build_root = BuildRoot::new(&args.build_root)?; fs::create_dir_all(&args.output)?; @@ -96,12 +103,6 @@ impl SyscareBuild { .start() .context("Failed to initialize logger")?; - // Initialize signal handler - ctrlc::set_handler(|| { - eprintln!("Interrupt"); - }) - .context("Failed to initialize signal handler")?; - Ok(Self { args, logger, @@ -117,12 +118,12 @@ impl SyscareBuild { self.args.patch_arch.as_str() == os::cpu::arch(), "Cross compilation is unsupported" ); - Ok(()) } fn collect_package_info(&self) -> Result> { - let mut pkg_list = Vec::new(); + let mut source_pkg_list = Vec::new(); + let mut debug_pkg_list = Vec::new(); for pkg_path in self.args.source.clone() { let mut pkg_info = PKG_IMPL.parse_package_info(&pkg_path)?; @@ -139,7 +140,7 @@ impl SyscareBuild { bail!("File {} is not a source package", pkg_info.short_name()); } - pkg_list.push(pkg_info); + source_pkg_list.push(pkg_info); } for pkg_path in self.args.debuginfo.clone() { @@ -160,10 +161,22 @@ impl SyscareBuild { self.args.patch_arch ); } + debug_pkg_list.push(pkg_info); } info!("------------------------------"); - Ok(pkg_list) + for debug_pkg in &debug_pkg_list { + ensure!( + source_pkg_list.iter().any(|source_pkg| { + debug_pkg.version == source_pkg.version + && debug_pkg.release == source_pkg.release + }), + "Package {} has no matching source package", + debug_pkg.full_name(), + ); + } + + Ok(source_pkg_list) } fn prepare_source_code( @@ -211,10 +224,10 @@ impl SyscareBuild { .find(|entry| entry.target_pkg.name == KERNEL_PKG_NAME); match (pkg_entry, kernel_entry) { - (Some(entry), Some(kernel_entry)) => Ok(( + (Some(p_entry), Some(k_entry)) => Ok(( PatchType::KernelPatch, - entry.clone(), - Some(kernel_entry.clone()), + p_entry.clone(), + Some(k_entry.clone()), )), (None, Some(entry)) => Ok((PatchType::KernelPatch, entry.clone(), None)), (Some(entry), None) => Ok((PatchType::UserPatch, entry.clone(), None)), @@ -279,12 +292,13 @@ impl SyscareBuild { } // Override patch list - let mut new_patch_files = PatchHelper::collect_patch_files(fs::list_files_by_ext( - &patch_metadata.metadata_dir, - PATCH_FILE_EXT, - fs::TraverseOptions { recursive: false }, - )?) - .context("Failed to collect patch file from metadata directory")?; + let mut new_patch_files = PatchHelper::collect_patch_files( + saved_patch_info + .patches + .iter() + .map(|patch_file| &patch_file.path), + ) + .context("Failed to collect patch file from patch metadata")?; new_patch_files.extend(patch_files); patch_files = new_patch_files; @@ -292,11 +306,11 @@ impl SyscareBuild { info!("- Generating build parameters"); let build_params = BuildParameters { - work_dir: self.args.work_dir.to_owned(), build_root: self.build_root.to_owned(), pkg_build_root, build_entry, kernel_build_entry, + kernel_config: self.args.kconfig.to_owned(), patch_name: self.args.patch_name.to_owned(), patch_version: self.args.patch_version.to_owned(), patch_release: self.args.patch_release, @@ -305,6 +319,7 @@ impl SyscareBuild { patch_type, patch_files, jobs: self.args.jobs, + override_line_macros: self.args.override_line_macros, skip_compiler_check: self.args.skip_compiler_check, skip_cleanup: self.args.skip_cleanup, verbose: self.args.verbose, diff --git a/syscare-build/src/package/build_root.rs b/syscare-build/src/package/build_root.rs index 6124156a31f0aee787d1be4bef03342d792f3c58..d8ee182e56399874cac5c0a5b5ac5e102812498e 100644 --- a/syscare-build/src/package/build_root.rs +++ b/syscare-build/src/package/build_root.rs @@ -36,8 +36,8 @@ pub struct PackageBuildRoot { } impl PackageBuildRoot { - pub fn new>(path: P) -> Result { - let path = path.as_ref().to_path_buf(); + pub fn new>(directory: P) -> Result { + let path = directory.as_ref().to_path_buf(); let build = path.join(BUILD_DIR_NAME); let buildroot = path.join(BUILDROOT_DIR_NAME); let rpms = path.join(RPMS_DIR_NAME); diff --git a/syscare-build/src/package/dependency.rs b/syscare-build/src/package/dependency.rs deleted file mode 100644 index 37bdf92aa141214246593b1980b5611993588fcc..0000000000000000000000000000000000000000 --- a/syscare-build/src/package/dependency.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub struct PackageDependency { - requires: HashSet, - conflicts: HashSet, - suggests: HashSet, - recommends: HashSet, -} - -impl PackageDependency { - -} \ No newline at end of file diff --git a/syscare-build/src/package/mod.rs b/syscare-build/src/package/mod.rs index 9ba29a1d38865cb6848e1da2f1e70bd8b4e33072..72b555abf3c14f75b10e3e0ec840e40eacb146da 100644 --- a/syscare-build/src/package/mod.rs +++ b/syscare-build/src/package/mod.rs @@ -33,7 +33,6 @@ pub use spec_writer::*; pub use tar::*; trait Package { - fn extension(&self) -> &'static str; fn parse_package_info(&self, pkg_path: &Path) -> Result; fn query_package_files(&self, pkg_path: &Path) -> Result>; fn extract_package(&self, pkg_path: &Path, output_dir: &Path) -> Result<()>; diff --git a/syscare-build/src/package/rpm/mod.rs b/syscare-build/src/package/rpm/mod.rs index 5b4bf072f85fc1a65b7f8f0b6c0796ef6fd49213..ee7f19f665baa5f73b42e009e74b779579df3ef4 100644 --- a/syscare-build/src/package/rpm/mod.rs +++ b/syscare-build/src/package/rpm/mod.rs @@ -50,6 +50,7 @@ impl RpmPackage { .arg("--query") .arg("--queryformat") .arg(format) + .arg("--nosignature") .arg("--package") .arg(pkg_path.as_ref().as_os_str()) .run_with_output()?; @@ -60,10 +61,6 @@ impl RpmPackage { } impl Package for RpmPackage { - fn extension(&self) -> &'static str { - PKG_FILE_EXT - } - fn parse_package_info(&self, pkg_path: &Path) -> Result { let query_result = Self::query_package_info( pkg_path, @@ -78,9 +75,10 @@ impl Package for RpmPackage { } let name = pkg_info[0].to_owned(); - let kind = match pkg_info[6] == SPEC_TAG_VALUE_NONE { - true => PackageType::SourcePackage, - false => PackageType::BinaryPackage, + let kind = if pkg_info[6] == SPEC_TAG_VALUE_NONE { + PackageType::SourcePackage + } else { + PackageType::BinaryPackage }; let arch = pkg_info[1].to_owned(); let epoch = pkg_info[2].to_owned(); @@ -109,6 +107,7 @@ impl Package for RpmPackage { let output = Command::new(RPM_BIN) .arg("--query") .arg("--list") + .arg("--nosignature") .arg("--package") .arg(pkg_path) .run_with_output()?; diff --git a/syscare-build/src/package/rpm/spec_builder.rs b/syscare-build/src/package/rpm/spec_builder.rs index 5570c345004406499daf7d967cb966e32be8e602..a24954f7fd56f0177291ba0330a3f28a2150871a 100644 --- a/syscare-build/src/package/rpm/spec_builder.rs +++ b/syscare-build/src/package/rpm/spec_builder.rs @@ -113,7 +113,7 @@ impl RpmSpecBuilder { patch_info.name ); let pkg_version = format!("{}-{}", patch_info.version, patch_info.release); - let pkg_root = Path::new(PKG_INSTALL_DIR).join(&patch_info.uuid.to_string()); + let pkg_root = Path::new(PKG_INSTALL_DIR).join(patch_info.uuid.to_string()); let mut spec = RpmSpecFile::new( pkg_name, diff --git a/syscare-build/src/package/rpm/spec_file.rs b/syscare-build/src/package/rpm/spec_file.rs index 17dcbe365c04dcf14e8c85307523ef0ba1701f67..bd7b647792242279345aa6ad8f06f6a75cdbbbd2 100644 --- a/syscare-build/src/package/rpm/spec_file.rs +++ b/syscare-build/src/package/rpm/spec_file.rs @@ -62,33 +62,33 @@ impl RpmSpecFile { description: String, ) -> Self { Self { - defines: Default::default(), + defines: BTreeSet::default(), name, version, release, - group: Default::default(), + group: Option::default(), license, - url: Default::default(), + url: Option::default(), summary, - build_requires: Default::default(), - requires: Default::default(), - conflicts: Default::default(), - suggests: Default::default(), - recommends: Default::default(), + build_requires: HashSet::default(), + requires: HashSet::default(), + conflicts: HashSet::default(), + suggests: HashSet::default(), + recommends: HashSet::default(), description, prep: SPEC_SCRIPT_VALUE_NONE.to_string(), build: SPEC_SCRIPT_VALUE_NONE.to_string(), install: SPEC_SCRIPT_VALUE_NONE.to_string(), - check: Default::default(), - pre: Default::default(), - post: Default::default(), - preun: Default::default(), - postun: Default::default(), - defattr: Default::default(), - files: Default::default(), - source: Default::default(), - patch: Default::default(), - change_log: Default::default(), + check: Option::default(), + pre: Option::default(), + post: Option::default(), + preun: Option::default(), + postun: Option::default(), + defattr: Option::default(), + files: BTreeSet::default(), + source: BTreeSet::default(), + patch: BTreeSet::default(), + change_log: Option::default(), } } } @@ -204,33 +204,33 @@ impl RpmSpecFile { impl Default for RpmSpecFile { fn default() -> Self { Self { - defines: Default::default(), + defines: BTreeSet::default(), name: SPEC_TAG_VALUE_NONE.to_string(), version: SPEC_TAG_VALUE_NONE.to_string(), release: SPEC_TAG_VALUE_NONE.to_string(), - group: Default::default(), + group: Option::default(), license: SPEC_TAG_VALUE_NONE.to_string(), - url: Default::default(), + url: Option::default(), summary: SPEC_TAG_VALUE_NONE.to_string(), - build_requires: Default::default(), - requires: Default::default(), - conflicts: Default::default(), - suggests: Default::default(), - recommends: Default::default(), + build_requires: HashSet::default(), + requires: HashSet::default(), + conflicts: HashSet::default(), + suggests: HashSet::default(), + recommends: HashSet::default(), description: SPEC_TAG_VALUE_NONE.to_string(), prep: SPEC_SCRIPT_VALUE_NONE.to_string(), build: SPEC_SCRIPT_VALUE_NONE.to_string(), install: SPEC_SCRIPT_VALUE_NONE.to_string(), - check: Default::default(), - pre: Default::default(), - post: Default::default(), - preun: Default::default(), - postun: Default::default(), - defattr: Default::default(), - files: Default::default(), - source: Default::default(), - patch: Default::default(), - change_log: Default::default(), + check: Option::default(), + pre: Option::default(), + post: Option::default(), + preun: Option::default(), + postun: Option::default(), + defattr: Option::default(), + files: BTreeSet::default(), + source: BTreeSet::default(), + patch: BTreeSet::default(), + change_log: Option::default(), } } } diff --git a/syscare-build/src/package/rpm/spec_writer.rs b/syscare-build/src/package/rpm/spec_writer.rs index dc33459bbc2dbfe30b8a9d55a00a6a656e6aa00e..0059aee4923fdf03459c0a5575b1fb2287207e6b 100644 --- a/syscare-build/src/package/rpm/spec_writer.rs +++ b/syscare-build/src/package/rpm/spec_writer.rs @@ -121,7 +121,7 @@ impl PackageSpecWriter for RpmSpecWriter { let mut lines_to_write = BTreeSet::new(); let last_tag_id = source_tags .into_iter() - .last() + .next_back() .map(|tag| tag.id) .unwrap_or_default(); @@ -152,3 +152,33 @@ impl PackageSpecWriter for RpmSpecWriter { .context("Failed to write rpm spec file") } } + +#[test] +fn tests_spec_writer() { + use std::fs::File; + use std::io::{Read, Write}; + + let mut specfile = File::create("/tmp/test.spec").unwrap(); + specfile.write_all(b"Source: kerneltest-1.tar.gz").unwrap(); + specfile.write_all(b"%description").unwrap(); + + specfile.sync_all().unwrap(); + let filepath = PathBuf::from("/tmp/test.spec"); + + let test = RpmSpecWriter; + test.add_source_files( + &filepath, + vec![PathBuf::from("test1"), PathBuf::from("test2")], + ) + .unwrap(); + + let mut fileread = File::open(&filepath).unwrap(); + let mut content = String::new(); + fileread.read_to_string(&mut content).unwrap(); + assert!(content.contains("Source1: test1")); + assert!(content.contains("Source2: test2")); + + if filepath.exists() { + fs::remove_file(filepath).unwrap(); + } +} diff --git a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs index bcae96280ba7d9f2542b0a36f92f379814415062..930e6fb0a837d260ea1314afe2ee488fc9042353 100644 --- a/syscare-build/src/patch/kernel_patch/kpatch_builder.rs +++ b/syscare-build/src/patch/kernel_patch/kpatch_builder.rs @@ -12,7 +12,7 @@ * See the Mulan PSL v2 for more details. */ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use anyhow::{Context, Result}; use log::{info, Level}; @@ -22,6 +22,7 @@ use syscare_abi::{PackageInfo, PatchEntity, PatchFile, PatchInfo, PatchType}; use syscare_common::{ concat_os, ffi::OsStrExt, + fs, process::{Command, CommandArgs, CommandEnvs}, util::digest, }; @@ -35,13 +36,14 @@ use crate::{ use super::kpatch_helper::{KernelPatchHelper, KPATCH_SUFFIX, VMLINUX_FILE_NAME}; const KPATCH_BUILD_BIN: &str = "kpatch-build"; +const GENERATED_KCONFIG_NAME: &str = ".config"; struct KBuildParameters { pkg_build_dir: PathBuf, patch_build_root: PathBuf, patch_output_dir: PathBuf, kernel_source_dir: PathBuf, - oot_source_dir: Option, + kmod_source_dir: Option, config_file: PathBuf, vmlinux_file: PathBuf, patch_name: String, @@ -54,11 +56,12 @@ struct KBuildParameters { patch_files: Vec, jobs: usize, skip_compiler_check: bool, + verbose: bool, } struct KernelPatchEntity { source_dir: PathBuf, - module_path: Option, + kmod_path: Option, patch_entity: PatchEntity, } @@ -85,26 +88,45 @@ impl KernelPatchBuilder { let patch_output_dir: PathBuf = build_params.build_root.patch.output.clone(); let kernel_pkg = &kernel_entry.target_pkg; - let kernel_source_dir: PathBuf = self - .pkg_impl - .find_source_directory( - &kernel_entry.build_source, - &format!( - "linux-{}-{}.{}", - kernel_pkg.version, kernel_pkg.release, kernel_pkg.arch - ), - ) - .context("Cannot find kernel source directory")?; + let kernel_source_dir: PathBuf = fs::find_dir( + &kernel_entry.build_source, + format!("linux-{}", kernel_pkg.version), + fs::FindOptions { + fuzz: true, + recursive: true, + }, + ) + .context("Cannot find kernel source directory")?; + let kmod_source_dir = oot_module_entry.map(|entry| entry.build_source.clone()); let kernel_debug_dir = &build_params.build_root.package.debuginfo; - let oot_source_dir = oot_module_entry.map(|build_entry| build_entry.build_source.clone()); - info!("- Generating kernel default config"); - KernelPatchHelper::generate_defconfig(&kernel_source_dir) - .context("Failed to generate default config")?; + /* + * Kernel config: + * If it's a valid path, use it directly as an exteral file. + * Otherwise, we treat it as a kernel config name. + */ + let config_file = match fs::canonicalize(&build_params.kernel_config).ok() { + Some(file_path) => { + info!( + "- Using kernel config file '{}'", + build_params.kernel_config.to_string_lossy() + ); + file_path + } + None => { + info!( + "- Using kernel config '{}'", + build_params.kernel_config.to_string_lossy() + ); + KernelPatchHelper::generate_config_file( + &kernel_source_dir, + &build_params.kernel_config, + ) + .context("Failed to generate kernel config")?; - info!("- Finding kernel config"); - let config_file = KernelPatchHelper::find_kernel_config(&kernel_source_dir) - .context("Cannot find kernel config")?; + kernel_source_dir.join(GENERATED_KCONFIG_NAME) + } + }; info!("- Finding vmlinux"); let vmlinux_file = KernelPatchHelper::find_vmlinux(kernel_debug_dir) @@ -125,7 +147,7 @@ impl KernelPatchBuilder { patch_build_root, patch_output_dir, kernel_source_dir, - oot_source_dir, + kmod_source_dir, config_file, vmlinux_file, patch_name: build_params.patch_name.to_owned(), @@ -138,6 +160,7 @@ impl KernelPatchBuilder { patch_files: build_params.patch_files.to_owned(), jobs: build_params.jobs, skip_compiler_check: build_params.skip_compiler_check, + verbose: build_params.verbose, }) } @@ -147,54 +170,63 @@ impl KernelPatchBuilder { ) -> Result> { let mut entity_list = Vec::new(); - match &kbuild_params.oot_source_dir { + match &kbuild_params.kmod_source_dir { // Kernel patch None => { - let entity_uuid = Uuid::new_v4(); - let entity_target = VMLINUX_FILE_NAME; - let entity_name = format!("{}-{}", entity_target, entity_uuid); - + let uuid = Uuid::new_v4(); + let uuid_short = uuid + .to_string() + .split_once('-') + .map(|s| s.0.to_string()) + .expect("Unexpected kernel patch uuid"); + + let patch_entity = PatchEntity { + uuid, + patch_name: concat_os!(VMLINUX_FILE_NAME, "-", uuid_short), + patch_target: VMLINUX_FILE_NAME.into(), + checksum: String::new(), + }; entity_list.push(KernelPatchEntity { source_dir: kbuild_params.kernel_source_dir.to_owned(), - module_path: None, - patch_entity: PatchEntity { - uuid: entity_uuid, - patch_name: entity_name.into(), - patch_target: entity_target.into(), - checksum: String::default(), - }, + kmod_path: None, + patch_entity, }); } // Kernel module patch - Some(oot_source_dir) => { - let module_list = + Some(kmod_source_dir) => { + let kmod_list = KernelPatchHelper::find_kernel_modules(&kbuild_params.pkg_build_dir) .context("Failed to find any kernel module")?; - let module_suffix = format!(".{}", KPATCH_SUFFIX); - - for module_path in module_list { - let file_name = module_path + for kmod_path in kmod_list { + let uuid = Uuid::new_v4(); + let uuid_short = uuid + .to_string() + .split_once('-') + .map(|s| s.0.to_string()) + .expect("Unexpected kernel module uuid"); + let file_name = kmod_path .file_name() - .context("Cannot get patch file name")?; - let module_name = file_name - .strip_suffix(&module_suffix) - .context("Unexpected patch suffix")? - .to_string_lossy() - .replace(['.', '-'], "_"); - - let entity_uuid = Uuid::new_v4(); - let entitiy_name = format!("{}-{}", module_name, entity_uuid); - let entity_target = file_name.to_os_string(); - + .expect("Unexpected kernel module file name"); + let module_name = { + let mut kmod_file = kmod_path.clone(); + kmod_file.set_extension(""); + + kmod_file + .file_name() + .expect("Unexpected kernel module name") + .replace(['.', '-'], "_") + }; + + let patch_entity = PatchEntity { + uuid, + patch_name: concat_os!(module_name, "-", uuid_short), + patch_target: file_name.into(), + checksum: String::new(), + }; entity_list.push(KernelPatchEntity { - source_dir: oot_source_dir.to_owned(), - module_path: Some(module_path), - patch_entity: PatchEntity { - uuid: entity_uuid, - patch_name: entitiy_name.into(), - patch_target: entity_target.into(), - checksum: String::default(), - }, + source_dir: kmod_source_dir.to_owned(), + kmod_path: Some(kmod_path), + patch_entity, }); } } @@ -218,37 +250,47 @@ impl KernelPatchBuilder { .arg("--config") .arg(&kbuild_params.config_file) .arg("--vmlinux") - .arg(&kbuild_params.vmlinux_file) - .arg("--jobs") - .arg(kbuild_params.jobs.to_string()) - .arg("--output") - .arg(&kbuild_params.patch_output_dir) - .arg("--non-replace"); + .arg(&kbuild_params.vmlinux_file); - if let Some(oot_module) = &kbuild_entity.module_path { - cmd_args.arg("--oot-module").arg(oot_module); + if let Some(kmod_path) = &kbuild_entity.kmod_path { + cmd_args.arg("--oot-module").arg(kmod_path); cmd_args .arg("--oot-module-src") .arg(&kbuild_entity.source_dir); } - cmd_args.args(kbuild_params.patch_files.iter().map(|patch| &patch.path)); + + cmd_args + .arg("--jobs") + .arg(kbuild_params.jobs.to_string()) + .arg("--non-replace") + .arg("--skip-cleanup"); if kbuild_params.skip_compiler_check { cmd_args.arg("--skip-compiler-check"); } - cmd_args.arg("--skip-cleanup"); + if kbuild_params.verbose { + cmd_args.arg("--debug"); + } + + cmd_args + .arg("--output") + .arg(&kbuild_params.patch_output_dir) + .args(kbuild_params.patch_files.iter().map(|f| &f.path)); cmd_args } - fn parse_kbuild_cmd_envs(&self, build_root: &Path) -> CommandEnvs { + fn parse_kbuild_cmd_envs(&self, kbuild_params: &KBuildParameters) -> CommandEnvs { let mut cmd_envs = CommandEnvs::new(); cmd_envs - .env("CACHEDIR", build_root) + .env("CACHEDIR", &kbuild_params.patch_build_root) .env("NO_PROFILING_CALLS", "yes") .env("DISABLE_AFTER_LOAD", "yes") .env("KEEP_JUMP_LABEL", "yes"); - + if let Some(kmod_source_dir) = &kbuild_params.kmod_source_dir { + cmd_envs.env("OOT_MODULE", "yes"); + cmd_envs.env("USERMODBUILDDIR", kmod_source_dir); + } cmd_envs } @@ -263,7 +305,7 @@ impl KernelPatchBuilder { for mut kbuild_entity in kpatch_entities { Command::new(KPATCH_BUILD_BIN) .args(self.parse_kbuild_cmd_args(kbuild_params, &kbuild_entity)) - .envs(self.parse_kbuild_cmd_envs(&kbuild_params.patch_build_root)) + .envs(self.parse_kbuild_cmd_envs(kbuild_params)) .stdout(Level::Debug) .run_with_output()? .exit_ok()?; diff --git a/syscare-build/src/patch/kernel_patch/kpatch_helper.rs b/syscare-build/src/patch/kernel_patch/kpatch_helper.rs index 8cbe9b31434c659ac3675af6cd622076879bb42e..f109b646a70aa3985b83be936361520839886d33 100644 --- a/syscare-build/src/patch/kernel_patch/kpatch_helper.rs +++ b/syscare-build/src/patch/kernel_patch/kpatch_helper.rs @@ -12,7 +12,10 @@ * See the Mulan PSL v2 for more details. */ -use std::path::{Path, PathBuf}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, +}; use anyhow::Result; use log::Level; @@ -26,31 +29,20 @@ const MAKE_BIN: &str = "make"; pub struct KernelPatchHelper; impl KernelPatchHelper { - pub fn generate_defconfig>(source_dir: P) -> Result<()> { - const DEFCONFIG_FILE_NAME: &str = "openeuler_defconfig"; - + pub fn generate_config_file(source_dir: P, name: S) -> Result<()> + where + P: AsRef, + S: AsRef, + { Command::new(MAKE_BIN) .arg("-C") .arg(source_dir.as_ref()) - .arg(DEFCONFIG_FILE_NAME) + .arg(name) .stdout(Level::Debug) .run_with_output()? .exit_ok() } - pub fn find_kernel_config>(directory: P) -> Result { - const KERNEL_CONFIG_FILE_NAME: &str = ".config"; - - Ok(fs::find_file( - directory, - KERNEL_CONFIG_FILE_NAME, - fs::FindOptions { - fuzz: false, - recursive: true, - }, - )?) - } - pub fn find_vmlinux>(directory: P) -> std::io::Result { fs::find_file( directory, diff --git a/syscare-build/src/patch/metadata.rs b/syscare-build/src/patch/metadata.rs index 918b487d64029242e1cfc44f67513f6ae4622c81..5eadf523dd3bfa3d797bcf4c1679902cd0cc2b89 100644 --- a/syscare-build/src/patch/metadata.rs +++ b/syscare-build/src/patch/metadata.rs @@ -35,8 +35,8 @@ pub struct PatchMetadata { } impl PatchMetadata { - pub fn new>(root_dir: P) -> Self { - let root_dir = root_dir.as_ref().to_path_buf(); + pub fn new>(directory: P) -> Self { + let root_dir = directory.as_ref().to_path_buf(); let metadata_dir = root_dir.join(METADATA_DIR_NAME); let package_path = root_dir.join(METADATA_PKG_NAME); let metadata_path = metadata_dir.join(METADATA_FILE_NAME); @@ -90,8 +90,16 @@ impl PatchMetadata { .decompress(&self.root_dir) .context("Failed to decompress patch metadata")?; - serde::deserialize_with_magic::(&self.metadata_path, PATCH_INFO_MAGIC) - .context("Failed to read patch metadata") + let mut patch_info: PatchInfo = + serde::deserialize_with_magic(&self.metadata_path, PATCH_INFO_MAGIC) + .context("Failed to read patch metadata")?; + + // rewrite file path to metadata directory path + for patch_file in &mut patch_info.patches { + patch_file.path = self.metadata_dir.join(&patch_file.name) + } + + Ok(patch_info) } pub fn write>( diff --git a/syscare-build/src/patch/mod.rs b/syscare-build/src/patch/mod.rs index f590b1c7f2826e63c22e6642bcce441998a2ab06..558bfeae33198b4f7aabd4ad6506b5c5b1566f39 100644 --- a/syscare-build/src/patch/mod.rs +++ b/syscare-build/src/patch/mod.rs @@ -12,8 +12,6 @@ * See the Mulan PSL v2 for more details. */ -pub const PATCH_FILE_EXT: &str = "patch"; - mod kernel_patch; mod metadata; mod patch_builder; diff --git a/syscare-build/src/patch/patch_helper.rs b/syscare-build/src/patch/patch_helper.rs index fc8324a94a57c532ecd7aa098b255b2d1c72feaa..9fc3ed175484e6b051b1bca48bdad87b4722ea01 100644 --- a/syscare-build/src/patch/patch_helper.rs +++ b/syscare-build/src/patch/patch_helper.rs @@ -47,7 +47,7 @@ impl PatchHelper { digest: file_digest, }); } - patch_list.sort(); + Ok(patch_list) } } diff --git a/syscare-build/src/patch/user_patch/elf_relation.rs b/syscare-build/src/patch/user_patch/file_relation.rs similarity index 84% rename from syscare-build/src/patch/user_patch/elf_relation.rs rename to syscare-build/src/patch/user_patch/file_relation.rs index 1eae665b82dd1161247ed182aa210bf2db8fd8fc..fea7c40330a04b28a85838dfaa238d14048d0265 100644 --- a/syscare-build/src/patch/user_patch/elf_relation.rs +++ b/syscare-build/src/patch/user_patch/file_relation.rs @@ -22,13 +22,13 @@ use syscare_common::ffi::OsStrExt; use super::{DEBUGINFO_FILE_EXT, DEBUGINFO_INSTALL_DIR}; #[derive(Debug, Clone)] -pub struct ElfRelation { - pub elf: PathBuf, +pub struct FileRelation { + pub binary: PathBuf, pub debuginfo: PathBuf, } -impl ElfRelation { - pub fn parse(root: P, package: &PackageInfo, debuginfo: Q) -> Result +impl FileRelation { + pub fn parse(root: P, package: &PackageInfo, debuginfo: Q) -> Result where P: AsRef, Q: AsRef, @@ -36,7 +36,7 @@ impl ElfRelation { let prefix = root.as_ref().join(DEBUGINFO_INSTALL_DIR); let debuginfo_path = debuginfo.as_ref().to_path_buf(); - let elf_path = debuginfo_path + let binary_path = debuginfo_path .as_os_str() .strip_prefix(prefix.as_os_str()) .and_then(|name| { @@ -56,24 +56,24 @@ impl ElfRelation { .map(PathBuf::from) .with_context(|| { format!( - "Cannot parse elf path from {}, suffix mismatched", + "Cannot parse binary path from {}, suffix mismatched", debuginfo_path.display() ) })?; - Ok(ElfRelation { - elf: elf_path, + Ok(FileRelation { + binary: binary_path, debuginfo: debuginfo_path, }) } } -impl std::fmt::Display for ElfRelation { +impl std::fmt::Display for FileRelation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Elf: {}, debuginfo: {}", - self.elf.display(), + "Binary: {}, debuginfo: {}", + self.binary.display(), self.debuginfo.display() ) } diff --git a/syscare-build/src/patch/user_patch/mod.rs b/syscare-build/src/patch/user_patch/mod.rs index 3914bc2540a786d4d6234657e629b88829d91711..e3b5bea13dd2260bf1ade2360ce849af93afeeff 100644 --- a/syscare-build/src/patch/user_patch/mod.rs +++ b/syscare-build/src/patch/user_patch/mod.rs @@ -15,7 +15,7 @@ const DEBUGINFO_FILE_EXT: &str = "debug"; const DEBUGINFO_INSTALL_DIR: &str = "usr/lib/debug"; -mod elf_relation; +mod file_relation; mod upatch_builder; pub use upatch_builder::*; diff --git a/syscare-build/src/patch/user_patch/upatch_builder.rs b/syscare-build/src/patch/user_patch/upatch_builder.rs index 1e0e6b64814a0a13b7a37b1052de18ba0848198e..b54d11f309839ad90fcc44492c5534b4d53dee39 100644 --- a/syscare-build/src/patch/user_patch/upatch_builder.rs +++ b/syscare-build/src/patch/user_patch/upatch_builder.rs @@ -21,7 +21,6 @@ use std::{ use anyhow::{ensure, Context, Result}; use log::{debug, info, trace, Level}; use uuid::Uuid; -use which::which; use syscare_abi::{PackageInfo, PatchEntity, PatchFile, PatchInfo, PatchType}; use syscare_common::{ @@ -32,20 +31,19 @@ use syscare_common::{ use crate::{build_params::BuildParameters, package::PackageImpl, patch::PatchBuilder}; -use super::{elf_relation::ElfRelation, DEBUGINFO_FILE_EXT}; +use super::{file_relation::FileRelation, DEBUGINFO_FILE_EXT}; -const UPATCH_BUILD_BIN: &str = "/usr/libexec/syscare/upatch-build"; +const UPATCH_BUILD_BIN: &str = "upatch-build"; const RPMBUILD_BIN: &str = "rpmbuild"; struct UBuildParameters { - work_dir: PathBuf, pkg_binary_dir: PathBuf, pkg_output_dir: PathBuf, patch_build_root: PathBuf, patch_source_dir: PathBuf, patch_output_dir: PathBuf, - compiler_list: Vec, - elf_relations: Vec, + compilers: Vec, + file_relations: Vec, prepare_cmd: OsString, build_cmd: OsString, patch_name: String, @@ -56,6 +54,7 @@ struct UBuildParameters { patch_target: PackageInfo, patch_description: String, patch_files: Vec, + override_line_macros: bool, skip_compiler_check: bool, verbose: bool, } @@ -72,14 +71,14 @@ impl UserPatchBuilder { impl UserPatchBuilder { fn detect_compilers() -> Vec { - const COMPILER_NAMES: [&str; 4] = ["cc", "gcc", "c++", "g++"]; - - // Get compiler path and filter invalid one - let compiler_set = COMPILER_NAMES - .iter() - .filter_map(|compiler_name| which(compiler_name).ok()) - .collect::>(); + const COMPILERS: [&str; 2] = ["/usr/bin/cc", "/usr/bin/c++"]; + let mut compiler_set = HashSet::new(); + for compiler in COMPILERS { + if let Ok(compiler_path) = fs::canonicalize(compiler) { + compiler_set.insert(compiler_path); + } + } compiler_set.into_iter().collect() } @@ -89,18 +88,23 @@ impl UserPatchBuilder { fn create_build_macros(jobs: usize) -> OsString { args_os!( - concat_os!("--define '_smp_build_ncpus ", jobs.to_string(), "'"), - "--define '__spec_install_post %{nil}'", + format!("--define '_smp_build_ncpus {}'", jobs), + "--define '__brp_strip %{nil}'", + "--define '__brp_strip_comment_note %{nil}'", + "--define '__brp_strip_static_archive %{nil}'", + "--define '__brp_compress %{nil}'", + "--define '__check_files %{nil}'", "--define '__find_provides %{nil}'", "--define '__find_requires %{nil}'", + "--define '__spec_install_post %{nil}'", "--define '_use_internal_dependency_generator 0'", ) } - fn parse_elf_relations( + fn parse_file_relations( package: &PackageInfo, debuginfo_root: &Path, - ) -> Result> { + ) -> Result> { let debuginfo_files = fs::list_files_by_ext( debuginfo_root, DEBUGINFO_FILE_EXT, @@ -111,14 +115,14 @@ impl UserPatchBuilder { "Cannot find any debuginfo file" ); - let mut elf_relations = Vec::new(); + let mut file_relations = Vec::new(); for debuginfo in &debuginfo_files { - // Skip elf relation error check may cause unknown error - if let Ok(elf_relation) = ElfRelation::parse(debuginfo_root, package, debuginfo) { - elf_relations.push(elf_relation); + // Skip file relation error check may cause unknown error + if let Ok(file_relation) = FileRelation::parse(debuginfo_root, package, debuginfo) { + file_relations.push(file_relation); } } - Ok(elf_relations) + Ok(file_relations) } } @@ -150,27 +154,24 @@ impl UserPatchBuilder { ); info!("- Detecting compilers"); - let compiler_list = Self::detect_compilers(); - for compiler in &compiler_list { + let compilers = Self::detect_compilers(); + for compiler in &compilers { debug!("{}", compiler.display()) } info!("- Parsing elf relations"); - let elf_relations = Self::parse_elf_relations(patch_target, debuginfo_pkg_root) + let file_relations = Self::parse_file_relations(patch_target, debuginfo_pkg_root) .context("Failed to parse elf relation")?; - for elf_relation in &elf_relations { - trace!("{}", elf_relation); - } + trace!("{:#?}", file_relations); let ubuild_params = UBuildParameters { - work_dir: build_params.work_dir.to_owned(), pkg_binary_dir, pkg_output_dir, patch_build_root, patch_source_dir, patch_output_dir, - compiler_list, - elf_relations, + compilers, + file_relations, prepare_cmd, build_cmd, patch_name: build_params.patch_name.to_owned(), @@ -181,6 +182,7 @@ impl UserPatchBuilder { patch_target: build_params.build_entry.target_pkg.to_owned(), patch_description: build_params.patch_description.to_owned(), patch_files: build_params.patch_files.to_owned(), + override_line_macros: build_params.override_line_macros, skip_compiler_check: build_params.skip_compiler_check, verbose: build_params.verbose, }; @@ -190,36 +192,42 @@ impl UserPatchBuilder { fn parse_ubuild_cmd_args(&self, ubuild_params: &UBuildParameters) -> CommandArgs { let mut cmd_args = CommandArgs::new(); + cmd_args - .arg("--work-dir") - .arg(&ubuild_params.work_dir) .arg("--build-root") .arg(&ubuild_params.patch_build_root) .arg("--source-dir") .arg(&ubuild_params.patch_source_dir) - .arg("--elf-dir") - .arg(&ubuild_params.pkg_binary_dir) + .arg("--compiler") + .args(&ubuild_params.compilers) .arg("--prepare-cmd") .arg(&ubuild_params.prepare_cmd) .arg("--build-cmd") .arg(&ubuild_params.build_cmd) + .arg("--binary-dir") + .arg(&ubuild_params.pkg_binary_dir) + .arg("--binary") + .args( + ubuild_params + .file_relations + .iter() + .map(|relation| &relation.binary), + ) + .arg("--debuginfo") + .args( + ubuild_params + .file_relations + .iter() + .map(|relation| &relation.debuginfo), + ) + .arg("--patch") + .args(ubuild_params.patch_files.iter().map(|patch| &patch.path)) .arg("--output-dir") .arg(&ubuild_params.patch_output_dir); - for compiler in &ubuild_params.compiler_list { - cmd_args.arg("--compiler").arg(compiler); - } - for elf_relation in &ubuild_params.elf_relations { - cmd_args - .arg("--elf") - .arg(concat_os!("*", &elf_relation.elf)) - .arg("--debuginfo") - .arg(&elf_relation.debuginfo); + if ubuild_params.override_line_macros { + cmd_args.arg("--override-line-macros"); } - cmd_args - .arg("--patch") - .args(ubuild_params.patch_files.iter().map(|patch| &patch.path)); - if ubuild_params.skip_compiler_check { cmd_args.arg("--skip-compiler-check"); } @@ -251,8 +259,8 @@ impl UserPatchBuilder { .collect::>(); let mut patch_entities = Vec::new(); - for elf_relation in &ubuild_params.elf_relations { - let elf_file = &elf_relation.elf; + for relation in &ubuild_params.file_relations { + let elf_file = &relation.binary; let elf_name = fs::file_name(elf_file); if !pkg_file_list.contains(elf_file) { continue; diff --git a/syscare-common/Cargo.toml b/syscare-common/Cargo.toml index 83849934f5b4b232ce376f8a49cf4a48548510eb..f0b6446c92201bd793b6c979bdc9d78879578677 100644 --- a/syscare-common/Cargo.toml +++ b/syscare-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syscare-common" -version = "1.2.1" +version = "1.2.2" authors = ["renoseven "] description = "Syscare common library" license = "MulanPSL-2.0" @@ -10,10 +10,11 @@ build = "build.rs" [dependencies] anyhow = { version = "1.0" } -lazy_static = { version = "1.4" } log = { version = "0.4" } memmap2 = { version = "0.9" } nix = { version = "0.26" } +num_cpus = { version = "1.14" } +object = { version = "0.29" } regex = { version = "1.7" } sha2 = { version = "0.10" } serde = { version = "1.0", features = ["derive"] } diff --git a/syscare-common/build.rs b/syscare-common/build.rs index 10199989c5aa73fef55bdd0265f70e9231c7abe9..ca9d78cb59e250900562d5533a61e5d4d883c453 100644 --- a/syscare-common/build.rs +++ b/syscare-common/build.rs @@ -12,26 +12,25 @@ * See the Mulan PSL v2 for more details. */ -use std::{env, process::Command}; +use std::{env, ffi::OsStr, os::unix::ffi::OsStrExt, process::Command}; fn rewrite_version() { - const ENV_VERSION_NAME: &str = "BUILD_VERSION"; - const PKG_VERSION_NAME: &str = "CARGO_PKG_VERSION"; + const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + const ENV_VERSION: Option<&str> = option_env!("BUILD_VERSION"); - let version = env::var(ENV_VERSION_NAME).unwrap_or_else(|_| { - let pkg_version = env::var(PKG_VERSION_NAME).expect("Failed to fetch package version"); - let git_output = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|output| String::from_utf8(output.stdout).expect("Failed to fetch git version")); - - match git_output { - Ok(git_version) => format!("{}-g{}", pkg_version, git_version), - Err(_) => pkg_version, - } - }); - - println!("cargo:rustc-env={}={}", PKG_VERSION_NAME, version); + println!( + "cargo:rustc-env=CARGO_PKG_VERSION={}", + ENV_VERSION.map(String::from).unwrap_or_else(|| { + Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|output| { + let git_version = OsStr::from_bytes(&output.stdout).to_string_lossy(); + format!("{}-g{}", PKG_VERSION, git_version) + }) + .unwrap_or_else(|_| PKG_VERSION.to_string()) + }) + ); } fn main() { diff --git a/syscare-common/src/ffi/os_str.rs b/syscare-common/src/ffi/os_str.rs index 43a9adc8b8786524ef0eeafd804bf899adadb766..0c414e1875f079ba05a496a34e8c67996764db47 100644 --- a/syscare-common/src/ffi/os_str.rs +++ b/syscare-common/src/ffi/os_str.rs @@ -21,7 +21,7 @@ use std::{ use crate::os_str::{ pattern::{Pattern, ReverseSearcher, SearchStep, Searcher}, - CharIndices, Lines, Split, SplitImpl, SplitInclusive, + CharIndices, Chars, Lines, Split, SplitImpl, SplitInclusive, }; pub type SplitFn = fn(char) -> bool; @@ -37,10 +37,7 @@ pub trait OsStrExt: AsRef { let haystack = self.as_ref().as_bytes(); match haystack.get(index) { - Some(&b) => { - // This is bit magic equivalent to: b < 128 || b >= 192 - b as i8 >= -0x40 - } + Some(&b) => !(128..192).contains(&b), None => index == haystack.len(), } } @@ -55,6 +52,16 @@ pub trait OsStrExt: AsRef { } } + fn chars(&self) -> Chars<'_> { + let haystack = self.as_ref().as_bytes(); + + Chars { + char_bytes: haystack, + front_idx: 0, + back_idx: haystack.len(), + } + } + fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { pat.into_searcher(self.as_ref().as_bytes()) .next_match() @@ -112,7 +119,7 @@ pub trait OsStrExt: AsRef { } } - fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split

{ + fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { let haystack = self.as_ref().as_bytes(); Split(SplitImpl { @@ -135,14 +142,12 @@ pub trait OsStrExt: AsRef { } fn split_whitespace(&self) -> Filter, FilterFn> { - self.split(char::is_whitespace as SplitFn) + self.split(SplitFn::from(char::is_whitespace)) .filter(|s| !s.is_empty()) } fn split_at(&self, mid: usize) -> (&OsStr, &OsStr) { - if !self.is_char_boundary(mid) { - panic!("Failed to slice osstring"); - } + debug_assert!(self.is_char_boundary(mid), "Split out of char boundary"); let (lhs, rhs) = self.as_ref().as_bytes().split_at(mid); (OsStr::from_bytes(lhs), OsStr::from_bytes(rhs)) @@ -274,14 +279,31 @@ fn test() { ); } + println!("Testing OsStrExt::chars()..."); + assert_eq!( + orig_str.chars().collect::>(), + test_str.chars().collect::>() + ); + assert_eq!( + orig_str.chars().rev().collect::>(), + test_str.chars().rev().collect::>() + ); + println!("Testing OsStrExt::char_indices()..."); assert_eq!( orig_str.char_indices().collect::>(), - test_str.char_indices().collect::>() + test_str + .char_indices() + .map(|(i, c)| (i, *c.as_char())) + .collect::>() ); assert_eq!( orig_str.char_indices().rev().collect::>(), - test_str.char_indices().rev().collect::>() + test_str + .char_indices() + .map(|(i, c)| (i, *c.as_char())) + .rev() + .collect::>() ); println!("Testing OsStrExt::find()..."); diff --git a/syscare-common/src/fs/flock.rs b/syscare-common/src/fs/flock.rs index b5639a842b822110e32e139603d9929c1760bc50..931a9c75a18dec8b956bad696ee6aa30b722cc20 100644 --- a/syscare-common/src/fs/flock.rs +++ b/syscare-common/src/fs/flock.rs @@ -14,117 +14,174 @@ use std::{ fs::File, - os::unix::io::{AsRawFd, RawFd}, + io::Result, + ops::{Deref, DerefMut}, + os::{fd::RawFd, unix::io::AsRawFd}, path::Path, }; -use anyhow::{Context, Result}; -use nix::fcntl::{flock, FlockArg}; +use nix::fcntl; #[derive(Debug)] pub enum FileLockType { Shared, Exclusive, + SharedNonBlock, + ExclusiveNonBlock, } #[derive(Debug)] pub struct FileLock { - inner: File, - kind: FileLockType, + file: File, } impl FileLock { + fn new(file_path: &Path, kind: FileLockType) -> Result { + let file = File::open(file_path)?; + Self::acquire_flock(file.as_raw_fd(), kind)?; + + Ok(Self { file }) + } + #[inline] - fn acquire_lock(&self) -> Result<()> { - let fd = self.inner.as_raw_fd(); - let arg = match self.kind { - FileLockType::Shared => FlockArg::LockSharedNonblock, - FileLockType::Exclusive => FlockArg::LockExclusiveNonblock, + fn acquire_flock(fd: RawFd, kind: FileLockType) -> Result<()> { + let arg = match kind { + FileLockType::Shared => fcntl::FlockArg::LockShared, + FileLockType::Exclusive => fcntl::FlockArg::LockExclusive, + FileLockType::SharedNonBlock => fcntl::FlockArg::LockSharedNonblock, + FileLockType::ExclusiveNonBlock => fcntl::FlockArg::LockExclusiveNonblock, }; + fcntl::flock(fd, arg)?; - flock(fd, arg).with_context(|| format!("Failed to acquire flock for fd {}", fd)) + Ok(()) } #[inline] - fn release_lock(&self) -> Result<()> { - let fd = self.inner.as_raw_fd(); - let arg = FlockArg::Unlock; - - flock(fd, arg).with_context(|| format!("Failed to release flock for fd {}", fd)) + fn release_flock(fd: RawFd) { + fcntl::flock(fd, fcntl::FlockArg::Unlock).expect("Failed to release file lock"); } } -impl FileLock { - pub fn new>(path: P, kind: FileLockType) -> Result { - let file_path = path.as_ref(); - let inner = match file_path.exists() { - true => File::open(file_path), - false => File::create(file_path), - } - .with_context(|| format!("Failed to create flock on {}", file_path.display()))?; - - let instance = Self { inner, kind }; - instance.acquire_lock()?; - - Ok(instance) +impl Deref for FileLock { + type Target = File; + + fn deref(&self) -> &Self::Target { + &self.file } } -impl AsRawFd for FileLock { - fn as_raw_fd(&self) -> RawFd { - self.inner.as_raw_fd() +impl DerefMut for FileLock { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.file } } impl Drop for FileLock { fn drop(&mut self) { - self.release_lock().ok(); + Self::release_flock(self.file.as_raw_fd()); } } +pub fn flock_exists>(file_path: P, kind: FileLockType) -> Result { + FileLock::new(file_path.as_ref(), kind) +} + +pub fn flock>(file_path: P, kind: FileLockType) -> Result { + let file_path = file_path.as_ref(); + if !file_path.exists() { + File::create(file_path)?; + } + self::flock_exists(file_path, kind) +} + #[test] -fn test() { +fn test() -> anyhow::Result<()> { + use anyhow::{anyhow, ensure}; use std::fs; - const EXIST_FILE: &str = "/etc/os-release"; - const NON_EXIST_FILE: &str = "/tmp/flock_test"; + let file_path = std::env::temp_dir().join("flock_test"); + let non_exist_file = std::env::temp_dir().join("flock_test_non_exist"); - println!("Testing exclusive flock on {}...", NON_EXIST_FILE); - fs::remove_file(NON_EXIST_FILE).ok(); + fs::remove_file(&file_path).ok(); + fs::remove_file(&non_exist_file).ok(); - let exclusive_lock = FileLock::new(NON_EXIST_FILE, FileLockType::Exclusive) - .expect("Failed to create exclusive flock"); - drop(exclusive_lock); + fs::write(&file_path, "flock_test")?; - println!("Testing shared flock on {}...", NON_EXIST_FILE); - fs::remove_file(NON_EXIST_FILE).ok(); + println!("Testing fs::flock_exists()..."); + println!("- Shared flock '{}'...", file_path.display()); let shared_lock = - FileLock::new(NON_EXIST_FILE, FileLockType::Shared).expect("Failed to create shared flock"); + self::flock_exists(&file_path, FileLockType::SharedNonBlock).map_err(|e| { + anyhow!( + "Failed to create shared flock '{}', {}", + file_path.display(), + e + ) + })?; + let shared_lock1 = + self::flock_exists(&file_path, FileLockType::SharedNonBlock).map_err(|e| { + anyhow!( + "Failed to create shared flock '{}', {}", + file_path.display(), + e + ) + })?; + ensure!( + self::flock_exists(&file_path, FileLockType::ExclusiveNonBlock).is_err(), + "Exclusive flock '{}' should be failed", + file_path.display() + ); drop(shared_lock); + drop(shared_lock1); - fs::remove_file(NON_EXIST_FILE).ok(); - - println!("Testing exclusive flock on {}...", EXIST_FILE); - let exclusive_lock = FileLock::new(EXIST_FILE, FileLockType::Exclusive) - .expect("Failed to create exclusive flock"); - let _exclusive_err = FileLock::new(EXIST_FILE, FileLockType::Exclusive) - .expect_err("Exclusive flock should be failed"); - let _shared_err = - FileLock::new(EXIST_FILE, FileLockType::Shared).expect_err("Shared flock should be failed"); - + println!("- Exclusive flock '{}'...", file_path.display()); + let exclusive_lock = + self::flock_exists(&file_path, FileLockType::ExclusiveNonBlock).map_err(|e| { + anyhow!( + "Failed to create exclusive flock '{}', {}", + file_path.display(), + e + ) + })?; + ensure!( + self::flock_exists(&file_path, FileLockType::SharedNonBlock).is_err(), + "Shared flock '{}' should be failed", + file_path.display() + ); + ensure!( + self::flock_exists(&file_path, FileLockType::ExclusiveNonBlock).is_err(), + "Exclusive flock '{}' should be failed", + file_path.display() + ); drop(exclusive_lock); - println!("Testing shared flock on {}...", EXIST_FILE); - let shared_lock1 = - FileLock::new(EXIST_FILE, FileLockType::Shared).expect("Failed to create shared flock"); - let shared_lock2 = - FileLock::new(EXIST_FILE, FileLockType::Shared).expect("Failed to create shared flock"); - let _exclusive_err = FileLock::new(EXIST_FILE, FileLockType::Exclusive) - .expect_err("Exclusive flock should be failed"); - let shared_lock3 = - FileLock::new(EXIST_FILE, FileLockType::Shared).expect("Failed to create shared flock"); - - drop(shared_lock1); - drop(shared_lock2); - drop(shared_lock3); + println!("- Non-exist flock '{}'...", non_exist_file.display()); + ensure!( + self::flock_exists(&non_exist_file, FileLockType::SharedNonBlock).is_err(), + "Shared flock '{}' should be failed", + non_exist_file.display() + ); + ensure!( + self::flock_exists(&non_exist_file, FileLockType::ExclusiveNonBlock).is_err(), + "Exclusive flock '{}' should be failed", + non_exist_file.display() + ); + + println!("Testing fs::flock()..."); + println!("- Non-exist flock '{}'...", non_exist_file.display()); + let _ = self::flock(&non_exist_file, FileLockType::SharedNonBlock).map_err(|e| { + anyhow!( + "Failed to create shared flock '{}', {}", + file_path.display(), + e + ) + })?; + let _ = self::flock(&non_exist_file, FileLockType::ExclusiveNonBlock).map_err(|e| { + anyhow!( + "Failed to create exclusive flock '{}', {}", + file_path.display(), + e + ) + })?; + + Ok(()) } diff --git a/syscare-common/src/fs/fs_impl.rs b/syscare-common/src/fs/fs_impl.rs index af0f60c01fce04a5535bb8119a915638e3699a05..a048804155d2db3a03e1c1f4ac28cdc38f6f2853 100644 --- a/syscare-common/src/fs/fs_impl.rs +++ b/syscare-common/src/fs/fs_impl.rs @@ -14,15 +14,14 @@ use std::{ env, - ffi::{CStr, OsStr, OsString}, + ffi::{OsStr, OsString}, fs::{File, FileType, Metadata, Permissions, ReadDir}, io, - os::{raw::c_void, unix::fs::PermissionsExt}, + os::unix::fs::PermissionsExt, path::{Component, Path, PathBuf}, - ptr::null_mut, }; -use crate::ffi::{CStrExt, OsStrExt}; +use crate::ffi::OsStrExt; trait RewriteError { fn rewrite_err(self, err_msg: String) -> Self; @@ -196,92 +195,14 @@ pub fn file_ext>(path: P) -> OsString { .unwrap_or_default() } -pub fn getxattr(path: P, name: S) -> std::io::Result -where - P: AsRef, - S: AsRef, -{ - let path = path.as_ref().to_cstring()?; - let name = name.as_ref().to_cstring()?; - - /* - * SAFETY: - * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. - * In our implementation, the buffer is checked properly, so that would be safe. - */ - let ret = unsafe { nix::libc::getxattr(path.as_ptr(), name.as_ptr(), null_mut(), 0) }; - if ret == -1 { - return Err(std::io::Error::last_os_error()).rewrite_err(format!( - "Cannot get path {} xattr {}", - path.to_string_lossy(), - name.to_string_lossy() - )); - } - - let mut buf = vec![0; ret as usize]; - let value = buf.as_mut_ptr() as *mut c_void; - - /* - * SAFETY: - * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. - * In our implementation, the buffer is checked properly, so that would be safe. - */ - let ret = unsafe { nix::libc::getxattr(path.as_ptr(), name.as_ptr(), value, buf.len()) }; - if ret == -1 { - return Err(std::io::Error::last_os_error()).rewrite_err(format!( - "Cannot get path {} xattr {}", - path.to_string_lossy(), - name.to_string_lossy(), - )); - } - - let value = CStr::from_bytes_with_nul(&buf[0..ret as usize]) - .expect("asdf") - .to_os_string(); - - Ok(value) -} - -pub fn setxattr(path: P, name: S, value: T) -> std::io::Result<()> -where - P: AsRef, - S: AsRef, - T: AsRef, -{ - let path = path.as_ref().to_cstring()?; - let name = name.as_ref().to_cstring()?; - let value = value.as_ref().to_cstring()?; - let size = value.to_bytes_with_nul().len(); - - /* - * SAFETY: - * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. - * In our implementation, the buffer is checked properly, so that would be safe. - */ - let ret = unsafe { - nix::libc::setxattr( - path.as_ptr(), - name.as_ptr(), - value.as_ptr() as *const c_void, - size, - 0, - ) - }; - if ret == -1 { - return Err(std::io::Error::last_os_error()).rewrite_err(format!( - "Cannot set {} xattr {}", - path.to_string_lossy(), - name.to_string_lossy() - )); - } - - Ok(()) -} - pub fn normalize>(path: P) -> io::Result { let mut new_path = PathBuf::new(); let orig_path = path.as_ref(); + if orig_path.as_os_str().is_empty() { + return Ok(new_path); + } + if orig_path.is_relative() { new_path.push(env::current_dir()?); } @@ -367,10 +288,10 @@ where if !file_type.is_file() { return false; } - return file_path + file_path .extension() .map(|s| s == ext.as_ref()) - .unwrap_or(false); + .unwrap_or(false) }) } @@ -425,13 +346,10 @@ where return false; } if let Some(file_name) = file_path.file_name() { - match options.fuzz { - false => { - return file_name == name.as_ref(); - } - true => { - return file_name.contains(name.as_ref()); - } + if options.fuzz { + return file_name.contains(name.as_ref()); + } else { + return file_name == name.as_ref(); } } false @@ -459,13 +377,10 @@ where return false; } if let Some(file_name) = file_path.file_name() { - match options.fuzz { - false => { - return file_name == name.as_ref(); - } - true => { - return file_name.contains(name.as_ref()); - } + if options.fuzz { + return file_name.contains(name.as_ref()); + } else { + return file_name == name.as_ref(); } } false @@ -526,13 +441,10 @@ where return false; } if let Some(file_name) = file_path.file_name() { - match options.fuzz { - false => { - return file_name == name.as_ref(); - } - true => { - return file_name.contains(name.as_ref()); - } + if options.fuzz { + return file_name.contains(name.as_ref()); + } else { + return file_name == name.as_ref(); } } false diff --git a/syscare-common/src/fs/glob.rs b/syscare-common/src/fs/glob.rs new file mode 100644 index 0000000000000000000000000000000000000000..9c115f42c904e041dd6ff9538171ba3af2bab833 --- /dev/null +++ b/syscare-common/src/fs/glob.rs @@ -0,0 +1,170 @@ +use std::{ + ffi::{OsStr, OsString}, + fs, + io::Result, + path::{Path, PathBuf}, +}; + +use crate::{ffi::OsStrExt, os_str::CharByte}; + +const WILDCARD_ONE: char = '?'; +const WILDCARD_ALL: char = '*'; +const WILDCARD_RECURSIVE: &str = "**"; + +pub struct Glob { + components: Vec, + stack: Vec<(PathBuf, usize)>, // current path, component index +} + +impl Glob { + fn new>(path: P) -> Self { + let match_dir = path.as_ref().to_path_buf(); + let matching = match_dir + .components() + .map(|c| c.as_os_str().to_os_string()) + .collect::>(); + + Glob { + components: matching, + stack: vec![(match_dir, 0)], + } + } + + fn match_chars(name: I, mut pattern: P) -> bool + where + I: Iterator, + P: Iterator, + { + for current in name { + match pattern.next() { + Some(matching) => { + if matching == WILDCARD_ONE { + continue; // matched one char + } + if matching == WILDCARD_ALL { + return true; // matched all chars + } + if current != matching { + return false; // not matched + } + } + None => return false, // pattern not enough + } + } + + true + } + + fn match_component_name(name: &OsStr, component: &OsStr) -> bool { + if !Self::match_chars(name.chars(), component.chars()) { + return false; + } + // If pattern contains "*", we have to do reverse match to + // make sure tail chars were also matched to the pattern. + if component.contains(WILDCARD_ALL) { + return Self::match_chars(name.chars().rev(), component.chars().rev()); + } + + true + } + + fn match_component(&mut self, path: PathBuf, index: usize) -> Result> { + let last_index = self.components.len().saturating_sub(1); + + for dir_entry in fs::read_dir(&path)? { + let next_path = dir_entry?.path(); + let next_name = next_path.file_name().unwrap_or_default(); + + let component = self.components[index].as_os_str(); + if !Self::match_component_name(next_name, component) { + continue; // not matched, skip + } + + if index == last_index { + return Ok(Some(next_path)); + } + + if next_path.is_dir() { + self.stack.push((next_path, index + 1)); + } + } + + Ok(None) + } + + fn match_recursive_wildcard(&mut self, path: PathBuf, index: usize) -> Result<()> { + // push files & directories in current directory to stack + for dir_entry in fs::read_dir(&path)? { + let next_path = dir_entry?.path(); + if next_path.is_dir() { + self.stack.push((next_path, index)); // recursive match, keep index + } + } + // push current path back to stack, match next component + self.stack.push((path, index + 1)); + + Ok(()) + } + + fn match_multiple_wildcard(&mut self, path: PathBuf, index: usize) -> Result<()> { + // push files & directories in current directory to stack + for dir_entry in fs::read_dir(&path)? { + let next_path = dir_entry?.path(); + if next_path.is_dir() { + self.stack.push((next_path, index + 1)); + } + } + // push current path back to stack, match next component + self.stack.push((path, index + 1)); + + Ok(()) + } +} + +impl Iterator for Glob { + type Item = Result; + + fn next(&mut self) -> Option { + while let Some((mut curr_path, mut index)) = self.stack.pop() { + let last_index = self.components.len().saturating_sub(1); + + // iterate all of components over matching path + while index <= last_index { + let component = self.components[index].as_os_str(); + + if component == WILDCARD_RECURSIVE { + if let Err(e) = self.match_recursive_wildcard(curr_path, index) { + return Some(Err(e)); + } + } else if component == OsStr::new(&WILDCARD_ALL.to_string()) { + if let Err(e) = self.match_multiple_wildcard(curr_path, index) { + return Some(Err(e)); + } + } else if component.contains(WILDCARD_ONE) || component.contains(WILDCARD_ALL) { + let result = self.match_component(curr_path, index).transpose(); + if result.is_some() { + return result; + } + } else { + // normal component, push to current path + curr_path.push(component); + + if (index == last_index) && curr_path.exists() { + return Some(Ok(curr_path.clone())); + } + + index += 1; + continue; + } + + break; + } + } + + None + } +} + +pub fn glob>(path: P) -> Glob { + Glob::new(path) +} diff --git a/syscare-common/src/fs/mapped_file.rs b/syscare-common/src/fs/mapped_file.rs deleted file mode 100644 index d05e3076091755a427fad46e7a58ef3c8d1fabb9..0000000000000000000000000000000000000000 --- a/syscare-common/src/fs/mapped_file.rs +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - io::{BufRead, Cursor, Read}, - os::unix::io::AsRawFd, - path::Path, -}; - -use anyhow::{ensure, Context, Result}; -use memmap2::{Mmap, MmapOptions}; - -use super::flock::{FileLock, FileLockType}; - -#[derive(Debug)] -pub struct MappedFile { - _flock: FileLock, - cursor: Cursor, -} - -impl MappedFile { - pub fn open>(path: P) -> Result { - let file_path = path.as_ref(); - ensure!( - !file_path.starts_with("/proc"), - "Mmap does not support procfs" - ); - - let flock = FileLock::new(file_path, FileLockType::Shared)?; - /* - * SAFETY: - * All file-backed memory map constructors are marked unsafe because of the - * potential for Undefined Behavior (UB) using the map if the underlying file - * is subsequently modified, in or out of process. - * Our implementation uses shared file lock to avoid cross-process file access. - * This mapped area would be safe. - */ - let mmap = unsafe { - MmapOptions::new() - .map(flock.as_raw_fd()) - .with_context(|| format!("Failed to mmap file {}", path.as_ref().display()))? - }; - - let cursor = Cursor::new(mmap); - Ok(Self { - _flock: flock, - cursor, - }) - } - - pub fn as_bytes(&self) -> &[u8] { - self.cursor.get_ref() - } -} - -impl Read for MappedFile { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - self.cursor.read(buf) - } -} - -impl BufRead for MappedFile { - fn fill_buf(&mut self) -> std::io::Result<&[u8]> { - self.cursor.fill_buf() - } - - fn consume(&mut self, amt: usize) { - self.cursor.consume(amt) - } -} - -#[test] -fn test() { - use std::{fs::File, io::Read}; - - const PROC_FS_PATH: &str = "/proc/version"; - const FILE_PATH: &str = "/etc/os-release"; - - println!("Testing MappedFile..."); - let mut file_buf = vec![]; - let mut mapped_buf = vec![]; - - let mut normal_file = File::open(FILE_PATH).expect("Failed to open normal file"); - normal_file - .read_to_end(&mut file_buf) - .expect("Failed to read normal file"); - - let mut mapped_file = MappedFile::open(FILE_PATH).expect("Failed to open mapped file"); - mapped_file - .read_to_end(&mut mapped_buf) - .expect("Failed to read mapped file"); - - let _map_procfs_err = - MappedFile::open(PROC_FS_PATH).expect_err("Procfs should not be supported"); - - assert_eq!(file_buf, mapped_buf); - assert_eq!(mapped_buf, mapped_file.as_bytes()); -} diff --git a/syscare-common/src/fs/mmap.rs b/syscare-common/src/fs/mmap.rs new file mode 100644 index 0000000000000000000000000000000000000000..07c9a17d29b382c77a0e2d3e1dcc411a91ba8f28 --- /dev/null +++ b/syscare-common/src/fs/mmap.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Mulan PSL v2 +/* + * Copyright (c) 2024 Huawei Technologies Co., Ltd. + * syscare-common is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use std::{ops::Deref, os::unix::io::AsRawFd, path::Path}; + +use memmap2::{Advice, Mmap, MmapOptions}; + +use super::flock; + +#[derive(Debug)] +pub struct FileMmap { + _lock: flock::FileLock, + mmap: Mmap, +} + +impl Deref for FileMmap { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.mmap + } +} + +pub fn mmap>(file_path: P) -> std::io::Result { + /* + * SAFETY: + * All file-backed memory map constructors are marked unsafe because of the + * potential for Undefined Behavior (UB) using the map if the underlying file + * is subsequently modified, in or out of process. + * Our implementation uses shared file lock to avoid cross-process file access. + * This mapped area would be safe. + */ + let lock = flock::flock_exists(file_path, flock::FileLockType::Shared)?; + let mmap = unsafe { MmapOptions::new().map(lock.as_raw_fd())? }; + mmap.advise(Advice::Random)?; + + Ok(FileMmap { _lock: lock, mmap }) +} + +#[cfg(test)] +mod test { + use std::fs; + + use super::*; + + #[test] + fn mmap_file() -> std::io::Result<()> { + let file_path = std::env::temp_dir().join("mmap_test"); + fs::remove_file(&file_path).ok(); + fs::write(&file_path, "mmap_test")?; + + let orig_file = fs::read(&file_path)?; + let map_file = self::mmap(&file_path)?; + assert_eq!(orig_file, map_file.as_ref()); + + fs::remove_file(&file_path)?; + Ok(()) + } + + #[test] + fn mmap_non_exists_file() { + const NON_EXISTS_FILE: &str = "/non_exists_file"; + fs::remove_file(NON_EXISTS_FILE).ok(); + + assert!(self::mmap(NON_EXISTS_FILE).is_err(),); + } + + #[test] + fn mmap_proc_file() { + const PROC_FS_PATH: &str = "/proc/version"; + assert!(self::mmap(PROC_FS_PATH).is_err(),); + } + + #[test] + fn mmap_sys_file() { + const SYS_FS_PATH: &str = "/sys/kernel/vmcoreinfo"; + assert!(self::mmap(SYS_FS_PATH).is_err(),); + } +} diff --git a/syscare-common/src/fs/mod.rs b/syscare-common/src/fs/mod.rs index 6091455f41fe69e8f07593607a6d9ce0faf5fa21..b6d2d049a488a03b0684993a9335f8eee2528e96 100644 --- a/syscare-common/src/fs/mod.rs +++ b/syscare-common/src/fs/mod.rs @@ -14,8 +14,12 @@ mod flock; mod fs_impl; -mod mapped_file; +mod glob; +mod mmap; +mod xattr; pub use flock::*; pub use fs_impl::*; -pub use mapped_file::*; +pub use glob::*; +pub use mmap::*; +pub use xattr::*; diff --git a/syscare-common/src/fs/xattr.rs b/syscare-common/src/fs/xattr.rs new file mode 100644 index 0000000000000000000000000000000000000000..237c70b97c1a1fa8fde92916203767250979f9ca --- /dev/null +++ b/syscare-common/src/fs/xattr.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Mulan PSL v2 +/* + * Copyright (c) 2024 Huawei Technologies Co., Ltd. + * syscare-common is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use std::{ + ffi::{c_void, CString, OsStr, OsString}, + os::unix::ffi::{OsStrExt, OsStringExt}, + path::Path, + ptr, +}; + +pub fn getxattr(path: P, name: S) -> std::io::Result +where + P: AsRef, + S: AsRef, +{ + let file_path = CString::new(path.as_ref().as_os_str().as_bytes())?; + let xattr_name = CString::new(name.as_ref().as_bytes())?; + + /* + * SAFETY: + * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. + * In our implementation, the buffer is checked properly, so that would be safe. + */ + let buf_size = + unsafe { nix::libc::getxattr(file_path.as_ptr(), xattr_name.as_ptr(), ptr::null_mut(), 0) }; + if buf_size == -1 { + return Err(std::io::Error::last_os_error()); + } + + let mut buf = vec![0; buf_size.unsigned_abs()]; + let value_ptr = buf.as_mut_ptr().cast::(); + + /* + * SAFETY: + * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. + * In our implementation, the buffer is checked properly, so that would be safe. + */ + let bytes_read = unsafe { + nix::libc::getxattr( + file_path.as_ptr(), + xattr_name.as_ptr(), + value_ptr, + buf.len(), + ) + }; + if bytes_read == -1 { + return Err(std::io::Error::last_os_error()); + } + if buf.last() == Some(&0) { + buf.pop(); + } + + Ok(OsString::from_vec(buf)) +} + +pub fn setxattr(path: P, name: S, value: T) -> std::io::Result<()> +where + P: AsRef, + S: AsRef, + T: AsRef, +{ + let file_path = CString::new(path.as_ref().as_os_str().as_bytes())?; + let xattr_name = CString::new(name.as_ref().as_bytes())?; + let xattr_value = CString::new(value.as_ref().as_bytes())?; + let size = xattr_value.to_bytes_with_nul().len(); + + /* + * SAFETY: + * This libc function is marked 'unsafe' as unchecked buffer may cause overflow. + * In our implementation, the buffer is checked properly, so that would be safe. + */ + let ret = unsafe { + nix::libc::setxattr( + file_path.as_ptr(), + xattr_name.as_ptr(), + xattr_value.as_ptr().cast::(), + size, + 0, + ) + }; + if ret == -1 { + return Err(std::io::Error::last_os_error()); + } + + Ok(()) +} diff --git a/syscare-common/src/io/mod.rs b/syscare-common/src/io/mod.rs deleted file mode 100644 index a36f750c98de4bd242b20e14fed7f5cf6f7791e9..0000000000000000000000000000000000000000 --- a/syscare-common/src/io/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -mod os_lines; -mod select; - -pub use os_lines::*; -pub use select::*; diff --git a/syscare-common/src/io/os_lines.rs b/syscare-common/src/io/os_lines.rs deleted file mode 100644 index 08ae0cefff8b46ad34deac59cbca734426ad5ffd..0000000000000000000000000000000000000000 --- a/syscare-common/src/io/os_lines.rs +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ffi::OsString, io::BufRead, os::unix::prelude::OsStringExt}; - -pub struct OsLines { - buf: R, -} - -impl Iterator for OsLines { - type Item = std::io::Result; - - fn next(&mut self) -> Option { - const CHAR_LF: [u8; 1] = [b'\n']; - const CHAR_CR: [u8; 1] = [b'\r']; - - let mut buf = Vec::new(); - match self.buf.read_until(CHAR_LF[0], &mut buf) { - Ok(0) => None, - Ok(_) => { - // Drop "\n" or "\r\n" on the buf tail - if buf.ends_with(&CHAR_LF) { - buf.pop(); - if buf.ends_with(&CHAR_CR) { - buf.pop(); - } - } - buf.shrink_to_fit(); - Some(Ok(OsString::from_vec(buf))) - } - Err(_) => Some(self.buf.read_to_end(&mut buf).map(|_| { - buf.shrink_to_fit(); - OsString::from_vec(buf) - })), - } - } -} - -pub trait BufReadOsLines: BufRead + Sized { - fn os_lines(self) -> OsLines { - OsLines { buf: self } - } -} - -impl BufReadOsLines for R {} - -#[test] -fn test() { - use crate::fs; - use std::io::BufReader; - - let buf_reader = - BufReader::new(fs::open_file("/proc/self/cmdline").expect("Failed to open procfs")); - let lines = buf_reader.lines(); - for str in lines.flatten() { - println!("{}", str); - assert!(!str.is_empty()); - } - - let buf_reader = - BufReader::new(fs::open_file("/proc/self/cmdline").expect("Failed to open procfs")); - let os_lines = buf_reader.os_lines(); - for str in os_lines.flatten() { - println!("{}", str.to_string_lossy()); - assert!(!str.is_empty()); - } -} diff --git a/syscare-common/src/io/select.rs b/syscare-common/src/io/select.rs deleted file mode 100644 index 59e28acddaa07da8cf0edf3eaed93a8110b5a363..0000000000000000000000000000000000000000 --- a/syscare-common/src/io/select.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::{ - os::unix::io::{AsRawFd, RawFd}, - time::Duration, -}; - -use anyhow::Result; -use nix::sys::{ - select::{select, FdSet}, - time::TimeVal, -}; - -pub enum SelectResult { - Readable(RawFd), - Writable(RawFd), - Error(RawFd), -} - -pub struct Select { - fd_set: FdSet, - readfds: FdSet, - writefds: FdSet, - errorfds: FdSet, - timeout: Option, -} - -impl Select { - pub fn new(fds: I) -> Self - where - I: IntoIterator, - F: AsRawFd, - { - Self::with_timeout(fds, None) - } - - pub fn with_timeout(fds: I, timeout: Option) -> Self - where - I: IntoIterator, - F: AsRawFd, - { - let mut fd_set = FdSet::new(); - for fd in fds { - fd_set.insert(fd.as_raw_fd()); - } - let readfds = FdSet::new(); - let writefds = FdSet::new(); - let errorfds = FdSet::new(); - let timeout = timeout - .map(|timeout| TimeVal::new(timeout.as_secs() as i64, timeout.subsec_micros() as i64)); - - Self { - fd_set, - readfds, - writefds, - errorfds, - timeout, - } - } - - pub fn select(&mut self) -> Result + '_> { - self.readfds = self.fd_set; - self.writefds = self.fd_set; - self.errorfds = self.fd_set; - - select( - None, - &mut self.readfds, - &mut self.writefds, - &mut self.errorfds, - &mut self.timeout, - )?; - - let rd_fds = self.readfds.fds(None).map(SelectResult::Readable); - let wr_fds = self.writefds.fds(None).map(SelectResult::Writable); - let err_fds = self.errorfds.fds(None).map(SelectResult::Error); - - Ok(rd_fds.chain(wr_fds).chain(err_fds)) - } -} diff --git a/syscare-common/src/lib.rs b/syscare-common/src/lib.rs index a3188e6da7841457522ec06612c8cb18c543d1d1..b568a2cce73ce7c24c2cee011343bc3c0668484a 100644 --- a/syscare-common/src/lib.rs +++ b/syscare-common/src/lib.rs @@ -14,7 +14,6 @@ pub mod ffi; pub mod fs; -pub mod io; mod macros; pub mod os; pub mod os_str; diff --git a/syscare-common/src/os/cpu.rs b/syscare-common/src/os/cpu.rs index 62baa176572073e4a8a485bed7bd1feebc6c7dbf..9d39bca245ef17afd6e009007e4777e22aa12865 100644 --- a/syscare-common/src/os/cpu.rs +++ b/syscare-common/src/os/cpu.rs @@ -12,39 +12,35 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::OsStr; +use std::ffi::OsString; -use lazy_static::lazy_static; - -use nix::{ - sched::{sched_getaffinity, CpuSet}, - unistd::getpid, -}; +use num_cpus; use super::platform; -pub fn arch() -> &'static OsStr { +pub fn arch() -> OsString { platform::arch() } pub fn num() -> usize { - lazy_static! { - static ref CPU_NUM: usize = { - let cpu_set = sched_getaffinity(getpid()).expect("Failed to get thread CPU affinity"); - let mut cpu_count = 0; - for i in 0..CpuSet::count() { - if cpu_set.is_set(i).expect("Failed to check cpu set") { - cpu_count += 1; - } - } - cpu_count - }; - } - *CPU_NUM + num_cpus::get() } -#[test] -fn test() { - println!("arch: {}", arch().to_string_lossy()); - println!("num: {}", num()) +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_arch() { + let arch = self::arch(); + println!("cpu arch: {}", arch.to_string_lossy()); + assert!(!arch.is_empty()); + } + + #[test] + fn test_num() { + let num = self::num(); + println!("cpu num: {}", num); + assert_ne!(num, 0); + } } diff --git a/syscare-common/src/os/disk.rs b/syscare-common/src/os/disk.rs deleted file mode 100644 index 7ace80490ee9caa5f095424d0531e169a9fab16b..0000000000000000000000000000000000000000 --- a/syscare-common/src/os/disk.rs +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::ffi::OsStr; -use std::path::{Path, PathBuf}; - -use crate::fs; - -#[inline(always)] -fn find_disk, S: AsRef>(directory: P, name: S) -> std::io::Result { - #[inline(always)] - fn __find_disk(directory: &Path, name: &OsStr) -> std::io::Result { - let dev = fs::find_symlink( - directory, - name, - fs::FindOptions { - fuzz: false, - recursive: false, - }, - )?; - fs::canonicalize(dev) - } - - __find_disk(directory.as_ref(), name.as_ref()).map_err(|_| { - std::io::Error::new( - std::io::ErrorKind::NotFound, - format!( - "Cannot find block device by label \"{}\"", - name.as_ref().to_string_lossy() - ), - ) - }) -} - -pub fn find_by_id>(name: S) -> std::io::Result { - find_disk("/dev/disk/by-id", name) -} - -pub fn find_by_label>(name: S) -> std::io::Result { - find_disk("/dev/disk/by-label", name) -} - -pub fn find_by_uuid>(name: S) -> std::io::Result { - find_disk("/dev/disk/by-uuid", name) -} - -pub fn find_by_partuuid>(name: S) -> std::io::Result { - find_disk("/dev/disk/by-partuuid", name) -} - -pub fn find_by_path>(name: S) -> std::io::Result { - find_disk("/dev/disk/by-path", name) -} diff --git a/syscare-common/src/os/grub.rs b/syscare-common/src/os/grub.rs deleted file mode 100644 index 54299d8168429e74a25e18950587a87a758db4f3..0000000000000000000000000000000000000000 --- a/syscare-common/src/os/grub.rs +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::collections::HashMap; -use std::ffi::{OsStr, OsString}; -use std::io::{BufRead, BufReader}; -use std::os::unix::prelude::OsStrExt as StdOsStrExt; -use std::path::{Path, PathBuf}; - -use lazy_static::lazy_static; -use log::debug; -use regex::bytes::Regex; - -use super::{disk, proc_mounts}; -use crate::{ - ffi::OsStrExt, - fs, - io::{BufReadOsLines, OsLines}, -}; - -#[derive(Debug, Clone, Copy)] -enum BootType { - Csm, - Uefi, -} - -#[derive(Debug)] -pub struct GrubMenuEntry { - name: OsString, - root: PathBuf, - kernel: PathBuf, - initrd: PathBuf, -} - -impl GrubMenuEntry { - pub fn get_name(&self) -> &OsStr { - &self.name - } - - pub fn get_root(&self) -> &Path { - &self.root - } - - pub fn get_kernel(&self) -> PathBuf { - // Path is stripped by regular expression, thus, it would always start with '/' - self.root.join(self.kernel.strip_prefix("/").unwrap()) - } - - pub fn get_initrd(&self) -> PathBuf { - // Path is stripped by regular expression, thus, it would always start with '/' - self.root.join(self.initrd.strip_prefix("/").unwrap()) - } -} - -struct GrubConfigParser { - lines: OsLines, - is_matching: bool, - entry_name: Option, - entry_root: Option, - entry_kernel: Option, - entry_initrd: Option, -} - -impl GrubConfigParser { - pub fn new(buf: R) -> Self { - Self { - lines: buf.os_lines(), - is_matching: false, - entry_name: None, - entry_root: None, - entry_kernel: None, - entry_initrd: None, - } - } - - #[inline(always)] - fn parse_name(str: &OsStr) -> Option { - lazy_static! { - static ref RE: Regex = Regex::new(r"'([^']*)'").unwrap(); - } - RE.captures(str.as_bytes()) - .and_then(|captures| captures.get(1)) - .map(|matched| OsStr::from_bytes(matched.as_bytes()).to_os_string()) - } - - #[inline(always)] - fn parse_uuid(str: &OsStr) -> Option { - str.split_whitespace() - .filter_map(|str| { - let arg = str.trim(); - if arg != OsStr::new("search") && !arg.starts_with("--") { - return Some(arg.to_os_string()); - } - None - }) - .next() - } - - #[inline(always)] - fn parse_path(str: &OsStr) -> Option { - lazy_static! { - static ref RE: Regex = Regex::new(r"/\.?\w+([\w\-\.])*").unwrap(); - } - RE.find(str.as_bytes()) - .map(|matched| PathBuf::from(OsStr::from_bytes(matched.as_bytes()))) - } - - #[inline(always)] - fn parse_mount_point(str: &OsStr) -> Option { - let find_dev = Self::parse_uuid(str).and_then(|uuid| disk::find_by_uuid(uuid).ok()); - if let (Some(dev_name), Ok(mounts)) = (find_dev, proc_mounts::Mounts::new()) { - for mount in mounts { - if mount.mount_source == dev_name { - return Some(mount.mount_point); - } - } - } - None - } -} - -impl Iterator for GrubConfigParser { - type Item = GrubMenuEntry; - - fn next(&mut self) -> Option { - for line in (&mut self.lines).flatten() { - if line.starts_with("#") { - continue; - } - - let str = line.trim(); - if str.is_empty() { - continue; - } - - if !self.is_matching { - if str.starts_with("menuentry '") { - self.entry_name = Self::parse_name(str); - self.is_matching = true; - } - continue; - } - if str.starts_with("search") { - self.entry_root = Self::parse_mount_point(str); - } else if str.starts_with("linux") { - self.entry_kernel = Self::parse_path(str); - } else if str.starts_with("initrd") { - self.entry_initrd = Self::parse_path(str); - } else if str.starts_with("}") { - let entry = match ( - &self.entry_name, - &self.entry_root, - &self.entry_kernel, - &self.entry_initrd, - ) { - (Some(name), Some(root), Some(kernel), Some(initrd)) => Some(GrubMenuEntry { - name: name.to_os_string(), - root: root.to_path_buf(), - kernel: kernel.to_path_buf(), - initrd: initrd.to_path_buf(), - }), - _ => None, - }; - self.is_matching = false; - self.entry_name = None; - self.entry_root = None; - self.entry_kernel = None; - self.entry_initrd = None; - - return entry; - } - } - None - } -} - -struct GrubEnvParser { - lines: OsLines, -} - -impl GrubEnvParser { - pub fn new(buf: R) -> Self { - Self { - lines: buf.os_lines(), - } - } -} - -impl Iterator for GrubEnvParser { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option { - for line in (&mut self.lines).flatten() { - if line.starts_with("#") { - continue; - } - - let str = line.trim(); - if str.is_empty() { - continue; - } - - let mut kv = line.split('='); - if let (Some(key), Some(value)) = (kv.next(), kv.next()) { - return Some((key.trim().to_os_string(), value.trim().to_os_string())); - } - } - - None - } -} - -fn get_boot_type() -> BootType { - const UEFI_SYS_INTERFACE: &str = "/sys/firmware/efi"; - - match fs::metadata(UEFI_SYS_INTERFACE) { - Ok(_) => BootType::Uefi, - Err(_) => BootType::Csm, - } -} - -fn get_grub_path(boot_type: BootType) -> PathBuf { - const CSM_GRUB_PATH: &str = "/boot/grub2"; - const UEFI_GRUB_PATH: &str = "/boot/efi/EFI"; - - match boot_type { - BootType::Csm => PathBuf::from(CSM_GRUB_PATH), - BootType::Uefi => PathBuf::from(UEFI_GRUB_PATH), - } -} - -fn find_grub_config>(grub_root: P) -> std::io::Result { - const GRUB_CFG_NAME: &str = "grub.cfg"; - - fs::find_file( - grub_root, - GRUB_CFG_NAME, - fs::FindOptions { - fuzz: false, - recursive: true, - }, - ) -} - -fn find_grub_env>(grub_root: P) -> std::io::Result { - const GRUB_ENV_NAME: &str = "grubenv"; - - fs::find_file( - grub_root, - GRUB_ENV_NAME, - fs::FindOptions { - fuzz: false, - recursive: true, - }, - ) -} - -pub fn read_menu_entries>(grub_root: P) -> std::io::Result> { - let grub_config = find_grub_config(grub_root)?; - - let result = GrubConfigParser::new(BufReader::new(fs::open_file(grub_config)?)).collect(); - - Ok(result) -} - -pub fn read_grub_env>(grub_root: P) -> std::io::Result> { - let grub_env = find_grub_env(grub_root).unwrap(); - - let result = GrubEnvParser::new(BufReader::new(fs::open_file(grub_env)?)).collect(); - - Ok(result) -} - -pub fn get_boot_entry() -> std::io::Result { - let boot_type = get_boot_type(); - let grub_root = get_grub_path(boot_type); - debug!("Boot type: {:?}", boot_type); - - let menu_entries = read_menu_entries(&grub_root)?; - debug!("Boot entries: {:#?}", menu_entries); - - let grub_env = read_grub_env(&grub_root)?; - let default_entry_name = grub_env.get(OsStr::new("saved_entry")).ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::Other, - "Cannot read grub default entry name", - ) - })?; - debug!("Default entry: {:?}", default_entry_name); - - for entry in menu_entries { - if entry.get_name() == default_entry_name { - return Ok(entry); - } - } - - Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Cannot find grub default entry {:?}", default_entry_name), - )) -} diff --git a/syscare-common/src/os/kernel.rs b/syscare-common/src/os/kernel.rs index b89850bdefeff1e0b3664faf108b7a80192244e7..c35516797de71be11b04169e054beb989432e04a 100644 --- a/syscare-common/src/os/kernel.rs +++ b/syscare-common/src/os/kernel.rs @@ -12,51 +12,165 @@ * See the Mulan PSL v2 for more details. */ -use std::{ffi::OsStr, path::Path}; +// SPDX-License-Identifier: Mulan PSL v2 +/* + * Copyright (c) 2024 Huawei Technologies Co., Ltd. + * syscare-common is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use std::{ + collections::HashSet, + ffi::{CStr, CString, OsStr, OsString}, + fs::File, + os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, +}; + +use log::{error, trace}; +use nix::{errno::Errno, kmod}; +use object::{Object, ObjectSection}; -use anyhow::Result; +use super::{platform, selinux}; +use crate::fs; + +const SYS_MODULE_DIR: &str = "/sys/module"; + +pub type KernelModuleInfo = ModuleInfo; +pub type KernelModuleGuard = ModuleGuard; + +#[derive(Debug)] +pub struct ModuleInfo { + pub name: OsString, + pub depends: HashSet, + pub module_path: PathBuf, +} -const KEXEC_PATH: &str = "kexec"; -const SYSTEMCTL_PATH: &str = "systemctl"; +#[derive(Debug)] +pub struct ModuleGuard(ModuleInfo); -use super::platform; -use crate::{concat_os, process::Command}; +impl Drop for ModuleGuard { + fn drop(&mut self) { + if !self.0.module_path.exists() { + return; + } + if let Err(e) = self::remove_module(&self.0.name) { + error!( + "Failed to remove kernel module '{}', {}", + self.0.name.to_string_lossy(), + e.to_string().to_lowercase() + ); + } + } +} -pub fn version() -> &'static OsStr { +pub fn version() -> OsString { platform::release() } -pub fn load(kernel: P, initramfs: Q) -> Result<()> -where - P: AsRef, - Q: AsRef, -{ - Command::new(KEXEC_PATH) - .arg("--load") - .arg(kernel.as_ref()) - .arg(concat_os!("--initrd=", initramfs.as_ref())) - .arg("--reuse-cmdline") - .run_with_output()? - .exit_ok() +pub fn list_modules() -> std::io::Result> { + const LIST_OPTIONS: fs::TraverseOptions = fs::TraverseOptions { recursive: false }; + + let modules = fs::list_dirs(SYS_MODULE_DIR, LIST_OPTIONS)? + .into_iter() + .filter_map(|path| path.file_name().map(OsStr::to_os_string)) + .collect(); + Ok(modules) } -pub fn unload() -> Result<()> { - Command::new(KEXEC_PATH) - .arg("--unload") - .run_with_output()? - .exit_ok() +pub fn relable_module_file>(file_path: P) -> std::io::Result<()> { + const KMOD_FILE_TYPE: &str = "modules_object_t"; + + let file_path = file_path.as_ref(); + let mut context = selinux::get_security_context(file_path)?; + if context.get_type() == KMOD_FILE_TYPE { + return Ok(()); + } + + context.set_type(KMOD_FILE_TYPE)?; + selinux::set_security_context(file_path, &context)?; + + Ok(()) } -pub fn systemd_exec() -> Result<()> { - Command::new(SYSTEMCTL_PATH) - .arg("kexec") - .run_with_output()? - .exit_ok() +pub fn module_info>(file_path: P) -> std::io::Result { + const MODINFO_SECTION_NAME: &str = ".modinfo"; + const MODINFO_NAME_PREFIX: &[u8] = b"name="; + const MODINFO_DEPENDS_PREFIX: &[u8] = b"depends="; + + let file_path = file_path.as_ref().to_path_buf(); + let mmap = fs::mmap(&file_path)?; + let file = object::File::parse(&*mmap).map_err(|_| Errno::ENOEXEC)?; + if file.format() != object::BinaryFormat::Elf { + return Err(std::io::Error::from(Errno::ENOEXEC)); + } + + let data = file + .section_by_name(MODINFO_SECTION_NAME) + .and_then(|section| section.data().ok()) + .ok_or(Errno::ENOEXEC)?; + let name = data + .split(|b| *b == b'\0') + .find_map(|entry| entry.strip_prefix(MODINFO_NAME_PREFIX)) + .map(|slice| OsStr::from_bytes(slice).to_os_string()) + .ok_or(Errno::ENOEXEC)?; + let depends = data + .split(|b| *b == b'\0') + .find_map(|entry| entry.strip_prefix(MODINFO_DEPENDS_PREFIX)) + .unwrap_or_default() + .split(|b| *b == b',') + .filter(|b| !b.is_empty()) + .map(|b| OsStr::from_bytes(b).to_os_string()) + .collect(); + let module_path = Path::new(SYS_MODULE_DIR).join(&name); + + let kmod = ModuleInfo { + name, + depends, + module_path, + }; + Ok(kmod) } -pub fn force_exec() -> Result<()> { - Command::new(KEXEC_PATH) - .arg("--exec") - .run_with_output()? - .exit_ok() +pub fn insert_module>(file_path: P) -> std::io::Result<()> { + const PARAM_VALUES: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; + + let file_path = file_path.as_ref(); + trace!("Inserting kernel module '{}'...", file_path.display()); + + let file = File::open(file_path)?; + kmod::finit_module(&file, PARAM_VALUES, kmod::ModuleInitFlags::empty())?; + + Ok(()) +} + +pub fn insert_module_guarded>(file_path: P) -> std::io::Result { + let file_path = file_path.as_ref(); + + let modinfo = self::module_info(file_path)?; + if !modinfo.module_path.exists() { + self::insert_module(file_path)?; + } + + Ok(ModuleGuard(modinfo)) +} + +pub fn remove_module>(module_name: S) -> std::io::Result<()> { + let module_name = module_name.as_ref(); + trace!( + "Removing kernel module '{}'...", + module_name.to_string_lossy() + ); + + let name = CString::new(module_name.as_bytes())?; + kmod::delete_module(&name, kmod::DeleteModuleFlags::O_NONBLOCK)?; + + Ok(()) } diff --git a/syscare-common/src/os/mod.rs b/syscare-common/src/os/mod.rs index 8e6d5c1a85f2fcd0ccfd8cabfea5ac9a0363c09b..9d3301014befe2fcefc4f77d2022c6b4bbbab88b 100644 --- a/syscare-common/src/os/mod.rs +++ b/syscare-common/src/os/mod.rs @@ -13,12 +13,8 @@ */ pub mod cpu; -pub mod disk; -pub mod grub; pub mod kernel; pub mod platform; -pub mod proc_maps; -pub mod proc_mounts; pub mod process; pub mod selinux; pub mod umask; diff --git a/syscare-common/src/os/platform.rs b/syscare-common/src/os/platform.rs index 8d38b44f1b2bdb92e4f5c7cc3b7b352bf6b3e2b2..178ecd4e645e06a93163c4aa73d05bc35cded41b 100644 --- a/syscare-common/src/os/platform.rs +++ b/syscare-common/src/os/platform.rs @@ -12,59 +12,71 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::OsStr; +use std::ffi::OsString; -use lazy_static::lazy_static; -use nix::sys::utsname::{uname, UtsName}; +use nix::sys::utsname; #[inline(always)] -fn info() -> &'static UtsName { - lazy_static! { - static ref PLATFORM_INFO: UtsName = uname().expect("Failed to get uname"); - } - &PLATFORM_INFO +fn sysinfo() -> utsname::UtsName { + utsname::uname().expect("Failed to get system infomation") } -pub fn hostname() -> &'static OsStr { - info().nodename() +pub fn sysname() -> OsString { + self::sysinfo().sysname().to_os_string() } -pub fn sysname() -> &'static OsStr { - info().sysname() +pub fn hostname() -> OsString { + self::sysinfo().nodename().to_os_string() } -pub fn release() -> &'static OsStr { - info().release() +pub fn release() -> OsString { + self::sysinfo().release().to_os_string() } -pub fn version() -> &'static OsStr { - info().version() +pub fn version() -> OsString { + self::sysinfo().version().to_os_string() } -pub fn arch() -> &'static OsStr { - info().machine() +pub fn arch() -> OsString { + self::sysinfo().machine().to_os_string() } -#[test] -fn test() { - let sysname = sysname(); - let hostname = hostname(); - let release = release(); - let version = version(); - let arch = arch(); +#[cfg(test)] +mod test { + use super::*; - println!("sysname: {}", sysname.to_string_lossy()); - assert!(!sysname.is_empty()); + #[test] + fn test_sysname() { + let sysname = self::sysname(); + println!("sysname: {}", sysname.to_string_lossy()); + assert!(!sysname.is_empty()); + } - println!("hostname: {}", hostname.to_string_lossy()); - assert!(!hostname.is_empty()); + #[test] + fn test_hostname() { + let hostname = self::hostname(); + println!("hostname: {}", hostname.to_string_lossy()); + assert!(!hostname.is_empty()); + } - println!("release: {}", release.to_string_lossy()); - assert!(!release.is_empty()); + #[test] + fn test_release() { + let release = self::release(); + println!("release: {}", release.to_string_lossy()); + assert!(!release.is_empty()); + } - println!("version: {}", version.to_string_lossy()); - assert!(!version.is_empty()); + #[test] + fn test_version() { + let version = self::version(); + println!("version: {}", version.to_string_lossy()); + assert!(!version.is_empty()); + } - println!("arch: {}", arch.to_string_lossy()); - assert!(!arch.is_empty()); + #[test] + fn test_arch() { + let arch = self::arch(); + println!("arch: {}", arch.to_string_lossy()); + assert!(!arch.is_empty()); + } } diff --git a/syscare-common/src/os/proc_maps.rs b/syscare-common/src/os/proc_maps.rs deleted file mode 100644 index 2855a4ae23676e56c56e35185f5c457bc09ac75b..0000000000000000000000000000000000000000 --- a/syscare-common/src/os/proc_maps.rs +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{convert::TryFrom, ffi::OsString, fs::File, io::BufReader}; - -use anyhow::{ensure, Result}; - -use crate::{ - ffi::OsStrExt, - fs, - io::{BufReadOsLines, OsLines}, -}; - -#[derive(Debug)] -pub struct ProcMap { - pub address: OsString, - pub permission: OsString, - pub offset: OsString, - pub dev: OsString, - pub inode: OsString, - pub path_name: OsString, -} - -impl TryFrom for ProcMap { - type Error = anyhow::Error; - - fn try_from(value: OsString) -> std::result::Result { - const MAP_FIELD_NUM: usize = 6; - - let fields = value.split_whitespace().collect::>(); - ensure!( - fields.len() == MAP_FIELD_NUM, - "Failed to parse process mapping" - ); - - Ok(Self { - address: fields[0].to_owned(), - permission: fields[1].to_owned(), - offset: fields[2].to_owned(), - dev: fields[3].to_owned(), - inode: fields[4].to_owned(), - path_name: fields[5].to_owned(), - }) - } -} - -pub struct ProcMaps { - lines: OsLines>, -} - -impl ProcMaps { - pub fn new(pid: i32) -> Result { - let file_path = format!("/proc/{}/maps", pid); - let lines = BufReader::new(fs::open_file(file_path)?).os_lines(); - - Ok(Self { lines }) - } -} - -impl Iterator for ProcMaps { - type Item = ProcMap; - - fn next(&mut self) -> Option { - self.lines - .next() - .and_then(Result::ok) - .map(ProcMap::try_from) - .and_then(Result::ok) - } -} - -#[test] -fn test() { - use super::process; - - for map in ProcMaps::new(process::id()).expect("Failed to read proc maps") { - println!("{:#?}", map); - } -} diff --git a/syscare-common/src/os/proc_mounts.rs b/syscare-common/src/os/proc_mounts.rs deleted file mode 100644 index 1e720bfcc631d637f0113bc487a15df246f6cdb2..0000000000000000000000000000000000000000 --- a/syscare-common/src/os/proc_mounts.rs +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::ffi::{OsStr, OsString}; -use std::fs::File; -use std::io::BufReader; -use std::os::unix::ffi::{OsStrExt as StdOsStrExt, OsStringExt}; -use std::path::PathBuf; - -use anyhow::Result; - -use crate::{ - ffi::OsStrExt, - fs, - io::{BufReadOsLines, OsLines}, -}; - -#[derive(Debug)] -pub struct MountInfo { - pub mount_id: u32, - pub parent_id: u32, - pub device_id: OsString, - pub root: PathBuf, - pub mount_point: PathBuf, - pub mount_opts: Vec, - pub optional: Vec, - pub filesystem: OsString, - pub mount_source: PathBuf, - pub super_opts: Vec, -} - -struct MountInfoParser<'a> { - data: &'a [u8], - num: usize, - pos: usize, -} - -impl<'a> Iterator for MountInfoParser<'a> { - type Item = &'a OsStr; - - fn next(&mut self) -> Option { - const OPTION_INDEX: usize = 6; - const NORMAL_SPLITTER: char = ' '; - const OPTION_SPLITTER: char = '-'; - - let data = &self.data[self.pos..]; - let new_str = OsStr::from_bytes(data); - if new_str.is_empty() { - return None; - } - - for (index, char) in new_str.char_indices() { - let pattern; - let skip_len; - - match self.num { - OPTION_INDEX => { - pattern = OPTION_SPLITTER; - skip_len = 2; - } - _ => { - pattern = NORMAL_SPLITTER; - skip_len = 1; - } - }; - if char == pattern { - self.num += 1; - self.pos += index + skip_len; - - return Some(OsStr::from_bytes(&data[..index])); - } - } - - self.pos = self.data.len() - 1; - Some(OsStr::from_bytes(data)) - } -} - -pub struct Mounts { - lines: OsLines>, -} - -impl Mounts { - pub fn new() -> Result { - const MOUNTINFO_PATH: &str = "/proc/self/mountinfo"; - - Ok(Self { - lines: BufReader::new(fs::open_file(MOUNTINFO_PATH)?).os_lines(), - }) - } -} - -impl Mounts { - fn parse_line(str: OsString) -> Option { - const VALUE_SPLITTER: char = ','; - - let str_bytes = str.into_vec(); - let mut iter = MountInfoParser { - data: &str_bytes, - num: 0, - pos: 0, - }; - - Some(MountInfo { - mount_id: iter.next()?.to_string_lossy().parse::().ok()?, - parent_id: iter.next()?.to_string_lossy().parse::().ok()?, - device_id: iter.next()?.to_os_string(), - root: PathBuf::from(iter.next()?), - mount_point: PathBuf::from(iter.next()?), - mount_opts: iter - .next()? - .split(VALUE_SPLITTER) - .map(OsStrExt::trim) - .map(OsString::from) - .collect::>(), - optional: iter - .next()? - .split_whitespace() - .map(OsString::from) - .collect::>(), - filesystem: iter.next()?.to_os_string(), - mount_source: PathBuf::from(iter.next()?), - super_opts: iter - .next()? - .split(VALUE_SPLITTER) - .map(OsStrExt::trim) - .map(OsString::from) - .collect::>(), - }) - } -} - -impl Iterator for Mounts { - type Item = MountInfo; - - fn next(&mut self) -> Option { - self.lines.next()?.ok().and_then(Self::parse_line) - } -} - -#[test] -fn test() { - let mount_info = Mounts::new().expect("Failed to read mount info"); - for mount in mount_info { - println!(); - println!("{:#?}", mount); - assert!(mount.mount_point.exists()) - } -} diff --git a/syscare-common/src/os/process.rs b/syscare-common/src/os/process.rs index 2cf4ec9547fce017f9bf94d70708a2a630c68e46..fc8c6cd0146d49c2a183aff2421782b6ff9e6ea5 100644 --- a/syscare-common/src/os/process.rs +++ b/syscare-common/src/os/process.rs @@ -13,31 +13,48 @@ */ use std::ffi::{OsStr, OsString}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; -use lazy_static::lazy_static; -use nix::unistd::getpid; - -use crate::fs; +use nix::errno::Errno; +use nix::unistd; pub fn id() -> i32 { - lazy_static! { - static ref PROCESS_ID: i32 = getpid().as_raw(); - } - *PROCESS_ID + unistd::getpid().as_raw() } -pub fn path() -> &'static Path { - lazy_static! { - static ref PROCESS_PATH: PathBuf = - std::env::current_exe().expect("Read process path failed"); - } - PROCESS_PATH.as_path() +pub fn path() -> std::io::Result { + std::env::current_exe() +} + +pub fn name() -> std::io::Result { + self::path()? + .file_name() + .map(OsStr::to_os_string) + .ok_or(std::io::Error::from(Errno::EINVAL)) } -pub fn name() -> &'static OsStr { - lazy_static! { - static ref PROCESS_NAME: OsString = fs::file_name(path()); +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_id() { + let pid = self::id(); + println!("pid: {}", pid); + assert!(pid > 1) + } + + #[test] + fn test_path() { + let path = self::path().expect("Failed to get executable path"); + println!("path: {}", path.display()); + assert!(path.exists()); + } + + #[test] + fn test_name() { + let name = self::name().expect("Failed to get executable name"); + println!("name: {}", name.to_string_lossy()); + assert!(!name.is_empty()); } - PROCESS_NAME.as_os_str() } diff --git a/syscare-common/src/os/selinux.rs b/syscare-common/src/os/selinux.rs index cf3b5b226d2a0da8524c191f0f651106ea1c29da..e1aa73ea569f0b2ca7ad0535b5318400c5d3863c 100644 --- a/syscare-common/src/os/selinux.rs +++ b/syscare-common/src/os/selinux.rs @@ -12,113 +12,304 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::OsString; -use std::os::unix::ffi::OsStringExt as UnixOsStringExt; -use std::path::Path; +use std::{ + ffi::{OsStr, OsString}, + fmt::Debug, + os::unix::ffi::{OsStrExt, OsStringExt}, + path::Path, +}; -use anyhow::{bail, ensure, Context, Result}; +use nix::errno::Errno; -use crate::{concat_os, ffi::OsStrExt, fs}; +use crate::fs; const SELINUX_SYS_FILE: &str = "/sys/fs/selinux/enforce"; -const SELINUX_XATTR_NAME: &str = "security.selinux"; -const SELINUX_XATTR_SPLITTER: &str = ":"; -const SECURITY_XATTR_LEN: usize = 4; +const SELINUX_STATUS_PERMISSIVE: &str = "0"; +const SELINUX_STATUS_ENFORCING: &str = "1"; + +const SECURITY_CONTEXT_XATTR_NAME: &str = "security.selinux"; +const SECURITY_CONTEXT_SPLITER: u8 = b':'; +const SECURITY_CONTEXT_SPLITER_COUNT: usize = 3; +const SECURITY_CONTEXT_ATTR_COUNT: usize = SECURITY_CONTEXT_SPLITER_COUNT + 1; + +const SECURITY_CONTEXT_USER_INDEX: usize = 0; +const SECURITY_CONTEXT_ROLE_INDEX: usize = 1; +const SECURITY_CONTEXT_TYPE_INDEX: usize = 2; +const SECURITY_CONTEXT_LEVEL_INDEX: usize = 3; + +pub type SELinuxStatus = Status; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Status { + Disabled, Permissive, Enforcing, - Disabled, } impl std::fmt::Display for Status { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{:?}", self)) + write!(f, "{:?}", self) } } -pub fn get_status() -> Result { - if !Path::new(SELINUX_SYS_FILE).is_file() { - return Ok(Status::Disabled); +pub fn get_status() -> Status { + let value = fs::read_to_string(SELINUX_SYS_FILE).unwrap_or_default(); + match value.as_str() { + SELINUX_STATUS_PERMISSIVE => Status::Permissive, + SELINUX_STATUS_ENFORCING => Status::Enforcing, + _ => Status::Disabled, } +} - let value = OsString::from_vec(fs::read(SELINUX_SYS_FILE)?) - .to_string_lossy() - .parse::() - .context("Failed to parse selinux status")?; +pub fn set_status(value: Status) -> std::io::Result<()> { + let contents = match value { + Status::Permissive => SELINUX_STATUS_PERMISSIVE, + Status::Enforcing => SELINUX_STATUS_ENFORCING, + _ => return Err(std::io::Error::from(Errno::EINVAL)), + }; + fs::write(SELINUX_SYS_FILE, contents)?; - Ok(match value { - 0 => Status::Permissive, - 1 => Status::Enforcing, - _ => Status::Disabled, - }) + Ok(()) } -pub fn set_status(value: Status) -> Result<()> { - if (value != Status::Permissive) && (value != Status::Enforcing) { - bail!("Status {} is invalid", value); +#[derive(Clone, PartialEq, Eq)] +pub struct SecurityContext(OsString); + +impl SecurityContext { + fn get_attribute(&self, index: usize) -> &OsStr { + self.0 + .as_bytes() + .splitn(SECURITY_CONTEXT_ATTR_COUNT, |&b| { + b == SECURITY_CONTEXT_SPLITER + }) + .nth(index) + .map(OsStr::from_bytes) + .expect("Unexpected security context format") } - fs::write(SELINUX_SYS_FILE, value.to_string())?; - Ok(()) + fn set_attribute>(&mut self, index: usize, value: S) -> std::io::Result<()> { + let value = value.as_ref().as_bytes(); + + if value.is_empty() { + return Err(std::io::Error::from(Errno::EINVAL)); + } + if (index != SECURITY_CONTEXT_LEVEL_INDEX) && (value.contains(&SECURITY_CONTEXT_SPLITER)) { + return Err(std::io::Error::from(Errno::EINVAL)); + } + let attrs = self.0.as_bytes().splitn(SECURITY_CONTEXT_ATTR_COUNT, |&b| { + b == SECURITY_CONTEXT_SPLITER + }); + + let mut new_context = Vec::new(); + for (i, attr) in attrs.enumerate() { + new_context.extend_from_slice(if i != index { attr } else { value }); + new_context.push(SECURITY_CONTEXT_SPLITER); + } + new_context.pop(); + + self.0 = OsString::from_vec(new_context); + Ok(()) + } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SecurityContext { - pub user: OsString, - pub role: OsString, - pub kind: OsString, - pub level: OsString, +impl SecurityContext { + pub fn parse>(value: S) -> std::io::Result { + let context = value.as_ref(); + if context.is_empty() { + return Err(std::io::Error::from(Errno::EINVAL)); + } + + let spliter_count = context + .as_bytes() + .iter() + .filter(|&b| *b == SECURITY_CONTEXT_SPLITER) + .count(); + if spliter_count < SECURITY_CONTEXT_SPLITER_COUNT { + return Err(std::io::Error::from(Errno::EINVAL)); + } + + Ok(Self(context.to_os_string())) + } + + pub fn get_user(&self) -> &OsStr { + self.get_attribute(SECURITY_CONTEXT_USER_INDEX) + } + + pub fn get_role(&self) -> &OsStr { + self.get_attribute(SECURITY_CONTEXT_ROLE_INDEX) + } + + pub fn get_type(&self) -> &OsStr { + self.get_attribute(SECURITY_CONTEXT_TYPE_INDEX) + } + + pub fn get_level(&self) -> &OsStr { + self.get_attribute(SECURITY_CONTEXT_LEVEL_INDEX) + } + + pub fn set_user>(&mut self, value: S) -> std::io::Result<()> { + self.set_attribute(SECURITY_CONTEXT_USER_INDEX, value) + } + + pub fn set_role>(&mut self, value: S) -> std::io::Result<()> { + self.set_attribute(SECURITY_CONTEXT_ROLE_INDEX, value) + } + + pub fn set_type>(&mut self, value: S) -> std::io::Result<()> { + self.set_attribute(SECURITY_CONTEXT_TYPE_INDEX, value) + } + + pub fn set_level>(&mut self, value: S) -> std::io::Result<()> { + self.set_attribute(SECURITY_CONTEXT_LEVEL_INDEX, value) + } + + pub fn as_os_str(&self) -> &OsStr { + &self.0 + } } -impl AsRef for SecurityContext { - fn as_ref(&self) -> &SecurityContext { - self +impl Debug for SecurityContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) } } -pub fn get_security_context

(file_path: P) -> Result +impl AsRef for SecurityContext { + fn as_ref(&self) -> &OsStr { + self.as_os_str() + } +} + +pub fn get_security_context

(file_path: P) -> std::io::Result where P: AsRef, { - let value = fs::getxattr(file_path, SELINUX_XATTR_NAME)?; - let data = value.split(SELINUX_XATTR_SPLITTER).collect::>(); - ensure!( - data.len() == SECURITY_XATTR_LEN, - "Failed to parse selinux security context" - ); - - Ok(SecurityContext { - user: data[0].to_os_string(), - role: data[1].to_os_string(), - kind: data[2].to_os_string(), - level: data[3].to_os_string(), - }) + SecurityContext::parse(fs::getxattr(file_path, SECURITY_CONTEXT_XATTR_NAME)?) } -pub fn set_security_context(file_path: P, value: S) -> Result<()> +pub fn set_security_context

(file_path: P, value: &SecurityContext) -> std::io::Result<()> where P: AsRef, - S: AsRef, { - let old_value = get_security_context(&file_path)?; - let new_value = value.as_ref(); + fs::setxattr(file_path, SECURITY_CONTEXT_XATTR_NAME, value) +} + +#[cfg(test)] +mod test { + use std::{fs, path::Path}; - if &old_value == new_value { - return Ok(()); + use super::*; + + #[test] + fn test_selinux_status() { + let status = self::get_status(); + println!("SELinux status: {}", status); + + let sys_file = Path::new(self::SELINUX_SYS_FILE); + if sys_file.exists() { + assert!(self::get_status() == Status::Disabled); + } else { + assert!(self::get_status() == Status::Disabled); + } + assert!(self::set_status(Status::Disabled).is_err()); } - let new_context = concat_os!( - &new_value.user, - SELINUX_XATTR_SPLITTER, - &new_value.role, - SELINUX_XATTR_SPLITTER, - &new_value.kind, - SELINUX_XATTR_SPLITTER, - &new_value.level, - ); - fs::setxattr(&file_path, SELINUX_XATTR_NAME, new_context)?; + #[test] + fn test_security_context_parse() { + const TEST_CASES: &[&str] = &[ + "system_u:object_r:bin_t:s0", + "user_u:role_r:type_t:s0:c1,c2", + "user.dom:role-1:type_x:s0:c1.c2", + "a:b:c:d.e-f,g", + "a_:b_:c_:d_", + ]; + for str in TEST_CASES { + let result = SecurityContext::parse(str).is_ok(); + assert!(result, "Failed to parse security context '{}'", str); + } + } - Ok(()) + #[test] + fn test_security_context_get() { + const TEST_CASES: &[(&str, [&str; 4])] = &[ + ( + "system_u:object_r:bin_t:s0", + ["system_u", "object_r", "bin_t", "s0"], + ), + ( + "user_u:role_r:type_t:s0:c1,c2", + ["user_u", "role_r", "type_t", "s0:c1,c2"], + ), + ( + "user.dom:role-1:type_x:s0:c1.c2", + ["user.dom", "role-1", "type_x", "s0:c1.c2"], + ), + ("a:b:c:d.e-f,g", ["a", "b", "c", "d.e-f,g"]), + ("a_:b_:c_:d_", ["a_", "b_", "c_", "d_"]), + ]; + + for (case, attrs) in TEST_CASES { + let context = SecurityContext::parse(case).expect("Failed to parse security context"); + assert_eq!(context.get_user(), attrs[0]); + assert_eq!(context.get_role(), attrs[1]); + assert_eq!(context.get_type(), attrs[2]); + assert_eq!(context.get_level(), attrs[3]); + } + } + + #[test] + fn test_security_context_set() { + const DEFAULT_CONTEXT: &str = "unconfined_u:object_r:default_t:s0"; + const TEST_CASES: &[(&str, [&str; 4])] = &[ + ( + "system_u:object_r:bin_t:s0", + ["system_u", "object_r", "bin_t", "s0"], + ), + ( + "user_u:role_r:type_t:s0:c1,c2", + ["user_u", "role_r", "type_t", "s0:c1,c2"], + ), + ( + "user.dom:role-1:type_x:s0:c1.c2", + ["user.dom", "role-1", "type_x", "s0:c1.c2"], + ), + ("a:b:c:d.e-f,g", ["a", "b", "c", "d.e-f,g"]), + ("a_:b_:c_:d_", ["a_", "b_", "c_", "d_"]), + ]; + + for (result, attrs) in TEST_CASES { + let mut context = + SecurityContext::parse(DEFAULT_CONTEXT).expect("Failed to parse security context"); + assert!(context.set_user(attrs[0]).is_ok()); + assert!(context.set_role(attrs[1]).is_ok()); + assert!(context.set_type(attrs[2]).is_ok()); + assert!(context.set_level(attrs[3]).is_ok()); + println!("{:?}", context); + + assert_eq!(context.as_os_str(), *result); + } + } + + #[test] + fn test_get_set_security_context() { + const TEST_FILE: &str = "selinux_test"; + const TEST_CONTEXT: &str = "unconfined_u:object_r:default_t:s0"; + + let file_path = std::env::temp_dir().join(TEST_FILE); + + fs::remove_file(&file_path).ok(); + fs::write(&file_path, TEST_FILE).expect("Failed to write test file"); + + let set_context = SecurityContext::parse(TEST_CONTEXT).expect("Invalid security context"); + self::set_security_context(&file_path, &set_context) + .expect("Failed to set security context"); + println!("set context: {:#?}", set_context); + + let get_context = + self::get_security_context(&file_path).expect("Failed to get security context"); + println!("get context: {:#?}", get_context); + + assert_eq!(set_context, get_context); + fs::remove_file(&file_path).expect("Failed to remove test file"); + } } diff --git a/syscare-common/src/os/umask.rs b/syscare-common/src/os/umask.rs index ab624cf10ae7c975e43801330ea7ba049476835c..cb0d3ee620dde66933a08acf644b457c6e810769 100644 --- a/syscare-common/src/os/umask.rs +++ b/syscare-common/src/os/umask.rs @@ -12,45 +12,49 @@ * See the Mulan PSL v2 for more details. */ -use nix::sys::stat::{umask, Mode}; +use nix::{libc::mode_t, sys::stat}; -pub fn set_umask(mode: u32) -> u32 { - umask(Mode::from_bits_truncate(mode)).bits() +pub fn set_umask(mode: mode_t) -> mode_t { + stat::umask(stat::Mode::from_bits_truncate(mode)).bits() } -#[test] -fn test() { - use std::{fs, fs::File, os::unix::fs::PermissionsExt}; - - const FILE_PATH: &str = "/tmp/umask_test"; - const UMASK1: u32 = 0o077; // 10600 - const UMASK2: u32 = 0o022; // 10644 - - fs::remove_file(FILE_PATH).ok(); - - println!("Testing umask {:03o}...", UMASK1); - set_umask(UMASK1); - let file1 = File::create(FILE_PATH).expect("Failed to create file"); - let perm1 = file1 - .metadata() - .map(|s| s.permissions()) - .expect("Failed to read file permission"); - - println!("umask: {:03o}, perm: {:05o}", UMASK1, perm1.mode()); - - drop(file1); - fs::remove_file(FILE_PATH).ok(); - - println!("Testing umask {:03o}...", UMASK2); - set_umask(UMASK2); - let file2 = File::create(FILE_PATH).expect("Failed to create file"); - let perm2 = file2 - .metadata() - .map(|s| s.permissions()) - .expect("Failed to read file permission"); - - println!("umask: {:03o}, perm: {:05o}", UMASK2, perm2.mode()); - - drop(file2); - fs::remove_file(FILE_PATH).ok(); +#[cfg(test)] +mod test { + #[test] + fn test_set_umask() { + use std::{fs, fs::File, os::unix::fs::PermissionsExt}; + + let file_path = std::env::temp_dir().join("umask_test"); + const UMASK1: u32 = 0o077; // 10600 + const UMASK2: u32 = 0o022; // 10644 + + fs::remove_file(&file_path).ok(); + + println!("Testing umask {:03o}...", UMASK1); + super::set_umask(UMASK1); + let file1 = File::create(&file_path).expect("Failed to create file"); + let perm1 = file1 + .metadata() + .map(|s| s.permissions()) + .expect("Failed to read file permission"); + println!("umask: {:03o}, perm: {:05o}", UMASK1, perm1.mode()); + assert_eq!(perm1.mode() & 0o777, 0o600); + + drop(file1); + fs::remove_file(&file_path).expect("Failed to remove file"); + + println!("Testing umask {:03o}...", UMASK2); + super::set_umask(UMASK2); + let file2 = File::create(&file_path).expect("Failed to create file"); + let perm2 = file2 + .metadata() + .map(|s| s.permissions()) + .expect("Failed to read file permission"); + + println!("umask: {:03o}, perm: {:05o}", UMASK2, perm2.mode()); + assert_eq!(perm2.mode() & 0o777, 0o644); + + drop(file2); + fs::remove_file(&file_path).expect("Failed to remove file"); + } } diff --git a/syscare-common/src/os/user.rs b/syscare-common/src/os/user.rs index ebf2a149aa924bb265e99c043256b2ac93db3cd8..1e79cdb877fdc6c82694e22100082d2e08874ea9 100644 --- a/syscare-common/src/os/user.rs +++ b/syscare-common/src/os/user.rs @@ -12,71 +12,94 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::OsStr; -use std::path::Path; +use std::{ffi::OsString, os::unix::ffi::OsStringExt, path::PathBuf}; -use lazy_static::*; -use nix::unistd::{getuid, User}; - -use crate::ffi::CStrExt; +use nix::unistd; #[inline(always)] -fn info() -> &'static User { - lazy_static! { - static ref USER: User = User::from_uid(getuid()) - .expect("Failed to read user info") - .unwrap(); - } - &USER +fn userinfo() -> unistd::User { + unistd::User::from_uid(unistd::getuid()) + .expect("Failed to get user infomation") + .expect("Invalid user id") } -pub fn name() -> &'static str { - info().name.as_str() +pub fn uid() -> u32 { + unistd::getuid().as_raw() } -pub fn passwd() -> &'static OsStr { - info().passwd.as_os_str() +pub fn gid() -> u32 { + unistd::getgid().as_raw() } -pub fn id() -> u32 { - info().uid.as_raw() +pub fn name() -> String { + self::userinfo().name } -pub fn gid() -> u32 { - info().gid.as_raw() +pub fn passwd() -> OsString { + OsString::from_vec(self::userinfo().passwd.into_bytes()) } -pub fn gecos() -> &'static OsStr { - info().gecos.as_os_str() +pub fn gecos() -> OsString { + OsString::from_vec(self::userinfo().gecos.into_bytes()) } -pub fn home() -> &'static Path { - info().dir.as_path() +pub fn home() -> PathBuf { + self::userinfo().dir } -pub fn shell() -> &'static Path { - info().shell.as_path() +pub fn shell() -> PathBuf { + self::userinfo().shell } -#[test] -fn test() { - println!("name: {}", name()); - assert!(!name().is_empty()); +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_uid() { + let uid = self::uid(); + println!("uid: {}", uid); + assert!(uid < u32::MAX); + } - println!("passwd: {}", passwd().to_string_lossy()); - assert!(!passwd().is_empty()); + #[test] + fn test_gid() { + let gid = self::gid(); + println!("gid: {}", gid); + assert!(gid < u32::MAX); + } - println!("id: {}", id()); - assert!(id() > 0); + #[test] + fn test_name() { + let name = self::name(); + println!("name: {}", name); + assert!(!name.is_empty()); + } - println!("gid: {}", gid()); - assert!(gid() > 0); + #[test] + fn test_passwd() { + let passwd = self::passwd(); + println!("passwd: {}", passwd.to_string_lossy()); + assert!(!passwd.is_empty()); + } - println!("gecos: {}", gecos().to_string_lossy()); + #[test] + fn test_gecos() { + let gecos = self::gecos(); + println!("gecos: {}", gecos.to_string_lossy()); + } - println!("home: {}", home().display()); - assert!(home().exists()); + #[test] + fn test_home() { + let home = self::home(); + println!("home: {}", home.display()); + assert!(home.exists()); + } - println!("shell: {}", shell().display()); - assert!(shell().exists()); + #[test] + fn test_shell() { + let shell = self::shell(); + println!("shell: {}", shell.display()); + assert!(shell.exists()); + } } diff --git a/syscare-common/src/os_str/char_byte.rs b/syscare-common/src/os_str/char_byte.rs new file mode 100644 index 0000000000000000000000000000000000000000..3a8749e27cc07b0826f95e50ac79b2fa9dc879a1 --- /dev/null +++ b/syscare-common/src/os_str/char_byte.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Mulan PSL v2 +/* +* Copyright (c) 2024 Huawei Technologies Co., Ltd. +* syscare-common is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/* CharByte */ + +use std::{ffi::OsString, iter::FromIterator, ops::Deref, os::unix::ffi::OsStringExt}; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum CharByte { + Byte(char), // save u8, but use char as storage + Char(char), +} + +impl CharByte { + pub fn as_char(&self) -> &char { + match self { + CharByte::Char(c) => c, + CharByte::Byte(b) => b, + } + } + + pub fn into_char(self) -> char { + match self { + CharByte::Char(c) => c, + CharByte::Byte(b) => b, + } + } +} + +impl Default for CharByte { + fn default() -> Self { + CharByte::from(0) + } +} + +impl From for CharByte { + fn from(value: u8) -> Self { + CharByte::Byte(char::from(value)) + } +} + +impl From for CharByte { + fn from(value: char) -> Self { + CharByte::Char(value) + } +} + +impl PartialEq for CharByte { + fn eq(&self, other: &char) -> bool { + self.as_char().eq(other) + } +} + +impl PartialEq for char { + fn eq(&self, other: &CharByte) -> bool { + self.eq(other.as_char()) + } +} + +impl PartialOrd for CharByte { + fn partial_cmp(&self, other: &char) -> Option { + self.as_char().partial_cmp(other) + } +} + +impl PartialOrd for char { + fn partial_cmp(&self, other: &CharByte) -> Option { + self.partial_cmp(other.as_char()) + } +} + +impl FromIterator for OsString { + fn from_iter>(iter: T) -> Self { + let buf = iter.into_iter().fold(Vec::new(), |mut buf, char_byte| { + match char_byte { + CharByte::Char(c) => buf.extend(c.to_string().as_bytes()), + CharByte::Byte(b) => buf.push(b as u8), + } + buf + }); + OsString::from_vec(buf) + } +} + +impl Deref for CharByte { + type Target = char; + + fn deref(&self) -> &Self::Target { + self.as_char() + } +} + +impl std::fmt::Debug for CharByte { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CharByte::Char(c) => write!(f, "{}", c), + CharByte::Byte(b) => write!(f, "\\x{:X}", *b as u8), + } + } +} + +impl std::fmt::Display for CharByte { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +} diff --git a/syscare-common/src/os_str/iter.rs b/syscare-common/src/os_str/iter.rs index db48771d60902b2d0360405969400daedb1ef991..e472d0ca568f44d08a85b84dbc6c7a2f9495f861 100644 --- a/syscare-common/src/os_str/iter.rs +++ b/syscare-common/src/os_str/iter.rs @@ -12,65 +12,105 @@ * See the Mulan PSL v2 for more details. */ -use std::{ffi::OsStr, os::unix::ffi::OsStrExt as StdOsStrExt}; +use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; use super::{ + char_byte::CharByte, pattern::{Pattern, Searcher}, utf8, }; -/* CharByteIndices */ -pub struct CharIndices<'a> { +/* Chars */ +pub struct Chars<'a> { pub(crate) char_bytes: &'a [u8], pub(crate) front_idx: usize, pub(crate) back_idx: usize, } -impl Iterator for CharIndices<'_> { - type Item = (usize, char); +impl Iterator for Chars<'_> { + type Item = CharByte; fn next(&mut self) -> Option { + if self.front_idx >= self.char_bytes.len() { + return None; + } + let char_bytes = &self.char_bytes[self.front_idx..]; - if char_bytes.is_empty() { + if let Some((len, c)) = utf8::next_valid_char(char_bytes) { + self.front_idx += len; + return Some(CharByte::from(c)); + } + + // Unable to parse utf-8 char, fallback to byte + self.front_idx += 1; + Some(CharByte::from(char_bytes[0])) + } +} + +impl DoubleEndedIterator for Chars<'_> { + fn next_back(&mut self) -> Option { + if self.back_idx == 0 { return None; } - match utf8::next_valid_char(char_bytes) { - Some((len, c)) => { - let result = (self.front_idx, c); - self.front_idx += len; + let char_bytes = &self.char_bytes[..self.back_idx]; + if let Some((len, c)) = utf8::next_back_valid_char(char_bytes) { + self.back_idx -= len; + return Some(CharByte::from(c)); + } - Some(result) - } - None => { - // Unable to parse utf-8 char, fallback to byte - let result = (self.front_idx, char_bytes[0] as char); - self.front_idx += 1; + // Unable to parse utf-8 char, fallback to byte + self.back_idx -= 1; + char_bytes.last().map(|&b| CharByte::from(b)) + } +} - Some(result) - } +/* CharByteIndices */ +pub struct CharIndices<'a> { + pub(crate) char_bytes: &'a [u8], + pub(crate) front_idx: usize, + pub(crate) back_idx: usize, +} + +impl Iterator for CharIndices<'_> { + type Item = (usize, CharByte); + + fn next(&mut self) -> Option { + if self.front_idx >= self.char_bytes.len() { + return None; } + + let char_bytes = &self.char_bytes[self.front_idx..]; + if let Some((len, c)) = utf8::next_valid_char(char_bytes) { + let result = (self.front_idx, CharByte::from(c)); + self.front_idx += len; + return Some(result); + } + + // Unable to parse utf-8 char, fallback to byte + let result = (self.front_idx, CharByte::from(char_bytes[0])); + self.front_idx += 1; + Some(result) } } impl DoubleEndedIterator for CharIndices<'_> { fn next_back(&mut self) -> Option { - let char_bytes = &self.char_bytes[..self.back_idx]; - if char_bytes.is_empty() { + if self.back_idx == 0 { return None; } - match utf8::next_back_valid_char(char_bytes) { - Some((len, c)) => { - self.back_idx -= len; - Some((self.back_idx, c)) - } - None => { - // Unable to parse utf-8 char, fallback to byte - self.back_idx -= 1; - Some((self.back_idx, *char_bytes.last().unwrap() as char)) - } + let char_bytes = &self.char_bytes[..self.back_idx]; + if let Some((len, c)) = utf8::next_back_valid_char(char_bytes) { + self.back_idx -= len; + return Some((self.back_idx, CharByte::from(c))); } + + // Unable to parse utf-8 char, fallback to byte + self.back_idx -= 1; + char_bytes + .last() + .map(|&b| (self.back_idx, CharByte::from(b))) } } @@ -125,7 +165,7 @@ impl<'a, P: Pattern<'a>> SplitImpl<'a, P> { } self.finished = true; - return Some(OsStr::from_bytes(&haystack[self.position..])); + Some(OsStr::from_bytes(&haystack[self.position..])) } } diff --git a/syscare-common/src/os_str/mod.rs b/syscare-common/src/os_str/mod.rs index 11612f698cf91c77bf07a07cc6608d5c8fd71fab..48319948e32b963f57fafbab7b3ac6e29dd7d6f6 100644 --- a/syscare-common/src/os_str/mod.rs +++ b/syscare-common/src/os_str/mod.rs @@ -12,8 +12,10 @@ * See the Mulan PSL v2 for more details. */ +mod char_byte; mod iter; pub mod pattern; mod utf8; +pub use char_byte::*; pub use iter::*; diff --git a/syscare-common/src/os_str/pattern.rs b/syscare-common/src/os_str/pattern.rs index 6e0c1f010f57208d06210bad371e74dbe4348d24..4a878d3596ed0b0bf9d913e8fe23feaef07b15d2 100644 --- a/syscare-common/src/os_str/pattern.rs +++ b/syscare-common/src/os_str/pattern.rs @@ -108,9 +108,10 @@ impl<'a> Searcher<'a> for CharLiteralSearcher<'a> { match self.indices.next() { Some((char_idx, c)) => { let new_idx = char_idx + c.len_utf8(); - match self.literals.contains(&c) { - true => SearchStep::Match(char_idx, new_idx), - false => SearchStep::Reject(char_idx, new_idx), + if self.literals.contains(&c) { + SearchStep::Match(char_idx, new_idx) + } else { + SearchStep::Reject(char_idx, new_idx) } } None => SearchStep::Done, @@ -123,9 +124,10 @@ impl<'a> ReverseSearcher<'a> for CharLiteralSearcher<'a> { match self.indices.next_back() { Some((char_idx, c)) => { let new_idx = char_idx + c.len_utf8(); - match self.literals.contains(&c) { - true => SearchStep::Match(char_idx, new_idx), - false => SearchStep::Reject(char_idx, new_idx), + if self.literals.contains(&c) { + SearchStep::Match(char_idx, new_idx) + } else { + SearchStep::Reject(char_idx, new_idx) } } None => SearchStep::Done, @@ -161,9 +163,10 @@ impl<'a, P: FnMut(char) -> bool> Searcher<'a> for CharPredicateSearcher<'a, P> { match self.indices.next() { Some((char_idx, c)) => { let new_idx = char_idx + c.len_utf8(); - match (self.predicate)(c) { - true => SearchStep::Match(char_idx, new_idx), - false => SearchStep::Reject(char_idx, new_idx), + if (self.predicate)(c.into_char()) { + SearchStep::Match(char_idx, new_idx) + } else { + SearchStep::Reject(char_idx, new_idx) } } None => SearchStep::Done, @@ -176,9 +179,10 @@ impl<'a, P: FnMut(char) -> bool> ReverseSearcher<'a> for CharPredicateSearcher<' match self.indices.next_back() { Some((char_idx, c)) => { let new_idx = char_idx + c.len_utf8(); - match (self.predicate)(c) { - true => SearchStep::Match(char_idx, new_idx), - false => SearchStep::Reject(char_idx, new_idx), + if (self.predicate)(c.into_char()) { + SearchStep::Match(char_idx, new_idx) + } else { + SearchStep::Reject(char_idx, new_idx) } } None => SearchStep::Done, diff --git a/syscare-common/src/os_str/utf8.rs b/syscare-common/src/os_str/utf8.rs index 9a90bc055eb36e529176a1b7e06d1007d9abe9af..6cb224b4aa5852a0329f0f4c312a7b4b2cd1e0ab 100644 --- a/syscare-common/src/os_str/utf8.rs +++ b/syscare-common/src/os_str/utf8.rs @@ -39,7 +39,7 @@ pub const fn char_width(b: u8) -> usize { } pub fn next_valid_char(bytes: &[u8]) -> Option<(usize, char)> { - let first_byte = bytes[0]; + let first_byte = *bytes.first()?; let char_width = self::char_width(first_byte); if (char_width == 0) || (char_width > bytes.len()) { return None; @@ -47,21 +47,17 @@ pub fn next_valid_char(bytes: &[u8]) -> Option<(usize, char)> { let mut code = match char_width { 1 => return Some((1, first_byte as char)), - 2 => u32::from(first_byte & 0x1F) << 0x6, - 3 => u32::from(first_byte & 0x0F) << 0xC, - 4 => u32::from(first_byte & 0x07) << 0x12, + 2 => (first_byte & 0x1F) as u32, + 3 => (first_byte & 0x0F) as u32, + 4 => (first_byte & 0x07) as u32, _ => unreachable!(), }; - let mut index = 1; // start from second byte - while index < char_width { - let byte = bytes[index]; + for &byte in &bytes[1..char_width] { if byte & 0xC0 != 0x80 { - // check if it starts with 0b10 return None; } - code |= u32::from(byte & 0x3F) << ((char_width - index - 1) * 6); - index += 1; + code = (code << 6) | (byte & 0x3F) as u32; } char::from_u32(code).map(|c| (char_width, c)) diff --git a/syscare-common/src/process/args.rs b/syscare-common/src/process/args.rs deleted file mode 100644 index 7d7e0941c6937d6cd5ff5f39c5703813b847b69c..0000000000000000000000000000000000000000 --- a/syscare-common/src/process/args.rs +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::ffi::{OsStr, OsString}; - -#[derive(Clone, Default)] -pub struct CommandArgs { - args: Vec, -} - -impl CommandArgs { - pub fn new() -> Self { - Self { args: Vec::new() } - } - - pub fn arg(&mut self, arg: S) -> &mut Self - where - S: AsRef, - { - self.args.push(arg.as_ref().to_os_string()); - self - } - - pub fn args(&mut self, args: I) -> &mut Self - where - I: IntoIterator, - S: AsRef, - { - for arg in args { - self.arg(arg); - } - self - } -} - -impl IntoIterator for CommandArgs { - type Item = OsString; - - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.args.into_iter() - } -} diff --git a/syscare-common/src/process/child.rs b/syscare-common/src/process/child.rs index 4ce66645a5f89640f8c921a840b0f23083185015..5379ef20cad4fe3f55ebb041343e38b50c7c8dae 100644 --- a/syscare-common/src/process/child.rs +++ b/syscare-common/src/process/child.rs @@ -13,58 +13,57 @@ */ use std::{ - ffi::OsString, - ops::Deref, - os::unix::process::ExitStatusExt, - process::{Child as StdChild, ExitStatus as StdExitStatus}, - thread::JoinHandle, + ffi::OsString, ops::Deref, os::unix::process::ExitStatusExt, process, thread::JoinHandle, }; -use anyhow::{ensure, Context, Result}; +use anyhow::{anyhow, ensure, Context, Result}; use log::trace; -use super::{Stdio, StdioLevel}; +use super::output; pub struct Child { - pub(super) id: u32, pub(super) name: String, - pub(super) stdio_level: StdioLevel, - pub(super) inner: StdChild, + pub(super) child: process::Child, + pub(super) log_level: output::LogLevel, } impl Child { - fn capture_stdio(&mut self) -> Result> { - Stdio::new( - self.name.clone(), - self.inner - .stdout - .take() - .context("Failed to capture stdout")?, - self.inner - .stderr - .take() - .context("Failed to capture stderr")?, - self.stdio_level, - ) - .capture() + fn redirect_outputs(&mut self) -> Result> { + let stdout = self + .child + .stdout + .take() + .context("Failed to capture stdout")?; + let stderr = self + .child + .stderr + .take() + .context("Failed to capture stderr")?; + let outputs = output::Outputs::new(stdout, stderr, self.log_level); + + std::thread::Builder::new() + .name(self.name.clone()) + .spawn(|| outputs.redirect()) + .with_context(|| format!("Failed to create thread {}", self.name)) } } impl Child { pub fn kill(&mut self) -> Result<()> { - self.inner + let id = self.child.id(); + self.child .kill() - .with_context(|| format!("Failed to kill process {} ({})", self.name, self.id)) + .with_context(|| format!("Failed to kill process {} ({})", self.name, id)) } pub fn wait(&mut self) -> Result { + let id = self.child.id(); let status = self - .inner + .child .wait() - .with_context(|| format!("Failed to wait process {} ({})", self.name, self.id))?; - + .with_context(|| format!("Failed to wait process {} ({})", self.name, id))?; let exit_status = ExitStatus { - id: self.id, + id, name: self.name.clone(), status, }; @@ -79,22 +78,25 @@ impl Child { } pub fn wait_with_output(&mut self) -> Result { - let stdio_thread = self.capture_stdio()?; + let thread = self.redirect_outputs()?; let status = self.wait()?; - let (stdout, stderr) = stdio_thread.join().expect("Failed to join stdio thread"); + let (stdout, stderr) = thread + .join() + .map_err(|_| anyhow!("Failed to join stdio thread"))?; - Ok(Output { + let output = Output { status, stdout, stderr, - }) + }; + Ok(output) } } pub struct ExitStatus { id: u32, name: String, - status: StdExitStatus, + status: process::ExitStatus, } impl ExitStatus { diff --git a/syscare-common/src/process/command.rs b/syscare-common/src/process/command.rs deleted file mode 100644 index 06d3568ebdfd40c10c5c4d7ca52c6a9f89b97d7c..0000000000000000000000000000000000000000 --- a/syscare-common/src/process/command.rs +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - ffi::OsStr, - path::Path, - process::{Command as StdCommand, Stdio}, -}; - -use anyhow::{Context, Result}; -use log::{trace, Level}; - -use super::{Child, ExitStatus, Output, StdioLevel}; - -pub struct Command { - inner: StdCommand, - stdio_level: StdioLevel, -} - -impl Command { - pub fn new>(program: S) -> Self { - Self { - inner: StdCommand::new(program), - stdio_level: StdioLevel::default(), - } - } - - pub fn arg>(&mut self, arg: S) -> &mut Self { - self.inner.arg(arg); - self - } - - pub fn args(&mut self, args: I) -> &mut Self - where - I: IntoIterator, - S: AsRef, - { - for arg in args { - self.arg(arg.as_ref()); - } - self - } - - pub fn env(&mut self, key: K, val: V) -> &mut Self - where - K: AsRef, - V: AsRef, - { - self.inner.env(key, val); - self - } - - pub fn envs(&mut self, vars: I) -> &mut Self - where - I: IntoIterator, - K: AsRef, - V: AsRef, - { - for (ref key, ref val) in vars { - self.env(key, val); - } - self - } - - pub fn env_clear(&mut self) -> &mut Self { - self.inner.env_clear(); - self - } - - pub fn current_dir>(&mut self, dir: P) -> &mut Self { - self.inner.current_dir(dir); - self - } - - pub fn stdin>(&mut self, cfg: T) -> &mut Self { - self.inner.stdin(cfg); - self - } - - pub fn stdout>>(&mut self, level: T) -> &mut Self { - self.stdio_level.stdout = level.into(); - self - } - - pub fn stderr(&mut self, level: Level) -> &mut Self { - self.stdio_level.stderr = level.into(); - self - } - - pub fn spawn(&mut self) -> Result { - let name = Path::new(self.inner.get_program()) - .file_name() - .context("Failed to get process name")? - .to_string_lossy() - .to_string(); - - trace!("Executing {:?}", self.inner); - let child = self - .inner - .spawn() - .with_context(|| format!("Failed to start {}", name))?; - - Ok(Child { - id: child.id(), - name, - stdio_level: self.stdio_level, - inner: child, - }) - } - - pub fn run(&mut self) -> Result { - self.inner.stdout(Stdio::null()).stderr(Stdio::null()); - - self.spawn()?.wait() - } - - pub fn run_with_output(&mut self) -> Result { - self.inner.stdout(Stdio::piped()).stderr(Stdio::piped()); - - self.spawn()?.wait_with_output() - } -} diff --git a/syscare-common/src/process/envs.rs b/syscare-common/src/process/envs.rs deleted file mode 100644 index 038ad60eae0df72c4e1d5c58cbe119d3e7343b0c..0000000000000000000000000000000000000000 --- a/syscare-common/src/process/envs.rs +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - collections::HashMap, - ffi::{OsStr, OsString}, -}; - -pub struct CommandEnvs { - envs: HashMap, -} - -impl CommandEnvs { - pub fn new() -> Self { - Self { - envs: HashMap::new(), - } - } - - pub fn env(&mut self, key: K, value: V) -> &mut Self - where - K: AsRef, - V: AsRef, - { - self.envs - .insert(key.as_ref().to_os_string(), value.as_ref().to_os_string()); - self - } - - pub fn envs(&mut self, vars: I) -> &mut Self - where - I: IntoIterator, - K: AsRef, - V: AsRef, - { - for (key, value) in vars { - self.env(key, value); - } - self - } -} - -impl Default for CommandEnvs { - fn default() -> Self { - Self::new() - } -} - -impl IntoIterator for CommandEnvs { - type Item = (OsString, OsString); - - type IntoIter = std::collections::hash_map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.envs.into_iter() - } -} diff --git a/syscare-common/src/process/mod.rs b/syscare-common/src/process/mod.rs index 894707cc53e757584bbc371019be1e3e0626f715..1fe472c9537c531c8b161cc69bc160a667ba11d3 100644 --- a/syscare-common/src/process/mod.rs +++ b/syscare-common/src/process/mod.rs @@ -12,151 +12,434 @@ * See the Mulan PSL v2 for more details. */ -mod args; +use std::{ + collections::HashMap, + ffi::{OsStr, OsString}, + path::Path, + process, +}; + +use anyhow::{Context, Result}; +use log::{trace, Level}; + mod child; -mod command; -mod envs; -mod stdio; +mod output; + +#[derive(Debug, Clone)] +pub struct CommandArgs(Vec); + +impl CommandArgs { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn arg(&mut self, arg: S) -> &mut Self + where + S: AsRef, + { + self.0.push(arg.as_ref().to_os_string()); + self + } + + pub fn args(&mut self, args: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + { + for arg in args { + self.arg(arg); + } + self + } +} -pub use args::*; -pub use child::*; -pub use command::*; -pub use envs::*; +impl Default for CommandArgs { + fn default() -> Self { + Self::new() + } +} -use stdio::{Stdio, StdioLevel}; +impl IntoIterator for CommandArgs { + type Item = OsString; -#[test] -fn test() { - use log::Level; - use std::fs::File; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +#[derive(Debug, Clone)] +pub struct CommandEnvs(HashMap); + +impl CommandEnvs { + pub fn new() -> Self { + Self(HashMap::new()) + } + + pub fn env(&mut self, key: K, value: V) -> &mut Self + where + K: AsRef, + V: AsRef, + { + self.0 + .insert(key.as_ref().to_os_string(), value.as_ref().to_os_string()); + self + } + + pub fn envs(&mut self, vars: I) -> &mut Self + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + for (key, value) in vars { + self.env(key, value); + } + self + } +} - use crate::ffi::OsStrExt; +impl Default for CommandEnvs { + fn default() -> Self { + Self::new() + } +} + +impl IntoIterator for CommandEnvs { + type Item = (OsString, OsString); - println!("Testing Command::new()..."); - let mut echo_cmd = Command::new("echo"); - let mut env_cmd = Command::new("env"); - let mut pwd_cmd = Command::new("pwd"); - let mut grep_cmd = Command::new("grep"); - let mut ls_cmd = Command::new("ls"); + type IntoIter = std::collections::hash_map::IntoIter; - let mut test_cmd = Command::new("test"); - let mut cat_cmd = Command::new("cat"); - - let mut err_cmd = Command::new("/cmd/not/exist"); - - println!("Testing Command::arg()..."); - echo_cmd.arg("Test:"); - - test_cmd.arg("1"); - ls_cmd.arg("/file/not/exist"); - - println!("Testing Command::args()..."); - echo_cmd.args(["Hello", "World!"]); - - println!("Testing Command::env_clear()..."); - env_cmd.env_clear(); - - println!("Testing Command::env()..."); - env_cmd.env("test_key1", "test_val1"); - - println!("Testing Command::envs()..."); - env_cmd.envs([("test_key2", "test_val2"), ("test_key3", "test_val3")]); - - println!("Testing Command::current_dir()..."); - pwd_cmd.current_dir("/tmp"); - - println!("Testing Command::stdin()..."); - grep_cmd.stdin(File::open("/proc/self/maps").expect("Failed to open file")); - grep_cmd.arg("vdso"); - - println!("Testing Command::stdout()..."); - echo_cmd.stdout(Level::Info); - - println!("Testing Command::stderr()..."); - echo_cmd.stderr(Level::Info); - - println!("Testing Command::spawn()..."); - let mut echo_proc = echo_cmd.spawn().expect("Failed to spawn process"); - let mut env_proc = env_cmd.spawn().expect("Failed to spawn process"); - let mut pwd_proc = pwd_cmd.spawn().expect("Failed to spawn process"); - let mut grep_proc = grep_cmd.spawn().expect("Failed to spawn process"); - let mut ls_proc = ls_cmd.spawn().expect("Failed to spawn process"); - - let mut test_proc = test_cmd.spawn().expect("Failed to spawn process"); - let mut cat_proc = cat_cmd.spawn().expect("Failed to spawn process"); - - assert_eq!(err_cmd.spawn().is_err(), true); - - println!("Testing Child::kill()..."); - cat_proc.kill().expect("Failed to kill process"); - - println!("Testing Child::wait()..."); - let test_status = test_proc.wait().expect("Failed to wait process"); - let cat_status = cat_proc.wait().expect("Process should not be waited"); - - println!("Testing Child::wait_with_output()..."); - let echo_output = echo_proc - .wait_with_output() - .expect("Failed to wait process"); - let env_output = env_proc.wait_with_output().expect("Failed to wait process"); - let pwd_output = pwd_proc.wait_with_output().expect("Failed to wait process"); - let grep_output = grep_proc - .wait_with_output() - .expect("Failed to wait process"); - let ls_output = ls_proc.wait_with_output().expect("Failed to wait process"); - - println!("Testing ExitStatus::exit_code()..."); - assert_eq!(test_status.exit_code(), 0); - assert_eq!(cat_status.exit_code(), 137); - - println!("Testing ExitStatus::exit_ok()..."); - assert_eq!(test_status.exit_ok().is_ok(), true); - assert_eq!(cat_status.exit_ok().is_ok(), false); - - println!("Testing ExitStatus::success()..."); - assert_eq!(test_status.success(), true); - assert_eq!(cat_status.success(), false); - - println!("Testing Output::exit_code()..."); - assert_eq!(echo_output.exit_code(), 0); - assert_eq!(env_output.exit_code(), 0); - assert_eq!(pwd_output.exit_code(), 0); - assert_eq!(grep_output.exit_code(), 0); - assert_eq!(ls_output.exit_code(), 2); - - println!("Testing Output::exit_ok()..."); - assert_eq!(echo_output.exit_ok().is_ok(), true); - assert_eq!(env_output.exit_ok().is_ok(), true); - assert_eq!(pwd_output.exit_ok().is_ok(), true); - assert_eq!(grep_output.exit_ok().is_ok(), true); - assert_eq!(ls_output.exit_ok().is_ok(), false); - - println!("Testing Output::success()..."); - assert_eq!(echo_output.success(), true); - assert_eq!(env_output.success(), true); - assert_eq!(pwd_output.success(), true); - assert_eq!(grep_output.success(), true); - assert_eq!(ls_output.success(), false); - - println!("Testing Output::stdout..."); - assert_eq!(echo_output.stdout.is_empty(), false); - assert_eq!(env_output.stdout.is_empty(), false); - assert_eq!(pwd_output.stdout.is_empty(), false); - assert_eq!(grep_output.stdout.is_empty(), false); - assert_eq!(ls_output.stdout.is_empty(), true); - - assert_eq!(echo_output.stdout, "Test: Hello World!"); - assert_eq!( - env_output.stdout, - "test_key1=test_val1\ntest_key2=test_val2\ntest_key3=test_val3" - ); - assert_eq!(pwd_output.stdout, "/tmp"); - assert_eq!(grep_output.stdout.contains("vdso"), true); - - println!("Testing ProcessOutput::stderr..."); - assert_eq!(echo_output.stderr.is_empty(), true); - assert_eq!(env_output.stderr.is_empty(), true); - assert_eq!(pwd_output.stderr.is_empty(), true); - assert_eq!(grep_output.stderr.is_empty(), true); - assert_eq!(ls_output.stderr.is_empty(), false); + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +pub struct Command { + inner: process::Command, + log_level: output::LogLevel, +} + +impl Command { + pub fn new>(program: S) -> Self { + Self { + inner: process::Command::new(program), + log_level: output::LogLevel::default(), + } + } + + pub fn arg>(&mut self, arg: S) -> &mut Self { + self.inner.arg(arg); + self + } + + pub fn args(&mut self, args: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + { + for arg in args { + self.arg(arg.as_ref()); + } + self + } + + pub fn env(&mut self, key: K, val: V) -> &mut Self + where + K: AsRef, + V: AsRef, + { + self.inner.env(key, val); + self + } + + pub fn envs(&mut self, vars: I) -> &mut Self + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + for (key, val) in vars { + self.env(key, val); + } + self + } + + pub fn env_clear(&mut self) -> &mut Self { + self.inner.env_clear(); + self + } + + pub fn current_dir>(&mut self, dir: P) -> &mut Self { + self.inner.current_dir(dir); + self + } + + pub fn stdin>(&mut self, cfg: T) -> &mut Self { + self.inner.stdin(cfg); + self + } + + pub fn stdout(&mut self, level: Level) -> &mut Self { + self.log_level.stdout = Some(level); + self + } + + pub fn stderr(&mut self, level: Level) -> &mut Self { + self.log_level.stderr = Some(level); + self + } + + pub fn pipe_output(&mut self) -> &mut Self { + self.inner + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()); + self + } + + pub fn ignore_output(&mut self) -> &mut Self { + self.inner + .stdout(process::Stdio::null()) + .stderr(process::Stdio::null()); + self + } + + pub fn spawn(&mut self) -> Result { + let name = Path::new(self.inner.get_program()) + .file_name() + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_default(); + + trace!("Executing {:?}", self.inner); + let child = self + .inner + .spawn() + .with_context(|| format!("Failed to start {}", name))?; + let log_level = self.log_level; + + Ok(child::Child { + name, + child, + log_level, + }) + } + + pub fn run(&mut self) -> Result { + self.ignore_output().spawn()?.wait() + } + + pub fn run_with_output(&mut self) -> Result { + self.pipe_output().spawn()?.wait_with_output() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use log::Level; + use std::ffi::OsStr; + use std::path::Path; + use std::time::Duration; + + #[test] + fn test_command_args_new() { + let args = CommandArgs::new(); + assert!(args.0.is_empty()); + } + + #[test] + fn test_command_args_default() { + let args = CommandArgs::default(); + assert!(args.0.is_empty()); + } + + #[test] + fn test_command_args_arg() { + let mut args = CommandArgs::new(); + args.arg("test"); + assert_eq!(args.0.len(), 1); + assert_eq!(args.0[0], OsStr::new("test")); + } + + #[test] + fn test_command_args_args() { + let mut args = CommandArgs::new(); + args.args(vec!["arg1", "arg2"]); + assert_eq!(args.0.len(), 2); + assert_eq!(args.0[0], OsStr::new("arg1")); + assert_eq!(args.0[1], OsStr::new("arg2")); + } + + #[test] + fn test_command_args_into_iter() { + let mut args = CommandArgs::new(); + args.args(vec!["a", "b", "c"]); + let mut iter = args.into_iter(); + assert_eq!(iter.next(), Some(OsString::from("a"))); + assert_eq!(iter.next(), Some(OsString::from("b"))); + assert_eq!(iter.next(), Some(OsString::from("c"))); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_command_envs_new() { + let envs = CommandEnvs::new(); + assert!(envs.0.is_empty()); + } + + #[test] + fn test_command_envs_default() { + let envs = CommandEnvs::default(); + assert!(envs.0.is_empty()); + } + + #[test] + fn test_command_envs_env() { + let mut envs = CommandEnvs::new(); + envs.env("KEY", "VALUE"); + assert_eq!(envs.0.len(), 1); + assert_eq!( + envs.0.get(&OsString::from("KEY")), + Some(&OsString::from("VALUE")) + ); + } + + #[test] + fn test_command_envs_envs() { + let mut envs = CommandEnvs::new(); + envs.envs([("K1", "V1"), ("K2", "V2")]); + assert_eq!(envs.0.len(), 2); + assert_eq!( + envs.0.get(&OsString::from("K1")), + Some(&OsString::from("V1")) + ); + assert_eq!( + envs.0.get(&OsString::from("K2")), + Some(&OsString::from("V2")) + ); + } + + #[test] + fn test_command_new() { + let cmd = Command::new("test"); + assert_eq!(cmd.inner.get_program(), OsStr::new("test")); + assert!(cmd.inner.get_args().next().is_none()); + } + + #[test] + fn test_command_arg() { + let mut cmd = Command::new("test"); + cmd.arg("arg1"); + let mut args = cmd.inner.get_args(); + assert_eq!(args.next(), Some(OsStr::new("arg1"))); + assert!(args.next().is_none()); + } + + #[test] + fn test_command_args() { + let mut cmd = Command::new("test"); + cmd.args(["a", "b"]); + let mut args = cmd.inner.get_args(); + assert_eq!(args.next(), Some(OsStr::new("a"))); + assert_eq!(args.next(), Some(OsStr::new("b"))); + assert!(args.next().is_none()); + } + + #[test] + fn test_command_env() { + let mut cmd = Command::new("test"); + cmd.env("K", "V"); + let envs = cmd.inner.get_envs().collect::>(); + assert_eq!(envs.get(&OsStr::new("K")), Some(&Some(OsStr::new("V")))); + } + + #[test] + fn test_command_envs() { + let mut cmd = Command::new("test"); + cmd.envs([("K1", "V1"), ("K2", "V2")]); + + let envs = cmd.inner.get_envs().collect::>(); + assert_eq!(envs.len(), 2); + assert_eq!(envs.get(&OsStr::new("K1")), Some(&Some(OsStr::new("V1")))); + assert_eq!(envs.get(&OsStr::new("K2")), Some(&Some(OsStr::new("V2")))); + } + + #[test] + fn test_command_env_clear() { + let mut cmd = Command::new("test"); + cmd.env("K", "V").env_clear(); + let envs = cmd.inner.get_envs().collect::>(); + assert!(envs.is_empty()); + } + + #[test] + fn test_command_current_dir() { + let mut cmd = Command::new("test"); + cmd.current_dir("/tmp"); + assert_eq!(cmd.inner.get_current_dir(), Some(Path::new("/tmp"))); + } + + #[test] + fn test_command_stdout_stderr() { + let mut cmd = Command::new("test"); + cmd.stdout(Level::Info).stderr(Level::Error); + assert_eq!(cmd.log_level.stdout, Some(Level::Info)); + assert_eq!(cmd.log_level.stderr, Some(Level::Error)); + } + + #[test] + fn test_command_pipe_output() { + let mut cmd = Command::new("test"); + + cmd.pipe_output(); + assert!(cmd.inner.spawn().unwrap().stdout.is_some()); + assert!(cmd.inner.spawn().unwrap().stderr.is_some()); + } + + #[test] + fn test_command_ignore_output() { + let mut cmd = Command::new("test"); + + cmd.pipe_output(); + assert!(cmd.inner.spawn().unwrap().stdout.is_some()); + assert!(cmd.inner.spawn().unwrap().stderr.is_some()); + } + + #[test] + fn test_command_spawn_kill() { + let mut cmd = Command::new("yes"); + cmd.ignore_output(); + + let mut child = cmd.spawn().unwrap(); + std::thread::sleep(Duration::from_millis(100)); + + assert!(child.kill().is_ok()); + } + + #[test] + fn test_command_spawn_wait() { + let mut cmd = Command::new("echo"); + cmd.arg("test").ignore_output(); + + let mut child = cmd.spawn().unwrap(); + let status = child.wait().unwrap(); + assert!(status.success()); + } + + #[test] + fn test_command_run() { + let mut cmd = Command::new("true"); + let status = cmd.run().unwrap(); + assert!(status.success()); + } + + #[test] + fn test_command_run_with_output() { + let mut cmd = Command::new("env"); + let output = cmd.run_with_output().unwrap(); + + assert!(output.status.success()); + assert!(!output.stdout.is_empty()); + } } diff --git a/syscare-common/src/process/output.rs b/syscare-common/src/process/output.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e97c7edf6217a6708075050b85cc0801bedb15e --- /dev/null +++ b/syscare-common/src/process/output.rs @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: Mulan PSL v2 +/* + * Copyright (c) 2024 Huawei Technologies Co., Ltd. + * syscare-common is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use std::{ + ffi::OsString, + io::Read, + os::unix::{ffi::OsStringExt, io::AsRawFd}, + process::{ChildStderr, ChildStdout}, +}; + +use log::{error, log, Level}; +use nix::poll::{poll, PollFd, PollFlags}; + +const STREAM_BUFFER_SIZE: usize = 4096; + +#[derive(Debug, Clone, Copy)] +pub struct LogLevel { + pub stdout: Option, + pub stderr: Option, +} + +impl Default for LogLevel { + fn default() -> Self { + Self { + stdout: None, + stderr: Some(Level::Error), + } + } +} + +struct Stream { + stream: R, + buffer: Vec, + offset: usize, + log_level: Option, + is_closed: bool, +} + +impl Stream { + fn new(stream: R, log_level: Option) -> Self { + Self { + stream, + buffer: Vec::with_capacity(STREAM_BUFFER_SIZE), + offset: 0, + log_level, + is_closed: false, + } + } + + fn read_buf(&mut self) -> std::io::Result { + if self.buffer.capacity().wrapping_sub(self.buffer.len()) < STREAM_BUFFER_SIZE { + self.buffer.reserve(STREAM_BUFFER_SIZE); + } + + let spare_cap = self.buffer.spare_capacity_mut(); + let spare_buf = unsafe { + std::slice::from_raw_parts_mut(spare_cap.as_mut_ptr() as *mut u8, spare_cap.len()) + }; + + let len = self.stream.read(spare_buf)?; + unsafe { + self.buffer.set_len(self.buffer.len() + len); + } + + Ok(len) + } + + fn print_logs(&mut self) { + if let Some(level) = self.log_level { + let start = self.offset; + if start >= self.buffer.len() { + return; + } + + let slice = if !self.is_closed { + let end = match self.buffer[start..].iter().rposition(|&b| b == b'\n') { + Some(pos) => start + pos, + None => return, + }; + self.offset = end + 1; // skip '\n' + &self.buffer[start..end] + } else { + self.offset = self.buffer.len(); + &self.buffer[start..] + }; + if slice.is_empty() { + return; + } + + let lines = slice.split(|&b| b == b'\n').map(String::from_utf8_lossy); + for line in lines { + log!(level, "{}", line); + } + } + } + + fn handle_revents(&mut self, revents: PollFlags) { + if revents.contains(PollFlags::POLLIN) { + match self.read_buf() { + Ok(0) => self.is_closed = true, // EOF + Ok(_) => {} + Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {} + Err(e) => { + error!("Failed to read stream, {}", e); + self.is_closed = true; + } + } + } + if revents.contains(PollFlags::POLLHUP) { + self.is_closed = true; + } + + self.print_logs(); + } +} + +pub struct Outputs { + fds: [PollFd; 2], + stdout: Stream, + stderr: Stream, +} + +impl Outputs { + pub fn new(stdout: ChildStdout, stderr: ChildStderr, log_level: LogLevel) -> Self { + Self { + fds: [ + PollFd::new(stdout.as_raw_fd(), PollFlags::POLLIN | PollFlags::POLLHUP), + PollFd::new(stderr.as_raw_fd(), PollFlags::POLLIN | PollFlags::POLLHUP), + ], + stdout: Stream::new(stdout, log_level.stdout), + stderr: Stream::new(stderr, log_level.stderr), + } + } + + pub fn redirect(mut self) -> (OsString, OsString) { + const POLL_TIMEOUT: i32 = -1; + + loop { + match poll(&mut self.fds, POLL_TIMEOUT) { + Ok(events) => { + if events == 0 { + break; + } + for (i, fd) in self.fds.iter().enumerate() { + let revents = fd.revents().expect("Invalid poll event"); + match i { + 0 => self.stdout.handle_revents(revents), + 1 => self.stderr.handle_revents(revents), + _ => unreachable!("Invalid poll fd"), + }; + } + } + Err(e) => { + error!("Failed to poll events, {}", e); + break; + } + } + if self.stdout.is_closed && self.stderr.is_closed { + break; + } + } + + ( + OsString::from_vec(self.stdout.buffer), + OsString::from_vec(self.stderr.buffer), + ) + } +} diff --git a/syscare-common/src/process/stdio.rs b/syscare-common/src/process/stdio.rs deleted file mode 100644 index fca944f0e7fe8a50dfcb802a2c49e7a644e57b82..0000000000000000000000000000000000000000 --- a/syscare-common/src/process/stdio.rs +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare-common is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - collections::HashMap, - ffi::OsString, - io::BufReader, - os::unix::{ - ffi::OsStringExt, - io::{AsRawFd, RawFd}, - }, - process::{ChildStderr, ChildStdout}, - thread::JoinHandle, -}; - -use anyhow::{Context, Result}; -use log::{error, log, Level}; - -use crate::io::{BufReadOsLines, OsLines, Select, SelectResult}; - -#[derive(Debug, Clone, Copy)] -pub struct StdioLevel { - pub(super) stdout: Option, - pub(super) stderr: Option, -} - -impl Default for StdioLevel { - fn default() -> Self { - Self { - stdout: None, - stderr: Some(Level::Error), - } - } -} - -pub enum StdioOutput { - Stdout(OsString), - Stderr(OsString), -} - -enum StdioLines { - Stdout(OsLines>), - Stderr(OsLines>), -} - -struct StdioReader { - select: Select, - stdio_map: HashMap, - line_buf: Vec, -} - -impl StdioReader { - fn new(stdout: ChildStdout, stderr: ChildStderr) -> Self { - let line_buf = Vec::new(); - let stdio_map = HashMap::from([ - ( - stdout.as_raw_fd(), - StdioLines::Stdout(BufReader::new(stdout).os_lines()), - ), - ( - stderr.as_raw_fd(), - StdioLines::Stderr(BufReader::new(stderr).os_lines()), - ), - ]); - let select = Select::new(stdio_map.keys().copied()); - - Self { - select, - stdio_map, - line_buf, - } - } -} - -impl Iterator for StdioReader { - type Item = StdioOutput; - - fn next(&mut self) -> Option { - match self.select.select().context("Failed to select stdio") { - Ok(result) => { - let stdio_map = &mut self.stdio_map; - let outputs = result.into_iter().filter_map(|fd| match fd { - SelectResult::Readable(fd) => { - stdio_map.get_mut(&fd).and_then(|stdio| match stdio { - StdioLines::Stdout(lines) => { - lines.next().and_then(Result::ok).map(StdioOutput::Stdout) - } - StdioLines::Stderr(lines) => { - lines.next().and_then(Result::ok).map(StdioOutput::Stderr) - } - }) - } - _ => None, - }); - self.line_buf.extend(outputs); - } - Err(e) => { - error!("{:?}", e); - } - }; - - self.line_buf.pop() - } -} - -pub struct Stdio { - name: String, - stdout: ChildStdout, - stderr: ChildStderr, - level: StdioLevel, -} - -impl Stdio { - pub fn new(name: String, stdout: ChildStdout, stderr: ChildStderr, level: StdioLevel) -> Self { - Self { - name, - stdout, - stderr, - level, - } - } - - pub fn capture(self) -> Result> { - let stdio_level = self.level; - let stdio_reader = StdioReader::new(self.stdout, self.stderr); - - let thread_name = self.name.as_str(); - let thread = std::thread::Builder::new() - .name(thread_name.to_string()) - .spawn(move || -> (OsString, OsString) { - let mut stdout_buf = Vec::new(); - let mut stderr_buf = Vec::new(); - - for output in stdio_reader { - match output { - StdioOutput::Stdout(str) => { - if let Some(level) = stdio_level.stdout { - log!(level, "{}", str.to_string_lossy()); - } - stdout_buf.extend(str.into_vec()); - stdout_buf.push(b'\n'); - } - StdioOutput::Stderr(str) => { - if let Some(level) = stdio_level.stderr { - log!(level, "{}", str.to_string_lossy()); - } - stderr_buf.extend(str.into_vec()); - stderr_buf.push(b'\n'); - } - } - } - if stdout_buf.ends_with(&[b'\n']) { - stdout_buf.pop(); - } - if stderr_buf.ends_with(&[b'\n']) { - stderr_buf.pop(); - } - - ( - OsString::from_vec(stdout_buf), - OsString::from_vec(stderr_buf), - ) - }) - .with_context(|| format!("Failed to create thread {}", thread_name))?; - - Ok(thread) - } -} diff --git a/syscare-common/src/util/digest.rs b/syscare-common/src/util/digest.rs index bb879cba244c32c5f3616aad2ee880019db9eb29..4ed7357b0a7ba147dbb156867bffa2ab57ff8ea0 100644 --- a/syscare-common/src/util/digest.rs +++ b/syscare-common/src/util/digest.rs @@ -14,23 +14,21 @@ use std::path::Path; -use sha2::Digest; -use sha2::Sha256; +use nix::errno::Errno; +use sha2::{Digest, Sha256}; use crate::fs; -pub fn bytes>(bytes: S) -> String { - let mut hasher = Sha256::new(); - hasher.update(bytes); - - format!("{:#x}", hasher.finalize()) +pub fn bytes>(bytes: T) -> String { + format!("{:#x}", Sha256::digest(bytes)) } -pub fn file>(file: P) -> std::io::Result { - let mut hasher = Sha256::new(); - hasher.update(fs::read(file)?); - - Ok(format!("{:#x}", hasher.finalize())) +pub fn file>(path: P) -> std::io::Result { + let file_path = path.as_ref(); + if !file_path.is_file() { + return Err(std::io::Error::from(Errno::EINVAL)); + } + Ok(self::bytes(&*fs::mmap(file_path)?)) } pub fn file_list(file_list: I) -> std::io::Result @@ -39,16 +37,201 @@ where P: AsRef, { let mut hasher = Sha256::new(); + for file in file_list { - hasher.update(fs::read(file)?); + let file_path = file.as_ref(); + if file_path.is_file() { + hasher.update(&*fs::mmap(file)?); + } } Ok(format!("{:#x}", hasher.finalize())) } -pub fn dir>(directory: P) -> std::io::Result { - file_list(fs::list_files( - directory, - fs::TraverseOptions { recursive: true }, - )?) +pub fn dir>(path: P) -> std::io::Result { + let dir_path = path.as_ref(); + if !dir_path.is_dir() { + return Err(std::io::Error::from(Errno::EINVAL)); + } + + let mut file_list = fs::list_files(path, fs::TraverseOptions { recursive: true })?; + file_list.sort_unstable(); + + self::file_list(file_list) +} + +pub fn path>(path: P) -> std::io::Result { + if path.as_ref().is_file() { + self::file(path) + } else { + self::dir(path) + } +} + +#[cfg(test)] +mod tests { + use std::{ + fs::File, + io::Write, + path::PathBuf, + time::{SystemTime, UNIX_EPOCH}, + }; + + use super::*; + + fn unique_name(prefix: &str) -> std::io::Result { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + Ok(format!("{}_{}", prefix, timestamp.as_nanos())) + } + + fn create_temp_file(content: &[u8]) -> std::io::Result { + let temp_file = std::env::temp_dir().join(self::unique_name("digest_test")?); + + let mut file = File::create(&temp_file)?; + file.write_all(content)?; + + Ok(temp_file) + } + + fn create_temp_dir() -> std::io::Result { + let temp_dir = std::env::temp_dir(); + let test_dir = temp_dir.join(unique_name("test_dir")?); + + fs::create_dir(&test_dir)?; + File::create(test_dir.join("file1.txt"))?.write_all(b"file1")?; + File::create(test_dir.join("file2.bin"))?.write_all(b"file2")?; + fs::create_dir(test_dir.join("subdir"))?; + File::create(test_dir.join("subdir/file3.txt"))?.write_all(b"file3")?; + + Ok(test_dir) + } + + #[test] + fn test_bytes() -> std::io::Result<()> { + assert_eq!( + self::bytes(b"hello"), + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" + ); + Ok(()) + } + + #[test] + fn test_file() -> std::io::Result<()> { + let file_path = self::create_temp_file(b"hello")?; + let hash = self::file(&file_path)?; + + std::fs::remove_file(&file_path).ok(); + assert_eq!( + hash, + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" + ); + + Ok(()) + } + + #[test] + fn test_file_not_exists() -> std::io::Result<()> { + let result = self::file("/non_exist_file"); + + assert!(result.is_err()); + if let Err(e) = &result { + assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); + } + + Ok(()) + } + + #[test] + fn test_file_is_dir() -> std::io::Result<()> { + let result = self::file(std::env::temp_dir()); + + assert!(result.is_err()); + if let Err(e) = &result { + assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); + } + + Ok(()) + } + + #[test] + fn test_file_list() -> std::io::Result<()> { + let files = vec![ + self::create_temp_file(b"file1")?, + self::create_temp_file(b"file2")?, + self::create_temp_file(b"file3")?, + ]; + + let hash = self::file_list(&files)?; + for file in files { + std::fs::remove_file(file)?; + } + assert_eq!( + hash, + "d944e85974a48cfc20a944738d9617ad5ffde6e1219cf4c362dc058a47419848" + ); + + Ok(()) + } + + #[test] + fn test_dir() -> std::io::Result<()> { + let test_dir = self::create_temp_dir()?; + let hash = self::dir(&test_dir)?; + std::fs::remove_dir_all(test_dir)?; + + assert_eq!( + hash, + "d944e85974a48cfc20a944738d9617ad5ffde6e1219cf4c362dc058a47419848" + ); + Ok(()) + } + + #[test] + fn test_dir_not_exists() -> std::io::Result<()> { + let result = self::dir("/non_exist_file"); + + assert!(result.is_err()); + if let Err(e) = &result { + assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); + } + + Ok(()) + } + + #[test] + fn test_dir_is_file() -> std::io::Result<()> { + let file_path = self::create_temp_file(b"hello")?; + let result = self::dir(&file_path); + std::fs::remove_file(file_path)?; + + assert!(result.is_err()); + if let Err(e) = &result { + assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); + } + + Ok(()) + } + + #[test] + fn test_path() -> std::io::Result<()> { + let file_path = self::create_temp_file(b"hello")?; + let dir_path = self::create_temp_dir()?; + + let file_hash = self::path(&file_path)?; + let dir_hash = self::path(&dir_path)?; + std::fs::remove_file(&file_path)?; + std::fs::remove_dir_all(&dir_path)?; + + assert_eq!( + file_hash, + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" + ); + assert_eq!( + dir_hash, + "d944e85974a48cfc20a944738d9617ad5ffde6e1219cf4c362dc058a47419848" + ); + Ok(()) + } } diff --git a/syscare/Cargo.toml b/syscare/Cargo.toml index be0d04e925e989dd082d325f08cf6c3c4f1966b8..3cb4b7164da0bec1fe6f01716000ad65d3f498e8 100644 --- a/syscare/Cargo.toml +++ b/syscare/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syscare" -version = "1.2.1" +version = "1.2.2" authors = ["renoseven "] description = "Syscare command line interface" license = "MulanPSL-2.0" diff --git a/syscare/build.rs b/syscare/build.rs index 45eb14a6be54fd76524a59bda3f9d58aaae64bd9..826ba2224f25131ed02e987b709e7778eff4961f 100644 --- a/syscare/build.rs +++ b/syscare/build.rs @@ -12,26 +12,25 @@ * See the Mulan PSL v2 for more details. */ -use std::{env, process::Command}; +use std::{env, ffi::OsStr, os::unix::ffi::OsStrExt, process::Command}; fn rewrite_version() { - const ENV_VERSION_NAME: &str = "BUILD_VERSION"; - const PKG_VERSION_NAME: &str = "CARGO_PKG_VERSION"; + const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + const ENV_VERSION: Option<&str> = option_env!("BUILD_VERSION"); - let version = env::var(ENV_VERSION_NAME).unwrap_or_else(|_| { - let pkg_version = env::var(PKG_VERSION_NAME).expect("Failed to fetch package version"); - let git_output = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|output| String::from_utf8(output.stdout).expect("Failed to fetch git version")); - - match git_output { - Ok(git_version) => format!("{}-g{}", pkg_version, git_version), - Err(_) => pkg_version, - } - }); - - println!("cargo:rustc-env={}={}", PKG_VERSION_NAME, version); + println!( + "cargo:rustc-env=CARGO_PKG_VERSION={}", + ENV_VERSION.map(String::from).unwrap_or_else(|| { + Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|output| { + let git_version = OsStr::from_bytes(&output.stdout).to_string_lossy(); + format!("{}-g{}", PKG_VERSION, git_version) + }) + .unwrap_or_else(|_| PKG_VERSION.to_string()) + }) + ); } fn main() { diff --git a/syscare/src/args.rs b/syscare/src/args.rs index 4f034a08f879afa525bf6f74468aa08851991b42..5f19835f427cb788688d54087a45921a99c37b6d 100644 --- a/syscare/src/args.rs +++ b/syscare/src/args.rs @@ -12,9 +12,9 @@ * See the Mulan PSL v2 for more details. */ -use std::path::PathBuf; +use std::{ffi::OsString, path::PathBuf}; -use anyhow::{ensure, Result}; +use anyhow::Result; use clap::{AppSettings, ColorChoice, Parser, Subcommand}; use syscare_common::fs; @@ -28,6 +28,7 @@ const DEFAULT_WORK_DIR: &str = "/var/run/syscare"; bin_name = CLI_NAME, version = CLI_VERSION, about = CLI_ABOUT, + allow_external_subcommands(true), arg_required_else_help(true), color(ColorChoice::Never), disable_help_subcommand(true), @@ -37,7 +38,7 @@ const DEFAULT_WORK_DIR: &str = "/var/run/syscare"; pub struct Arguments { /// Command name #[clap(subcommand)] - pub command: SubCommand, + pub subcommand: SubCommand, /// Path for working directory #[clap(short, long, default_value=DEFAULT_WORK_DIR)] @@ -50,13 +51,6 @@ pub struct Arguments { #[derive(Debug, Clone, Subcommand)] pub enum SubCommand { - /// Build a patch - #[clap( - disable_help_flag(true), - subcommand_precedence_over_arg(true), - allow_hyphen_values(true) - )] - Build { args: Vec }, /// Show patch info Info { /// Patch identifier @@ -127,11 +121,16 @@ pub enum SubCommand { #[clap(long)] accepted: bool, }, + /// Rescan all patches + Rescan, + /// External subcommand + #[clap(external_subcommand)] + External(Vec), } impl Arguments { pub fn new() -> Result { - Self::parse().normalize_path().and_then(Self::check) + Self::parse().normalize_path() } fn normalize_path(mut self) -> Result { @@ -139,16 +138,6 @@ impl Arguments { Ok(self) } - - fn check(self) -> Result { - let work_dir = &self.work_dir; - ensure!( - work_dir.is_dir(), - format!("Cannot find directory {}", work_dir.display()) - ); - - Ok(self) - } } impl std::fmt::Display for Arguments { diff --git a/syscare/src/executor/build.rs b/syscare/src/executor/build.rs deleted file mode 100644 index f9027c73b7a6af8c0bad1890bd6849c63b5cfea8..0000000000000000000000000000000000000000 --- a/syscare/src/executor/build.rs +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{os::unix::process::CommandExt, process::Command}; - -use anyhow::{bail, Context, Result}; - -use super::CommandExecutor; -use crate::args::SubCommand; - -const SYSCARE_BUILD_PATH: &str = "/usr/libexec/syscare/syscare-build"; - -pub struct BuildCommandExecutor; - -impl CommandExecutor for BuildCommandExecutor { - fn invoke(&self, command: &SubCommand) -> Result> { - if let SubCommand::Build { args } = command { - let e = Command::new(SYSCARE_BUILD_PATH).args(args).exec(); - - match e.kind() { - std::io::ErrorKind::NotFound => { - bail!("Package syscare-build is not installed"); - } - _ => { - return Err(e) - .with_context(|| format!("Failed to start {}", SYSCARE_BUILD_PATH)) - } - } - } - - Ok(None) - } -} diff --git a/syscare/src/executor/mod.rs b/syscare/src/executor/mod.rs deleted file mode 100644 index 882cd0f53b6236680bc02938dd073d490998de22..0000000000000000000000000000000000000000 --- a/syscare/src/executor/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use anyhow::{ensure, Result}; -use syscare_common::os; - -use super::args::SubCommand; - -pub mod build; -pub mod patch; - -pub trait CommandExecutor { - fn invoke(&self, command: &SubCommand) -> Result>; - - fn check_root_permission(&self) -> Result<()> { - const ROOT_UID: u32 = 0; - - ensure!( - os::user::id() == ROOT_UID, - "This command has to be run with superuser privileges (under the root user on most systems)." - ); - - Ok(()) - } -} diff --git a/syscare/src/executor/patch.rs b/syscare/src/executor/patch.rs deleted file mode 100644 index 1c4109bae95ba9ee89f8ebd3923387a3f46ae672..0000000000000000000000000000000000000000 --- a/syscare/src/executor/patch.rs +++ /dev/null @@ -1,256 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{fmt::Write, path::PathBuf}; - -use anyhow::{anyhow, Error, Result}; -use log::info; - -use syscare_abi::{PackageInfo, PatchInfo, PatchListRecord, PatchStateRecord}; -use syscare_common::fs::{FileLock, FileLockType}; - -use crate::{args::SubCommand, rpc::RpcProxy}; - -use super::CommandExecutor; - -pub struct PatchCommandExecutor { - proxy: RpcProxy, - lock_file: PathBuf, -} - -impl PatchCommandExecutor { - pub fn new(proxy: RpcProxy, lock_file: PathBuf) -> Self { - Self { proxy, lock_file } - } -} - -impl PatchCommandExecutor { - fn check_error(mut error_list: Vec) -> Result<()> { - match error_list.len() { - 0 => Ok(()), - 1 => Err(error_list.pop().unwrap()), - _ => { - let mut err_msg = String::new(); - for (idx, e) in error_list.into_iter().enumerate() { - writeln!(err_msg, "{}. {}", idx, e)?; - } - err_msg.pop(); - - Err(anyhow!(err_msg)) - } - } - } - - fn show_patch_info(patch_list: impl IntoIterator) { - for (identifier, patch) in patch_list { - info!("-------------------------------------------"); - info!("Patch: {}", identifier); - info!("-------------------------------------------"); - info!("{}", patch); - } - info!("-------------------------------------------"); - } - - fn show_patch_target(pkg_list: impl IntoIterator) { - for (identifier, package) in pkg_list { - info!("-------------------------------------------"); - info!("Patch: {}", identifier); - info!("-------------------------------------------"); - info!("{}", package); - } - info!("-------------------------------------------"); - } - - fn show_patch_status(status_list: impl IntoIterator) { - for record in status_list { - info!("{}: {}", record.name, record.status) - } - } - - fn show_patch_list(patch_list: impl IntoIterator) { - info!("{:<40} {:<60} {:<12}", "Uuid", "Name", "Status"); - for record in patch_list { - info!( - "{:<40} {:<60} {:<12}", - record.uuid, record.name, record.status - ) - } - } -} - -impl CommandExecutor for PatchCommandExecutor { - fn invoke(&self, command: &SubCommand) -> Result> { - self.check_root_permission()?; - - match command { - SubCommand::Info { identifiers } => { - let mut patch_list = vec![]; - let mut error_list = vec![]; - - for identifier in identifiers { - match self.proxy.get_patch_info(identifier) { - Ok(patch) => patch_list.push((identifier.to_owned(), patch)), - Err(e) => error_list.push(e), - } - } - Self::show_patch_info(patch_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Target { identifiers } => { - let mut pkg_list = vec![]; - let mut error_list = vec![]; - - for identifier in identifiers { - match self.proxy.get_patch_target(identifier) { - Ok(pkg) => pkg_list.push((identifier.to_owned(), pkg)), - Err(e) => error_list.push(e), - } - } - Self::show_patch_target(pkg_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Status { identifiers } => { - let mut status_list = vec![]; - let mut error_list = vec![]; - - for identifier in identifiers { - match self.proxy.get_patch_status(identifier) { - Ok(new_status) => status_list.extend(new_status), - Err(e) => error_list.push(e), - } - } - Self::show_patch_status(status_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::List => { - Self::show_patch_list(self.proxy.get_patch_list()?); - return Ok(Some(0)); - } - SubCommand::Check { identifiers } => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - let mut error_list = vec![]; - for identifier in identifiers { - if let Err(e) = self.proxy.check_patch(identifier) { - error_list.push(e); - } - } - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Apply { identifiers, force } => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - let mut status_list = vec![]; - let mut error_list = vec![]; - for identifier in identifiers { - match self.proxy.apply_patch(identifier, *force) { - Ok(new_status) => status_list.extend(new_status), - Err(e) => error_list.push(e), - } - } - Self::show_patch_status(status_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Remove { identifiers } => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - let mut status_list = vec![]; - let mut error_list = vec![]; - for identifier in identifiers { - match self.proxy.remove_patch(identifier) { - Ok(new_status) => status_list.extend(new_status), - Err(e) => error_list.push(e), - } - } - Self::show_patch_status(status_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Active { identifiers, force } => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - let mut status_list = vec![]; - let mut error_list = vec![]; - for identifier in identifiers { - match self.proxy.active_patch(identifier, *force) { - Ok(new_status) => status_list.extend(new_status), - Err(e) => error_list.push(e), - } - } - Self::show_patch_status(status_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Deactive { identifiers } => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - let mut status_list = vec![]; - let mut error_list = vec![]; - for identifier in identifiers { - match self.proxy.deactive_patch(identifier) { - Ok(new_status) => status_list.extend(new_status), - Err(e) => error_list.push(e), - } - } - Self::show_patch_status(status_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Accept { identifiers } => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - let mut status_list = vec![]; - let mut error_list = vec![]; - for identifier in identifiers { - match self.proxy.accept_patch(identifier) { - Ok(new_status) => status_list.extend(new_status), - Err(e) => error_list.push(e), - } - } - Self::show_patch_status(status_list); - Self::check_error(error_list)?; - - return Ok(Some(0)); - } - SubCommand::Save => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - self.proxy.save_patch_status()?; - return Ok(Some(0)); - } - SubCommand::Restore { accepted } => { - let _file_lock = FileLock::new(&self.lock_file, FileLockType::Exclusive)?; - - self.proxy.restore_patch_status(*accepted)?; - return Ok(Some(0)); - } - _ => {} - } - - Ok(None) - } -} diff --git a/syscare/src/main.rs b/syscare/src/main.rs index c709f6a2dcd9b0f977a87742af240baacc160d4a..ff7a4ecf145a6ade8f68fa02121180628d30db8a 100644 --- a/syscare/src/main.rs +++ b/syscare/src/main.rs @@ -12,123 +12,129 @@ * See the Mulan PSL v2 for more details. */ -use std::{process, rc::Rc}; +use std::{env, ffi::OsString, process::Command}; -use anyhow::{Context, Result}; -use flexi_logger::{DeferredNow, LogSpecification, Logger, LoggerHandle, WriteMode}; -use log::{debug, error, LevelFilter, Record}; +use anyhow::{anyhow, Context, Result}; +use args::SubCommand; +use flexi_logger::{LogSpecification, Logger, WriteMode}; +use log::{debug, LevelFilter}; + +use syscare_common::{concat_os, ffi::OsStrExt, os}; mod args; -mod executor; mod rpc; -use args::Arguments; -use executor::{build::BuildCommandExecutor, patch::PatchCommandExecutor, CommandExecutor}; -use rpc::{RpcProxy, RpcRemote}; -use syscare_common::os; +use self::{ + args::Arguments, + rpc::{PatchProxy, RpcClient}, +}; pub const CLI_NAME: &str = env!("CARGO_PKG_NAME"); pub const CLI_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const CLI_ABOUT: &str = env!("CARGO_PKG_DESCRIPTION"); -const CLI_UMASK: u32 = 0o077; - -const SOCKET_FILE_NAME: &str = "syscared.sock"; -const PATCH_OP_LOCK_NAME: &str = "patch_op.lock"; - -struct SyscareCLI { - args: Arguments, - logger: LoggerHandle, +pub const CLI_UMASK: u32 = 0o022; + +const PATH_ENV_NAME: &str = "PATH"; +const PATH_ENV_VALUE: &str = "/usr/libexec/syscare"; +const EXTERNAL_CMD_PREFIX: &str = "syscare-"; + +fn exec_external_cmd(mut args: Vec) -> Result<()> { + let program = concat_os!(EXTERNAL_CMD_PREFIX, args.remove(0).trim()); + + let _ = Command::new(&program) + .args(&args) + .status() + .map_err(|e| match e.kind() { + std::io::ErrorKind::NotFound => anyhow!( + "External command '{}' is not installed", + program.to_string_lossy() + ), + _ => anyhow!( + "Failed to execute '{}': {}", + program.to_string_lossy(), + e.to_string().to_lowercase() + ), + })?; + + Ok(()) } -impl SyscareCLI { - fn format_log( - w: &mut dyn std::io::Write, - _now: &mut DeferredNow, - record: &Record, - ) -> std::io::Result<()> { - write!(w, "{}", &record.args()) - } +fn main() -> Result<()> { + // Parse arguments + let args = Arguments::new()?; - fn new() -> Result { - // Initialize arguments & prepare environments - os::umask::set_umask(CLI_UMASK); - - let args = Arguments::new()?; - - // Initialize logger - let log_level_max = match args.verbose { - false => LevelFilter::Info, - true => LevelFilter::Trace, - }; - let log_spec = LogSpecification::builder().default(log_level_max).build(); - let logger = Logger::with(log_spec) - .log_to_stdout() - .format(Self::format_log) - .write_mode(WriteMode::Direct) - .start() - .context("Failed to initialize logger")?; - - Ok(Self { args, logger }) + // Set up environments + os::umask::set_umask(CLI_UMASK); + if let Some(path_env) = env::var_os(PATH_ENV_NAME) { + env::set_var(PATH_ENV_NAME, concat_os!(PATH_ENV_VALUE, ":", path_env)); } -} - -impl SyscareCLI { - fn run(&self) -> Result { - debug!("Start with {:#?}", self.args); - - debug!("Initializing remote procedure call client..."); - let socket_file = self.args.work_dir.join(SOCKET_FILE_NAME); - let remote = Rc::new(RpcRemote::new(socket_file)); - - debug!("Initializing remote procedure calls..."); - let patch_proxy = RpcProxy::new(remote); - - debug!("Initializing command executors..."); - let patch_lock_file = self.args.work_dir.join(PATCH_OP_LOCK_NAME); - let executors = vec![ - Box::new(BuildCommandExecutor) as Box, - Box::new(PatchCommandExecutor::new(patch_proxy, patch_lock_file)) - as Box, - ]; - - let command = &self.args.command; - debug!("Invoking command: {:#?}", command); - for executor in &executors { - if let Some(exit_code) = executor.invoke(command)? { - debug!("Done"); - return Ok(exit_code); - } - } - Ok(0) + // Initialize logger + let max_log_level = if args.verbose { + LevelFilter::Trace + } else { + LevelFilter::Info + }; + let log_spec = LogSpecification::builder().default(max_log_level).build(); + let _ = Logger::with(log_spec) + .log_to_stdout() + .format(|w, _, record| write!(w, "{}", record.args())) + .write_mode(WriteMode::Direct) + .start() + .context("Failed to initialize logger")?; + + debug!("Start with {:#?}", args); + if let SubCommand::External(cmd_args) = args.subcommand { + self::exec_external_cmd(cmd_args)?; + return Ok(()); } -} -impl Drop for SyscareCLI { - fn drop(&mut self) { - self.logger.flush(); - self.logger.shutdown(); - } -} + debug!("Initializing rpc client..."); + let client = RpcClient::new(&args.work_dir).context("Failed to initialize rpc client")?; -fn main() { - let cli = match SyscareCLI::new() { - Ok(instance) => instance, - Err(e) => { - eprintln!("Error: {:?}", e); - process::exit(-1); + debug!("Invoking rpc call..."); + match &args.subcommand { + SubCommand::Info { identifiers } => { + PatchProxy::new(&client).show_patch_info(identifiers)?; } - }; - - match cli.run() { - Ok(exit_code) => { - process::exit(exit_code); + SubCommand::Target { identifiers } => { + PatchProxy::new(&client).show_patch_target(identifiers)?; } - Err(e) => { - error!("Error: {:?}", e); - - drop(cli); - process::exit(-1); + SubCommand::Status { identifiers } => { + PatchProxy::new(&client).show_patch_status(identifiers)?; + } + SubCommand::List => { + PatchProxy::new(&client).show_patch_list()?; + } + SubCommand::Check { identifiers } => { + PatchProxy::new(&client).check_patches(identifiers)?; + } + SubCommand::Apply { identifiers, force } => { + PatchProxy::new(&client).apply_patches(identifiers, *force)?; + } + SubCommand::Remove { identifiers } => { + PatchProxy::new(&client).remove_patches(identifiers)?; + } + SubCommand::Active { identifiers, force } => { + PatchProxy::new(&client).active_patches(identifiers, *force)?; } + SubCommand::Deactive { identifiers } => { + PatchProxy::new(&client).deactive_patches(identifiers)?; + } + SubCommand::Accept { identifiers } => { + PatchProxy::new(&client).accept_patches(identifiers)?; + } + SubCommand::Save => { + PatchProxy::new(&client).save_patches()?; + } + SubCommand::Restore { accepted } => { + PatchProxy::new(&client).restore_patches(*accepted)?; + } + SubCommand::Rescan => { + PatchProxy::new(&client).rescan_all_patches()?; + } + _ => unreachable!(), } + + Ok(()) } diff --git a/syscare/src/rpc/args.rs b/syscare/src/rpc/args.rs deleted file mode 100644 index a315a2199f42f205069dfc5014a55b8f8a459123..0000000000000000000000000000000000000000 --- a/syscare/src/rpc/args.rs +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::ops::Deref; - -use jsonrpc::serde_json::value::RawValue; -use serde::Serialize; - -#[derive(Debug, Default)] -pub struct RpcArguments { - args: Vec>, -} - -impl RpcArguments { - pub fn new() -> Self { - Self::default() - } - - pub fn arg(mut self, arg: T) -> Self { - self.args.push(jsonrpc::arg(arg)); - self - } -} - -impl Deref for RpcArguments { - type Target = [Box]; - - fn deref(&self) -> &Self::Target { - &self.args - } -} diff --git a/syscare/src/rpc/client.rs b/syscare/src/rpc/client.rs new file mode 100644 index 0000000000000000000000000000000000000000..e24227ac3cce5495e17fa2f36e4ee40af826e9b2 --- /dev/null +++ b/syscare/src/rpc/client.rs @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: Mulan PSL v2 +/* + * Copyright (c) 2024 Huawei Technologies Co., Ltd. + * syscare is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use std::{ + ops::Deref, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, ensure, Context, Error, Result}; +use jsonrpc::{ + serde_json::value::RawValue, + simple_uds::{self, UdsTransport}, + Client, +}; +use log::debug; +use serde::{Deserialize, Serialize}; + +use syscare_common::fs::{self, FileLock}; + +#[derive(Debug, Default)] +pub struct RpcArguments { + args: Vec>, +} + +impl RpcArguments { + pub fn new() -> Self { + Self::default() + } + + pub fn arg(mut self, arg: T) -> Self { + self.args.push(jsonrpc::arg(arg)); + self + } +} + +impl Deref for RpcArguments { + type Target = [Box]; + + fn deref(&self) -> &Self::Target { + &self.args + } +} + +pub struct RpcClient { + lock: PathBuf, + socket: PathBuf, + client: Client, +} + +impl RpcClient { + pub fn new(work_dir: &Path) -> Result { + const LOCK_FILE_NAME: &str = "syscare.lock"; + const SOCKET_FILE_NAME: &str = "syscared.sock"; + + ensure!( + work_dir.is_dir(), + "Working directory '{}' is invalid", + work_dir.display() + ); + let lock = work_dir.join(LOCK_FILE_NAME); + let socket = work_dir.join(SOCKET_FILE_NAME); + let client = Client::with_transport(UdsTransport::new(&socket)); + + Ok(Self { + lock, + socket, + client, + }) + } + + pub fn lock(&self) -> Result { + fs::flock(&self.lock, fs::FileLockType::Exclusive) + .with_context(|| format!("Failed to lock {}", self.lock.display())) + } + + pub fn call(&self, cmd: &str) -> Result + where + T: for<'a> Deserialize<'a>, + { + self.call_with_args::(cmd, RpcArguments::default()) + } + + pub fn call_with_args(&self, cmd: &str, args: RpcArguments) -> Result + where + T: for<'a> Deserialize<'a>, + { + let request = self.client.build_request(cmd, &args); + debug!("{:#?}", request); + + let response = self + .client + .send_request(request) + .map_err(|e| self.parse_request_error(e))?; + debug!("{:#?}", response); + + response.result().map_err(|e| self.parse_response_error(e)) + } +} + +impl RpcClient { + fn parse_transport_error(&self, error: Box) -> Error { + anyhow!( + "Cannot connect to syscare daemon at unix://{}, {}", + self.socket.display(), + match error.downcast::() { + Ok(err) => match *err { + simple_uds::Error::SocketError(e) => e.to_string(), + simple_uds::Error::Timeout => String::from("Connection timeout"), + simple_uds::Error::Json(_) => String::from("Invalid data format"), + }, + Err(_) => { + String::from("Unknown error") + } + } + ) + } + + fn parse_request_error(&self, error: jsonrpc::Error) -> Error { + match error { + jsonrpc::Error::Transport(err) => self.parse_transport_error(err), + jsonrpc::Error::Json(_) => anyhow!("Request format error"), + _ => anyhow!("Unknown request error"), + } + } + + fn parse_response_error(&self, error: jsonrpc::Error) -> Error { + match error { + jsonrpc::Error::Transport(err) => self.parse_transport_error(err), + jsonrpc::Error::Json(_) => anyhow!("Response format error"), + jsonrpc::Error::Rpc(e) => anyhow!(e.message), + _ => anyhow!("Unknown response error"), + } + } +} diff --git a/syscare/src/rpc/mod.rs b/syscare/src/rpc/mod.rs index 7d4d872669fe55a7821ae0e2c9fce08602f7f48c..93ba645958f91987033e80d09375d8d0131356e1 100644 --- a/syscare/src/rpc/mod.rs +++ b/syscare/src/rpc/mod.rs @@ -12,9 +12,8 @@ * See the Mulan PSL v2 for more details. */ -mod args; +mod client; mod proxy; -mod remote; -pub use proxy::*; -pub use remote::*; +pub use client::RpcClient; +pub use proxy::PatchProxy; diff --git a/syscare/src/rpc/proxy.rs b/syscare/src/rpc/proxy.rs index bb01a67f73a89a9ecdb3daefd96693dd5ead1a91..4e5966430fe2d8578833f22df660efe5fd0d1c06 100644 --- a/syscare/src/rpc/proxy.rs +++ b/syscare/src/rpc/proxy.rs @@ -12,95 +12,329 @@ * See the Mulan PSL v2 for more details. */ -use std::rc::Rc; +use std::fmt::Write; -use anyhow::Result; +use anyhow::{anyhow, Error, Result}; use function_name::named; +use log::info; use syscare_abi::{PackageInfo, PatchInfo, PatchListRecord, PatchStateRecord}; -use super::{args::RpcArguments, remote::RpcRemote}; +use super::client::{RpcArguments, RpcClient}; -pub struct RpcProxy { - remote: Rc, +pub struct PatchProxy<'a> { + client: &'a RpcClient, } -impl RpcProxy { - pub fn new(remote: Rc) -> Self { - Self { remote } +impl<'a> PatchProxy<'a> { + pub fn new(client: &'a RpcClient) -> Self { + Self { client } } +} +/* RPC methods */ +impl PatchProxy<'_> { #[named] - pub fn check_patch(&self, identifier: &str) -> Result<()> { - self.remote + fn check_patch(&self, identifier: &str) -> Result<()> { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(identifier)) } #[named] - pub fn apply_patch(&self, identifier: &str, force: bool) -> Result> { - self.remote.call_with_args( + fn apply_patch(&self, identifier: &str, force: bool) -> Result> { + self.client.call_with_args( function_name!(), RpcArguments::new().arg(identifier).arg(force), ) } #[named] - pub fn remove_patch(&self, identifier: &str) -> Result> { - self.remote + fn remove_patch(&self, identifier: &str) -> Result> { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(identifier)) } #[named] - pub fn active_patch(&self, identifier: &str, force: bool) -> Result> { - self.remote.call_with_args( + fn active_patch(&self, identifier: &str, force: bool) -> Result> { + self.client.call_with_args( function_name!(), RpcArguments::new().arg(identifier).arg(force), ) } #[named] - pub fn deactive_patch(&self, identifier: &str) -> Result> { - self.remote + fn deactive_patch(&self, identifier: &str) -> Result> { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(identifier)) } #[named] - pub fn accept_patch(&self, identifier: &str) -> Result> { - self.remote + fn accept_patch(&self, identifier: &str) -> Result> { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(identifier)) } #[named] - pub fn get_patch_list(&self) -> Result> { - self.remote.call(function_name!()) + fn get_patch_list(&self) -> Result> { + self.client.call(function_name!()) } #[named] - pub fn get_patch_status(&self, identifier: &str) -> Result> { - self.remote + fn get_patch_status(&self, identifier: &str) -> Result> { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(identifier)) } #[named] - pub fn get_patch_info(&self, identifier: &str) -> Result { - self.remote + fn get_patch_info(&self, identifier: &str) -> Result { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(identifier)) } #[named] - pub fn get_patch_target(&self, identifier: &str) -> Result { - self.remote + fn get_patch_target(&self, identifier: &str) -> Result { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(identifier)) } #[named] - pub fn save_patch_status(&self) -> Result<()> { - self.remote.call(function_name!()) + fn save_patch_status(&self) -> Result<()> { + self.client.call(function_name!()) } #[named] - pub fn restore_patch_status(&self, accepted_only: bool) -> Result<()> { - self.remote + fn restore_patch_status(&self, accepted_only: bool) -> Result<()> { + self.client .call_with_args(function_name!(), RpcArguments::new().arg(accepted_only)) } + + #[named] + fn rescan_patches(&self) -> Result<()> { + self.client.call(function_name!()) + } +} + +/* Internal methods */ +impl PatchProxy<'_> { + fn check_error(mut errors: Vec) -> Result<()> { + match errors.len() { + 0 => Ok(()), + 1 => Err(errors.remove(0)), + _ => { + let mut err_msg = String::new(); + for (idx, e) in errors.into_iter().enumerate() { + writeln!(err_msg, "{}. {}", idx, e)?; + } + err_msg.pop(); + + Err(anyhow!(err_msg)) + } + } + } +} + +/* External methods */ +impl PatchProxy<'_> { + pub fn show_patch_info(&self, identifiers: &[String]) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + + for identifier in identifiers { + match self.get_patch_info(identifier) { + Ok(patch_info) => results.push((identifier.as_str(), patch_info)), + Err(e) => errors.push(e), + } + } + + let mut result_iter = results.into_iter().peekable(); + while let Some((identifier, patch_info)) = result_iter.next() { + info!("-------------------------------------------"); + info!("Patch: {}", identifier); + info!("-------------------------------------------"); + info!("{}", patch_info); + if result_iter.peek().is_some() { + continue; + } + info!("-------------------------------------------"); + } + Self::check_error(errors) + } + + pub fn show_patch_target(&self, identifiers: &[String]) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + + for identifier in identifiers { + match self.get_patch_target(identifier) { + Ok(pkg_info) => results.push((identifier.as_str(), pkg_info)), + Err(e) => errors.push(e), + } + } + + let mut result_iter = results.into_iter().peekable(); + while let Some((identifier, pkg_info)) = result_iter.next() { + info!("-------------------------------------------"); + info!("Patch: {}", identifier); + info!("-------------------------------------------"); + info!("{}", pkg_info); + if result_iter.peek().is_some() { + continue; + } + info!("-------------------------------------------"); + } + Self::check_error(errors) + } + + pub fn show_patch_status(&self, identifiers: &[String]) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + + for identifier in identifiers { + match self.get_patch_status(identifier) { + Ok(status_list) => results.extend(status_list), + Err(e) => errors.push(e), + } + } + + for record in results { + info!("{}: {}", record.name, record.status) + } + Self::check_error(errors) + } + + pub fn show_patch_list(&self) -> Result<()> { + let _ = self.client.lock()?; + + let patch_list = self.get_patch_list()?; + + info!("{:<40} {:<60} {:<12}", "Uuid", "Name", "Status"); + for patch in patch_list { + info!("{:<40} {:<60} {:<12}", patch.uuid, patch.name, patch.status) + } + + Ok(()) + } + + pub fn check_patches(&self, identifiers: &[String]) -> Result<()> { + let _ = self.client.lock()?; + + let mut errors = vec![]; + for identifier in identifiers { + if let Err(e) = self.check_patch(identifier) { + errors.push(e); + } + } + Self::check_error(errors) + } + + pub fn apply_patches(&self, identifiers: &[String], force: bool) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + for identifier in identifiers { + match self.apply_patch(identifier, force) { + Ok(status_list) => results.extend(status_list), + Err(e) => errors.push(e), + } + } + + for result in results { + info!("{}: {}", result.name, result.status); + } + Self::check_error(errors) + } + + pub fn remove_patches(&self, identifiers: &[String]) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + for identifier in identifiers { + match self.remove_patch(identifier) { + Ok(status_list) => results.extend(status_list), + Err(e) => errors.push(e), + } + } + + for result in results { + info!("{}: {}", result.name, result.status); + } + Self::check_error(errors) + } + + pub fn active_patches(&self, identifiers: &[String], force: bool) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + for identifier in identifiers { + match self.active_patch(identifier, force) { + Ok(status_list) => results.extend(status_list), + Err(e) => errors.push(e), + } + } + + for result in results { + info!("{}: {}", result.name, result.status); + } + Self::check_error(errors) + } + + pub fn deactive_patches(&self, identifiers: &[String]) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + for identifier in identifiers { + match self.deactive_patch(identifier) { + Ok(status_list) => results.extend(status_list), + Err(e) => errors.push(e), + } + } + + for result in results { + info!("{}: {}", result.name, result.status); + } + Self::check_error(errors) + } + + pub fn accept_patches(&self, identifiers: &[String]) -> Result<()> { + let _ = self.client.lock()?; + + let mut results = vec![]; + let mut errors = vec![]; + for identifier in identifiers { + match self.accept_patch(identifier) { + Ok(status_list) => results.extend(status_list), + Err(e) => errors.push(e), + } + } + + for result in results { + info!("{}: {}", result.name, result.status); + } + Self::check_error(errors) + } + + pub fn save_patches(&self) -> Result<()> { + let _ = self.client.lock()?; + self.save_patch_status() + } + + pub fn restore_patches(&self, accepted_only: bool) -> Result<()> { + let _ = self.client.lock()?; + self.restore_patch_status(accepted_only) + } + + pub fn rescan_all_patches(&self) -> Result<()> { + let _ = self.client.lock()?; + self.rescan_patches() + } } diff --git a/syscare/src/rpc/remote.rs b/syscare/src/rpc/remote.rs deleted file mode 100644 index bd4c5988fc0a3bfb959da6cc367092b0f9476528..0000000000000000000000000000000000000000 --- a/syscare/src/rpc/remote.rs +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscare is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::{Path, PathBuf}; - -use anyhow::{anyhow, Result}; -use jsonrpc::{simple_uds::UdsTransport, Client, Error}; -use log::debug; -use serde::Deserialize; - -use super::args::RpcArguments; - -pub struct RpcRemote { - socket: PathBuf, - client: Client, -} - -impl RpcRemote { - pub fn new>(file_path: P) -> Self { - Self { - socket: file_path.as_ref().to_path_buf(), - client: Client::with_transport(UdsTransport::new(file_path)), - } - } - - pub fn call(&self, cmd: &str) -> Result - where - T: for<'a> Deserialize<'a>, - { - self.call_with_args::(cmd, RpcArguments::default()) - } - - pub fn call_with_args(&self, cmd: &str, args: RpcArguments) -> Result - where - T: for<'a> Deserialize<'a>, - { - let request = self.client.build_request(cmd, &args); - debug!("{:#?}", request); - - let response = self - .client - .send_request(request) - .map_err(|e| self.parse_error(e))?; - debug!("{:#?}", response); - - response.result().map_err(|e| self.parse_error(e)) - } -} - -impl RpcRemote { - fn parse_error(&self, error: Error) -> anyhow::Error { - match error { - Error::Transport(e) => { - anyhow!( - "Cannot connect to syscare daemon at unix://{}, {}", - self.socket.display(), - e.source() - .map(|e| e.to_string()) - .unwrap_or_else(|| "Connection timeout".to_string()) - ) - } - Error::Json(e) => { - debug!("Json parse error: {:?}", e); - anyhow!("Failed to parse response") - } - Error::Rpc(ref e) => match e.message == "Method not found" { - true => { - anyhow!("Method is unimplemented") - } - false => { - anyhow!("{}", e.message) - } - }, - _ => { - debug!("{:?}", error); - anyhow!("Response is invalid") - } - } - } -} diff --git a/syscared/Cargo.toml b/syscared/Cargo.toml index 27f27e2c257179d9b4ca912414cf3fde14c258e4..2eecea802f589482315de94f5e22ef137cd8d77f 100644 --- a/syscared/Cargo.toml +++ b/syscared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syscared" -version = "1.2.1" +version = "1.2.2" authors = ["renoseven "] description = "Syscare daemon" license = "MulanPSL-2.0" @@ -16,7 +16,7 @@ clap = { version = "3.2", features = ["cargo", "derive"] } daemonize = { version = "0.5" } flexi_logger = { version = "0.24", features = ["compress"] } inotify = { version = "0.9" } -indexmap = { version = "1.9" } +indexmap = { version = "1.9", features = ["serde"] } jsonrpc-core = { version = "18.0" } jsonrpc-derive = { version = "18.0" } jsonrpc-ipc-server = { version = "18.0" } diff --git a/syscared/build.rs b/syscared/build.rs index ea4bee737480d6408bb19903ae33b1af6bd297db..3a07aa1d7cbb27859cfd6bad0306681eac531e3d 100644 --- a/syscared/build.rs +++ b/syscared/build.rs @@ -12,26 +12,25 @@ * See the Mulan PSL v2 for more details. */ -use std::{env, process::Command}; +use std::{env, ffi::OsStr, os::unix::ffi::OsStrExt, process::Command}; fn rewrite_version() { - const ENV_VERSION_NAME: &str = "BUILD_VERSION"; - const PKG_VERSION_NAME: &str = "CARGO_PKG_VERSION"; + const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + const ENV_VERSION: Option<&str> = option_env!("BUILD_VERSION"); - let version = env::var(ENV_VERSION_NAME).unwrap_or_else(|_| { - let pkg_version = env::var(PKG_VERSION_NAME).expect("Failed to fetch package version"); - let git_output = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|output| String::from_utf8(output.stdout).expect("Failed to fetch git version")); - - match git_output { - Ok(git_version) => format!("{}-g{}", pkg_version, git_version), - Err(_) => pkg_version, - } - }); - - println!("cargo:rustc-env={}={}", PKG_VERSION_NAME, version); + println!( + "cargo:rustc-env=CARGO_PKG_VERSION={}", + ENV_VERSION.map(String::from).unwrap_or_else(|| { + Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|output| { + let git_version = OsStr::from_bytes(&output.stdout).to_string_lossy(); + format!("{}-g{}", PKG_VERSION, git_version) + }) + .unwrap_or_else(|_| PKG_VERSION.to_string()) + }) + ); } fn main() { diff --git a/syscared/src/args.rs b/syscared/src/args.rs index 4c28dffd8c664921dc3c8733673e3a8ed23fa37d..f98b54b23dcb93fc51717cc854d084496558ed2f 100644 --- a/syscared/src/args.rs +++ b/syscared/src/args.rs @@ -37,7 +37,6 @@ const DEFAULT_LOG_LEVEL: &str = "info"; global_setting(AppSettings::DeriveDisplayOrder), term_width(120), )] - pub struct Arguments { /// Run as a daemon #[clap(short, long)] diff --git a/syscared/src/config.rs b/syscared/src/config.rs index af98a5180ccc9b4b70a7903d8672769e95b69ab3..5e0d7361d843f39d49df3af00c3c05134009282a 100644 --- a/syscared/src/config.rs +++ b/syscared/src/config.rs @@ -12,15 +12,36 @@ * See the Mulan PSL v2 for more details. */ -use std::path::Path; +use std::{ + ffi::OsString, + path::{Path, PathBuf}, +}; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; use syscare_common::fs; +const DEFAULT_MAX_LOG_SIZE: u64 = 2 * 1024 * 1024; +const DEFAULT_MAX_LOG_NUM: usize = 7; + const DEFAULT_SOCKET_UID: u32 = 0; const DEFAULT_SOCKET_GID: u32 = 0; +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct LogConfig { + pub max_file_size: u64, + pub max_file_num: usize, +} + +impl Default for LogConfig { + fn default() -> Self { + Self { + max_file_size: DEFAULT_MAX_LOG_SIZE, + max_file_num: DEFAULT_MAX_LOG_NUM, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SocketConfig { pub uid: u32, @@ -37,29 +58,52 @@ impl Default for SocketConfig { } #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct DaemonConfig { - pub socket: SocketConfig, +pub struct KernelPatchConfig { + pub blocked: Vec, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct UserPatchConfig { + pub skipped: Vec, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct PatchConfig { + pub kpatch: KernelPatchConfig, + pub upatch: UserPatchConfig, } #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Config { - pub daemon: DaemonConfig, + pub log: LogConfig, + pub socket: SocketConfig, + pub patch: PatchConfig, } impl Config { pub fn parse>(path: P) -> Result { let config_path = path.as_ref(); - let instance = serde_yaml::from_reader(fs::open_file(config_path)?) - .map_err(|_| anyhow!("Failed to parse config {}", config_path.display()))?; - - Ok(instance) + let config = serde_yaml::from_reader(fs::open_file(config_path)?).map_err(|e| { + anyhow!( + "Failed to parse config '{}', {}", + config_path.display(), + e.to_string().to_lowercase() + ) + })?; + + Ok(config) } pub fn write>(&self, path: P) -> Result<()> { let config_path = path.as_ref(); let config_file = fs::create_file(config_path)?; - serde_yaml::to_writer(config_file, self) - .map_err(|_| anyhow!("Failed to write config {}", config_path.display()))?; + serde_yaml::to_writer(config_file, self).map_err(|e| { + anyhow!( + "Failed to write config '{}', {}", + config_path.display(), + e.to_string().to_lowercase() + ) + })?; Ok(()) } diff --git a/syscared/src/fast_reboot/manager.rs b/syscared/src/fast_reboot/manager.rs deleted file mode 100644 index 8a4a928da6526660838d9d0655313a4f58ac4a6e..0000000000000000000000000000000000000000 --- a/syscared/src/fast_reboot/manager.rs +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::PathBuf; - -use anyhow::{Context, Result}; -use lazy_static::lazy_static; -use log::{error, info}; - -use syscare_common::{ - fs, - os::{grub, kernel}, -}; - -lazy_static! { - static ref BOOT_DIRECTORY: PathBuf = PathBuf::from("/boot"); -} - -pub enum RebootOption { - Normal, - Forced, -} - -struct LoadKernelOption { - name: String, - kernel: PathBuf, - initramfs: PathBuf, -} - -pub struct KExecManager; - -impl KExecManager { - fn find_kernel(kernel_version: &str) -> Result { - info!("Finding kernel {}...", kernel_version); - let kernel_file_name = format!("vmlinuz-{}", kernel_version); - let kernel_file = fs::find_file( - BOOT_DIRECTORY.as_path(), - kernel_file_name, - fs::FindOptions { - fuzz: false, - recursive: false, - }, - ) - .with_context(|| format!("Cannot find kernel {}", kernel_version))?; - - info!("Finding initramfs..."); - let initramfs_file_name = format!("initramfs-{}.img", kernel_version); - let initramfs_file = fs::find_file( - BOOT_DIRECTORY.as_path(), - initramfs_file_name, - fs::FindOptions { - fuzz: false, - recursive: false, - }, - ) - .with_context(|| format!("Cannot find kernel {} initramfs", kernel_version))?; - - Ok(LoadKernelOption { - name: kernel_version.to_owned(), - kernel: kernel_file, - initramfs: initramfs_file, - }) - } - - fn find_kernel_by_grub() -> Result { - info!("Parsing grub configuration..."); - let entry = grub::get_boot_entry().context("Failed to read grub boot entry")?; - let entry_name = entry - .get_name() - .to_str() - .context("Failed to parse grub entry name")?; - - Ok(LoadKernelOption { - name: entry_name.to_owned(), - kernel: entry.get_kernel(), - initramfs: entry.get_initrd(), - }) - } - - pub fn load_kernel(kernel_version: Option) -> Result<()> { - let load_option = match kernel_version { - Some(version) => Self::find_kernel(&version), - None => Self::find_kernel_by_grub().or_else(|e| { - error!("{:?}", e); - let version: &str = kernel::version() - .to_str() - .context("Failed to parse current kernel version")?; - - Self::find_kernel(version) - }), - }?; - - kernel::unload().context("Failed to unload kernel")?; - - let name = load_option.name; - let kernel = load_option.kernel; - let initramfs = load_option.initramfs; - info!("Loading {:?}", name); - info!("Using kernel: {:?}", kernel); - info!("Using initrd: {:?}", initramfs); - - kernel::load(&kernel, &initramfs).context("Failed to load kernel") - } - - pub fn execute(option: RebootOption) -> Result<()> { - match option { - RebootOption::Normal => kernel::systemd_exec(), - RebootOption::Forced => kernel::force_exec(), - } - .context("Failed to execute kernel") - } -} diff --git a/syscared/src/fast_reboot/mod.rs b/syscared/src/fast_reboot/mod.rs deleted file mode 100644 index 8c40eb93fe91e1666417cc46607b320f5de56bf4..0000000000000000000000000000000000000000 --- a/syscared/src/fast_reboot/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -mod manager; - -pub use manager::*; diff --git a/syscared/src/main.rs b/syscared/src/main.rs index b840abf999ae29bc596b9e8fec1238f4b6f4bcbc..bfff3d7d2643b04e4dc97513775d2227ed921e4d 100644 --- a/syscared/src/main.rs +++ b/syscared/src/main.rs @@ -12,7 +12,7 @@ * See the Mulan PSL v2 for more details. */ -use std::{fs::Permissions, os::unix::fs::PermissionsExt, panic, process, sync::Arc}; +use std::{env, fs::Permissions, os::unix::fs::PermissionsExt, panic, process, sync::Arc}; use anyhow::{ensure, Context, Result}; use daemonize::Daemonize; @@ -22,33 +22,32 @@ use flexi_logger::{ }; use jsonrpc_core::IoHandler; use jsonrpc_ipc_server::{Server, ServerBuilder}; -use log::{debug, error, info, warn, LevelFilter, Record}; +use log::{error, info, LevelFilter, Record}; use nix::unistd::{chown, Gid, Uid}; use parking_lot::RwLock; use patch::manager::PatchManager; use signal_hook::{consts::TERM_SIGNALS, iterator::Signals, low_level::signal_name}; -use syscare_common::{fs, os}; +use syscare_common::{concat_os, fs, os}; mod args; mod config; -mod fast_reboot; mod patch; mod rpc; use args::Arguments; use config::Config; use patch::monitor::PatchMonitor; -use rpc::{ - skeleton::{FastRebootSkeleton, PatchSkeleton}, - skeleton_impl::{FastRebootSkeletonImpl, PatchSkeletonImpl}, -}; +use rpc::{skeleton::PatchSkeleton, skeleton_impl::PatchSkeletonImpl}; const DAEMON_NAME: &str = env!("CARGO_PKG_NAME"); const DAEMON_VERSION: &str = env!("CARGO_PKG_VERSION"); const DAEMON_ABOUT: &str = env!("CARGO_PKG_DESCRIPTION"); const DAEMON_UMASK: u32 = 0o077; +const PATH_ENV_NAME: &str = "PATH"; +const PATH_ENV_VALUE: &str = "/usr/libexec/syscare"; + const CONFIG_FILE_NAME: &str = "syscared.yaml"; const PID_FILE_NAME: &str = "syscared.pid"; const SOCKET_FILE_NAME: &str = "syscared.sock"; @@ -60,10 +59,6 @@ const LOG_DIR_PERM: u32 = 0o700; const SOCKET_FILE_PERM: u32 = 0o660; const SOCKET_FILE_PERM_STRICT: u32 = 0o600; -const MAIN_THREAD_NAME: &str = "main"; -const UNNAMED_THREAD_NAME: &str = ""; -const LOG_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f"; - struct Daemon { args: Arguments, config: Config, @@ -75,15 +70,14 @@ impl Daemon { now: &mut DeferredNow, record: &Record, ) -> std::io::Result<()> { + const UNNAMED_THREAD_NAME: &str = ""; + const LOG_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f"; + thread_local! { - static THREAD_NAME: String = std::thread::current().name().and_then(|name| { - if name == MAIN_THREAD_NAME { - return os::process::name().to_str(); - } - Some(name) - }) - .unwrap_or(UNNAMED_THREAD_NAME) - .to_string(); + static THREAD_NAME: String = std::thread::current() + .name() + .unwrap_or(UNNAMED_THREAD_NAME) + .to_string(); } THREAD_NAME.with(|thread_name| { @@ -101,12 +95,15 @@ impl Daemon { fn new() -> Result { // Check root permission ensure!( - os::user::id() == 0, + os::user::uid() == 0, "This command has to be run with superuser privileges (under the root user on most systems)." ); // Initialize arguments & prepare environments os::umask::set_umask(DAEMON_UMASK); + if let Some(path_env) = env::var_os(PATH_ENV_NAME) { + env::set_var(PATH_ENV_NAME, concat_os!(PATH_ENV_VALUE, ":", path_env)); + } let args = Arguments::new()?; fs::create_dir_all(&args.config_dir)?; @@ -125,11 +122,27 @@ impl Daemon { ) })?; + // Initialize config + println!("Initializing configuation..."); + let config_file = args.config_dir.join(CONFIG_FILE_NAME); + let config = match Config::parse(&config_file) { + Ok(config) => config, + Err(e) => { + eprintln!("{:?}", e); + println!("Using default configuration..."); + let config = Config::default(); + config.write(&config_file)?; + + config + } + }; + // Initialize logger let max_level = args.log_level; - let stdout_level = match args.daemon { - true => LevelFilter::Off, - false => max_level, + let stdout_level = if args.daemon { + LevelFilter::Off + } else { + max_level }; let log_spec = LogSpecification::builder().default(max_level).build(); let file_spec = FileSpec::default() @@ -140,29 +153,14 @@ impl Daemon { .format(Self::format_log) .duplicate_to_stdout(Duplicate::from(stdout_level)) .rotate( - Criterion::Age(Age::Day), + Criterion::AgeOrSize(Age::Day, config.log.max_file_size), Naming::Timestamps, - Cleanup::KeepCompressedFiles(30), + Cleanup::KeepCompressedFiles(config.log.max_file_num), ) .write_mode(WriteMode::Direct) .start() .context("Failed to initialize logger")?; - // Initialize config - debug!("Initializing configuation..."); - let config_file = args.config_dir.join(CONFIG_FILE_NAME); - let config = match Config::parse(&config_file) { - Ok(config) => config, - Err(e) => { - warn!("{:?}", e); - info!("Using default configuration..."); - let config = Config::default(); - config.write(&config_file)?; - - config - } - }; - // Print panic to log incase it really happens panic::set_hook(Box::new(|info| error!("{}", info))); @@ -189,7 +187,6 @@ impl Daemon { let mut io_handler = IoHandler::new(); io_handler.extend_with(PatchSkeletonImpl::new(patch_manager).to_delegate()); - io_handler.extend_with(FastRebootSkeletonImpl.to_delegate()); Ok(io_handler) } @@ -203,15 +200,16 @@ impl Daemon { .context("Failed to convert socket path to string")?, )?; - let socket_owner = Uid::from_raw(self.config.daemon.socket.uid); - let socket_group = Gid::from_raw(self.config.daemon.socket.gid); + let socket_owner = Uid::from_raw(self.config.socket.uid); + let socket_group = Gid::from_raw(self.config.socket.gid); chown(&socket_file, Some(socket_owner), Some(socket_group))?; fs::set_permissions( &socket_file, - match socket_owner.as_raw() == socket_group.as_raw() { - true => Permissions::from_mode(SOCKET_FILE_PERM_STRICT), - false => Permissions::from_mode(SOCKET_FILE_PERM), + if socket_owner.as_raw() == socket_group.as_raw() { + Permissions::from_mode(SOCKET_FILE_PERM_STRICT) + } else { + Permissions::from_mode(SOCKET_FILE_PERM) }, )?; @@ -228,7 +226,8 @@ impl Daemon { info!("Initializing patch manager..."); let patch_root = &self.args.data_dir; let patch_manager = Arc::new(RwLock::new( - PatchManager::new(patch_root).context("Failed to initialize patch manager")?, + PatchManager::new(&self.config.patch, patch_root) + .context("Failed to initialize patch manager")?, )); info!("Initializing patch monitor..."); diff --git a/syscared/src/patch/driver/kpatch/mod.rs b/syscared/src/patch/driver/kpatch/mod.rs index 970da92da5c9e71aa89c24d71f4fbe5d87e50841..8e39670394b4a6d8295ddbb06a5afc3fa704237c 100644 --- a/syscared/src/patch/driver/kpatch/mod.rs +++ b/syscared/src/patch/driver/kpatch/mod.rs @@ -13,18 +13,23 @@ */ use std::{ - ffi::{OsStr, OsString}, + collections::{HashMap, HashSet}, + ffi::OsString, fmt::Write, + iter::FromIterator, }; -use anyhow::{ensure, Result}; -use indexmap::{indexset, IndexMap, IndexSet}; -use log::{debug, info}; +use anyhow::{anyhow, ensure, Context, Result}; +use log::debug; use syscare_abi::PatchStatus; -use syscare_common::{concat_os, os, util::digest}; +use syscare_common::{ + concat_os, + os::{self, kernel, selinux}, + util::digest, +}; -use crate::patch::entity::{KernelPatch, KernelPatchFunction}; +use crate::{config::KernelPatchConfig, patch::entity::KernelPatch}; mod sys; mod target; @@ -32,78 +37,34 @@ mod target; use target::PatchTarget; pub struct KernelPatchDriver { - target_map: IndexMap, + target_map: HashMap, // object name -> object + blocked_targets: HashSet, } impl KernelPatchDriver { - pub fn new() -> Result { + pub fn new(config: &KernelPatchConfig) -> Result { Ok(Self { - target_map: IndexMap::new(), + target_map: HashMap::new(), + blocked_targets: HashSet::from_iter(config.blocked.iter().cloned()), }) } } impl KernelPatchDriver { - fn group_patch_targets(patch: &KernelPatch) -> IndexSet<&OsStr> { - let mut patch_targets = IndexSet::new(); - - for function in &patch.functions { - patch_targets.insert(function.object.as_os_str()); - } - patch_targets - } - - pub fn group_patch_functions( - patch: &KernelPatch, - ) -> IndexMap<&OsStr, Vec<&KernelPatchFunction>> { - let mut patch_function_map: IndexMap<&OsStr, Vec<&KernelPatchFunction>> = IndexMap::new(); - - for function in &patch.functions { - patch_function_map - .entry(function.object.as_os_str()) - .or_default() - .push(function); - } - patch_function_map - } -} - -impl KernelPatchDriver { - fn add_patch_target(&mut self, patch: &KernelPatch) { - for target_name in Self::group_patch_targets(patch) { - if !self.target_map.contains_key(target_name) { - self.target_map.insert( - target_name.to_os_string(), - PatchTarget::new(target_name.to_os_string()), - ); - } - } - } - - fn remove_patch_target(&mut self, patch: &KernelPatch) { - for target_name in Self::group_patch_targets(patch) { - if let Some(target) = self.target_map.get_mut(target_name) { - if !target.has_function() { - self.target_map.remove(target_name); - } - } - } - } - - fn add_patch_functions(&mut self, patch: &KernelPatch) { - for (target_name, functions) in Self::group_patch_functions(patch) { - if let Some(target) = self.target_map.get_mut(target_name) { - target.add_functions(patch.uuid, functions); - } + fn register_patch(&mut self, patch: &KernelPatch) { + for object_name in patch.functions.keys() { + self.target_map + .entry(object_name.clone()) + .or_insert_with(|| PatchTarget::new(object_name.clone())) + .add_patch(patch); } } - fn remove_patch_functions(&mut self, patch: &KernelPatch) { - for (target_name, functions) in Self::group_patch_functions(patch) { - if let Some(target) = self.target_map.get_mut(target_name) { - target.remove_functions(&patch.uuid, functions); - } - } + fn unregister_patch(&mut self, patch: &KernelPatch) { + self.target_map.retain(|_, object| { + object.remove_patch(patch); + object.is_patched() + }); } } @@ -124,13 +85,14 @@ impl KernelPatchDriver { const KERNEL_NAME_PREFIX: &str = "kernel-"; let patch_target = patch.pkg_name.as_str(); + if !patch_target.starts_with(KERNEL_NAME_PREFIX) { + return Ok(()); + } + let current_kernel = concat_os!(KERNEL_NAME_PREFIX, os::kernel::version()); debug!("Patch target: '{}'", patch_target); debug!("Current kernel: '{}'", current_kernel.to_string_lossy()); - if !patch_target.starts_with("KERNEL_NAME_PREFIX") { - return Ok(()); - } ensure!( current_kernel == patch_target, "Kpatch: Patch is incompatible", @@ -141,151 +103,134 @@ impl KernelPatchDriver { fn check_dependency(patch: &KernelPatch) -> Result<()> { const VMLINUX_MODULE_NAME: &str = "vmlinux"; - let mut non_exist_kmod = IndexSet::new(); - - let kmod_list = sys::list_kernel_modules()?; - for kmod_name in Self::group_patch_targets(patch) { - if kmod_name == VMLINUX_MODULE_NAME { - continue; - } - if kmod_list.iter().any(|name| name == kmod_name) { - continue; - } - non_exist_kmod.insert(kmod_name); - } - - ensure!(non_exist_kmod.is_empty(), { - let mut err_msg = String::new(); - - writeln!(&mut err_msg, "Kpatch: Patch target does not exist")?; - for kmod_name in non_exist_kmod { - writeln!(&mut err_msg, "* Module '{}'", kmod_name.to_string_lossy())?; + let depend_modules = patch.functions.keys().cloned().collect::>(); + let inserted_modules = + kernel::list_modules().context("Kpatch: Failed to list kernel modules")?; + let needed_modules = depend_modules + .difference(&inserted_modules) + .filter(|&module_name| module_name != VMLINUX_MODULE_NAME) + .collect::>(); + + ensure!(needed_modules.is_empty(), { + let mut msg = String::new(); + writeln!(msg, "Kpatch: Patch target does not exist")?; + for name in needed_modules { + writeln!(msg, "* Module '{}'", name.to_string_lossy())?; } - err_msg.pop(); - - err_msg + msg.pop(); + msg }); Ok(()) } - pub fn check_conflict_functions(&self, patch: &KernelPatch) -> Result<()> { - let mut conflict_patches = indexset! {}; - - let target_functions = Self::group_patch_functions(patch); - for (target_name, functions) in target_functions { - if let Some(target) = self.target_map.get(target_name) { - conflict_patches.extend( - target - .get_conflicts(functions) - .into_iter() - .map(|record| record.uuid), - ); - } - } - - ensure!(conflict_patches.is_empty(), { - let mut err_msg = String::new(); + pub fn check_conflicted_patches(&self, patch: &KernelPatch) -> Result<()> { + let conflicted: HashSet<_> = self + .target_map + .values() + .flat_map(|object| object.get_conflicted_patches(patch)) + .collect(); - writeln!(&mut err_msg, "Kpatch: Patch is conflicted with")?; - for uuid in conflict_patches.into_iter() { - writeln!(&mut err_msg, "* Patch '{}'", uuid)?; + ensure!(conflicted.is_empty(), { + let mut msg = String::new(); + writeln!(msg, "Kpatch: Patch is conflicted with")?; + for uuid in conflicted { + writeln!(msg, "* Patch '{}'", uuid)?; } - err_msg.pop(); - - err_msg + msg.pop(); + msg }); Ok(()) } - pub fn check_override_functions(&self, patch: &KernelPatch) -> Result<()> { - let mut override_patches = indexset! {}; - - let target_functions = Self::group_patch_functions(patch); - for (target_name, functions) in target_functions { - if let Some(target) = self.target_map.get(target_name) { - override_patches.extend( - target - .get_overrides(&patch.uuid, functions) - .into_iter() - .map(|record| record.uuid), - ); - } - } - - ensure!(override_patches.is_empty(), { - let mut err_msg = String::new(); + pub fn check_overridden_patches(&self, patch: &KernelPatch) -> Result<()> { + let overridden: HashSet<_> = self + .target_map + .values() + .flat_map(|object| object.get_overridden_patches(patch)) + .collect(); - writeln!(&mut err_msg, "Kpatch: Patch is overrided by")?; - for uuid in override_patches.into_iter() { - writeln!(&mut err_msg, "* Patch '{}'", uuid)?; + ensure!(overridden.is_empty(), { + let mut msg = String::new(); + writeln!(msg, "Kpatch: Patch is overridden by")?; + for uuid in overridden { + writeln!(msg, "* Patch '{}'", uuid)?; } - err_msg.pop(); - - err_msg + msg.pop(); + msg }); Ok(()) } } impl KernelPatchDriver { - pub fn status(&self, patch: &KernelPatch) -> Result { - sys::read_patch_status(patch) - } - - pub fn check(&self, patch: &KernelPatch) -> Result<()> { + pub fn check_patch(&self, patch: &KernelPatch) -> Result<()> { Self::check_consistency(patch)?; Self::check_compatiblity(patch)?; Self::check_dependency(patch)?; - Ok(()) } - pub fn apply(&mut self, patch: &KernelPatch) -> Result<()> { - info!( - "Applying patch '{}' ({})", - patch.uuid, - patch.patch_file.display() - ); - - sys::selinux_relable_patch(patch)?; - sys::apply_patch(patch)?; - self.add_patch_target(patch); - - Ok(()) + pub fn get_patch_status(&self, patch: &KernelPatch) -> Result { + sys::get_patch_status(&patch.status_file).map_err(|e| { + anyhow!( + "Kpatch: Failed to get patch status, {}", + e.to_string().to_lowercase() + ) + }) } - pub fn remove(&mut self, patch: &KernelPatch) -> Result<()> { - info!( - "Removing patch '{}' ({})", - patch.uuid, - patch.patch_file.display() + pub fn load_patch(&mut self, patch: &KernelPatch) -> Result<()> { + ensure!( + !self.blocked_targets.contains(&patch.target_name), + "Kpatch: Patch target '{}' is blocked", + patch.target_name.to_string_lossy(), ); - sys::remove_patch(patch)?; - self.remove_patch_target(patch); - Ok(()) + if selinux::get_status() == selinux::Status::Enforcing { + kernel::relable_module_file(&patch.patch_file).map_err(|e| { + anyhow!( + "Kpatch: Failed to relable patch file, {}", + e.to_string().to_lowercase() + ) + })?; + } + sys::load_patch(&patch.patch_file).map_err(|e| { + anyhow!( + "Kpatch: Failed to load patch, {}", + e.to_string().to_lowercase() + ) + }) } - pub fn active(&mut self, patch: &KernelPatch) -> Result<()> { - info!( - "Activating patch '{}' ({})", - patch.uuid, - patch.patch_file.display() - ); - sys::active_patch(patch)?; - self.add_patch_functions(patch); + pub fn remove_patch(&mut self, patch: &KernelPatch) -> Result<()> { + sys::remove_patch(&patch.module.name).map_err(|e| { + anyhow!( + "Kpatch: Failed to remove patch, {}", + e.to_string().to_lowercase() + ) + }) + } + + pub fn active_patch(&mut self, patch: &KernelPatch) -> Result<()> { + sys::active_patch(&patch.status_file).map_err(|e| { + anyhow!( + "Kpatch: Failed to active patch, {}", + e.to_string().to_lowercase() + ) + })?; + self.register_patch(patch); Ok(()) } - pub fn deactive(&mut self, patch: &KernelPatch) -> Result<()> { - info!( - "Deactivating patch '{}' ({})", - patch.uuid, - patch.patch_file.display() - ); - sys::deactive_patch(patch)?; - self.remove_patch_functions(patch); + pub fn deactive_patch(&mut self, patch: &KernelPatch) -> Result<()> { + sys::deactive_patch(&patch.status_file).map_err(|e| { + anyhow!( + "Kpatch: Failed to deactive patch, {}", + e.to_string().to_lowercase() + ) + })?; + self.unregister_patch(patch); Ok(()) } diff --git a/syscared/src/patch/driver/kpatch/sys.rs b/syscared/src/patch/driver/kpatch/sys.rs index ea6b68f740e749ad8a7c62dfd36121888c6379ec..838fe3b809558099c5f9bb64b306019c361c5f3c 100644 --- a/syscared/src/patch/driver/kpatch/sys.rs +++ b/syscared/src/patch/driver/kpatch/sys.rs @@ -12,106 +12,69 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::{CString, OsString}; +use std::{ffi::OsStr, fs, path::Path}; -use anyhow::{anyhow, bail, Context, Result}; use log::debug; -use nix::kmod; +use nix::errno::Errno; use syscare_abi::PatchStatus; -use syscare_common::{ffi::OsStrExt, fs, os}; +use syscare_common::os::kernel; -use crate::patch::entity::KernelPatch; +const KPATCH_STATUS_DISABLE: &str = "0"; +const KPATCH_STATUS_ENABLE: &str = "1"; -const SYS_MODULE_DIR: &str = "/sys/module"; -const KPATCH_STATUS_DISABLED: &str = "0"; -const KPATCH_STATUS_ENABLED: &str = "1"; - -pub fn list_kernel_modules() -> Result> { - let module_names = fs::list_dirs(SYS_MODULE_DIR, fs::TraverseOptions { recursive: false })? - .into_iter() - .filter_map(|dir| dir.file_name().map(|name| name.to_os_string())) - .collect(); - - Ok(module_names) -} - -pub fn selinux_relable_patch(patch: &KernelPatch) -> Result<()> { - const KPATCH_PATCH_SEC_TYPE: &str = "modules_object_t"; - - if os::selinux::get_status()? != os::selinux::Status::Enforcing { - return Ok(()); - } +pub fn load_patch>(patch_file: P) -> std::io::Result<()> { + let patch_file = patch_file.as_ref(); debug!( - "Relabeling patch module '{}'...", - patch.module_name.to_string_lossy() + "Kpatch: Inserting patch module '{}'...", + patch_file.display() ); - let mut sec_context = os::selinux::get_security_context(&patch.patch_file)?; - if sec_context.kind != KPATCH_PATCH_SEC_TYPE { - sec_context.kind = OsString::from(KPATCH_PATCH_SEC_TYPE); - os::selinux::set_security_context(&patch.patch_file, sec_context)?; - } - - Ok(()) + kernel::insert_module(patch_file) } -pub fn read_patch_status(patch: &KernelPatch) -> Result { - let sys_file = patch.sys_file.as_path(); - debug!("Reading {}", sys_file.display()); - - let status = match fs::read_to_string(sys_file) { - Ok(str) => match str.trim() { - KPATCH_STATUS_DISABLED => Ok(PatchStatus::Deactived), - KPATCH_STATUS_ENABLED => Ok(PatchStatus::Actived), - _ => bail!("Kpatch: Invalid patch status"), - }, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(PatchStatus::NotApplied), - Err(e) => Err(e), - } - .context("Kpatch: Failed to read patch status")?; +pub fn remove_patch>(module_name: S) -> std::io::Result<()> { + let module_name = module_name.as_ref(); - Ok(status) + debug!( + "Kpatch: Removing patch module '{}'...", + module_name.to_string_lossy() + ); + kernel::remove_module(module_name) } -fn write_patch_status(patch: &KernelPatch, value: &str) -> Result<()> { - let sys_file = patch.sys_file.as_path(); +pub fn active_patch>(status_file: P) -> std::io::Result<()> { + let status_file = status_file.as_ref(); - debug!("Writing '{}' to {}", value, sys_file.display()); - fs::write(sys_file, value).context("Kpatch: Failed to write patch status") -} - -pub fn apply_patch(patch: &KernelPatch) -> Result<()> { debug!( - "Inserting patch module '{}'...", - patch.module_name.to_string_lossy() + "Kpatch: Writing '{}' to '{}'...", + stringify!(KPATCH_STATUS_ENABLE), + status_file.display() ); - let patch_module = fs::open_file(&patch.patch_file)?; - kmod::finit_module( - &patch_module, - CString::new("")?.as_c_str(), - kmod::ModuleInitFlags::MODULE_INIT_IGNORE_VERMAGIC, - ) - .map_err(|e| anyhow!("Kpatch: {}", std::io::Error::from_raw_os_error(e as i32))) + fs::write(status_file, KPATCH_STATUS_ENABLE) } -pub fn remove_patch(patch: &KernelPatch) -> Result<()> { +pub fn deactive_patch>(status_file: P) -> std::io::Result<()> { + let status_file = status_file.as_ref(); + debug!( - "Removing patch module '{}'...", - patch.module_name.to_string_lossy() + "Kpatch: Writing '{}' to '{}'...", + stringify!(KPATCH_STATUS_DISABLE), + status_file.display() ); - - kmod::delete_module( - patch.module_name.to_cstring()?.as_c_str(), - kmod::DeleteModuleFlags::O_NONBLOCK, - ) - .map_err(|e| anyhow!("Kpatch: {}", std::io::Error::from_raw_os_error(e as i32))) + fs::write(status_file, KPATCH_STATUS_DISABLE) } -pub fn active_patch(patch: &KernelPatch) -> Result<()> { - self::write_patch_status(patch, KPATCH_STATUS_ENABLED) -} +pub fn get_patch_status>(status_file: P) -> std::io::Result { + let status_file = status_file.as_ref(); + if !status_file.exists() { + return Ok(PatchStatus::NotApplied); + } -pub fn deactive_patch(patch: &KernelPatch) -> Result<()> { - self::write_patch_status(patch, KPATCH_STATUS_DISABLED) + debug!("Kpatch: Reading '{}'...", status_file.display()); + match fs::read_to_string(status_file)?.trim() { + KPATCH_STATUS_DISABLE => Ok(PatchStatus::Deactived), + KPATCH_STATUS_ENABLE => Ok(PatchStatus::Actived), + _ => Err(std::io::Error::from(Errno::EINVAL)), + } } diff --git a/syscared/src/patch/driver/kpatch/target.rs b/syscared/src/patch/driver/kpatch/target.rs index beab803b19ad461ec6649ca3bb538d72d602784a..3f4a4650f8a604377e9c49ea1938e7c75f7976bb 100644 --- a/syscared/src/patch/driver/kpatch/target.rs +++ b/syscared/src/patch/driver/kpatch/target.rs @@ -12,126 +12,88 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::OsString; +use std::{ + collections::{hash_map::Entry, HashMap}, + ffi::OsString, +}; -use indexmap::IndexMap; +use indexmap::IndexSet; use uuid::Uuid; -use crate::patch::entity::KernelPatchFunction; - -#[derive(Debug)] -pub struct PatchFunction { - pub uuid: Uuid, - pub name: OsString, - pub size: u64, -} - -impl PatchFunction { - fn new(uuid: Uuid, function: &KernelPatchFunction) -> Self { - Self { - uuid, - name: function.name.to_os_string(), - size: function.new_size, - } - } - - fn is_same_function(&self, uuid: &Uuid, function: &KernelPatchFunction) -> bool { - (self.uuid == *uuid) && (self.name == function.name) && (self.size == function.new_size) - } -} +use crate::patch::entity::KernelPatch; #[derive(Debug)] pub struct PatchTarget { - name: OsString, - function_map: IndexMap>, // function addr -> function collision list + object_name: OsString, + collision_map: HashMap>, // function name -> patch collision list } impl PatchTarget { - pub fn new(name: OsString) -> Self { + pub fn new(object_name: OsString) -> Self { Self { - name, - function_map: IndexMap::new(), + object_name, + collision_map: HashMap::new(), } } } impl PatchTarget { - pub fn has_function(&self) -> bool { - self.function_map.is_empty() - } - - pub fn add_functions<'a, I>(&mut self, uuid: Uuid, functions: I) - where - I: IntoIterator, - { - for function in functions { - if self.name != function.object { - continue; + pub fn add_patch(&mut self, patch: &KernelPatch) { + if let Some(functions) = patch.functions.get(&self.object_name) { + for function in functions { + self.collision_map + .entry(function.name.clone()) + .or_default() + .insert(patch.uuid); } - self.function_map - .entry(function.old_addr) - .or_default() - .push(PatchFunction::new(uuid, function)); } } - pub fn remove_functions<'a, I>(&mut self, uuid: &Uuid, functions: I) - where - I: IntoIterator, - { - for function in functions { - if self.name != function.object { - continue; - } - if let Some(collision_list) = self.function_map.get_mut(&function.old_addr) { - if let Some(index) = collision_list - .iter() - .position(|patch_function| patch_function.is_same_function(uuid, function)) + pub fn remove_patch(&mut self, patch: &KernelPatch) { + if let Some(functions) = patch.functions.get(&self.object_name) { + for function in functions { + if let Entry::Occupied(mut entry) = self.collision_map.entry(function.name.clone()) { - collision_list.remove(index); - if collision_list.is_empty() { - self.function_map.remove(&function.old_addr); + let patch_set = entry.get_mut(); + patch_set.shift_remove(&patch.uuid); + + if patch_set.is_empty() { + entry.remove(); } } } } } -} -impl PatchTarget { - pub fn get_conflicts<'a, I>( + pub fn is_patched(&self) -> bool { + !self.collision_map.is_empty() + } + + pub fn get_conflicted_patches<'a>( &'a self, - functions: I, - ) -> impl IntoIterator - where - I: IntoIterator, - { - functions.into_iter().filter_map(move |function| { - if self.name != function.object { - return None; - } - self.function_map - .get(&function.old_addr) - .and_then(|list| list.last()) - }) + patch: &'a KernelPatch, + ) -> impl Iterator + 'a { + let functions = patch.functions.get(&self.object_name).into_iter().flatten(); + functions + .filter_map(move |function| self.collision_map.get(&function.name)) + .flatten() + .copied() + .filter(move |&uuid| uuid != patch.uuid) } - pub fn get_overrides<'a, I>( + pub fn get_overridden_patches<'a>( &'a self, - uuid: &'a Uuid, - functions: I, - ) -> impl IntoIterator - where - I: IntoIterator, - { - functions.into_iter().filter_map(move |function| { - if self.name != function.object { - return None; - } - self.function_map - .get(&function.old_addr) - .and_then(|list| list.last()) - .filter(|patch_function| !patch_function.is_same_function(uuid, function)) - }) + patch: &'a KernelPatch, + ) -> impl Iterator + 'a { + let functions = patch.functions.get(&self.object_name).into_iter().flatten(); + functions + .filter_map(move |function| self.collision_map.get(&function.name)) + .flat_map(move |collision_list| { + collision_list + .iter() + .copied() + .skip_while(move |&uuid| uuid != patch.uuid) + .skip(1) + }) } } diff --git a/syscared/src/patch/driver/mod.rs b/syscared/src/patch/driver/mod.rs index d64c2d4112aa84524dd5ed9490247b5f12d350ae..3752ca850478714afd99b9f6a385aba04de5fa69 100644 --- a/syscared/src/patch/driver/mod.rs +++ b/syscared/src/patch/driver/mod.rs @@ -14,7 +14,7 @@ use anyhow::{Context, Result}; -use log::info; +use log::{debug, info}; use syscare_abi::PatchStatus; mod kpatch; @@ -29,6 +29,8 @@ pub enum PatchOpFlag { Force, } +use crate::config::PatchConfig; + use super::entity::Patch; pub struct PatchDriver { @@ -37,30 +39,30 @@ pub struct PatchDriver { } impl PatchDriver { - fn check_conflict_functions(&self, patch: &Patch) -> Result<()> { + fn check_conflicted_patches(&self, patch: &Patch) -> Result<()> { match patch { - Patch::KernelPatch(patch) => self.kpatch.check_conflict_functions(patch), - Patch::UserPatch(patch) => self.upatch.check_conflict_functions(patch), + Patch::KernelPatch(kpatch) => self.kpatch.check_conflicted_patches(kpatch), + Patch::UserPatch(upatch) => self.upatch.check_conflicted_patches(upatch), } } - fn check_override_functions(&self, patch: &Patch) -> Result<()> { + fn check_overridden_patches(&self, patch: &Patch) -> Result<()> { match patch { - Patch::KernelPatch(patch) => self.kpatch.check_override_functions(patch), - Patch::UserPatch(patch) => self.upatch.check_override_functions(patch), + Patch::KernelPatch(kpatch) => self.kpatch.check_overridden_patches(kpatch), + Patch::UserPatch(upatch) => self.upatch.check_overridden_patches(upatch), } } } impl PatchDriver { - pub fn new() -> Result { + pub fn new(config: &PatchConfig) -> Result { info!("Initializing kernel patch driver..."); - let kpatch_driver = - KernelPatchDriver::new().context("Failed to initialize kernel patch driver")?; + let kpatch_driver = KernelPatchDriver::new(&config.kpatch) + .context("Failed to initialize kernel patch driver")?; info!("Initializing user patch driver..."); - let upatch_driver = - UserPatchDriver::new().context("Failed to initialize user patch driver")?; + let upatch_driver = UserPatchDriver::new(&config.upatch) + .context("Failed to initialize user patch driver")?; Ok(Self { kpatch: kpatch_driver, @@ -68,54 +70,62 @@ impl PatchDriver { }) } - /// Fetch and return the patch status. - pub fn patch_status(&self, patch: &Patch) -> Result { - match patch { - Patch::KernelPatch(patch) => self.kpatch.status(patch), - Patch::UserPatch(patch) => self.upatch.status(patch), + /// Perform patch confliction check.
+ /// Used for patch check. + pub fn check_confliction(&self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { + if flag == PatchOpFlag::Force { + return Ok(()); } - .with_context(|| format!("Failed to get patch '{}' status", patch)) + self.check_conflicted_patches(patch) + .with_context(|| format!("Patch '{}' is conflicted", patch)) } /// Perform patch file intergrity & consistency check.
/// Should be used before patch application. pub fn check_patch(&self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { + info!("Checking patch '{}'...", patch); + if flag == PatchOpFlag::Force { return Ok(()); } match patch { - Patch::KernelPatch(patch) => self.kpatch.check(patch), - Patch::UserPatch(patch) => self.upatch.check(patch), + Patch::KernelPatch(kpatch) => self.kpatch.check_patch(kpatch), + Patch::UserPatch(upatch) => self.upatch.check_patch(upatch), } .with_context(|| format!("Patch '{}' is not patchable", patch)) } - /// Perform patch confliction check.
- /// Used for patch check. - pub fn check_confliction(&self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { - if flag == PatchOpFlag::Force { - return Ok(()); + /// Fetch and return the patch status. + pub fn get_patch_status(&self, patch: &Patch) -> Result { + debug!("Fetching patch '{}' status...", patch); + + match patch { + Patch::KernelPatch(kpatch) => self.kpatch.get_patch_status(kpatch), + Patch::UserPatch(upatch) => self.upatch.get_patch_status(upatch), } - self.check_conflict_functions(patch) - .with_context(|| format!("Patch '{}' is conflicted", patch)) + .with_context(|| format!("Failed to get patch '{}' status", patch)) } - /// Apply a patch.
+ /// Load a patch.
/// After this action, the patch status would be changed to 'DEACTIVED'. - pub fn apply_patch(&mut self, patch: &Patch) -> Result<()> { + pub fn load_patch(&mut self, patch: &Patch) -> Result<()> { + info!("Loading patch '{}'...", patch); + match patch { - Patch::KernelPatch(patch) => self.kpatch.apply(patch), - Patch::UserPatch(patch) => self.upatch.apply(patch), + Patch::KernelPatch(kpatch) => self.kpatch.load_patch(kpatch), + Patch::UserPatch(upatch) => self.upatch.load_patch(upatch), } - .with_context(|| format!("Failed to apply patch '{}'", patch)) + .with_context(|| format!("Failed to load patch '{}'", patch)) } /// Remove a patch.
/// After this action, the patch status would be changed to 'NOT-APPLIED'. pub fn remove_patch(&mut self, patch: &Patch) -> Result<()> { + info!("Removing patch '{}'...", patch); + match patch { - Patch::KernelPatch(patch) => self.kpatch.remove(patch), - Patch::UserPatch(patch) => self.upatch.remove(patch), + Patch::KernelPatch(kpatch) => self.kpatch.remove_patch(kpatch), + Patch::UserPatch(upatch) => self.upatch.remove_patch(upatch), } .with_context(|| format!("Failed to remove patch '{}'", patch)) } @@ -123,12 +133,15 @@ impl PatchDriver { /// Active a patch.
/// After this action, the patch status would be changed to 'ACTIVED'. pub fn active_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { + info!("Activating patch '{}'...", patch); + if flag != PatchOpFlag::Force { - self.check_conflict_functions(patch)?; + self.check_conflicted_patches(patch)?; } + match patch { - Patch::KernelPatch(patch) => self.kpatch.active(patch), - Patch::UserPatch(patch) => self.upatch.active(patch), + Patch::KernelPatch(kpatch) => self.kpatch.active_patch(kpatch), + Patch::UserPatch(upatch) => self.upatch.active_patch(upatch), } .with_context(|| format!("Failed to active patch '{}'", patch)) } @@ -136,12 +149,15 @@ impl PatchDriver { /// Deactive a patch.
/// After this action, the patch status would be changed to 'DEACTIVED'. pub fn deactive_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { + info!("Deactivating patch '{}'...", patch); + if flag != PatchOpFlag::Force { - self.check_override_functions(patch)?; + self.check_overridden_patches(patch)?; } + match patch { - Patch::KernelPatch(patch) => self.kpatch.deactive(patch), - Patch::UserPatch(patch) => self.upatch.deactive(patch), + Patch::KernelPatch(kpatch) => self.kpatch.deactive_patch(kpatch), + Patch::UserPatch(upatch) => self.upatch.deactive_patch(upatch), } .with_context(|| format!("Failed to deactive patch '{}'", patch)) } diff --git a/syscared/src/patch/driver/upatch/entity.rs b/syscared/src/patch/driver/upatch/entity.rs deleted file mode 100644 index 80932c4f3a2156fd0776847bc17cb993a061d9cb..0000000000000000000000000000000000000000 --- a/syscared/src/patch/driver/upatch/entity.rs +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::PathBuf; - -use indexmap::{indexset, IndexSet}; - -#[derive(Debug)] -pub struct PatchEntity { - pub patch_file: PathBuf, - process_list: IndexSet, - ignored_list: IndexSet, -} - -impl PatchEntity { - pub fn new(patch_file: PathBuf) -> Self { - Self { - patch_file, - process_list: indexset! {}, - ignored_list: indexset! {}, - } - } -} - -impl PatchEntity { - pub fn add_process(&mut self, pid: i32) { - self.process_list.insert(pid); - } - - pub fn remove_process(&mut self, pid: i32) { - self.process_list.remove(&pid); - } - - pub fn ignore_process(&mut self, pid: i32) { - self.ignored_list.insert(pid); - } - - pub fn clean_dead_process(&mut self, process_list: &IndexSet) { - self.process_list.retain(|pid| process_list.contains(pid)); - self.ignored_list.retain(|pid| process_list.contains(pid)); - } - - pub fn need_ignored(&self, process_list: &IndexSet) -> IndexSet { - process_list - .intersection(&self.ignored_list) - .copied() - .collect() - } - - pub fn need_actived(&self, process_list: &IndexSet) -> IndexSet { - process_list - .difference(&self.process_list) - .copied() - .collect() - } - - pub fn need_deactived(&self, process_list: &IndexSet) -> IndexSet { - process_list - .intersection(&self.process_list) - .copied() - .collect() - } -} diff --git a/syscared/src/patch/driver/upatch/mod.rs b/syscared/src/patch/driver/upatch/mod.rs index 66eecf5add1a034ce17567c85852c5b54b415648..58f01df9c8c18ab0c47412e6e903cc38e9ee9844 100644 --- a/syscared/src/patch/driver/upatch/mod.rs +++ b/syscared/src/patch/driver/upatch/mod.rs @@ -13,15 +13,16 @@ */ use std::{ + collections::{HashMap, HashSet}, ffi::OsStr, fmt::Write, + iter::FromIterator, os::linux::fs::MetadataExt, path::{Path, PathBuf}, sync::Arc, }; -use anyhow::{bail, ensure, Context, Result}; -use indexmap::{indexset, IndexMap, IndexSet}; +use anyhow::{bail, ensure, Result}; use log::{debug, info, warn}; use parking_lot::RwLock; use uuid::Uuid; @@ -29,9 +30,8 @@ use uuid::Uuid; use syscare_abi::PatchStatus; use syscare_common::{fs, util::digest}; -use crate::patch::{driver::upatch::entity::PatchEntity, entity::UserPatch}; +use crate::{config::UserPatchConfig, patch::entity::UserPatch}; -mod entity; mod monitor; mod sys; mod target; @@ -40,29 +40,33 @@ use monitor::UserPatchMonitor; use target::PatchTarget; pub struct UserPatchDriver { - status_map: IndexMap, - target_map: Arc>>, + status_map: HashMap, + target_map: Arc>>, + skipped_files: Arc>, monitor: UserPatchMonitor, } impl UserPatchDriver { - pub fn new() -> Result { - let status_map = IndexMap::new(); - let target_map = Arc::new(RwLock::new(IndexMap::new())); - let monitor = UserPatchMonitor::new(target_map.clone(), Self::patch_new_process)?; - let instance = Self { - status_map, - target_map, - monitor, - }; - - Ok(instance) + pub fn new(config: &UserPatchConfig) -> Result { + let target_map = Arc::new(RwLock::new(HashMap::new())); + let skipped_files = Arc::new(HashSet::from_iter(config.skipped.iter().cloned())); + + Ok(Self { + status_map: HashMap::new(), + target_map: target_map.clone(), + skipped_files: skipped_files.clone(), + monitor: UserPatchMonitor::new(move |target_elfs| { + for target_elf in target_elfs { + Self::patch_new_process(&target_map, &skipped_files, target_elf); + } + })?, + }) } } impl UserPatchDriver { #[inline] - fn get_patch_status(&self, uuid: &Uuid) -> PatchStatus { + fn read_patch_status(&self, uuid: &Uuid) -> PatchStatus { self.status_map .get(uuid) .copied() @@ -70,7 +74,7 @@ impl UserPatchDriver { } #[inline] - fn set_patch_status(&mut self, uuid: &Uuid, value: PatchStatus) { + fn write_patch_status(&mut self, uuid: &Uuid, value: PatchStatus) { *self.status_map.entry(*uuid).or_default() = value; } @@ -79,28 +83,6 @@ impl UserPatchDriver { } } -impl UserPatchDriver { - fn add_patch_target(&mut self, patch: &UserPatch) { - let target_elf = patch.target_elf.as_path(); - let mut target_map = self.target_map.write(); - - if !target_map.contains_key(target_elf) { - target_map.insert(target_elf.to_path_buf(), PatchTarget::default()); - } - } - - fn remove_patch_target(&mut self, patch: &UserPatch) { - let target_elf = patch.target_elf.as_path(); - let mut target_map = self.target_map.write(); - - if let Some(target) = target_map.get_mut(target_elf) { - if !target.is_patched() { - target_map.remove(target_elf); - } - } - } -} - impl UserPatchDriver { fn check_consistency(patch: &UserPatch) -> Result<()> { let real_checksum = digest::file(&patch.patch_file)?; @@ -114,56 +96,39 @@ impl UserPatchDriver { Ok(()) } - fn check_compatiblity(_patch: &UserPatch) -> Result<()> { - Ok(()) - } - - pub fn check_conflict_functions(&self, patch: &UserPatch) -> Result<()> { - let conflict_patches = match self.target_map.read().get(&patch.target_elf) { - Some(target) => target - .get_conflicts(&patch.functions) - .into_iter() - .map(|record| record.uuid) - .collect(), - None => indexset! {}, + pub fn check_conflicted_patches(&self, patch: &UserPatch) -> Result<()> { + let conflicted = match self.target_map.read().get(&patch.target_elf) { + Some(target) => target.get_conflicted_patches(patch).collect(), + None => HashSet::new(), }; - ensure!(conflict_patches.is_empty(), { - let mut err_msg = String::new(); - - writeln!(&mut err_msg, "Upatch: Patch is conflicted with")?; - for uuid in conflict_patches.into_iter() { - writeln!(&mut err_msg, "* Patch '{}'", uuid)?; + ensure!(conflicted.is_empty(), { + let mut msg = String::new(); + writeln!(msg, "Upatch: Patch is conflicted with")?; + for uuid in conflicted.into_iter() { + writeln!(msg, "* Patch '{}'", uuid)?; } - err_msg.pop(); - - err_msg + msg.pop(); + msg }); Ok(()) } - pub fn check_override_functions(&self, patch: &UserPatch) -> Result<()> { - let override_patches = match self.target_map.read().get(&patch.target_elf) { - Some(target) => target - .get_overrides(&patch.uuid, &patch.functions) - .into_iter() - .map(|record| record.uuid) - .collect(), - None => indexset! {}, + pub fn check_overridden_patches(&self, patch: &UserPatch) -> Result<()> { + let overridden = match self.target_map.read().get(&patch.target_elf) { + Some(target) => target.get_overridden_patches(patch).collect(), + None => HashSet::new(), }; - ensure!(override_patches.is_empty(), { - let mut err_msg = String::new(); - - writeln!(&mut err_msg, "Upatch: Patch is overrided by")?; - for uuid in override_patches.into_iter() { - writeln!(&mut err_msg, "* Patch '{}'", uuid)?; + ensure!(overridden.is_empty(), { + let mut msg = String::new(); + writeln!(msg, "Upatch: Patch is overridden by")?; + for uuid in overridden.into_iter() { + writeln!(msg, "* Patch '{}'", uuid)?; } - err_msg.pop(); - - err_msg + msg.pop(); + msg }); - Ok(()) } } @@ -178,8 +143,11 @@ impl UserPatchDriver { .and_then(Result::ok) } - fn find_target_process>(target_elf: P) -> Result> { - let mut target_pids = IndexSet::new(); + fn find_target_process>( + skipped_files: &HashSet, + target_elf: P, + ) -> Result> { + let mut target_pids = HashSet::new(); let target_path = target_elf.as_ref(); let target_inode = target_path.metadata()?.st_ino(); @@ -192,6 +160,9 @@ impl UserPatchDriver { Ok(file_path) => file_path, Err(_) => continue, }; + if skipped_files.contains(&exec_path) { + continue; + } // Try to match binary path if exec_path == target_path { target_pids.insert(pid); @@ -219,10 +190,11 @@ impl UserPatchDriver { } fn patch_new_process( - target_map: Arc>>, + target_map: &RwLock>, + skipped_files: &HashSet, target_elf: &Path, ) { - let process_list = match Self::find_target_process(target_elf) { + let process_list = match Self::find_target_process(skipped_files, target_elf) { Ok(pids) => pids, Err(_) => return, }; @@ -232,35 +204,29 @@ impl UserPatchDriver { Some(target) => target, None => return, }; + patch_target.clean_dead_process(&process_list); - for (patch_uuid, patch_entity) in patch_target.all_patches() { - patch_entity.clean_dead_process(&process_list); - - // Active patch - let need_ignored = patch_entity.need_ignored(&process_list); - - let mut need_actived = patch_entity.need_actived(&process_list); - need_actived.retain(|pid| !need_ignored.contains(pid)); + let all_patches = patch_target.all_patches().collect::>(); + for (uuid, patch_file) in all_patches { + let need_actived = patch_target.need_actived_process(&process_list, &uuid); if !need_actived.is_empty() { - debug!( - "Activating patch '{}' ({}) for process {:?}", - patch_uuid, + info!( + "Upatch: Activating patch '{}' ({}) on new process {:?}", + uuid, target_elf.display(), need_actived, ); } - - for pid in need_actived { - match sys::active_patch(patch_uuid, pid, target_elf, &patch_entity.patch_file) { - Ok(_) => patch_entity.add_process(pid), + for &pid in &need_actived { + match sys::active_patch(&uuid, pid, target_elf, &patch_file) { + Ok(_) => patch_target.process_register_patch(pid, &uuid), Err(e) => { warn!( - "Upatch: Failed to active patch '{}' for process {}, {}", - patch_uuid, + "Upatch: Failed to active patch '{}' on process {}, {}", + uuid, pid, e.to_string().to_lowercase(), ); - patch_entity.ignore_process(pid) } } } @@ -269,202 +235,152 @@ impl UserPatchDriver { } impl UserPatchDriver { - pub fn status(&self, patch: &UserPatch) -> Result { - Ok(self.get_patch_status(&patch.uuid)) - } - - pub fn check(&self, patch: &UserPatch) -> Result<()> { + pub fn check_patch(&self, patch: &UserPatch) -> Result<()> { Self::check_consistency(patch)?; - Self::check_compatiblity(patch)?; - Ok(()) } - pub fn apply(&mut self, patch: &UserPatch) -> Result<()> { - info!( - "Applying patch '{}' ({})", - patch.uuid, - patch.patch_file.display() - ); - - self.add_patch_target(patch); - self.set_patch_status(&patch.uuid, PatchStatus::Deactived); + pub fn get_patch_status(&self, patch: &UserPatch) -> Result { + Ok(self.read_patch_status(&patch.uuid)) + } + pub fn load_patch(&mut self, patch: &UserPatch) -> Result<()> { + self.write_patch_status(&patch.uuid, PatchStatus::Deactived); Ok(()) } - pub fn remove(&mut self, patch: &UserPatch) -> Result<()> { - info!( - "Removing patch '{}' ({})", - patch.uuid, - patch.patch_file.display() - ); - - self.remove_patch_target(patch); + pub fn remove_patch(&mut self, patch: &UserPatch) -> Result<()> { self.remove_patch_status(&patch.uuid); - Ok(()) } - pub fn active(&mut self, patch: &UserPatch) -> Result<()> { - let patch_uuid = &patch.uuid; - let patch_file = patch.patch_file.as_path(); - let patch_functions = patch.functions.as_slice(); - let target_elf = patch.target_elf.as_path(); - - let process_list = Self::find_target_process(target_elf)?; + pub fn active_patch(&mut self, patch: &UserPatch) -> Result<()> { + let process_list = Self::find_target_process(&self.skipped_files, &patch.target_elf)?; let mut target_map = self.target_map.write(); - let patch_target = target_map - .get_mut(target_elf) - .context("Upatch: Cannot find patch target")?; - let mut patch_entity = match patch_target.get_patch(patch_uuid) { - Some(_) => bail!("Upatch: Patch is already exist"), - None => PatchEntity::new(patch_file.to_path_buf()), - }; + let patch_target = target_map.entry(patch.target_elf.clone()).or_default(); + patch_target.clean_dead_process(&process_list); + + // If target is not patched before, start watching it + let start_watch = !patch_target.is_patched(); // Active patch + let need_actived = patch_target.need_actived_process(&process_list, &patch.uuid); info!( - "Activating patch '{}' ({}) for {}", - patch_uuid, - patch_file.display(), - target_elf.display(), + "Upatch: Activating patch '{}' ({}) on process {:?}", + patch.uuid, + patch.target_elf.display(), + need_actived, ); + let mut results = Vec::new(); - for pid in patch_entity.need_actived(&process_list) { - let result = sys::active_patch(patch_uuid, pid, target_elf, patch_file); - match result { - Ok(_) => patch_entity.add_process(pid), - Err(_) => patch_entity.ignore_process(pid), - } + for pid in need_actived { + let result = sys::active_patch(&patch.uuid, pid, &patch.target_elf, &patch.patch_file); results.push((pid, result)); } - // Check results, return error if all process fails + // Return error if all process fails if !results.is_empty() && results.iter().all(|(_, result)| result.is_err()) { - let mut err_msg = String::new(); - - writeln!(err_msg, "Upatch: Failed to active patch")?; + let mut msg = String::new(); + writeln!(msg, "Upatch: Failed to active patch")?; for (pid, result) in &results { if let Err(e) = result { - writeln!(err_msg, "* Process {}: {}", pid, e)?; + writeln!(msg, "* Process {}: {}", pid, e)?; } } - err_msg.pop(); - bail!(err_msg); + msg.pop(); + bail!(msg); } - // Print failure results - for (pid, result) in &results { - if let Err(e) = result { - warn!( - "Upatch: Failed to active patch '{}' for process {}, {}", - patch_uuid, - pid, - e.to_string().to_lowercase(), - ); + // Process results + for (pid, result) in results { + match result { + Ok(_) => patch_target.process_register_patch(pid, &patch.uuid), + Err(e) => { + warn!( + "Upatch: Failed to active patch '{}' on process {}, {}", + patch.uuid, + pid, + e.to_string().to_lowercase(), + ); + } } } - - // If target is no patched before, start watching it - let need_start_watch = !patch_target.is_patched(); - - // Apply patch to target - patch_target.add_patch(*patch_uuid, patch_entity); - patch_target.add_functions(*patch_uuid, patch_functions); + patch_target.add_patch(patch); // Drop the lock drop(target_map); - if need_start_watch { - self.monitor.watch_file(target_elf)?; + if start_watch { + self.monitor.watch_file(&patch.target_elf)?; } - self.set_patch_status(patch_uuid, PatchStatus::Actived); + self.write_patch_status(&patch.uuid, PatchStatus::Actived); Ok(()) } - pub fn deactive(&mut self, patch: &UserPatch) -> Result<()> { - let patch_uuid = &patch.uuid; - let patch_file = patch.patch_file.as_path(); - let patch_functions = patch.functions.as_slice(); - let target_elf = patch.target_elf.as_path(); - - let process_list = Self::find_target_process(target_elf)?; + pub fn deactive_patch(&mut self, patch: &UserPatch) -> Result<()> { + let process_list = Self::find_target_process(&self.skipped_files, &patch.target_elf)?; let mut target_map = self.target_map.write(); - let patch_target = target_map - .get_mut(target_elf) - .context("Upatch: Cannot find patch target")?; - let patch_entity = patch_target - .get_patch(patch_uuid) - .context("Upatch: Cannot find patch entity")?; - - // Remove dead process - patch_entity.clean_dead_process(&process_list); + let patch_target = target_map.entry(patch.target_elf.clone()).or_default(); + patch_target.clean_dead_process(&process_list); // Deactive patch + let need_deactived = patch_target.need_deactived_process(&process_list, &patch.uuid); info!( - "Deactivating patch '{}' ({}) for {}", - patch_uuid, - patch_file.display(), - target_elf.display(), + "Upatch: Deactivating patch '{}' ({}) on process {:?}", + patch.uuid, + patch.target_elf.display(), + need_deactived, ); - let need_ignored = patch_entity.need_ignored(&process_list); - - let mut need_deactived = patch_entity.need_deactived(&process_list); - need_deactived.retain(|pid| need_ignored.contains(pid)); - let mut results = Vec::new(); - for pid in patch_entity.need_deactived(&process_list) { - let result = sys::deactive_patch(patch_uuid, pid, target_elf, patch_file); - if result.is_ok() { - patch_entity.remove_process(pid) - } + for pid in need_deactived { + let result = + sys::deactive_patch(&patch.uuid, pid, &patch.target_elf, &patch.patch_file); results.push((pid, result)); } - // Check results, return error if any process failes + // Return error if all process fails if !results.is_empty() && results.iter().any(|(_, result)| result.is_err()) { - let mut err_msg = String::new(); - - writeln!(err_msg, "Upatch: Failed to deactive patch")?; + let mut msg = String::new(); + writeln!(msg, "Upatch: Failed to deactive patch")?; for (pid, result) in &results { if let Err(e) = result { - writeln!(err_msg, "* Process {}: {}", pid, e)?; + writeln!(msg, "* Process {}: {}", pid, e)?; } } - err_msg.pop(); - bail!(err_msg); + msg.pop(); + bail!(msg); } - // Print failure results - for (pid, result) in &results { - if let Err(e) = result { - warn!( - "Upatch: Failed to deactive patch '{}' for process {}, {}", - patch_uuid, - pid, - e.to_string().to_lowercase(), - ); + // Process results + for (pid, result) in results { + match result { + Ok(_) => patch_target.process_unregister_patch(pid, &patch.uuid), + Err(e) => { + warn!( + "Upatch: Failed to deactive patch '{}' on process {}, {}", + patch.uuid, + pid, + e.to_string().to_lowercase(), + ); + } } } + patch_target.remove_patch(patch); - // Remove patch functions from target - patch_target.remove_patch(patch_uuid); - patch_target.remove_functions(patch_uuid, patch_functions); - - // If target is no longer patched, stop watching it - let need_stop_watch = !patch_target.is_patched(); + // If target is no longer has patch, stop watching it + let stop_watch = !patch_target.is_patched(); drop(target_map); - if need_stop_watch { - self.monitor.ignore_file(target_elf)?; + if stop_watch { + self.monitor.ignore_file(&patch.target_elf)?; } - self.set_patch_status(patch_uuid, PatchStatus::Deactived); + self.write_patch_status(&patch.uuid, PatchStatus::Deactived); Ok(()) } } diff --git a/syscared/src/patch/driver/upatch/monitor.rs b/syscared/src/patch/driver/upatch/monitor.rs index 09df2a25cd6b3f81b2a3bfe53ffedb1077498992..150d1a3526cda30f87256da578d6fe24235eeea6 100644 --- a/syscared/src/patch/driver/upatch/monitor.rs +++ b/syscared/src/patch/driver/upatch/monitor.rs @@ -20,15 +20,13 @@ use std::{ time::Duration, }; -use anyhow::{bail, Context, Result}; -use indexmap::{IndexMap, IndexSet}; +use anyhow::{anyhow, bail, Context, Result}; +use indexmap::IndexMap; use inotify::{Inotify, WatchDescriptor, WatchMask}; use log::info; use parking_lot::{Mutex, RwLock}; use syscare_common::ffi::OsStrExt; -use super::target::PatchTarget; - const MONITOR_THREAD_NAME: &str = "upatch_monitor"; const MONITOR_CHECK_PERIOD: u64 = 100; const MONITOR_EVENT_BUFFER_CAPACITY: usize = 16 * 64; // inotify event size: 16 @@ -41,12 +39,9 @@ pub(super) struct UserPatchMonitor { } impl UserPatchMonitor { - pub fn new( - patch_target_map: Arc>>, - callback: F, - ) -> Result + pub fn new(callback: F) -> Result where - F: Fn(Arc>>, &Path) + Send + Sync + 'static, + F: Fn(Vec<&Path>) + Send + Sync + 'static, { let inotify = Arc::new(Mutex::new(Some( Inotify::init().context("Failed to initialize inotify")?, @@ -56,7 +51,6 @@ impl UserPatchMonitor { let monitor_thread = MonitorThread { inotify: inotify.clone(), watch_file_map: watch_file_map.clone(), - patch_target_map, callback, } .run()?; @@ -81,13 +75,19 @@ impl UserPatchMonitor { Some(inotify) => { let wd = inotify .add_watch(watch_file, WatchMask::OPEN) - .with_context(|| format!("Failed to watch file {}", watch_file.display()))?; + .map_err(|e| { + anyhow!( + "Failed to watch file '{}', {}", + watch_file.display(), + e.to_string().to_lowercase() + ) + })?; self.watch_file_map .write() .insert(wd.clone(), watch_file.to_owned()); self.watch_wd_map.lock().insert(watch_file.to_owned(), wd); - info!("Start watching file {}", watch_file.display()); + info!("Start watching file '{}'", watch_file.display()); } None => bail!("Inotify does not exist"), } @@ -103,10 +103,14 @@ impl UserPatchMonitor { Some(inotify) => { self.watch_file_map.write().remove(&wd); - inotify.rm_watch(wd).with_context(|| { - format!("Failed to stop watch file {}", ignore_file.display()) + inotify.rm_watch(wd).map_err(|e| { + anyhow!( + "Failed to stop watch file '{}', {}", + ignore_file.display(), + e.to_string().to_lowercase() + ) })?; - info!("Stop watching file {}", ignore_file.display()); + info!("Stop watching file '{}'", ignore_file.display()); } None => bail!("Inotify does not exist"), } @@ -119,19 +123,24 @@ impl UserPatchMonitor { struct MonitorThread { inotify: Arc>>, watch_file_map: Arc>>, - patch_target_map: Arc>>, callback: F, } impl MonitorThread where - F: Fn(Arc>>, &Path) + Send + Sync + 'static, + F: Fn(Vec<&Path>) + Send + Sync + 'static, { fn run(self) -> Result> { thread::Builder::new() .name(MONITOR_THREAD_NAME.to_string()) .spawn(move || self.thread_main()) - .with_context(|| format!("Failed to create thread '{}'", MONITOR_THREAD_NAME)) + .map_err(|e| { + anyhow!( + "Failed to create thread '{}', {}", + MONITOR_THREAD_NAME, + e.to_string().to_lowercase() + ) + }) } #[inline] @@ -152,13 +161,12 @@ where if let Ok(events) = inotify.read_events(&mut buffer) { let watch_file_map = self.watch_file_map.read(); - let target_elfs = events + let path_list = events .filter_map(|event| watch_file_map.get(&event.wd)) .filter(|path| Self::filter_blacklist_path(path)) - .collect::>(); - for target_elf in target_elfs { - (self.callback)(self.patch_target_map.clone(), target_elf); - } + .map(|path| path.as_ref()) + .collect::>(); + (self.callback)(path_list) } thread::park_timeout(Duration::from_millis(MONITOR_CHECK_PERIOD)) diff --git a/syscared/src/patch/driver/upatch/sys.rs b/syscared/src/patch/driver/upatch/sys.rs index a388bc631986fbb7013298e7d8c5407402c93c19..851884017aa74fc5c7a64cf75a636dc4bc540620 100644 --- a/syscared/src/patch/driver/upatch/sys.rs +++ b/syscared/src/patch/driver/upatch/sys.rs @@ -1,14 +1,27 @@ use std::path::Path; use anyhow::{bail, Result}; -use log::Level; +use log::{debug, Level}; use uuid::Uuid; use syscare_common::process::Command; -const UPATCH_MANAGE_BIN: &str = "/usr/libexec/syscare/upatch-manage"; +const UPATCH_MANAGE_BIN: &str = "upatch-manage"; -pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) -> Result<()> { +pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: P, patch_file: Q) -> Result<()> +where + P: AsRef, + Q: AsRef, +{ + let target_elf = target_elf.as_ref(); + let patch_file = patch_file.as_ref(); + + debug!( + "Upatch: Patching '{}' to '{}' (pid: {})...", + patch_file.display(), + target_elf.display(), + pid, + ); let exit_code = Command::new(UPATCH_MANAGE_BIN) .arg("patch") .arg("--uuid") @@ -19,7 +32,7 @@ pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) .arg(target_elf) .arg("--upatch") .arg(patch_file) - .stdout(Level::Debug) + .stdout(Level::Error) .run_with_output()? .exit_code(); @@ -29,7 +42,20 @@ pub fn active_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) } } -pub fn deactive_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Path) -> Result<()> { +pub fn deactive_patch(uuid: &Uuid, pid: i32, target_elf: P, patch_file: Q) -> Result<()> +where + P: AsRef, + Q: AsRef, +{ + let target_elf = target_elf.as_ref(); + let patch_file = patch_file.as_ref(); + + debug!( + "Upatch: Unpatching '{}' from '{}' (pid: {})...", + patch_file.display(), + target_elf.display(), + pid, + ); let exit_code = Command::new(UPATCH_MANAGE_BIN) .arg("unpatch") .arg("--uuid") @@ -40,7 +66,7 @@ pub fn deactive_patch(uuid: &Uuid, pid: i32, target_elf: &Path, patch_file: &Pat .arg(target_elf) .arg("--upatch") .arg(patch_file) - .stdout(Level::Debug) + .stdout(Level::Error) .run_with_output()? .exit_code(); diff --git a/syscared/src/patch/driver/upatch/target.rs b/syscared/src/patch/driver/upatch/target.rs index 26c3ed321b440e9fb1c8e1f1872399dfdd5b1cad..be5aabcd11d938fa68e16290c5d0e6f96dcceedb 100644 --- a/syscared/src/patch/driver/upatch/target.rs +++ b/syscared/src/patch/driver/upatch/target.rs @@ -12,125 +12,134 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::OsString; +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + path::PathBuf, +}; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use uuid::Uuid; -use crate::patch::entity::UserPatchFunction; - -use super::entity::PatchEntity; - -#[derive(Debug)] -pub struct PatchFunction { - pub uuid: Uuid, - pub name: OsString, - pub size: u64, -} - -impl PatchFunction { - pub fn new(uuid: Uuid, function: &UserPatchFunction) -> Self { - Self { - uuid, - name: function.name.to_os_string(), - size: function.new_size, - } - } - - pub fn is_same_function(&self, uuid: &Uuid, function: &UserPatchFunction) -> bool { - (self.uuid == *uuid) && (self.name == function.name) && (self.size == function.new_size) - } -} +use crate::patch::entity::UserPatch; #[derive(Debug, Default)] pub struct PatchTarget { - patch_map: IndexMap, // patched file data - function_map: IndexMap>, // function addr -> function collision list + process_map: HashMap>, // pid -> patch list + patch_map: IndexMap, // uuid -> patch file + collision_map: HashMap>, // function old addr -> patch collision list } impl PatchTarget { - pub fn is_patched(&self) -> bool { - !self.patch_map.is_empty() + pub fn process_register_patch(&mut self, pid: i32, uuid: &Uuid) { + self.process_map.entry(pid).or_default().insert(*uuid); } - pub fn add_patch(&mut self, uuid: Uuid, entity: PatchEntity) { - self.patch_map.insert(uuid, entity); + pub fn process_unregister_patch(&mut self, pid: i32, uuid: &Uuid) { + if let Some(patch_list) = self.process_map.get_mut(&pid) { + patch_list.remove(uuid); + } } - pub fn remove_patch(&mut self, uuid: &Uuid) { - self.patch_map.remove(uuid); + pub fn need_actived_process(&self, process_list: &HashSet, uuid: &Uuid) -> HashSet { + let mut need_actived = HashSet::with_capacity(process_list.len()); + + for pid in process_list { + match self.process_map.get(pid) { + Some(patch_list) => { + if !patch_list.contains(uuid) { + need_actived.insert(*pid); + } + } + None => { + need_actived.insert(*pid); + } + } + } + + need_actived } - pub fn get_patch(&mut self, uuid: &Uuid) -> Option<&mut PatchEntity> { - self.patch_map.get_mut(uuid) + pub fn need_deactived_process(&self, process_list: &HashSet, uuid: &Uuid) -> HashSet { + let mut need_deactived = HashSet::with_capacity(process_list.len()); + + for pid in process_list { + if let Some(patch_list) = self.process_map.get(pid) { + if patch_list.contains(uuid) { + need_deactived.insert(*pid); + } + } + } + + need_deactived } - pub fn all_patches(&mut self) -> impl IntoIterator { - self.patch_map.iter_mut() + pub fn clean_dead_process(&mut self, process_list: &HashSet) { + self.process_map.retain(|pid, _| process_list.contains(pid)); } } impl PatchTarget { - pub fn add_functions<'a, I>(&mut self, uuid: Uuid, functions: I) - where - I: IntoIterator, - { - for function in functions { - self.function_map + pub fn add_patch(&mut self, patch: &UserPatch) { + for function in &patch.functions { + self.collision_map .entry(function.old_addr) .or_default() - .push(PatchFunction::new(uuid, function)); + .insert(patch.uuid); } + self.patch_map.insert(patch.uuid, patch.patch_file.clone()); } - pub fn remove_functions<'a, I>(&mut self, uuid: &Uuid, functions: I) - where - I: IntoIterator, - { - for function in functions { - if let Some(collision_list) = self.function_map.get_mut(&function.old_addr) { - if let Some(index) = collision_list - .iter() - .position(|patch_function| patch_function.is_same_function(uuid, function)) - { - collision_list.remove(index); - if collision_list.is_empty() { - self.function_map.remove(&function.old_addr); - } + pub fn remove_patch(&mut self, patch: &UserPatch) { + for function in &patch.functions { + if let Entry::Occupied(mut entry) = self.collision_map.entry(function.old_addr) { + let patch_set = entry.get_mut(); + patch_set.shift_remove(&patch.uuid); + + if patch_set.is_empty() { + entry.remove(); } } } + self.patch_map.remove(&patch.uuid); } -} -impl PatchTarget { - pub fn get_conflicts<'a, I>( + pub fn is_patched(&self) -> bool { + !self.collision_map.is_empty() + } + + pub fn all_patches(&self) -> impl Iterator + '_ { + self.patch_map + .iter() + .map(|(uuid, path)| (*uuid, path.to_path_buf())) + } + + pub fn get_conflicted_patches<'a>( &'a self, - functions: I, - ) -> impl IntoIterator - where - I: IntoIterator, - { - functions.into_iter().filter_map(move |function| { - self.function_map - .get(&function.old_addr) - .and_then(|list| list.last()) - }) + patch: &'a UserPatch, + ) -> impl Iterator + 'a { + patch + .functions + .iter() + .filter_map(move |function| self.collision_map.get(&function.old_addr)) + .flatten() + .copied() + .filter(move |&uuid| uuid != patch.uuid) } - pub fn get_overrides<'a, I>( + pub fn get_overridden_patches<'a>( &'a self, - uuid: &'a Uuid, - functions: I, - ) -> impl IntoIterator - where - I: IntoIterator, - { - functions.into_iter().filter_map(move |function| { - self.function_map - .get(&function.old_addr) - .and_then(|list| list.last()) - .filter(|patch_function| !patch_function.is_same_function(uuid, function)) - }) + patch: &'a UserPatch, + ) -> impl Iterator + 'a { + patch + .functions + .iter() + .filter_map(move |function| self.collision_map.get(&function.old_addr)) + .flat_map(move |collision_list| { + collision_list + .iter() + .copied() + .skip_while(move |&uuid| uuid != patch.uuid) + .skip(1) + }) } } diff --git a/syscared/src/patch/entity/kpatch.rs b/syscared/src/patch/entity/kpatch.rs index ab2c8b24ea09c4279a0fff7d04324ffb247cc4fa..09e759ddbf89990af82ab758dbd1468e17df4645 100644 --- a/syscared/src/patch/entity/kpatch.rs +++ b/syscared/src/patch/entity/kpatch.rs @@ -12,11 +12,78 @@ * See the Mulan PSL v2 for more details. */ -use std::{ffi::OsString, path::PathBuf, sync::Arc}; +use std::{ + collections::HashMap, + ffi::{CStr, OsStr, OsString}, + path::{Path, PathBuf}, + sync::Arc, +}; -use syscare_abi::{PatchInfo, PatchType}; +use anyhow::{anyhow, Context, Result}; +use object::{File, Object, ObjectSection}; use uuid::Uuid; +use syscare_abi::{PatchEntity, PatchInfo}; +use syscare_common::{ffi::CStrExt, fs, os::kernel}; + +mod ffi { + use std::os::raw::{c_char, c_long, c_ulong}; + + use object::{Pod, Relocation, SectionRelocationIterator}; + + #[repr(C)] + #[derive(Debug, Clone, Copy)] + /// Corresponds to `struct kpatch_patch_func` defined in `kpatch-patch.h` + pub struct KpatchFunction { + pub new_addr: c_ulong, + pub new_size: c_ulong, + pub old_addr: c_ulong, + pub old_size: c_ulong, + pub sympos: u64, + pub name: *const c_char, + pub obj_name: *const c_char, + pub ref_name: *const c_char, + pub ref_offset: c_long, + } + + pub const KPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); + pub const KPATCH_FUNCTION_OFFSET: usize = 40; + pub const KPATCH_OBJECT_OFFSET: usize = 48; + + /* + * SAFETY: This struct is + * - #[repr(C)] + * - have no invalid byte values + * - have no padding + */ + unsafe impl Pod for KpatchFunction {} + + pub struct KpatchRelocation { + pub _addr: (u64, Relocation), + pub name: (u64, Relocation), + pub object: (u64, Relocation), + } + + pub struct KpatchRelocationIterator<'data, 'file>(pub SectionRelocationIterator<'data, 'file>); + + impl Iterator for KpatchRelocationIterator<'_, '_> { + type Item = KpatchRelocation; + + fn next(&mut self) -> Option { + if let (Some(addr), Some(name), Some(object)) = + (self.0.next(), self.0.next(), self.0.next()) + { + return Some(KpatchRelocation { + _addr: addr, + name, + object, + }); + } + None + } + } +} + /// Kernel patch function definition #[derive(Clone)] pub struct KernelPatchFunction { @@ -61,12 +128,159 @@ impl std::fmt::Display for KernelPatchFunction { pub struct KernelPatch { pub uuid: Uuid, pub name: OsString, - pub kind: PatchType, pub info: Arc, pub pkg_name: String, - pub module_name: OsString, - pub functions: Vec, + pub target_name: OsString, pub patch_file: PathBuf, - pub sys_file: PathBuf, + pub status_file: PathBuf, + pub module: kernel::ModuleInfo, + pub functions: HashMap>, // object name -> function list pub checksum: String, } + +impl KernelPatch { + fn parse_functions(patch_file: &Path) -> Result>> { + const KPATCH_FUNCS_SECTION: &str = ".kpatch.funcs"; + const KPATCH_STRINGS_SECTION: &str = ".kpatch.strings"; + + let mmap = fs::mmap(patch_file).map_err(|e| { + anyhow!( + "Failed to mmap '{}', {}", + patch_file.display(), + e.to_string().to_lowercase() + ) + })?; + let file = File::parse(mmap.as_ref()).map_err(|e| { + anyhow!( + "Failed to parse '{}', {}", + patch_file.display(), + e.to_string().to_lowercase() + ) + })?; + + // Read sections + let function_section = file + .section_by_name(KPATCH_FUNCS_SECTION) + .with_context(|| format!("Cannot find section '{}'", KPATCH_FUNCS_SECTION))?; + let string_section = file + .section_by_name(KPATCH_STRINGS_SECTION) + .with_context(|| format!("Cannot find section '{}'", KPATCH_STRINGS_SECTION))?; + let function_data = function_section.data().map_err(|e| { + anyhow!( + "Failed to read section '{}', {}", + KPATCH_FUNCS_SECTION, + e.to_string().to_lowercase() + ) + })?; + let string_data = string_section.data().map_err(|e| { + anyhow!( + "Failed to read section '{}', {}", + KPATCH_STRINGS_SECTION, + e.to_string().to_lowercase() + ) + })?; + + // Resolve patch functions + let (slice, _) = object::slice_from_bytes::( + function_data, + function_data.len() / ffi::KPATCH_FUNCTION_SIZE, + ) + .map_err(|_| anyhow!("Invalid patch function layout"))?; + + let mut functions: Vec<_> = slice + .iter() + .map(|function| KernelPatchFunction { + name: OsString::new(), + object: OsString::new(), + old_addr: function.old_addr, + old_size: function.old_size, + new_addr: function.new_addr, + new_size: function.new_size, + }) + .collect(); + + // Relocate patch functions + for relocation in ffi::KpatchRelocationIterator(function_section.relocations()) { + let (name_offset, name_reloc) = relocation.name; + let (object_offset, obj_reloc) = relocation.object; + + // Relocate patch function name + let name_index = + (name_offset as usize - ffi::KPATCH_FUNCTION_OFFSET) / ffi::KPATCH_FUNCTION_SIZE; + let name_function = functions + .get_mut(name_index) + .with_context(|| format!("Invalid patch function index, index={}", name_index))?; + let name_addend = name_reloc.addend() as usize; + name_function.name = CStr::from_bytes_with_next_nul(&string_data[name_addend..]) + .map_err(|_| anyhow!("Invalid patch function name"))? + .to_os_string(); + + // Relocate patch function object + let object_index = + (object_offset as usize - ffi::KPATCH_OBJECT_OFFSET) / ffi::KPATCH_FUNCTION_SIZE; + let object_function = functions + .get_mut(object_index) + .with_context(|| format!("Invalid patch object index, index={}", object_index))?; + let object_addend = obj_reloc.addend() as usize; + object_function.object = CStr::from_bytes_with_next_nul(&string_data[object_addend..]) + .map_err(|_| anyhow!("Invalid patch object name"))? + .to_os_string(); + } + + // group functions by it's object + let mut function_map: HashMap<_, Vec<_>> = HashMap::new(); + for function in functions { + function_map + .entry(function.object.clone()) + .or_default() + .push(function); + } + + Ok(function_map) + } + + pub fn parse( + name: S, + patch_info: Arc, + patch_entity: &PatchEntity, + patch_file: P, + ) -> Result + where + S: AsRef, + P: AsRef, + { + const KPATCH_SYS_DIR: &str = "/sys/kernel/livepatch"; + const KPATCH_STATUS_FILE_NAME: &str = "enabled"; + + let patch_file = patch_file.as_ref(); + let module = kernel::module_info(patch_file).map_err(|e| { + anyhow!( + "Failed to parse '{}' modinfo, {}", + patch_file.display(), + e.to_string().to_lowercase() + ) + })?; + + let patch = Self { + uuid: patch_entity.uuid, + name: name.as_ref().to_os_string(), + info: patch_info.clone(), + pkg_name: patch_info.target.full_name(), + target_name: patch_entity.patch_name.as_os_str().to_os_string(), + patch_file: patch_file.to_path_buf(), + status_file: PathBuf::from(KPATCH_SYS_DIR) + .join(&module.name) + .join(KPATCH_STATUS_FILE_NAME), + module, + functions: Self::parse_functions(patch_file)?, + checksum: patch_entity.checksum.clone(), + }; + Ok(patch) + } +} + +impl std::fmt::Display for KernelPatch { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name.to_string_lossy()) + } +} diff --git a/syscared/src/patch/entity/patch.rs b/syscared/src/patch/entity/patch.rs index 2ff398a6842b9629eb6f9f84fd5a711aa320f3e5..094138991dacad809197d65f33e234cbc4d96d0e 100644 --- a/syscared/src/patch/entity/patch.rs +++ b/syscared/src/patch/entity/patch.rs @@ -78,6 +78,9 @@ impl std::cmp::Ord for Patch { impl std::fmt::Display for Patch { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name().to_string_lossy()) + match self { + Patch::KernelPatch(patch) => write!(f, "{}", patch), + Patch::UserPatch(patch) => write!(f, "{}", patch), + } } } diff --git a/syscared/src/patch/entity/symbol.rs b/syscared/src/patch/entity/symbol.rs deleted file mode 100644 index c7845aa617407cc7c66fa84dcc1b5e436a99cabf..0000000000000000000000000000000000000000 --- a/syscared/src/patch/entity/symbol.rs +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::ffi::OsString; - -/// Patch function definiation -#[derive(Clone)] -pub struct PatchFunction { - pub name: OsString, - pub target: OsString, - pub old_addr: u64, - pub old_size: u64, - pub new_addr: u64, - pub new_size: u64, -} - -impl std::fmt::Debug for PatchFunction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PatchFunction") - .field("name", &self.name) - .field("target", &self.target) - .field("old_addr", &format!("0x{}", self.old_addr)) - .field("old_size", &format!("0x{}", self.old_size)) - .field("new_addr", &format!("0x{}", self.new_addr)) - .field("new_size", &format!("0x{}", self.new_size)) - .finish() - } -} - -impl std::fmt::Display for PatchFunction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "name: {}, target: {}, old_addr: 0x{:x}, old_size: 0x{:x}, new_addr: 0x{:x}, new_size: 0x{:x}", - self.name.to_string_lossy(), - self.target.to_string_lossy(), - self.old_addr, - self.old_size, - self.new_addr, - self.new_size, - ) - } -} diff --git a/syscared/src/patch/entity/upatch.rs b/syscared/src/patch/entity/upatch.rs index ef24866bffc63f364198f5fa0c72f358384245c5..aaaee1c77e882dd815683a36e9f1dfe2e391489e 100644 --- a/syscared/src/patch/entity/upatch.rs +++ b/syscared/src/patch/entity/upatch.rs @@ -12,11 +12,66 @@ * See the Mulan PSL v2 for more details. */ -use std::{ffi::OsString, path::PathBuf, sync::Arc}; +use std::{ + ffi::{CStr, OsStr, OsString}, + path::{Path, PathBuf}, + sync::Arc, +}; -use syscare_abi::{PatchInfo, PatchType}; +use anyhow::{anyhow, Context, Result}; +use object::{File, Object, ObjectSection}; use uuid::Uuid; +use syscare_abi::{PatchEntity, PatchInfo}; +use syscare_common::{ffi::CStrExt, fs}; + +mod ffi { + use std::os::raw::{c_char, c_ulong}; + + use object::{Pod, Relocation, SectionRelocationIterator}; + + #[repr(C)] + #[derive(Debug, Clone, Copy)] + /// Corresponds to `struct upatch_path_func` defined in `upatch-patch.h` + pub struct UpatchFunction { + pub new_addr: c_ulong, + pub new_size: c_ulong, + pub old_addr: c_ulong, + pub old_size: c_ulong, + pub sympos: c_ulong, + pub name: *const c_char, + } + + /* + * SAFETY: This struct is + * - #[repr(C)] + * - have no invalid byte values + * - have no padding + */ + unsafe impl Pod for UpatchFunction {} + + pub const UPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); + pub const UPATCH_FUNCTION_OFFSET: usize = 40; + + pub struct UpatchRelocation { + pub _addr: (u64, Relocation), + pub name: (u64, Relocation), + } + + pub struct UpatchRelocationIterator<'data, 'file>(pub SectionRelocationIterator<'data, 'file>); + + impl Iterator for UpatchRelocationIterator<'_, '_> { + type Item = UpatchRelocation; + + fn next(&mut self) -> Option { + if let (Some(addr), Some(name)) = (self.0.next(), self.0.next()) { + return Some(UpatchRelocation { _addr: addr, name }); + } + None + } + } +} + /// User patch function definition #[derive(Clone)] pub struct UserPatchFunction { @@ -31,10 +86,10 @@ impl std::fmt::Debug for UserPatchFunction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UserPatchFunction") .field("name", &self.name) - .field("old_addr", &format!("0x{}", self.old_addr)) - .field("old_size", &format!("0x{}", self.old_size)) - .field("new_addr", &format!("0x{}", self.new_addr)) - .field("new_size", &format!("0x{}", self.new_size)) + .field("old_addr", &format!("0x{:x}", self.old_addr)) + .field("old_size", &format!("0x{:x}", self.old_size)) + .field("new_addr", &format!("0x{:x}", self.new_addr)) + .field("new_size", &format!("0x{:x}", self.new_size)) .finish() } } @@ -58,11 +113,120 @@ impl std::fmt::Display for UserPatchFunction { pub struct UserPatch { pub uuid: Uuid, pub name: OsString, - pub kind: PatchType, pub info: Arc, pub pkg_name: String, - pub functions: Vec, - pub patch_file: PathBuf, pub target_elf: PathBuf, + pub patch_file: PathBuf, + pub functions: Vec, pub checksum: String, } + +impl UserPatch { + fn parse_functions(patch_file: &Path) -> Result> { + const UPATCH_FUNCS_SECTION: &str = ".upatch.funcs"; + const UPATCH_STRINGS_SECTION: &str = ".upatch.strings"; + + let mmap = fs::mmap(patch_file).map_err(|e| { + anyhow!( + "Failed to mmap '{}', {}", + patch_file.display(), + e.to_string().to_lowercase() + ) + })?; + let file = File::parse(mmap.as_ref()).map_err(|e| { + anyhow!( + "Failed to parse '{}', {}", + patch_file.display(), + e.to_string().to_lowercase() + ) + })?; + + // Read sections + let function_section = file + .section_by_name(UPATCH_FUNCS_SECTION) + .with_context(|| format!("Cannot find section '{}'", UPATCH_FUNCS_SECTION))?; + let string_section = file + .section_by_name(UPATCH_STRINGS_SECTION) + .with_context(|| format!("Cannot find section '{}'", UPATCH_STRINGS_SECTION))?; + let function_data = function_section.data().map_err(|e| { + anyhow!( + "Failed to read section '{}', {}", + UPATCH_FUNCS_SECTION, + e.to_string().to_lowercase() + ) + })?; + let string_data = string_section.data().map_err(|e| { + anyhow!( + "Failed to read section '{}', {}", + UPATCH_STRINGS_SECTION, + e.to_string().to_lowercase() + ) + })?; + + // Resolve patch functions + let (slice, _) = object::slice_from_bytes::( + function_data, + function_data.len() / ffi::UPATCH_FUNCTION_SIZE, + ) + .map_err(|_| anyhow!("Invalid patch function layout"))?; + + let mut functions: Vec<_> = slice + .iter() + .map(|function| UserPatchFunction { + name: OsString::new(), + old_addr: function.old_addr, + old_size: function.old_size, + new_addr: function.new_addr, + new_size: function.new_size, + }) + .collect(); + + // Relocate patch functions + for relocation in ffi::UpatchRelocationIterator(function_section.relocations()) { + let (value, reloc) = relocation.name; + + let index = (value as usize - ffi::UPATCH_FUNCTION_OFFSET) / ffi::UPATCH_FUNCTION_SIZE; + let function = functions + .get_mut(index) + .with_context(|| format!("Invalid patch function index, index={}", index))?; + let addend = reloc.addend() as usize; + + function.name = CStr::from_bytes_with_next_nul(&string_data[addend..]) + .map_err(|_| anyhow!("Invalid patch function name"))? + .to_os_string(); + } + + Ok(functions) + } + + pub fn parse( + name: S, + patch_info: Arc, + patch_entity: &PatchEntity, + patch_file: P, + ) -> Result + where + S: AsRef, + P: AsRef, + { + let patch_file = patch_file.as_ref(); + + let patch = Self { + uuid: patch_entity.uuid, + name: name.as_ref().to_os_string(), + info: patch_info.clone(), + pkg_name: patch_info.target.full_name(), + target_elf: patch_entity.patch_target.clone(), + patch_file: patch_file.to_path_buf(), + functions: Self::parse_functions(patch_file)?, + checksum: patch_entity.checksum.clone(), + }; + Ok(patch) + } +} + +impl std::fmt::Display for UserPatch { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name.to_string_lossy()) + } +} diff --git a/syscared/src/patch/manager.rs b/syscared/src/patch/manager.rs index f6335671a844f23f6741455c26073e2418e4d0e0..724843006e74dd5d3cde2a206663cb046b6604dd 100644 --- a/syscared/src/patch/manager.rs +++ b/syscared/src/patch/manager.rs @@ -13,8 +13,6 @@ */ use std::{ - cmp::Ordering, - collections::HashMap, path::{Path, PathBuf}, str::FromStr, sync::Arc, @@ -26,10 +24,16 @@ use lazy_static::lazy_static; use log::{debug, error, info, trace, warn}; use uuid::Uuid; -use syscare_abi::PatchStatus; +use syscare_abi::{PatchEntity, PatchInfo, PatchStatus, PatchType, PATCH_INFO_MAGIC}; use syscare_common::{concat_os, ffi::OsStrExt, fs, util::serde}; -use crate::patch::resolver::PatchResolver; +use crate::{ + config::PatchConfig, + patch::{ + entity::{KernelPatch, UserPatch}, + PATCH_INFO_FILE_NAME, + }, +}; use super::{ driver::{PatchDriver, PatchOpFlag}, @@ -42,26 +46,27 @@ type TransitionAction = &'static (dyn Fn(&mut PatchManager, &Patch, PatchOpFlag) -> Result<()> + Sync); const PATCH_CHECK: TransitionAction = &PatchManager::driver_check_patch; -const PATCH_APPLY: TransitionAction = &PatchManager::driver_apply_patch; +const PATCH_LOAD: TransitionAction = &PatchManager::driver_load_patch; const PATCH_REMOVE: TransitionAction = &PatchManager::driver_remove_patch; const PATCH_ACTIVE: TransitionAction = &PatchManager::driver_active_patch; const PATCH_DEACTIVE: TransitionAction = &PatchManager::driver_deactive_patch; const PATCH_ACCEPT: TransitionAction = &PatchManager::driver_accept_patch; +const PATCH_DECLINE: TransitionAction = &PatchManager::driver_decline_patch; lazy_static! { static ref STATUS_TRANSITION_MAP: IndexMap> = indexmap! { - (PatchStatus::NotApplied, PatchStatus::Deactived) => vec![PATCH_CHECK, PATCH_APPLY], - (PatchStatus::NotApplied, PatchStatus::Actived) => vec![PATCH_CHECK, PATCH_APPLY, PATCH_ACTIVE], - (PatchStatus::NotApplied, PatchStatus::Accepted) => vec![PATCH_CHECK, PATCH_APPLY, PATCH_ACTIVE, PATCH_ACCEPT], + (PatchStatus::NotApplied, PatchStatus::Deactived) => vec![PATCH_CHECK, PATCH_LOAD], + (PatchStatus::NotApplied, PatchStatus::Actived) => vec![PATCH_CHECK, PATCH_LOAD, PATCH_ACTIVE], + (PatchStatus::NotApplied, PatchStatus::Accepted) => vec![PATCH_CHECK, PATCH_LOAD, PATCH_ACTIVE, PATCH_ACCEPT], (PatchStatus::Deactived, PatchStatus::NotApplied) => vec![PATCH_REMOVE], - (PatchStatus::Deactived, PatchStatus::Actived) => vec![PATCH_ACTIVE], + (PatchStatus::Deactived, PatchStatus::Actived) => vec![PATCH_CHECK, PATCH_ACTIVE], (PatchStatus::Deactived, PatchStatus::Accepted) => vec![PATCH_ACTIVE, PATCH_ACCEPT], (PatchStatus::Actived, PatchStatus::NotApplied) => vec![PATCH_DEACTIVE, PATCH_REMOVE], (PatchStatus::Actived, PatchStatus::Deactived) => vec![PATCH_DEACTIVE], (PatchStatus::Actived, PatchStatus::Accepted) => vec![PATCH_ACCEPT], - (PatchStatus::Accepted, PatchStatus::NotApplied) => vec![PATCH_ACCEPT, PATCH_DEACTIVE, PATCH_REMOVE], - (PatchStatus::Accepted, PatchStatus::Deactived) => vec![PATCH_ACCEPT, PATCH_DEACTIVE], - (PatchStatus::Accepted, PatchStatus::Actived) => vec![PATCH_ACCEPT], + (PatchStatus::Accepted, PatchStatus::NotApplied) => vec![PATCH_DECLINE, PATCH_DEACTIVE, PATCH_REMOVE], + (PatchStatus::Accepted, PatchStatus::Deactived) => vec![PATCH_DECLINE, PATCH_DEACTIVE], + (PatchStatus::Accepted, PatchStatus::Actived) => vec![PATCH_DECLINE], }; } @@ -76,8 +81,8 @@ pub struct PatchManager { } impl PatchManager { - pub fn new>(patch_root: P) -> Result { - let driver = PatchDriver::new()?; + pub fn new>(patch_config: &PatchConfig, patch_root: P) -> Result { + let driver = PatchDriver::new(patch_config)?; let patch_install_dir = patch_root.as_ref().join(PATCH_INSTALL_DIR); let patch_status_file = patch_root.as_ref().join(PATCH_STATUS_FILE_NAME); let patch_map = Self::scan_patches(&patch_install_dir)?; @@ -134,7 +139,7 @@ impl PatchManager { .unwrap_or_default(); if status == PatchStatus::Unknown { - status = self.driver_patch_status(patch, PatchOpFlag::Normal)?; + status = self.driver_get_patch_status(patch)?; self.set_patch_status(patch, status)?; } @@ -142,6 +147,7 @@ impl PatchManager { } pub fn check_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { + info!("Check patch '{}'", patch); self.driver.check_patch(patch, flag)?; self.driver.check_confliction(patch, flag)?; @@ -220,19 +226,16 @@ impl PatchManager { pub fn save_patch_status(&mut self) -> Result<()> { info!("Saving all patch status..."); - debug!("Updating all patch status..."); + debug!("Updating patch status..."); for patch in self.get_patch_list() { - debug!("Update patch '{}' status", patch); self.get_patch_status(&patch)?; } - let mut status_map = HashMap::new(); + debug!("Writing patch status..."); for (uuid, status) in &self.status_map { - status_map.insert(uuid, status); + debug!("Patch '{}' status: {}", uuid, status); } - - debug!("Writing patch status file"); - serde::serialize(&status_map, &self.patch_status_file) + serde::serialize(&self.status_map, &self.patch_status_file) .context("Failed to write patch status file")?; fs::sync(); @@ -243,68 +246,45 @@ impl PatchManager { pub fn restore_patch_status(&mut self, accepted_only: bool) -> Result<()> { info!("Restoring all patch status..."); + if !self.patch_status_file.exists() { + return Ok(()); + } debug!("Reading patch status..."); - let status_file = &self.patch_status_file; - let status_map: HashMap = match status_file.exists() { - true => serde::deserialize(status_file).context("Failed to read patch status")?, - false => { - warn!("Cannot find patch status file"); - return Ok(()); + let status_map: IndexMap = + serde::deserialize(&self.patch_status_file) + .context("Failed to read patch status file")?; + for (uuid, status) in &status_map { + debug!("Patch '{}' status: {}", uuid, status); + } + for (uuid, status) in status_map { + if accepted_only && (status != PatchStatus::Accepted) { + continue; } - }; - - /* - * To ensure that we won't load multiple patches for same target at the same time, - * we take a sort operation of the status to make sure do REMOVE operation at first - */ - let mut restore_list = status_map - .into_iter() - .filter_map(|(uuid, status)| match self.find_patch_by_uuid(&uuid) { + match self.find_patch_by_uuid(&uuid) { Ok(patch) => { - if accepted_only && (status != PatchStatus::Accepted) { - debug!( - "Skipped patch '{}', status is not '{}'", - patch, - PatchStatus::Accepted - ); - return None; + info!("Restore patch '{}' status to '{}'", patch, status); + if let Err(e) = self.do_status_transition(&patch, status, PatchOpFlag::Force) { + error!("{:?}", e); } - Some((patch, status)) } Err(e) => { - error!("{:?}", e); - None + error!("{}", e); } - }) - .collect::>(); - - restore_list.sort_by(|(lhs_patch, lhs_status), (rhs_patch, rhs_status)| { - match lhs_status.cmp(rhs_status) { - Ordering::Less => Ordering::Less, - Ordering::Equal => lhs_patch.cmp(rhs_patch), - Ordering::Greater => Ordering::Greater, - } - }); - - for (patch, target_status) in restore_list { - debug!("Restore patch '{}' status to '{}'", patch, target_status); - if let Err(e) = self.do_status_transition(&patch, target_status, PatchOpFlag::Force) { - error!("{}", e); } } - info!("All patch status were restored"); + info!("All patch status were restored"); Ok(()) } pub fn rescan_patches(&mut self) -> Result<()> { self.patch_map = Self::scan_patches(&self.patch_install_dir)?; - let status_keys = self.status_map.keys().cloned().collect::>(); + let status_keys = self.status_map.keys().copied().collect::>(); for patch_uuid in status_keys { if !self.patch_map.contains_key(&patch_uuid) { - trace!("Patch '{}' was removed, remove its status", patch_uuid); + trace!("Patch '{}' was removed", patch_uuid); self.status_map.remove(&patch_uuid); } } @@ -356,19 +336,110 @@ impl PatchManager { } impl PatchManager { + fn parse_user_patch( + root_dir: &Path, + patch_info: Arc, + patch_entity: &PatchEntity, + ) -> Result { + let patch_name = concat_os!( + patch_info.target.short_name(), + "/", + patch_info.name(), + "/", + patch_entity.patch_target.file_name().unwrap_or_default() + ); + let patch_file = root_dir.join(&patch_entity.patch_name); + + let patch = UserPatch::parse(&patch_name, patch_info, patch_entity, patch_file) + .with_context(|| { + format!( + "Failed to parse patch '{}' ({})", + patch_entity.uuid, + patch_name.to_string_lossy(), + ) + })?; + + debug!("Found patch '{}' ({})", patch.uuid, patch); + Ok(patch) + } + + fn parse_kernel_patch( + root_dir: &Path, + patch_info: Arc, + patch_entity: &PatchEntity, + ) -> Result { + const KPATCH_EXTENSION: &str = "ko"; + + let patch_name = concat_os!( + patch_info.target.short_name(), + "/", + patch_info.name(), + "/", + &patch_entity.patch_target, + ); + let mut patch_file = root_dir.join(&patch_entity.patch_name); + patch_file.set_extension(KPATCH_EXTENSION); + + let patch = KernelPatch::parse(&patch_name, patch_info, patch_entity, patch_file) + .with_context(|| { + format!( + "Failed to parse patch '{}' ({})", + patch_entity.uuid, + patch_name.to_string_lossy(), + ) + })?; + + debug!("Found patch '{}' ({})", patch.uuid, patch); + Ok(patch) + } + + fn parse_patches(root_dir: &Path) -> Result> { + let root_name = root_dir.file_name().expect("Invalid patch root directory"); + let patch_metadata = root_dir.join(PATCH_INFO_FILE_NAME); + let patch_info = Arc::new( + serde::deserialize_with_magic::(patch_metadata, PATCH_INFO_MAGIC) + .with_context(|| { + format!( + "Failed to parse patch '{}' metadata", + root_name.to_string_lossy(), + ) + })?, + ); + + patch_info + .entities + .iter() + .map(|patch_entity| { + let patch_info = patch_info.clone(); + match patch_info.kind { + PatchType::UserPatch => Ok(Patch::UserPatch(Self::parse_user_patch( + root_dir, + patch_info, + patch_entity, + )?)), + PatchType::KernelPatch => Ok(Patch::KernelPatch(Self::parse_kernel_patch( + root_dir, + patch_info, + patch_entity, + )?)), + } + }) + .collect::>>() + } + fn scan_patches>(directory: P) -> Result>> { const TRAVERSE_OPTION: fs::TraverseOptions = fs::TraverseOptions { recursive: false }; let mut patch_map = IndexMap::new(); - info!("Scanning patches from {}...", directory.as_ref().display()); - for patch_root in fs::list_dirs(directory, TRAVERSE_OPTION)? { - let resolve_result = PatchResolver::resolve_patch(&patch_root) - .with_context(|| format!("Failed to resolve patch from {}", patch_root.display())); - match resolve_result { + info!( + "Scanning patches from '{}'...", + directory.as_ref().display() + ); + for root_dir in fs::list_dirs(directory, TRAVERSE_OPTION)? { + match Self::parse_patches(&root_dir) { Ok(patches) => { for patch in patches { - debug!("Detected patch '{}'", patch); patch_map.insert(*patch.uuid(), Arc::new(patch)); } } @@ -426,14 +497,12 @@ impl PatchManager { bail!("Cannot set patch '{}' status to '{}'", patch, value); } - let status_map = &mut self.status_map; - match status_map.get_mut(patch.uuid()) { - Some(status) => { - *status = value; - } - None => { - status_map.insert(*patch.uuid(), value); - } + let uuid = *patch.uuid(); + let (curr_index, _) = self.status_map.insert_full(uuid, value); + + let last_index = self.status_map.len().saturating_sub(1); + if curr_index != last_index { + self.status_map.move_index(curr_index, last_index); } Ok(()) @@ -441,16 +510,16 @@ impl PatchManager { } impl PatchManager { - fn driver_patch_status(&self, patch: &Patch, _flag: PatchOpFlag) -> Result { - self.driver.patch_status(patch) - } - fn driver_check_patch(&mut self, patch: &Patch, flag: PatchOpFlag) -> Result<()> { self.driver.check_patch(patch, flag) } - fn driver_apply_patch(&mut self, patch: &Patch, _flag: PatchOpFlag) -> Result<()> { - self.driver.apply_patch(patch)?; + fn driver_get_patch_status(&self, patch: &Patch) -> Result { + self.driver.get_patch_status(patch) + } + + fn driver_load_patch(&mut self, patch: &Patch, _flag: PatchOpFlag) -> Result<()> { + self.driver.load_patch(patch)?; self.set_patch_status(patch, PatchStatus::Deactived) } @@ -472,6 +541,10 @@ impl PatchManager { fn driver_accept_patch(&mut self, patch: &Patch, _flag: PatchOpFlag) -> Result<()> { self.set_patch_status(patch, PatchStatus::Accepted) } + + fn driver_decline_patch(&mut self, patch: &Patch, _flag: PatchOpFlag) -> Result<()> { + self.set_patch_status(patch, PatchStatus::Actived) + } } impl Drop for PatchManager { diff --git a/syscared/src/patch/mod.rs b/syscared/src/patch/mod.rs index 26a97da69a3659b2c67522bef9b1b6cb2ee80930..edd247abd8fb842ebf46d8bfd2b43fa7c923499d 100644 --- a/syscared/src/patch/mod.rs +++ b/syscared/src/patch/mod.rs @@ -16,7 +16,6 @@ pub mod driver; pub mod entity; pub mod manager; pub mod monitor; -pub mod resolver; pub mod transaction; const PATCH_INFO_FILE_NAME: &str = "patch_info"; diff --git a/syscared/src/patch/monitor.rs b/syscared/src/patch/monitor.rs index 17987814d526449d601ba921c4abefce29387a8c..c793600f9d96ba937705c86ec3bd0684131f087b 100644 --- a/syscared/src/patch/monitor.rs +++ b/syscared/src/patch/monitor.rs @@ -44,13 +44,13 @@ impl PatchMonitor { patch_root: P, patch_manager: Arc>, ) -> Result { - let patch_root = patch_root.as_ref().join(PATCH_INSTALL_DIR); + let patch_install_dir = patch_root.as_ref().join(PATCH_INSTALL_DIR); let inotify = Arc::new(Mutex::new(Some({ let mut inotify = Inotify::init().context("Failed to initialize inotify")?; inotify .add_watch( - &patch_root, + &patch_install_dir, WatchMask::CREATE | WatchMask::DELETE | WatchMask::ONLYDIR, ) .context("Failed to monitor patch directory")?; @@ -59,7 +59,7 @@ impl PatchMonitor { }))); let monitor_thread = MonitorThread { - patch_root, + patch_root: patch_install_dir, inotify: inotify.clone(), patch_manager, } diff --git a/syscared/src/patch/resolver/kpatch.rs b/syscared/src/patch/resolver/kpatch.rs deleted file mode 100644 index 524482e5d2b47c9c64ac8e44379e321e12ca6160..0000000000000000000000000000000000000000 --- a/syscared/src/patch/resolver/kpatch.rs +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - ffi::{CStr, OsString}, - path::{Path, PathBuf}, - sync::Arc, -}; - -use anyhow::{anyhow, Context, Result}; -use object::{NativeFile, Object, ObjectSection}; - -use syscare_abi::{PatchEntity, PatchInfo, PatchType}; -use syscare_common::{ - concat_os, - ffi::{CStrExt, OsStrExt}, - fs, -}; - -use super::PatchResolverImpl; -use crate::patch::entity::{KernelPatch, KernelPatchFunction, Patch}; - -const KPATCH_SUFFIX: &str = ".ko"; -const KPATCH_SYS_DIR: &str = "/sys/kernel/livepatch"; -const KPATCH_SYS_FILE_NAME: &str = "enabled"; - -mod ffi { - use std::os::raw::{c_char, c_long, c_ulong}; - - use object::{ - read::elf::{ElfSectionRelocationIterator, FileHeader}, - Pod, Relocation, - }; - - #[repr(C)] - #[derive(Debug, Clone, Copy)] - /// Corresponds to `struct kpatch_patch_func` defined in `kpatch-patch.h` - pub struct KpatchFunction { - pub new_addr: c_ulong, - pub new_size: c_ulong, - pub old_addr: c_ulong, - pub old_size: c_ulong, - pub sympos: u64, - pub name: *const c_char, - pub obj_name: *const c_char, - pub ref_name: *const c_char, - pub ref_offset: c_long, - } - - pub const KPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); - pub const KPATCH_FUNCTION_OFFSET: usize = 40; - pub const KPATCH_OBJECT_OFFSET: usize = 48; - - /* - * SAFETY: This struct is - * - #[repr(C)] - * - have no invalid byte values - * - have no padding - */ - unsafe impl Pod for KpatchFunction {} - - pub struct KpatchRelocation { - pub addr: (u64, Relocation), - pub name: (u64, Relocation), - pub object: (u64, Relocation), - } - - pub struct KpatchRelocationIterator<'data, 'file, Elf: FileHeader>( - ElfSectionRelocationIterator<'data, 'file, Elf, &'data [u8]>, - ); - - impl<'data, 'file, Elf: FileHeader> KpatchRelocationIterator<'data, 'file, Elf> { - pub fn new(relocations: ElfSectionRelocationIterator<'data, 'file, Elf>) -> Self { - Self(relocations) - } - } - - impl<'data, 'file, Elf: FileHeader> Iterator for KpatchRelocationIterator<'data, 'file, Elf> { - type Item = KpatchRelocation; - - fn next(&mut self) -> Option { - if let (Some(addr), Some(name), Some(object)) = - (self.0.next(), self.0.next(), self.0.next()) - { - return Some(KpatchRelocation { addr, name, object }); - } - None - } - } -} - -use ffi::*; - -const KPATCH_FUNCS_SECTION: &str = ".kpatch.funcs"; -const KPATCH_STRINGS_SECTION: &str = ".kpatch.strings"; - -pub struct KpatchResolverImpl; - -impl KpatchResolverImpl { - #[inline] - fn resolve_patch_file(patch: &mut KernelPatch) -> Result<()> { - let patch_file = - fs::MappedFile::open(&patch.patch_file).context("Failed to map patch file")?; - let patch_elf = NativeFile::parse(patch_file.as_bytes()).context("Invalid patch format")?; - - // Read sections - let function_section = patch_elf - .section_by_name(KPATCH_FUNCS_SECTION) - .with_context(|| format!("Cannot find section '{}'", KPATCH_FUNCS_SECTION))?; - let string_section = patch_elf - .section_by_name(KPATCH_STRINGS_SECTION) - .with_context(|| format!("Cannot find section '{}'", KPATCH_STRINGS_SECTION))?; - let function_data = function_section - .data() - .with_context(|| format!("Failed to read section '{}'", KPATCH_FUNCS_SECTION))?; - let string_data = string_section - .data() - .with_context(|| format!("Failed to read section '{}'", KPATCH_FUNCS_SECTION))?; - - // Resolve patch functions - let patch_functions = &mut patch.functions; - let kpatch_function_slice = object::slice_from_bytes::( - function_data, - function_data.len() / KPATCH_FUNCTION_SIZE, - ) - .map(|(f, _)| f) - .map_err(|_| anyhow!("Invalid data format")) - .context("Failed to resolve patch functions")?; - - for function in kpatch_function_slice { - patch_functions.push(KernelPatchFunction { - name: OsString::new(), - object: OsString::new(), - old_addr: function.old_addr, - old_size: function.old_size, - new_addr: function.new_addr, - new_size: function.new_size, - }); - } - - // Relocate patch functions - for relocation in KpatchRelocationIterator::new(function_section.relocations()) { - let (name_reloc_offset, name_reloc) = relocation.name; - let (object_reloc_offset, obj_reloc) = relocation.object; - - // Relocate patch function name - let name_index = - (name_reloc_offset as usize - KPATCH_FUNCTION_OFFSET) / KPATCH_FUNCTION_SIZE; - let name_function = patch_functions - .get_mut(name_index) - .context("Failed to find patch function")?; - let name_offset = name_reloc.addend() as usize; - let name_string = CStr::from_bytes_with_next_nul(&string_data[name_offset..]) - .context("Failed to parse patch object name")? - .to_os_string(); - - name_function.name = name_string; - - // Relocate patch function object - let object_index = - (object_reloc_offset as usize - KPATCH_OBJECT_OFFSET) / KPATCH_FUNCTION_SIZE; - let object_function = patch_functions - .get_mut(object_index) - .context("Failed to find patch function")?; - let object_offset = obj_reloc.addend() as usize; - let object_string = CStr::from_bytes_with_next_nul(&string_data[object_offset..]) - .context("Failed to parse patch function name")? - .to_os_string(); - - object_function.object = object_string; - } - - Ok(()) - } -} - -impl PatchResolverImpl for KpatchResolverImpl { - fn resolve_patch( - &self, - patch_root: &Path, - patch_info: Arc, - patch_entity: &PatchEntity, - ) -> Result { - let module_name = patch_entity.patch_name.replace(['-', '.'], "_"); - let patch_file = patch_root.join(concat_os!(&patch_entity.patch_name, KPATCH_SUFFIX)); - let sys_file = PathBuf::from(KPATCH_SYS_DIR) - .join(&module_name) - .join(KPATCH_SYS_FILE_NAME); - - let mut patch = KernelPatch { - uuid: patch_entity.uuid, - name: concat_os!( - patch_info.target.short_name(), - "/", - patch_info.name(), - "/", - &patch_entity.patch_target - ), - kind: PatchType::KernelPatch, - info: patch_info.clone(), - pkg_name: patch_info.target.full_name(), - module_name, - patch_file, - sys_file, - functions: Vec::new(), - checksum: patch_entity.checksum.clone(), - }; - Self::resolve_patch_file(&mut patch).context("Failed to resolve patch")?; - - Ok(Patch::KernelPatch(patch)) - } -} diff --git a/syscared/src/patch/resolver/mod.rs b/syscared/src/patch/resolver/mod.rs deleted file mode 100644 index 80e26ea028b03ad67c2ceab73f4d654e66b2de4d..0000000000000000000000000000000000000000 --- a/syscared/src/patch/resolver/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{path::Path, sync::Arc}; - -use super::{entity::Patch, PATCH_INFO_FILE_NAME}; - -use anyhow::{Context, Result}; -use syscare_abi::{PatchEntity, PatchInfo, PatchType, PATCH_INFO_MAGIC}; -use syscare_common::util::serde; - -mod kpatch; -mod upatch; - -use kpatch::KpatchResolverImpl; -use upatch::UpatchResolverImpl; - -pub trait PatchResolverImpl { - fn resolve_patch( - &self, - patch_root: &Path, - patch_info: Arc, - patch_entity: &PatchEntity, - ) -> Result; -} - -pub struct PatchResolver; - -impl PatchResolver { - pub fn resolve_patch>(patch_root: P) -> Result> { - let patch_root = patch_root.as_ref(); - let patch_info = Arc::new( - serde::deserialize_with_magic::( - patch_root.join(PATCH_INFO_FILE_NAME), - PATCH_INFO_MAGIC, - ) - .context("Failed to resolve patch metadata")?, - ); - let resolver = match patch_info.kind { - PatchType::UserPatch => &UpatchResolverImpl as &dyn PatchResolverImpl, - PatchType::KernelPatch => &KpatchResolverImpl as &dyn PatchResolverImpl, - }; - - let mut patch_list = Vec::with_capacity(patch_info.entities.len()); - for patch_entity in &patch_info.entities { - let patch = resolver.resolve_patch(patch_root, patch_info.clone(), patch_entity)?; - patch_list.push(patch); - } - - Ok(patch_list) - } -} diff --git a/syscared/src/patch/resolver/upatch.rs b/syscared/src/patch/resolver/upatch.rs deleted file mode 100644 index 5df11db5abf7689bb798e1556861d6e8eaab95e6..0000000000000000000000000000000000000000 --- a/syscared/src/patch/resolver/upatch.rs +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - ffi::{CStr, OsString}, - path::Path, - sync::Arc, -}; - -use anyhow::{anyhow, Context, Result}; -use object::{NativeFile, Object, ObjectSection}; - -use syscare_abi::{PatchEntity, PatchInfo, PatchType}; -use syscare_common::{concat_os, ffi::CStrExt, fs}; - -use super::PatchResolverImpl; -use crate::patch::entity::{Patch, UserPatch, UserPatchFunction}; - -mod ffi { - use std::os::raw::{c_char, c_ulong}; - - use object::{ - read::elf::{ElfSectionRelocationIterator, FileHeader}, - Pod, Relocation, - }; - - #[repr(C)] - #[derive(Debug, Clone, Copy)] - /// Corresponds to `struct upatch_path_func` defined in `upatch-patch.h` - pub struct UpatchFunction { - pub new_addr: c_ulong, - pub new_size: c_ulong, - pub old_addr: c_ulong, - pub old_size: c_ulong, - pub sympos: c_ulong, - pub name: *const c_char, - } - - /* - * SAFETY: This struct is - * - #[repr(C)] - * - have no invalid byte values - * - have no padding - */ - unsafe impl Pod for UpatchFunction {} - - pub const UPATCH_FUNCTION_SIZE: usize = std::mem::size_of::(); - pub const UPATCH_FUNCTION_OFFSET: usize = 40; - - pub struct UpatchRelocation { - pub addr: (u64, Relocation), - pub name: (u64, Relocation), - } - - pub struct UpatchRelocationIterator<'data, 'file, Elf: FileHeader>( - ElfSectionRelocationIterator<'data, 'file, Elf, &'data [u8]>, - ); - - impl<'data, 'file, Elf: FileHeader> UpatchRelocationIterator<'data, 'file, Elf> { - pub fn new(relocations: ElfSectionRelocationIterator<'data, 'file, Elf>) -> Self { - Self(relocations) - } - } - - impl<'data, 'file, Elf: FileHeader> Iterator for UpatchRelocationIterator<'data, 'file, Elf> { - type Item = UpatchRelocation; - - fn next(&mut self) -> Option { - if let (Some(addr), Some(name)) = (self.0.next(), self.0.next()) { - return Some(UpatchRelocation { addr, name }); - } - None - } - } -} - -use ffi::*; - -const UPATCH_FUNCS_SECTION: &str = ".upatch.funcs"; -const UPATCH_STRINGS_SECTION: &str = ".upatch.strings"; - -pub struct UpatchResolverImpl; - -impl UpatchResolverImpl { - #[inline] - fn resolve_patch_elf(patch: &mut UserPatch) -> Result<()> { - let patch_file = - fs::MappedFile::open(&patch.patch_file).context("Failed to map patch file")?; - let patch_elf = NativeFile::parse(patch_file.as_bytes()).context("Invalid patch format")?; - - // Read sections - let function_section = patch_elf - .section_by_name(UPATCH_FUNCS_SECTION) - .with_context(|| format!("Cannot find section '{}'", UPATCH_FUNCS_SECTION))?; - let string_section = patch_elf - .section_by_name(UPATCH_STRINGS_SECTION) - .with_context(|| format!("Cannot find section '{}'", UPATCH_STRINGS_SECTION))?; - let function_data = function_section - .data() - .with_context(|| format!("Failed to read section '{}'", UPATCH_FUNCS_SECTION))?; - let string_data = string_section - .data() - .with_context(|| format!("Failed to read section '{}'", UPATCH_FUNCS_SECTION))?; - - // Resolve patch functions - let patch_functions = &mut patch.functions; - let upatch_function_slice = object::slice_from_bytes::( - function_data, - function_data.len() / UPATCH_FUNCTION_SIZE, - ) - .map(|(f, _)| f) - .map_err(|_| anyhow!("Invalid data format")) - .context("Failed to resolve patch functions")?; - - for function in upatch_function_slice { - patch_functions.push(UserPatchFunction { - name: OsString::new(), - old_addr: function.old_addr, - old_size: function.old_size, - new_addr: function.new_addr, - new_size: function.new_size, - }); - } - - // Relocate patch functions - for relocation in UpatchRelocationIterator::new(function_section.relocations()) { - let (name_reloc_offset, name_reloc) = relocation.name; - - let name_index = - (name_reloc_offset as usize - UPATCH_FUNCTION_OFFSET) / UPATCH_FUNCTION_SIZE; - let name_function = patch_functions - .get_mut(name_index) - .context("Failed to find patch function")?; - let name_offset = name_reloc.addend() as usize; - let name_string = CStr::from_bytes_with_next_nul(&string_data[name_offset..]) - .context("Failed to parse patch function name")? - .to_os_string(); - - name_function.name = name_string; - } - - Ok(()) - } -} - -impl PatchResolverImpl for UpatchResolverImpl { - fn resolve_patch( - &self, - patch_root: &Path, - patch_info: Arc, - patch_entity: &PatchEntity, - ) -> Result { - let mut patch = UserPatch { - uuid: patch_entity.uuid, - name: concat_os!( - patch_info.target.short_name(), - "/", - patch_info.name(), - "/", - fs::file_name(&patch_entity.patch_target) - ), - kind: PatchType::UserPatch, - info: patch_info.clone(), - pkg_name: patch_info.target.full_name(), - patch_file: patch_root.join(&patch_entity.patch_name), - target_elf: patch_entity.patch_target.clone(), - functions: Vec::new(), - checksum: patch_entity.checksum.clone(), - }; - Self::resolve_patch_elf(&mut patch).context("Failed to resolve patch")?; - - Ok(Patch::UserPatch(patch)) - } -} diff --git a/syscared/src/rpc/skeleton/fast_reboot.rs b/syscared/src/rpc/skeleton/fast_reboot.rs deleted file mode 100644 index 1a7b496bdd8515a6730ac24141e9a36bee39e011..0000000000000000000000000000000000000000 --- a/syscared/src/rpc/skeleton/fast_reboot.rs +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use super::function::{rpc, RpcResult}; - -#[rpc(server)] -pub trait FastRebootSkeleton { - #[rpc(name = "fast_reboot")] - fn fast_reboot(&self, kernel_version: Option, force: bool) -> RpcResult<()>; -} diff --git a/syscared/src/rpc/skeleton/mod.rs b/syscared/src/rpc/skeleton/mod.rs index 74456cade90e9cf709ffe87f58ac6c48e734d9d9..6fa6b604066f5ce1e8ffd2e751b702675fcebd48 100644 --- a/syscared/src/rpc/skeleton/mod.rs +++ b/syscared/src/rpc/skeleton/mod.rs @@ -14,8 +14,6 @@ use super::function; -mod fast_reboot; mod patch; -pub use fast_reboot::*; pub use patch::*; diff --git a/syscared/src/rpc/skeleton/patch.rs b/syscared/src/rpc/skeleton/patch.rs index caeb446b1e44a2477fb908af02c370f7c734d655..99902b50bb24feccc15c9d2ca2724ad907cadf30 100644 --- a/syscared/src/rpc/skeleton/patch.rs +++ b/syscared/src/rpc/skeleton/patch.rs @@ -53,4 +53,7 @@ pub trait PatchSkeleton { #[rpc(name = "restore_patch_status")] fn restore_patch_status(&self, accepted_only: bool) -> RpcResult<()>; + + #[rpc(name = "rescan_patches")] + fn rescan_patches(&self) -> RpcResult<()>; } diff --git a/syscared/src/rpc/skeleton_impl/fast_reboot.rs b/syscared/src/rpc/skeleton_impl/fast_reboot.rs deleted file mode 100644 index aeab458a777df63f4b40178e1d211633e7f1724e..0000000000000000000000000000000000000000 --- a/syscared/src/rpc/skeleton_impl/fast_reboot.rs +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * syscared is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use anyhow::{Context, Result}; - -use crate::fast_reboot::{KExecManager, RebootOption}; -use log::info; - -use super::{ - function::{RpcFunction, RpcResult}, - skeleton::FastRebootSkeleton, -}; - -pub struct FastRebootSkeletonImpl; - -impl FastRebootSkeleton for FastRebootSkeletonImpl { - fn fast_reboot(&self, kernel_version: Option, force: bool) -> RpcResult<()> { - RpcFunction::call(move || -> Result<()> { - info!("Rebooting system..."); - - KExecManager::load_kernel(kernel_version) - .and_then(|_| { - KExecManager::execute(match force { - true => RebootOption::Forced, - false => RebootOption::Normal, - }) - }) - .context("Failed to reboot system") - }) - } -} diff --git a/syscared/src/rpc/skeleton_impl/mod.rs b/syscared/src/rpc/skeleton_impl/mod.rs index a238df4be11f5fa1455630fd8ccdff05fd70635e..70379469a5720404ada482461e6f6cdd4ac24fce 100644 --- a/syscared/src/rpc/skeleton_impl/mod.rs +++ b/syscared/src/rpc/skeleton_impl/mod.rs @@ -15,8 +15,6 @@ use super::function; use super::skeleton; -mod fast_reboot; mod patch; -pub use fast_reboot::*; pub use patch::*; diff --git a/syscared/src/rpc/skeleton_impl/patch.rs b/syscared/src/rpc/skeleton_impl/patch.rs index b009d46b0547e497dfca4b57e3658b0ee81a31ac..38c9f3706a33cd1d0bfd4c8114b46272778a9fd9 100644 --- a/syscared/src/rpc/skeleton_impl/patch.rs +++ b/syscared/src/rpc/skeleton_impl/patch.rs @@ -96,9 +96,10 @@ impl PatchSkeleton for PatchSkeletonImpl { format!("Apply patch '{}'", identifier), self.patch_manager.clone(), PatchManager::apply_patch, - match force { - false => PatchOpFlag::Normal, - true => PatchOpFlag::Force, + if force { + PatchOpFlag::Force + } else { + PatchOpFlag::Normal }, identifier, ) @@ -131,9 +132,10 @@ impl PatchSkeleton for PatchSkeletonImpl { format!("Active patch '{}'", identifier), self.patch_manager.clone(), PatchManager::active_patch, - match force { - false => PatchOpFlag::Normal, - true => PatchOpFlag::Force, + if force { + PatchOpFlag::Force + } else { + PatchOpFlag::Normal }, identifier, ) @@ -232,4 +234,13 @@ impl PatchSkeleton for PatchSkeletonImpl { .context("Failed to restore patch status") }) } + + fn rescan_patches(&self) -> RpcResult<()> { + RpcFunction::call(move || -> Result<()> { + self.patch_manager + .write() + .rescan_patches() + .context("Failed to rescan patches") + }) + } } diff --git a/upatch-build/Cargo.toml b/upatch-build/Cargo.toml index 6e85b8c50feddc2c105e46ebc5504022b7164fe6..5834d28f8afb03e87c49531e6a42a16b3510538f 100644 --- a/upatch-build/Cargo.toml +++ b/upatch-build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "upatch-build" -version = "1.2.1" +version = "1.2.2" authors = ["lizongwu@huawei.com"] description = "Upatch build utility" license = "MulanPSL-2.0" @@ -12,17 +12,17 @@ build = "build.rs" syscare-common = { package = "syscare-common", path = "../syscare-common" } anyhow = { version = "1.0" } clap = { version = "3.2", features = ["cargo", "derive"] } -ctrlc = { version = "3.3" } flexi_logger = { version = "0.24" } function_name = { version = "0.3" } gimli = { version = "0.26" } indexmap = { version = "1.9" } -jsonrpc = { version = "0.16", features = ["simple_uds"] } lazy_static = { version = "1.0" } log = { version = "0.4" } memmap2 = { version = "0.9" } memoffset = { version = "0.7" } object = { version = "0.29", features = ["write"] } +once_cell = { version = "1.18" } +regex = { version = "1.7" } serde = { version = "1.0", features = ["derive"] } typed-arena = { version = "2.0" } which = { version = "4.4" } diff --git a/upatch-build/build.rs b/upatch-build/build.rs index 8cbb156ed72c497a56f42e9aa0166fa50cf07042..9ef3a360a0154338180a68bb73a60849ba9b33a6 100644 --- a/upatch-build/build.rs +++ b/upatch-build/build.rs @@ -12,26 +12,25 @@ * See the Mulan PSL v2 for more details. */ -use std::{env, process::Command}; +use std::{env, ffi::OsStr, os::unix::ffi::OsStrExt, process::Command}; fn rewrite_version() { - const ENV_VERSION_NAME: &str = "BUILD_VERSION"; - const PKG_VERSION_NAME: &str = "CARGO_PKG_VERSION"; + const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + const ENV_VERSION: Option<&str> = option_env!("BUILD_VERSION"); - let version = env::var(ENV_VERSION_NAME).unwrap_or_else(|_| { - let pkg_version = env::var(PKG_VERSION_NAME).expect("Failed to fetch package version"); - let git_output = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|output| String::from_utf8(output.stdout).expect("Failed to fetch git version")); - - match git_output { - Ok(git_version) => format!("{}-g{}", pkg_version, git_version), - Err(_) => pkg_version, - } - }); - - println!("cargo:rustc-env={}={}", PKG_VERSION_NAME, version); + println!( + "cargo:rustc-env=CARGO_PKG_VERSION={}", + ENV_VERSION.map(String::from).unwrap_or_else(|| { + Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|output| { + let git_version = OsStr::from_bytes(&output.stdout).to_string_lossy(); + format!("{}-g{}", PKG_VERSION, git_version) + }) + .unwrap_or_else(|_| PKG_VERSION.to_string()) + }) + ); } fn main() { diff --git a/upatch-build/src/args.rs b/upatch-build/src/args.rs index c3b6f483654872a284b30a8b470c9eaa2b1f6561..e3fc45b3d5b0b5b54061413e5abee433da471866 100644 --- a/upatch-build/src/args.rs +++ b/upatch-build/src/args.rs @@ -17,16 +17,15 @@ use std::path::PathBuf; use anyhow::{ensure, Result}; use clap::{AppSettings, ColorChoice, Parser}; -use syscare_common::fs; +use syscare_common::{fs, os}; +use which::which; use super::{CLI_ABOUT, CLI_NAME, CLI_VERSION}; -const DEFAULT_WORK_DIR: &str = "/var/run/syscare"; -const DEFAULT_BUILD_ROOT: &str = "./upatch"; -const DEFAULT_ELF_DIR: &str = ""; -const DEFAULT_OBJECT_DIR: &str = ""; -const DEFAULT_CMD: &str = ""; -const DEFAULT_COMPILERS: &str = "cc"; +const DEFAULT_EMPTY_VALUE: &str = ""; +const DEFAULT_SOURCE_EXTS: &[&str] = &["h", "hpp", "hxx", "c", "cpp", "cxx", "in", "inc"]; +const DEFAULT_COMPILERS: &[&str] = &["gcc", "g++"]; +const DEFAULT_BUILD_ROOT: &str = "."; const DEFAULT_OUTPUT_DIR: &str = "."; #[derive(Parser, Debug)] @@ -40,58 +39,66 @@ const DEFAULT_OUTPUT_DIR: &str = "."; term_width(120), )] pub struct Arguments { - /// Specify output name - #[clap(short, long, default_value = "", hide_default_value = true)] - pub name: OsString, + /// Patch name prefix + #[clap(long, default_value = DEFAULT_EMPTY_VALUE)] + pub prefix: OsString, - /// Specify working directory - #[clap(long, default_value = DEFAULT_WORK_DIR)] - pub work_dir: PathBuf, - - /// Specify build temporary directory + /// Build temporary directory #[clap(long, default_value = DEFAULT_BUILD_ROOT)] pub build_root: PathBuf, - /// Specify source directory + /// Source directory #[clap(short, long)] pub source_dir: PathBuf, - /// Specify build prepare command - #[clap(long, default_value = DEFAULT_CMD, hide_default_value = true)] - pub prepare_cmd: String, + /// Source file extension(s) + #[clap(long, multiple = true, default_values = &DEFAULT_SOURCE_EXTS)] + pub source_ext: Vec, - /// Specify build source command - #[clap(long)] - pub build_cmd: String, + /// Build compiler(s) + #[clap(long, multiple = true, default_values = &DEFAULT_COMPILERS)] + pub compiler: Vec, - /// Specify debuginfo files - #[clap(short, long, multiple = true, required = true)] - pub debuginfo: Vec, + /// Build prepare command + #[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)] + pub prepare_cmd: OsString, - /// Specify the directory for searching elf [default: ] - #[clap(long, default_value = DEFAULT_ELF_DIR, hide_default_value = true)] - pub elf_dir: PathBuf, + /// Build command + #[clap(short('c'), long)] + pub build_cmd: OsString, - /// Specify the directory for searching object [default: ] - #[clap(long, default_value = DEFAULT_OBJECT_DIR, hide_default_value = true)] + /// Build clean command + #[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)] + pub clean_cmd: OsString, + + /// Object searching directoy [default: ] + #[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)] pub object_dir: PathBuf, - /// Specify elf's relative path relate to 'elf' or absolute patch list - #[clap(long, multiple = true, required = true)] - pub elf: Vec, + /// Binary searching directoy [default: ] + #[clap(long, default_value = DEFAULT_EMPTY_VALUE, hide_default_value = true)] + pub binary_dir: PathBuf, - /// Specify compiler(s) - #[clap(short, long, multiple = true, default_value = DEFAULT_COMPILERS)] - pub compiler: Vec, + /// Binary file(s) + #[clap(short, long, multiple = true, required = true)] + pub binary: Vec, + + /// Debuginfo file(s) + #[clap(short, long, multiple = true, required = true)] + pub debuginfo: Vec, /// Patch file(s) #[clap(short, long, multiple = true, required = true)] pub patch: Vec, - /// Specify output directory [default: ] - #[clap(short, long, default_value = DEFAULT_OUTPUT_DIR, hide_default_value = true)] + /// Output directory + #[clap(short, long, default_value = DEFAULT_OUTPUT_DIR)] pub output_dir: PathBuf, + /// Override line macros to a fixed value + #[clap(long)] + pub override_line_macros: bool, + /// Skip compiler version check (not recommended) #[clap(long)] pub skip_compiler_check: bool, @@ -107,36 +114,45 @@ pub struct Arguments { impl Arguments { pub fn new() -> Result { - let mut args = Self::parse().normalize_path()?.check()?; + let mut args = Self::parse(); - if !args.name.is_empty() { - args.name.push("-"); - } + args.apply_defaults() + .setup_build_root() + .normalize()? + .check()?; - args.elf_dir = match args.elf_dir.as_os_str().is_empty() { - false => fs::normalize(&args.elf_dir)?, - true => args.source_dir.clone(), - }; - args.object_dir = match args.object_dir.as_os_str().is_empty() { - false => fs::normalize(&args.object_dir)?, - true => args.source_dir.clone(), - }; - - for elf_path in &mut args.elf { - *elf_path = args.elf_dir.join(&elf_path); + Ok(args) + } + + fn setup_build_root(&mut self) -> &mut Self { + self.build_root = self + .build_root + .join(format!("upatch-build.{}", os::process::id())); + + self + } + + fn apply_defaults(&mut self) -> &mut Self { + if self.object_dir.as_os_str().is_empty() { + self.object_dir = self.source_dir.clone(); + } + if self.binary_dir.as_os_str().is_empty() { + self.binary_dir = self.source_dir.clone(); } - Ok(args) + self } - fn normalize_path(mut self) -> Result { - self.work_dir = fs::normalize(&self.work_dir)?; - self.build_root = fs::normalize(self.build_root)?; + fn normalize(&mut self) -> Result<&mut Self> { + self.build_root = fs::normalize(&self.build_root)?; self.source_dir = fs::normalize(&self.source_dir)?; - self.elf_dir = fs::normalize(&self.elf_dir)?; self.object_dir = fs::normalize(&self.object_dir)?; + self.binary_dir = fs::normalize(&self.binary_dir)?; self.output_dir = fs::normalize(&self.output_dir)?; + for compiler in &mut self.compiler { + *compiler = which(&compiler)?; + } for debuginfo in &mut self.debuginfo { *debuginfo = fs::normalize(&debuginfo)?; } @@ -147,41 +163,35 @@ impl Arguments { Ok(self) } - fn check(self) -> Result { - ensure!( - self.work_dir.is_dir(), - format!("Cannot find working directory {}", self.work_dir.display()) - ); + fn check(&self) -> Result<()> { ensure!( self.source_dir.is_dir(), format!("Cannot find source directory {}", self.source_dir.display()) ); - ensure!( - self.elf_dir.is_dir(), - format!("Cannot find elf directory {}", self.elf_dir.display()) - ); - ensure!( - self.object_dir.is_dir(), - format!("Cannot find object directory {}", self.object_dir.display()) - ); + for compiler in &self.compiler { + ensure!( + compiler.is_file(), + format!("Cannot find compiler {}", compiler.display()) + ); + } for debuginfo in &self.debuginfo { ensure!( debuginfo.is_file(), format!("Cannot find debuginfo {}", debuginfo.display()) ); } + ensure!( + self.binary.len() == self.debuginfo.len(), + "Cannot match the debuginfo corresponds to binary files" + ); for patch in &self.patch { ensure!( patch.is_file(), format!("Cannot find patch {}", patch.display()) ); } - ensure!( - self.elf.len() == self.debuginfo.len(), - "Cannot match the debuginfo corresponds to elf files" - ); - Ok(self) + Ok(()) } } diff --git a/upatch-build/src/build_root.rs b/upatch-build/src/build_root.rs index 556231df93102670236a3a4bfe9afe3614c58036..dfed7f64197d3a05f988abce6ebd4fffec8f7806 100644 --- a/upatch-build/src/build_root.rs +++ b/upatch-build/src/build_root.rs @@ -19,30 +19,38 @@ use syscare_common::fs; pub struct BuildRoot { pub path: PathBuf, + pub bin_dir: PathBuf, + pub script_dir: PathBuf, + pub build_dir: PathBuf, pub original_dir: PathBuf, pub patched_dir: PathBuf, - pub temp_dir: PathBuf, pub log_file: PathBuf, } impl BuildRoot { - pub fn new>(path: P) -> Result { - let path = path.as_ref().to_path_buf(); + pub fn new>(directory: P) -> Result { + let path = directory.as_ref().to_path_buf(); + let bin_dir = path.join("bin"); + let script_dir = path.join("script"); + let build_dir = path.join("build"); let original_dir = path.join("original"); let patched_dir = path.join("patched"); - let temp_dir = path.join("temp"); let log_file = path.join("build.log"); fs::create_dir_all(&path)?; + fs::create_dir_all(&bin_dir)?; + fs::create_dir_all(&script_dir)?; + fs::create_dir_all(&build_dir)?; fs::create_dir_all(&original_dir)?; fs::create_dir_all(&patched_dir)?; - fs::create_dir_all(&temp_dir)?; Ok(Self { path, + bin_dir, + script_dir, + build_dir, original_dir, patched_dir, - temp_dir, log_file, }) } diff --git a/upatch-build/src/compiler.rs b/upatch-build/src/compiler.rs index 7778f0b53d157a8a78e62010879c6eeab0df79ba..071103337f5b29968579831c55c50a70a73123ba 100644 --- a/upatch-build/src/compiler.rs +++ b/upatch-build/src/compiler.rs @@ -13,220 +13,171 @@ */ use std::{ - ffi::OsString, + ffi::{OsStr, OsString}, + os::unix::ffi::OsStrExt, path::{Path, PathBuf}, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use indexmap::IndexSet; -use log::debug; use which::which; -use syscare_common::{ffi::OsStrExt, fs, process::Command}; - -use super::dwarf::Dwarf; - -const COMPILER_STANDARDS: [&str; 42] = [ - "c89", - "c90", - "iso9899:1990", - "iso9899:199409", - "c99", - "c9x", - "iso9899:1999", - "iso9899:199x", - "c11", - "c1x", - "iso9899:2011", - "c17", - "c18", - "iso9899:2017", - "iso9899:2018", - "gnu89", - "gnu90", - "gnu99", - "gnu9x", - "gnu11", - "gnu1x", - "gnu17", - "gnu18", - "gnu2x", - "c++98", - "c++03", - "c++11", - "c++0x", - "gnu++11", - "gnu++0x", - "c++14", - "c++1y", - "gnu++14", - "gnu++1y", - "c++17", - "c++1z", - "gnu++17", - "gnu++1z", - "c++20", - "c++2a", - "gnu++20", - "gnu++2a", -]; -const ASSEMBLER_NAME: &str = "as"; -const LINKER_NAME: &str = "ld"; +use syscare_common::{concat_os, ffi::OsStrExt as _, fs, process::Command}; + +use crate::dwarf::{Producer, ProducerParser, ProducerType}; #[derive(Debug, Clone)] pub struct Compiler { + pub prefix: Option, + pub name: OsString, + pub kind: ProducerType, + pub version: OsString, pub path: PathBuf, - pub assembler: PathBuf, pub linker: PathBuf, - pub versions: IndexSet, } impl Compiler { - fn get_component_name>(compiler: P, name: &str) -> Result { - let output = Command::new(compiler.as_ref()) - .arg(format!("-print-prog-name={}", name)) - .run_with_output()?; - - output.exit_ok()?; - Ok(output.stdout) - } - - fn run_assembler_test(&self, source_file: P, output_dir: Q) -> Result + pub fn parse(path: P, output_dir: Q) -> Result where P: AsRef, Q: AsRef, { - let assembler_file = output_dir.as_ref().join("test.s"); - let object_file = output_dir.as_ref().join("test.o"); + let path = path.as_ref().to_path_buf(); + let name = path + .file_name() + .context("Failed to parse compiler name")? + .to_os_string(); + + let output_path = Self::build_test_object(&path, &name, output_dir.as_ref()) + .context("Failed to build test object")?; + let prefix = Self::parse_compiler_prefix(&name).map(OsStr::to_os_string); + let producer = Self::parse_compiler_producer(&output_path) + .context("Failed to parse compiler producer")?; + let linker = Self::get_compiler_linker(&path, &prefix, &producer) + .context("Failed to get compiler linker")?; + + Ok(Self { + prefix, + name, + kind: producer.kind, + version: producer.version, + path, + linker, + }) + } - Command::new(&self.path) - .arg("-S") - .arg(source_file.as_ref()) - .arg("-o") - .arg(assembler_file.as_path()) - .run()? - .exit_ok()?; + fn build_test_object(path: &Path, name: &OsStr, output_dir: &Path) -> Result { + let source_file = output_dir.join("test.c"); + let output_file = output_dir.join(concat_os!(name, "-test")); - Command::new(&self.assembler) + if !source_file.exists() { + fs::write(&source_file, "int main() { return 0; }")?; + } + Command::new(path) .arg("-g") - .arg(assembler_file) + .arg(&source_file) .arg("-o") - .arg(object_file.as_path()) + .arg(&output_file) .run()? .exit_ok()?; - Ok(object_file) + Ok(output_file) } - fn run_compiler_test(&self, source_file: P, output_dir: Q) -> Result> - where - P: AsRef, - Q: AsRef, - { - let mut object_files = Vec::new(); - - for std_name in COMPILER_STANDARDS { - let object_file = output_dir.as_ref().join(format!("test_{}.o", std_name)); - let build_success = Command::new(&self.path) - .arg(format!("-std={}", std_name)) - .args(["-g", "-c"]) - .arg(source_file.as_ref()) - .arg("-o") - .arg(object_file.as_path()) - .run()? - .success(); - - if build_success { - object_files.push(object_file); + fn parse_compiler_prefix(compiler: &OsStr) -> Option<&OsStr> { + /* + * Matches compiler prefix of compiler name + * eg. x86_64-linux-gnu-gcc -> x86_64-linux-gnu- + * eg. aarch64-target-linux-clang -> aarch64-target-linux- + */ + let slice = compiler.as_bytes(); + + let spliter_indices = slice.iter().enumerate().rev().filter_map(|(index, &b)| { + if b == b'-' { + return Some(index); + } + None + }); + + for pos in spliter_indices { + let (prefix, name) = slice.split_at(pos + 1); + if name.iter().any(|&b| !b.is_ascii_digit()) { + return Some(OsStr::from_bytes(prefix)); } } - Ok(object_files) + None } - fn fetch_versions>(&mut self, output_dir: P) -> Result<()> { - let source_file = output_dir.as_ref().join("test.c"); - fs::write(&source_file, "int main() { return 0; }") - .context("Failed to write source file")?; - - let mut objects = self - .run_compiler_test(&source_file, &output_dir) - .context("Compiler test failed")?; - let asm_object = self - .run_assembler_test(&source_file, &output_dir) - .context("Assembler test failed")?; - objects.push(asm_object); - - for object in objects { - let versions = Dwarf::parse_compiler_versions(&object).with_context(|| { - format!("Failed to parse compiler name of {}", object.display()) - })?; - self.versions.extend(versions); + fn parse_compiler_producer(path: &Path) -> Result { + let producer_parser = ProducerParser::open(path) + .with_context(|| format!("Failed to open {}", path.display()))?; + let producer_iter = producer_parser + .parse() + .with_context(|| format!("Failed to parse {}", path.display()))?; + + let mut producer_map = IndexSet::new(); + for parse_result in producer_iter { + let producer = parse_result.context("Failed to parse object producer")?; + producer_map.insert(producer); } + producer_map.sort(); - Ok(()) + // Compiler producer would be highest supported. + producer_map.pop().context("No object producer") } -} -impl Compiler { - pub fn parse(compilers: I, temp_dir: Q) -> Result> - where - I: IntoIterator, - P: AsRef, - Q: AsRef, - { - let mut result = Vec::new(); - - for compiler in compilers { - let compiler = compiler.as_ref(); - let compiler_name = compiler - .file_name() - .context("Failed to parse compiler name")?; - - let output_dir = temp_dir.as_ref().join(compiler_name); - fs::create_dir_all(&output_dir)?; - - debug!("- Checking {}", compiler.display()); - let assembler_name = - Self::get_component_name(compiler, ASSEMBLER_NAME).with_context(|| { - format!("Failed to get assembler name of {}", compiler.display()) - })?; - let linker_name = Self::get_component_name(compiler, LINKER_NAME) - .with_context(|| format!("Failed to get linker name of {}", compiler.display()))?; - - let path = compiler.to_path_buf(); - let assembler = which(assembler_name.trim()).with_context(|| { - format!("Cannot find assembler {}", assembler_name.to_string_lossy()) - })?; - let linker = which(linker_name.trim()) - .with_context(|| format!("Cannot find linker {}", linker_name.to_string_lossy()))?; - let versions = IndexSet::new(); - - let mut compiler = Self { - path, - assembler, - linker, - versions, - }; - compiler - .fetch_versions(output_dir) - .context("Failed to fetch supported versions")?; - - result.push(compiler); + fn get_component( + path: &Path, + prefix: &Option, + name: &str, + ) -> Result> { + let get_component_path = |name: &OsStr| -> Result> { + let output = Command::new(path) + .arg(concat_os!("-print-prog-name=", name)) + .run_with_output()?; + output.exit_ok()?; + Ok(which(output.stdout.trim()).ok()) + }; + + if let Some(prefixed_name) = prefix.as_ref().map(|pfx| concat_os!(pfx, name)) { + let component = get_component_path(&prefixed_name)?; + if component.is_some() { + return Ok(component); + } } + get_component_path(OsStr::new(name)) + } - Ok(result) + fn get_compiler_linker( + path: &Path, + prefix: &Option, + producer: &Producer, + ) -> Result { + const CLANG_LINKER_NAMES: &[&str] = &["ld.lld", "ld"]; + const GNU_LINKER_NAMES: &[&str] = &["ld"]; + + let linkers = if matches!(producer.kind, ProducerType::ClangC | ProducerType::ClangCxx) { + // Clang may use llvm linker, we will try it firstly. + CLANG_LINKER_NAMES + } else { + GNU_LINKER_NAMES + }; + for name in linkers { + if let Some(path) = Self::get_component(path, prefix, name)? { + return Ok(path); + } + } + bail!("No suitable linker") } } impl std::fmt::Display for Compiler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Path: {}, assembler: {}, linker: {}", - self.path.display(), - self.assembler.display(), - self.linker.display() - ) + f.write_fmt(format_args!( + "{}{}", + self.prefix.as_deref().unwrap_or_default().to_string_lossy(), + self.name.to_string_lossy() + )) } } diff --git a/upatch-build/src/dwarf.rs b/upatch-build/src/dwarf.rs new file mode 100644 index 0000000000000000000000000000000000000000..fe0821c6b38b0c40ccc14771563279ee35e84481 --- /dev/null +++ b/upatch-build/src/dwarf.rs @@ -0,0 +1,376 @@ +use std::{ + cell::RefCell, + collections::HashMap, + ffi::{OsStr, OsString}, + os::unix::ffi::OsStrExt, + path::Path, + rc::Rc, +}; + +use anyhow::{bail, Context, Result}; +use gimli::{ + constants::*, AttributeValue, DebugInfoUnitHeadersIter, DebuggingInformationEntry, DwLang, + Dwarf, EndianRcSlice, Endianity, Reader, RunTimeEndian, SectionId, Unit, UnitOffset, +}; +use object::{Endianness, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget}; +use once_cell::sync::Lazy; +use regex::bytes::Regex; + +use syscare_common::fs::{self, FileMmap}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ProducerType { + GnuAs, + LlvmAs, + GnuC, + ClangC, + GnuCxx, + ClangCxx, + Unknown, +} + +impl std::fmt::Display for ProducerType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self { + ProducerType::GnuAs => "GNU AS", + ProducerType::LlvmAs => "LLVM AS", + ProducerType::GnuC => "GNU C", + ProducerType::ClangC => "Clang C", + ProducerType::GnuCxx => "GNU C++", + ProducerType::ClangCxx => "Clang C++", + ProducerType::Unknown => "Unknown", + }; + f.write_str(name) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Producer { + pub kind: ProducerType, + pub name: OsString, + pub version: OsString, +} + +impl Producer { + pub fn is_assembler(&self) -> bool { + matches!(self.kind, ProducerType::GnuAs | ProducerType::LlvmAs) + } +} + +impl std::fmt::Display for Producer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "{} {}", + self.name.to_string_lossy(), + self.version.to_string_lossy() + )) + } +} + +pub struct ProducerParser { + mmap: FileMmap, + data_map: RefCell>>, +} + +impl ProducerParser { + pub fn open>(path: P) -> Result { + let parser = Self { + mmap: fs::mmap(&path) + .with_context(|| format!("Failed to mmap file {}", path.as_ref().display()))?, + data_map: RefCell::new(HashMap::new()), + }; + Ok(parser) + } + + pub fn parse(&self) -> Result + '_>> { + let dwarf = Dwarf::load(|section_id| -> Result<_> { self.load_section(section_id) }) + .context("Failed to load DWARF information")?; + let headers = dwarf.units(); + + Ok(ProducerIterator { + dwarf, + headers, + state: None, + }) + } +} + +impl ProducerParser { + fn load_section(&self, section_id: SectionId) -> Result + '_> { + const U8_TYPE_SIZE: usize = 1; + const U16_TYPE_SIZE: usize = 2; + const U32_TYPE_SIZE: usize = 4; + const U64_TYPE_SIZE: usize = 8; + const BYTE_BIT_NUM: u8 = 8; + + let file = object::File::parse(self.mmap.as_ref())?; + let endian = match file.endianness() { + Endianness::Little => RunTimeEndian::Little, + Endianness::Big => RunTimeEndian::Big, + }; + + let section_name = section_id.name(); + let section = match file.section_by_name(section_name) { + Some(section) => section, + None => return Ok(EndianRcSlice::new(Rc::new([]), endian)), + }; + + let mut section_data = section + .uncompressed_data() + .map(|slice| slice.into_owned()) + .with_context(|| format!("Failed to read section {}", section_name))?; + for (offset, reloc) in section.relocations() { + if let RelocationTarget::Symbol(index) = reloc.target() { + if !matches!(reloc.kind(), RelocationKind::Absolute) { + continue; + } + + let symbol = file.symbol_by_index(index)?; + let addend = reloc.addend(); + let value = if addend >= 0 { + symbol.address().checked_add(addend.unsigned_abs()) + } else { + symbol.address().checked_sub(addend.unsigned_abs()) + } + .context("Relocation overflow")?; + + let len = (reloc.size() / BYTE_BIT_NUM) as usize; + let buf = &mut section_data[offset as usize..offset as usize + len]; + + match len { + U8_TYPE_SIZE => buf[0] = value as u8, + U16_TYPE_SIZE => endian.write_u16(buf, value as u16), + U32_TYPE_SIZE => endian.write_u32(buf, value as u32), + U64_TYPE_SIZE => endian.write_u64(buf, value), + _ => bail!("Invalid relocation length"), + } + } else { + bail!("Unsupported relocation type"); + } + } + + let bytes: Rc<[u8]> = Rc::from(section_data.into_boxed_slice()); + self.data_map.borrow_mut().insert(section_id, bytes.clone()); + + Ok(EndianRcSlice::new(bytes, endian)) + } + + fn parse_producer_attr( + dwarf: &Dwarf, + unit: &Unit, + attr: AttributeValue, + ) -> Result + where + R: Reader, + { + let attr = dwarf + .attr_string(unit, attr) + .context("Cannot find attribute string")?; + let slice = attr + .to_slice() + .context("Failed to read attribute string data")?; + + Ok(OsStr::from_bytes(&slice).to_os_string()) + } + + fn parse_producer_name(str: &OsStr) -> Option<&OsStr> { + /* + * Matches name in producer string + * eg. GNU C17 12.3.1 (openEuler 12.3.1-62.oe2403sp1) -> GNU C17 + * eg. clang version 17.0.6 (17.0.6-30-oe2043sp1) -> clang + */ + static PRODUCER_NAME_REGEX: Lazy = Lazy::new(|| { + Regex::new(r"^((?:\s?[A-Za-z]+\d*)+)").expect("Invalid producer name regex") + }); + + PRODUCER_NAME_REGEX + .captures(str.as_bytes()) + .and_then(|captures| captures.get(1)) + .map(|matched| matched.as_bytes()) + .map(|bytes| bytes.strip_suffix(b" version").unwrap_or(bytes)) + .map(OsStr::from_bytes) + } + + fn parse_producer_version(str: &OsStr) -> Option<&OsStr> { + /* + * Matches version in producer string + * eg. GNU C17 12.3.1 (openEuler 12.3.1-62.oe2403sp1) -> 12.3.1 + * eg. clang version 17.0.6 (17.0.6-30-oe2043sp1) -> 17.0.6 + */ + static PRODUCER_VERSION_REGEX: Lazy = + Lazy::new(|| Regex::new(r"(\d+(?:\.\d+)+)").expect("Invalid producer version regex")); + + PRODUCER_VERSION_REGEX + .captures(str.as_bytes()) + .and_then(|captures| captures.get(1)) + .map(|matched| matched.as_bytes()) + .map(OsStr::from_bytes) + } + + fn parse_producer_type(str: &OsStr, attr: AttributeValue) -> Result + where + R: Reader, + { + const DW_LANGS_AS: &[DwLang] = &[ + DW_LANG_Mips_Assembler, + DW_LANG_SUN_Assembler, + DW_LANG_ALTIUM_Assembler, + ]; + const DW_LANGS_C: &[DwLang] = &[ + DW_LANG_C, + DW_LANG_C89, + DW_LANG_C99, + DW_LANG_C11, + DW_LANG_C17, + ]; + const DW_LANGS_CXX: &[DwLang] = &[ + DW_LANG_C_plus_plus, + DW_LANG_C_plus_plus_03, + DW_LANG_C_plus_plus_11, + DW_LANG_C_plus_plus_14, + DW_LANG_C_plus_plus_17, + DW_LANG_C_plus_plus_20, + ]; + const GNU_PRODUCER_PREFIX: &str = "GNU"; + + let lang = match attr { + AttributeValue::Language(lang) => lang, + _ => bail!("Unexpected attribute type"), + }; + let is_gnu = str.to_string_lossy().starts_with(GNU_PRODUCER_PREFIX); + let kind = match lang { + lang if DW_LANGS_AS.contains(&lang) => { + if is_gnu { + ProducerType::GnuAs + } else { + ProducerType::LlvmAs + } + } + lang if DW_LANGS_C.contains(&lang) => { + if is_gnu { + ProducerType::GnuC + } else { + ProducerType::ClangC + } + } + lang if DW_LANGS_CXX.contains(&lang) => { + if is_gnu { + ProducerType::GnuCxx + } else { + ProducerType::ClangCxx + } + } + _ => ProducerType::Unknown, + }; + + Ok(kind) + } + + fn parse_producer( + dwarf: &Dwarf, + unit: &Unit, + die: DebuggingInformationEntry, + ) -> Result> + where + R: Reader, + { + if die.tag() != DW_TAG_compile_unit { + return Ok(None); + } + + let str = match die.attr_value(DW_AT_producer)? { + Some(attr) => Self::parse_producer_attr(dwarf, unit, attr)?, + None => bail!("Invalid DW_AT_producer attribute"), + }; + let kind = match die.attr_value(DW_AT_language)? { + Some(attr) => Self::parse_producer_type(&str, attr)?, + _ => bail!("Invalid DW_AT_language attribute"), + }; + let name = Self::parse_producer_name(&str).context("Invalid producer name")?; + let version = Self::parse_producer_version(&str).context("Invalid producer version")?; + + Ok(Some(Producer { + kind, + name: name.to_os_string(), + version: version.to_os_string(), + })) + } +} + +pub struct ProducerIterator { + dwarf: Dwarf, + headers: DebugInfoUnitHeadersIter, + state: Option<(Unit, Vec)>, +} + +impl> ProducerIterator { + fn current(&self) -> Result, DebuggingInformationEntry)>> { + if let Some((unit, offsets)) = &self.state { + if let Some(offset) = offsets.last() { + return Ok(Some((unit, unit.entry(*offset)?))); + } + } + Ok(None) + } + + fn has_die(&self) -> bool { + self.state + .as_ref() + .map(|(_, offsets)| !offsets.is_empty()) + .unwrap_or(false) + } + + fn next_die(&mut self) { + if let Some((_, offsets)) = &mut self.state { + offsets.pop(); + } + } + + fn next_unit(&mut self) -> Result<()> { + if let Some(header) = self.headers.next()? { + let unit = self.dwarf.unit(header)?; + + let mut offsets = Vec::new(); + let mut cursor = unit.entries(); + while let Some((_, entry)) = cursor.next_dfs()? { + offsets.push(entry.offset()); + } + offsets.reverse(); + + self.state = Some((unit, offsets)); + } + + Ok(()) + } +} + +impl> Iterator for ProducerIterator { + type Item = Result; + + fn next(&mut self) -> Option { + loop { + let producer = match self.current() { + Ok(Some((unit, die))) => { + ProducerParser::parse_producer(&self.dwarf, unit, die).transpose() + } + Ok(None) => None, + Err(e) => Some(Err(e)), + }; + + self.next_die(); + + if producer.is_some() { + return producer; + } + + if !self.has_die() { + if let Err(e) = self.next_unit() { + return Some(Err(e)); + } + if !self.has_die() { + return None; + } + } + } + } +} diff --git a/upatch-build/src/dwarf/mod.rs b/upatch-build/src/dwarf/mod.rs deleted file mode 100644 index 35f359eee7d0f1c0ba7e12eb5bc85aec9af5c01a..0000000000000000000000000000000000000000 --- a/upatch-build/src/dwarf/mod.rs +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatch-build is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -// mod dump; -mod relocate; - -use std::{ - borrow::{Borrow, Cow}, - ffi::{OsStr, OsString}, - os::unix::ffi::OsStrExt as UnixOsStrExt, - path::{Path, PathBuf}, -}; - -use anyhow::{Context, Result}; -use gimli::{ - constants, Attribute, AttributeValue, EndianSlice, Endianity, Reader, RunTimeEndian, SectionId, -}; -use indexmap::{IndexMap, IndexSet}; -use log::trace; -use object::{ - File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationKind, RelocationTarget, - Section, -}; -use typed_arena::Arena; - -use syscare_common::ffi::OsStrExt; - -use relocate::Relocate; - -#[allow(non_snake_case)] -#[derive(Debug, Clone)] -pub struct CompileUnit { - pub producer: OsString, // DW_AT_producer - pub compile_dir: PathBuf, // DW_AT_comp_dir - pub file_name: PathBuf, // DW_AT_name -} - -pub struct Dwarf; - -impl Dwarf { - pub fn parse>(elf: P) -> Result> { - // use mmap here, but depend on some devices - let elf = elf.as_ref(); - let file = std::fs::File::open(elf)?; - let mmap = unsafe { memmap2::Mmap::map(&file)? }; - - let object = File::parse(&*mmap)?; - let endian = if object.is_little_endian() { - gimli::RunTimeEndian::Little - } else { - gimli::RunTimeEndian::Big - }; - - Self::get_files(&object, endian) - } - - pub fn parse_compiler_versions>(object: P) -> Result> { - let compiler_versions = Dwarf::parse(&object) - .with_context(|| format!("Failed to read dwarf of {}", object.as_ref().display()))? - .into_iter() - .filter_map(|dwarf| { - dwarf - .producer - .split('-') - .next() - .map(|s| s.trim().to_os_string()) - }) - .collect::>(); - - Ok(compiler_versions) - } -} - -impl Dwarf { - fn add_relocations( - relocations: &mut IndexMap, - file: &File, - section: &Section, - ) { - for (offset64, mut relocation) in section.relocations() { - let offset = offset64 as usize; - if offset as u64 != offset64 { - continue; - } - match relocation.kind() { - RelocationKind::Absolute => { - if let RelocationTarget::Symbol(symbol_idx) = relocation.target() { - match file.symbol_by_index(symbol_idx) { - Ok(symbol) => { - let addend = - symbol.address().wrapping_add(relocation.addend() as u64); - relocation.set_addend(addend as i64); - } - Err(_) => { - trace!("Relocation with invalid symbol for section {} at offset 0x{:08x}", - section.name().unwrap(), - offset - ); - } - } - } - if relocations.insert(offset, relocation).is_some() { - trace!( - "Multiple relocations for section {} at offset 0x{:08x}", - section.name().unwrap(), - offset - ); - } - } - _ => { - trace!( - "Unsupported relocation for section {} at offset 0x{:08x}", - section.name().unwrap(), - offset - ); - } - } - } - } - - fn load_file_section<'input, 'arena, Endian: Endianity>( - id: SectionId, - file: &File<'input>, - endian: Endian, - arena_data: &'arena Arena>, - arena_relocations: &'arena Arena>, - ) -> Result>> { - let mut relocations = IndexMap::new(); - let name = Some(id.name()); - let data = match name.and_then(|name| file.section_by_name(name)) { - Some(ref section) => { - // DWO sections never have relocations, so don't bother. - Self::add_relocations(&mut relocations, file, section); - section.uncompressed_data()? - } - // Use a non-zero capacity so that `ReaderOffsetId`s are unique. - None => Cow::Owned(Vec::with_capacity(1)), - }; - let data_ref = (*arena_data.alloc(data)).borrow(); - let reader = EndianSlice::new(data_ref, endian); - let section = reader; - let relocations = (*arena_relocations.alloc(relocations)).borrow(); - Ok(Relocate { - relocations, - section, - reader, - }) - } - - fn get_files(file: &File, endian: RunTimeEndian) -> Result> { - let arena_data = Arena::new(); - let arena_relocations = Arena::new(); - - // Load a section and return as `Cow<[u8]>`. - let mut load_section = |id: SectionId| -> Result<_> { - Self::load_file_section(id, file, endian, &arena_data, &arena_relocations) - }; - - let dwarf = gimli::Dwarf::load(&mut load_section)?; - - Self::__get_files(&dwarf) - } - - fn __get_files(dwarf: &gimli::Dwarf) -> Result> { - let mut result = Vec::new(); - let mut iter = dwarf.units(); - while let Some(header) = iter.next()? { - let unit = dwarf.unit(header)?; - let mut entries = unit.entries(); - while let Some((_, entry)) = entries.next_dfs()? { - if entry.tag() != constants::DW_TAG_compile_unit { - break; - } - // Iterate over the attributes in the DIE. - let mut attrs = entry.attrs(); - let mut element = CompileUnit { - producer: OsString::new(), - compile_dir: PathBuf::new(), - file_name: PathBuf::new(), - }; - - while let Some(attr) = attrs.next()? { - match attr.name() { - constants::DW_AT_comp_dir => { - element.compile_dir.push(&Self::attr_value(&attr, dwarf)); - } - constants::DW_AT_name => { - element.file_name.push(&Self::attr_value(&attr, dwarf)); - } - constants::DW_AT_producer => { - element.producer.push(&Self::attr_value(&attr, dwarf)); - } - _ => continue, - } - } - - result.push(element); - } - } - Ok(result) - } - - fn attr_value(attr: &Attribute, dwarf: &gimli::Dwarf) -> OsString { - let value = attr.value(); - match value { - AttributeValue::DebugLineStrRef(offset) => { - if let Ok(s) = dwarf.debug_line_str.get_str(offset) { - OsStr::from_bytes(&s.to_slice().ok().unwrap_or_default()).to_os_string() - } else { - OsString::default() - } - } - AttributeValue::DebugStrRef(offset) => { - if let Ok(s) = dwarf.debug_str.get_str(offset) { - OsStr::from_bytes(&s.to_slice().ok().unwrap_or_default()).to_os_string() - } else { - OsString::default() - } - } - _ => OsString::default(), - } - } -} diff --git a/upatch-build/src/dwarf/relocate.rs b/upatch-build/src/dwarf/relocate.rs deleted file mode 100644 index 2a6e3930621734459acc42948e4e1aa13717b1f1..0000000000000000000000000000000000000000 --- a/upatch-build/src/dwarf/relocate.rs +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatch-build is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::borrow::Cow; - -use indexmap::IndexMap; -use object::Relocation; - -#[derive(Debug, Clone)] -pub struct Relocate<'a, R: gimli::Reader> { - pub relocations: &'a IndexMap, - pub section: R, - pub reader: R, -} - -impl<'a, R: gimli::Reader> Relocate<'a, R> { - pub fn relocate(&self, offset: usize, value: u64) -> u64 { - if let Some(relocation) = self.relocations.get(&offset) { - if relocation.kind() == object::RelocationKind::Absolute { - return match relocation.has_implicit_addend() { - // Use the explicit addend too, because it may have the symbol value. - true => value.wrapping_add(relocation.addend() as u64), - false => relocation.addend() as u64, - }; - } - }; - value - } -} - -impl<'a, R: gimli::Reader> gimli::Reader for Relocate<'a, R> { - type Endian = R::Endian; - type Offset = R::Offset; - - fn read_address(&mut self, address_size: u8) -> gimli::Result { - let offset = self.reader.offset_from(&self.section); - let value = self.reader.read_address(address_size)?; - Ok(self.relocate(offset, value)) - } - - fn read_length(&mut self, format: gimli::Format) -> gimli::Result { - let offset = self.reader.offset_from(&self.section); - let value = self.reader.read_length(format)?; - ::from_u64(self.relocate(offset, value as u64)) - } - - fn read_offset(&mut self, format: gimli::Format) -> gimli::Result { - let offset = self.reader.offset_from(&self.section); - let value = self.reader.read_offset(format)?; - ::from_u64(self.relocate(offset, value as u64)) - } - - fn read_sized_offset(&mut self, size: u8) -> gimli::Result { - let offset = self.reader.offset_from(&self.section); - let value = self.reader.read_sized_offset(size)?; - ::from_u64(self.relocate(offset, value as u64)) - } - - #[inline] - fn split(&mut self, len: Self::Offset) -> gimli::Result { - let mut other = self.clone(); - other.reader.truncate(len)?; - self.reader.skip(len)?; - Ok(other) - } - - // All remaining methods simply delegate to `self.reader`. - - #[inline] - fn endian(&self) -> Self::Endian { - self.reader.endian() - } - - #[inline] - fn len(&self) -> Self::Offset { - self.reader.len() - } - - #[inline] - fn empty(&mut self) { - self.reader.empty() - } - - #[inline] - fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> { - self.reader.truncate(len) - } - - #[inline] - fn offset_from(&self, base: &Self) -> Self::Offset { - self.reader.offset_from(&base.reader) - } - - #[inline] - fn offset_id(&self) -> gimli::ReaderOffsetId { - self.reader.offset_id() - } - - #[inline] - fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option { - self.reader.lookup_offset_id(id) - } - - #[inline] - fn find(&self, byte: u8) -> gimli::Result { - self.reader.find(byte) - } - - #[inline] - fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> { - self.reader.skip(len) - } - - #[inline] - fn to_slice(&self) -> gimli::Result> { - self.reader.to_slice() - } - - #[inline] - fn to_string(&self) -> gimli::Result> { - self.reader.to_string() - } - - #[inline] - fn to_string_lossy(&self) -> gimli::Result> { - self.reader.to_string_lossy() - } - - #[inline] - fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> { - self.reader.read_slice(buf) - } -} diff --git a/upatch-build/src/elf/elfs.rs b/upatch-build/src/elf/elfs.rs index c777058008800a5698be1c50e9f5fe37cb86feaa..8d9abb1517ccc5056ce5eee9ea03e89e9fb722e8 100644 --- a/upatch-build/src/elf/elfs.rs +++ b/upatch-build/src/elf/elfs.rs @@ -12,13 +12,50 @@ * See the Mulan PSL v2 for more details. */ -use std::fs::File; +use std::{ + fs::File, + path::{Path, PathBuf}, +}; use anyhow::{bail, ensure, Context, Result}; use memmap2::MmapOptions; +use object::{Object, ObjectKind}; + +use syscare_common::fs; use super::{Endian, Endianness}; +fn parse_elf_kind>(file_path: P) -> Result { + let path = file_path.as_ref(); + let mmap = fs::mmap(path).with_context(|| format!("Failed to mmap file {}", path.display()))?; + let file = object::File::parse(mmap.as_ref()) + .with_context(|| format!("Failed to parse {}", path.display()))?; + + Ok(file.kind()) +} + +pub fn elf_kind>(file_path: P) -> ObjectKind { + self::parse_elf_kind(file_path).unwrap_or(ObjectKind::Unknown) +} + +pub fn find_elf_files(directory: P, predicate: F) -> Result> +where + P: AsRef, + F: Fn(&Path, ObjectKind) -> bool, +{ + let mut elf_files = Vec::new(); + for file_path in fs::list_files(&directory, fs::TraverseOptions { recursive: true })? { + if let Ok(obj_kind) = self::parse_elf_kind(&file_path) { + if predicate(&file_path, obj_kind) { + elf_files.push(file_path); + } + } + } + elf_files.sort(); + + Ok(elf_files) +} + pub fn check_elf(file: &File) -> Result { if file.metadata()?.len() < 64 { return Ok(false); diff --git a/upatch-build/src/elf/header.rs b/upatch-build/src/elf/header.rs index ad9f8fded7fd0bcbcb6e79e516dd83edc1b4edab..a4c4ca853f421f6fa8497256fef098cd28e2b4d6 100644 --- a/upatch-build/src/elf/header.rs +++ b/upatch-build/src/elf/header.rs @@ -48,10 +48,6 @@ pub trait HeaderWrite: OperateWrite { fn set_e_ident(&mut self, e_ident: u128) { self.set(offset_of!(FileHeader64, e_ident), e_ident) } - - fn set_e_shnum(&mut self, e_shnum: u16) { - self.set(offset_of!(FileHeader64, e_shnum), e_shnum) - } } #[repr(C)] diff --git a/upatch-build/src/elf/read/elfs.rs b/upatch-build/src/elf/read/elfs.rs index 11d57e3d9f3ace569c69f07cfe90798cbf744ae8..52c5e6b6168c81b82c52141a89adc53043b5bb35 100644 --- a/upatch-build/src/elf/read/elfs.rs +++ b/upatch-build/src/elf/read/elfs.rs @@ -19,10 +19,10 @@ use anyhow::bail; use anyhow::Result; use memmap2::{Mmap, MmapOptions}; -use super::super::*; -use super::Header; -use super::SectionHeaderTable; -use super::SymbolHeaderTable; +use super::{ + super::{check_elf, check_header, Endian, HeaderRead, SectionRead, SymbolHeader64, SHT_SYMTAB}, + Header, SectionHeaderTable, SymbolHeaderTable, +}; #[derive(Debug)] pub struct Elf { @@ -61,7 +61,7 @@ impl Elf { pub fn symbols(&self) -> Result { let sections = self.sections()?; - for section in sections { + for section in sections.clone() { if section.get_sh_type().eq(&SHT_SYMTAB) { let offset = section.get_sh_offset() as usize; let size_sum = section.get_sh_size() as usize; diff --git a/upatch-build/src/elf/read/section.rs b/upatch-build/src/elf/read/section.rs index a12c4f3aff84ceb25d0a7af1174dddcdf06bebf2..8f79c8bc48ae8f28b21ce422ba8a1242a1a5d1cd 100644 --- a/upatch-build/src/elf/read/section.rs +++ b/upatch-build/src/elf/read/section.rs @@ -44,7 +44,7 @@ impl OperateRead for SectionHeader<'_> { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct SectionHeaderTable<'a> { mmap: &'a Mmap, endian: Endian, @@ -86,16 +86,13 @@ impl<'a> Iterator for SectionHeaderTable<'a> { type Item = SectionHeader<'a>; fn next(&mut self) -> Option { - match self.count < self.num { - true => { - let offset = self.count * self.size + self.offset; - self.count += 1; - Some(SectionHeader::from(self.mmap, self.endian, offset)) - } - false => { - self.count = 0; - None - } + if self.count < self.num { + let offset = self.count * self.size + self.offset; + self.count += 1; + Some(SectionHeader::from(self.mmap, self.endian, offset)) + } else { + self.count = 0; + None } } } diff --git a/upatch-build/src/elf/read/symbol.rs b/upatch-build/src/elf/read/symbol.rs index 70f03e167483ac2f601cbff5b8787946bcb41e8a..0c05cb6c3e77795a28cdfff70bee4e15c401318b 100644 --- a/upatch-build/src/elf/read/symbol.rs +++ b/upatch-build/src/elf/read/symbol.rs @@ -99,9 +99,10 @@ impl<'a> SymbolHeaderTable<'a> { } pub fn reset(&mut self, n: usize) { - match n < self.num { - true => self.count = n, - false => self.count = 0, + if n < self.num { + self.count = n; + } else { + self.count = 0; } } } @@ -110,21 +111,18 @@ impl<'a> Iterator for SymbolHeaderTable<'a> { type Item = SymbolHeader<'a>; fn next(&mut self) -> Option { - match self.count < self.num { - true => { - let offset = self.count * self.size + self.offset; - self.count += 1; - Some(SymbolHeader::from( - self.mmap, - self.endian, - self.strtab, - offset, - )) - } - false => { - self.count = 0; - None - } + if self.count < self.num { + let offset = self.count * self.size + self.offset; + self.count += 1; + Some(SymbolHeader::from( + self.mmap, + self.endian, + self.strtab, + offset, + )) + } else { + self.count = 0; + None } } } diff --git a/upatch-build/src/elf/write/elfs.rs b/upatch-build/src/elf/write/elfs.rs index e41ad0f544e644ca9b3921b3d751b3ff130b4baa..9ce2847898bf4a221632a441fb252b887723ba25 100644 --- a/upatch-build/src/elf/write/elfs.rs +++ b/upatch-build/src/elf/write/elfs.rs @@ -15,11 +15,13 @@ use std::fs::{File, OpenOptions}; use std::path::Path; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use memmap2::{Mmap, MmapOptions}; -use super::super::*; -use super::{Header, SectionHeader, SymbolHeaderTable}; +use super::{ + super::{check_elf, check_header, Endian, HeaderRead, SectionRead, SymbolHeader64, SHT_SYMTAB}, + Header, SectionHeader, SymbolHeaderTable, +}; #[derive(Debug)] pub struct Elf { @@ -88,7 +90,7 @@ impl Elf { return Ok(SymbolHeaderTable::from( &self.file, self.endian, - self.strtab.as_ref().unwrap(), + self.strtab.as_ref().context("Invalid strtab")?, offset, size, offset + size_sum, diff --git a/upatch-build/src/elf/write/symbol.rs b/upatch-build/src/elf/write/symbol.rs index 59468972d5588eb7e0a7619df6d982039cc9924e..0a2d08a068540d4bb024cc4fdee06b227c690439 100644 --- a/upatch-build/src/elf/write/symbol.rs +++ b/upatch-build/src/elf/write/symbol.rs @@ -38,13 +38,12 @@ impl<'a> SymbolHeader<'a> { } pub fn get_st_name(&mut self) -> &OsStr { - match self.name.is_empty() { - false => <&std::ffi::OsStr>::clone(&self.name), - true => { - let name_offset = self.get_st_name_offset() as usize; - self.name = self.read_to_os_string(name_offset); - self.name - } + if !self.name.is_empty() { + self.name + } else { + let name_offset = self.get_st_name_offset() as usize; + self.name = self.read_to_os_string(name_offset); + self.name } } } @@ -120,22 +119,19 @@ impl<'a> Iterator for SymbolHeaderTable<'a> { fn next(&mut self) -> Option { let offset = self.count * self.size + self.start; - match offset < self.end { - true => { - self.count += 1; - let mmap = unsafe { - MmapOptions::new() - .offset(offset as u64) - .len(self.size) - .map_mut(self.file) - .unwrap() - }; - Some(SymbolHeader::from(mmap, self.endian, self.strtab)) - } - false => { - self.count = 0; - None + if offset < self.end { + self.count += 1; + unsafe { + MmapOptions::new() + .offset(offset as u64) + .len(self.size) + .map_mut(self.file) + .ok() + .map(|mmap_mut| SymbolHeader::from(mmap_mut, self.endian, self.strtab)) } + } else { + self.count = 0; + None } } } diff --git a/upatch-build/src/file_relation.rs b/upatch-build/src/file_relation.rs index 5a58d04f901a1070b1f81292809e428c14fd77c2..677cb8bce04616b76e6dd74f30d0eb745bd7a9e6 100644 --- a/upatch-build/src/file_relation.rs +++ b/upatch-build/src/file_relation.rs @@ -12,21 +12,31 @@ * See the Mulan PSL v2 for more details. */ -use std::ffi::{OsStr, OsString}; -use std::path::{Path, PathBuf}; +use std::{ + ffi::{OsStr, OsString}, + os::unix::ffi::OsStrExt, + path::{Path, PathBuf}, +}; -use anyhow::{bail, ensure, Context, Result}; +use anyhow::{ensure, Context, Result}; use indexmap::{IndexMap, IndexSet}; -use syscare_common::{ffi::OsStrExt, fs}; +use log::warn; +use object::{Object, ObjectKind, ObjectSymbol}; -use super::{ - elf::{check_elf, read}, - pattern_path::glob, -}; +use syscare_common::{concat_os, ffi::OsStrExt as _, fs}; + +use crate::elf; + +const UPATCH_ID_PREFIX: &str = ".upatch_"; -const UPATCH_SYM_PREFIX: &str = ".upatch_"; -const OBJECT_EXTENSION: &str = "o"; +const NON_EXIST_PATH: &str = "/dev/null"; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ObjectRelation { + pub original_object: PathBuf, + pub patched_object: PathBuf, +} /* * The task of this class is to find out: @@ -34,189 +44,276 @@ const OBJECT_EXTENSION: &str = "o"; * 2. relationship between output binaries and objects * 3. relationship between original objects and patched objects */ - #[derive(Debug)] pub struct FileRelation { - debuginfo_map: IndexMap, // Binary -> Debuginfo - symlink_map: IndexMap, // Symlink object -> Orignal object - patch_objects_map: IndexMap>, // Binary -> Patched objects - original_object_map: IndexMap, // Patched object -> Original object + binary_debuginfo_map: IndexMap, // Binary -> Debuginfo + binary_relation_map: IndexMap>, // Binary -> [ObjectRelation] + original_object_map: IndexMap, // Output object -> Original object } impl FileRelation { pub fn new() -> Self { Self { - debuginfo_map: IndexMap::new(), - symlink_map: IndexMap::new(), - patch_objects_map: IndexMap::new(), + binary_debuginfo_map: IndexMap::new(), + binary_relation_map: IndexMap::new(), original_object_map: IndexMap::new(), } } - pub fn collect_debuginfo(&mut self, binaries: I, debuginfos: J) -> Result<()> + pub fn collect_debuginfo( + &mut self, + binary_dir: P, + binaries: I, + debuginfos: J, + ) -> Result<()> where - I: IntoIterator, - J: IntoIterator, P: AsRef, + I: IntoIterator, + J: IntoIterator, Q: AsRef, + R: AsRef, { - let mut binaries = binaries.into_iter(); - let mut debuginfos = debuginfos.into_iter(); + let mut binary_iter = binaries.into_iter(); + let mut debuginfo_iter = debuginfos.into_iter(); - while let (Some(binary), Some(debuginfo)) = (binaries.next(), debuginfos.next()) { - let binary = Self::find_binary_file(binary)?; - let debuginfo = debuginfo.as_ref().to_path_buf(); + while let (Some(binary), Some(debuginfo)) = (binary_iter.next(), debuginfo_iter.next()) { + let binary_dir = binary_dir.as_ref(); + let binary_path = binary.as_ref().as_os_str(); - self.debuginfo_map.insert(binary, debuginfo); + let mut binary_files = IndexSet::new(); + for match_result in fs::glob(binary_dir) { + let matched_dir = match_result.with_context(|| { + format!("Cannot match binary directory {}", binary_dir.display()) + })?; + let found_files = + fs::list_files(matched_dir, fs::TraverseOptions { recursive: true })? + .into_iter() + .filter(|file_path| file_path.ends_with(binary_path)) + .filter(|file_path| { + matches!( + elf::elf_kind(file_path), + ObjectKind::Executable | ObjectKind::Dynamic + ) + }); + binary_files.extend(found_files); + } + let binary_file = binary_files + .pop() + .with_context(|| format!("Cannot find any binary in {}", binary_dir.display()))?; + ensure!( + binary_files.is_empty(), + "Binary {} matched to too many files", + binary_path.to_string_lossy() + ); + self.binary_debuginfo_map + .insert(binary_file, debuginfo.as_ref().to_path_buf()); } Ok(()) } - pub fn collect_original_build(&mut self, object_dir: P, expected_dir: Q) -> Result<()> + pub fn collect_original_build(&mut self, object_dir: P, collect_dir: Q) -> Result<()> where P: AsRef, Q: AsRef, { - let symlinks = fs::list_symlinks(&object_dir, fs::TraverseOptions { recursive: true })?; - for symlink in symlinks { - let object = fs::read_link(&symlink)?; - if !object.starts_with(expected_dir.as_ref().as_os_str()) { - continue; - } - self.symlink_map.insert(symlink, object); + let id_object_map = Self::collect_objects(&object_dir, &collect_dir)?; + for (_, (object_file, object_archive)) in id_object_map { + self.original_object_map.insert(object_file, object_archive); } - ensure!( - !self.symlink_map.is_empty(), - "Cannot find any valid objects in {}", - object_dir.as_ref().display() - ); Ok(()) } - pub fn collect_patched_build(&mut self, object_dir: P, expected_dir: Q) -> Result<()> + pub fn collect_patched_build(&mut self, object_dir: P, collect_dir: Q) -> Result<()> where P: AsRef, Q: AsRef, { - let mut symlink_map = IndexMap::new(); - let symlinks = fs::list_symlinks(&object_dir, fs::TraverseOptions { recursive: true })?; - for symlink in symlinks { - let object = fs::read_link(&symlink)?; - if !object.starts_with(expected_dir.as_ref().as_os_str()) { - continue; - } - symlink_map.insert(object, symlink); - } - ensure!( - !self.symlink_map.is_empty(), - "Cannot find any valid objects in {}", - object_dir.as_ref().display() - ); + let id_object_map = Self::collect_objects(&object_dir, &collect_dir)?; + let binary_id_map = Self::collect_binaries(self.binary_debuginfo_map.keys())?; - for (binary, _) in &self.debuginfo_map { - let mut objects = IndexSet::new(); + for (binary_file, upatch_ids) in binary_id_map { + let mut object_relation = IndexMap::new(); - let upatch_ids = Self::parse_upatch_ids(binary) - .with_context(|| format!("Failed to parse upatch id of {}", binary.display()))?; for upatch_id in upatch_ids { - let patched_object = Self::get_object_file(&expected_dir, &upatch_id) - .with_context(|| { - format!("Failed to get object of {}", upatch_id.to_string_lossy()) - })?; - let original_object = symlink_map - .get(&patched_object) - .and_then(|path| self.symlink_map.get(path)) - .with_context(|| { - format!( - "failed to find original object of {}", - patched_object.display() - ) - }) - .cloned()?; - - // Update object relations - self.original_object_map - .insert(patched_object.to_owned(), original_object); - objects.insert(patched_object); + match id_object_map.get(&upatch_id) { + Some((object_file, patched_object)) => { + let original_object = self + .original_object_map + .get(object_file) + .map(|p| p.as_path()) + .unwrap_or_else(|| Path::new(NON_EXIST_PATH)); + object_relation + .insert(patched_object.to_path_buf(), original_object.to_path_buf()); + } + None => { + warn!( + "Cannot find patched object of {} in target {}", + upatch_id.to_string_lossy(), + binary_file.display() + ); + } + } } - self.patch_objects_map.insert(binary.to_owned(), objects); + object_relation.sort_keys(); + + self.binary_relation_map + .insert(binary_file, object_relation); } - self.symlink_map.clear(); // clear useless records Ok(()) } pub fn get_files(&self) -> impl IntoIterator { - self.debuginfo_map + self.binary_debuginfo_map .iter() .map(|(binary, debuginfo)| (binary.as_path(), debuginfo.as_path())) } - pub fn get_patched_objects>(&self, binary: P) -> Option<&IndexSet> { - self.patch_objects_map.get(binary.as_ref()) - } - - pub fn get_original_object>(&self, object: P) -> Option<&Path> { - self.original_object_map - .get(object.as_ref()) - .map(|p| p.as_path()) + pub fn binary_objects>(&self, binary: P) -> Option<&IndexMap> { + self.binary_relation_map.get(binary.as_ref()) } } impl FileRelation { - fn find_binary_file>(binary: P) -> Result { - let binary_file = binary.as_ref(); - let matched_file = glob(binary_file)? - .into_iter() - .filter(|path| { - path.is_file() - && fs::open_file(path) - .map(|file| check_elf(&file).is_ok()) - .unwrap_or(false) - }) - .collect::>(); // for rpm's "BUILDROOT/*/path" - - match matched_file.len() { - 1 => Ok(matched_file[0].clone()), - 0 => bail!("Path does not match to any binary"), - _ => bail!("Path matches to too many binaries"), + fn parse_upatch_ids>(file_path: P) -> Result> { + let file_path = file_path.as_ref(); + let mmap = fs::mmap(file_path) + .with_context(|| format!("Failed to mmap {}", file_path.display()))?; + let file = object::File::parse(mmap.as_ref()) + .with_context(|| format!("Failed to parse {}", file_path.display()))?; + + let mut upatch_ids = IndexSet::new(); + for symbol in file.symbols() { + let name_slice = symbol.name_bytes().with_context(|| { + format!("Failed to parse symbol name, index={}", symbol.index().0) + })?; + if !name_slice.starts_with(UPATCH_ID_PREFIX.as_bytes()) { + continue; + } + upatch_ids.insert(OsStr::from_bytes(name_slice).to_os_string()); + } + + Ok(upatch_ids) + } + + fn collect_binaries(binaries: I) -> Result>> + where + I: IntoIterator, + P: AsRef, + { + let mut binary_id_map = IndexMap::new(); + + for binary in binaries { + let binary_file = binary.as_ref(); + binary_id_map.insert( + binary_file.to_path_buf(), + Self::parse_upatch_ids(binary_file).with_context(|| { + format!("Failed to parse upatch id of {}", binary_file.display()) + })?, + ); } + binary_id_map.sort_keys(); + + Ok(binary_id_map) } - fn get_object_file(object_dir: P, upatch_id: S) -> Result + fn collect_objects( + object_dir: P, + target_dir: Q, + ) -> Result> where P: AsRef, - S: AsRef, + Q: AsRef, { - let mut file_path = object_dir.as_ref().join(upatch_id.as_ref()); - file_path.set_extension(OBJECT_EXTENSION); + let object_dir = object_dir.as_ref(); + let target_dir = target_dir.as_ref(); + + let mut file_id = 1usize; + let mut object_info = Vec::new(); + for match_result in fs::glob(object_dir) { + let matched_dir = match_result.with_context(|| { + format!("Cannot match object directory {}", object_dir.display()) + })?; + + let file_list = fs::list_files(&matched_dir, fs::TraverseOptions { recursive: true })?; + for object_file in file_list { + let mmap = fs::mmap(&object_file) + .with_context(|| format!("Failed to mmap {}", object_file.display()))?; + + // Try to parse file as elf + let file = match object::File::parse(mmap.as_ref()) { + Ok(f) => f, + Err(_) => continue, + }; + + // We only care about object file + if !matches!(file.kind(), ObjectKind::Relocatable) { + continue; + } + // Copy object file to target directory + let obj_name = object_file.file_name().unwrap_or_default(); + let archive_file = + target_dir.join(concat_os!(format!("{:04}-", file_id), obj_name)); + if fs::hard_link(&object_file, &archive_file).is_err() { + fs::copy(&object_file, &archive_file)?; + } + file_id += 1; + + // Parse upatch id of the object + let upatch_ids = file + .symbols() + .filter_map(|symbol| { + let sym_name = symbol.name_bytes().unwrap_or_default(); + if !sym_name.starts_with(UPATCH_ID_PREFIX.as_bytes()) { + return None; + } + Some(OsStr::from_bytes(sym_name).to_os_string()) + }) + .collect::>(); + if upatch_ids.is_empty() { + warn!( + "Object {} does not contain upatch id", + object_file.display() + ); + continue; + } + + object_info.push((object_file, archive_file, upatch_ids)); + } + } ensure!( - file_path.is_file(), - "Cannot access object {}", - file_path.display() + !object_info.is_empty(), + "Cannot find any object in {}", + object_dir.display() + ); + + // We want subsequent objects to contain more identifiers. + object_info.sort_by(|(_, _, lhs), (_, _, rhs)| rhs.len().cmp(&lhs.len())); + + let mut upatch_id_map = IndexMap::new(); + for (object, archive, ids) in object_info { + for id in ids { + let result = upatch_id_map.insert(id.clone(), (object.clone(), archive.clone())); + if let Some((old_object, _)) = result { + warn!( + "{}: Object {} is replaced by {}", + id.to_string_lossy(), + old_object.display(), + object.display() + ); + } + } + } + + ensure!( + !upatch_id_map.is_empty(), + "Cannot find any upatch id in {}", + object_dir.display() ); - Ok(file_path) - } - /* - * To find out the relationship between the object and the binary file, - * we add a marker symbol to the object that matches its file name, named ."upatch_xxx." - * Once the binary is linked, all of the object's marker symbols will be linked into the binary. - * Thus, we can find out which object is associated w/ the binary by looking up the marker symbols. - */ - fn parse_upatch_ids>(binary: P) -> Result> { - let object_path = binary.as_ref(); - let object_elf = read::Elf::parse(object_path).context("Failed to parse elf")?; - let object_ids = object_elf - .symbols() - .context("Failed to read symbols")? - .filter_map(|symbol| symbol.get_st_name().strip_prefix(UPATCH_SYM_PREFIX)) - .map(|upatch_id| upatch_id.to_os_string()) - .collect::>(); - - Ok(object_ids) + Ok(upatch_id_map) } } diff --git a/upatch-build/src/hijacker.rs b/upatch-build/src/hijacker.rs deleted file mode 100644 index 3060495238d9511af7cab5af817978486afb15f9..0000000000000000000000000000000000000000 --- a/upatch-build/src/hijacker.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::{path::Path, rc::Rc}; - -use anyhow::{Context, Result}; -use indexmap::IndexSet; -use log::{error, info}; - -use super::compiler::Compiler; -use crate::rpc::{RpcRemote, UpatchProxy}; - -const UPATCHD_SOCKET_NAME: &str = "upatchd.sock"; - -pub struct Hijacker<'a> { - proxy: UpatchProxy, - programs: IndexSet<&'a Path>, - finished: Vec<&'a Path>, -} - -impl<'a> Hijacker<'a> { - pub fn new(compilers: I, work_dir: P) -> Result - where - I: IntoIterator, - P: AsRef, - { - let socket_file = work_dir.as_ref().join(UPATCHD_SOCKET_NAME); - let remote = RpcRemote::new(socket_file); - let proxy = UpatchProxy::new(Rc::new(remote)); - - let mut programs = IndexSet::new(); - for compiler in compilers { - programs.insert(compiler.path.as_path()); - programs.insert(compiler.assembler.as_path()); - } - - let mut instance = Self { - proxy, - programs, - finished: vec![], - }; - instance.hijack()?; - - Ok(instance) - } -} - -impl Hijacker<'_> { - fn hijack(&mut self) -> Result<()> { - info!("Hijacking compiler(s)"); - for exec_path in &self.programs { - info!("- {}", exec_path.display()); - self.proxy - .enable_hijack(exec_path) - .with_context(|| format!("Failed to hijack {}", exec_path.display()))?; - - self.finished.push(exec_path); - } - - Ok(()) - } - - fn unhack(&mut self) { - info!("Releasing compiler(s)"); - while let Some(exec_path) = self.finished.pop() { - info!("- {}", exec_path.display()); - let result = self - .proxy - .disable_hijack(exec_path) - .with_context(|| format!("Failed to release {}", exec_path.display())); - - if let Err(e) = result { - error!("{:?}", e); - } - } - } -} - -impl Drop for Hijacker<'_> { - fn drop(&mut self) { - self.unhack() - } -} diff --git a/upatch-build/src/main.rs b/upatch-build/src/main.rs index b5c14a85722d13bf4a8658c4357adb351fa179cb..2ae47fe78914af6e1d4b39f3ea5551cb2a933283 100644 --- a/upatch-build/src/main.rs +++ b/upatch-build/src/main.rs @@ -13,6 +13,7 @@ */ use std::{ + env, ffi::OsStr, fs::Permissions, os::unix::fs::PermissionsExt, @@ -20,13 +21,12 @@ use std::{ process, }; -use anyhow::{ensure, Context, Result}; -use flexi_logger::{ - DeferredNow, Duplicate, FileSpec, LogSpecification, Logger, LoggerHandle, WriteMode, -}; -use indexmap::IndexSet; -use log::{debug, error, info, warn, Level, LevelFilter, Record}; -use object::{write, Object, ObjectSection, SectionKind}; +use anyhow::{bail, ensure, Context, Result}; +use flexi_logger::{Duplicate, FileSpec, LogSpecification, Logger, LoggerHandle, WriteMode}; +use indexmap::{IndexMap, IndexSet}; +use log::{debug, error, info, trace, warn, Level, LevelFilter}; +use object::{write, Object, ObjectKind, ObjectSection, SectionKind}; + use syscare_common::{concat_os, fs, os, process::Command}; mod args; @@ -35,19 +35,17 @@ mod compiler; mod dwarf; mod elf; mod file_relation; -mod hijacker; -mod pattern_path; mod project; mod resolve; -mod rpc; -use args::Arguments; -use build_root::BuildRoot; -use compiler::Compiler; -use dwarf::Dwarf; -use file_relation::FileRelation; -use hijacker::Hijacker; -use project::Project; +use crate::{ + args::Arguments, + build_root::BuildRoot, + compiler::Compiler, + dwarf::{ProducerParser, ProducerType}, + file_relation::FileRelation, + project::Project, +}; const CLI_NAME: &str = "upatch build"; const CLI_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -56,34 +54,28 @@ const CLI_UMASK: u32 = 0o022; const LOG_FILE_NAME: &str = "build"; -struct BuildInfo { - files: FileRelation, - linker: PathBuf, - temp_dir: PathBuf, - output_dir: PathBuf, - verbose: bool, -} +const PATH_ENV: &str = "PATH"; +const BINARY_INSTALL_PATH: &str = "/usr/libexec/syscare"; +const UPATCH_DIFF_BIN: &str = "upatch-diff"; struct UpatchBuild { args: Arguments, logger: LoggerHandle, build_root: BuildRoot, + compiler_map: IndexMap, + file_relation: FileRelation, } -/* Initialization */ +/* Main process */ impl UpatchBuild { - fn format_log( - w: &mut dyn std::io::Write, - _now: &mut DeferredNow, - record: &Record, - ) -> std::io::Result<()> { - write!(w, "{}", &record.args()) - } - fn new() -> Result { - // Initialize arguments & prepare environments + // Setup environment variable & umask + let path_env = env::var_os(PATH_ENV) + .with_context(|| format!("Cannot read environment variable {}", PATH_ENV))?; + env::set_var(PATH_ENV, concat_os!(BINARY_INSTALL_PATH, ":", path_env)); os::umask::set_umask(CLI_UMASK); + // Parse arguments let args = Arguments::new()?; let build_root = BuildRoot::new(&args.build_root)?; fs::create_dir_all(&args.output_dir)?; @@ -104,236 +96,213 @@ impl UpatchBuild { let logger = Logger::with(log_spec) .log_to_file(file_spec) .duplicate_to_stdout(Duplicate::from(log_level_stdout)) - .format(Self::format_log) + .format(|w, _, record| write!(w, "{}", record.args())) .write_mode(WriteMode::Direct) .start() .context("Failed to initialize logger")?; - // Initialize signal handler - ctrlc::set_handler(|| { - eprintln!("Interrupt"); - }) - .context("Failed to initialize signal handler")?; - Ok(Self { args, logger, build_root, + compiler_map: IndexMap::new(), + file_relation: FileRelation::new(), }) } -} -/* Tool functions */ -impl UpatchBuild { - fn check_debuginfo(compilers: &[Compiler], debuginfos: &[PathBuf]) -> Result<()> { - let supported_versions = compilers - .iter() - .flat_map(|c| c.versions.iter().map(|s| s.as_os_str())) - .collect::>(); - - debug!("Supported versions:"); - for version in &supported_versions { - debug!("- {}", version.to_string_lossy()); - } - - for debuginfo in debuginfos { - let versions = Dwarf::parse_compiler_versions(debuginfo).with_context(|| { - format!("Failed to parse compiler name of {}", debuginfo.display()) - })?; - for version in versions { - ensure!( - supported_versions.contains(version.as_os_str()), - "{} version mismatched, version={}", - debuginfo.display(), - version.to_string_lossy() - ); + fn detect_compilers(&mut self) -> Result<()> { + let mut c_compilers = 0usize; + let mut cxx_compilers = 0usize; + + for compiler_path in &self.args.compiler { + let compiler = Compiler::parse(compiler_path, &self.build_root.build_dir) + .with_context(|| format!("Failed to detect {}", compiler_path.display()))?; + match compiler.kind { + ProducerType::GnuC | ProducerType::ClangC => c_compilers += 1, + ProducerType::GnuCxx | ProducerType::ClangCxx => cxx_compilers += 1, + _ => bail!("Unknown compiler type"), } + info!( + "[{}] name: {}, version: {}", + compiler.kind, + compiler, + compiler.version.to_string_lossy(), + ); + self.compiler_map.insert(compiler.kind, compiler); } - Ok(()) - } - fn create_note, Q: AsRef>(debuginfo: P, path: Q) -> Result<()> { - let debuginfo_elf = unsafe { memmap2::Mmap::map(&std::fs::File::open(debuginfo)?)? }; - - let input_obj = - object::File::parse(&*debuginfo_elf).context("Failed to parse debuginfo")?; - let mut output_obj = write::Object::new( - input_obj.format(), - input_obj.architecture(), - input_obj.endianness(), + ensure!( + c_compilers <= 1 && cxx_compilers <= 1, + "Cannot define multiple C/C++ compilers" ); + self.compiler_map.sort_keys(); - for input_section in input_obj.sections() { - if input_section.kind() != SectionKind::Note { - continue; - } - - let section_name = input_section.name().context("Failed to get section name")?; - let section_data = input_section.data().context("Failed to get section data")?; - let section_id = output_obj.add_section( - vec![], - section_name.as_bytes().to_vec(), - input_section.kind(), - ); + Ok(()) + } - let output_section = output_obj.section_mut(section_id); - output_section.set_data(section_data, input_section.align()); - output_section.flags = input_section.flags(); + fn check_compiler_version(&self) -> Result<()> { + for path in &self.args.debuginfo { + let producer_parser = ProducerParser::open(path) + .with_context(|| format!("Failed to open {}", path.display()))?; + let producer_iter = producer_parser + .parse() + .with_context(|| format!("Failed to parse {}", path.display()))?; + + for parse_result in producer_iter { + let producer = parse_result.context("Failed to parse debuginfo producer")?; + if producer.is_assembler() { + continue; + } + let matched = self + .compiler_map + .get(&producer.kind) + .map(|compiler| compiler.version == producer.version) + .unwrap_or(false); + ensure!(matched, "Producer {} mismatched", producer); + } } - let contents = output_obj - .write() - .context("Failed to serialize note object")?; - fs::write(path, contents)?; - Ok(()) } - fn create_diff_objs( - original_object: &Path, - patched_object: &Path, - debuginfo: &Path, - output_dir: &Path, - verbose: bool, - ) -> Result<()> { - const UPATCH_DIFF_BIN: &str = "/usr/libexec/syscare/upatch-diff"; - - let ouput_name = original_object.file_name().with_context(|| { - format!( - "Failed to parse patch file name of {}", - original_object.display() - ) - })?; - let output_file = output_dir.join(ouput_name); - - let mut command = Command::new(UPATCH_DIFF_BIN); - command - .arg("-s") - .arg(original_object) - .arg("-p") - .arg(patched_object) - .arg("-o") - .arg(output_file) - .arg("-r") - .arg(debuginfo); + fn find_linker(&self, debuginfo: &Path) -> Result<&Path> { + let mut producers = ProducerParser::open(debuginfo) + .with_context(|| format!("Failed to open {}", debuginfo.display()))? + .parse() + .with_context(|| format!("Failed to parse {}", debuginfo.display()))? + .filter_map(|result| result.ok()) + .collect::>(); + producers.sort(); - if verbose { - command.arg("-d"); - } + let compiler = producers + .pop() + .and_then(|producer| self.compiler_map.get(&producer.kind)) + .context("Cannot find linking compiler")?; - command.stdout(Level::Trace).run_with_output()?.exit_ok() + Ok(compiler.linker.as_path()) } - fn link_objects(linker: P, objects: I, output: Q) -> Result<()> - where - P: AsRef, - I: IntoIterator, - S: AsRef, - Q: AsRef, - { - Command::new(linker.as_ref()) + fn link_objects(&self, objects: &[PathBuf], debuginfo: &Path, output: &Path) -> Result<()> { + let linker = self.find_linker(debuginfo).context("Cannot find linker")?; + + Command::new(linker) .args(["-r", "-o"]) - .arg(output.as_ref()) + .arg(output) .args(objects) - .run()? + .stdout(Level::Trace) + .run_with_output()? .exit_ok() } -} -/* Main process */ -impl UpatchBuild { - fn build_patch( - &self, - build_info: &BuildInfo, - binary: &Path, - debuginfo: &Path, - output_file: &Path, - ) -> Result<()> { - const OBJECT_EXTENSION: &str = "o"; + fn parse_text_offset(&self, binary: &Path) -> Result { + const TEXT_SECTION_NAME: &str = ".text"; + + let mmap = fs::mmap(binary)?; + let file = object::File::parse(mmap.as_ref())?; + let text_offset = file + .section_by_name(TEXT_SECTION_NAME) + .map(|section| { + let address = section.address(); + let offset = section.file_range().map(|(start, _)| start).unwrap_or(0); + address - offset + }) + .unwrap_or(0); + + Ok(text_offset) + } + + fn build_patch(&self, patch_name: &OsStr, binary: &Path, debuginfo: &Path) -> Result<()> { const NOTES_OBJECT_NAME: &str = "notes.o"; - let binary_name = binary.file_name().context("Failed to parse binary name")?; - let debuginfo_name = debuginfo - .file_name() - .context("Failed to parse debuginfo name")?; - let temp_dir = build_info.temp_dir.join(binary_name); - let new_debuginfo = temp_dir.join(debuginfo_name); + let temp_dir = self.build_root.build_dir.join(patch_name); + let output_dir = self.args.output_dir.as_path(); + + let debuginfo_file = temp_dir.join( + debuginfo + .file_name() + .context("Failed to parse debuginfo name")?, + ); + let output_file = output_dir.join(patch_name); - debug!("- Preparing to build patch"); fs::create_dir_all(&temp_dir)?; - fs::copy(debuginfo, &new_debuginfo)?; - fs::set_permissions(&new_debuginfo, Permissions::from_mode(0o644))?; + if fs::hard_link(debuginfo, &debuginfo_file).is_err() { + fs::copy(debuginfo, &debuginfo_file)?; + } + fs::set_permissions(&debuginfo_file, Permissions::from_mode(0o644))?; debug!("- Resolving debuginfo"); - resolve::resolve_dynamic(&new_debuginfo).context("Failed to resolve debuginfo")?; + resolve::resolve_dynamic(&debuginfo_file).context("Failed to resolve debuginfo")?; debug!("- Creating diff objects"); - let patched_objects = build_info - .files - .get_patched_objects(binary) + let binary_objects = self + .file_relation + .binary_objects(binary) .with_context(|| format!("Failed to find objects of {}", binary.display()))?; - for patched_object in patched_objects { - let original_object = build_info - .files - .get_original_object(patched_object) - .with_context(|| { - format!( - "Failed to find patched object of {}", - patched_object.display() - ) - })?; - - UpatchBuild::create_diff_objs( + let text_offset = self + .parse_text_offset(binary) + .with_context(|| format!("Failed to parse {} text section offset", binary.display()))?; + for (patched_object, original_object) in binary_objects { + debug!( + "* {}", + patched_object + .file_name() + .unwrap_or(patched_object.as_os_str()) + .to_string_lossy() + ); + Self::create_diff_objs( original_object, patched_object, - &new_debuginfo, + &debuginfo_file, + text_offset, &temp_dir, - build_info.verbose, ) - .with_context(|| format!("Failed to create diff objects for {}", binary.display()))?; + .with_context(|| { + format!( + "Failed to create diff objects for {}", + patch_name.to_string_lossy() + ) + })?; } debug!("- Collecting changes"); - let mut changed_objects = fs::list_files_by_ext( - &temp_dir, - OBJECT_EXTENSION, - fs::TraverseOptions { recursive: false }, - )?; - if changed_objects.is_empty() { + let mut objects = + elf::find_elf_files(&temp_dir, |_, kind| matches!(kind, ObjectKind::Relocatable))?; + if objects.is_empty() { debug!("- No functional changes"); return Ok(()); } debug!("- Creating patch notes"); - let notes_object = temp_dir.join(NOTES_OBJECT_NAME); - Self::create_note(&new_debuginfo, ¬es_object).context("Failed to create patch notes")?; - changed_objects.push(notes_object); + let notes_object = temp_dir.join(concat_os!(patch_name, "-", NOTES_OBJECT_NAME)); + Self::create_note(&debuginfo_file, ¬es_object) + .context("Failed to create patch notes")?; + objects.push(notes_object); - debug!("- Linking patch objects"); - Self::link_objects(&build_info.linker, &changed_objects, output_file) + debug!("- Linking patch"); + self.link_objects(&objects, &debuginfo_file, &output_file) .context("Failed to link patch objects")?; debug!("- Resolving patch"); - resolve::resolve_upatch(output_file, &new_debuginfo).context("Failed to resolve patch")?; + resolve::resolve_upatch(&output_file, &debuginfo_file) + .context("Failed to resolve patch")?; - debug!("- Patch: {}", output_file.display()); + debug!("- Done"); Ok(()) } - fn build_patches(&self, build_info: BuildInfo, name: &OsStr) -> Result<()> { - for (binary, debuginfo) in build_info.files.get_files() { + fn build_patches(&self) -> Result<()> { + for (binary, debuginfo) in self.file_relation.get_files() { let binary_name = binary .file_name() .with_context(|| format!("Failed to parse binary name of {}", binary.display()))?; - let patch_name = match name.is_empty() { - true => binary_name.to_os_string(), - false => concat_os!(name, "-", binary_name), + let patch_name = if self.args.prefix.is_empty() { + binary_name.to_os_string() + } else { + concat_os!(&self.args.prefix, "-", binary_name) }; - let output_file = build_info.output_dir.join(&patch_name); - - info!("Generating patch for '{}'", patch_name.to_string_lossy()); - self.build_patch(&build_info, binary, debuginfo, &output_file) + debug!("Generating patch '{}'", patch_name.to_string_lossy()); + self.build_patch(&patch_name, binary, debuginfo) .with_context(|| { format!("Failed to build patch '{}'", patch_name.to_string_lossy()) })?; @@ -343,102 +312,108 @@ impl UpatchBuild { } fn run(&mut self) -> Result<()> { - let work_dir = self.args.work_dir.as_path(); - let name = self.args.name.as_os_str(); - let output_dir = self.args.output_dir.as_path(); - let object_dir = self.args.object_dir.as_path(); - let binaries = self.args.elf.as_slice(); - let debuginfos = self.args.debuginfo.as_slice(); - let verbose = self.args.verbose; - - let temp_dir = self.build_root.temp_dir.as_path(); - let original_dir = self.build_root.original_dir.as_path(); - let patched_dir = self.build_root.patched_dir.as_path(); - info!("=============================="); info!("{}", CLI_ABOUT); info!("=============================="); - info!("Checking compiler(s)"); - - let compilers = Compiler::parse(&self.args.compiler, temp_dir)?; - let linker = compilers - .iter() - .map(|c| c.linker.clone()) - .collect::>() - .pop() - .context("Failed to find any linker")?; + trace!("{:#?}", self.args); - debug!("------------------------------"); - debug!("Compiler"); - debug!("------------------------------"); - for compiler in &compilers { - debug!("{}", compiler); - } - debug!("------------------------------"); + info!("Detecting compiler(s)"); + info!("------------------------------"); + info!("Compiler"); + info!("------------------------------"); + self.detect_compilers()?; - let project = Project::new(&self.args, &self.build_root); + let project = Project::new(&self.args, &self.build_root, &self.compiler_map)?; info!("------------------------------"); - info!("Project {}", project); + info!("Project"); info!("------------------------------"); info!("Testing patch file(s)"); - project - .test_patches(&self.args.patch) - .context("Patch test failed")?; + project.test_patches().context("Patch test failed")?; + + info!("Checking compiler version(s)"); + if self.args.skip_compiler_check { + warn!("Warning: Skipped compiler version check!") + } else { + self.check_compiler_version() + .context("Compiler version check failed")?; + } - info!("Checking debuginfo version(s)"); - match self.args.skip_compiler_check { - false => { - Self::check_debuginfo(&compilers, debuginfos).context("Debuginfo check failed")?; - } - true => warn!("Warning: Skipped compiler version check!"), + if !self.args.prepare_cmd.is_empty() { + info!("Preparing '{}'", project); + project + .prepare() + .with_context(|| format!("Failed to prepare {}", project))?; + } + + if !self.args.clean_cmd.is_empty() { + info!("Cleaning '{}'", project); + project + .clean() + .with_context(|| format!("Failed to clean {}", project))?; } - let mut files = FileRelation::new(); - let hijacker = Hijacker::new(&compilers, work_dir).context("Failed to hack compilers")?; + if self.args.override_line_macros { + info!("Overriding line macros"); + project + .override_line_macros() + .context("Failed to override line macros")?; + } - info!("Preparing {}", project); - project - .prepare() - .with_context(|| format!("Failed to prepare {}", project))?; + let binary_dir = self.args.binary_dir.as_path(); + let object_dir = self.args.object_dir.as_path(); + let binaries = self.args.binary.as_slice(); + let debuginfos = self.args.debuginfo.as_slice(); - info!("Building {}", project); + let original_dir = self.build_root.original_dir.as_path(); + let patched_dir = self.build_root.patched_dir.as_path(); + + info!("Building '{}'", project); project .build() .with_context(|| format!("Failed to build {}", project))?; + self.file_relation + .collect_debuginfo(binary_dir, binaries, debuginfos)?; + self.file_relation + .collect_original_build(object_dir, original_dir)?; + + if !self.args.prepare_cmd.is_empty() { + info!("Preparing '{}'", project); + project + .prepare() + .with_context(|| format!("Failed to prepare {}", project))?; + } - info!("Collecting file relations"); - files.collect_debuginfo(binaries, debuginfos)?; - files.collect_original_build(object_dir, original_dir)?; - - info!("Preparing {}", project); - project - .prepare() - .with_context(|| format!("Failed to prepare {}", project))?; + if !self.args.clean_cmd.is_empty() { + info!("Cleaning '{}'", project); + project + .clean() + .with_context(|| format!("Failed to clean {}", project))?; + } - info!("Patching {}", project); + info!("Patching '{}'", project); project - .apply_patches(&self.args.patch) + .apply_patches() .with_context(|| format!("Failed to patch {}", project))?; - info!("Rebuilding {}", project); - project - .rebuild() - .with_context(|| format!("Failed to rebuild {}", project))?; - - info!("Collecting file relations"); - files.collect_patched_build(object_dir, patched_dir)?; + if self.args.override_line_macros { + info!("Overriding line macros"); + project + .override_line_macros() + .context("Failed to override line macros")?; + } - // Unhack compilers - drop(hijacker); + info!("Building '{}'", project); + project + .build() + .with_context(|| format!("Failed to build {}", project))?; + self.file_relation + .collect_patched_build(object_dir, patched_dir)?; + trace!("{:#?}", self.file_relation); - let build_info = BuildInfo { - linker, - files, - temp_dir: temp_dir.to_path_buf(), - output_dir: output_dir.to_path_buf(), - verbose, - }; - self.build_patches(build_info, name)?; + info!("------------------------------"); + info!("Patches"); + info!("------------------------------"); + self.build_patches()?; if !self.args.skip_cleanup { info!("Cleaning up"); @@ -450,6 +425,71 @@ impl UpatchBuild { } } +/* Tool functions */ +impl UpatchBuild { + fn create_note(debuginfo: &Path, output: &Path) -> Result<()> { + let mmap = fs::mmap(debuginfo) + .with_context(|| format!("Failed to mmap file {}", debuginfo.display()))?; + let file = object::File::parse(mmap.as_ref()) + .with_context(|| format!("Failed to parse {}", debuginfo.display()))?; + + let mut new_object = + write::Object::new(file.format(), file.architecture(), file.endianness()); + + for section in file.sections() { + if section.kind() != SectionKind::Note { + continue; + } + let section_name = section.name().context("Failed to get section name")?; + let section_data = section.data().context("Failed to get section data")?; + let section_id = + new_object.add_section(vec![], section_name.as_bytes().to_vec(), section.kind()); + + let new_section = new_object.section_mut(section_id); + new_section.set_data(section_data, section.align()); + new_section.flags = section.flags(); + } + + let contents = new_object + .write() + .context("Failed to serialize note object")?; + fs::write(output, contents)?; + + Ok(()) + } + + fn create_diff_objs( + original_object: &Path, + patched_object: &Path, + debuginfo: &Path, + text_offset: u64, + output_dir: &Path, + ) -> Result<()> { + let ouput_name = original_object.file_name().with_context(|| { + format!( + "Failed to parse patch file name of {}", + original_object.display() + ) + })?; + let output_file = output_dir.join(ouput_name); + + let mut command = Command::new(UPATCH_DIFF_BIN); + command + .arg("-s") + .arg(original_object) + .arg("-p") + .arg(patched_object) + .arg("-r") + .arg(debuginfo); + if text_offset > 0 { + command.arg("-t").arg(text_offset.to_string()); + } + command.arg("-o").arg(output_file); + + command.stdout(Level::Trace).run_with_output()?.exit_ok() + } +} + impl Drop for UpatchBuild { fn drop(&mut self) { self.logger.flush(); diff --git a/upatch-build/src/pattern_path.rs b/upatch-build/src/pattern_path.rs deleted file mode 100644 index 4ef86e12ef66467f4047185b25bb310cac068fa1..0000000000000000000000000000000000000000 --- a/upatch-build/src/pattern_path.rs +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatch-build is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - ffi::{OsStr, OsString}, - os::unix::prelude::OsStrExt, - path::{Component, Path, PathBuf}, -}; - -use syscare_common::fs; - -pub fn glob>(path: P) -> std::io::Result> { - let components = Path::new(path.as_ref()).components().collect::>(); - let mut pathes = vec![PathBuf::new()]; - - if components[0].ne(&Component::RootDir) - && components[0].ne(&Component::CurDir) - && components[0].ne(&Component::ParentDir) - { - push_path(Component::CurDir, &mut pathes); - } - - for i in 0..components.len() { - match components[i] { - Component::RootDir | Component::CurDir | Component::ParentDir => { - push_path(components[i], &mut pathes); - } - _ => { - let mut path_clone = vec![]; - for p in &mut pathes { - let tmp = p.join(components[i]); - match tmp.exists() { - true => path_clone.push(tmp), - false => { - let all_pathes = match i == (components.len() - 1) { - true => { - fs::list_files(&p, fs::TraverseOptions { recursive: false }) - } - false => { - fs::list_dirs(&p, fs::TraverseOptions { recursive: false }) - } - }?; - for name in find_name(components[i].as_os_str(), all_pathes)? { - path_clone.push(p.join(name)); - } - } - }; - } - pathes = path_clone; - } - }; - } - Ok(pathes) -} - -fn push_path>(name: O, pathes: &mut Vec) { - for p in pathes { - *p = p.join(name.as_ref()); - } -} - -fn find_name(name: &OsStr, all_pathes: Vec) -> std::io::Result> { - let mut result = Vec::new(); - - for dir in all_pathes { - if let Some(path_name) = dir.file_name() { - if pattern_match(path_name.as_bytes(), name.as_bytes()) { - result.push(path_name.to_os_string()); - } - } - } - - Ok(result) -} - -fn pattern_match(name: &[u8], pattern: &[u8]) -> bool { - let (mut i, mut j) = (0, 0); - let (mut i_star, mut j_star) = (-1, -1); - let (m, n) = (name.len(), pattern.len()); - - while i < m { - if j < n && (name[i].eq(&pattern[j]) || pattern[j].eq(&63)) { - i += 1; - j += 1; - } else if j < n && pattern[j].eq(&42) { - i_star = i as i32; - j_star = j as i32; - j += 1; - } else if i_star >= 0 { - i_star += 1; - i = i_star as usize; - j = (j_star + 1) as usize; - } else { - return false; - } - } - while j < n && pattern[j].eq(&42) { - j += 1; - } - j == n -} diff --git a/upatch-build/src/project.rs b/upatch-build/src/project.rs index b36c26bc2452133748c1db7bc7548495b1806618..c05cd18d3ff8bfb0cebb7fb89881e6f51d9395a4 100644 --- a/upatch-build/src/project.rs +++ b/upatch-build/src/project.rs @@ -13,56 +13,95 @@ */ use std::{ + env, ffi::{OsStr, OsString}, fs::File, io::Write, - os::unix::ffi::OsStrExt, + os::unix::ffi::{OsStrExt as _, OsStringExt}, path::{Path, PathBuf}, }; use anyhow::{Context, Result}; - +use indexmap::IndexMap; use log::{debug, Level}; -use syscare_common::{fs, process::Command}; +use which::which; -use crate::{args::Arguments, build_root::BuildRoot}; +use crate::{args::Arguments, build_root::BuildRoot, compiler::Compiler, dwarf::ProducerType}; +use syscare_common::{concat_os, ffi::OsStrExt, fs, process::Command}; const PATCH_BIN: &str = "patch"; -const COMPILER_CMD_ENV: &str = "UPATCH_HIJACKER"; +const UPATCH_HELPER_BIN: &str = "upatch-helper"; +const UPATCH_HELPER_CC_BIN: &str = "upatch-cc"; +const UPATCH_HELPER_CXX_BIN: &str = "upatch-c++"; const PREPARE_SCRIPT_NAME: &str = "prepare.sh"; const BUILD_SCRIPT_NAME: &str = "build.sh"; +const CLEAN_SCRIPT_NAME: &str = "clean.sh"; + +const PATH_ENV: &str = "PATH"; +const CC_ENV: &str = "CC"; +const CXX_ENV: &str = "CXX"; + +const UPATCH_CC_ENV: &str = "UPATCH_HELPER_CC"; +const UPATCH_CXX_ENV: &str = "UPATCH_HELPER_CXX"; pub struct Project<'a> { name: OsString, - root_dir: &'a Path, - build_dir: &'a Path, - original_dir: &'a Path, - patched_dir: &'a Path, - prepare_cmd: &'a str, - build_cmd: &'a str, + build_root: &'a BuildRoot, + source_dir: &'a Path, + source_ext: &'a [OsString], + prepare_cmd: &'a OsStr, + build_cmd: &'a OsStr, + clean_cmd: &'a OsStr, + patches: &'a [PathBuf], } impl<'a> Project<'a> { - pub fn new(args: &'a Arguments, build_root: &'a BuildRoot) -> Self { - let root_dir = args.source_dir.as_path(); - let build_dir = build_root.temp_dir.as_path(); - let original_dir = build_root.original_dir.as_path(); - let patched_dir = build_root.patched_dir.as_path(); - - let name = fs::file_name(root_dir); - let prepare_cmd = args.prepare_cmd.as_str(); - let build_cmd = args.build_cmd.as_str(); - - Self { - name, - root_dir, - build_dir, - original_dir, - patched_dir, - prepare_cmd, - build_cmd, + pub fn new( + args: &'a Arguments, + build_root: &'a BuildRoot, + compiler_map: &'a IndexMap, + ) -> Result { + let path_env = env::var_os(PATH_ENV) + .with_context(|| format!("Cannot read environment variable '{}'", PATH_ENV))?; + let upatch_helper = which(UPATCH_HELPER_BIN) + .with_context(|| format!("Cannot find component '{}'", UPATCH_HELPER_BIN))?; + + for (kind, compiler) in compiler_map { + let compiler_path = compiler.path.as_path(); + let compiler_name = compiler_path + .file_name() + .context("Failed to parse compiler name")?; + match kind { + ProducerType::GnuC | ProducerType::ClangC => { + env::set_var(UPATCH_CC_ENV, compiler_path) + } + ProducerType::GnuCxx | ProducerType::ClangCxx => { + env::set_var(UPATCH_CXX_ENV, compiler_path) + } + _ => {} + } + fs::soft_link(&upatch_helper, build_root.bin_dir.join(compiler_name))?; } + + env::set_var(PATH_ENV, concat_os!(&build_root.bin_dir, ":", path_env)); + env::set_var(CC_ENV, UPATCH_HELPER_CC_BIN); + env::set_var(CXX_ENV, UPATCH_HELPER_CXX_BIN); + + Ok(Self { + name: args + .source_dir + .file_name() + .context("Failed to parse project name")? + .to_os_string(), + build_root, + source_dir: args.source_dir.as_path(), + source_ext: args.source_ext.as_slice(), + prepare_cmd: args.prepare_cmd.as_os_str(), + build_cmd: args.build_cmd.as_os_str(), + clean_cmd: args.clean_cmd.as_os_str(), + patches: args.patch.as_slice(), + }) } } @@ -77,7 +116,7 @@ impl Project<'_> { .args(args) .arg("-i") .arg(patch_file.as_ref()) - .current_dir(self.root_dir) + .current_dir(self.source_dir) .run_with_output()? .exit_ok() } @@ -87,12 +126,11 @@ impl Project<'_> { S: AsRef, T: AsRef, { - let script = self.build_dir.join(script_name.as_ref()); + let script = self.build_root.script_dir.join(script_name.as_ref()); let mut script_file = File::create(&script)?; script_file.write_all(b"#!/bin/bash\n")?; script_file.write_all(command.as_ref().as_bytes())?; - drop(script_file); Ok(script) } @@ -101,32 +139,14 @@ impl Project<'_> { where S: AsRef, T: AsRef, - { - let script = self.create_script(script_name, command)?; - - Command::new("sh") - .arg(script) - .current_dir(self.root_dir) - .stdout(Level::Debug) - .run_with_output()? - .exit_ok() - } - - fn exec_build_command(&self, script_name: S, command: T, object_dir: P) -> Result<()> - where - S: AsRef, - T: AsRef, - P: AsRef, { if command.as_ref().is_empty() { return Ok(()); } let script = self.create_script(script_name, command)?; - Command::new("sh") .arg(script) - .env(COMPILER_CMD_ENV, object_dir.as_ref()) - .current_dir(self.root_dir) + .current_dir(self.source_dir) .stdout(Level::Debug) .run_with_output()? .exit_ok() @@ -134,29 +154,52 @@ impl Project<'_> { } impl Project<'_> { - pub fn apply_patches>(&self, patches: &[P]) -> Result<()> { - for patch in patches { - debug!("- Applying patch"); + pub fn apply_patches(&self) -> Result<()> { + for patch in self.patches.iter() { + debug!("* {}", patch.display()); self.patch(patch, ["-N", "-p1"]) - .with_context(|| format!("Failed to patch {}", patch.as_ref().display()))?; + .with_context(|| format!("Failed to patch {}", patch.display()))?; } Ok(()) } - pub fn remove_patches>(&self, patches: &[P]) -> Result<()> { - debug!("- Removing patch"); - for patch in patches.iter().rev() { + pub fn remove_patches(&self) -> Result<()> { + for patch in self.patches.iter().rev() { self.patch(patch, ["-R", "-p1"]) - .with_context(|| format!("Failed to unpatch {}", patch.as_ref().display()))?; + .with_context(|| format!("Failed to unpatch {}", patch.display()))?; } Ok(()) } - pub fn test_patches>(&self, patches: &[P]) -> Result<()> { - self.apply_patches(patches)?; - self.remove_patches(patches)?; + pub fn test_patches(&self) -> Result<()> { + self.apply_patches()?; + self.remove_patches()?; + + Ok(()) + } + + pub fn override_line_macros(&self) -> Result<()> { + const LINE_MACRO_NAME: &str = "__LINE__"; + const LINE_MACRO_VALUE: &str = "0"; + + let file_list = fs::list_files(self.source_dir, fs::TraverseOptions { recursive: true })?; + for file_path in file_list { + let file_ext = file_path.extension().unwrap_or_default(); + if self.source_ext.iter().all(|ext| ext != file_ext) { + continue; + } + + let old_contents = fs::read(&file_path)?; + let new_contents = OsStr::from_bytes(&old_contents) + .replace(LINE_MACRO_NAME, LINE_MACRO_VALUE) + .into_vec(); + if old_contents != new_contents { + debug!("* {}", file_path.display()); + fs::write(&file_path, new_contents)?; + } + } Ok(()) } @@ -166,11 +209,11 @@ impl Project<'_> { } pub fn build(&self) -> Result<()> { - self.exec_build_command(BUILD_SCRIPT_NAME, self.build_cmd, self.original_dir) + self.exec_command(BUILD_SCRIPT_NAME, self.build_cmd) } - pub fn rebuild(&self) -> Result<()> { - self.exec_build_command(BUILD_SCRIPT_NAME, self.build_cmd, self.patched_dir) + pub fn clean(&self) -> Result<()> { + self.exec_command(CLEAN_SCRIPT_NAME, self.clean_cmd) } } @@ -179,3 +222,9 @@ impl std::fmt::Display for Project<'_> { write!(f, "{}", self.name.to_string_lossy()) } } + +impl Drop for Project<'_> { + fn drop(&mut self) { + self.remove_patches().ok(); + } +} diff --git a/upatch-build/src/resolve.rs b/upatch-build/src/resolve.rs index 58947c0b1bbbcb9db1959da5f4ebecf09780aa68..d579901290a13d796db4fd357981a0188f17cb12 100644 --- a/upatch-build/src/resolve.rs +++ b/upatch-build/src/resolve.rs @@ -17,19 +17,19 @@ use std::path::Path; use anyhow::Result; use log::trace; -use crate::elf::*; +use crate::elf::{self, HeaderRead, HeaderWrite, SymbolRead, SymbolWrite}; pub fn resolve_upatch(patch: Q, debuginfo: P) -> Result<()> where P: AsRef, Q: AsRef, { - let mut patch_elf = write::Elf::parse(patch)?; - let debuginfo_elf = read::Elf::parse(debuginfo)?; + let mut patch_elf = elf::write::Elf::parse(patch)?; + let debuginfo_elf = elf::read::Elf::parse(debuginfo)?; let debuginfo_e_ident = debuginfo_elf.header()?.get_e_ident(); let debuginfo_e_type = debuginfo_elf.header()?.get_e_type(); - let ei_osabi = elf_ei_osabi(debuginfo_e_ident); + let ei_osabi = elf::elf_ei_osabi(debuginfo_e_ident); patch_elf.header()?.set_e_ident(debuginfo_e_ident); @@ -37,28 +37,29 @@ where for mut symbol in &mut patch_elf.symbols()? { /* No need to handle section symbol */ - let sym_info = symbol.get_st_info(); - if elf_st_type(sym_info) == STT_SECTION { + let sym_st_info = symbol.get_st_info(); + if elf::elf_st_type(sym_st_info) == elf::STT_SECTION { continue; } let sym_other = symbol.get_st_other(); - if sym_other & SYM_OTHER != 0 { + if sym_other & elf::SYM_OTHER != 0 { // TODO: we can delete these symbol's section here. - symbol.set_st_other(sym_other & !SYM_OTHER); + symbol.set_st_other(sym_other & !elf::SYM_OTHER); match symbol.get_st_value() { - 0 => symbol.set_st_shndx(SHN_UNDEF), - _ => symbol.set_st_shndx(SHN_LIVEPATCH), + 0 => symbol.set_st_shndx(elf::SHN_UNDEF), + _ => symbol.set_st_shndx(elf::SHN_LIVEPATCH), }; - } else if symbol.get_st_shndx() == SHN_UNDEF { - if elf_st_bind(sym_info) == STB_LOCAL { + } else if symbol.get_st_shndx() == elf::SHN_UNDEF { + if elf::elf_st_bind(sym_st_info) == elf::STB_LOCAL { /* only partly resolved undefined symbol could have st_value */ if symbol.get_st_value() != 0 { - symbol.set_st_shndx(SHN_LIVEPATCH); + symbol.set_st_shndx(elf::SHN_LIVEPATCH); } } else { __partial_resolve_patch(&mut symbol, debuginfo_syms, ei_osabi)?; } + } else { /* do nothing */ } /* @@ -66,21 +67,20 @@ where * Such code accesses all constant addresses through a global offset table (GOT). * TODO: consider check PIE */ - let sym_info = symbol.get_st_info(); - if debuginfo_e_type == ET_DYN - && elf_st_bind(sym_info) == STB_GLOBAL - && elf_st_type(sym_info) == STT_OBJECT - && symbol.get_st_shndx() == SHN_LIVEPATCH + if debuginfo_e_type == elf::ET_DYN + && elf::elf_st_bind(sym_st_info) == elf::STB_GLOBAL + && elf::elf_st_type(sym_st_info) == elf::STT_OBJECT + && symbol.get_st_shndx() == elf::SHN_LIVEPATCH { - symbol.set_st_shndx(SHN_UNDEF); + symbol.set_st_shndx(elf::SHN_UNDEF); } } Ok(()) } fn __partial_resolve_patch( - symbol: &mut write::SymbolHeader, - debuginfo_syms: &mut read::SymbolHeaderTable, + symbol: &mut elf::write::SymbolHeader, + debuginfo_syms: &mut elf::read::SymbolHeaderTable, ei_osabi: u8, ) -> Result<()> { debuginfo_syms.reset(0); @@ -88,28 +88,29 @@ fn __partial_resolve_patch( for debuginfo_sym in debuginfo_syms { /* No need to handle section symbol */ let sym_info = debuginfo_sym.get_st_info(); - if elf_st_type(sym_info) == STT_SECTION { + if elf::elf_st_type(sym_info) == elf::STT_SECTION { continue; } let debuginfo_name = debuginfo_sym.get_st_name(); - if elf_st_bind(sym_info).ne(&elf_st_bind(symbol.get_st_info())) + if elf::elf_st_bind(sym_info).ne(&elf::elf_st_bind(symbol.get_st_info())) || debuginfo_name.ne(symbol.get_st_name()) { continue; } /* leave it to be handled in running time */ - if debuginfo_sym.get_st_shndx() == SHN_UNDEF { + if debuginfo_sym.get_st_shndx() == elf::SHN_UNDEF { continue; } // symbol type is STT_IFUNC, need search st_value in .plt table in upatch. - let is_ifunc = (ei_osabi.eq(&ELFOSABI_GNU) || ei_osabi.eq(&ELFOSABI_FREEBSD)) - && elf_st_type(sym_info).eq(&STT_IFUNC); - symbol.set_st_shndx(match is_ifunc { - true => SHN_UNDEF, - false => SHN_LIVEPATCH, + let is_ifunc = (ei_osabi.eq(&elf::ELFOSABI_GNU) || ei_osabi.eq(&elf::ELFOSABI_FREEBSD)) + && elf::elf_st_type(sym_info).eq(&elf::STT_IFUNC); + symbol.set_st_shndx(if is_ifunc { + elf::SHN_UNDEF + } else { + elf::SHN_LIVEPATCH }); symbol.set_st_info(sym_info); symbol.set_st_other(debuginfo_sym.get_st_other()); @@ -131,17 +132,17 @@ fn __partial_resolve_patch( * then we can't match these symbols, we change these symbols to GLOBAL here. */ pub fn resolve_dynamic>(debuginfo: P) -> Result<()> { - let mut debuginfo_elf = write::Elf::parse(debuginfo)?; + let mut debuginfo_elf = elf::write::Elf::parse(debuginfo)?; let debuginfo_header = debuginfo_elf.header()?; - if debuginfo_header.get_e_type().ne(&ET_DYN) { + if debuginfo_header.get_e_type().ne(&elf::ET_DYN) { return Ok(()); } let mut debuginfo_symbols = debuginfo_elf.symbols()?; for mut symbol in &mut debuginfo_symbols { - if elf_st_type(symbol.get_st_info()).ne(&STT_FILE) { + if elf::elf_st_type(symbol.get_st_info()).ne(&elf::STT_FILE) { continue; } @@ -154,23 +155,23 @@ pub fn resolve_dynamic>(debuginfo: P) -> Result<()> { Ok(()) } -fn _resolve_dynamic(debuginfo_symbols: &mut write::SymbolHeaderTable) -> Result<()> { +fn _resolve_dynamic(debuginfo_symbols: &mut elf::write::SymbolHeaderTable) -> Result<()> { for mut symbol in debuginfo_symbols { - if elf_st_type(symbol.get_st_info()).eq(&STT_FILE) { + if elf::elf_st_type(symbol.get_st_info()).eq(&elf::STT_FILE) { break; } - let info = symbol.get_st_info(); - if elf_st_bind(info).ne(&STB_GLOBAL) { + let st_info = symbol.get_st_info(); + if elf::elf_st_bind(st_info).ne(&elf::STB_GLOBAL) { let symbol_name = symbol.get_st_name(); trace!( "resolve_dynamic: set {} bind {} to 1", symbol_name.to_string_lossy(), - elf_st_bind(info) + elf::elf_st_bind(st_info) ); - let info = elf_st_type(symbol.get_st_info()) | (STB_GLOBAL << 4); - symbol.set_st_info(info); + let new_st_info = elf::elf_st_type(st_info) | (elf::STB_GLOBAL << 4); + symbol.set_st_info(new_st_info); } } Ok(()) diff --git a/upatch-build/src/rpc/args.rs b/upatch-build/src/rpc/args.rs deleted file mode 100644 index 7354da60b75ab71caca1d4444f4224739ef19a74..0000000000000000000000000000000000000000 --- a/upatch-build/src/rpc/args.rs +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatch-build is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use jsonrpc::serde_json::value::RawValue; -use serde::Serialize; - -use std::ops::Deref; - -#[derive(Debug, Default)] -pub struct RpcArguments { - args: Vec>, -} - -impl RpcArguments { - pub fn new() -> Self { - Self::default() - } - - pub fn arg(mut self, arg: T) -> Self { - self.args.push(jsonrpc::arg(arg)); - self - } -} - -impl Deref for RpcArguments { - type Target = [Box]; - - fn deref(&self) -> &Self::Target { - &self.args - } -} diff --git a/upatch-build/src/rpc/mod.rs b/upatch-build/src/rpc/mod.rs deleted file mode 100644 index a0f5b032daed6aa7306ebbcbaaecbf74558f1a5d..0000000000000000000000000000000000000000 --- a/upatch-build/src/rpc/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatch-build is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -mod args; -mod proxy; -mod remote; - -pub use proxy::*; -pub use remote::*; diff --git a/upatch-build/src/rpc/proxy.rs b/upatch-build/src/rpc/proxy.rs deleted file mode 100644 index e249fa5c61c67a7ec3f41620592942543bbbea91..0000000000000000000000000000000000000000 --- a/upatch-build/src/rpc/proxy.rs +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatch-build is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{path::Path, rc::Rc}; - -use anyhow::Result; -use function_name::named; - -use super::{args::RpcArguments, remote::RpcRemote}; - -#[derive(Clone)] -pub struct UpatchProxy { - remote: Rc, -} - -impl UpatchProxy { - pub fn new(remote: Rc) -> Self { - Self { remote } - } - - #[named] - pub fn enable_hijack>(&self, exec_path: P) -> Result<()> { - self.remote.call_with_args( - function_name!(), - RpcArguments::new().arg(exec_path.as_ref().to_path_buf()), - ) - } - - #[named] - pub fn disable_hijack>(&self, exec_path: P) -> Result<()> { - self.remote.call_with_args( - function_name!(), - RpcArguments::new().arg(exec_path.as_ref().to_path_buf()), - ) - } -} diff --git a/upatch-build/src/rpc/remote.rs b/upatch-build/src/rpc/remote.rs deleted file mode 100644 index 9927ece10108c9a12a4932a68b2d9d8395d92242..0000000000000000000000000000000000000000 --- a/upatch-build/src/rpc/remote.rs +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatch-build is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::{Path, PathBuf}; - -use anyhow::{anyhow, Result}; -use jsonrpc::{simple_uds::UdsTransport, Client, Error}; -use log::{debug, trace}; -use serde::Deserialize; - -use super::args::RpcArguments; - -pub struct RpcRemote { - socket: PathBuf, - client: Client, -} - -impl RpcRemote { - pub fn new>(file_path: P) -> Self { - Self { - socket: file_path.as_ref().to_path_buf(), - client: Client::with_transport(UdsTransport::new(file_path)), - } - } - - pub fn call_with_args(&self, cmd: &str, args: RpcArguments) -> Result - where - T: for<'a> Deserialize<'a>, - { - let request = self.client.build_request(cmd, &args); - trace!("{:?}", request); - - let response = self - .client - .send_request(request) - .map_err(|e| self.parse_error(e))?; - trace!("{:?}", response); - - response.result().map_err(|e| self.parse_error(e)) - } -} - -impl RpcRemote { - fn parse_error(&self, error: Error) -> anyhow::Error { - match error { - Error::Transport(e) => { - anyhow!( - "Cannot connect to upatch daemon at unix://{}, {}", - self.socket.display(), - e.source() - .map(|e| e.to_string()) - .unwrap_or_else(|| "Connection timeout".to_string()) - ) - } - Error::Json(e) => { - debug!("Json parse error: {:?}", e); - anyhow!("Failed to parse response") - } - Error::Rpc(ref e) => match e.message == "Method not found" { - true => { - anyhow!("Method is unimplemented") - } - false => { - anyhow!("{}", e.message) - } - }, - _ => { - debug!("{:?}", error); - anyhow!("Response is invalid") - } - } - } -} diff --git a/upatch-diff/CMakeLists.txt b/upatch-diff/CMakeLists.txt index 45091fc56c4ec2da8fa44b32630b0f4f2f41f71d..a1c86886bb6e5743e3b37ff31c982803da97c737 100644 --- a/upatch-diff/CMakeLists.txt +++ b/upatch-diff/CMakeLists.txt @@ -18,8 +18,8 @@ install( TARGETS upatch-diff PERMISSIONS - OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION ${SYSCARE_LIBEXEC_DIR} diff --git a/upatch-diff/create-diff-object.c b/upatch-diff/create-diff-object.c index 1a0586915b9ab5388508f67f872e45e8dc4f2de4..886d8b21431b36e16b3ae5400423cc5cb0308eb3 100644 --- a/upatch-diff/create-diff-object.c +++ b/upatch-diff/create-diff-object.c @@ -57,12 +57,11 @@ #include "elf-resolve.h" #include "elf-create.h" #include "running-elf.h" -//#include "upatch-manage.h" #include "upatch-patch.h" #define PROG_VERSION "upatch-diff "BUILD_VERSION -enum LogLevel g_loglevel = NORMAL; +enum log_level g_loglevel = NORMAL; char *g_logprefix; char *g_uelf_name; char *g_relf_name; @@ -72,48 +71,42 @@ struct arguments { char *patched_obj; char *running_elf; char *output_obj; + unsigned long text_offset; bool debug; }; -static struct argp_option options[] = { - {"debug", 'd', NULL, 0, "Show debug output"}, - {"source", 's', "source", 0, "Source object"}, - {"patched", 'p', "patched", 0, "Patched object"}, - {"running", 'r', "running", 0, "Running binary file"}, - {"output", 'o', "output", 0, "Output object"}, +static const struct argp_option ARGP_OPTION[] = { + {"source", 's', "", 0, "Source object", 0}, + {"patched", 'p', "", 0, "Patched object", 1}, + {"running", 'r', "", 0, "Running binary file", 2}, + {"output", 'o', "", 0, "Output object", 3}, + {"text-offset", 't', "", 0, "Text section offset", 4}, + {"debug", 'd', NULL, 0, "Show debug output", 5}, {NULL} }; - -static char program_doc[] = - "upatch-build -- generate a patch object based on the source object"; - -static char args_doc[] = "-s source_obj -p patched_obj -r elf_file -o output_obj"; - +static const char ARGP_DOC[] = "Generate a patch object based on source object"; const char *argp_program_version = PROG_VERSION; -static error_t check_opt(struct argp_state *state) +static void parse_text_offset(struct argp_state *state, const char *arg) { - struct arguments *arguments = state->input; + errno = 0; + char *endptr = NULL; - if (arguments->source_obj == NULL || - arguments->patched_obj == NULL || - arguments->running_elf == NULL || - arguments->output_obj == NULL) { - argp_usage(state); - return ARGP_ERR_UNKNOWN; + unsigned long offset = strtoul(arg, &endptr, 0); + if ((errno != 0) || (*endptr != '\0') || + ((errno == ERANGE) && (offset == ULONG_MAX))) { + argp_error(state, "ERROR: Invalid text section offset '%s'", arg); } - return 0; + + struct arguments *arguments = state->input; + arguments->text_offset = offset; } static error_t parse_opt(int key, char *arg, struct argp_state *state) { struct arguments *arguments = state->input; - switch (key) - { - case 'd': - arguments->debug = true; - break; + switch (key) { case 's': arguments->source_obj = arg; break; @@ -126,45 +119,70 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 'o': arguments->output_obj = arg; break; - case ARGP_KEY_ARG: + case 't': + parse_text_offset(state, arg); + break; + case 'd': + arguments->debug = true; break; - case ARGP_KEY_END: - return check_opt(state); default: return ARGP_ERR_UNKNOWN; } return 0; } -static struct argp argp = {options, parse_opt, args_doc, program_doc}; - -/* - * Key point for chreate-diff-object: - * 1. find changed func/data for each object - * 2. link all these objects into a relocatable file - * 3. add sections for management (hash/init/patch info etc.) - * 4. locate old symbols for the relocatable file - */ +static bool check_args(struct arguments *arguments) +{ + if (arguments->source_obj == NULL) { + log_error("The argument '--source ' requires a value\n"); + return false; + } + if (arguments->patched_obj == NULL) { + log_error("The argument '--patched ' requires a value\n"); + return false; + } + if (arguments->running_elf == NULL) { + log_error("The argument '--running ' requires a value\n"); + return false; + } + if (arguments->output_obj == NULL) { + log_error("The argument '--output ' requires a value\n"); + return false; + } + if (arguments->text_offset > UINT32_MAX) { + ERROR("Text section offset 0x%lx overflow", arguments->text_offset); + } + if ((arguments->text_offset & 0xFFF) != 0) { + ERROR("Text section offset 0x%lx is not 4K-aligned", + arguments->text_offset); + } + return true; +} -/* Format of output file is the only export API */ static void show_program_info(struct arguments *arguments) { - log_debug("source object: %s\n", arguments->source_obj); + log_debug("==============================\n"); + log_debug("%s\n", PROG_VERSION); + log_debug("==============================\n"); + log_debug("source object: %s\n", arguments->source_obj); log_debug("patched object: %s\n", arguments->patched_obj); log_debug("running binary: %s\n", arguments->running_elf); - log_debug("output object: %s\n", arguments->output_obj); + log_debug("output object: %s\n", arguments->output_obj); + log_debug("text offset: 0x%lx\n", arguments->text_offset); + log_debug("------------------------------\n\n"); } -static void compare_elf_headers(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) +static void compare_elf_headers(struct upatch_elf *uelf_source, + struct upatch_elf *uelf_patched) { GElf_Ehdr ehdr_source, ehdr_patched; - if (!gelf_getehdr(uelf_source->elf, &ehdr_source)) + if (!gelf_getehdr(uelf_source->elf, &ehdr_source)) { ERROR("gelf_getehdr source failed for %s.", elf_errmsg(0)); - - if (!gelf_getehdr(uelf_patched->elf, &ehdr_patched)) + } + if (!gelf_getehdr(uelf_patched->elf, &ehdr_patched)) { ERROR("gelf_getehdr patched failed for %s.", elf_errmsg(0)); - + } if (memcmp(ehdr_source.e_ident, ehdr_patched.e_ident, EI_NIDENT) || ehdr_source.e_type != ehdr_patched.e_type || ehdr_source.e_machine != ehdr_patched.e_machine || @@ -175,32 +193,22 @@ static void compare_elf_headers(struct upatch_elf *uelf_source, struct upatch_el ehdr_source.e_ehsize != ehdr_patched.e_ehsize || ehdr_source.e_phentsize != ehdr_patched.e_phentsize || ehdr_source.e_shentsize != ehdr_patched.e_shentsize) { - ERROR("compare_elf_headers failed."); - } -} - -/* we can sure we only handle relocatable file, this is unnecessary */ -static void check_program_headers(struct upatch_elf *uelf) -{ - size_t ph_nr; - if (elf_getphdrnum(uelf->elf, &ph_nr)) - ERROR("elf_getphdrnum with error %s.", elf_errmsg(0)); - - if (ph_nr != 0) - ERROR("ELF contains program header."); + ERROR("compare_elf_headers failed."); + } } static char *strarrcmp(char *name, char **prefix) { size_t len; - if (name == NULL) + if (name == NULL) { return NULL; - + } while (*prefix != NULL) { len = strlen(*prefix); - if (!strncmp(name, *prefix, len)) + if (!strncmp(name, *prefix, len)) { return name + len; + } prefix++; } @@ -229,27 +237,33 @@ static bool is_bundleable(struct symbol *sym) NULL, }; - if (sym == NULL || sym->sec == NULL) + if (sym == NULL || sym->sec == NULL) { return false; + } - if (sym->type == STT_FUNC) + if (sym->type == STT_FUNC) { name = strarrcmp(sym->sec->name, func_prefix); - else if (sym->type == STT_OBJECT) + } else if (sym->type == STT_OBJECT) { name = strarrcmp(sym->sec->name, obj_prefix); + } /* no prefix found or invalid type */ - if (name == NULL) + if (name == NULL) { return false; - - if (!strcmp(name, sym->name)) + } + if (!strcmp(name, sym->name)) { return true; + } /* special case for cold func */ text_name_len = strlen(".text.unlikely."); - if (sym->type == STT_FUNC && !strncmp(sym->sec->name, ".text.unlikely.", text_name_len) && + if (sym->type == STT_FUNC && + !strncmp(sym->sec->name, ".text.unlikely.", text_name_len) && strstr(sym->name, ".cold") && - !strncmp(sym->sec->name + text_name_len, sym->name, strlen(sym->sec->name) - text_name_len)) + !strncmp(sym->sec->name + text_name_len, sym->name, + strlen(sym->sec->name) - text_name_len)) { return true; + } return false; } @@ -266,14 +280,14 @@ static void bundle_symbols(struct upatch_elf *uelf) list_for_each_entry(sym, &uelf->symbols, list) { if (is_bundleable(sym)) { if (sym->sym.st_value != 0 && - is_gcc6_localentry_bundled_sym(uelf, sym)) { - ERROR("Symbol '%s' at offset %lu within section '%s', expected 0.", + is_gcc6_localentry_bundled_sym(uelf)) { + ERROR("Symbol '%s' at offset %lu of section '%s', expected 0.", sym->name, sym->sym.st_value, sym->sec->name); } - sym->sec->sym = sym; + sym->sec->bundle_sym = sym; /* except handler is also a kind of bundle symbol */ } else if (sym->type == STT_SECTION && is_except_section(sym->sec)) { - sym->sec->sym = sym; + sym->sec->bundle_sym = sym; } } } @@ -291,200 +305,29 @@ static void detect_child_functions(struct upatch_elf *uelf) char *pname; list_for_each_entry(sym, &uelf->symbols, list) { - if (sym->type != STT_FUNC) + if (sym->type != STT_FUNC) { continue; - + } childstr = strstr(sym->name, ".cold"); - if (!childstr) + if (!childstr) { childstr = strstr(sym->name, ".part"); - - if (!childstr) + } + if (!childstr) { continue; + } - pname = strndup(sym->name, childstr - sym->name); + pname = strndup(sym->name, (size_t)(childstr - sym->name)); log_debug("symbol '%s', pname: '%s'\n", sym->name, pname); - if (!pname) + if (!pname) { ERROR("detect_child_functions strndup failed."); + } sym->parent = find_symbol_by_name(&uelf->symbols, pname); - if (sym->parent) + if (sym->parent) { list_add_tail(&sym->subfunction_node, &sym->parent->children); - - free(pname); - } -} - -static bool discarded_sym(struct running_elf *relf, struct symbol *sym) -{ - if (!sym || !sym->sec || !sym->sec->name) - return false; - - /* - * ".gnu.warning." section is to prevent some symbols in the dynamic library being used by external programs. - * in the exec program, these sections are discarded in linker. so we discard these symbols. - */ - if (relf->is_exec && !strncmp(sym->sec->name, ".gnu.warning.", strlen(".gnu.warning."))) - return true; - - return false; -} - -enum LOCAL_MATCH { - FOUND, - NOT_FOUND, - EMPTY, -}; - -static enum LOCAL_MATCH locals_match( - struct upatch_elf *uelf, struct running_elf *relf, - struct symbol *file_sym, int file_sym_idx) -{ - struct symbol *uelf_sym = NULL; - struct debug_symbol *relf_sym = NULL; - enum LOCAL_MATCH found = EMPTY; - - for (int i = file_sym_idx + 1; i < relf->obj_nr; i++) { - relf_sym = &relf->obj_syms[i]; - - if (relf_sym->type == STT_FILE) { - break; // find until next file - } - if (relf_sym->bind != STB_LOCAL) { - continue; - } - if ((relf_sym->type != STT_FUNC) && - (relf_sym->type != STT_OBJECT)) { - continue; - } - - found = NOT_FOUND; - uelf_sym = file_sym; - list_for_each_entry_continue(uelf_sym, &uelf->symbols, list) { - if (uelf_sym->type == STT_FILE) { - break; // find until next file - } - if(uelf_sym->bind != STB_LOCAL) { - continue; - } - if ((uelf_sym->type == relf_sym->type) && - (strcmp(uelf_sym->name, relf_sym->name) == 0)) { - found = FOUND; - break; - } - } - - if (found == NOT_FOUND) { - log_warn("Cannot find symbol '%s' in %s\n", - relf_sym->name, g_relf_name); - return NOT_FOUND; - } - } - - uelf_sym = file_sym; - list_for_each_entry_continue(uelf_sym, &uelf->symbols, list) { - if (uelf_sym->type == STT_FILE) { - break; // find until next file - } - if(uelf_sym->bind != STB_LOCAL) { - continue; - } - if ((relf_sym->type != STT_FUNC) && - (relf_sym->type != STT_OBJECT)) { - continue; - } - if (discarded_sym(relf, uelf_sym)) { - continue; - } - - found = NOT_FOUND; - for (int i = file_sym_idx + 1; i < relf->obj_nr; i++) { - relf_sym = &relf->obj_syms[i]; - - if (relf_sym->type == STT_FILE) { - break; // find until next file - } - if (relf_sym->bind != STB_LOCAL) { - continue; - } - if ((uelf_sym->type == relf_sym->type) && - (strcmp(uelf_sym->name, relf_sym->name) == 0)) { - found = FOUND; - break; - } } - if (found == NOT_FOUND) { - log_warn("Cannot find symbol '%s' in %s\n", - uelf_sym->name, g_uelf_name); - return NOT_FOUND; - } - } - - return found; -} - -static void find_local_syms(struct upatch_elf *uelf, struct running_elf *relf, - struct symbol *file_sym) -{ - struct debug_symbol *relf_sym = NULL; - struct debug_symbol *found_sym = NULL; - enum LOCAL_MATCH found; - - for (int i = 0; i < relf->obj_nr; i++) { - relf_sym = &relf->obj_syms[i]; - - if (relf_sym->type != STT_FILE) { - continue; - } - if (strcmp(file_sym->name, relf_sym->name)) { - continue; - } - - found = locals_match(uelf, relf, file_sym, i); - if (found == NOT_FOUND) { - continue; - } - else if (found == EMPTY) { - found_sym = relf_sym; - break; - } - else { - if (found_sym) { - ERROR("Found duplicate local symbols in '%s'", g_relf_name); - } - found_sym = relf_sym; - } - } - - if (!found_sym) { - ERROR("Cannot find local symbol in '%s'", g_relf_name); - } - - list_for_each_entry_continue(file_sym, &uelf->symbols, list) { - if (file_sym->type == STT_FILE) { - break; - } - file_sym->relf_sym = found_sym; - } -} - -/* - * Because there can be duplicate symbols in elf, we need correlate each symbol from - * source elf to it's corresponding symbol in running elf. - * Both the source elf and the running elf can be split on STT_FILE - * symbols into blocks of symbols originating from a single source file. - * We then compare local symbol lists from both blocks and store the pointer - * to STT_FILE symbol in running elf for later using. - */ -static void find_debug_symbol(struct upatch_elf *uelf, struct running_elf *relf) -{ - struct symbol *file_sym = NULL; - - list_for_each_entry(file_sym, &uelf->symbols, list) { - if ((file_sym->type == STT_FILE) && (file_sym->status == CHANGED)) { - log_debug("file '%s' is CHANGED\n", file_sym->name); - find_local_syms(uelf, relf, file_sym); - } + free(pname); } } @@ -509,24 +352,26 @@ static void mark_file_symbols(struct upatch_elf *uelf) static void mark_grouped_sections(struct upatch_elf *uelf) { - struct section *groupsec, *sec; - unsigned int *data, *end; - + struct section *groupsec; list_for_each_entry(groupsec, &uelf->sections, list) { - if (groupsec->sh.sh_type != SHT_GROUP) + if (groupsec->sh.sh_type != SHT_GROUP) { continue; - data = groupsec->data->d_buf; - end = groupsec->data->d_buf + groupsec->data->d_size; - data++; /* skip first flag word (e.g. GRP_COMDAT) */ - while (data < end) { - sec = find_section_by_index(&uelf->sections, *data); - if (!sec) - ERROR("Group section not found"); - sec->grouped = 1; - log_debug("Marking section '%s' (%d) as grouped\n", - sec->name, sec->index); - data++; - } + } + + GElf_Word *data = groupsec->data->d_buf; + GElf_Word *end = groupsec->data->d_buf + groupsec->data->d_size; + data++; /* skip first flag word (e.g. GRP_COMDAT) */ + + while (data < end) { + struct section *sec = find_section_by_index(&uelf->sections, (GElf_Section)*data); + if (sec == NULL) { + ERROR("Cannot find group section, index=%d", *data); + } + sec->grouped = true; + log_debug("Marking grouped section, index: %d, name: '%s'\n", + sec->index, sec->name); + data++; + } } } @@ -545,26 +390,27 @@ static void replace_section_syms(struct upatch_elf *uelf) bool found = false; list_for_each_entry(relasec, &uelf->sections, list) { - if (!is_rela_section(relasec) || is_debug_section(relasec) || is_note_section(relasec)) + if (!is_rela_section(relasec) || + is_debug_section(relasec) || + is_note_section(relasec)) { continue; + } list_for_each_entry(rela, &relasec->relas, list) { - if (!rela->sym || !rela->sym->sec || rela->sym->type != STT_SECTION) + if (!rela->sym || !rela->sym->sec || + rela->sym->type != STT_SECTION) { continue; - - log_debug("Found replace symbol for section '%s' \n", rela->sym->name); - + } /* * for section symbol, rela->sym->sec is the section itself. - * rela->sym->sec->sym is the bundleable symbol which is a function or object. + * rela->sym->sec->sym is the bundleable symbol which is + * a function or object. */ - if (rela->sym->sec->sym) { - log_debug("Act: Replace it with '%s' <- '%s' \n", rela->sym->sec->sym->name, rela->sym->sec->name); - rela->sym = rela->sym->sec->sym; - - if (rela->sym->sym.st_value != 0) + if (rela->sym->sec->bundle_sym) { + rela->sym = rela->sym->sec->bundle_sym; + if (rela->sym->sym.st_value != 0) { ERROR("Symbol offset is not zero."); - + } continue; } @@ -572,31 +418,35 @@ static void replace_section_syms(struct upatch_elf *uelf) list_for_each_entry(sym, &uelf->symbols, list) { long start, end; - /* find object which belongs to this section, it could be .data .rodata etc */ - if (sym->type == STT_SECTION || sym->sec != rela->sym->sec) + /* + * find object which belongs to this section, + * it could be .data .rodata etc. + */ + if (sym->type == STT_SECTION || sym->sec != rela->sym->sec) { continue; + } - start = sym->sym.st_value; - end = sym->sym.st_value + sym->sym.st_size; + start = (long)sym->sym.st_value; + end = (long)(sym->sym.st_value + sym->sym.st_size); /* text section refer other sections */ if (is_text_section(relasec->base) && !is_text_section(sym->sec) && - (rela->type == R_X86_64_32S || rela->type == R_X86_64_32 || rela->type == R_AARCH64_ABS64) && + (rela->type == R_X86_64_32S || + rela->type == R_X86_64_32 || + rela->type == R_AARCH64_ABS64 || + rela->type == R_RISCV_64) && rela->addend == (long)sym->sec->sh.sh_size && - end == (long)sym->sec->sh.sh_size) + end == (long)sym->sec->sh.sh_size) { ERROR("Relocation refer end of data sections."); - else if (target_off == start && target_off == end){ - if(is_mapping_symbol(uelf, sym)) + } else if (target_off == start && target_off == end) { + if (is_mapping_symbol(uelf, sym)) { continue; - log_debug("Find relocation reference for empty symbol.\n"); - } - else if (target_off < start || target_off >= end) + } + } else if (target_off < start || target_off >= end) { continue; + } - log_debug("'%s': Replacing '%s+%ld' reference with '%s+%ld'\n", - relasec->name, rela->sym->name, rela->addend, - sym->name, rela->addend - start); found = true; rela->sym = sym; rela->addend -= start; @@ -638,7 +488,7 @@ static void replace_section_syms(struct upatch_elf *uelf) if (!found && !is_string_literal_section(rela->sym->sec) && strncmp(rela->sym->name, ".rodata", strlen(".rodata")) && strncmp(rela->sym->name, ".data", strlen(".data"))) { - ERROR("%s+0x%x: Cannot find replacement symbol for '%s+%ld' reference.", + ERROR("%s+0x%lx: Cannot find replacement symbol for '%s+%ld' reference.", relasec->base->name, rela->offset, rela->sym->name, rela->addend); } } @@ -647,24 +497,34 @@ static void replace_section_syms(struct upatch_elf *uelf) static void mark_ignored_sections(struct upatch_elf *uelf) { - /* Ignore any discarded sections */ - struct section *sec; + static const char *const IGNORED_SECTIONS[] = { + ".eh_frame", + ".note", + ".debug_", + ".comment", + ".discard", + ".rela.discard", + ".GCC.command.line", + }; + static const size_t IGNORED_SECTION_NUM = + sizeof(IGNORED_SECTIONS) / sizeof(IGNORED_SECTIONS[0]); + struct section *sec = NULL; list_for_each_entry(sec, &uelf->sections, list) { - if (!strncmp(sec->name, ".discard", strlen(".discard")) || - !strncmp(sec->name, ".rela.discard", strlen(".rela.discard"))) { - log_debug("Found discard section '%s'\n", sec->name); - sec->ignore = 1; + for (size_t i = 0; i < IGNORED_SECTION_NUM; i++) { + const char *const ignored_name = IGNORED_SECTIONS[i]; + const size_t name_len = strlen(ignored_name); + const char *sec_name = is_rela_section(sec) ? + sec->base->name : sec->name; + if (strncmp(sec_name, ignored_name, name_len) == 0) { + sec->ignored = true; + log_debug("Marking ignored section, index: %d, name: '%s'\n", + sec->index, sec->name); + break; } + } } - - /* TODO: handle ignore information from sections or settings */ } - -/* TODO: we do not handle it now */ -static void mark_ignored_functions_same(struct upatch_elf *uelf) {} -static void mark_ignored_sections_same(struct upatch_elf *uelf) {} - /* * For a local symbol referenced in the rela list of a changing function, * if it has no section, it will link error in arm. @@ -672,26 +532,30 @@ static void mark_ignored_sections_same(struct upatch_elf *uelf) {} * We use st_other to mark these symbols. */ static void include_special_local_section(struct upatch_elf *uelf) { - struct symbol *sym, *sym_changed; + struct symbol *sym; + struct symbol *sym_changed; struct rela *rela; list_for_each_entry(sym_changed, &uelf->symbols, list) { - if (!(sym_changed->status == CHANGED && sym_changed->type == STT_FUNC)) + if (!(sym_changed->status == CHANGED && sym_changed->type == STT_FUNC)) { continue; - - if (!sym_changed->sec || !sym_changed->sec->rela) + } + if (!sym_changed->sec || !sym_changed->sec->rela) { continue; + } list_for_each_entry(rela, &sym_changed->sec->rela->relas, list) { sym = rela->sym; - if (sym->sec && sym->status == SAME && sym->bind == STB_LOCAL && !sym->sec->include) { + if (sym->sec && sym->bind == STB_LOCAL && + sym->status == SAME && !sym->sec->include) { sym->sym.st_other |= SYM_OTHER; - sym->sec->include = 1; + sym->sec->include = true; sym->sec->data->d_buf = NULL; sym->sec->data->d_size = 0; // arm error: (.debug_info+0x...) undefined reference to `no symbol' - if (sym->sec->secsym) - sym->sec->secsym->include = 1; + if (sym->sec->sym) { + sym->sec->sym->include = true; + } } } } @@ -700,16 +564,22 @@ static void include_special_local_section(struct upatch_elf *uelf) { static void include_section(struct section *sec); static void include_symbol(struct symbol *sym) { - if (sym->include) + if ((sym == NULL) || sym->include) { return; - + } /* * The symbol gets included even if its section isn't needed, as it * might be needed: either permanently for a rela, or temporarily for * the later creation of a dynrela. */ - sym->include = 1; - + sym->include = true; + /* + * For special static symbols, we need include it's section + * to ensure we don't get link error. + */ + if (is_special_static_symbol(sym)) { + sym->sec->include = true; + } /* * For a function/object symbol, if it has a section, we only need to * include the section if it has changed. Otherwise the symbol will be @@ -718,174 +588,199 @@ static void include_symbol(struct symbol *sym) * For section symbols, we always include the section because * references to them can't otherwise be resolved externally. */ - if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME)) + if ((sym->status != SAME) || (sym->type == STT_SECTION)) { + include_section(sym->sec); + } +#ifdef __riscv + /* .L symbols not exist in EXE. If they are included, so are their sections. */ + else if (sym->sec && !sym->sec->include && !strncmp(sym->name, ".L", 2)) { include_section(sym->sec); + } +#endif } static void include_section(struct section *sec) { - struct rela *rela; - - if (sec->include) + if ((sec == NULL) || sec->include) { return; + } - sec->include = 1; - if (sec->secsym) - sec->secsym->include = 1; + sec->include = true; - if (!sec->rela) + if (is_rela_section(sec)) { + struct rela *rela = NULL; + list_for_each_entry(rela, &sec->relas, list) { + include_symbol(rela->sym); + } return; - - sec->rela->include = 1; - list_for_each_entry(rela, &sec->rela->relas, list) - include_symbol(rela->sym); + } else { + include_symbol(sec->sym); + include_section(sec->rela); + } } static void include_standard_elements(struct upatch_elf *uelf) { - struct section *sec; - struct symbol *sym; + struct section *sec = NULL; list_for_each_entry(sec, &uelf->sections, list) { - if (!strcmp(sec->name, ".shstrtab") || - !strcmp(sec->name, ".strtab") || - !strcmp(sec->name, ".symtab") || - !strcmp(sec->name, ".rodata") || - is_string_literal_section(sec)) + if (sec->ignored) { + continue; + } + if (is_symtab_section(sec) || is_strtab_section(sec)) { include_section(sec); + } } - list_for_each_entry(sym, &uelf->symbols, list) - if (sym->sec && is_string_literal_section(sym->sec)) - sym->include = 1; - /* include the NULL symbol */ - list_entry(uelf->symbols.next, struct symbol, list)->include = 1; + struct symbol *sym = find_symbol_by_index(&uelf->symbols, 0); + if (sym == NULL) { + ERROR("Cannot find null symbol"); + } + include_symbol(sym); } -static int include_changed_functions(struct upatch_elf *uelf) +static int include_changes(struct upatch_elf *uelf) { - struct symbol *sym; - int changed_nr = 0; + int count = 0; + struct symbol *sym = NULL; list_for_each_entry(sym, &uelf->symbols, list) { - if (sym->status == CHANGED && - sym->type == STT_FUNC) { - changed_nr++; - include_symbol(sym); + if ((sym->status == SAME) || is_symbol_ignored(sym)) { + continue; } - /* exception handler is a special function */ - if (sym->status == CHANGED && - sym->type == STT_SECTION && - sym->sec && is_except_section(sym->sec)) { - log_warn("Exception section '%s' is changed\n", sym->sec->name); - changed_nr++; + if ((sym->type == STT_OBJECT) || + (sym->type == STT_FUNC) || + (sym->type == STT_COMMON) || + (sym->type == STT_TLS) || + (sym->type == STT_GNU_IFUNC)) { include_symbol(sym); + count++; + } else if (sym->type == STT_SECTION) { + if ((sym->sec != NULL) && is_rela_section(sym->sec)) { + continue; + } + include_symbol(sym); + count++; } - - if (sym->type == STT_FILE) - sym->include = 1; } - return changed_nr; + return count; } -static int include_new_globals(struct upatch_elf *uelf) +static int verify_symbol_patchability(struct upatch_elf *uelf) { - struct symbol *sym; - int nr = 0; + int err_count = 0; + struct symbol *sym = NULL; list_for_each_entry(sym, &uelf->symbols, list) { - if (sym->bind == STB_GLOBAL && sym->sec && - sym->status == NEW) { - include_symbol(sym); - nr++; + if (!sym->include) { + continue; + } + if ((sym->bind == STB_LOCAL) && (sym->sym.st_shndx == SHN_UNDEF) && + (sym->index != 0)) { + log_warn("Symbol '%s' is local, but sh_shndx is SHN_UNDEF\n", + sym->name); + err_count++; + } + if (sym->type == STT_GNU_IFUNC) { + log_warn("Symbol '%s' is included, but IFUNC is not supported\n", + sym->name); + err_count++; } } - return nr; + return err_count; } -static void include_debug_sections(struct upatch_elf *uelf) +static int verify_section_patchability(struct upatch_elf *uelf) { - struct rela *rela, *saferela; - struct section *sec = NULL, *eh_sec = NULL; - - /* include all .debug_* sections */ - list_for_each_entry(sec, &uelf->sections, list) { - if (is_debug_section(sec)) { - sec->include = 1; - - if (!is_rela_section(sec) && sec->secsym) - sec->secsym->include = 1; + int err_count = 0; - if (!is_rela_section(sec) && is_eh_frame(sec)) - eh_sec = sec; - } - } - - /* - * modify relocation entry here - * remove unincluded symbol in debug relocation section - * for eh_frame section, sync the FDE at the same time - */ + struct section *sec = NULL; list_for_each_entry(sec, &uelf->sections, list) { - if (!is_rela_section(sec) || !is_debug_section(sec)) + if (sec->ignored) { continue; - - list_for_each_entry_safe(rela, saferela, &sec->relas, list) - // The shndex of symbol is SHN_COMMON, there is no related section - if (rela->sym && !rela->sym->include) - list_del(&rela->list); + } + if ((sec->status == NEW) && !sec->include) { + // new sections should be included + log_warn("Section '%s' is %s, but it is not included\n", + sec->name, status_str(sec->status)); + err_count++; + } else if ((sec->status == CHANGED) && !sec->include) { + // changed sections should be included + if (is_rela_section(sec)) { + continue; + } + log_warn("Section '%s' is %s, but it is not included\n", + sec->name, status_str(sec->status)); + err_count++; + } else if ((sec->status == CHANGED) && sec->include) { + // changed group section cannot be included + if (is_group_section(sec) || sec->grouped) { + log_warn("Section '%s' is %s, but it is not supported\n", + sec->name, status_str(sec->status)); + err_count++; + } + // changed .data & .bss section cannot be included + if (is_data_section(sec) || is_bss_section(sec)) { + struct rela *rela = NULL; + list_for_each_entry(rela, &sec->rela->relas, list) { + if ((rela->sym == NULL) || (rela->sym->status != CHANGED)) { + continue; + } + if (is_read_only_section(rela->sym->sec) || + is_string_literal_section(rela->sym->sec)) { + continue; + } + log_warn("Section '%s' is %s, but it is not supported\n", + sec->name, status_str(sec->status)); + err_count++; + } + } + } } - if (eh_sec) - upatch_rebuild_eh_frame(eh_sec); + return err_count; } -/* currently, there si no special section need to be handled */ -static void process_special_sections(struct upatch_elf *uelf) {} - static void verify_patchability(struct upatch_elf *uelf) { - struct section *sec; - int errs = 0; - - list_for_each_entry(sec, &uelf->sections, list) { - if (sec->status == CHANGED && !sec->include) { - log_normal("Section '%s' is changed, but it is not selected for inclusion\n", sec->name); - errs++; - } - - if (sec->status != SAME && sec->grouped) { - log_normal("Section '%s' is changed, but it is a part of a section group\n", sec->name); - errs++; - } + int err_count = 0; - if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) { - log_normal("Section '%s' is new, but type 'SHT_GROUP' is not supported\n", sec->name); - errs++; - } - - if (sec->include && sec->status != NEW && - (!strncmp(sec->name, ".data", 5) || !strncmp(sec->name, ".bss", 4)) && - (strcmp(sec->name, ".data.unlikely") && strcmp(sec->name, ".data.once"))) { - log_normal("Data section '%s' is selected for inclusion\n", sec->name); - errs++; - } + err_count += verify_symbol_patchability(uelf); + err_count += verify_section_patchability(uelf); + if (err_count != 0) { + ERROR("Found %d unexpected changes", err_count); } +} - if (errs) - DIFF_FATAL("%d, Unsupported section changes", errs); +/* + * These types are for linker optimization and memory layout. + * They have no associated symbols and their names are empty + * string which would mismatch running-elf symbols in later + * lookup_relf(). Drop these useless items now. + */ +static void rv_drop_useless_rela(struct section *relasec) +{ + struct rela *rela, *saferela; + list_for_each_entry_safe(rela, saferela, &relasec->relas, list) + if (rela->type == R_RISCV_RELAX || rela->type == R_RISCV_ALIGN) { + list_del(&rela->list); + memset(rela, 0, sizeof(*rela)); + free(rela); + } } -static void migrate_included_elements(struct upatch_elf *uelf_patched, struct upatch_elf *uelf_out) +static void migrate_included_elements(struct upatch_elf *uelf_patched, + struct upatch_elf *uelf_out) { - struct section *sec, *safesec; - struct symbol *sym, *safesym; + struct section *sec; + struct section *safesec; + struct symbol *sym; + struct symbol *safesym; - memset(uelf_out, 0, sizeof(struct upatch_elf)); uelf_out->arch = uelf_patched->arch; INIT_LIST_HEAD(&uelf_out->sections); @@ -894,62 +789,81 @@ static void migrate_included_elements(struct upatch_elf *uelf_patched, struct up /* migrate included sections from uelf_patched to uelf_out */ list_for_each_entry_safe(sec, safesec, &uelf_patched->sections, list) { - if (!sec->include) + if (!sec->include) { continue; - + } list_del(&sec->list); list_add_tail(&sec->list, &uelf_out->sections); sec->index = 0; - if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include) - sec->secsym = NULL; // break link to non-included section symbol + + if (!is_rela_section(sec)) { + if (sec->sym && !sec->sym->include) { + sec->sym = NULL; // break link to non-included section symbol + } + } else if (uelf_patched->arch == RISCV64) { + rv_drop_useless_rela(sec); + } } /* migrate included symbols from kelf to out */ list_for_each_entry_safe(sym, safesym, &uelf_patched->symbols, list) { - if (!sym->include) + if (!sym->include) { continue; + } list_del(&sym->list); list_add_tail(&sym->list, &uelf_out->symbols); sym->index = 0; - sym->strip = SYMBOL_DEFAULT; - if (sym->sec && !sym->sec->include) + sym->strip = false; + + if (sym->sec && !sym->sec->include) { sym->sec = NULL; // break link to non-included section + } } } -int main(int argc, char*argv[]) +/* + * Key point for upatch-diff: + * 1. find changed func/data for each object + * 2. link all these objects into a relocatable file + * 3. add sections for management (hash/init/patch info etc.) + * 4. locate old symbols for the relocatable file + */ +int main(int argc, char **argv) { - struct arguments arguments; - struct upatch_elf uelf_source, uelf_patched, uelf_out; - struct running_elf relf; - - int num_changed, new_globals_exist; - - memset(&arguments, 0, sizeof(arguments)); - argp_parse(&argp, argc, argv, 0, NULL, &arguments); - - if (arguments.debug) + static const struct argp ARGP = { + ARGP_OPTION, parse_opt, NULL, ARGP_DOC, NULL, NULL, NULL + }; + struct arguments args = { 0 }; + struct upatch_elf uelf_source = { 0 }; + struct upatch_elf uelf_patched = { 0 }; + struct upatch_elf uelf_out = { 0 }; + struct running_elf relf = { 0 }; + + if (argp_parse(&ARGP, argc, argv, 0, NULL, &args) != 0) { + return EXIT_FAILURE; + } + if (!check_args(&args)) { + return EXIT_FAILURE; + } + if (args.debug) { g_loglevel = DEBUG; - g_logprefix = basename(arguments.source_obj); - show_program_info(&arguments); - - if (elf_version(EV_CURRENT) == EV_NONE) - ERROR("ELF library initialization failed"); - - /* TODO: with debug info, this may changed */ - g_uelf_name = arguments.source_obj; - g_relf_name = arguments.running_elf; + } + show_program_info(&args); - /* check error in log, since errno may be from libelf */ - upatch_elf_open(&uelf_source, arguments.source_obj); - upatch_elf_open(&uelf_patched, arguments.patched_obj); + if (elf_version(EV_CURRENT) == EV_NONE) { + log_error("Failed to initialize elf library\n"); + return EXIT_FAILURE; + } - relf_init(arguments.running_elf, &relf); + uelf_open(&uelf_source, args.source_obj); + uelf_open(&uelf_patched, args.patched_obj); + relf_open(&relf, args.running_elf); + g_logprefix = basename(args.source_obj); + g_uelf_name = args.source_obj; + g_relf_name = args.running_elf; compare_elf_headers(&uelf_source, &uelf_patched); - check_program_headers(&uelf_source); - check_program_headers(&uelf_patched); bundle_symbols(&uelf_source); bundle_symbols(&uelf_patched); @@ -957,6 +871,8 @@ int main(int argc, char*argv[]) detect_child_functions(&uelf_source); detect_child_functions(&uelf_patched); + mark_ignored_sections(&uelf_source); + mark_ignored_sections(&uelf_patched); mark_grouped_sections(&uelf_patched); replace_section_syms(&uelf_source); @@ -964,54 +880,33 @@ int main(int argc, char*argv[]) upatch_correlate_elf(&uelf_source, &uelf_patched); upatch_correlate_static_local_variables(&uelf_source, &uelf_patched); - - /* Now, we can only check uelf_patched, all we need is in the twin part */ - /* Also, we choose part of uelf_patched and output new object */ - mark_ignored_sections(&uelf_patched); + upatch_print_correlation(&uelf_patched); upatch_compare_correlated_elements(&uelf_patched); mark_file_symbols(&uelf_source); - find_debug_symbol(&uelf_source, &relf); - - mark_ignored_functions_same(&uelf_patched); - mark_ignored_sections_same(&uelf_patched); - - upatch_elf_teardown(&uelf_source); - upatch_elf_free(&uelf_source); include_standard_elements(&uelf_patched); - - num_changed = include_changed_functions(&uelf_patched); - new_globals_exist = include_new_globals(&uelf_patched); - if (!num_changed && !new_globals_exist) { + int change_count = include_changes(&uelf_patched); + if (change_count == 0) { log_normal("No functional changes\n"); + uelf_close(&uelf_source); + uelf_close(&uelf_patched); + relf_close(&relf); return 0; } - - include_debug_sections(&uelf_patched); - - process_special_sections(&uelf_patched); - upatch_print_changes(&uelf_patched); - upatch_dump_kelf(&uelf_patched); - verify_patchability(&uelf_patched); include_special_local_section(&uelf_patched); migrate_included_elements(&uelf_patched, &uelf_out); - /* since out elf still point to it, we only destroy it, not free it */ - upatch_elf_teardown(&uelf_patched); - upatch_create_strings_elements(&uelf_out); - upatch_create_patches_sections(&uelf_out, &relf); + upatch_create_patches_sections(&uelf_out, &relf, args.text_offset); - upatch_create_intermediate_sections(&uelf_out, &relf); - - create_kpatch_arch_section(&uelf_out); + create_kpatch_arch_section(); upatch_build_strings_section_data(&uelf_out); @@ -1029,8 +924,6 @@ int main(int argc, char*argv[]) upatch_rebuild_relocations(&uelf_out); - upatch_check_relocations(&uelf_out); - upatch_create_shstrtab(&uelf_out); upatch_create_strtab(&uelf_out); @@ -1039,15 +932,15 @@ int main(int argc, char*argv[]) upatch_create_symtab(&uelf_out); - upatch_dump_kelf(&uelf_out); - - upatch_write_output_elf(&uelf_out, uelf_patched.elf, arguments.output_obj, 0664); + upatch_write_output_elf(&uelf_out, uelf_patched.elf, args.output_obj, 0664); + log_normal("Done\n"); - relf_destroy(&relf); - upatch_elf_free(&uelf_patched); - upatch_elf_teardown(&uelf_out); - upatch_elf_free(&uelf_out); + uelf_close(&uelf_out); + uelf_close(&uelf_patched); + uelf_close(&uelf_source); + relf_close(&relf); - log_normal("Done\n"); - return 0; + fflush(stdout); + fflush(stderr); + return EXIT_SUCCESS; } diff --git a/upatch-diff/elf-common.c b/upatch-diff/elf-common.c index f895e9bdf11fcf5c96f49f52255529e53762a9c7..22d12ca96551ba0179dc3b803b23d013b9a0dd57 100644 --- a/upatch-diff/elf-common.c +++ b/upatch-diff/elf-common.c @@ -26,98 +26,224 @@ #include "elf-common.h" +#ifdef __riscv +/* + * .L local symbols are named as ".L" + "class prefix" + "number". + * The numbers are volatile due to code change. + * Compare class prefix(composed of letters) only. + */ +static int mangled_strcmp_dot_L(char *str1, char *str2) +{ + if (!*str2 || strncmp(str2, ".L", 2)) { + return 1; + } + + /* RISCV_FAKE_LABEL_NAME matched exactly */ + if (!strcmp(str1, ".L0 ") || !strcmp(str2, ".L0 ")) { + return strcmp(str1, str2); + } + + char *p = str1 + 2; + char *q = str2 + 2; + while (*p < '0' || *p > '9') p++; + while (*q < '0' || *q > '9') q++; + if ((p - str1 != q - str2) || strncmp(str1, str2, (size_t)(p - str1))) { + return 1; + } + + return 0; +} +#endif + +static bool is_dynamic_debug_symbol(struct symbol *sym) +{ + static const char *SEC_NAMES[] = { + "__verbose", + "__dyndbg", + NULL, + }; + + if (sym->sec == NULL) { + return false; + } + if ((sym->type == STT_OBJECT) || (sym->type == STT_SECTION)) { + const char **sec_name; + for (sec_name = SEC_NAMES; *sec_name; sec_name++) { + if (strcmp(sym->sec->name, *sec_name) == 0) { + return true; + } + } + } + + return false; +} + +bool is_special_static_symbol(struct symbol *sym) +{ + static const char *SYM_NAMES[] = { + ".__key", + ".__warned", + ".__already_done.", + ".__func__", + ".__FUNCTION__", + ".__PRETTY_FUNCTION__", + "._rs", + ".CSWTCH", + "._entry", + ".C", + ".L", + NULL, + }; + + if (sym == NULL) { + return false; + } + + /* pr_debug() uses static local variables in __verbose or __dyndbg section */ + if (is_dynamic_debug_symbol(sym)) { + return true; + } + + if (sym->type == STT_SECTION) { + /* make sure section is bundled */ + if (is_rela_section(sym->sec) || (sym->sec->bundle_sym == NULL)) { + return false; + } + /* use bundled object object/function symbol for matching */ + sym = sym->sec->bundle_sym; + } + + if ((sym->type != STT_OBJECT) || (sym->bind != STB_LOCAL)) { + return false; + } + if (!strcmp(sym->sec->name, ".data.once")) { + return true; + } + + const char **sym_name; + for (sym_name = SYM_NAMES; *sym_name; sym_name++) { + /* Check gcc-style statics: '.' */ + if (strcmp(sym->name, (*sym_name + 1)) == 0) { + return true; + } + /* Check clang-style statics: '.' */ + if (strstr(sym->name, *sym_name)) { + return true; + } + } + + return false; +} + +bool is_special_static_section(struct section *sec) +{ + struct symbol *sym = is_rela_section(sec) ? + sec->base->sym : sec->sym; + return is_special_static_symbol(sym); +} + int mangled_strcmp(char *str1, char *str2) { /* * ELF string sections aren't mangled, though they look that way. */ - if (strstr(str1, ".str1.")) - return strcmp(str1, str2); + if (strstr(str1, ".str1.")) { + return strcmp(str1, str2); + } - while (*str1 == *str2) { - if (!*str2) - return 0; +#ifdef __riscv + if (!strncmp(str1, ".L", 2)) { + return mangled_strcmp_dot_L(str1, str2); + } +#endif + while (*str1 == *str2) { + if (!*str2) { + return 0; + } // format like ".[0-9]" - if (*str1 == '.' && isdigit(str1[1])) { - if (!isdigit(str2[1])) - return 1; - while (isdigit(*++str1)) - ; - while (isdigit(*++str2)) - ; - } else { - str1++; - str2++; - } - } - - if ((!*str1 && has_digit_tail(str2)) || - (!*str2 && has_digit_tail(str1))) - return 0; - - return 1; + if (*str1 == '.' && isdigit(str1[1])) { + if (!isdigit(str2[1])) { + return 1; + } + while (isdigit(*++str1)) { + // empty loop body + } + while (isdigit(*++str2)) { + // empty loop body + } + } else { + str1++; + str2++; + } + } + + if ((!*str1 && has_digit_tail(str2)) || + (!*str2 && has_digit_tail(str1))) { + return 0; + } + + return 1; } bool is_normal_static_local(struct symbol *sym) { // only handle local variable - if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) - return false; - - // TODO: .Local ? need a example here - if (!strncmp(sym->name, ".L", 2)) { - ERROR("find no-local variable \n"); - return false; + if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL) { + return false; } - - if (!strchr(sym->name, '.')) - return false; - - if (is_special_static(sym)) - return false; - - return true; + if (!strchr(sym->name, '.')) { + return false; + } + if (is_special_static_symbol(sym)) { + return false; + } + return true; } int offset_of_string(struct list_head *list, char *name) { - struct string *string; - int index = 0; - - list_for_each_entry(string, list, list) { - if (!strcmp(string->name, name)) - return index; - index += (int)strlen(string->name) + 1; - } - - ALLOC_LINK(string, list); - string->name = name; - return index; + struct string *string; + int index = 0; + + list_for_each_entry(string, list, list) { + if (!strcmp(string->name, name)) { + return index; + } + index += (int)strlen(string->name) + 1; + } + + ALLOC_LINK(string, list); + string->name = name; + return index; } // no need for X86 -bool is_gcc6_localentry_bundled_sym(struct upatch_elf *uelf, struct symbol *sym) +bool is_gcc6_localentry_bundled_sym(struct upatch_elf *uelf) { - switch(uelf->arch) { - case AARCH64: - return false; - case X86_64: - return false; - default: - ERROR("unsupported arch"); - } - return false; + switch (uelf->arch) { + case AARCH64: + return false; + case X86_64: + return false; + case RISCV64: + return false; + default: + ERROR("unsupported arch"); + } + return false; } bool is_mapping_symbol(struct upatch_elf *uelf, struct symbol *sym) { - if (uelf->arch != AARCH64) - return false; - - if (sym->name && sym->name[0] == '$' - && sym->type == STT_NOTYPE - && sym->bind == STB_LOCAL) - return true; - return false; + if ((uelf->arch != AARCH64) && (uelf->arch != RISCV64)) { + return false; + } + if (sym->name && sym->name[0] == '$' && + sym->type == STT_NOTYPE && + sym->bind == STB_LOCAL) { + return true; + } + + return false; } diff --git a/upatch-diff/elf-common.h b/upatch-diff/elf-common.h index 9e44a7cc6c450c72e71ec9282e38c035cfd13435..f252cb95875d7cb0f0ee11122c064d745423c923 100644 --- a/upatch-diff/elf-common.h +++ b/upatch-diff/elf-common.h @@ -35,136 +35,206 @@ #include "list.h" #include "log.h" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + #define ALLOC_LINK(_new, _list) \ -{ \ - (_new) = calloc(1, sizeof(*(_new))); \ - if (!(_new)) \ - ERROR("calloc"); \ - INIT_LIST_HEAD(&(_new)->list); \ - if (_list) \ - list_add_tail(&(_new)->list, (_list)); \ + do { \ + (_new) = calloc(1, sizeof(*(_new))); \ + if (!(_new)) { \ + ERROR("calloc"); \ + } \ + INIT_LIST_HEAD(&(_new)->list); \ + if (_list) { \ + list_add_tail(&(_new)->list, (_list)); \ + } \ + } while (0) + +static inline bool is_text_section(struct section *sec) +{ + return (sec != NULL) && + (sec->sh.sh_type == SHT_PROGBITS) && + (sec->sh.sh_flags & SHF_EXECINSTR); +} + +static inline bool is_data_section(struct section *sec) +{ + return (sec != NULL) && + (sec->sh.sh_type == SHT_PROGBITS) && + (sec->sh.sh_flags & SHF_WRITE); +} + +static inline bool is_rodata_section(struct section *sec) +{ + return (sec != NULL) && + (sec->sh.sh_type == SHT_PROGBITS) && + ((sec->sh.sh_flags & SHF_WRITE) == 0); +} + +static inline bool is_symtab_section(struct section *sec) +{ + return (sec != NULL) && (sec->sh.sh_type == SHT_SYMTAB); +} + +static inline bool is_strtab_section(struct section *sec) +{ + return (sec != NULL) && (sec->sh.sh_type == SHT_STRTAB); } static inline bool is_rela_section(struct section *sec) { - /* - * An architecture usually only accepts one type. - * And, X86_64 only uses RELA - */ - return (sec->sh.sh_type == SHT_RELA); + return (sec != NULL) && (sec->sh.sh_type == SHT_RELA); } +static inline bool is_note_section(struct section *sec) +{ + if (is_rela_section(sec)) { + sec = sec->base; + } + return (sec->sh.sh_type == SHT_NOTE); +} -static inline bool is_text_section(struct section *sec) +static inline bool is_bss_section(struct section *sec) { - return (sec->sh.sh_type == SHT_PROGBITS && - (sec->sh.sh_flags & SHF_EXECINSTR)); + return (sec != NULL) && (sec->sh.sh_type == SHT_NOBITS); +} + +static inline bool is_group_section(struct section *sec) +{ + return (sec != NULL) && (sec->sh.sh_type == SHT_GROUP); +} + +static inline bool is_read_only_section(struct section *sec) +{ + return (sec != NULL) && ((sec->sh.sh_flags & SHF_WRITE) == 0); } static inline bool is_string_section(struct section *sec) { - return sec->sh.sh_flags & SHF_STRINGS; + return (sec != NULL) && (sec->sh.sh_flags & SHF_STRINGS); } -/* used for c++ exception handle */ -static inline bool is_except_section(struct section *sec) +static inline bool is_string_literal_section(struct section *sec) { - return !strncmp(sec->name, ".gcc_except_table", 17); + return (sec != NULL) && + (sec->sh.sh_flags & SHF_STRINGS) && + ((sec->sh.sh_flags & SHF_WRITE) == 0); } -static inline bool is_note_section(struct section *sec) +/* used for c++ exception handle */ +static inline bool is_except_section(struct section *sec) { - if (is_rela_section(sec)) - sec = sec->base; - return sec->sh.sh_type == SHT_NOTE; + return !strncmp(sec->name, ".gcc_except_table", 17); } static inline bool is_eh_frame(struct section *sec) { - char *name; - if (is_rela_section(sec)) - name = sec->base->name; - else - name = sec->name; - - return !strncmp(name, ".eh_frame", 9); + char *name; + if (is_rela_section(sec)) { + name = sec->base->name; + } else { + name = sec->name; + } + return !strncmp(name, ".eh_frame", 9); } static inline bool is_debug_section(struct section *sec) { - char *name; - if (is_rela_section(sec)) - name = sec->base->name; - else - name = sec->name; - - /* .eh_frame_hdr will be generated by the linker */ - return !strncmp(name, ".debug_", 7) || - !strncmp(name, ".eh_frame", 9); + char *name; + if (is_rela_section(sec)) { + name = sec->base->name; + } else { + name = sec->name; + } + /* .eh_frame_hdr will be generated by the linker */ + return !strncmp(name, ".debug_", 7) || + !strncmp(name, ".eh_frame", 9); +} + +static inline bool is_symbol_ignored(struct symbol *sym) +{ + return (sym->sec != NULL) && sym->sec->ignored; } -static inline struct symbol *find_symbol_by_index(struct list_head *list, size_t index) +static inline struct symbol *find_symbol_by_index(struct list_head *list, GElf_Word index) { - struct symbol *sym; + struct symbol *sym; - list_for_each_entry(sym, list, list) - if (sym->index == index) - return sym; + list_for_each_entry(sym, list, list) { + if (sym->index == index) { + return sym; + } + } - return NULL; + return NULL; } static inline struct symbol *find_symbol_by_name(struct list_head *list, const char *name) { - struct symbol *sym; + struct symbol *sym; - list_for_each_entry(sym, list, list) - if (sym->name && !strcmp(sym->name, name)) - return sym; + list_for_each_entry(sym, list, list) { + if (sym->name && !strcmp(sym->name, name)) { + return sym; + } + } - return NULL; + return NULL; } -static inline struct section *find_section_by_index(struct list_head *list, unsigned int index) +static inline struct section *find_section_by_index(struct list_head *list, GElf_Section index) { - struct section *sec; + struct section *sec; - list_for_each_entry(sec, list, list) - if (sec->index == index) - return sec; + list_for_each_entry(sec, list, list) { + if (sec->index == index) { + return sec; + } + } - return NULL; + return NULL; } static inline struct section *find_section_by_name(struct list_head *list, const char *name) { - struct section *sec; + struct section *sec; - list_for_each_entry(sec, list, list) - if (!strcmp(sec->name, name)) - return sec; + list_for_each_entry(sec, list, list) { + if (!strcmp(sec->name, name)) { + return sec; + } + } - return NULL; + return NULL; } -// section like .rodata.str1. and .rodata.__func__. -static inline bool is_string_literal_section(struct section *sec) +static inline struct section *find_section_by_type(struct list_head *list, GElf_Word type) { - return !strncmp(sec->name, ".rodata.", 8) && (strstr(sec->name, ".str") || strstr(sec->name, "__func__")); + struct section *sec; + + list_for_each_entry(sec, list, list) { + if (sec->sh.sh_type == type) { + return sec; + } + } + + return NULL; } static inline bool has_digit_tail(char *tail) { - if (*tail != '.') - return false; + if (*tail != '.') { + return false; + } - while (isdigit(*++tail)) - ; + while (isdigit(*++tail)) { + // empty loop body + } - if (!*tail) - return true; + if (!*tail) { + return true; + } - return false; + return false; } /* @@ -174,76 +244,76 @@ static inline bool has_digit_tail(char *tail) */ int mangled_strcmp(char *, char *); - -/* - * TODO: Special static local variables should never be correlated and should always - * be included if they are referenced by an included function. - */ -static inline bool is_special_static(struct symbol *sym){ - /* Not need it now. */ - return false; -} - bool is_normal_static_local(struct symbol *); static inline char *section_function_name(struct section *sec) { - if (is_rela_section(sec)) - sec = sec->base; - return sec->sym ? sec->sym->name : sec->name; + if (is_rela_section(sec)) { + sec = sec->base; + } + return sec->bundle_sym ? sec->bundle_sym->name : sec->name; } static inline char *status_str(enum status status) { - switch(status) { - case NEW: - return "NEW"; - case CHANGED: - return "CHANGED"; - case SAME: - return "SAME"; - default: - ERROR("status_str"); - } - return NULL; + switch (status) { + case NEW: + return "NEW"; + case CHANGED: + return "CHANGED"; + case SAME: + return "SAME"; + default: + ERROR("status_str"); + } + return NULL; } int offset_of_string(struct list_head *, char *); static inline unsigned int absolute_rela_type(struct upatch_elf *uelf) { - switch(uelf->arch) { - case AARCH64: - return R_AARCH64_ABS64; - case X86_64: - return R_X86_64_64; - default: - ERROR("unsupported arch"); - } - return 0; + switch (uelf->arch) { + case AARCH64: + return R_AARCH64_ABS64; + case X86_64: + return R_X86_64_64; + case RISCV64: + return R_RISCV_64; + default: + ERROR("unsupported arch"); + } + return 0; } static inline bool is_null_sym(struct symbol *sym) { - return !strlen(sym->name); + return (sym->type == STT_NOTYPE) && + (sym->bind == STB_LOCAL) && + (strlen(sym->name) == 0); } static inline bool is_file_sym(struct symbol *sym) { - return sym->type == STT_FILE; + return sym->type == STT_FILE; } static inline bool is_local_func_sym(struct symbol *sym) { - return sym->bind == STB_LOCAL && sym->type == STT_FUNC; + return sym->bind == STB_LOCAL && sym->type == STT_FUNC; +} + +static inline bool is_subfunction_sym(struct symbol *sym) +{ + return sym->parent != NULL; } static inline bool is_local_sym(struct symbol *sym) { - return sym->bind == STB_LOCAL; + return sym->bind == STB_LOCAL; } -bool is_gcc6_localentry_bundled_sym(struct upatch_elf *, struct symbol *); +bool is_gcc6_localentry_bundled_sym(struct upatch_elf *); /* * Mapping symbols are used to mark and label the transitions between code and @@ -253,4 +323,13 @@ bool is_gcc6_localentry_bundled_sym(struct upatch_elf *, struct symbol *); */ bool is_mapping_symbol(struct upatch_elf *, struct symbol *); -#endif \ No newline at end of file +/* + * This function detects whether the given symbol is a "special" static local + * variable (for lack of a better term). + * Special static local variables shoe never be correlated and should always + * be included if they are referenced by an included function. + */ +bool is_special_static_symbol(struct symbol *sym); +bool is_special_static_section(struct section *sec); + +#endif diff --git a/upatch-diff/elf-compare.c b/upatch-diff/elf-compare.c index ef8dd236d0f6a229022a68102e4fa9bb491d3a69..eb8bdc163f0279046aba5f8c9dd0e95b45594b25 100644 --- a/upatch-diff/elf-compare.c +++ b/upatch-diff/elf-compare.c @@ -29,352 +29,200 @@ #include "elf-compare.h" #include "elf-insn.h" -static void compare_correlated_symbol(struct symbol *sym, struct symbol *symtwin) -{ - // compare bind and type info - if (sym->sym.st_info != symtwin->sym.st_info || - (sym->sec && !symtwin->sec) || - (symtwin->sec && !sym->sec)) - DIFF_FATAL("symbol info mismatch: %s", sym->name); - - // check if correlated symbols have correlated sections - if (sym->sec && symtwin->sec && sym->sec->twin != symtwin->sec) - DIFF_FATAL("symbol changed sections: %s", sym->name); - - // data object can't change size - if (sym->type == STT_OBJECT && sym->sym.st_size != symtwin->sym.st_size) - DIFF_FATAL("object size mismatch: %s", sym->name); +static int compare_correlated_section(struct section *sec, struct section *twin); - if (sym->sym.st_shndx == SHN_UNDEF || - sym->sym.st_shndx == SHN_ABS) - sym->status = SAME; +static void compare_correlated_symbol(struct symbol *sym, struct symbol *twin) +{ + // symbol type & binding cannot be changed + if (sym->type != twin->type) { + ERROR("Symbol '%s' type mismatched", sym->name); + } + if (sym->sym.st_info != twin->sym.st_info) { + ERROR("Symbol '%s' st_info mismatched", sym->name); + } /* * For local symbols, we handle them based on their matching sections. */ + if ((sym->sym.st_shndx == SHN_UNDEF) || + (sym->sym.st_shndx == SHN_ABS) || + (sym->sym.st_shndx == SHN_COMMON)) { + sym->status = SAME; + return; + } + + if ((sym->sec == NULL) || (sym->sym.st_shndx == SHN_ABS)) { + ERROR("Symbol '%s' don't have section\n", sym->name); + } + + if (sym->sec->twin != twin->sec) { + ERROR("Symbol '%s' section mismatched", sym->name); + } + + compare_correlated_section(sym->sec, twin->sec); + if (sym->sec->status == CHANGED) { + sym->status = CHANGED; + } else if (!is_rela_section(sym->sec) && + (sym->sec->rela != NULL) && + (sym->sec->rela->status == CHANGED)) { + sym->status = CHANGED; + } else { + sym->status = SAME; + } } void upatch_compare_symbols(struct upatch_elf *uelf) { - struct symbol *sym; - - list_for_each_entry(sym, &uelf->symbols, list) { - if (sym->twin) - compare_correlated_symbol(sym, sym->twin); - else - sym->status = NEW; + struct symbol *sym; - log_debug("symbol %s is %s\n", sym->name, status_str(sym->status)); - } + list_for_each_entry(sym, &uelf->symbols, list) { + if (is_symbol_ignored(sym)) { + continue; + } + if (is_special_static_symbol(sym)) { + sym->status = SAME; + } else if (sym->twin == NULL) { + sym->status = NEW; + } else { + compare_correlated_symbol(sym, sym->twin); + } + } } static bool rela_equal(struct rela *rela1, struct rela *rela2) { - if (rela1->type != rela2->type || - rela1->offset != rela2->offset) + if (rela1->type != rela2->type || rela1->offset != rela2->offset) { return false; - + } /* TODO: handle altinstr_aux */ - /* TODO: handle rela for toc section */ - - if (rela1->string) - return rela2->string && !strcmp(rela1->string, rela2->string); - - if (rela1->addend != rela2->addend) { - log_debug("relocation addend has changed from %ld to %ld", rela1->addend, rela2->addend); - return false; + if (rela1->string) { + return rela2->string && !strcmp(rela1->string, rela2->string); + } + if (rela1->addend != rela2->addend) { + log_debug("relocation addend has changed from %ld to %ld", + rela1->addend, rela2->addend); + return false; } return !mangled_strcmp(rela1->sym->name, rela2->sym->name); } -static void compare_correlated_rela_section(struct section *relasec, struct section *relasec_twin) +static void compare_correlated_rela_section(struct section *relasec, + struct section *relasec_twin) { - struct rela *rela1, *rela2 = NULL; + struct rela *rela1 = NULL; + struct rela *rela2 = NULL; /* check relocation item one by one, order matters */ - rela2 = list_entry(relasec_twin->relas.next, struct rela, list); - list_for_each_entry(rela1, &relasec->relas, list) { - if (rela_equal(rela1, rela2)) { - rela2 = list_entry(rela2->list.next, struct rela, list); - continue; - } - relasec->status = CHANGED; - return; - } - - relasec->status = SAME; + rela2 = list_entry(relasec_twin->relas.next, struct rela, list); + list_for_each_entry(rela1, &relasec->relas, list) { + if (rela_equal(rela1, rela2)) { + rela2 = list_entry(rela2->list.next, struct rela, list); + continue; + } + relasec->status = CHANGED; + return; + } + relasec->status = SAME; } -static void compare_correlated_nonrela_section(struct section *sec, struct section *sectwin) +static void compare_correlated_nonrela_section(struct section *sec, + struct section *sectwin) { - if (sec->sh.sh_type != SHT_NOBITS && + if (sec->sh.sh_type != SHT_NOBITS && (sec->data->d_size != sectwin->data->d_size || - memcmp(sec->data->d_buf, sectwin->data->d_buf, sec->data->d_size))) - sec->status = CHANGED; - else - sec->status = SAME; + memcmp(sec->data->d_buf, sectwin->data->d_buf, sec->data->d_size))) { + sec->status = CHANGED; + } else { + sec->status = SAME; + } } // we may change status of sec, they are not same -static int compare_correlated_section(struct section *sec, struct section *sectwin) +static int compare_correlated_section(struct section *sec, struct section *twin) { - /* TODO: addr align of rodata has changed. after strlen(str) >= 30, align 8 exists */ - /* compare section headers */ - if (sec->sh.sh_type != sectwin->sh.sh_type || - sec->sh.sh_flags != sectwin->sh.sh_flags || - sec->sh.sh_entsize != sectwin->sh.sh_entsize || - (sec->sh.sh_addralign != sectwin->sh.sh_addralign && - !is_text_section(sec) && !is_string_section(sec))) { - DIFF_FATAL("%s section header details differ from %s", sec->name, sectwin->name); - return -1; - } - - if (is_note_section(sec)) { - sec->status = SAME; - goto out; - } - - /* As above but for aarch64 */ - if (!strcmp(sec->name, ".rela__patchable_function_entries") || - !strcmp(sec->name, "__patchable_function_entries")) { - sec->status = SAME; - goto out; - } + /* We allow sh_flags and sh_addralign changes. + When we change the initial value of variables + sh_flags & sh_addralign may change in .rodata section */ + if ((sec->sh.sh_type != twin->sh.sh_type) || + (sec->sh.sh_entsize != twin->sh.sh_entsize)) { + ERROR("%s section header details differ from %s", + sec->name, twin->name); + return -1; + } + if (sec->sh.sh_flags != twin->sh.sh_flags) { + log_warn("Section '%s' sh_flags changed from %ld to %ld\n", + sec->name, sec->sh.sh_flags, twin->sh.sh_flags); + } + if (sec->sh.sh_addralign != twin->sh.sh_addralign) { + log_warn("Section '%s' sh_addralign changed from %ld to %ld\n", + sec->name, sec->sh.sh_addralign, twin->sh.sh_addralign); + } + if (is_note_section(sec)) { + sec->status = SAME; + return 0; + } + /* As above but for aarch64 */ + if (!strcmp(sec->name, ".rela__patchable_function_entries") || + !strcmp(sec->name, "__patchable_function_entries")) { + sec->status = SAME; + return 0; + } /* compare file size and data size(memory size) */ - if (sec->sh.sh_size != sectwin->sh.sh_size || - sec->data->d_size != sectwin->data->d_size || - (sec->rela && !sectwin->rela) || (!sec->rela && sectwin->rela)) { - sec->status = CHANGED; - goto out; - } - - if (is_rela_section(sec)) - compare_correlated_rela_section(sec, sectwin); - else - compare_correlated_nonrela_section(sec, sectwin); + if (sec->sh.sh_size != twin->sh.sh_size || + sec->data->d_size != twin->data->d_size || + (sec->rela && !twin->rela) || (!sec->rela && twin->rela)) { + sec->status = CHANGED; + return 0; + } -out: - if(sec->status == CHANGED) - log_debug("section %s has changed\n", sec->name); + if (is_rela_section(sec)) { + compare_correlated_rela_section(sec, twin); + } else { + compare_correlated_nonrela_section(sec, twin); + } return 0; } -bool upatch_handle_redis_line(const char *symname) +static void update_section_status(struct section *sec, enum status status) { - if (!strncmp(symname, "_serverPanic", 12) || - !strncmp(symname, "_serverAssert", 13) || - !strncmp(symname, "_serverAssertWithInfo", 21) || - !strncmp(symname, "rdbReportError", 14) || - !strncmp(symname, "RedisModule__Assert", 19)) - return true; - return false; -} - -/* TODO: let user support this list or generate by the compiler ? */ -bool check_line_func(struct upatch_elf *uelf, const char *symname) -{ - if (!strncmp(basename(g_relf_name), "redis-server", 12)) - return upatch_handle_redis_line(symname); - - return false; -} - -/* Determine if a section has changed only due to a __LINE__ bumber change. - * For example, a WARN() or might_sleep() macro's embedding of the line number into an - * instruction operand. - */ -static bool _line_macro_change_only(struct upatch_elf *uelf, struct section *sec) -{ - unsigned long offset, insn1_len, insn2_len; - void *data1, *data2, *insn1, *insn2; - struct rela *rela; - bool found, found_any = false; - - if (sec->status != CHANGED || - is_rela_section(sec) || - !is_text_section(sec) || - sec->sh.sh_size != sec->twin->sh.sh_size || - !sec->rela || - sec->rela->status != SAME) - return false; - - data1 = sec->twin->data->d_buf; - data2 = sec->data->d_buf; - for (offset = 0; offset < sec->sh.sh_size; offset += insn1_len) { - insn1 = data1 + offset; - insn2 = data2 + offset; - - insn1_len = insn_length(uelf, insn1); - insn2_len = insn_length(uelf, insn2); - - if (!insn1_len || !insn2_len) - ERROR("decode instruction in section %s at offset 0x%lx failed", - sec->name, offset); - - if (insn1_len != insn2_len) - return false; - - /* if insn are same, continue*/ - if (!memcmp(insn1, insn2, insn1_len)) - continue; - - log_debug("check list for %s at 0x%lx \n", sec->name, offset); - - /* - * Here we found a differece between two instructions of the - * same length. Only ignore the change if: - * - * 1) the instruction match a known pattern of a '__LINE__' - * macro immediate value which was embedded in the instruction. - * - * 2) the instructions are followed by certain expected relocations. - * (white-list) - */ - if (!insn_is_load_immediate(uelf, insn1) || - !insn_is_load_immediate(uelf, insn2)) - return false; - - found = false; - list_for_each_entry(rela, &sec->rela->relas, list) { - if (rela->offset < offset + insn1_len) - continue; - - if (rela->string) - continue; - - /* TODO: we may need black list ? */ - if (check_line_func(uelf, rela->sym->name)) { - found = true; - break; - } - - return false; - } - if (!found) - return false; - - found_any = true; - } - - if (!found_any) - ERROR("no instruction changes detected for changed section %s", - sec->name); - - return true; -} - -static bool _line_macro_change_only_aarch64(struct upatch_elf *uelf, struct section *sec) -{ - - unsigned long start1, start2, size, offset; - struct rela *rela; - bool found_any = false, found; - unsigned int mov_imm_mask = ((1<<16) - 1)<<5; - unsigned long insn_len = insn_length(uelf, NULL); - - if (sec->status != CHANGED || - is_rela_section(sec) || - !is_text_section(sec) || - sec->sh.sh_size != sec->twin->sh.sh_size || - !sec->rela || - sec->rela->status != SAME) - return false; - - start1 = (unsigned long)sec->twin->data->d_buf; - start2 = (unsigned long)sec->data->d_buf; - size = sec->sh.sh_size; - for (offset = 0; offset < size; offset += insn_len) { - if (!memcmp((void *)start1 + offset, (void *)start2 + offset, insn_len)) - continue; - - /* verify it's a mov immediate to w1 */ - if ((*(int *)(start1 + offset) & ~mov_imm_mask) != - (*(int *)(start2 + offset) & ~mov_imm_mask)) - return false; - - found = false; - list_for_each_entry(rela, &sec->rela->relas, list) { - if (rela->offset < offset + insn_len) - continue; - if (rela->string) - continue; - - /* TODO: we may need black list ? */ - if (check_line_func(uelf, rela->sym->name)) { - found = true; - break; - } - return false; - } - if (!found) - return false; - - found_any = true; - } - - if (!found_any) - ERROR("no instruction changes detected for changed section %s", - sec->name); - - return true; -} - -static bool line_macro_change_only(struct upatch_elf *uelf, struct section *sec) -{ - switch(uelf->arch) { - case AARCH64: - return _line_macro_change_only_aarch64(uelf, sec); - case X86_64: - return _line_macro_change_only(uelf, sec); - default: - ERROR("unsupported arch"); - } - return false; -} - -static inline void update_section_status(struct section *sec, enum status status) -{ - if (sec == NULL) { - return; - } - if (sec->twin != NULL) { - sec->twin->status = status; - } - if (is_rela_section(sec)) { - if ((sec->base != NULL) && - (sec->base->sym != NULL)) { - sec->base->sym->status = status; - } - } - else { - if (sec->sym != NULL) { - sec->sym->status = status; - } - } + if (sec == NULL) { + return; + } + if (sec->twin != NULL) { + sec->twin->status = status; + } + if (is_rela_section(sec)) { + if ((sec->base != NULL) && (sec->base->bundle_sym != NULL) && status != SAME) { + sec->base->bundle_sym->status = status; + } + } else { + if (sec->bundle_sym != NULL) { + sec->bundle_sym->status = status; + } + } } void upatch_compare_sections(struct upatch_elf *uelf) { - struct section *sec = NULL; + struct section *sec = NULL; - list_for_each_entry(sec, &uelf->sections, list) { - if (sec->twin == NULL) { - sec->status = NEW; - } - else { - compare_correlated_section(sec, sec->twin); - } - /* exclude WARN-only, might_sleep changes */ - if (line_macro_change_only(uelf, sec)) { - log_debug("reverting macro / line number section %s status to SAME\n", sec->name); - sec->status = SAME; - } - /* sync status */ - update_section_status(sec, sec->status); - update_section_status(sec->twin, sec->status); - } -} \ No newline at end of file + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->ignored) { + continue; + } + if (is_special_static_section(sec)) { + sec->status = SAME; + } else if (sec->twin == NULL) { + sec->status = NEW; + } else { + compare_correlated_section(sec, sec->twin); + } + /* sync status */ + update_section_status(sec, sec->status); + update_section_status(sec->twin, sec->status); + } +} diff --git a/upatch-diff/elf-compare.h b/upatch-diff/elf-compare.h index 3fd46a9d78173303d400d33cfa794995aa2b550b..5202fb03b3d64497c99e3d5e098290bcf6006c02 100644 --- a/upatch-diff/elf-compare.h +++ b/upatch-diff/elf-compare.h @@ -25,7 +25,7 @@ #ifndef __UPATCH_ELF_COMPARE_H_ #define __UPATCH_ELF_COMPARE_H_ -#include "upatch-elf.h" +struct upatch_elf; void upatch_compare_symbols(struct upatch_elf *); @@ -37,4 +37,4 @@ static inline void upatch_compare_correlated_elements(struct upatch_elf *uelf) upatch_compare_symbols(uelf); } -#endif \ No newline at end of file +#endif diff --git a/upatch-diff/elf-correlate.c b/upatch-diff/elf-correlate.c index a0fe66980688e5253fae3280f083be97a39538be..5991c2cffafaa76d5dcaccb842fcd7a00ac53eea 100644 --- a/upatch-diff/elf-correlate.c +++ b/upatch-diff/elf-correlate.c @@ -27,68 +27,91 @@ #include "elf-common.h" #include "elf-correlate.h" -static void correlate_symbol(struct symbol *sym_orig, struct symbol *sym_patched) +static void correlate_symbol(struct symbol *sym_orig, + struct symbol *sym_patched) { - log_debug("correlate symbol %s <-> %s \n", sym_orig->name, sym_patched->name); - sym_orig->twin = sym_patched; sym_patched->twin = sym_orig; sym_orig->status = sym_patched->status = SAME; if (strcmp(sym_orig->name, sym_patched->name)) { - log_debug("renaming symbol %s to %s \n", sym_patched->name, sym_orig->name); - sym_patched->name = strdup(sym_orig->name); - if (!sym_patched->name) - ERROR("strdup"); + log_debug("renaming symbol %s to %s\n", + sym_patched->name, sym_orig->name); + sym_patched->name = sym_orig->name; + } + if (sym_patched->sec && sym_orig->sec) { + sym_patched->sec->twin = sym_orig->sec; + sym_orig->sec->twin = sym_patched->sec; } - if (sym_orig->relf_sym && !sym_patched->relf_sym) - sym_patched->relf_sym = sym_orig->relf_sym; } -void upatch_correlate_symbols(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) +void upatch_correlate_symbols(struct upatch_elf *uelf_source, + struct upatch_elf *uelf_patched) { - struct symbol *sym_orig, *sym_patched; + struct symbol *sym_orig; + struct symbol *sym_patched; - list_for_each_entry(sym_orig, &uelf_source->symbols, list) { - if (sym_orig->twin) - continue; + list_for_each_entry(sym_orig, &uelf_source->symbols, list) { + if (is_symbol_ignored(sym_orig)) { + continue; + } + if (sym_orig->twin) { + continue; + } + /* + * Special static local variables should never be correlated + * and should always be included if they are referenced by + * an included function. + */ + if (is_special_static_symbol(sym_orig)) { + continue; + } /* find matched symbol */ - list_for_each_entry(sym_patched, &uelf_patched->symbols, list) { - if (mangled_strcmp(sym_orig->name, sym_patched->name) || - sym_orig->type != sym_patched->type || sym_patched->twin) - continue; - - if (is_special_static(sym_orig)) - continue; - - /* - * The .LCx symbols point to string literals in - * '.rodata..str1.*' sections. They get included - * in include_standard_elements(). - * Clang creates similar .Ltmp%d symbols in .rodata.str - */ - if (sym_orig->type == STT_NOTYPE && - (!strncmp(sym_orig->name, ".LC", 3) || !strncmp(sym_orig->name, ".Ltmp", 5))) - continue; - - if (is_mapping_symbol(uelf_source, sym_orig)) - continue; - - /* group section symbols must have correlated sections */ - if (sym_orig->sec && sym_orig->sec->sh.sh_type == SHT_GROUP && - sym_orig->sec->twin != sym_patched->sec) - continue; - - correlate_symbol(sym_orig, sym_patched); - break; - } - } + list_for_each_entry(sym_patched, &uelf_patched->symbols, list) { + if (mangled_strcmp(sym_orig->name, sym_patched->name) || + sym_orig->type != sym_patched->type || sym_patched->twin) { + continue; + } + if (is_special_static_symbol(sym_patched)) { + continue; + } + /* + * The .LCx symbols point to string literals in + * '.rodata..str1.*' sections. They get included + * in include_standard_elements(). + * Clang creates similar .Ltmp%d symbols in .rodata.str + */ + if (sym_orig->type == STT_NOTYPE && + (!strncmp(sym_orig->name, ".LC", 3) || + !strncmp(sym_orig->name, ".Ltmp", 5))) { + continue; + } + + /* on RISC-V: .L symbols should not change section */ + if (uelf_source->arch == RISCV64 && !strncmp(sym_orig->name, ".L", 2) && + sym_orig->sec && sym_orig->sec->twin != sym_patched->sec) { + continue; + } + + if (is_mapping_symbol(uelf_source, sym_orig)) { + continue; + } + + /* group section symbols must have correlated sections */ + if (sym_orig->sec && sym_orig->sec->sh.sh_type == SHT_GROUP && + sym_orig->sec->twin != sym_patched->sec) { + continue; + } + + correlate_symbol(sym_orig, sym_patched); + break; + } + } } -static void __correlate_section(struct section *sec_orig, struct section *sec_patched) +static void correlate_section_impl(struct section *sec_orig, + struct section *sec_patched) { - log_debug("correlate section %s <-> %s \n", sec_orig->name, sec_patched->name); - sec_orig->twin = sec_patched; sec_patched->twin = sec_orig; /* set initial status, might change */ @@ -96,98 +119,112 @@ static void __correlate_section(struct section *sec_orig, struct section *sec_pa /* Make sure these two sections have the same name */ if (strcmp(sec_orig->name, sec_patched->name)) { - log_debug("renaming section %s to %s \n", sec_patched->name, sec_orig->name); - sec_patched->name = strdup(sec_orig->name); - if (!sec_patched->name) - ERROR("strdup"); + log_debug("renaming section %s to %s\n", + sec_patched->name, sec_orig->name); + sec_patched->name = sec_orig->name; + sec_patched->name_source = DATA_SOURCE_REF; } } -static void correlate_section(struct section *sec_orig, struct section *sec_patched) +static void correlate_section(struct section *sec_orig, + struct section *sec_patched) { - __correlate_section(sec_orig, sec_patched); - - if (is_rela_section(sec_orig)) { - __correlate_section(sec_orig->base, sec_patched->base); + correlate_section_impl(sec_orig, sec_patched); + if (is_rela_section(sec_orig)) { + correlate_section_impl(sec_orig->base, sec_patched->base); /* handle symbol for base section now */ - sec_orig = sec_orig->base; - sec_patched = sec_patched->base; - } else if (sec_orig->rela && sec_patched->rela) { - __correlate_section(sec_orig->rela, sec_patched->rela); - } - - if (sec_orig->secsym && sec_patched->secsym) { - correlate_symbol(sec_orig->secsym, sec_patched->secsym); - } - - if (sec_orig->sym) { - correlate_symbol(sec_orig->sym, sec_patched->sym); - } + sec_orig = sec_orig->base; + sec_patched = sec_patched->base; + } else if (sec_orig->rela && sec_patched->rela) { + correlate_section_impl(sec_orig->rela, sec_patched->rela); + } + if (sec_orig->sym && sec_patched->sym) { + correlate_symbol(sec_orig->sym, sec_patched->sym); + } + if (sec_orig->bundle_sym) { + correlate_symbol(sec_orig->bundle_sym, sec_patched->bundle_sym); + } } -void upatch_correlate_sections(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) +void upatch_correlate_sections(struct upatch_elf *uelf_source, + struct upatch_elf *uelf_patched) { - struct section *sec_orig, *sec_patched; + struct section *sec_orig; + struct section *sec_patched; - list_for_each_entry(sec_orig, &uelf_source->sections, list) { + list_for_each_entry(sec_orig, &uelf_source->sections, list) { /* already found */ - if (sec_orig->twin) - continue; - - list_for_each_entry(sec_patched, &uelf_patched->sections, list) { - if (mangled_strcmp(sec_orig->name, sec_patched->name) || - sec_patched->twin) - continue; - - if (is_special_static(is_rela_section(sec_orig) ? - sec_orig->base->secsym : - sec_orig->secsym)) - continue; - - /* - * Group sections must match exactly to be correlated. - */ - if (sec_orig->sh.sh_type == SHT_GROUP) { - if (sec_orig->data->d_size != sec_patched->data->d_size) - continue; - if (memcmp(sec_orig->data->d_buf, sec_patched->data->d_buf, - sec_orig->data->d_size)) - continue; - } - - correlate_section(sec_orig, sec_patched); - break; - } - } + if (sec_orig->twin != NULL) { + continue; + } + /* + * Special static local variables should never be correlated + * and should always be included if they are referenced by + * an included function. + */ + if (is_special_static_section(sec_orig)) { + continue; + } + list_for_each_entry(sec_patched, &uelf_patched->sections, list) { + if (sec_patched->twin != NULL) { + continue; + } + if (is_special_static_section(sec_patched)) { + continue; + } + if (mangled_strcmp(sec_orig->name, sec_patched->name)) { + continue; + } + /* + * Group sections must match exactly to be correlated. + */ + if (sec_orig->sh.sh_type == SHT_GROUP) { + if (sec_orig->data->d_size != sec_patched->data->d_size) { + continue; + } + if (memcmp(sec_orig->data->d_buf, sec_patched->data->d_buf, + sec_orig->data->d_size)) { + continue; + } + } + + correlate_section(sec_orig, sec_patched); + break; + } + } } /* TODO: need handle .toc section */ -static struct symbol *find_uncorrelated_rela(struct section *relasec, struct symbol *sym) +static struct symbol *find_uncorrelated_rela(struct section *relasec, + struct symbol *sym) { - struct rela *rela; + struct rela *rela; - /* find the patched object's corresponding variable */ - list_for_each_entry(rela, &relasec->relas, list) { - struct symbol *patched_sym = rela->sym; - if (patched_sym->twin) - continue; + /* find the patched object's corresponding variable */ + list_for_each_entry(rela, &relasec->relas, list) { + struct symbol *patched_sym = rela->sym; - if (sym->type != patched_sym->type || - (sym->type == STT_OBJECT && - sym->sym.st_size != patched_sym->sym.st_size)) - continue; - - if (mangled_strcmp(patched_sym->name, sym->name)) - continue; + if (patched_sym->twin) { + continue; + } + if (sym->type != patched_sym->type || + ( + sym->type == STT_OBJECT && + sym->sym.st_size != patched_sym->sym.st_size + )) { + continue; + } + if (mangled_strcmp(patched_sym->name, sym->name)) { + continue; + } - log_debug("find uncorrelated rela symbol successful %s [%s] \n", + log_debug("find uncorrelated rela symbol successful %s [%s]\n", patched_sym->name, section_function_name(relasec)); + return patched_sym; + } - return patched_sym; - } - - return NULL; + return NULL; } /* @@ -195,27 +232,30 @@ static struct symbol *find_uncorrelated_rela(struct section *relasec, struct sym * in the base object, find a corresponding usage of a similarly named symbol * in the patched object. */ -static struct symbol *find_static_twin(struct section *relasec, struct symbol *sym) +static struct symbol *find_static_twin(struct section *relasec, + struct symbol *sym) { /* TODO: handle .part symbol is neccessry */ + if (!relasec->twin) { + return NULL; + } - if (!relasec->twin) - return NULL; - - return find_uncorrelated_rela(relasec->twin, sym); + return find_uncorrelated_rela(relasec->twin, sym); } -static struct rela *find_static_twin_ref(struct section *relasec, struct symbol *sym) +static struct rela *find_static_twin_ref(struct section *relasec, + struct symbol *sym) { - struct rela *rela; + struct rela *rela; - list_for_each_entry(rela, &relasec->relas, list) { - if (rela->sym == sym->twin) - return rela; - } + list_for_each_entry(rela, &relasec->relas, list) { + if (rela->sym == sym->twin) { + return rela; + } + } - /* TODO: handle child func here */ - return NULL; + /* TODO: handle child func here */ + return NULL; } /* Check two things: @@ -224,58 +264,66 @@ static struct rela *find_static_twin_ref(struct section *relasec, struct symbol * a corresponding reference in the patched object * (because a staticlocal can be referenced by more than one section) */ -static void check_static_variable_correlate(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) +static void check_static_variable_correlate(struct upatch_elf *uelf_source, + struct upatch_elf *uelf_patched) { - struct section *relasec; - struct rela *rela; + struct section *relasec; + struct rela *rela; struct symbol *sym; - list_for_each_entry(relasec, &uelf_source->sections, list) { - if (!is_rela_section(relasec) || - is_debug_section(relasec) || - is_note_section(relasec)) - continue; + list_for_each_entry(relasec, &uelf_source->sections, list) { + if (!is_rela_section(relasec) || + is_debug_section(relasec) || + is_note_section(relasec)) { + continue; + } - list_for_each_entry(rela, &relasec->relas, list) { + list_for_each_entry(rela, &relasec->relas, list) { sym = rela->sym; - if (!is_normal_static_local(sym)) - continue; - - if (!sym->twin || !relasec->twin) - DIFF_FATAL("reference to static local variable %s in %s was removed", + if (!is_normal_static_local(sym)) { + continue; + } + if (!sym->twin || !relasec->twin) { + log_warn("reference to static local variable %s in %s was removed", sym->name, section_function_name(relasec)); - - if(!find_static_twin_ref(relasec->twin, sym)) - DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it", - sym->name, sym->twin->name, section_function_name(relasec->twin)); + continue; + } + if (!find_static_twin_ref(relasec->twin, sym)) { + ERROR("static local %s has been correlated with %s, " + "but patched %s is missing a reference to it", + sym->name, sym->twin->name, + section_function_name(relasec->twin)); + } } } - /* - * Now go through the patched object and look for any uncorrelated - * static locals to see if we need to print any warnings about new - * variables. - */ - - list_for_each_entry(relasec, &uelf_patched->sections, list) { - - if (!is_rela_section(relasec) || - is_debug_section(relasec) || - is_note_section(relasec)) - continue; - - list_for_each_entry(rela, &relasec->relas, list) { - sym = rela->sym; - if (!is_normal_static_local(sym)) - continue; + /* + * Now go through the patched object and look for any uncorrelated + * static locals to see if we need to print any warnings about new + * variables. + */ + + list_for_each_entry(relasec, &uelf_patched->sections, list) { + if (!is_rela_section(relasec) || + is_debug_section(relasec) || + is_note_section(relasec)) { + continue; + } - if (sym->twin) - continue; + list_for_each_entry(rela, &relasec->relas, list) { + sym = rela->sym; - log_normal("unable to correlate static local variable %s used by %s, assuming variable is new \n", - sym->name, section_function_name(relasec)); - } - } + if (!is_normal_static_local(sym)) { + continue; + } + if (sym->twin) { + continue; + } + log_normal("unable to correlate static local variable %s used by %s" + ", assuming variable is new\n", + sym->name, section_function_name(relasec)); + } + } } static void uncorrelate_symbol(struct symbol *sym) @@ -314,88 +362,102 @@ static void uncorrelate_section(struct section *sec) * they can occasionally be referenced by data sections as * well. */ -void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, struct upatch_elf *uelf_patched) +void upatch_correlate_static_local_variables(struct upatch_elf *uelf_source, + struct upatch_elf *uelf_patched) { - struct symbol *sym, *patched_sym; - struct section *relasec; - struct rela *rela; - int bundled, patched_bundled; - - /* - * undo the correlations for all static locals. Two static locals can have the same numbered suffix in the orig - * and patchedobjects by coincidence. - */ - list_for_each_entry(sym, &uelf_source->symbols, list) { - if (!is_normal_static_local(sym)) - continue; + struct symbol *sym; + struct symbol *patched_sym; - log_debug("find normal symbol %s \n", sym->name); - if (sym->twin) - uncorrelate_symbol(sym); + struct section *relasec; + struct rela *rela; - bundled = (sym == sym->sec->sym) ? 1 : 0; - if (bundled && sym->sec->twin) { - log_debug("find bundled static symbol %s \n", sym->name); + int bundled; + int patched_bundled; - uncorrelate_section(sym->sec); + /* + * undo the correlations for all static locals. + * Two static locals can have the same numbered suffix in the orig + * and patchedobjects by coincidence. + */ + list_for_each_entry(sym, &uelf_source->symbols, list) { + if (!is_normal_static_local(sym)) { + continue; + } - if (sym->sec->secsym) - uncorrelate_symbol(sym->sec->secsym); + if (sym->twin) { + uncorrelate_symbol(sym); + } - if (sym->sec->rela) - uncorrelate_section(sym->sec->rela); // uncorrelate relocation section which is not equal to reference - } - } + bundled = (sym == sym->sec->bundle_sym) ? 1 : 0; + if (bundled && sym->sec->twin) { + uncorrelate_section(sym->sec); + if (sym->sec->sym) { + uncorrelate_symbol(sym->sec->sym); + } + if (sym->sec->rela) { + // uncorrelate relocation section which not equals to reference + uncorrelate_section(sym->sec->rela); + } + } + } /* - * Do the correlations: for each section reference to a static local, - * look for a corresponding reference in the section's twin. - */ - list_for_each_entry(relasec, &uelf_source->sections, list) { - + * Do the correlations: for each section reference to a static local, + * look for a corresponding reference in the section's twin. + */ + list_for_each_entry(relasec, &uelf_source->sections, list) { /* handle .rela.toc sectoins */ - if (!is_rela_section(relasec) || - is_debug_section(relasec) || - is_note_section(relasec)) - continue; + if (!is_rela_section(relasec) || + is_debug_section(relasec) || + is_note_section(relasec)) { + continue; + } /* check all relocation symbols */ - list_for_each_entry(rela, &relasec->relas, list) { + list_for_each_entry(rela, &relasec->relas, list) { sym = rela->sym; - if (!is_normal_static_local(sym)) - continue; - - if (sym->twin) - continue; - - bundled = (sym == sym->sec->sym) ? 1 : 0; - if (bundled && sym->sec == relasec->base) { - /* - * TODO: A rare case where a static local data structure references itself. - * There's no reliable way to correlate this. Hopefully - * to the symbol somewhere that can be used. - */ - log_debug("can't correlate static local %s's reference to itself\n", sym->name); - continue; - } - - patched_sym = find_static_twin(relasec, sym); - if (!patched_sym) - DIFF_FATAL("reference to static local variable %s in %s was removed", + if (!is_normal_static_local(sym)) { + continue; + } + if (sym->twin) { + continue; + } + + bundled = (sym == sym->sec->bundle_sym) ? 1 : 0; + if (bundled && sym->sec == relasec->base) { + /* + * TODO: + * A rare case where a static local data structure references + * itself. + * There's no reliable way to correlate this. + * Hopefully to the symbol somewhere that can be used. + */ + log_debug("can't correlate static local %s's ref to itself\n", + sym->name); + continue; + } + + patched_sym = find_static_twin(relasec, sym); + if (!patched_sym) { + log_warn("reference to static local variable %s in %s was removed", sym->name, section_function_name(relasec)); - - patched_bundled = (patched_sym == patched_sym->sec->sym) ? 1 : 0; - if (bundled != patched_bundled) - ERROR("bundle mismatch for symbol %s", sym->name); - if (!bundled && sym->sec->twin != patched_sym->sec) - ERROR("sections %s and %s aren't correlated for symbol %s", - sym->sec->name, patched_sym->sec->name, sym->name); - - correlate_symbol(sym, patched_sym); - - if (bundled) - correlate_section(sym->sec, patched_sym->sec); + continue; + } + + patched_bundled = (patched_sym == patched_sym->sec->bundle_sym) ? 1 : 0; + if (bundled != patched_bundled) { + ERROR("bundle mismatch for symbol %s", sym->name); + } + if (!bundled && sym->sec->twin && sym->sec->twin != patched_sym->sec) { + ERROR("sections %s and %s aren't correlated for symbol %s", + sym->sec->name, patched_sym->sec->name, sym->name); + } + + correlate_symbol(sym, patched_sym); + if (bundled) { + correlate_section(sym->sec, patched_sym->sec); + } } } diff --git a/upatch-diff/elf-correlate.h b/upatch-diff/elf-correlate.h index 212203012e09c1a26445bacd898e97c3673b92bd..2e443027d680424816aff17d0d9210263b716e4f 100644 --- a/upatch-diff/elf-correlate.h +++ b/upatch-diff/elf-correlate.h @@ -25,7 +25,7 @@ #ifndef __UPATCH_ELF_CORRELATE_H_ #define __UPATCH_ELF_CORRELATE_H_ -#include "upatch-elf.h" +struct upatch_elf; void upatch_correlate_sections(struct upatch_elf *, struct upatch_elf *); @@ -39,4 +39,4 @@ static inline void upatch_correlate_elf(struct upatch_elf *uelf_source, struct u void upatch_correlate_static_local_variables(struct upatch_elf *, struct upatch_elf *); -#endif \ No newline at end of file +#endif diff --git a/upatch-diff/elf-create.c b/upatch-diff/elf-create.c index 873b3a942fe1ee1901c8f41b1340cd1d6978fcce..c1310296a6ff571198b43877add7d4e0633d8183 100644 --- a/upatch-diff/elf-create.c +++ b/upatch-diff/elf-create.c @@ -34,17 +34,22 @@ #include "elf-create.h" #include "upatch-patch.h" #include "upatch-dynrela.h" +#include "upatch-elf.h" +#include "running-elf.h" /* create text and relocation sections */ static struct section *create_section_pair(struct upatch_elf *uelf, char *name, - int entsize, int nr) + unsigned int entsize, unsigned int nr) { char *relaname; - struct section *sec, *relasec; + struct section *sec; + struct section *relasec; + size_t size = strlen(name) + strlen(".rela") + 1; - relaname = malloc(strlen(name) + strlen(".rela") + 1); - if (!relaname) + relaname = calloc(1, size); + if (!relaname) { ERROR("relaname malloc failed."); + } strcpy(relaname, ".rela"); strcat(relaname, name); @@ -52,13 +57,17 @@ static struct section *create_section_pair(struct upatch_elf *uelf, char *name, /* allocate text section resourcce */ ALLOC_LINK(sec, &uelf->sections); sec->name = name; - sec->data = malloc(sizeof(*sec->data)); - if (!sec->data) + sec->data = calloc(1, sizeof(Elf_Data)); + if (!sec->data) { ERROR("section data malloc failed."); + } + sec->data_source = DATA_SOURCE_ALLOC; sec->data->d_buf = calloc(nr, entsize); - if (!sec->data->d_buf) + if (!sec->data->d_buf) { ERROR("d_buf of section data malloc failed."); + } + sec->dbuf_source = DATA_SOURCE_ALLOC; sec->data->d_size = entsize * nr; sec->data->d_type = ELF_T_BYTE; @@ -73,12 +82,15 @@ static struct section *create_section_pair(struct upatch_elf *uelf, char *name, /* set relocation section */ ALLOC_LINK(relasec, &uelf->sections); relasec->name = relaname; + relasec->name_source = DATA_SOURCE_ALLOC; INIT_LIST_HEAD(&relasec->relas); /* buffers will be generated by upatch_rebuild_rela_section_data */ - relasec->data = malloc(sizeof(*relasec->data)); - if (!relasec->data) + relasec->data = calloc(1, sizeof(Elf_Data)); + if (!relasec->data) { ERROR("relasec data malloc failed."); + } + relasec->data_source = DATA_SOURCE_ALLOC; relasec->data->d_type = ELF_T_RELA; @@ -103,9 +115,12 @@ void upatch_create_strings_elements(struct upatch_elf *uelf) ALLOC_LINK(sec, &uelf->sections); sec->name = ".upatch.strings"; - sec->data = malloc(sizeof(*sec->data)); - if (!sec->data) + sec->data = calloc(1, sizeof(Elf_Data)); + if (!sec->data) { ERROR("section data malloc failed"); + } + sec->data_source = DATA_SOURCE_ALLOC; + sec->data->d_type = ELF_T_BYTE; /* set section header */ @@ -124,14 +139,20 @@ void upatch_create_strings_elements(struct upatch_elf *uelf) } /* create upatch func info section */ -void upatch_create_patches_sections(struct upatch_elf *uelf, struct running_elf *relf) +void upatch_create_patches_sections(struct upatch_elf *uelf, + struct running_elf *relf, unsigned long text_offset) { - struct symbol *sym, *strsym; - struct section *sec, *relasec; + struct symbol *sym; + struct symbol *strsym; + + struct section *sec; + struct section *relasec; + struct upatch_patch_func *funcs; struct rela *rela; - struct lookup_result symbol; - int nr = 0, index = 0; + + unsigned int nr = 0; + unsigned int index = 0; /* find changed func */ list_for_each_entry(sym, &uelf->symbols, list) { @@ -141,6 +162,11 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, struct running_elf nr++; } + if (nr == 0) { + log_warn("No functional changes\n"); + return; + } + /* create text/rela section pair */ sec = create_section_pair(uelf, ".upatch.funcs", sizeof(*funcs), nr); relasec = sec->rela; @@ -156,24 +182,28 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, struct running_elf continue; } - if (!lookup_relf(relf, sym, &symbol)) { + struct relf_symbol *symbol = lookup_relf(relf, sym); + if (symbol == NULL) { ERROR("Cannot find symbol '%s' in %s", sym->name, g_relf_name); } - - if (sym->bind == STB_LOCAL && symbol.global) { + if ((sym->bind == STB_LOCAL) && (symbol->bind != STB_LOCAL)) { ERROR("Cannot find local symbol '%s' in symbol table.", sym->name); } - - log_debug("lookup for %s: symbol name %s sympos=%lu size=%lu.\n", - sym->name, symbol.symbol->name, symbol.sympos, symbol.symbol->size); + log_debug("lookup for %s: symbol name %s sympos=%u size=%lu.\n", + sym->name, symbol->name, symbol->index, symbol->size); /* ATTENTION: kpatch convert global symbols to local symbols here. */ - funcs[index].old_addr = symbol.symbol->addr; - funcs[index].old_size = symbol.symbol->size; + if (symbol->addr < text_offset) { + ERROR("Text section offset 0x%lx overflow, sym_addr=0x%lx", + text_offset, symbol->addr); + } + funcs[index].old_addr = symbol->addr - text_offset; + funcs[index].old_size = symbol->size; funcs[index].new_size = sym->sym.st_size; - funcs[index].sympos = symbol.sympos; + funcs[index].sympos = symbol->index; - log_debug("change func %s from 0x%lx.\n", sym->name, funcs[index].old_addr); + log_debug("change func %s from 0x%lx.\n", + sym->name, funcs[index].old_addr); /* Add a rela than will handle funcs[index].new_addr */ ALLOC_LINK(rela, &relasec->relas); @@ -193,105 +223,9 @@ void upatch_create_patches_sections(struct upatch_elf *uelf, struct running_elf index++; } - if (index != nr) - ERROR("sanity check failed in funcs sections. \n"); -} - -static bool need_dynrela(struct upatch_elf *uelf, struct running_elf *relf, - struct section *relasec, struct rela *rela) -{ - struct lookup_result symbol; - - if (is_debug_section(relasec) || - is_note_section(relasec)) - return false; - - if (!lookup_relf(relf, rela->sym, &symbol)) { - /* relocation is based on new symbol. */ - return false; - } - - if (rela->sym->bind == STB_LOCAL) { - if (symbol.global) - ERROR("No releated local symbol found. \n"); - return true; - } - - return false; -} - -/* - * This function is used to handle relocations which cannot be handled normally - * - * Situations: - * 1. refer to old symbols - * - */ -void upatch_create_intermediate_sections(struct upatch_elf *uelf, struct running_elf *relf) -{ - struct rela *rela, *rela_safe; - struct section *relasec, *usym_sec, *urela_sec; - struct upatch_symbol *usyms; - struct upatch_relocation *urelas; - struct symbol *strsym, *usym_sec_sym; - int nr = 0, index = 0; - - list_for_each_entry(relasec, &uelf->sections, list) { - if (!is_rela_section(relasec)) - continue; - /* no need to handle upatch meta section. */ - if (!strcmp(relasec->name, ".rela.upatch.funcs")) - continue; - - list_for_each_entry(rela, &relasec->relas, list) { - nr++; - if (need_dynrela(uelf, relf, relasec, rela)){ - rela->need_dynrela = 1; - } - } - } - - urela_sec = create_section_pair(uelf, ".upatch.relocations", sizeof(*urelas), nr); - urelas = urela_sec->data->d_buf; - - usym_sec = create_section_pair(uelf, ".upatch.symbols", sizeof(*usyms), nr); - usyms = usym_sec->data->d_buf; - - ALLOC_LINK(usym_sec_sym, &uelf->symbols); - usym_sec_sym->sec = usym_sec; - usym_sec_sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); - usym_sec_sym->type = STT_SECTION; - usym_sec_sym->bind = STB_LOCAL; - usym_sec_sym->name = ".upatch.symbols"; - - strsym = find_symbol_by_name(&uelf->symbols, ".upatch.strings"); - if (!strsym) - ERROR("can't find .upatch.strings symbol. \n"); - - list_for_each_entry(relasec, &uelf->sections, list) { - if (!is_rela_section(relasec)) - continue; - if (!strcmp(relasec->name, ".rela.upatch.funcs") || - !strcmp(relasec->name, ".rela.upatch.relocations") || - !strcmp(relasec->name, ".rela.upatch.symbols")) - continue; - - list_for_each_entry_safe(rela, rela_safe, &relasec->relas, list) { - if (!rela->need_dynrela) { - rela->sym->strip = SYMBOL_USED; - continue; - } - } + if (index != nr) { + ERROR("sanity check failed in funcs sections.\n"); } - - log_debug("generate %d dynamic relocations.\n", index); - - /* set size to actual number of kyms/krelas */ - usym_sec->data->d_size = index * sizeof(struct upatch_symbol); - usym_sec->sh.sh_size = usym_sec->data->d_size; - - urela_sec->data->d_size = index * sizeof(struct upatch_relocation); - urela_sec->sh.sh_size = urela_sec->data->d_size; } void upatch_build_strings_section_data(struct upatch_elf *uelf) @@ -302,20 +236,24 @@ void upatch_build_strings_section_data(struct upatch_elf *uelf) char *strtab; sec = find_section_by_name(&uelf->sections, ".upatch.strings"); - if (!sec) + if (!sec) { ERROR("can't find strings section."); + } size = 0; - list_for_each_entry(string, &uelf->strings, list) + list_for_each_entry(string, &uelf->strings, list) { size += strlen(string->name) + 1; + } /* allocate section resources */ - strtab = malloc(size); - if (!strtab) + strtab = calloc(1, size); + if (!strtab) { ERROR("strtab malloc failed."); + } sec->data->d_buf = strtab; sec->data->d_size = size; + sec->dbuf_source = DATA_SOURCE_ALLOC; /* populate strings section data */ list_for_each_entry(string, &uelf->strings, list) { @@ -331,9 +269,9 @@ static void migrate_symbols(struct list_head *src, struct symbol *sym, *sym_safe; list_for_each_entry_safe(sym, sym_safe, src, list) { - if (select && !select(sym)) + if (select && !select(sym)) { continue; - + } list_del(&sym->list); list_add_tail(&sym->list, dst); } @@ -365,7 +303,7 @@ void upatch_strip_unneeded_syms(struct upatch_elf *uelf) struct symbol *sym, *sym_safe; list_for_each_entry_safe(sym, sym_safe, &uelf->symbols, list) { - if (sym->strip == SYMBOL_STRIP) { + if (sym->strip) { list_del(&sym->list); free(sym); } @@ -376,7 +314,7 @@ void upatch_reindex_elements(struct upatch_elf *uelf) { struct section *sec; struct symbol *sym; - unsigned int index; + GElf_Section index; index = 1; list_for_each_entry(sec, &uelf->sections, list) { @@ -388,9 +326,9 @@ void upatch_reindex_elements(struct upatch_elf *uelf) list_for_each_entry(sym, &uelf->symbols, list) { sym->index = index; index++; - if (sym->sec) - sym->sym.st_shndx = (unsigned short)sym->sec->index; - else if (sym->sym.st_shndx != SHN_ABS) { + if (sym->sec) { + sym->sym.st_shndx = sym->sec->index; + } else if (sym->sym.st_shndx != SHN_ABS) { sym->sym.st_shndx = SHN_UNDEF; } } @@ -401,19 +339,24 @@ static void rebuild_rela_section_data(struct section *sec) struct rela *rela; GElf_Rela *relas; size_t size; - int nr = 0, index = 0; - list_for_each_entry(rela, &sec->relas, list) + unsigned int nr = 0; + unsigned int index = 0; + + list_for_each_entry(rela, &sec->relas, list) { nr++; + } size = nr * sizeof(*relas); - relas = malloc(size); - if (!relas) + relas = calloc(1, size); + if (!relas) { ERROR("relas malloc failed."); + } sec->data->d_buf = relas; sec->data->d_size = size; sec->sh.sh_size = size; + sec->dbuf_source = DATA_SOURCE_ALLOC; list_for_each_entry(rela, &sec->relas, list) { relas[index].r_offset = rela->offset; @@ -422,109 +365,105 @@ static void rebuild_rela_section_data(struct section *sec) index++; } - if (index != nr) + if (index != nr) { ERROR("size mismatch in rebuild rela section."); + } } /* update index for relocations */ void upatch_rebuild_relocations(struct upatch_elf *uelf) { - struct section *relasec, *symtab; + struct section *relasec; + struct section *symtab; symtab = find_section_by_name(&uelf->sections, ".symtab"); - if (!symtab) - ERROR("missing .symtab section in rebuild relocations. \n"); + if (!symtab) { + ERROR("missing .symtab section in rebuild relocations.\n"); + } list_for_each_entry(relasec, &uelf->sections, list) { - if (!is_rela_section(relasec)) + if (!is_rela_section(relasec)) { continue; - relasec->sh.sh_link = symtab->index; - relasec->sh.sh_info = relasec->base->index; + } + relasec->sh.sh_link = (Elf64_Word)symtab->index; + relasec->sh.sh_info = (Elf64_Word)relasec->base->index; rebuild_rela_section_data(relasec); } } -void upatch_check_relocations(struct upatch_elf *uelf) -{ - log_debug("upatch_check_relocations does not work now.\n"); - return; -} - -static void print_strtab(char *buf, size_t size) -{ - size_t i; - - for (i = 0; i < size; i++) { - if (buf[i] == 0) - log_debug("\\0"); - else - log_debug("%c", buf[i]); - } -} - void upatch_create_shstrtab(struct upatch_elf *uelf) { - size_t size, offset, len; - struct section *shstrtab, *sec; + struct section *shstrtab; + struct section *sec; + char *buf; + size_t size; + size_t offset; + size_t len; shstrtab = find_section_by_name(&uelf->sections, ".shstrtab"); - if (!shstrtab) + if (!shstrtab) { ERROR("find_section_by_name failed."); + } /* determine size of string table */ size = 1; - list_for_each_entry(sec, &uelf->sections, list) + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->ignored) { + continue; + } size += strlen(sec->name) + 1; + } - buf = malloc(size); - if (!buf) + buf = calloc(1, size); + if (!buf) { ERROR("malloc shstrtab failed."); - memset(buf, 0, size); + } offset = 1; list_for_each_entry(sec, &uelf->sections, list) { + if (sec->ignored) { + continue; + } len = strlen(sec->name) + 1; sec->sh.sh_name = (unsigned int)offset; memcpy(buf + offset, sec->name, len); offset += len; } - if (offset != size) + if (offset != size) { + free(buf); ERROR("shstrtab size mismatch."); + } shstrtab->data->d_buf = buf; shstrtab->data->d_size = size; - - log_debug("shstrtab: "); - print_strtab(buf, size); - log_debug("\n"); - - list_for_each_entry(sec, &uelf->sections, list) - log_debug("%s @ shstrtab offset %d\n", sec->name, sec->sh.sh_name); + shstrtab->dbuf_source = DATA_SOURCE_ALLOC; } void upatch_create_strtab(struct upatch_elf *uelf) { - struct section *strtab; - struct symbol *sym; - size_t size = 0, offset = 0, len = 0; - char *buf; + size_t size = 0; + size_t offset = 0; + size_t len = 0; - strtab = find_section_by_name(&uelf->sections, ".strtab"); - if (!strtab) + struct section *strtab = find_section_by_name(&uelf->sections, ".strtab"); + if (!strtab) { ERROR("find section failed in create strtab."); + } + struct symbol *sym = NULL; list_for_each_entry(sym, &uelf->symbols, list) { - if (sym->type == STT_SECTION) + if (sym->type == STT_SECTION) { continue; + } size += strlen(sym->name) + 1; } - buf = malloc(size); - if (!buf) + char *buf = calloc(1, size); + if (!buf) { ERROR("malloc buf failed in create strtab"); - memset(buf, 0, size); + } list_for_each_entry(sym, &uelf->symbols, list) { if (sym->type == STT_SECTION) { @@ -537,18 +476,14 @@ void upatch_create_strtab(struct upatch_elf *uelf) offset += len; } - if (offset != size) + if (offset != size) { + free(buf); ERROR("shstrtab size mismatch."); + } strtab->data->d_buf = buf; strtab->data->d_size = size; - - log_debug("strtab: "); - print_strtab(buf, size); - log_debug("\n"); - - list_for_each_entry(sym, &uelf->symbols, list) - log_debug("%s @ strtab offset %d\n", sym->name, sym->sym.st_name); + strtab->dbuf_source = DATA_SOURCE_ALLOC; } void upatch_create_symtab(struct upatch_elf *uelf) @@ -556,70 +491,90 @@ void upatch_create_symtab(struct upatch_elf *uelf) struct section *symtab; struct section *strtab; struct symbol *sym; - size_t size; + + unsigned int nr = 0; + unsigned int nr_local = 0; + char *buf; - int nr = 0, nr_local = 0; + size_t size; unsigned long offset = 0; symtab = find_section_by_name(&uelf->sections, ".symtab"); - if (!symtab) + if (!symtab) { ERROR("find_section_by_name failed."); + } - list_for_each_entry(sym, &uelf->symbols, list) + list_for_each_entry(sym, &uelf->symbols, list) { nr++; + } size = nr * symtab->sh.sh_entsize; - buf = malloc(size); - if (!buf) + buf = calloc(1, size); + if (!buf) { ERROR("malloc buf failed in create symtab."); - memset(buf, 0, size); + } offset = 0; list_for_each_entry(sym, &uelf->symbols, list) { memcpy(buf + offset, &sym->sym, symtab->sh.sh_entsize); offset += symtab->sh.sh_entsize; - - if (is_local_sym(sym)) + if (is_local_sym(sym)) { nr_local++; + } } symtab->data->d_buf = buf; symtab->data->d_size = size; + symtab->dbuf_source = DATA_SOURCE_ALLOC; /* update symtab section header */ strtab = find_section_by_name(&uelf->sections, ".strtab"); - if (!strtab) + if (!strtab) { ERROR("missing .strtab section in create symtab."); + } - symtab->sh.sh_link = strtab->index; + symtab->sh.sh_link = (Elf64_Word)strtab->index; symtab->sh.sh_info = nr_local; } -void upatch_write_output_elf(struct upatch_elf *uelf, Elf *elf, char *outfile, mode_t mode) +void upatch_write_output_elf(struct upatch_elf *uelf, Elf *elf, + char *outfile, mode_t mode) { int fd; + Elf *elfout; + Elf_Scn *scn; Elf_Data *data; - GElf_Ehdr eh, ehout; + + GElf_Ehdr eh; + GElf_Ehdr ehout; + GElf_Shdr sh; - struct section *sec, *shstrtab; + + struct section *sec; + struct section *shstrtab; fd = creat(outfile, mode); - if (fd == -1) + if (fd == -1) { ERROR("creat failed."); + } elfout = elf_begin(fd, ELF_C_WRITE, NULL); - if (!elfout) + if (!elfout) { ERROR("elf_begin failed."); + } /* alloc ELF header */ - if (!gelf_newehdr(elfout, gelf_getclass(elf))) + if (!gelf_newehdr(elfout, gelf_getclass(elf))) { ERROR("gelf_newehdr failed."); - if (!gelf_getehdr(elfout, &ehout)) + } + if (!gelf_getehdr(elfout, &ehout)) { ERROR("gelf_getehdr elfout failed."); - if (!gelf_getehdr(elf, &eh)) + } + if (!gelf_getehdr(elf, &eh)) { ERROR("gelf_getehdr elf failed."); + } memset(&ehout, 0, sizeof(ehout)); ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA]; @@ -628,43 +583,55 @@ void upatch_write_output_elf(struct upatch_elf *uelf, Elf *elf, char *outfile, m ehout.e_version = EV_CURRENT; shstrtab = find_section_by_name(&uelf->sections, ".shstrtab"); - if (!shstrtab) + if (!shstrtab) { ERROR("missing .shstrtab sections in write output elf"); + } ehout.e_shstrndx = (unsigned short)shstrtab->index; /* add changed sections */ list_for_each_entry(sec, &uelf->sections, list) { + if (sec->ignored) { + continue; + } + scn = elf_newscn(elfout); - if (!scn) + if (!scn) { ERROR("elf_newscn failed."); + } data = elf_newdata(scn); - if (!data) + if (!data) { ERROR("elf_newdata failed."); + } - if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) + if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) { ERROR("elf_flagdata failed."); + } data->d_type = sec->data->d_type; data->d_buf = sec->data->d_buf; data->d_size = sec->data->d_size; - if (!gelf_getshdr(scn, &sh)) + if (!gelf_getshdr(scn, &sh)) { ERROR("gelf_getshdr in adding changed sections"); + } sh = sec->sh; - if (!gelf_update_shdr(scn, &sh)) + if (!gelf_update_shdr(scn, &sh)) { ERROR("gelf_update_shdr failed."); + } } - if (!gelf_update_ehdr(elfout, &ehout)) + if (!gelf_update_ehdr(elfout, &ehout)) { ERROR("gelf_update_ehdr failed."); + } - if (elf_update(elfout, ELF_C_WRITE) < 0) + if (elf_update(elfout, ELF_C_WRITE) < 0) { ERROR("elf_update failed."); + } elf_end(elfout); close(fd); -} \ No newline at end of file +} diff --git a/upatch-diff/elf-create.h b/upatch-diff/elf-create.h index 1b4dc6c97664c44ed9b5e1d88f620025c4cad011..c0f4fa5dc924748ed85d85b5eaf09785f8a25100 100644 --- a/upatch-diff/elf-create.h +++ b/upatch-diff/elf-create.h @@ -25,15 +25,15 @@ #ifndef __UPATCH_CREATE_H_ #define __UPATCH_CREATE_H_ -#include "upatch-elf.h" +struct upatch_elf; +struct running_elf; void upatch_create_strings_elements(struct upatch_elf *); -void upatch_create_patches_sections(struct upatch_elf *, struct running_elf *); +void upatch_create_patches_sections(struct upatch_elf *, struct running_elf *, + unsigned long); -void upatch_create_intermediate_sections(struct upatch_elf *, struct running_elf *); - -static inline void create_kpatch_arch_section(struct upatch_elf *uelf) {} +static inline void create_kpatch_arch_section(void) {} void upatch_build_strings_section_data(struct upatch_elf *); @@ -45,8 +45,6 @@ void upatch_reindex_elements(struct upatch_elf *); void upatch_rebuild_relocations(struct upatch_elf *); -void upatch_check_relocations(struct upatch_elf *); - void upatch_create_shstrtab(struct upatch_elf *); void upatch_create_strtab(struct upatch_elf *); @@ -55,4 +53,4 @@ void upatch_create_symtab(struct upatch_elf *); void upatch_write_output_elf(struct upatch_elf *, Elf *, char *, mode_t); -#endif /* __UPATCH_CREATE_H_ */ \ No newline at end of file +#endif /* __UPATCH_CREATE_H_ */ diff --git a/upatch-diff/elf-debug.c b/upatch-diff/elf-debug.c index f9c5327c3d5ad3506474fa5d7b76a20b0a2e34d6..44b0e09e0f5d30cb3d4ecc0cde24e6a83967e561 100644 --- a/upatch-diff/elf-debug.c +++ b/upatch-diff/elf-debug.c @@ -31,165 +31,111 @@ #include "elf-debug.h" #include "upatch-elf.h" -void upatch_print_changes(struct upatch_elf *uelf) +void upatch_print_correlation(struct upatch_elf *uelf) { - struct symbol *sym; - - list_for_each_entry(sym, &uelf->symbols, list) { - if (!sym->include || !sym->sec || sym->type != STT_FUNC || sym->parent) - continue; - if (sym->status == NEW) - log_normal("New function: %s\n", sym->name); - else if (sym->status == CHANGED) - log_normal("Changed function: %s\n", sym->name); + if (uelf == NULL) { + return; } -} -void upatch_dump_kelf(struct upatch_elf *uelf) -{ + log_debug("\n------------------------------\n"); + log_debug("Section\n"); + log_debug("------------------------------\n"); struct section *sec; - struct symbol *sym; - struct rela *rela; - - log_debug("\n=== Sections ===\n"); list_for_each_entry(sec, &uelf->sections, list) { - log_debug("%02d %s (%s)", sec->index, sec->name, status_str(sec->status)); - if (is_rela_section(sec)) { - log_debug(", base-> %s\n", sec->base->name); - if (is_debug_section(sec) || is_note_section(sec)) - goto next; - log_debug("rela section expansion\n"); - list_for_each_entry(rela, &sec->relas, list) { - log_debug("sym %d, offset %d, type %d, %s %s %ld \n", - rela->sym->index, rela->offset, - rela->type, rela->sym->name, - (rela->addend < 0) ? "-" : "+", - labs(rela->addend)); - } + if (sec->twin != NULL) { + log_debug("index: %04d, name: '%s' -> index: %04d, name: '%s'\n", + sec->index, sec->name, sec->twin->index, sec->twin->name); } else { - if (sec->sym) - log_debug(", sym-> %s", sec->sym->name); - if (sec->secsym) - log_debug(", secsym-> %s", sec->secsym->name); - if (sec->rela) - log_debug(", rela-> %s", sec->rela->name); + log_debug("index: %04d, name: '%s' -> None\n", + sec->index, sec->name); } -next: - log_debug("\n"); } - - log_debug("\n=== Symbols ===\n"); + log_debug("------------------------------\n"); + log_debug("\n"); + log_debug("------------------------------\n"); + log_debug("Symbol\n"); + log_debug("------------------------------\n"); + struct symbol *sym; list_for_each_entry(sym, &uelf->symbols, list) { - log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)", - sym->index, sym->type, sym->bind, sym->sym.st_shndx, - sym->name, status_str(sym->status)); - if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT)) - log_debug(" -> %s", sym->sec->name); - log_debug("\n"); - } -} - -/* debuginfo releated */ -static inline bool skip_bytes(unsigned char **iter, unsigned char *end, unsigned int len) -{ - if ((unsigned int)(end - *iter) < len) { - *iter = end; - return false; + if (sym->twin != NULL) { + log_debug("index: %04d, name: '%s' -> index: %04d, name: '%s'\n", + sym->index, sym->name, sym->twin->index, sym->twin->name); + } else { + log_debug("index: %04d, name: '%s' -> None\n", + sym->index, sym->name); + } } - *iter += len; - return true; + log_debug("------------------------------\n"); } -void upatch_rebuild_eh_frame(struct section *sec) +void upatch_print_changes(struct upatch_elf *uelf) { - void *eh_frame; - unsigned long long frame_size; - struct rela *rela; - unsigned char *data, *data_end; - unsigned int hdr_length, hdr_id; - unsigned int current_offset; - unsigned int count = 0; - - /* sanity check */ - if (!is_eh_frame(sec) || is_rela_section(sec) || !sec->rela) - return; - - list_for_each_entry(rela, &sec->rela->relas, list) - count ++; + struct symbol *sym; + struct section *sec; - /* currently, only delete is possible */ - if (sec->rela->sh.sh_entsize != 0 && - count == sec->rela->sh.sh_size / sec->rela->sh.sh_entsize) + if (uelf == NULL) { return; + } - log_debug("sync modification for eh_frame \n"); - - data = sec->data->d_buf; - data_end = sec->data->d_buf + sec->data->d_size; - - /* in this time, some relcation entries may have been deleted */ - frame_size = 0; - eh_frame = malloc(sec->data->d_size); - if (!eh_frame) - ERROR("malloc eh_frame failed \n"); - - /* 8 is the offset of PC begin */ - current_offset = 8; - list_for_each_entry(rela, &sec->rela->relas, list) { - unsigned int offset = rela->offset; - bool found_rela = false; - log_debug("handle relocaton offset at 0x%x \n", offset); - while (data != data_end) { - void *__src = data; - - log_debug("current handle offset is 0x%x \n", current_offset); - - REQUIRE(skip_bytes(&data, data_end, 4), "no length to be read"); - hdr_length = *(unsigned int *)(data - 4); - - REQUIRE(hdr_length != 0xffffffff, "64 bit .eh_frame is not supported"); - /* if it is 0, we reach the end. */ - if (hdr_length == 0) - break; - - REQUIRE(skip_bytes(&data, data_end, 4), "no length to be read"); - hdr_id = *(unsigned int *)(data - 4); - - REQUIRE(skip_bytes(&data, data_end, hdr_length - 4), "no length to be read"); - - if (current_offset == offset) - found_rela = true; - - /* CIE or relocation releated FDE */ - if (hdr_id == 0 || found_rela) { - memcpy(eh_frame + frame_size, __src, hdr_length + 4); - /* update rela offset to point to new offset, and also hdr_id */ - if (found_rela) { - /* 4 is the offset of hdr_id and 8 is the offset of PC begin */ - *(unsigned int *)(eh_frame + frame_size + 4) = frame_size + 4; - rela->offset = frame_size + 8; - } - - frame_size += (hdr_length + 4); - } else { - log_debug("remove FDE at 0x%x \n", current_offset); - } - - /* hdr_length(value) + hdr_length(body) */ - current_offset += (4 + hdr_length); - - if (found_rela) - break; + log_normal("\n------------------------------\n"); + log_normal("New symbol\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sym, &uelf->symbols, list) { + if (sym->status == NEW) { + log_normal("index: %04d, name: '%s'\n", sym->index, sym->name); } } - - /* - * FIXME: data may not reach the data_end, since we have found - * all FDE for relocation entries, the only problem here is - * we may miss the CIE, but CIE is always in the beginning ? - */ - - sec->data->d_buf = eh_frame; - sec->data->d_size = frame_size; - sec->sh.sh_size = frame_size; + log_normal("------------------------------\n"); + log_normal("\n"); + log_normal("------------------------------\n"); + log_normal("New section\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->status == NEW) { + log_normal("index: %04d, name: '%s'\n", sec->index, sec->name); + } + } + log_normal("------------------------------\n"); + log_normal("\n"); + log_normal("------------------------------\n"); + log_normal("Changed symbol\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sym, &uelf->symbols, list) { + if (sym->status == CHANGED) { + log_normal("index: %04d, name: '%s'\n", sym->index, sym->name); + } + } + log_normal("------------------------------\n"); + log_normal("\n"); + log_normal("------------------------------\n"); + log_normal("Changed section\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->status == CHANGED) { + log_normal("index: %04d, name: '%s'\n", sec->index, sec->name); + } + } + log_normal("------------------------------\n"); + log_normal("\n"); + log_normal("------------------------------\n"); + log_normal("Included symbol\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sym, &uelf->symbols, list) { + if (sym->include) { + log_normal("index: %04d, name: '%s', status: %s\n", + sym->index, sym->name, status_str(sym->status)); + } + } + log_normal("------------------------------\n"); + log_normal("\n"); + log_normal("------------------------------\n"); + log_normal("Included section\n"); + log_normal("------------------------------\n"); + list_for_each_entry(sec, &uelf->sections, list) { + if (sec->include) { + log_normal("index: %04d, name: '%s', status: %s\n", + sec->index, sec->name, status_str(sec->status)); + } + } + log_normal("------------------------------\n"); } diff --git a/upatch-diff/elf-debug.h b/upatch-diff/elf-debug.h index 0852c94a4b741f3b6128b9acdb3c2390bc57a1e6..c196ed7e88b92c36c3b329d99a9cd784bbf11c68 100644 --- a/upatch-diff/elf-debug.h +++ b/upatch-diff/elf-debug.h @@ -25,12 +25,9 @@ #ifndef __UPATCH_ELF_DEBUG_H_ #define __UPATCH_ELF_DEBUG_H_ -#include "upatch-elf.h" +struct upatch_elf; -void upatch_print_changes(struct upatch_elf *); - -void upatch_dump_kelf(struct upatch_elf *); - -void upatch_rebuild_eh_frame(struct section *); +void upatch_print_correlation(struct upatch_elf *uelf); +void upatch_print_changes(struct upatch_elf *uelf); #endif /* __UPATCH_ELF_DEBUG_H_ */ diff --git a/upatch-diff/elf-insn.c b/upatch-diff/elf-insn.c index 41252fed406cc69f0df80ebf4c58eeb051cc146b..466a00c93c83c6b29f6ec3ef0ba8dee07e89d771 100644 --- a/upatch-diff/elf-insn.c +++ b/upatch-diff/elf-insn.c @@ -25,64 +25,73 @@ #include +#include "insn/asm/insn.h" #include "elf-common.h" #include "elf-insn.h" void rela_insn(const struct section *sec, const struct rela *rela, struct insn *insn) { - unsigned long insn_addr, start, end, rela_addr; + unsigned long start; + unsigned long end; + + unsigned long rela_addr; + unsigned long insn_addr; start = (unsigned long)sec->data->d_buf; end = start + sec->sh.sh_size; - if (end <= start) + if (end <= start) { ERROR("bad section size"); + } rela_addr = start + rela->offset; for (insn_addr = start; insn_addr < end; insn_addr += insn->length) { insn_init(insn, (void *)insn_addr, 1); insn_get_length(insn); - if (!insn->length) + if (!insn->length) { ERROR("can't decode instruction in section %s at offset 0x%lx", sec->name, insn_addr); - if (rela_addr >= insn_addr && - rela_addr < insn_addr + insn->length) + } + if (rela_addr >= insn_addr && rela_addr < insn_addr + insn->length) { return; + } } - ERROR("can't find instruction for rela at %s+0x%x", + ERROR("can't find instruction for rela at %s+0x%lx", sec->name, rela->offset); } long rela_target_offset(struct upatch_elf *uelf, struct section *relasec, struct rela *rela) { - long add_off; struct section *sec = relasec->base; + long add_off; - switch(uelf->arch) { - case AARCH64: - add_off = 0; - break; - case X86_64: - if (!is_text_section(sec) || - rela->type == R_X86_64_64 || - rela->type == R_X86_64_32 || - rela->type == R_X86_64_32S) + switch (uelf->arch) { + case RISCV64: + /* fall through */ + case AARCH64: add_off = 0; - else if (rela->type == R_X86_64_PC32 || - rela->type == R_X86_64_PLT32) { - struct insn insn; - rela_insn(sec, rela, &insn); - add_off = (long)insn.next_byte - - (long)sec->data->d_buf - - rela->offset; - } else { - ERROR("unable to handle rela type %d \n", rela->type); - } - break; - default: - ERROR("unsupported arch \n"); - break; + break; + case X86_64: + if (!is_text_section(sec) || + rela->type == R_X86_64_64 || + rela->type == R_X86_64_32 || + rela->type == R_X86_64_32S) { + add_off = 0; + } else if (rela->type == R_X86_64_PC32 || rela->type == R_X86_64_PLT32) { + struct insn insn; + rela_insn(sec, rela, &insn); + + add_off = (long)insn.next_byte - + (long)sec->data->d_buf - + (long)rela->offset; + } else { + ERROR("unable to handle rela type %d\n", rela->type); + } + break; + default: + ERROR("unsupported arch\n"); + break; } return rela->addend + add_off; @@ -92,15 +101,21 @@ unsigned int insn_length(struct upatch_elf *uelf, void *addr) { struct insn decoded_insn; - switch(uelf->arch) { - case AARCH64: - return ARM64_INSTR_LEN; - case X86_64: - insn_init(&decoded_insn, addr, 1); - insn_get_length(&decoded_insn); - return decoded_insn.length; - default: - ERROR("unsupported arch"); + switch (uelf->arch) { + case AARCH64: + return ARM64_INSTR_LEN; + case X86_64: + insn_init(&decoded_insn, addr, 1); + insn_get_length(&decoded_insn); + return decoded_insn.length; + case RISCV64: + /* LSB 2 bits distinguish insn size. Now only RV32, RVC supported. */ + if ((*(char *)addr & 0x3) == 0x3) { + return RISCV64_INSN_LEN_4; + } + return RISCV64_INSN_LEN_2; + default: + ERROR("unsupported arch"); } return 0; @@ -111,23 +126,23 @@ bool insn_is_load_immediate(struct upatch_elf *uelf, void *addr) { unsigned char *insn = addr; - switch(uelf->arch) { - case X86_64: - /* arg2: mov $imm, %esi */ - if (insn[0] == 0xbe) - return true; - - /* arg3: mov $imm, %edx */ - if (insn[0] == 0xba) - return true; - - /* 0x41 is the prefix extend - REX.B */ - if (insn[0] == 0x41 && insn[1] == 0xb8) - return true; - - break; - default: - ERROR("unsupported arch"); + switch (uelf->arch) { + case X86_64: + /* arg2: mov $imm, %esi */ + if (insn[0] == 0xbe) { + return true; + } + /* arg3: mov $imm, %edx */ + if (insn[0] == 0xba) { + return true; + } + /* 0x41 is the prefix extend - REX.B */ + if (insn[0] == 0x41 && insn[1] == 0xb8) { + return true; + } + break; + default: + ERROR("unsupported arch"); } return false; -} \ No newline at end of file +} diff --git a/upatch-diff/elf-insn.h b/upatch-diff/elf-insn.h index f64ebe190717ebb8c41ea41da9d079ba3794ca6e..fdbc2e343b10843fb55fc82deeb13cccfff908c6 100644 --- a/upatch-diff/elf-insn.h +++ b/upatch-diff/elf-insn.h @@ -26,11 +26,19 @@ #ifndef __UPATCH_INSN_H_ #define __UPATCH_INSN_H_ -#include "asm/insn.h" -#include "upatch-elf.h" +#include +struct section; +struct rela; +struct insn; + +// arm #define ARM64_INSTR_LEN 4 +// riscv +#define RISCV64_INSN_LEN_4 4 +#define RISCV64_INSN_LEN_2 2 + void rela_insn(const struct section *sec, const struct rela *rela, struct insn *insn); /* diff --git a/upatch-diff/elf-resolve.c b/upatch-diff/elf-resolve.c index 0eb55cc20c36157589646053587813566ab283d6..2375b73d2982549a3dfdf6b717a95d1bf87d0ff8 100755 --- a/upatch-diff/elf-resolve.c +++ b/upatch-diff/elf-resolve.c @@ -20,9 +20,10 @@ * 02110-1301, USA. */ - +#include #include +#include "upatch-elf.h" #include "running-elf.h" #include "upatch-patch.h" @@ -30,15 +31,15 @@ void upatch_partly_resolve(struct upatch_elf *uelf, struct running_elf *relf) { struct symbol *sym; - struct lookup_result symbol; - list_for_each_entry(sym, &uelf->symbols, list) { if (sym->sym.st_other & SYM_OTHER) { - if (!lookup_relf(relf, sym, &symbol)) + struct relf_symbol *symbol = lookup_relf(relf, sym); + if (symbol == NULL) { continue; + } /* keep it undefined for link purpose */ - sym->sym.st_value = symbol.symbol->addr; - sym->sym.st_size = symbol.symbol->size; + sym->sym.st_value = symbol->addr; + sym->sym.st_size = symbol->size; } } -} \ No newline at end of file +} diff --git a/upatch-diff/elf-resolve.h b/upatch-diff/elf-resolve.h index 9eccdfd6580cfe6d27d0afe7160b47b8dce03fc2..92a1012dffd3123372f6fd39def7cdd819622f70 100755 --- a/upatch-diff/elf-resolve.h +++ b/upatch-diff/elf-resolve.h @@ -23,6 +23,9 @@ #ifndef __UPATCH_RESOLVE_H_ #define __UPATCH_RESOLVE_H_ +struct upatch_elf; +struct running_elf; + void upatch_partly_resolve(struct upatch_elf *, struct running_elf *); -#endif /* __UPATCH_RESOLVE_H_ */ \ No newline at end of file +#endif /* __UPATCH_RESOLVE_H_ */ diff --git a/upatch-diff/insn/asm/inat-tables.h b/upatch-diff/insn/asm/inat-tables.h index 8419e2e417a7157034da9a06ec789ae8341320e5..331eb86ae1af8f302dbb3ae9205df5659127f3d1 100644 --- a/upatch-diff/insn/asm/inat-tables.h +++ b/upatch-diff/insn/asm/inat-tables.h @@ -11,922 +11,922 @@ /* Table: one byte opcode */ const insn_attr_t inat_primary_table[INAT_OPCODE_TABLE_SIZE] = { - [0x00] = INAT_MODRM, - [0x01] = INAT_MODRM, - [0x02] = INAT_MODRM, - [0x03] = INAT_MODRM, - [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x05] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x08] = INAT_MODRM, - [0x09] = INAT_MODRM, - [0x0a] = INAT_MODRM, - [0x0b] = INAT_MODRM, - [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x0d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x0f] = INAT_MAKE_ESCAPE(1), - [0x10] = INAT_MODRM, - [0x11] = INAT_MODRM, - [0x12] = INAT_MODRM, - [0x13] = INAT_MODRM, - [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x15] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x18] = INAT_MODRM, - [0x19] = INAT_MODRM, - [0x1a] = INAT_MODRM, - [0x1b] = INAT_MODRM, - [0x1c] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x1d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x20] = INAT_MODRM, - [0x21] = INAT_MODRM, - [0x22] = INAT_MODRM, - [0x23] = INAT_MODRM, - [0x24] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x25] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x26] = INAT_MAKE_PREFIX(INAT_PFX_ES), - [0x28] = INAT_MODRM, - [0x29] = INAT_MODRM, - [0x2a] = INAT_MODRM, - [0x2b] = INAT_MODRM, - [0x2c] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x2d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x2e] = INAT_MAKE_PREFIX(INAT_PFX_CS), - [0x30] = INAT_MODRM, - [0x31] = INAT_MODRM, - [0x32] = INAT_MODRM, - [0x33] = INAT_MODRM, - [0x34] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x35] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x36] = INAT_MAKE_PREFIX(INAT_PFX_SS), - [0x38] = INAT_MODRM, - [0x39] = INAT_MODRM, - [0x3a] = INAT_MODRM, - [0x3b] = INAT_MODRM, - [0x3c] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x3d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0x3e] = INAT_MAKE_PREFIX(INAT_PFX_DS), - [0x40] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x41] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x42] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x43] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x44] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x45] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x46] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x47] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x48] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x49] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x4a] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x4b] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x4c] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x4d] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x4e] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x4f] = INAT_MAKE_PREFIX(INAT_PFX_REX), - [0x50] = INAT_FORCE64, - [0x51] = INAT_FORCE64, - [0x52] = INAT_FORCE64, - [0x53] = INAT_FORCE64, - [0x54] = INAT_FORCE64, - [0x55] = INAT_FORCE64, - [0x56] = INAT_FORCE64, - [0x57] = INAT_FORCE64, - [0x58] = INAT_FORCE64, - [0x59] = INAT_FORCE64, - [0x5a] = INAT_FORCE64, - [0x5b] = INAT_FORCE64, - [0x5c] = INAT_FORCE64, - [0x5d] = INAT_FORCE64, - [0x5e] = INAT_FORCE64, - [0x5f] = INAT_FORCE64, - [0x62] = INAT_MODRM, - [0x63] = INAT_MODRM | INAT_MODRM, - [0x64] = INAT_MAKE_PREFIX(INAT_PFX_FS), - [0x65] = INAT_MAKE_PREFIX(INAT_PFX_GS), - [0x66] = INAT_MAKE_PREFIX(INAT_PFX_OPNDSZ), - [0x67] = INAT_MAKE_PREFIX(INAT_PFX_ADDRSZ), - [0x68] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x69] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, - [0x6a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, - [0x6b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, - [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x71] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x72] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x73] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x74] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x75] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x76] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x77] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x78] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x79] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x7a] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x7b] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x7c] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x7d] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x7e] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x7f] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0x80] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), - [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(1), - [0x82] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), - [0x83] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), - [0x84] = INAT_MODRM, - [0x85] = INAT_MODRM, - [0x86] = INAT_MODRM, - [0x87] = INAT_MODRM, - [0x88] = INAT_MODRM, - [0x89] = INAT_MODRM, - [0x8a] = INAT_MODRM, - [0x8b] = INAT_MODRM, - [0x8c] = INAT_MODRM, - [0x8d] = INAT_MODRM, - [0x8e] = INAT_MODRM, - [0x8f] = INAT_MAKE_GROUP(2) | INAT_MODRM | INAT_FORCE64, - [0x9a] = INAT_MAKE_IMM(INAT_IMM_PTR), - [0x9c] = INAT_FORCE64, - [0x9d] = INAT_FORCE64, - [0xa0] = INAT_MOFFSET, - [0xa1] = INAT_MOFFSET, - [0xa2] = INAT_MOFFSET, - [0xa3] = INAT_MOFFSET, - [0xa8] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xa9] = INAT_MAKE_IMM(INAT_IMM_VWORD32), - [0xb0] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb1] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb2] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb3] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb4] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb5] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb6] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb7] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xb8] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xb9] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xba] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xbb] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xbc] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xbd] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xbe] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xbf] = INAT_MAKE_IMM(INAT_IMM_VWORD), - [0xc0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), - [0xc1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), - [0xc2] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_FORCE64, - [0xc4] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX3), - [0xc5] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX2), - [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(4), - [0xc7] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(5), - [0xc8] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_SCNDIMM, - [0xc9] = INAT_FORCE64, - [0xca] = INAT_MAKE_IMM(INAT_IMM_WORD), - [0xcd] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xd0] = INAT_MODRM | INAT_MAKE_GROUP(3), - [0xd1] = INAT_MODRM | INAT_MAKE_GROUP(3), - [0xd2] = INAT_MODRM | INAT_MAKE_GROUP(3), - [0xd3] = INAT_MODRM | INAT_MAKE_GROUP(3), - [0xd4] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xd5] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xd8] = INAT_MODRM, - [0xd9] = INAT_MODRM, - [0xda] = INAT_MODRM, - [0xdb] = INAT_MODRM, - [0xdc] = INAT_MODRM, - [0xdd] = INAT_MODRM, - [0xde] = INAT_MODRM, - [0xdf] = INAT_MODRM, - [0xe0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, - [0xe1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, - [0xe2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, - [0xe3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, - [0xe4] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xe5] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xe6] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xe7] = INAT_MAKE_IMM(INAT_IMM_BYTE), - [0xe8] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0xe9] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0xea] = INAT_MAKE_IMM(INAT_IMM_PTR), - [0xeb] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, - [0xf0] = INAT_MAKE_PREFIX(INAT_PFX_LOCK), - [0xf2] = INAT_MAKE_PREFIX(INAT_PFX_REPNE) | INAT_MAKE_PREFIX(INAT_PFX_REPNE), - [0xf3] = INAT_MAKE_PREFIX(INAT_PFX_REPE) | INAT_MAKE_PREFIX(INAT_PFX_REPE), - [0xf6] = INAT_MODRM | INAT_MAKE_GROUP(6), - [0xf7] = INAT_MODRM | INAT_MAKE_GROUP(7), - [0xfe] = INAT_MAKE_GROUP(8), - [0xff] = INAT_MAKE_GROUP(9), + [0x00] = INAT_MODRM, + [0x01] = INAT_MODRM, + [0x02] = INAT_MODRM, + [0x03] = INAT_MODRM, + [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x05] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x08] = INAT_MODRM, + [0x09] = INAT_MODRM, + [0x0a] = INAT_MODRM, + [0x0b] = INAT_MODRM, + [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x0d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x0f] = INAT_MAKE_ESCAPE(1), + [0x10] = INAT_MODRM, + [0x11] = INAT_MODRM, + [0x12] = INAT_MODRM, + [0x13] = INAT_MODRM, + [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x15] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x18] = INAT_MODRM, + [0x19] = INAT_MODRM, + [0x1a] = INAT_MODRM, + [0x1b] = INAT_MODRM, + [0x1c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x1d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x20] = INAT_MODRM, + [0x21] = INAT_MODRM, + [0x22] = INAT_MODRM, + [0x23] = INAT_MODRM, + [0x24] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x25] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x26] = INAT_MAKE_PREFIX(INAT_PFX_ES), + [0x28] = INAT_MODRM, + [0x29] = INAT_MODRM, + [0x2a] = INAT_MODRM, + [0x2b] = INAT_MODRM, + [0x2c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x2d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x2e] = INAT_MAKE_PREFIX(INAT_PFX_CS), + [0x30] = INAT_MODRM, + [0x31] = INAT_MODRM, + [0x32] = INAT_MODRM, + [0x33] = INAT_MODRM, + [0x34] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x35] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x36] = INAT_MAKE_PREFIX(INAT_PFX_SS), + [0x38] = INAT_MODRM, + [0x39] = INAT_MODRM, + [0x3a] = INAT_MODRM, + [0x3b] = INAT_MODRM, + [0x3c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x3d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x3e] = INAT_MAKE_PREFIX(INAT_PFX_DS), + [0x40] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x41] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x42] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x43] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x44] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x45] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x46] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x47] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x48] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x49] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4a] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4b] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4c] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4d] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4e] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4f] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x50] = INAT_FORCE64, + [0x51] = INAT_FORCE64, + [0x52] = INAT_FORCE64, + [0x53] = INAT_FORCE64, + [0x54] = INAT_FORCE64, + [0x55] = INAT_FORCE64, + [0x56] = INAT_FORCE64, + [0x57] = INAT_FORCE64, + [0x58] = INAT_FORCE64, + [0x59] = INAT_FORCE64, + [0x5a] = INAT_FORCE64, + [0x5b] = INAT_FORCE64, + [0x5c] = INAT_FORCE64, + [0x5d] = INAT_FORCE64, + [0x5e] = INAT_FORCE64, + [0x5f] = INAT_FORCE64, + [0x62] = INAT_MODRM, + [0x63] = INAT_MODRM | INAT_MODRM, + [0x64] = INAT_MAKE_PREFIX(INAT_PFX_FS), + [0x65] = INAT_MAKE_PREFIX(INAT_PFX_GS), + [0x66] = INAT_MAKE_PREFIX(INAT_PFX_OPNDSZ), + [0x67] = INAT_MAKE_PREFIX(INAT_PFX_ADDRSZ), + [0x68] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x69] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x6a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0x6b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x71] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x72] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x73] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x74] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x75] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x76] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x77] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x78] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x79] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7a] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7b] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7d] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7e] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7f] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x80] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x82] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x83] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x84] = INAT_MODRM, + [0x85] = INAT_MODRM, + [0x86] = INAT_MODRM, + [0x87] = INAT_MODRM, + [0x88] = INAT_MODRM, + [0x89] = INAT_MODRM, + [0x8a] = INAT_MODRM, + [0x8b] = INAT_MODRM, + [0x8c] = INAT_MODRM, + [0x8d] = INAT_MODRM, + [0x8e] = INAT_MODRM, + [0x8f] = INAT_MAKE_GROUP(2) | INAT_MODRM | INAT_FORCE64, + [0x9a] = INAT_MAKE_IMM(INAT_IMM_PTR), + [0x9c] = INAT_FORCE64, + [0x9d] = INAT_FORCE64, + [0xa0] = INAT_MOFFSET, + [0xa1] = INAT_MOFFSET, + [0xa2] = INAT_MOFFSET, + [0xa3] = INAT_MOFFSET, + [0xa8] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xa9] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0xb0] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb1] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb2] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb3] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb6] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb7] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb8] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xb9] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xba] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbb] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbc] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbd] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbe] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbf] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xc0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), + [0xc1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), + [0xc2] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_FORCE64, + [0xc4] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX3), + [0xc5] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX2), + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(4), + [0xc7] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(5), + [0xc8] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_SCNDIMM, + [0xc9] = INAT_FORCE64, + [0xca] = INAT_MAKE_IMM(INAT_IMM_WORD), + [0xcd] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd0] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd1] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd2] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd3] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd8] = INAT_MODRM, + [0xd9] = INAT_MODRM, + [0xda] = INAT_MODRM, + [0xdb] = INAT_MODRM, + [0xdc] = INAT_MODRM, + [0xdd] = INAT_MODRM, + [0xde] = INAT_MODRM, + [0xdf] = INAT_MODRM, + [0xe0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe6] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe7] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe8] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0xe9] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0xea] = INAT_MAKE_IMM(INAT_IMM_PTR), + [0xeb] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xf0] = INAT_MAKE_PREFIX(INAT_PFX_LOCK), + [0xf2] = INAT_MAKE_PREFIX(INAT_PFX_REPNE) | INAT_MAKE_PREFIX(INAT_PFX_REPNE), + [0xf3] = INAT_MAKE_PREFIX(INAT_PFX_REPE) | INAT_MAKE_PREFIX(INAT_PFX_REPE), + [0xf6] = INAT_MODRM | INAT_MAKE_GROUP(6), + [0xf7] = INAT_MODRM | INAT_MAKE_GROUP(7), + [0xfe] = INAT_MAKE_GROUP(8), + [0xff] = INAT_MAKE_GROUP(9), }; /* Table: 2-byte opcode (0x0f) */ const insn_attr_t inat_escape_table_1[INAT_OPCODE_TABLE_SIZE] = { - [0x00] = INAT_MAKE_GROUP(10), - [0x01] = INAT_MAKE_GROUP(11), - [0x02] = INAT_MODRM, - [0x03] = INAT_MODRM, - [0x0d] = INAT_MAKE_GROUP(12), - [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, - [0x10] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x11] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x12] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x13] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x14] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x15] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x16] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x17] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x18] = INAT_MAKE_GROUP(13), - [0x1a] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, - [0x1b] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, - [0x1f] = INAT_MODRM, - [0x20] = INAT_MODRM, - [0x21] = INAT_MODRM, - [0x22] = INAT_MODRM, - [0x23] = INAT_MODRM, - [0x28] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x29] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x2a] = INAT_MODRM | INAT_VARIANT, - [0x2b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x2c] = INAT_MODRM | INAT_VARIANT, - [0x2d] = INAT_MODRM | INAT_VARIANT, - [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x38] = INAT_MAKE_ESCAPE(2), - [0x3a] = INAT_MAKE_ESCAPE(3), - [0x40] = INAT_MODRM, - [0x41] = INAT_MODRM, - [0x42] = INAT_MODRM, - [0x43] = INAT_MODRM, - [0x44] = INAT_MODRM, - [0x45] = INAT_MODRM, - [0x46] = INAT_MODRM, - [0x47] = INAT_MODRM, - [0x48] = INAT_MODRM, - [0x49] = INAT_MODRM, - [0x4a] = INAT_MODRM, - [0x4b] = INAT_MODRM, - [0x4c] = INAT_MODRM, - [0x4d] = INAT_MODRM, - [0x4e] = INAT_MODRM, - [0x4f] = INAT_MODRM, - [0x50] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x51] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x52] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x53] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x54] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x55] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x56] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x57] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x5b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x5c] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x5d] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x5e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x5f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x60] = INAT_MODRM | INAT_VARIANT, - [0x61] = INAT_MODRM | INAT_VARIANT, - [0x62] = INAT_MODRM | INAT_VARIANT, - [0x63] = INAT_MODRM | INAT_VARIANT, - [0x64] = INAT_MODRM | INAT_VARIANT, - [0x65] = INAT_MODRM | INAT_VARIANT, - [0x66] = INAT_MODRM | INAT_VARIANT, - [0x67] = INAT_MODRM | INAT_VARIANT, - [0x68] = INAT_MODRM | INAT_VARIANT, - [0x69] = INAT_MODRM | INAT_VARIANT, - [0x6a] = INAT_MODRM | INAT_VARIANT, - [0x6b] = INAT_MODRM | INAT_VARIANT, - [0x6c] = INAT_VARIANT, - [0x6d] = INAT_VARIANT, - [0x6e] = INAT_MODRM | INAT_VARIANT, - [0x6f] = INAT_MODRM | INAT_VARIANT, - [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x71] = INAT_MAKE_GROUP(14), - [0x72] = INAT_MAKE_GROUP(15), - [0x73] = INAT_MAKE_GROUP(16), - [0x74] = INAT_MODRM | INAT_VARIANT, - [0x75] = INAT_MODRM | INAT_VARIANT, - [0x76] = INAT_MODRM | INAT_VARIANT, - [0x77] = INAT_VEXOK | INAT_VEXOK, - [0x78] = INAT_MODRM, - [0x79] = INAT_MODRM, - [0x7c] = INAT_VARIANT, - [0x7d] = INAT_VARIANT, - [0x7e] = INAT_MODRM | INAT_VARIANT, - [0x7f] = INAT_MODRM | INAT_VARIANT, - [0x80] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x82] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x83] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x84] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x85] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x86] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x87] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x88] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x89] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x8a] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x8b] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x8c] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x8d] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x8e] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x8f] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, - [0x90] = INAT_MODRM, - [0x91] = INAT_MODRM, - [0x92] = INAT_MODRM, - [0x93] = INAT_MODRM, - [0x94] = INAT_MODRM, - [0x95] = INAT_MODRM, - [0x96] = INAT_MODRM, - [0x97] = INAT_MODRM, - [0x98] = INAT_MODRM, - [0x99] = INAT_MODRM, - [0x9a] = INAT_MODRM, - [0x9b] = INAT_MODRM, - [0x9c] = INAT_MODRM, - [0x9d] = INAT_MODRM, - [0x9e] = INAT_MODRM, - [0x9f] = INAT_MODRM, - [0xa0] = INAT_FORCE64, - [0xa1] = INAT_FORCE64, - [0xa3] = INAT_MODRM, - [0xa4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, - [0xa5] = INAT_MODRM, - [0xa6] = INAT_MAKE_GROUP(17), - [0xa7] = INAT_MAKE_GROUP(18), - [0xa8] = INAT_FORCE64, - [0xa9] = INAT_FORCE64, - [0xab] = INAT_MODRM, - [0xac] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, - [0xad] = INAT_MODRM, - [0xae] = INAT_MAKE_GROUP(19), - [0xaf] = INAT_MODRM, - [0xb0] = INAT_MODRM, - [0xb1] = INAT_MODRM, - [0xb2] = INAT_MODRM, - [0xb3] = INAT_MODRM, - [0xb4] = INAT_MODRM, - [0xb5] = INAT_MODRM, - [0xb6] = INAT_MODRM, - [0xb7] = INAT_MODRM, - [0xb8] = INAT_VARIANT, - [0xb9] = INAT_MAKE_GROUP(20), - [0xba] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(21), - [0xbb] = INAT_MODRM, - [0xbc] = INAT_MODRM | INAT_VARIANT, - [0xbd] = INAT_MODRM | INAT_VARIANT, - [0xbe] = INAT_MODRM, - [0xbf] = INAT_MODRM, - [0xc0] = INAT_MODRM, - [0xc1] = INAT_MODRM, - [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0xc3] = INAT_MODRM, - [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0xc7] = INAT_MAKE_GROUP(22), - [0xd0] = INAT_VARIANT, - [0xd1] = INAT_MODRM | INAT_VARIANT, - [0xd2] = INAT_MODRM | INAT_VARIANT, - [0xd3] = INAT_MODRM | INAT_VARIANT, - [0xd4] = INAT_MODRM | INAT_VARIANT, - [0xd5] = INAT_MODRM | INAT_VARIANT, - [0xd6] = INAT_VARIANT, - [0xd7] = INAT_MODRM | INAT_VARIANT, - [0xd8] = INAT_MODRM | INAT_VARIANT, - [0xd9] = INAT_MODRM | INAT_VARIANT, - [0xda] = INAT_MODRM | INAT_VARIANT, - [0xdb] = INAT_MODRM | INAT_VARIANT, - [0xdc] = INAT_MODRM | INAT_VARIANT, - [0xdd] = INAT_MODRM | INAT_VARIANT, - [0xde] = INAT_MODRM | INAT_VARIANT, - [0xdf] = INAT_MODRM | INAT_VARIANT, - [0xe0] = INAT_MODRM | INAT_VARIANT, - [0xe1] = INAT_MODRM | INAT_VARIANT, - [0xe2] = INAT_MODRM | INAT_VARIANT, - [0xe3] = INAT_MODRM | INAT_VARIANT, - [0xe4] = INAT_MODRM | INAT_VARIANT, - [0xe5] = INAT_MODRM | INAT_VARIANT, - [0xe6] = INAT_VARIANT, - [0xe7] = INAT_MODRM | INAT_VARIANT, - [0xe8] = INAT_MODRM | INAT_VARIANT, - [0xe9] = INAT_MODRM | INAT_VARIANT, - [0xea] = INAT_MODRM | INAT_VARIANT, - [0xeb] = INAT_MODRM | INAT_VARIANT, - [0xec] = INAT_MODRM | INAT_VARIANT, - [0xed] = INAT_MODRM | INAT_VARIANT, - [0xee] = INAT_MODRM | INAT_VARIANT, - [0xef] = INAT_MODRM | INAT_VARIANT, - [0xf0] = INAT_VARIANT, - [0xf1] = INAT_MODRM | INAT_VARIANT, - [0xf2] = INAT_MODRM | INAT_VARIANT, - [0xf3] = INAT_MODRM | INAT_VARIANT, - [0xf4] = INAT_MODRM | INAT_VARIANT, - [0xf5] = INAT_MODRM | INAT_VARIANT, - [0xf6] = INAT_MODRM | INAT_VARIANT, - [0xf7] = INAT_MODRM | INAT_VARIANT, - [0xf8] = INAT_MODRM | INAT_VARIANT, - [0xf9] = INAT_MODRM | INAT_VARIANT, - [0xfa] = INAT_MODRM | INAT_VARIANT, - [0xfb] = INAT_MODRM | INAT_VARIANT, - [0xfc] = INAT_MODRM | INAT_VARIANT, - [0xfd] = INAT_MODRM | INAT_VARIANT, - [0xfe] = INAT_MODRM | INAT_VARIANT, + [0x00] = INAT_MAKE_GROUP(10), + [0x01] = INAT_MAKE_GROUP(11), + [0x02] = INAT_MODRM, + [0x03] = INAT_MODRM, + [0x0d] = INAT_MAKE_GROUP(12), + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x10] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x11] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x12] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x13] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x14] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x15] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x16] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x17] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x18] = INAT_MAKE_GROUP(13), + [0x1a] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, + [0x1b] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, + [0x1f] = INAT_MODRM, + [0x20] = INAT_MODRM, + [0x21] = INAT_MODRM, + [0x22] = INAT_MODRM, + [0x23] = INAT_MODRM, + [0x28] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x29] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2a] = INAT_MODRM | INAT_VARIANT, + [0x2b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2c] = INAT_MODRM | INAT_VARIANT, + [0x2d] = INAT_MODRM | INAT_VARIANT, + [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x38] = INAT_MAKE_ESCAPE(2), + [0x3a] = INAT_MAKE_ESCAPE(3), + [0x40] = INAT_MODRM, + [0x41] = INAT_MODRM, + [0x42] = INAT_MODRM, + [0x43] = INAT_MODRM, + [0x44] = INAT_MODRM, + [0x45] = INAT_MODRM, + [0x46] = INAT_MODRM, + [0x47] = INAT_MODRM, + [0x48] = INAT_MODRM, + [0x49] = INAT_MODRM, + [0x4a] = INAT_MODRM, + [0x4b] = INAT_MODRM, + [0x4c] = INAT_MODRM, + [0x4d] = INAT_MODRM, + [0x4e] = INAT_MODRM, + [0x4f] = INAT_MODRM, + [0x50] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x51] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x52] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x53] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x54] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x55] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x56] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x57] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5c] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5d] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x60] = INAT_MODRM | INAT_VARIANT, + [0x61] = INAT_MODRM | INAT_VARIANT, + [0x62] = INAT_MODRM | INAT_VARIANT, + [0x63] = INAT_MODRM | INAT_VARIANT, + [0x64] = INAT_MODRM | INAT_VARIANT, + [0x65] = INAT_MODRM | INAT_VARIANT, + [0x66] = INAT_MODRM | INAT_VARIANT, + [0x67] = INAT_MODRM | INAT_VARIANT, + [0x68] = INAT_MODRM | INAT_VARIANT, + [0x69] = INAT_MODRM | INAT_VARIANT, + [0x6a] = INAT_MODRM | INAT_VARIANT, + [0x6b] = INAT_MODRM | INAT_VARIANT, + [0x6c] = INAT_VARIANT, + [0x6d] = INAT_VARIANT, + [0x6e] = INAT_MODRM | INAT_VARIANT, + [0x6f] = INAT_MODRM | INAT_VARIANT, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x71] = INAT_MAKE_GROUP(14), + [0x72] = INAT_MAKE_GROUP(15), + [0x73] = INAT_MAKE_GROUP(16), + [0x74] = INAT_MODRM | INAT_VARIANT, + [0x75] = INAT_MODRM | INAT_VARIANT, + [0x76] = INAT_MODRM | INAT_VARIANT, + [0x77] = INAT_VEXOK | INAT_VEXOK, + [0x78] = INAT_MODRM, + [0x79] = INAT_MODRM, + [0x7c] = INAT_VARIANT, + [0x7d] = INAT_VARIANT, + [0x7e] = INAT_MODRM | INAT_VARIANT, + [0x7f] = INAT_MODRM | INAT_VARIANT, + [0x80] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x82] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x83] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x84] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x85] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x86] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x87] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x88] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x89] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8a] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8b] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8c] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8d] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8e] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8f] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x90] = INAT_MODRM, + [0x91] = INAT_MODRM, + [0x92] = INAT_MODRM, + [0x93] = INAT_MODRM, + [0x94] = INAT_MODRM, + [0x95] = INAT_MODRM, + [0x96] = INAT_MODRM, + [0x97] = INAT_MODRM, + [0x98] = INAT_MODRM, + [0x99] = INAT_MODRM, + [0x9a] = INAT_MODRM, + [0x9b] = INAT_MODRM, + [0x9c] = INAT_MODRM, + [0x9d] = INAT_MODRM, + [0x9e] = INAT_MODRM, + [0x9f] = INAT_MODRM, + [0xa0] = INAT_FORCE64, + [0xa1] = INAT_FORCE64, + [0xa3] = INAT_MODRM, + [0xa4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0xa5] = INAT_MODRM, + [0xa6] = INAT_MAKE_GROUP(17), + [0xa7] = INAT_MAKE_GROUP(18), + [0xa8] = INAT_FORCE64, + [0xa9] = INAT_FORCE64, + [0xab] = INAT_MODRM, + [0xac] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0xad] = INAT_MODRM, + [0xae] = INAT_MAKE_GROUP(19), + [0xaf] = INAT_MODRM, + [0xb0] = INAT_MODRM, + [0xb1] = INAT_MODRM, + [0xb2] = INAT_MODRM, + [0xb3] = INAT_MODRM, + [0xb4] = INAT_MODRM, + [0xb5] = INAT_MODRM, + [0xb6] = INAT_MODRM, + [0xb7] = INAT_MODRM, + [0xb8] = INAT_VARIANT, + [0xb9] = INAT_MAKE_GROUP(20), + [0xba] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(21), + [0xbb] = INAT_MODRM, + [0xbc] = INAT_MODRM | INAT_VARIANT, + [0xbd] = INAT_MODRM | INAT_VARIANT, + [0xbe] = INAT_MODRM, + [0xbf] = INAT_MODRM, + [0xc0] = INAT_MODRM, + [0xc1] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0xc3] = INAT_MODRM, + [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0xc7] = INAT_MAKE_GROUP(22), + [0xd0] = INAT_VARIANT, + [0xd1] = INAT_MODRM | INAT_VARIANT, + [0xd2] = INAT_MODRM | INAT_VARIANT, + [0xd3] = INAT_MODRM | INAT_VARIANT, + [0xd4] = INAT_MODRM | INAT_VARIANT, + [0xd5] = INAT_MODRM | INAT_VARIANT, + [0xd6] = INAT_VARIANT, + [0xd7] = INAT_MODRM | INAT_VARIANT, + [0xd8] = INAT_MODRM | INAT_VARIANT, + [0xd9] = INAT_MODRM | INAT_VARIANT, + [0xda] = INAT_MODRM | INAT_VARIANT, + [0xdb] = INAT_MODRM | INAT_VARIANT, + [0xdc] = INAT_MODRM | INAT_VARIANT, + [0xdd] = INAT_MODRM | INAT_VARIANT, + [0xde] = INAT_MODRM | INAT_VARIANT, + [0xdf] = INAT_MODRM | INAT_VARIANT, + [0xe0] = INAT_MODRM | INAT_VARIANT, + [0xe1] = INAT_MODRM | INAT_VARIANT, + [0xe2] = INAT_MODRM | INAT_VARIANT, + [0xe3] = INAT_MODRM | INAT_VARIANT, + [0xe4] = INAT_MODRM | INAT_VARIANT, + [0xe5] = INAT_MODRM | INAT_VARIANT, + [0xe6] = INAT_VARIANT, + [0xe7] = INAT_MODRM | INAT_VARIANT, + [0xe8] = INAT_MODRM | INAT_VARIANT, + [0xe9] = INAT_MODRM | INAT_VARIANT, + [0xea] = INAT_MODRM | INAT_VARIANT, + [0xeb] = INAT_MODRM | INAT_VARIANT, + [0xec] = INAT_MODRM | INAT_VARIANT, + [0xed] = INAT_MODRM | INAT_VARIANT, + [0xee] = INAT_MODRM | INAT_VARIANT, + [0xef] = INAT_MODRM | INAT_VARIANT, + [0xf0] = INAT_VARIANT, + [0xf1] = INAT_MODRM | INAT_VARIANT, + [0xf2] = INAT_MODRM | INAT_VARIANT, + [0xf3] = INAT_MODRM | INAT_VARIANT, + [0xf4] = INAT_MODRM | INAT_VARIANT, + [0xf5] = INAT_MODRM | INAT_VARIANT, + [0xf6] = INAT_MODRM | INAT_VARIANT, + [0xf7] = INAT_MODRM | INAT_VARIANT, + [0xf8] = INAT_MODRM | INAT_VARIANT, + [0xf9] = INAT_MODRM | INAT_VARIANT, + [0xfa] = INAT_MODRM | INAT_VARIANT, + [0xfb] = INAT_MODRM | INAT_VARIANT, + [0xfc] = INAT_MODRM | INAT_VARIANT, + [0xfd] = INAT_MODRM | INAT_VARIANT, + [0xfe] = INAT_MODRM | INAT_VARIANT, }; const insn_attr_t inat_escape_table_1_1[INAT_OPCODE_TABLE_SIZE] = { - [0x10] = INAT_MODRM | INAT_VEXOK, - [0x11] = INAT_MODRM | INAT_VEXOK, - [0x12] = INAT_MODRM | INAT_VEXOK, - [0x13] = INAT_MODRM | INAT_VEXOK, - [0x14] = INAT_MODRM | INAT_VEXOK, - [0x15] = INAT_MODRM | INAT_VEXOK, - [0x16] = INAT_MODRM | INAT_VEXOK, - [0x17] = INAT_MODRM | INAT_VEXOK, - [0x28] = INAT_MODRM | INAT_VEXOK, - [0x29] = INAT_MODRM | INAT_VEXOK, - [0x2a] = INAT_MODRM, - [0x2b] = INAT_MODRM | INAT_VEXOK, - [0x2c] = INAT_MODRM, - [0x2d] = INAT_MODRM, - [0x2e] = INAT_MODRM | INAT_VEXOK, - [0x2f] = INAT_MODRM | INAT_VEXOK, - [0x50] = INAT_MODRM | INAT_VEXOK, - [0x51] = INAT_MODRM | INAT_VEXOK, - [0x54] = INAT_MODRM | INAT_VEXOK, - [0x55] = INAT_MODRM | INAT_VEXOK, - [0x56] = INAT_MODRM | INAT_VEXOK, - [0x57] = INAT_MODRM | INAT_VEXOK, - [0x58] = INAT_MODRM | INAT_VEXOK, - [0x59] = INAT_MODRM | INAT_VEXOK, - [0x5a] = INAT_MODRM | INAT_VEXOK, - [0x5b] = INAT_MODRM | INAT_VEXOK, - [0x5c] = INAT_MODRM | INAT_VEXOK, - [0x5d] = INAT_MODRM | INAT_VEXOK, - [0x5e] = INAT_MODRM | INAT_VEXOK, - [0x5f] = INAT_MODRM | INAT_VEXOK, - [0x60] = INAT_MODRM | INAT_VEXOK, - [0x61] = INAT_MODRM | INAT_VEXOK, - [0x62] = INAT_MODRM | INAT_VEXOK, - [0x63] = INAT_MODRM | INAT_VEXOK, - [0x64] = INAT_MODRM | INAT_VEXOK, - [0x65] = INAT_MODRM | INAT_VEXOK, - [0x66] = INAT_MODRM | INAT_VEXOK, - [0x67] = INAT_MODRM | INAT_VEXOK, - [0x68] = INAT_MODRM | INAT_VEXOK, - [0x69] = INAT_MODRM | INAT_VEXOK, - [0x6a] = INAT_MODRM | INAT_VEXOK, - [0x6b] = INAT_MODRM | INAT_VEXOK, - [0x6c] = INAT_MODRM | INAT_VEXOK, - [0x6d] = INAT_MODRM | INAT_VEXOK, - [0x6e] = INAT_MODRM | INAT_VEXOK, - [0x6f] = INAT_MODRM | INAT_VEXOK, - [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x74] = INAT_MODRM | INAT_VEXOK, - [0x75] = INAT_MODRM | INAT_VEXOK, - [0x76] = INAT_MODRM | INAT_VEXOK, - [0x7c] = INAT_MODRM | INAT_VEXOK, - [0x7d] = INAT_MODRM | INAT_VEXOK, - [0x7e] = INAT_MODRM | INAT_VEXOK, - [0x7f] = INAT_MODRM | INAT_VEXOK, - [0xbc] = INAT_MODRM, - [0xbd] = INAT_MODRM, - [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0xd0] = INAT_MODRM | INAT_VEXOK, - [0xd1] = INAT_MODRM | INAT_VEXOK, - [0xd2] = INAT_MODRM | INAT_VEXOK, - [0xd3] = INAT_MODRM | INAT_VEXOK, - [0xd4] = INAT_MODRM | INAT_VEXOK, - [0xd5] = INAT_MODRM | INAT_VEXOK, - [0xd6] = INAT_MODRM | INAT_VEXOK, - [0xd7] = INAT_MODRM | INAT_VEXOK, - [0xd8] = INAT_MODRM | INAT_VEXOK, - [0xd9] = INAT_MODRM | INAT_VEXOK, - [0xda] = INAT_MODRM | INAT_VEXOK, - [0xdb] = INAT_MODRM | INAT_VEXOK, - [0xdc] = INAT_MODRM | INAT_VEXOK, - [0xdd] = INAT_MODRM | INAT_VEXOK, - [0xde] = INAT_MODRM | INAT_VEXOK, - [0xdf] = INAT_MODRM | INAT_VEXOK, - [0xe0] = INAT_MODRM | INAT_VEXOK, - [0xe1] = INAT_MODRM | INAT_VEXOK, - [0xe2] = INAT_MODRM | INAT_VEXOK, - [0xe3] = INAT_MODRM | INAT_VEXOK, - [0xe4] = INAT_MODRM | INAT_VEXOK, - [0xe5] = INAT_MODRM | INAT_VEXOK, - [0xe6] = INAT_MODRM | INAT_VEXOK, - [0xe7] = INAT_MODRM | INAT_VEXOK, - [0xe8] = INAT_MODRM | INAT_VEXOK, - [0xe9] = INAT_MODRM | INAT_VEXOK, - [0xea] = INAT_MODRM | INAT_VEXOK, - [0xeb] = INAT_MODRM | INAT_VEXOK, - [0xec] = INAT_MODRM | INAT_VEXOK, - [0xed] = INAT_MODRM | INAT_VEXOK, - [0xee] = INAT_MODRM | INAT_VEXOK, - [0xef] = INAT_MODRM | INAT_VEXOK, - [0xf1] = INAT_MODRM | INAT_VEXOK, - [0xf2] = INAT_MODRM | INAT_VEXOK, - [0xf3] = INAT_MODRM | INAT_VEXOK, - [0xf4] = INAT_MODRM | INAT_VEXOK, - [0xf5] = INAT_MODRM | INAT_VEXOK, - [0xf6] = INAT_MODRM | INAT_VEXOK, - [0xf7] = INAT_MODRM | INAT_VEXOK, - [0xf8] = INAT_MODRM | INAT_VEXOK, - [0xf9] = INAT_MODRM | INAT_VEXOK, - [0xfa] = INAT_MODRM | INAT_VEXOK, - [0xfb] = INAT_MODRM | INAT_VEXOK, - [0xfc] = INAT_MODRM | INAT_VEXOK, - [0xfd] = INAT_MODRM | INAT_VEXOK, - [0xfe] = INAT_MODRM | INAT_VEXOK, + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x13] = INAT_MODRM | INAT_VEXOK, + [0x14] = INAT_MODRM | INAT_VEXOK, + [0x15] = INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MODRM | INAT_VEXOK, + [0x17] = INAT_MODRM | INAT_VEXOK, + [0x28] = INAT_MODRM | INAT_VEXOK, + [0x29] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM, + [0x2b] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM, + [0x2d] = INAT_MODRM, + [0x2e] = INAT_MODRM | INAT_VEXOK, + [0x2f] = INAT_MODRM | INAT_VEXOK, + [0x50] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x54] = INAT_MODRM | INAT_VEXOK, + [0x55] = INAT_MODRM | INAT_VEXOK, + [0x56] = INAT_MODRM | INAT_VEXOK, + [0x57] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5b] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x60] = INAT_MODRM | INAT_VEXOK, + [0x61] = INAT_MODRM | INAT_VEXOK, + [0x62] = INAT_MODRM | INAT_VEXOK, + [0x63] = INAT_MODRM | INAT_VEXOK, + [0x64] = INAT_MODRM | INAT_VEXOK, + [0x65] = INAT_MODRM | INAT_VEXOK, + [0x66] = INAT_MODRM | INAT_VEXOK, + [0x67] = INAT_MODRM | INAT_VEXOK, + [0x68] = INAT_MODRM | INAT_VEXOK, + [0x69] = INAT_MODRM | INAT_VEXOK, + [0x6a] = INAT_MODRM | INAT_VEXOK, + [0x6b] = INAT_MODRM | INAT_VEXOK, + [0x6c] = INAT_MODRM | INAT_VEXOK, + [0x6d] = INAT_MODRM | INAT_VEXOK, + [0x6e] = INAT_MODRM | INAT_VEXOK, + [0x6f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x74] = INAT_MODRM | INAT_VEXOK, + [0x75] = INAT_MODRM | INAT_VEXOK, + [0x76] = INAT_MODRM | INAT_VEXOK, + [0x7c] = INAT_MODRM | INAT_VEXOK, + [0x7d] = INAT_MODRM | INAT_VEXOK, + [0x7e] = INAT_MODRM | INAT_VEXOK, + [0x7f] = INAT_MODRM | INAT_VEXOK, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd0] = INAT_MODRM | INAT_VEXOK, + [0xd1] = INAT_MODRM | INAT_VEXOK, + [0xd2] = INAT_MODRM | INAT_VEXOK, + [0xd3] = INAT_MODRM | INAT_VEXOK, + [0xd4] = INAT_MODRM | INAT_VEXOK, + [0xd5] = INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM | INAT_VEXOK, + [0xd7] = INAT_MODRM | INAT_VEXOK, + [0xd8] = INAT_MODRM | INAT_VEXOK, + [0xd9] = INAT_MODRM | INAT_VEXOK, + [0xda] = INAT_MODRM | INAT_VEXOK, + [0xdb] = INAT_MODRM | INAT_VEXOK, + [0xdc] = INAT_MODRM | INAT_VEXOK, + [0xdd] = INAT_MODRM | INAT_VEXOK, + [0xde] = INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MODRM | INAT_VEXOK, + [0xe0] = INAT_MODRM | INAT_VEXOK, + [0xe1] = INAT_MODRM | INAT_VEXOK, + [0xe2] = INAT_MODRM | INAT_VEXOK, + [0xe3] = INAT_MODRM | INAT_VEXOK, + [0xe4] = INAT_MODRM | INAT_VEXOK, + [0xe5] = INAT_MODRM | INAT_VEXOK, + [0xe6] = INAT_MODRM | INAT_VEXOK, + [0xe7] = INAT_MODRM | INAT_VEXOK, + [0xe8] = INAT_MODRM | INAT_VEXOK, + [0xe9] = INAT_MODRM | INAT_VEXOK, + [0xea] = INAT_MODRM | INAT_VEXOK, + [0xeb] = INAT_MODRM | INAT_VEXOK, + [0xec] = INAT_MODRM | INAT_VEXOK, + [0xed] = INAT_MODRM | INAT_VEXOK, + [0xee] = INAT_MODRM | INAT_VEXOK, + [0xef] = INAT_MODRM | INAT_VEXOK, + [0xf1] = INAT_MODRM | INAT_VEXOK, + [0xf2] = INAT_MODRM | INAT_VEXOK, + [0xf3] = INAT_MODRM | INAT_VEXOK, + [0xf4] = INAT_MODRM | INAT_VEXOK, + [0xf5] = INAT_MODRM | INAT_VEXOK, + [0xf6] = INAT_MODRM | INAT_VEXOK, + [0xf7] = INAT_MODRM | INAT_VEXOK, + [0xf8] = INAT_MODRM | INAT_VEXOK, + [0xf9] = INAT_MODRM | INAT_VEXOK, + [0xfa] = INAT_MODRM | INAT_VEXOK, + [0xfb] = INAT_MODRM | INAT_VEXOK, + [0xfc] = INAT_MODRM | INAT_VEXOK, + [0xfd] = INAT_MODRM | INAT_VEXOK, + [0xfe] = INAT_MODRM | INAT_VEXOK, }; const insn_attr_t inat_escape_table_1_2[INAT_OPCODE_TABLE_SIZE] = { - [0x10] = INAT_MODRM | INAT_VEXOK, - [0x11] = INAT_MODRM | INAT_VEXOK, - [0x12] = INAT_MODRM | INAT_VEXOK, - [0x16] = INAT_MODRM | INAT_VEXOK, - [0x2a] = INAT_MODRM | INAT_VEXOK, - [0x2c] = INAT_MODRM | INAT_VEXOK, - [0x2d] = INAT_MODRM | INAT_VEXOK, - [0x51] = INAT_MODRM | INAT_VEXOK, - [0x52] = INAT_MODRM | INAT_VEXOK, - [0x53] = INAT_MODRM | INAT_VEXOK, - [0x58] = INAT_MODRM | INAT_VEXOK, - [0x59] = INAT_MODRM | INAT_VEXOK, - [0x5a] = INAT_MODRM | INAT_VEXOK, - [0x5b] = INAT_MODRM | INAT_VEXOK, - [0x5c] = INAT_MODRM | INAT_VEXOK, - [0x5d] = INAT_MODRM | INAT_VEXOK, - [0x5e] = INAT_MODRM | INAT_VEXOK, - [0x5f] = INAT_MODRM | INAT_VEXOK, - [0x6f] = INAT_MODRM | INAT_VEXOK, - [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x7e] = INAT_MODRM | INAT_VEXOK, - [0x7f] = INAT_MODRM | INAT_VEXOK, - [0xb8] = INAT_MODRM, - [0xbc] = INAT_MODRM, - [0xbd] = INAT_MODRM, - [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0xd6] = INAT_MODRM, - [0xe6] = INAT_MODRM | INAT_VEXOK, + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK, + [0x2d] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x52] = INAT_MODRM | INAT_VEXOK, + [0x53] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5b] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x6f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7e] = INAT_MODRM | INAT_VEXOK, + [0x7f] = INAT_MODRM | INAT_VEXOK, + [0xb8] = INAT_MODRM, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM, + [0xe6] = INAT_MODRM | INAT_VEXOK, }; const insn_attr_t inat_escape_table_1_3[INAT_OPCODE_TABLE_SIZE] = { - [0x10] = INAT_MODRM | INAT_VEXOK, - [0x11] = INAT_MODRM | INAT_VEXOK, - [0x12] = INAT_MODRM | INAT_VEXOK, - [0x2a] = INAT_MODRM | INAT_VEXOK, - [0x2c] = INAT_MODRM | INAT_VEXOK, - [0x2d] = INAT_MODRM | INAT_VEXOK, - [0x51] = INAT_MODRM | INAT_VEXOK, - [0x58] = INAT_MODRM | INAT_VEXOK, - [0x59] = INAT_MODRM | INAT_VEXOK, - [0x5a] = INAT_MODRM | INAT_VEXOK, - [0x5c] = INAT_MODRM | INAT_VEXOK, - [0x5d] = INAT_MODRM | INAT_VEXOK, - [0x5e] = INAT_MODRM | INAT_VEXOK, - [0x5f] = INAT_MODRM | INAT_VEXOK, - [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x7c] = INAT_MODRM | INAT_VEXOK, - [0x7d] = INAT_MODRM | INAT_VEXOK, - [0xbc] = INAT_MODRM, - [0xbd] = INAT_MODRM, - [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0xd0] = INAT_MODRM | INAT_VEXOK, - [0xd6] = INAT_MODRM, - [0xe6] = INAT_MODRM | INAT_VEXOK, - [0xf0] = INAT_MODRM | INAT_VEXOK, + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK, + [0x2d] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7c] = INAT_MODRM | INAT_VEXOK, + [0x7d] = INAT_MODRM | INAT_VEXOK, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd0] = INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM, + [0xe6] = INAT_MODRM | INAT_VEXOK, + [0xf0] = INAT_MODRM | INAT_VEXOK, }; /* Table: 3-byte opcode 1 (0x0f 0x38) */ const insn_attr_t inat_escape_table_2[INAT_OPCODE_TABLE_SIZE] = { - [0x00] = INAT_MODRM | INAT_VARIANT, - [0x01] = INAT_MODRM | INAT_VARIANT, - [0x02] = INAT_MODRM | INAT_VARIANT, - [0x03] = INAT_MODRM | INAT_VARIANT, - [0x04] = INAT_MODRM | INAT_VARIANT, - [0x05] = INAT_MODRM | INAT_VARIANT, - [0x06] = INAT_MODRM | INAT_VARIANT, - [0x07] = INAT_MODRM | INAT_VARIANT, - [0x08] = INAT_MODRM | INAT_VARIANT, - [0x09] = INAT_MODRM | INAT_VARIANT, - [0x0a] = INAT_MODRM | INAT_VARIANT, - [0x0b] = INAT_MODRM | INAT_VARIANT, - [0x0c] = INAT_VARIANT, - [0x0d] = INAT_VARIANT, - [0x0e] = INAT_VARIANT, - [0x0f] = INAT_VARIANT, - [0x10] = INAT_VARIANT, - [0x13] = INAT_VARIANT, - [0x14] = INAT_VARIANT, - [0x15] = INAT_VARIANT, - [0x16] = INAT_VARIANT, - [0x17] = INAT_VARIANT, - [0x18] = INAT_VARIANT, - [0x19] = INAT_VARIANT, - [0x1a] = INAT_VARIANT, - [0x1c] = INAT_MODRM | INAT_VARIANT, - [0x1d] = INAT_MODRM | INAT_VARIANT, - [0x1e] = INAT_MODRM | INAT_VARIANT, - [0x20] = INAT_VARIANT, - [0x21] = INAT_VARIANT, - [0x22] = INAT_VARIANT, - [0x23] = INAT_VARIANT, - [0x24] = INAT_VARIANT, - [0x25] = INAT_VARIANT, - [0x28] = INAT_VARIANT, - [0x29] = INAT_VARIANT, - [0x2a] = INAT_VARIANT, - [0x2b] = INAT_VARIANT, - [0x2c] = INAT_VARIANT, - [0x2d] = INAT_VARIANT, - [0x2e] = INAT_VARIANT, - [0x2f] = INAT_VARIANT, - [0x30] = INAT_VARIANT, - [0x31] = INAT_VARIANT, - [0x32] = INAT_VARIANT, - [0x33] = INAT_VARIANT, - [0x34] = INAT_VARIANT, - [0x35] = INAT_VARIANT, - [0x36] = INAT_VARIANT, - [0x37] = INAT_VARIANT, - [0x38] = INAT_VARIANT, - [0x39] = INAT_VARIANT, - [0x3a] = INAT_VARIANT, - [0x3b] = INAT_VARIANT, - [0x3c] = INAT_VARIANT, - [0x3d] = INAT_VARIANT, - [0x3e] = INAT_VARIANT, - [0x3f] = INAT_VARIANT, - [0x40] = INAT_VARIANT, - [0x41] = INAT_VARIANT, - [0x45] = INAT_VARIANT, - [0x46] = INAT_VARIANT, - [0x47] = INAT_VARIANT, - [0x58] = INAT_VARIANT, - [0x59] = INAT_VARIANT, - [0x5a] = INAT_VARIANT, - [0x78] = INAT_VARIANT, - [0x79] = INAT_VARIANT, - [0x80] = INAT_VARIANT, - [0x81] = INAT_VARIANT, - [0x82] = INAT_VARIANT, - [0x8c] = INAT_VARIANT, - [0x8e] = INAT_VARIANT, - [0x90] = INAT_VARIANT, - [0x91] = INAT_VARIANT, - [0x92] = INAT_VARIANT, - [0x93] = INAT_VARIANT, - [0x96] = INAT_VARIANT, - [0x97] = INAT_VARIANT, - [0x98] = INAT_VARIANT, - [0x99] = INAT_VARIANT, - [0x9a] = INAT_VARIANT, - [0x9b] = INAT_VARIANT, - [0x9c] = INAT_VARIANT, - [0x9d] = INAT_VARIANT, - [0x9e] = INAT_VARIANT, - [0x9f] = INAT_VARIANT, - [0xa6] = INAT_VARIANT, - [0xa7] = INAT_VARIANT, - [0xa8] = INAT_VARIANT, - [0xa9] = INAT_VARIANT, - [0xaa] = INAT_VARIANT, - [0xab] = INAT_VARIANT, - [0xac] = INAT_VARIANT, - [0xad] = INAT_VARIANT, - [0xae] = INAT_VARIANT, - [0xaf] = INAT_VARIANT, - [0xb6] = INAT_VARIANT, - [0xb7] = INAT_VARIANT, - [0xb8] = INAT_VARIANT, - [0xb9] = INAT_VARIANT, - [0xba] = INAT_VARIANT, - [0xbb] = INAT_VARIANT, - [0xbc] = INAT_VARIANT, - [0xbd] = INAT_VARIANT, - [0xbe] = INAT_VARIANT, - [0xbf] = INAT_VARIANT, - [0xdb] = INAT_VARIANT, - [0xdc] = INAT_VARIANT, - [0xdd] = INAT_VARIANT, - [0xde] = INAT_VARIANT, - [0xdf] = INAT_VARIANT, - [0xf0] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, - [0xf1] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, - [0xf2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xf3] = INAT_MAKE_GROUP(23), - [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, - [0xf6] = INAT_VARIANT, - [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, + [0x00] = INAT_MODRM | INAT_VARIANT, + [0x01] = INAT_MODRM | INAT_VARIANT, + [0x02] = INAT_MODRM | INAT_VARIANT, + [0x03] = INAT_MODRM | INAT_VARIANT, + [0x04] = INAT_MODRM | INAT_VARIANT, + [0x05] = INAT_MODRM | INAT_VARIANT, + [0x06] = INAT_MODRM | INAT_VARIANT, + [0x07] = INAT_MODRM | INAT_VARIANT, + [0x08] = INAT_MODRM | INAT_VARIANT, + [0x09] = INAT_MODRM | INAT_VARIANT, + [0x0a] = INAT_MODRM | INAT_VARIANT, + [0x0b] = INAT_MODRM | INAT_VARIANT, + [0x0c] = INAT_VARIANT, + [0x0d] = INAT_VARIANT, + [0x0e] = INAT_VARIANT, + [0x0f] = INAT_VARIANT, + [0x10] = INAT_VARIANT, + [0x13] = INAT_VARIANT, + [0x14] = INAT_VARIANT, + [0x15] = INAT_VARIANT, + [0x16] = INAT_VARIANT, + [0x17] = INAT_VARIANT, + [0x18] = INAT_VARIANT, + [0x19] = INAT_VARIANT, + [0x1a] = INAT_VARIANT, + [0x1c] = INAT_MODRM | INAT_VARIANT, + [0x1d] = INAT_MODRM | INAT_VARIANT, + [0x1e] = INAT_MODRM | INAT_VARIANT, + [0x20] = INAT_VARIANT, + [0x21] = INAT_VARIANT, + [0x22] = INAT_VARIANT, + [0x23] = INAT_VARIANT, + [0x24] = INAT_VARIANT, + [0x25] = INAT_VARIANT, + [0x28] = INAT_VARIANT, + [0x29] = INAT_VARIANT, + [0x2a] = INAT_VARIANT, + [0x2b] = INAT_VARIANT, + [0x2c] = INAT_VARIANT, + [0x2d] = INAT_VARIANT, + [0x2e] = INAT_VARIANT, + [0x2f] = INAT_VARIANT, + [0x30] = INAT_VARIANT, + [0x31] = INAT_VARIANT, + [0x32] = INAT_VARIANT, + [0x33] = INAT_VARIANT, + [0x34] = INAT_VARIANT, + [0x35] = INAT_VARIANT, + [0x36] = INAT_VARIANT, + [0x37] = INAT_VARIANT, + [0x38] = INAT_VARIANT, + [0x39] = INAT_VARIANT, + [0x3a] = INAT_VARIANT, + [0x3b] = INAT_VARIANT, + [0x3c] = INAT_VARIANT, + [0x3d] = INAT_VARIANT, + [0x3e] = INAT_VARIANT, + [0x3f] = INAT_VARIANT, + [0x40] = INAT_VARIANT, + [0x41] = INAT_VARIANT, + [0x45] = INAT_VARIANT, + [0x46] = INAT_VARIANT, + [0x47] = INAT_VARIANT, + [0x58] = INAT_VARIANT, + [0x59] = INAT_VARIANT, + [0x5a] = INAT_VARIANT, + [0x78] = INAT_VARIANT, + [0x79] = INAT_VARIANT, + [0x80] = INAT_VARIANT, + [0x81] = INAT_VARIANT, + [0x82] = INAT_VARIANT, + [0x8c] = INAT_VARIANT, + [0x8e] = INAT_VARIANT, + [0x90] = INAT_VARIANT, + [0x91] = INAT_VARIANT, + [0x92] = INAT_VARIANT, + [0x93] = INAT_VARIANT, + [0x96] = INAT_VARIANT, + [0x97] = INAT_VARIANT, + [0x98] = INAT_VARIANT, + [0x99] = INAT_VARIANT, + [0x9a] = INAT_VARIANT, + [0x9b] = INAT_VARIANT, + [0x9c] = INAT_VARIANT, + [0x9d] = INAT_VARIANT, + [0x9e] = INAT_VARIANT, + [0x9f] = INAT_VARIANT, + [0xa6] = INAT_VARIANT, + [0xa7] = INAT_VARIANT, + [0xa8] = INAT_VARIANT, + [0xa9] = INAT_VARIANT, + [0xaa] = INAT_VARIANT, + [0xab] = INAT_VARIANT, + [0xac] = INAT_VARIANT, + [0xad] = INAT_VARIANT, + [0xae] = INAT_VARIANT, + [0xaf] = INAT_VARIANT, + [0xb6] = INAT_VARIANT, + [0xb7] = INAT_VARIANT, + [0xb8] = INAT_VARIANT, + [0xb9] = INAT_VARIANT, + [0xba] = INAT_VARIANT, + [0xbb] = INAT_VARIANT, + [0xbc] = INAT_VARIANT, + [0xbd] = INAT_VARIANT, + [0xbe] = INAT_VARIANT, + [0xbf] = INAT_VARIANT, + [0xdb] = INAT_VARIANT, + [0xdc] = INAT_VARIANT, + [0xdd] = INAT_VARIANT, + [0xde] = INAT_VARIANT, + [0xdf] = INAT_VARIANT, + [0xf0] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0xf1] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0xf2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf3] = INAT_MAKE_GROUP(23), + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, + [0xf6] = INAT_VARIANT, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, }; const insn_attr_t inat_escape_table_2_1[INAT_OPCODE_TABLE_SIZE] = { - [0x00] = INAT_MODRM | INAT_VEXOK, - [0x01] = INAT_MODRM | INAT_VEXOK, - [0x02] = INAT_MODRM | INAT_VEXOK, - [0x03] = INAT_MODRM | INAT_VEXOK, - [0x04] = INAT_MODRM | INAT_VEXOK, - [0x05] = INAT_MODRM | INAT_VEXOK, - [0x06] = INAT_MODRM | INAT_VEXOK, - [0x07] = INAT_MODRM | INAT_VEXOK, - [0x08] = INAT_MODRM | INAT_VEXOK, - [0x09] = INAT_MODRM | INAT_VEXOK, - [0x0a] = INAT_MODRM | INAT_VEXOK, - [0x0b] = INAT_MODRM | INAT_VEXOK, - [0x0c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x0d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x0e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x0f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x10] = INAT_MODRM, - [0x13] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x14] = INAT_MODRM, - [0x15] = INAT_MODRM, - [0x16] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x17] = INAT_MODRM | INAT_VEXOK, - [0x18] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x19] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x1a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x1c] = INAT_MODRM | INAT_VEXOK, - [0x1d] = INAT_MODRM | INAT_VEXOK, - [0x1e] = INAT_MODRM | INAT_VEXOK, - [0x20] = INAT_MODRM | INAT_VEXOK, - [0x21] = INAT_MODRM | INAT_VEXOK, - [0x22] = INAT_MODRM | INAT_VEXOK, - [0x23] = INAT_MODRM | INAT_VEXOK, - [0x24] = INAT_MODRM | INAT_VEXOK, - [0x25] = INAT_MODRM | INAT_VEXOK, - [0x28] = INAT_MODRM | INAT_VEXOK, - [0x29] = INAT_MODRM | INAT_VEXOK, - [0x2a] = INAT_MODRM | INAT_VEXOK, - [0x2b] = INAT_MODRM | INAT_VEXOK, - [0x2c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x2d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x30] = INAT_MODRM | INAT_VEXOK, - [0x31] = INAT_MODRM | INAT_VEXOK, - [0x32] = INAT_MODRM | INAT_VEXOK, - [0x33] = INAT_MODRM | INAT_VEXOK, - [0x34] = INAT_MODRM | INAT_VEXOK, - [0x35] = INAT_MODRM | INAT_VEXOK, - [0x36] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x37] = INAT_MODRM | INAT_VEXOK, - [0x38] = INAT_MODRM | INAT_VEXOK, - [0x39] = INAT_MODRM | INAT_VEXOK, - [0x3a] = INAT_MODRM | INAT_VEXOK, - [0x3b] = INAT_MODRM | INAT_VEXOK, - [0x3c] = INAT_MODRM | INAT_VEXOK, - [0x3d] = INAT_MODRM | INAT_VEXOK, - [0x3e] = INAT_MODRM | INAT_VEXOK, - [0x3f] = INAT_MODRM | INAT_VEXOK, - [0x40] = INAT_MODRM | INAT_VEXOK, - [0x41] = INAT_MODRM | INAT_VEXOK, - [0x45] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x46] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x47] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x78] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x79] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x80] = INAT_MODRM, - [0x81] = INAT_MODRM, - [0x82] = INAT_MODRM, - [0x8c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x8e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x90] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x91] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x92] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x93] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x96] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x97] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x98] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x99] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x9a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x9b] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x9c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x9d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x9e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x9f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xa6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xa7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xa8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xa9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xaa] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xab] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xac] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xad] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xae] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xaf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xb6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xb7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xb8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xb9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xba] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xbb] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xbc] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xbd] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xbe] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xbf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xdb] = INAT_MODRM | INAT_VEXOK, - [0xdc] = INAT_MODRM | INAT_VEXOK, - [0xdd] = INAT_MODRM | INAT_VEXOK, - [0xde] = INAT_MODRM | INAT_VEXOK, - [0xdf] = INAT_MODRM | INAT_VEXOK, - [0xf0] = INAT_MODRM, - [0xf1] = INAT_MODRM, - [0xf6] = INAT_MODRM, - [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x00] = INAT_MODRM | INAT_VEXOK, + [0x01] = INAT_MODRM | INAT_VEXOK, + [0x02] = INAT_MODRM | INAT_VEXOK, + [0x03] = INAT_MODRM | INAT_VEXOK, + [0x04] = INAT_MODRM | INAT_VEXOK, + [0x05] = INAT_MODRM | INAT_VEXOK, + [0x06] = INAT_MODRM | INAT_VEXOK, + [0x07] = INAT_MODRM | INAT_VEXOK, + [0x08] = INAT_MODRM | INAT_VEXOK, + [0x09] = INAT_MODRM | INAT_VEXOK, + [0x0a] = INAT_MODRM | INAT_VEXOK, + [0x0b] = INAT_MODRM | INAT_VEXOK, + [0x0c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x10] = INAT_MODRM, + [0x13] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x14] = INAT_MODRM, + [0x15] = INAT_MODRM, + [0x16] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x17] = INAT_MODRM | INAT_VEXOK, + [0x18] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x19] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1c] = INAT_MODRM | INAT_VEXOK, + [0x1d] = INAT_MODRM | INAT_VEXOK, + [0x1e] = INAT_MODRM | INAT_VEXOK, + [0x20] = INAT_MODRM | INAT_VEXOK, + [0x21] = INAT_MODRM | INAT_VEXOK, + [0x22] = INAT_MODRM | INAT_VEXOK, + [0x23] = INAT_MODRM | INAT_VEXOK, + [0x24] = INAT_MODRM | INAT_VEXOK, + [0x25] = INAT_MODRM | INAT_VEXOK, + [0x28] = INAT_MODRM | INAT_VEXOK, + [0x29] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2b] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x30] = INAT_MODRM | INAT_VEXOK, + [0x31] = INAT_MODRM | INAT_VEXOK, + [0x32] = INAT_MODRM | INAT_VEXOK, + [0x33] = INAT_MODRM | INAT_VEXOK, + [0x34] = INAT_MODRM | INAT_VEXOK, + [0x35] = INAT_MODRM | INAT_VEXOK, + [0x36] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x37] = INAT_MODRM | INAT_VEXOK, + [0x38] = INAT_MODRM | INAT_VEXOK, + [0x39] = INAT_MODRM | INAT_VEXOK, + [0x3a] = INAT_MODRM | INAT_VEXOK, + [0x3b] = INAT_MODRM | INAT_VEXOK, + [0x3c] = INAT_MODRM | INAT_VEXOK, + [0x3d] = INAT_MODRM | INAT_VEXOK, + [0x3e] = INAT_MODRM | INAT_VEXOK, + [0x3f] = INAT_MODRM | INAT_VEXOK, + [0x40] = INAT_MODRM | INAT_VEXOK, + [0x41] = INAT_MODRM | INAT_VEXOK, + [0x45] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x46] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x47] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x78] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x79] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x80] = INAT_MODRM, + [0x81] = INAT_MODRM, + [0x82] = INAT_MODRM, + [0x8c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x8e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x90] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x91] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x92] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x93] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x96] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x97] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x98] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x99] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9b] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xaa] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xab] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xac] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xad] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xae] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xaf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xba] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbb] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbc] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbd] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbe] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xdb] = INAT_MODRM | INAT_VEXOK, + [0xdc] = INAT_MODRM | INAT_VEXOK, + [0xdd] = INAT_MODRM | INAT_VEXOK, + [0xde] = INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MODRM | INAT_VEXOK, + [0xf0] = INAT_MODRM, + [0xf1] = INAT_MODRM, + [0xf6] = INAT_MODRM, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, }; const insn_attr_t inat_escape_table_2_2[INAT_OPCODE_TABLE_SIZE] = { - [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xf6] = INAT_MODRM, - [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf6] = INAT_MODRM, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, }; const insn_attr_t inat_escape_table_2_3[INAT_OPCODE_TABLE_SIZE] = { - [0xf0] = INAT_MODRM | INAT_MODRM, - [0xf1] = INAT_MODRM | INAT_MODRM, - [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xf6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf0] = INAT_MODRM | INAT_MODRM, + [0xf1] = INAT_MODRM | INAT_MODRM, + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, }; /* Table: 3-byte opcode 2 (0x0f 0x3a) */ const insn_attr_t inat_escape_table_3[INAT_OPCODE_TABLE_SIZE] = { - [0x00] = INAT_VARIANT, - [0x01] = INAT_VARIANT, - [0x02] = INAT_VARIANT, - [0x04] = INAT_VARIANT, - [0x05] = INAT_VARIANT, - [0x06] = INAT_VARIANT, - [0x08] = INAT_VARIANT, - [0x09] = INAT_VARIANT, - [0x0a] = INAT_VARIANT, - [0x0b] = INAT_VARIANT, - [0x0c] = INAT_VARIANT, - [0x0d] = INAT_VARIANT, - [0x0e] = INAT_VARIANT, - [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x14] = INAT_VARIANT, - [0x15] = INAT_VARIANT, - [0x16] = INAT_VARIANT, - [0x17] = INAT_VARIANT, - [0x18] = INAT_VARIANT, - [0x19] = INAT_VARIANT, - [0x1d] = INAT_VARIANT, - [0x20] = INAT_VARIANT, - [0x21] = INAT_VARIANT, - [0x22] = INAT_VARIANT, - [0x38] = INAT_VARIANT, - [0x39] = INAT_VARIANT, - [0x40] = INAT_VARIANT, - [0x41] = INAT_VARIANT, - [0x42] = INAT_VARIANT, - [0x44] = INAT_VARIANT, - [0x46] = INAT_VARIANT, - [0x4a] = INAT_VARIANT, - [0x4b] = INAT_VARIANT, - [0x4c] = INAT_VARIANT, - [0x60] = INAT_VARIANT, - [0x61] = INAT_VARIANT, - [0x62] = INAT_VARIANT, - [0x63] = INAT_VARIANT, - [0xdf] = INAT_VARIANT, - [0xf0] = INAT_VARIANT, + [0x00] = INAT_VARIANT, + [0x01] = INAT_VARIANT, + [0x02] = INAT_VARIANT, + [0x04] = INAT_VARIANT, + [0x05] = INAT_VARIANT, + [0x06] = INAT_VARIANT, + [0x08] = INAT_VARIANT, + [0x09] = INAT_VARIANT, + [0x0a] = INAT_VARIANT, + [0x0b] = INAT_VARIANT, + [0x0c] = INAT_VARIANT, + [0x0d] = INAT_VARIANT, + [0x0e] = INAT_VARIANT, + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x14] = INAT_VARIANT, + [0x15] = INAT_VARIANT, + [0x16] = INAT_VARIANT, + [0x17] = INAT_VARIANT, + [0x18] = INAT_VARIANT, + [0x19] = INAT_VARIANT, + [0x1d] = INAT_VARIANT, + [0x20] = INAT_VARIANT, + [0x21] = INAT_VARIANT, + [0x22] = INAT_VARIANT, + [0x38] = INAT_VARIANT, + [0x39] = INAT_VARIANT, + [0x40] = INAT_VARIANT, + [0x41] = INAT_VARIANT, + [0x42] = INAT_VARIANT, + [0x44] = INAT_VARIANT, + [0x46] = INAT_VARIANT, + [0x4a] = INAT_VARIANT, + [0x4b] = INAT_VARIANT, + [0x4c] = INAT_VARIANT, + [0x60] = INAT_VARIANT, + [0x61] = INAT_VARIANT, + [0x62] = INAT_VARIANT, + [0x63] = INAT_VARIANT, + [0xdf] = INAT_VARIANT, + [0xf0] = INAT_VARIANT, }; const insn_attr_t inat_escape_table_3_1[INAT_OPCODE_TABLE_SIZE] = { - [0x00] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x01] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x02] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x05] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x06] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x08] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x09] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x0a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x0b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x0d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x0e] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x15] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x16] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x17] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x18] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x19] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x1d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x20] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x21] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x22] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x38] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x39] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x40] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x41] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x42] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x44] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x46] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x4a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x4b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x4c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x60] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x61] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x62] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x63] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0xdf] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x00] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x01] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x02] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x05] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x06] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x08] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x09] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0e] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x15] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x17] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x18] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x19] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x20] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x21] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x22] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x38] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x39] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x40] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x41] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x42] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x44] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x46] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x60] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x61] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x62] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x63] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, }; const insn_attr_t inat_escape_table_3_3[INAT_OPCODE_TABLE_SIZE] = { - [0xf0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, }; /* GrpTable: Grp1 */ @@ -937,159 +937,159 @@ const insn_attr_t inat_escape_table_3_3[INAT_OPCODE_TABLE_SIZE] = { /* GrpTable: Grp3_1 */ const insn_attr_t inat_group_table_6[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, - [0x2] = INAT_MODRM, - [0x3] = INAT_MODRM, - [0x4] = INAT_MODRM, - [0x5] = INAT_MODRM, - [0x6] = INAT_MODRM, - [0x7] = INAT_MODRM, + [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, }; /* GrpTable: Grp3_2 */ const insn_attr_t inat_group_table_7[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, - [0x2] = INAT_MODRM, - [0x3] = INAT_MODRM, - [0x4] = INAT_MODRM, - [0x5] = INAT_MODRM, - [0x6] = INAT_MODRM, - [0x7] = INAT_MODRM, + [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, }; /* GrpTable: Grp4 */ const insn_attr_t inat_group_table_8[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MODRM, - [0x1] = INAT_MODRM, + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, }; /* GrpTable: Grp5 */ const insn_attr_t inat_group_table_9[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MODRM, - [0x1] = INAT_MODRM, - [0x2] = INAT_MODRM | INAT_FORCE64, - [0x3] = INAT_MODRM, - [0x4] = INAT_MODRM | INAT_FORCE64, - [0x5] = INAT_MODRM, - [0x6] = INAT_MODRM | INAT_FORCE64, + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM | INAT_FORCE64, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM | INAT_FORCE64, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM | INAT_FORCE64, }; /* GrpTable: Grp6 */ const insn_attr_t inat_group_table_10[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MODRM, - [0x1] = INAT_MODRM, - [0x2] = INAT_MODRM, - [0x3] = INAT_MODRM, - [0x4] = INAT_MODRM, - [0x5] = INAT_MODRM, + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, }; /* GrpTable: Grp7 */ const insn_attr_t inat_group_table_11[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MODRM, - [0x1] = INAT_MODRM, - [0x2] = INAT_MODRM, - [0x3] = INAT_MODRM, - [0x4] = INAT_MODRM, - [0x6] = INAT_MODRM, - [0x7] = INAT_MODRM, + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, }; /* GrpTable: Grp8 */ /* GrpTable: Grp9 */ const insn_attr_t inat_group_table_22[INAT_GROUP_TABLE_SIZE] = { - [0x1] = INAT_MODRM, - [0x6] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, - [0x7] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0x1] = INAT_MODRM, + [0x6] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0x7] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, }; const insn_attr_t inat_group_table_22_1[INAT_GROUP_TABLE_SIZE] = { - [0x6] = INAT_MODRM, + [0x6] = INAT_MODRM, }; const insn_attr_t inat_group_table_22_2[INAT_GROUP_TABLE_SIZE] = { - [0x6] = INAT_MODRM, - [0x7] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, }; /* GrpTable: Grp10 */ /* GrpTable: Grp11A */ const insn_attr_t inat_group_table_4[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, - [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE), }; /* GrpTable: Grp11B */ const insn_attr_t inat_group_table_5[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, - [0x7] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x7] = INAT_MAKE_IMM(INAT_IMM_VWORD32), }; /* GrpTable: Grp12 */ const insn_attr_t inat_group_table_14[INAT_GROUP_TABLE_SIZE] = { - [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, }; const insn_attr_t inat_group_table_14_1[INAT_GROUP_TABLE_SIZE] = { - [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, }; /* GrpTable: Grp13 */ const insn_attr_t inat_group_table_15[INAT_GROUP_TABLE_SIZE] = { - [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, }; const insn_attr_t inat_group_table_15_1[INAT_GROUP_TABLE_SIZE] = { - [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, }; /* GrpTable: Grp14 */ const insn_attr_t inat_group_table_16[INAT_GROUP_TABLE_SIZE] = { - [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x3] = INAT_VARIANT, - [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, - [0x7] = INAT_VARIANT, + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x3] = INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x7] = INAT_VARIANT, }; const insn_attr_t inat_group_table_16_1[INAT_GROUP_TABLE_SIZE] = { - [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, - [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, }; /* GrpTable: Grp15 */ const insn_attr_t inat_group_table_19[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_VARIANT, - [0x1] = INAT_VARIANT, - [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, - [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x0] = INAT_VARIANT, + [0x1] = INAT_VARIANT, + [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, }; const insn_attr_t inat_group_table_19_2[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MODRM, - [0x1] = INAT_MODRM, - [0x2] = INAT_MODRM, - [0x3] = INAT_MODRM, + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, }; /* GrpTable: Grp16 */ const insn_attr_t inat_group_table_13[INAT_GROUP_TABLE_SIZE] = { - [0x0] = INAT_MODRM, - [0x1] = INAT_MODRM, - [0x2] = INAT_MODRM, - [0x3] = INAT_MODRM, + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, }; /* GrpTable: Grp17 */ const insn_attr_t inat_group_table_23[INAT_GROUP_TABLE_SIZE] = { - [0x1] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, - [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, }; /* GrpTable: GrpP */ @@ -1100,57 +1100,57 @@ const insn_attr_t inat_group_table_23[INAT_GROUP_TABLE_SIZE] = { /* Escape opcode map array */ const insn_attr_t * const inat_escape_tables[INAT_ESC_MAX + 1][INAT_LSTPFX_MAX + 1] = { - [1][0] = inat_escape_table_1, - [1][1] = inat_escape_table_1_1, - [1][2] = inat_escape_table_1_2, - [1][3] = inat_escape_table_1_3, - [2][0] = inat_escape_table_2, - [2][1] = inat_escape_table_2_1, - [2][2] = inat_escape_table_2_2, - [2][3] = inat_escape_table_2_3, - [3][0] = inat_escape_table_3, - [3][1] = inat_escape_table_3_1, - [3][3] = inat_escape_table_3_3, + [1][0] = inat_escape_table_1, + [1][1] = inat_escape_table_1_1, + [1][2] = inat_escape_table_1_2, + [1][3] = inat_escape_table_1_3, + [2][0] = inat_escape_table_2, + [2][1] = inat_escape_table_2_1, + [2][2] = inat_escape_table_2_2, + [2][3] = inat_escape_table_2_3, + [3][0] = inat_escape_table_3, + [3][1] = inat_escape_table_3_1, + [3][3] = inat_escape_table_3_3, }; /* Group opcode map array */ const insn_attr_t * const inat_group_tables[INAT_GRP_MAX + 1][INAT_LSTPFX_MAX + 1] = { - [4][0] = inat_group_table_4, - [5][0] = inat_group_table_5, - [6][0] = inat_group_table_6, - [7][0] = inat_group_table_7, - [8][0] = inat_group_table_8, - [9][0] = inat_group_table_9, - [10][0] = inat_group_table_10, - [11][0] = inat_group_table_11, - [13][0] = inat_group_table_13, - [14][0] = inat_group_table_14, - [14][1] = inat_group_table_14_1, - [15][0] = inat_group_table_15, - [15][1] = inat_group_table_15_1, - [16][0] = inat_group_table_16, - [16][1] = inat_group_table_16_1, - [19][0] = inat_group_table_19, - [19][2] = inat_group_table_19_2, - [22][0] = inat_group_table_22, - [22][1] = inat_group_table_22_1, - [22][2] = inat_group_table_22_2, - [23][0] = inat_group_table_23, + [4][0] = inat_group_table_4, + [5][0] = inat_group_table_5, + [6][0] = inat_group_table_6, + [7][0] = inat_group_table_7, + [8][0] = inat_group_table_8, + [9][0] = inat_group_table_9, + [10][0] = inat_group_table_10, + [11][0] = inat_group_table_11, + [13][0] = inat_group_table_13, + [14][0] = inat_group_table_14, + [14][1] = inat_group_table_14_1, + [15][0] = inat_group_table_15, + [15][1] = inat_group_table_15_1, + [16][0] = inat_group_table_16, + [16][1] = inat_group_table_16_1, + [19][0] = inat_group_table_19, + [19][2] = inat_group_table_19_2, + [22][0] = inat_group_table_22, + [22][1] = inat_group_table_22_1, + [22][2] = inat_group_table_22_2, + [23][0] = inat_group_table_23, }; /* AVX opcode map array */ const insn_attr_t * const inat_avx_tables[X86_VEX_M_MAX + 1][INAT_LSTPFX_MAX + 1] = { - [1][0] = inat_escape_table_1, - [1][1] = inat_escape_table_1_1, - [1][2] = inat_escape_table_1_2, - [1][3] = inat_escape_table_1_3, - [2][0] = inat_escape_table_2, - [2][1] = inat_escape_table_2_1, - [2][2] = inat_escape_table_2_2, - [2][3] = inat_escape_table_2_3, - [3][0] = inat_escape_table_3, - [3][1] = inat_escape_table_3_1, - [3][3] = inat_escape_table_3_3, + [1][0] = inat_escape_table_1, + [1][1] = inat_escape_table_1_1, + [1][2] = inat_escape_table_1_2, + [1][3] = inat_escape_table_1_3, + [2][0] = inat_escape_table_2, + [2][1] = inat_escape_table_2_1, + [2][2] = inat_escape_table_2_2, + [2][3] = inat_escape_table_2_3, + [3][0] = inat_escape_table_3, + [3][1] = inat_escape_table_3_1, + [3][3] = inat_escape_table_3_3, }; #endif diff --git a/upatch-diff/insn/asm/inat.h b/upatch-diff/insn/asm/inat.h index 95811f39c2eccb4c340d0d9c3f492b64f2eaa91a..887ddfe019e2108ae9059e1d027719eeb646de90 100644 --- a/upatch-diff/insn/asm/inat.h +++ b/upatch-diff/insn/asm/inat.h @@ -33,191 +33,189 @@ #define INAT_GROUP_TABLE_SIZE 8 /* Legacy last prefixes */ -#define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */ -#define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */ -#define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */ +#define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */ +#define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */ +#define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */ /* Other Legacy prefixes */ -#define INAT_PFX_LOCK 4 /* 0xF0 */ -#define INAT_PFX_CS 5 /* 0x2E */ -#define INAT_PFX_DS 6 /* 0x3E */ -#define INAT_PFX_ES 7 /* 0x26 */ -#define INAT_PFX_FS 8 /* 0x64 */ -#define INAT_PFX_GS 9 /* 0x65 */ -#define INAT_PFX_SS 10 /* 0x36 */ -#define INAT_PFX_ADDRSZ 11 /* 0x67 */ +#define INAT_PFX_LOCK 4 /* 0xF0 */ +#define INAT_PFX_CS 5 /* 0x2E */ +#define INAT_PFX_DS 6 /* 0x3E */ +#define INAT_PFX_ES 7 /* 0x26 */ +#define INAT_PFX_FS 8 /* 0x64 */ +#define INAT_PFX_GS 9 /* 0x65 */ +#define INAT_PFX_SS 10 /* 0x36 */ +#define INAT_PFX_ADDRSZ 11 /* 0x67 */ /* x86-64 REX prefix */ -#define INAT_PFX_REX 12 /* 0x4X */ +#define INAT_PFX_REX 12 /* 0x4X */ /* AVX VEX prefixes */ -#define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */ -#define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */ +#define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */ +#define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */ -#define INAT_LSTPFX_MAX 3 -#define INAT_LGCPFX_MAX 11 +#define INAT_LSTPFX_MAX 3 +#define INAT_LGCPFX_MAX 11 /* Immediate size */ -#define INAT_IMM_BYTE 1 -#define INAT_IMM_WORD 2 -#define INAT_IMM_DWORD 3 -#define INAT_IMM_QWORD 4 -#define INAT_IMM_PTR 5 -#define INAT_IMM_VWORD32 6 -#define INAT_IMM_VWORD 7 +#define INAT_IMM_BYTE 1 +#define INAT_IMM_WORD 2 +#define INAT_IMM_DWORD 3 +#define INAT_IMM_QWORD 4 +#define INAT_IMM_PTR 5 +#define INAT_IMM_VWORD32 6 +#define INAT_IMM_VWORD 7 /* Legacy prefix */ -#define INAT_PFX_OFFS 0 -#define INAT_PFX_BITS 4 -#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1) -#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS) +#define INAT_PFX_OFFS 0 +#define INAT_PFX_BITS 4 +#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1) +#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS) /* Escape opcodes */ -#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS) -#define INAT_ESC_BITS 2 -#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1) -#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS) +#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS) +#define INAT_ESC_BITS 2 +#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1) +#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS) /* Group opcodes (1-16) */ -#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS) -#define INAT_GRP_BITS 5 -#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1) -#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS) +#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS) +#define INAT_GRP_BITS 5 +#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1) +#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS) /* Immediates */ -#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS) -#define INAT_IMM_BITS 3 -#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS) +#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS) +#define INAT_IMM_BITS 3 +#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS) /* Flags */ -#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS) -#define INAT_MODRM (1 << (INAT_FLAG_OFFS)) -#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1)) -#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2)) -#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3)) -#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4)) -#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5)) -#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6)) +#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS) +#define INAT_MODRM (1 << (INAT_FLAG_OFFS)) +#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1)) +#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2)) +#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3)) +#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4)) +#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5)) +#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6)) /* Attribute making macros for attribute tables */ -#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) -#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) -#define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM) -#define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS) +#define INAT_MAKE_PREFIX(pfx) ((pfx) << INAT_PFX_OFFS) +#define INAT_MAKE_ESCAPE(esc) ((esc) << INAT_ESC_OFFS) +#define INAT_MAKE_GROUP(grp) (((grp) << INAT_GRP_OFFS) | INAT_MODRM) +#define INAT_MAKE_IMM(imm) ((imm) << INAT_IMM_OFFS) /* Attribute search APIs */ extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode); extern int inat_get_last_prefix_id(insn_byte_t last_pfx); extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, - int lpfx_id, - insn_attr_t esc_attr); + int lpfx_id, insn_attr_t esc_attr); extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm, - int lpfx_id, - insn_attr_t esc_attr); + int lpfx_id, insn_attr_t esc_attr); extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, - insn_byte_t vex_m, - insn_byte_t vex_pp); + insn_byte_t vex_m, insn_byte_t vex_pp); /* Attribute checking functions */ static inline int inat_is_legacy_prefix(insn_attr_t attr) { - attr &= INAT_PFX_MASK; - return attr && attr <= INAT_LGCPFX_MAX; + attr &= INAT_PFX_MASK; + return attr && attr <= INAT_LGCPFX_MAX; } static inline int inat_is_address_size_prefix(insn_attr_t attr) { - return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ; + return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ; } static inline int inat_is_operand_size_prefix(insn_attr_t attr) { - return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ; + return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ; } static inline int inat_is_rex_prefix(insn_attr_t attr) { - return (attr & INAT_PFX_MASK) == INAT_PFX_REX; + return (attr & INAT_PFX_MASK) == INAT_PFX_REX; } static inline int inat_last_prefix_id(insn_attr_t attr) { - if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX) - return 0; - else - return attr & INAT_PFX_MASK; + if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX) { + return 0; + } else { + return attr & INAT_PFX_MASK; + } } static inline int inat_is_vex_prefix(insn_attr_t attr) { - attr &= INAT_PFX_MASK; - return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3; + attr &= INAT_PFX_MASK; + return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3; } static inline int inat_is_vex3_prefix(insn_attr_t attr) { - return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3; + return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3; } static inline int inat_is_escape(insn_attr_t attr) { - return attr & INAT_ESC_MASK; + return attr & INAT_ESC_MASK; } -static inline int inat_escape_id(insn_attr_t attr) +static inline unsigned int inat_escape_id(insn_attr_t attr) { - return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS; + return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS; } static inline int inat_is_group(insn_attr_t attr) { - return attr & INAT_GRP_MASK; + return attr & INAT_GRP_MASK; } -static inline int inat_group_id(insn_attr_t attr) +static inline unsigned int inat_group_id(insn_attr_t attr) { - return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS; + return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS; } -static inline int inat_group_common_attribute(insn_attr_t attr) +static inline insn_attr_t inat_group_common_attribute(insn_attr_t attr) { - return attr & ~INAT_GRP_MASK; + return attr & ~(insn_attr_t)INAT_GRP_MASK; } static inline int inat_has_immediate(insn_attr_t attr) { - return attr & INAT_IMM_MASK; + return attr & INAT_IMM_MASK; } -static inline int inat_immediate_size(insn_attr_t attr) +static inline unsigned int inat_immediate_size(insn_attr_t attr) { - return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS; + return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS; } static inline int inat_has_modrm(insn_attr_t attr) { - return attr & INAT_MODRM; + return attr & INAT_MODRM; } static inline int inat_is_force64(insn_attr_t attr) { - return attr & INAT_FORCE64; + return attr & INAT_FORCE64; } static inline int inat_has_second_immediate(insn_attr_t attr) { - return attr & INAT_SCNDIMM; + return attr & INAT_SCNDIMM; } static inline int inat_has_moffset(insn_attr_t attr) { - return attr & INAT_MOFFSET; + return attr & INAT_MOFFSET; } static inline int inat_has_variant(insn_attr_t attr) { - return attr & INAT_VARIANT; + return attr & INAT_VARIANT; } static inline int inat_accept_vex(insn_attr_t attr) { - return attr & INAT_VEXOK; + return attr & INAT_VEXOK; } static inline int inat_must_vex(insn_attr_t attr) { - return attr & INAT_VEXONLY; + return attr & INAT_VEXONLY; } -#endif \ No newline at end of file +#endif diff --git a/upatch-diff/insn/asm/insn.h b/upatch-diff/insn/asm/insn.h index 041b351de089909a1e995f28b4f044534d6ce1f2..09cb33d6892f062d306dcf9f80f96b8edece1c34 100644 --- a/upatch-diff/insn/asm/insn.h +++ b/upatch-diff/insn/asm/insn.h @@ -25,79 +25,79 @@ #include struct insn_field { - union { - insn_value_t value; - insn_byte_t bytes[4]; - }; - /* !0 if we've run insn_get_xxx() for this field */ - unsigned char got; - unsigned char nbytes; + union { + insn_value_t value; + insn_byte_t bytes[4]; + }; + /* !0 if we've run insn_get_xxx() for this field */ + unsigned char got; + unsigned char nbytes; }; struct insn { - struct insn_field prefixes; /* - * Prefixes - * prefixes.bytes[3]: last prefix - */ - struct insn_field rex_prefix; /* REX prefix */ - struct insn_field vex_prefix; /* VEX prefix */ - struct insn_field opcode; /* - * opcode.bytes[0]: opcode1 - * opcode.bytes[1]: opcode2 - * opcode.bytes[2]: opcode3 - */ - struct insn_field modrm; - struct insn_field sib; - struct insn_field displacement; - union { - struct insn_field immediate; - struct insn_field moffset1; /* for 64bit MOV */ - struct insn_field immediate1; /* for 64bit imm or off16/32 */ - }; - union { - struct insn_field moffset2; /* for 64bit MOV */ - struct insn_field immediate2; /* for 64bit imm or seg16 */ - }; - - insn_attr_t attr; - unsigned char opnd_bytes; - unsigned char addr_bytes; - unsigned char length; - unsigned char x86_64; - - const insn_byte_t *kaddr; /* kernel address of insn to analyze */ - const insn_byte_t *next_byte; + struct insn_field prefixes; /* + * Prefixes + * prefixes.bytes[3]: last prefix + */ + struct insn_field rex_prefix; /* REX prefix */ + struct insn_field vex_prefix; /* VEX prefix */ + struct insn_field opcode; /* + * opcode.bytes[0]: opcode1 + * opcode.bytes[1]: opcode2 + * opcode.bytes[2]: opcode3 + */ + struct insn_field modrm; + struct insn_field sib; + struct insn_field displacement; + union { + struct insn_field immediate; + struct insn_field moffset1; /* for 64bit MOV */ + struct insn_field immediate1; /* for 64bit imm or off16/32 */ + }; + union { + struct insn_field moffset2; /* for 64bit MOV */ + struct insn_field immediate2; /* for 64bit imm or seg16 */ + }; + + insn_attr_t attr; + unsigned char opnd_bytes; + unsigned char addr_bytes; + unsigned char length; + unsigned char x86_64; + + const insn_byte_t *kaddr; /* kernel address of insn to analyze */ + insn_byte_t *next_byte; }; -#define MAX_INSN_SIZE 16 +#define MAX_INSN_SIZE 16 #define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6) #define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3) -#define X86_MODRM_RM(modrm) ((modrm) & 0x07) +#define X86_MODRM_RM(modrm) ((modrm) & 0x07) #define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6) #define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3) -#define X86_SIB_BASE(sib) ((sib) & 0x07) +#define X86_SIB_BASE(sib) ((sib) & 0x07) -#define X86_REX_W(rex) ((rex) & 8) -#define X86_REX_R(rex) ((rex) & 4) -#define X86_REX_X(rex) ((rex) & 2) -#define X86_REX_B(rex) ((rex) & 1) +#define X86_REX_W(rex) ((rex) & 8) +#define X86_REX_R(rex) ((rex) & 4) +#define X86_REX_X(rex) ((rex) & 2) +#define X86_REX_B(rex) ((rex) & 1) /* VEX bit flags */ -#define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */ -#define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */ -#define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */ -#define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */ -#define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */ +#define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */ +#define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */ +#define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */ +#define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */ +#define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */ /* VEX bit fields */ -#define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */ -#define X86_VEX2_M 1 /* VEX2.M always 1 */ -#define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */ -#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */ -#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */ +#define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */ +#define X86_VEX2_M 1 /* VEX2.M always 1 */ +#define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */ +#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */ +#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */ -extern void insn_init(struct insn *insn, const void *kaddr, int x86_64); +extern void insn_init(struct insn *insn, void *kaddr, int x86_64); extern void insn_get_prefixes(struct insn *insn); extern void insn_get_opcode(struct insn *insn); extern void insn_get_modrm(struct insn *insn); @@ -109,92 +109,105 @@ extern void insn_get_length(struct insn *insn); /* Attribute will be determined after getting ModRM (for opcode groups) */ static inline void insn_get_attribute(struct insn *insn) { - insn_get_modrm(insn); + insn_get_modrm(insn); } /* Instruction uses RIP-relative addressing */ extern int insn_rip_relative(struct insn *insn); /* Init insn for kernel text */ -static inline void kernel_insn_init(struct insn *insn, const void *kaddr) +static inline void kernel_insn_init(struct insn *insn, void *kaddr) { #ifdef CONFIG_X86_64 - insn_init(insn, kaddr, 1); + insn_init(insn, kaddr, 1); #else /* CONFIG_X86_32 */ - insn_init(insn, kaddr, 0); + insn_init(insn, kaddr, 0); #endif } static inline int insn_is_avx(struct insn *insn) { - if (!insn->prefixes.got) - insn_get_prefixes(insn); - return (insn->vex_prefix.value != 0); + if (!insn->prefixes.got) { + insn_get_prefixes(insn); + } + return (insn->vex_prefix.value != 0); } /* Ensure this instruction is decoded completely */ static inline int insn_complete(struct insn *insn) { - return insn->opcode.got && insn->modrm.got && insn->sib.got && - insn->displacement.got && insn->immediate.got; + return insn->opcode.got && insn->modrm.got && insn->sib.got && + insn->displacement.got && insn->immediate.got; } static inline insn_byte_t insn_vex_m_bits(struct insn *insn) { - if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ - return X86_VEX2_M; - else - return X86_VEX3_M(insn->vex_prefix.bytes[1]); + if (insn->vex_prefix.nbytes == 2) { + /* 2 bytes VEX */ + return X86_VEX2_M; + } else { + return X86_VEX3_M(insn->vex_prefix.bytes[1]); + } } static inline insn_byte_t insn_vex_p_bits(struct insn *insn) { - if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ - return X86_VEX_P(insn->vex_prefix.bytes[1]); - else - return X86_VEX_P(insn->vex_prefix.bytes[2]); + if (insn->vex_prefix.nbytes == 2) { + /* 2 bytes VEX */ + return X86_VEX_P(insn->vex_prefix.bytes[1]); + } else { + return X86_VEX_P(insn->vex_prefix.bytes[2]); + } } /* Get the last prefix id from last prefix or VEX prefix */ static inline int insn_last_prefix_id(struct insn *insn) { - if (insn_is_avx(insn)) - return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */ + if (insn_is_avx(insn)) { + return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */ + } - if (insn->prefixes.bytes[3]) - return inat_get_last_prefix_id(insn->prefixes.bytes[3]); + if (insn->prefixes.bytes[3]) { + return inat_get_last_prefix_id(insn->prefixes.bytes[3]); + } - return 0; + return 0; } /* Offset of each field from kaddr */ static inline int insn_offset_rex_prefix(struct insn *insn) { - return insn->prefixes.nbytes; + return insn->prefixes.nbytes; } + static inline int insn_offset_vex_prefix(struct insn *insn) { - return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes; + return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes; } + static inline int insn_offset_opcode(struct insn *insn) { - return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes; + return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes; } + static inline int insn_offset_modrm(struct insn *insn) { - return insn_offset_opcode(insn) + insn->opcode.nbytes; + return insn_offset_opcode(insn) + insn->opcode.nbytes; } + static inline int insn_offset_sib(struct insn *insn) { - return insn_offset_modrm(insn) + insn->modrm.nbytes; + return insn_offset_modrm(insn) + insn->modrm.nbytes; } + static inline int insn_offset_displacement(struct insn *insn) { - return insn_offset_sib(insn) + insn->sib.nbytes; + return insn_offset_sib(insn) + insn->sib.nbytes; } + static inline int insn_offset_immediate(struct insn *insn) { - return insn_offset_displacement(insn) + insn->displacement.nbytes; + return insn_offset_displacement(insn) + insn->displacement.nbytes; } -#endif /* _ASM_X86_INSN_H */ \ No newline at end of file +#endif /* _ASM_X86_INSN_H */ diff --git a/upatch-diff/insn/inat.c b/upatch-diff/insn/inat.c index 193583f0386dc7d16119a74bc382b00e88d85f5e..96b01158ded81c9db134d91e2ba32bedff900237 100644 --- a/upatch-diff/insn/inat.c +++ b/upatch-diff/insn/inat.c @@ -28,71 +28,78 @@ /* Attribute search APIs */ insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode) { - return inat_primary_table[opcode]; + return inat_primary_table[opcode]; } int inat_get_last_prefix_id(insn_byte_t last_pfx) { - insn_attr_t lpfx_attr; + insn_attr_t lpfx_attr; - lpfx_attr = inat_get_opcode_attribute(last_pfx); - return inat_last_prefix_id(lpfx_attr); + lpfx_attr = inat_get_opcode_attribute(last_pfx); + return inat_last_prefix_id(lpfx_attr); } insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id, - insn_attr_t esc_attr) + insn_attr_t esc_attr) { - const insn_attr_t *table; - int n; + const insn_attr_t *table; + unsigned int n; - n = inat_escape_id(esc_attr); + n = inat_escape_id(esc_attr); - table = inat_escape_tables[n][0]; - if (!table) - return 0; - if (inat_has_variant(table[opcode]) && lpfx_id) { - table = inat_escape_tables[n][lpfx_id]; - if (!table) - return 0; - } - return table[opcode]; + table = inat_escape_tables[n][0]; + if (!table) { + return 0; + } + if (inat_has_variant(table[opcode]) && lpfx_id) { + table = inat_escape_tables[n][lpfx_id]; + if (!table) { + return 0; + } + } + return table[opcode]; } insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id, - insn_attr_t grp_attr) + insn_attr_t grp_attr) { - const insn_attr_t *table; - int n; + const insn_attr_t *table; + unsigned int n; - n = inat_group_id(grp_attr); + n = inat_group_id(grp_attr); - table = inat_group_tables[n][0]; - if (!table) - return inat_group_common_attribute(grp_attr); - if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) { - table = inat_group_tables[n][lpfx_id]; - if (!table) - return inat_group_common_attribute(grp_attr); - } - return table[X86_MODRM_REG(modrm)] | - inat_group_common_attribute(grp_attr); + table = inat_group_tables[n][0]; + if (!table) { + return inat_group_common_attribute(grp_attr); + } + if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) { + table = inat_group_tables[n][lpfx_id]; + if (!table) { + return inat_group_common_attribute(grp_attr); + } + } + return table[X86_MODRM_REG(modrm)] | + inat_group_common_attribute(grp_attr); } insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m, - insn_byte_t vex_p) + insn_byte_t vex_p) { - const insn_attr_t *table; - if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX) - return 0; - /* At first, this checks the master table */ - table = inat_avx_tables[vex_m][0]; - if (!table) - return 0; - if (!inat_is_group(table[opcode]) && vex_p) { - /* If this is not a group, get attribute directly */ - table = inat_avx_tables[vex_m][vex_p]; - if (!table) - return 0; - } - return table[opcode]; -} \ No newline at end of file + const insn_attr_t *table; + if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX) { + return 0; + } + /* At first, this checks the master table */ + table = inat_avx_tables[vex_m][0]; + if (!table) { + return 0; + } + if (!inat_is_group(table[opcode]) && vex_p) { + /* If this is not a group, get attribute directly */ + table = inat_avx_tables[vex_m][vex_p]; + if (!table) { + return 0; + } + } + return table[opcode]; +} diff --git a/upatch-diff/insn/insn.c b/upatch-diff/insn/insn.c index 6dfca3267eb544cb10a8837d62f2ce706b2d4419..d38953c4aa4ad7fc0d2f75f3b48cfc10d6eafe79 100644 --- a/upatch-diff/insn/insn.c +++ b/upatch-diff/insn/insn.c @@ -26,45 +26,46 @@ #define unlikely(a) a /* Verify next sizeof(t) bytes can be on the same instruction */ -#define validate_next(t, insn, n) \ - ((insn)->next_byte + sizeof(t) + n - (insn)->kaddr <= MAX_INSN_SIZE) +#define validate_next(t, insn, n) \ + ((insn)->next_byte + sizeof(t) + (n) - (insn)->kaddr <= MAX_INSN_SIZE) -#define __get_next(t, insn) \ - ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) +#define next_type_of(t, insn) \ + ({ t r = *(t*)((insn)->next_byte); (insn)->next_byte += sizeof(t); r; }) -#define __peek_nbyte_next(t, insn, n) \ - ({ t r = *(t*)((insn)->next_byte + n); r; }) +#define peek_nbytes_of(t, insn, n) \ + ({ t r = *(t*)((insn)->next_byte + (n)); r; }) -#define get_next(t, insn) \ - ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); }) +#define get_next(t, insn) \ + ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; next_type_of(t, insn); }) -#define peek_nbyte_next(t, insn, n) \ - ({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); }) +#define peek_nbyte_next(t, insn, n) \ + ({ if (unlikely(!validate_next(t, insn, n))) goto err_out; peek_nbytes_of(t, insn, n); }) -#define peek_next(t, insn) peek_nbyte_next(t, insn, 0) +#define peek_next(t, insn) peek_nbyte_next(t, insn, 0) /** * insn_init() - initialize struct insn - * @insn: &struct insn to be initialized - * @kaddr: address (in kernel memory) of instruction (or copy thereof) - * @x86_64: !0 for 64-bit kernel or 64-bit app + * @insn: &struct insn to be initialized + * @kaddr: address (in kernel memory) of instruction (or copy thereof) + * @x86_64: !0 for 64-bit kernel or 64-bit app */ -void insn_init(struct insn *insn, const void *kaddr, int x86_64) +void insn_init(struct insn *insn, void *kaddr, int x86_64) { - memset(insn, 0, sizeof(*insn)); - insn->kaddr = kaddr; - insn->next_byte = kaddr; - insn->x86_64 = x86_64 ? 1 : 0; - insn->opnd_bytes = 4; - if (x86_64) - insn->addr_bytes = 8; - else - insn->addr_bytes = 4; + memset(insn, 0, sizeof(*insn)); + insn->kaddr = kaddr; + insn->next_byte = kaddr; + insn->x86_64 = x86_64 ? 1 : 0; + insn->opnd_bytes = 4; + if (x86_64) { + insn->addr_bytes = 8; + } else { + insn->addr_bytes = 4; + } } /** * insn_get_prefixes - scan x86 instruction prefix bytes - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * Populates the @insn->prefixes bitmap, and updates @insn->next_byte * to point to the (first) opcode. No effect if @insn->prefixes.got @@ -72,112 +73,124 @@ void insn_init(struct insn *insn, const void *kaddr, int x86_64) */ void insn_get_prefixes(struct insn *insn) { - struct insn_field *prefixes = &insn->prefixes; - insn_attr_t attr; - insn_byte_t b, lb; - int i, nb; - - if (prefixes->got) - return; - - nb = 0; - lb = 0; - b = peek_next(insn_byte_t, insn); - attr = inat_get_opcode_attribute(b); - while (inat_is_legacy_prefix(attr)) { - /* Skip if same prefix */ - for (i = 0; i < nb; i++) - if (prefixes->bytes[i] == b) - goto found; - if (nb == 4) - /* Invalid instruction */ - break; - prefixes->bytes[nb++] = b; - if (inat_is_address_size_prefix(attr)) { - /* address size switches 2/4 or 4/8 */ - if (insn->x86_64) - insn->addr_bytes ^= 12; - else - insn->addr_bytes ^= 6; - } else if (inat_is_operand_size_prefix(attr)) { - /* oprand size switches 2/4 */ - insn->opnd_bytes ^= 6; - } + struct insn_field *prefixes = &insn->prefixes; + insn_attr_t attr; + insn_byte_t b; + insn_byte_t lb; + int i; + int nb; + + if (prefixes->got) { + return; + } + + nb = 0; + lb = 0; + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + while (inat_is_legacy_prefix(attr)) { + /* Skip if same prefix */ + for (i = 0; i < nb; i++) { + if (prefixes->bytes[i] == b) { + goto found; + } + } + if (nb == 4) { + /* Invalid instruction */ + break; + } + prefixes->bytes[nb++] = b; + if (inat_is_address_size_prefix(attr)) { + /* address size switches 2/4 or 4/8 */ + if (insn->x86_64) { + insn->addr_bytes ^= 12; + } else { + insn->addr_bytes ^= 6; + } + } else if (inat_is_operand_size_prefix(attr)) { + /* oprand size switches 2/4 */ + insn->opnd_bytes ^= 6; + } found: - prefixes->nbytes++; - insn->next_byte++; - lb = b; - b = peek_next(insn_byte_t, insn); - attr = inat_get_opcode_attribute(b); - } - /* Set the last prefix */ - if (lb && lb != insn->prefixes.bytes[3]) { - if (unlikely(insn->prefixes.bytes[3])) { - /* Swap the last prefix */ - b = insn->prefixes.bytes[3]; - for (i = 0; i < nb; i++) - if (prefixes->bytes[i] == lb) - prefixes->bytes[i] = b; - } - insn->prefixes.bytes[3] = lb; - } - - /* Decode REX prefix */ - if (insn->x86_64) { - b = peek_next(insn_byte_t, insn); - attr = inat_get_opcode_attribute(b); - if (inat_is_rex_prefix(attr)) { - insn->rex_prefix.value = b; - insn->rex_prefix.nbytes = 1; - insn->next_byte++; - if (X86_REX_W(b)) - /* REX.W overrides opnd_size */ - insn->opnd_bytes = 8; - } - } - insn->rex_prefix.got = 1; - - /* Decode VEX prefix */ - b = peek_next(insn_byte_t, insn); - attr = inat_get_opcode_attribute(b); - if (inat_is_vex_prefix(attr)) { - insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1); - if (!insn->x86_64) { - /* - * In 32-bits mode, if the [7:6] bits (mod bits of - * ModRM) on the second byte are not 11b, it is - * LDS or LES. - */ - if (X86_MODRM_MOD(b2) != 3) - goto vex_end; - } - insn->vex_prefix.bytes[0] = b; - insn->vex_prefix.bytes[1] = b2; - if (inat_is_vex3_prefix(attr)) { - b2 = peek_nbyte_next(insn_byte_t, insn, 2); - insn->vex_prefix.bytes[2] = b2; - insn->vex_prefix.nbytes = 3; - insn->next_byte += 3; - if (insn->x86_64 && X86_VEX_W(b2)) - /* VEX.W overrides opnd_size */ - insn->opnd_bytes = 8; - } else { - insn->vex_prefix.nbytes = 2; - insn->next_byte += 2; - } - } + prefixes->nbytes++; + insn->next_byte++; + lb = b; + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + } + /* Set the last prefix */ + if (lb && lb != insn->prefixes.bytes[3]) { + if (unlikely(insn->prefixes.bytes[3])) { + /* Swap the last prefix */ + b = insn->prefixes.bytes[3]; + for (i = 0; i < nb; i++) { + if (prefixes->bytes[i] == lb) { + prefixes->bytes[i] = b; + } + } + } + insn->prefixes.bytes[3] = lb; + } + + /* Decode REX prefix */ + if (insn->x86_64) { + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + if (inat_is_rex_prefix(attr)) { + insn->rex_prefix.value = b; + insn->rex_prefix.nbytes = 1; + insn->next_byte++; + if (X86_REX_W(b)) { + /* REX.W overrides opnd_size */ + insn->opnd_bytes = 8; + } + } + } + insn->rex_prefix.got = 1; + + /* Decode VEX prefix */ + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + if (inat_is_vex_prefix(attr)) { + insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1); + if (!insn->x86_64) { + /* + * In 32-bits mode, if the [7:6] bits (mod bits of + * ModRM) on the second byte are not 11b, it is + * LDS or LES. + */ + if (X86_MODRM_MOD(b2) != 3) { + goto vex_end; + } + } + insn->vex_prefix.bytes[0] = b; + insn->vex_prefix.bytes[1] = b2; + if (inat_is_vex3_prefix(attr)) { + b2 = peek_nbyte_next(insn_byte_t, insn, 2); + insn->vex_prefix.bytes[2] = b2; + insn->vex_prefix.nbytes = 3; + insn->next_byte += 3; + if (insn->x86_64 && X86_VEX_W(b2)) { + /* VEX.W overrides opnd_size */ + insn->opnd_bytes = 8; + } + } else { + insn->vex_prefix.nbytes = 2; + insn->next_byte += 2; + } + } vex_end: - insn->vex_prefix.got = 1; + insn->vex_prefix.got = 1; - prefixes->got = 1; + prefixes->got = 1; err_out: - return; + return; } /** * insn_get_opcode - collect opcode(s) - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * Populates @insn->opcode, updates @insn->next_byte to point past the * opcode byte(s), and set @insn->attr (except for groups). @@ -187,50 +200,54 @@ err_out: */ void insn_get_opcode(struct insn *insn) { - struct insn_field *opcode = &insn->opcode; - insn_byte_t op; - int pfx_id; - if (opcode->got) - return; - if (!insn->prefixes.got) - insn_get_prefixes(insn); - - /* Get first opcode */ - op = get_next(insn_byte_t, insn); - opcode->bytes[0] = op; - opcode->nbytes = 1; - - /* Check if there is VEX prefix or not */ - if (insn_is_avx(insn)) { - insn_byte_t m, p; - m = insn_vex_m_bits(insn); - p = insn_vex_p_bits(insn); - insn->attr = inat_get_avx_attribute(op, m, p); - if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr)) - insn->attr = 0; /* This instruction is bad */ - goto end; /* VEX has only 1 byte for opcode */ - } - - insn->attr = inat_get_opcode_attribute(op); - while (inat_is_escape(insn->attr)) { - /* Get escaped opcode */ - op = get_next(insn_byte_t, insn); - opcode->bytes[opcode->nbytes++] = op; - pfx_id = insn_last_prefix_id(insn); - insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr); - } - if (inat_must_vex(insn->attr)) - insn->attr = 0; /* This instruction is bad */ + struct insn_field *opcode = &insn->opcode; + insn_byte_t op; + int pfx_id; + + if (opcode->got) { + return; + } + if (!insn->prefixes.got) { + insn_get_prefixes(insn); + } + + /* Get first opcode */ + op = get_next(insn_byte_t, insn); + opcode->bytes[0] = op; + opcode->nbytes = 1; + + /* Check if there is VEX prefix or not */ + if (insn_is_avx(insn)) { + insn_byte_t m = insn_vex_m_bits(insn); + insn_byte_t p = insn_vex_p_bits(insn); + insn->attr = inat_get_avx_attribute(op, m, p); + if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr)) { + insn->attr = 0; /* This instruction is bad */ + } + goto end; /* VEX has only 1 byte for opcode */ + } + + insn->attr = inat_get_opcode_attribute(op); + while (inat_is_escape(insn->attr)) { + /* Get escaped opcode */ + op = get_next(insn_byte_t, insn); + opcode->bytes[opcode->nbytes++] = op; + pfx_id = insn_last_prefix_id(insn); + insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr); + } + if (inat_must_vex(insn->attr)) { + insn->attr = 0; /* This instruction is bad */ + } end: - opcode->got = 1; + opcode->got = 1; err_out: - return; + return; } /** * insn_get_modrm - collect ModRM byte, if any - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * Populates @insn->modrm and updates @insn->next_byte to point past the * ModRM byte, if any. If necessary, first collects the preceding bytes @@ -238,90 +255,98 @@ err_out: */ void insn_get_modrm(struct insn *insn) { - struct insn_field *modrm = &insn->modrm; - insn_byte_t pfx_id, mod; - if (modrm->got) - return; - if (!insn->opcode.got) - insn_get_opcode(insn); - - if (inat_has_modrm(insn->attr)) { - mod = get_next(insn_byte_t, insn); - modrm->value = mod; - modrm->nbytes = 1; - if (inat_is_group(insn->attr)) { - pfx_id = insn_last_prefix_id(insn); - insn->attr = inat_get_group_attribute(mod, pfx_id, - insn->attr); - if (insn_is_avx(insn) && !inat_accept_vex(insn->attr)) - insn->attr = 0; /* This is bad */ - } - } - - if (insn->x86_64 && inat_is_force64(insn->attr)) - insn->opnd_bytes = 8; - modrm->got = 1; + struct insn_field *modrm = &insn->modrm; + insn_byte_t pfx_id; + insn_byte_t mod; + + if (modrm->got) { + return; + } + if (!insn->opcode.got) { + insn_get_opcode(insn); + } + + if (inat_has_modrm(insn->attr)) { + mod = get_next(insn_byte_t, insn); + modrm->value = mod; + modrm->nbytes = 1; + if (inat_is_group(insn->attr)) { + pfx_id = (insn_byte_t)insn_last_prefix_id(insn); + insn->attr = inat_get_group_attribute(mod, pfx_id, insn->attr); + if (insn_is_avx(insn) && !inat_accept_vex(insn->attr)) { + insn_get_opcode(insn); + } + } + } + + if (insn->x86_64 && inat_is_force64(insn->attr)) { + insn->opnd_bytes = 8; + } + modrm->got = 1; err_out: - return; + return; } /** * insn_rip_relative() - Does instruction use RIP-relative addressing mode? - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * If necessary, first collects the instruction up to and including the * ModRM byte. No effect if @insn->x86_64 is 0. */ int insn_rip_relative(struct insn *insn) { - struct insn_field *modrm = &insn->modrm; - - if (!insn->x86_64) - return 0; - if (!modrm->got) - insn_get_modrm(insn); - /* - * For rip-relative instructions, the mod field (top 2 bits) - * is zero and the r/m field (bottom 3 bits) is 0x5. - */ - return (modrm->nbytes && (modrm->value & 0xc7) == 0x5); + struct insn_field *modrm = &insn->modrm; + + if (!insn->x86_64) { + return 0; + } + if (!modrm->got) { + insn_get_modrm(insn); + } + /* + * For rip-relative instructions, the mod field (top 2 bits) + * is zero and the r/m field (bottom 3 bits) is 0x5. + */ + return (modrm->nbytes && (modrm->value & 0xc7) == 0x5); } /** * insn_get_sib() - Get the SIB byte of instruction - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * If necessary, first collects the instruction up to and including the * ModRM byte. */ void insn_get_sib(struct insn *insn) { - insn_byte_t modrm; - - if (insn->sib.got) - return; - if (!insn->modrm.got) - insn_get_modrm(insn); - if (insn->modrm.nbytes) { - modrm = (insn_byte_t)insn->modrm.value; - if (insn->addr_bytes != 2 && - X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) { - insn->sib.value = get_next(insn_byte_t, insn); - insn->sib.nbytes = 1; - } - } - insn->sib.got = 1; + insn_byte_t modrm; + + if (insn->sib.got) { + return; + } + if (!insn->modrm.got) { + insn_get_modrm(insn); + } + if (insn->modrm.nbytes) { + modrm = (insn_byte_t)insn->modrm.value; + if (insn->addr_bytes != 2 && + X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) { + insn->sib.value = get_next(insn_byte_t, insn); + insn->sib.nbytes = 1; + } + } + insn->sib.got = 1; err_out: - return; + return; } - /** * insn_get_displacement() - Get the displacement of instruction - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * If necessary, first collects the instruction up to and including the * SIB byte. @@ -329,169 +354,176 @@ err_out: */ void insn_get_displacement(struct insn *insn) { - insn_byte_t mod, rm, base; - - if (insn->displacement.got) - return; - if (!insn->sib.got) - insn_get_sib(insn); - if (insn->modrm.nbytes) { - /* - * Interpreting the modrm byte: - * mod = 00 - no displacement fields (exceptions below) - * mod = 01 - 1-byte displacement field - * mod = 10 - displacement field is 4 bytes, or 2 bytes if - * address size = 2 (0x67 prefix in 32-bit mode) - * mod = 11 - no memory operand - * - * If address size = 2... - * mod = 00, r/m = 110 - displacement field is 2 bytes - * - * If address size != 2... - * mod != 11, r/m = 100 - SIB byte exists - * mod = 00, SIB base = 101 - displacement field is 4 bytes - * mod = 00, r/m = 101 - rip-relative addressing, displacement - * field is 4 bytes - */ - mod = X86_MODRM_MOD(insn->modrm.value); - rm = X86_MODRM_RM(insn->modrm.value); - base = X86_SIB_BASE(insn->sib.value); - if (mod == 3) - goto out; - if (mod == 1) { - insn->displacement.value = get_next(char, insn); - insn->displacement.nbytes = 1; - } else if (insn->addr_bytes == 2) { - if ((mod == 0 && rm == 6) || mod == 2) { - insn->displacement.value = - get_next(short, insn); - insn->displacement.nbytes = 2; - } - } else { - if ((mod == 0 && rm == 5) || mod == 2 || - (mod == 0 && base == 5)) { - insn->displacement.value = get_next(int, insn); - insn->displacement.nbytes = 4; - } - } - } + insn_byte_t mod; + insn_byte_t rm; + insn_byte_t base; + + if (insn->displacement.got) { + return; + } + if (!insn->sib.got) { + insn_get_sib(insn); + } + if (insn->modrm.nbytes) { + /* + * Interpreting the modrm byte: + * mod = 00 - no displacement fields (exceptions below) + * mod = 01 - 1-byte displacement field + * mod = 10 - displacement field is 4 bytes, or 2 bytes if + * address size = 2 (0x67 prefix in 32-bit mode) + * mod = 11 - no memory operand + * + * If address size = 2... + * mod = 00, r/m = 110 - displacement field is 2 bytes + * + * If address size != 2... + * mod != 11, r/m = 100 - SIB byte exists + * mod = 00, SIB base = 101 - displacement field is 4 bytes + * mod = 00, r/m = 101 - rip-relative addressing, displacement + * field is 4 bytes + */ + mod = X86_MODRM_MOD(insn->modrm.value); + rm = X86_MODRM_RM(insn->modrm.value); + base = X86_SIB_BASE(insn->sib.value); + if (mod == 3) { + goto out; + } + if (mod == 1) { + insn->displacement.value = get_next(char, insn); + insn->displacement.nbytes = 1; + } else if (insn->addr_bytes == 2) { + if ((mod == 0 && rm == 6) || mod == 2) { + insn->displacement.value = + get_next(short, insn); + insn->displacement.nbytes = 2; + } + } else { + if ((mod == 0 && rm == 5) || mod == 2 || + (mod == 0 && base == 5)) { + insn->displacement.value = get_next(int, insn); + insn->displacement.nbytes = 4; + } + } + } out: - insn->displacement.got = 1; + insn->displacement.got = 1; err_out: - return; + return; } /* Decode moffset16/32/64. Return 0 if failed */ -static int __get_moffset(struct insn *insn) +static int get_moffset(struct insn *insn) { - switch (insn->addr_bytes) { - case 2: - insn->moffset1.value = get_next(short, insn); - insn->moffset1.nbytes = 2; - break; - case 4: - insn->moffset1.value = get_next(int, insn); - insn->moffset1.nbytes = 4; - break; - case 8: - insn->moffset1.value = get_next(int, insn); - insn->moffset1.nbytes = 4; - insn->moffset2.value = get_next(int, insn); - insn->moffset2.nbytes = 4; - break; - default: /* opnd_bytes must be modified manually */ - goto err_out; - } - insn->moffset1.got = insn->moffset2.got = 1; - - return 1; + switch (insn->addr_bytes) { + case 2: + insn->moffset1.value = get_next(short, insn); + insn->moffset1.nbytes = 2; + break; + case 4: + insn->moffset1.value = get_next(int, insn); + insn->moffset1.nbytes = 4; + break; + case 8: + insn->moffset1.value = get_next(int, insn); + insn->moffset1.nbytes = 4; + insn->moffset2.value = get_next(int, insn); + insn->moffset2.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->moffset1.got = insn->moffset2.got = 1; + + return 1; err_out: - return 0; + return 0; } /* Decode imm v32(Iz). Return 0 if failed */ -static int __get_immv32(struct insn *insn) +static int get_immv32(struct insn *insn) { - switch (insn->opnd_bytes) { - case 2: - insn->immediate.value = get_next(short, insn); - insn->immediate.nbytes = 2; - break; - case 4: - case 8: - insn->immediate.value = get_next(int, insn); - insn->immediate.nbytes = 4; - break; - default: /* opnd_bytes must be modified manually */ - goto err_out; - } - - return 1; + switch (insn->opnd_bytes) { + case 2: + insn->immediate.value = get_next(short, insn); + insn->immediate.nbytes = 2; + break; + case 4: + case 8: + insn->immediate.value = get_next(int, insn); + insn->immediate.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + + return 1; err_out: - return 0; + return 0; } /* Decode imm v64(Iv/Ov), Return 0 if failed */ -static int __get_immv(struct insn *insn) +static int get_immv(struct insn *insn) { - switch (insn->opnd_bytes) { - case 2: - insn->immediate1.value = get_next(short, insn); - insn->immediate1.nbytes = 2; - break; - case 4: - insn->immediate1.value = get_next(int, insn); - insn->immediate1.nbytes = 4; - break; - case 8: - insn->immediate1.value = get_next(int, insn); - insn->immediate1.nbytes = 4; - insn->immediate2.value = get_next(int, insn); - insn->immediate2.nbytes = 4; - break; - default: /* opnd_bytes must be modified manually */ - goto err_out; - } - insn->immediate1.got = insn->immediate2.got = 1; - - return 1; + switch (insn->opnd_bytes) { + case 2: + insn->immediate1.value = get_next(short, insn); + insn->immediate1.nbytes = 2; + break; + case 4: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + break; + case 8: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + insn->immediate2.value = get_next(int, insn); + insn->immediate2.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->immediate1.got = insn->immediate2.got = 1; + + return 1; + err_out: - return 0; + return 0; } /* Decode ptr16:16/32(Ap) */ -static int __get_immptr(struct insn *insn) +static int get_immptr(struct insn *insn) { - switch (insn->opnd_bytes) { - case 2: - insn->immediate1.value = get_next(short, insn); - insn->immediate1.nbytes = 2; - break; - case 4: - insn->immediate1.value = get_next(int, insn); - insn->immediate1.nbytes = 4; - break; - case 8: - /* ptr16:64 is not exist (no segment) */ - return 0; - default: /* opnd_bytes must be modified manually */ - goto err_out; - } - insn->immediate2.value = get_next(unsigned short, insn); - insn->immediate2.nbytes = 2; - insn->immediate1.got = insn->immediate2.got = 1; - - return 1; + switch (insn->opnd_bytes) { + case 2: + insn->immediate1.value = get_next(short, insn); + insn->immediate1.nbytes = 2; + break; + case 4: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + break; + case 8: + /* ptr16:64 is not exist (no segment) */ + return 0; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->immediate2.value = get_next(unsigned short, insn); + insn->immediate2.nbytes = 2; + insn->immediate1.got = insn->immediate2.got = 1; + + return 1; + err_out: - return 0; + return 0; } /** * insn_get_immediate() - Get the immediates of instruction - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * If necessary, first collects the instruction up to and including the * displacement bytes. @@ -500,80 +532,89 @@ err_out: */ void insn_get_immediate(struct insn *insn) { - if (insn->immediate.got) - return; - if (!insn->displacement.got) - insn_get_displacement(insn); - - if (inat_has_moffset(insn->attr)) { - if (!__get_moffset(insn)) - goto err_out; - goto done; - } - - if (!inat_has_immediate(insn->attr)) - /* no immediates */ - goto done; - - switch (inat_immediate_size(insn->attr)) { - case INAT_IMM_BYTE: - insn->immediate.value = get_next(char, insn); - insn->immediate.nbytes = 1; - break; - case INAT_IMM_WORD: - insn->immediate.value = get_next(short, insn); - insn->immediate.nbytes = 2; - break; - case INAT_IMM_DWORD: - insn->immediate.value = get_next(int, insn); - insn->immediate.nbytes = 4; - break; - case INAT_IMM_QWORD: - insn->immediate1.value = get_next(int, insn); - insn->immediate1.nbytes = 4; - insn->immediate2.value = get_next(int, insn); - insn->immediate2.nbytes = 4; - break; - case INAT_IMM_PTR: - if (!__get_immptr(insn)) - goto err_out; - break; - case INAT_IMM_VWORD32: - if (!__get_immv32(insn)) - goto err_out; - break; - case INAT_IMM_VWORD: - if (!__get_immv(insn)) - goto err_out; - break; - default: - /* Here, insn must have an immediate, but failed */ - goto err_out; - } - if (inat_has_second_immediate(insn->attr)) { - insn->immediate2.value = get_next(char, insn); - insn->immediate2.nbytes = 1; - } + if (insn->immediate.got) { + return; + } + if (!insn->displacement.got) { + insn_get_displacement(insn); + } + + if (inat_has_moffset(insn->attr)) { + if (!get_moffset(insn)) { + goto err_out; + } + goto done; + } + + if (!inat_has_immediate(insn->attr)) { + /* no immediates */ + goto done; + } + + switch (inat_immediate_size(insn->attr)) { + case INAT_IMM_BYTE: + insn->immediate.value = get_next(char, insn); + insn->immediate.nbytes = 1; + break; + case INAT_IMM_WORD: + insn->immediate.value = get_next(short, insn); + insn->immediate.nbytes = 2; + break; + case INAT_IMM_DWORD: + insn->immediate.value = get_next(int, insn); + insn->immediate.nbytes = 4; + break; + case INAT_IMM_QWORD: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + insn->immediate2.value = get_next(int, insn); + insn->immediate2.nbytes = 4; + break; + case INAT_IMM_PTR: + if (!get_immptr(insn)) { + goto err_out; + } + break; + case INAT_IMM_VWORD32: + if (!get_immv32(insn)) { + goto err_out; + } + break; + case INAT_IMM_VWORD: + if (!get_immv(insn)) { + goto err_out; + } + break; + default: + /* Here, insn must have an immediate, but failed */ + goto err_out; + } + if (inat_has_second_immediate(insn->attr)) { + insn->immediate2.value = get_next(char, insn); + insn->immediate2.nbytes = 1; + } done: - insn->immediate.got = 1; + insn->immediate.got = 1; err_out: - return; + return; } /** * insn_get_length() - Get the length of instruction - * @insn: &struct insn containing instruction + * @insn: &struct insn containing instruction * * If necessary, first collects the instruction up to and including the * immediates bytes. */ void insn_get_length(struct insn *insn) { - if (insn->length) - return; - if (!insn->immediate.got) - insn_get_immediate(insn); - insn->length = (unsigned char)((unsigned long)insn->next_byte - - (unsigned long)insn->kaddr); -} \ No newline at end of file + if (insn->length) { + return; + } + if (!insn->immediate.got) { + insn_get_immediate(insn); + } + insn->length = (unsigned char)((unsigned long)insn->next_byte - + (unsigned long)insn->kaddr); +} diff --git a/upatch-diff/list.h b/upatch-diff/list.h index 6205a72d26c5cfa4ecd87fa4f03cef51e2935346..0b091083370f1a374be9a935b7894321fc914ea1 100644 --- a/upatch-diff/list.h +++ b/upatch-diff/list.h @@ -28,18 +28,15 @@ #ifndef __UPATCH_LIST_H_ #define __UPATCH_LIST_H_ +#include + /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ -#define LIST_POISON1 ((void *) 0x00100100) -#define LIST_POISON2 ((void *) 0x00200200) - -/** - * Get offset of a member - */ -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) /** * Casts a member of a structure out to the containing structure @@ -48,9 +45,10 @@ * @param member the name of the member within the struct. * */ -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) +#define container_of(ptr, type, member) ({ \ + typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ +}) /** * Simple doubly linked list implementation. @@ -62,67 +60,66 @@ * using the generic single-entry routines. */ struct list_head { - struct list_head *next, *prev; + struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) + struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ - (ptr)->next = (ptr); (ptr)->prev = (ptr); \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) #define list_entry(ptr, type, member) \ - container_of(ptr, type, member) + container_of(ptr, type, member) /** * list_next_entry - get the next element in list - * @pos: the type * to cursor - * @member: the name of the list_struct within the struct. + * @pos: the type * to cursor + * @member: the name of the list_struct within the struct. */ #define list_next_entry(pos, member) \ - list_entry((pos)->member.next, typeof(*(pos)), member) + list_entry((pos)->member.next, typeof(*(pos)), member) /** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop counter. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) +#define list_for_each_entry(pos, head, member) \ + for ((pos) = list_entry((head)->next, typeof(*(pos)), member); \ + &((pos)->member) != (head); \ + (pos) = list_entry((pos)->member.next, typeof(*(pos)), member)) /** * list_for_each_entry_continue - continue iteration over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. * * Continue to iterate over list of given type, continuing after * the current position. */ -#define list_for_each_entry_continue(pos, head, member) \ - for (pos = list_next_entry(pos, member); \ - &pos->member != (head); \ - pos = list_next_entry(pos, member)) +#define list_for_each_entry_continue(pos, head, member) \ + for ((pos) = list_next_entry(pos, member); \ + &((pos)->member) != (head); \ + (pos) = list_next_entry(pos, member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop counter. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - +#define list_for_each_entry_safe(pos, n, head, member) \ + for ((pos) = list_entry((head)->next, typeof(*(pos)), member), \ + (n) = list_entry((pos)->member.next, typeof(*(pos)), member); \ + &((pos)->member) != (head); \ + (pos) = (n), (n) = list_entry((n)->member.next, typeof(*(n)), member)) /* * Insert a new entry between two known consecutive entries. @@ -130,14 +127,12 @@ struct list_head { * This is only for internal list manipulation where we know * the prev/next entries already! */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) +static inline void list_insert(struct list_head *new, struct list_head *prev, struct list_head *next) { - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; } /** @@ -150,7 +145,7 @@ static inline void __list_add(struct list_head *new, */ static inline void list_add(struct list_head *new, struct list_head *head) { - __list_add(new, head, head->next); + list_insert(new, head, head->next); } /** @@ -163,7 +158,7 @@ static inline void list_add(struct list_head *new, struct list_head *head) */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { - __list_add(new, head->prev, head); + list_insert(new, head->prev, head); } @@ -174,10 +169,10 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head) * This is only for internal list manipulation where we know * the prev/next entries already! */ -static inline void __list_del(struct list_head * prev, struct list_head * next) +static inline void list_remove(struct list_head * prev, struct list_head * next) { - next->prev = prev; - prev->next = next; + next->prev = prev; + prev->next = next; } /** @@ -188,9 +183,9 @@ static inline void __list_del(struct list_head * prev, struct list_head * next) */ static inline void list_del(struct list_head *entry) { - __list_del(entry->prev, entry->next); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; + list_remove(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; } /** @@ -200,13 +195,12 @@ static inline void list_del(struct list_head *entry) * * If @old was empty, it will be overwritten. */ -static inline void list_replace(struct list_head *old, - struct list_head *new) +static inline void list_replace(struct list_head *old, struct list_head *new) { - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; } -#endif \ No newline at end of file +#endif diff --git a/upatch-diff/log.h b/upatch-diff/log.h index 34b58bfa70b415d52ebfed928467c565ac2218c9..b91ae9a289074284b51daea5c44504e05a539470 100644 --- a/upatch-diff/log.h +++ b/upatch-diff/log.h @@ -29,43 +29,44 @@ #include /* Files that include log.h must define g_loglevel and g_logprefix */ -extern enum LogLevel g_loglevel; +extern enum log_level g_loglevel; extern char *g_logprefix; -enum exit_status{ - EXIT_STATUS_SUCCESS = 0, - EXIT_STATUS_ERROR = 1, - EXIT_STATUS_DIFF_FATAL = 2, - EXIT_STATUS_NO_CHANGE = 3, -}; - /* Since upatch-build is an one-shot program, we do not care about failure handler */ -#define ERROR(format, ...) \ - error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) - -#define DIFF_FATAL(format, ...) \ - error(EXIT_STATUS_DIFF_FATAL, 0, "ERROR: %s: %s: %d: " format, g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define ERROR(format, ...) do { \ + if (g_logprefix) { \ + error(EXIT_FAILURE, 0, "ERROR: %s: %s:%d: " format, \ + g_logprefix, __func__, __LINE__, ##__VA_ARGS__); \ + } else { \ + error(EXIT_FAILURE, 0, "ERROR: %s:%d: " format, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } \ +} while (0) /* it is time cost */ #define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__) -#define log_normal(format, ...) log(NORMAL, "%s: " format, g_logprefix, ##__VA_ARGS__) -#define log_warn(format, ...) log(WARN, "%s: " format, g_logprefix, ##__VA_ARGS__) +#define log_normal(format, ...) log(NORMAL, format, ##__VA_ARGS__) +#define log_warn(format, ...) log(WARN, format, ##__VA_ARGS__) +#define log_error(format, ...) log(ERR, format, ##__VA_ARGS__) -#define log(level, format, ...) \ -({ \ - if (g_loglevel <= (level)) \ - printf(format, ##__VA_ARGS__); \ -}) +#define log(level, format, ...) do { \ + if (g_loglevel > (level)) { break; } \ + fprintf((level <= NORMAL) ? stdout : stderr, format, ##__VA_ARGS__); \ +} while (0) #define REQUIRE(COND, message) \ - do \ - if (!(COND)) \ - ERROR(message); \ - while (0) + do { \ + if (!(COND)) { \ + ERROR(message); \ + } \ + } \ + while (0) -enum LogLevel { - DEBUG, - NORMAL, +enum log_level { + DEBUG, + NORMAL, WARN, + ERR, }; -#endif \ No newline at end of file + +#endif diff --git a/upatch-diff/running-elf.c b/upatch-diff/running-elf.c index 676880f186e4d1683e27a1b82b6570ccef6246d2..90969e55cbefea8dacf8e3b6741b187a25f72474 100644 --- a/upatch-diff/running-elf.c +++ b/upatch-diff/running-elf.c @@ -32,115 +32,114 @@ #include #include +#include "upatch-elf.h" #include "running-elf.h" #include "log.h" -/* TODO: need to judge whether running_elf is a Position-Independent Executable file - * https://github.com/bminor/binutils-gdb/blob/master/binutils/readelf.c - */ -static bool is_pie(struct Elf *elf) -{ - return true; -} - -static bool is_exec(struct Elf *elf) +void relf_open(struct running_elf *relf, const char *name) { GElf_Ehdr ehdr; - - if (!gelf_getehdr(elf, &ehdr)) - ERROR("gelf_getehdr running_file failed for %s.", elf_errmsg(0)); - - return ehdr.e_type == ET_EXEC || (ehdr.e_type == ET_DYN && is_pie(elf)); -} - -void relf_init(char *elf_name, struct running_elf *relf) -{ GElf_Shdr shdr; - Elf_Scn *scn = NULL; - Elf_Data *data; GElf_Sym sym; - unsigned int i; - relf->fd = open(elf_name, O_RDONLY); - if (relf->fd == -1) - ERROR("open with errno = %d", errno); + if (relf == NULL) { + return; + } + + relf->fd = open(name, O_RDONLY); + if (relf->fd == -1) { + ERROR("Failed to open '%s', %s", name, strerror(errno)); + } relf->elf = elf_begin(relf->fd, ELF_C_READ, NULL); - if (!relf->elf) - ERROR("elf_begin with error %s", elf_errmsg(0)); + if (!relf->elf) { + ERROR("Failed to read file '%s', %s", name, elf_errmsg(0)); + } - relf->is_exec = is_exec(relf->elf); + if (!gelf_getehdr(relf->elf, &ehdr)) { + ERROR("Failed to read file '%s' elf header, %s", name, elf_errmsg(0)); + } + relf->is_exec = ((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN)); + Elf_Scn *scn = NULL; while ((scn = elf_nextscn(relf->elf, scn)) != NULL) { - if (!gelf_getshdr(scn, &shdr)) - ERROR("gelf_getshdr with error %s", elf_errmsg(0)); - - if (shdr.sh_type == SHT_SYMTAB) + if (!gelf_getshdr(scn, &shdr)) { + ERROR("Failed to read file '%s' section header, %s", + name, elf_errmsg(0)); + } + if (shdr.sh_type == SHT_SYMTAB) { break; + } + } + + Elf_Data *data = elf_getdata(scn, NULL); + if (!data) { + ERROR("Failed to read file '%s' section data, %s", name, elf_errmsg(0)); + } + relf->symbol_count = (GElf_Word)(shdr.sh_size / shdr.sh_entsize); + relf->symbols = calloc((size_t)relf->symbol_count, sizeof(struct relf_symbol)); + if (!relf->symbols) { + ERROR("Failed to alloc memory, %s", strerror(errno)); } - data = elf_getdata(scn, NULL); - if (!data) - ERROR("elf_getdata with error %s", elf_errmsg(0)); - - relf->obj_nr = shdr.sh_size / shdr.sh_entsize; - relf->obj_syms = calloc(relf->obj_nr, sizeof(struct debug_symbol)); - if (!relf->obj_syms) - ERROR("calloc with errno = %d", errno); - - for (i = 0; i < relf->obj_nr; i ++) { - if (!gelf_getsym(data, i, &sym)) - ERROR("gelf_getsym with error %s", elf_errmsg(0)); - relf->obj_syms[i].name = elf_strptr(relf->elf, shdr.sh_link, sym.st_name); - if (!relf->obj_syms[i].name) - ERROR("elf_strptr with error %s", elf_errmsg(0)); - relf->obj_syms[i].type = GELF_ST_TYPE(sym.st_info); - relf->obj_syms[i].bind = GELF_ST_BIND(sym.st_info); - relf->obj_syms[i].shndx = sym.st_shndx; - relf->obj_syms[i].addr = sym.st_value; - relf->obj_syms[i].size = sym.st_size; + for (GElf_Word i = 0; i < relf->symbol_count; i++) { + if (!gelf_getsym(data, (int)i, &sym)) { + ERROR("Failed to read file '%s' symbol, index=%d, %s", + name, i, elf_errmsg(0)); + } + relf->symbols[i].name = elf_strptr(relf->elf, + shdr.sh_link, sym.st_name); + if (!relf->symbols[i].name) { + ERROR("Failed to read file '%s' symbol name, index=%d, %s", + name, i, elf_errmsg(0)); + } + relf->symbols[i].index = i; + relf->symbols[i].type = GELF_ST_TYPE(sym.st_info); + relf->symbols[i].bind = GELF_ST_BIND(sym.st_info); + relf->symbols[i].shndx = sym.st_shndx; + relf->symbols[i].addr = sym.st_value; + relf->symbols[i].size = sym.st_size; } } -int relf_destroy(struct running_elf *relf) +void relf_close(struct running_elf *relf) { - free(relf->obj_syms); - elf_end(relf->elf); + if (relf == NULL) { + return; + } + if (relf->symbols) { + free(relf->symbols); + } + if (relf->elf) { + elf_end(relf->elf); + } + if (relf->fd > 0) { + close(relf->fd); + } relf->elf = NULL; - close(relf->fd); relf->fd = -1; - - return 0; } -bool lookup_relf(struct running_elf *relf, - struct symbol *lookup_sym, struct lookup_result *result) +struct relf_symbol* lookup_relf(struct running_elf *relf, struct symbol *sym) { - struct debug_symbol *symbol = NULL; - unsigned long sympos = 0; - - log_debug("looking up symbol '%s'\n", lookup_sym->name); - memset(result, 0, sizeof(*result)); + struct relf_symbol *result = NULL; - for (int i = 0; i < relf->obj_nr; i++) { - symbol = &relf->obj_syms[i]; - sympos++; + for (GElf_Word i = 0; i < relf->symbol_count; i++) { + struct relf_symbol *symbol = &relf->symbols[i]; - if (strcmp(symbol->name, lookup_sym->name) != 0) { + if ((result != NULL) && (symbol->type == STT_FILE)) { + break; + } + if ((strcmp(symbol->name, sym->name) != 0) || + (symbol->bind != sym->bind)) { continue; } - if ((result->symbol != NULL) && - (result->symbol->bind == symbol->bind)) { - ERROR("Found duplicate symbol '%s' in %s", - lookup_sym->name, g_relf_name); + if ((result != NULL) && (result->bind == symbol->bind)) { + ERROR("Found duplicate symbol '%s' in %s", sym->name, g_relf_name); } - result->symbol = symbol; - result->sympos = sympos; - result->global = - ((symbol->bind == STB_GLOBAL) || (symbol->bind == STB_WEAK)); - log_debug("found symbol '%s'\n", lookup_sym->name); + result = symbol; } - return (result->symbol != NULL); + return result; } diff --git a/upatch-diff/running-elf.h b/upatch-diff/running-elf.h index 0646780b9c007c9483764bf51383fce4ec094de6..24e93261795e52cd87280ac64737326335573dff 100644 --- a/upatch-diff/running-elf.h +++ b/upatch-diff/running-elf.h @@ -30,36 +30,29 @@ #include #include -#include "upatch-elf.h" - struct symbol; -struct lookup_result { - struct debug_symbol *symbol; - unsigned long sympos; - bool global; -}; - -struct debug_symbol { +struct relf_symbol { + GElf_Word index; char *name; - unsigned char type, bind; - unsigned int shndx; - unsigned long addr; - unsigned long size; + unsigned char type; + unsigned char bind; + GElf_Section shndx; + GElf_Addr addr; + GElf_Xword size; }; struct running_elf { - int obj_nr; - struct debug_symbol *obj_syms; int fd; Elf *elf; + struct relf_symbol *symbols; + GElf_Word symbol_count; bool is_exec; }; -void relf_init(char *, struct running_elf *); - -int relf_destroy(struct running_elf *); +void relf_open(struct running_elf *relf, const char *name); +void relf_close(struct running_elf *relf); -bool lookup_relf(struct running_elf *, struct symbol *, struct lookup_result *); +struct relf_symbol* lookup_relf(struct running_elf *relf, struct symbol *sym); -#endif \ No newline at end of file +#endif diff --git a/upatch-diff/upatch-dynrela.h b/upatch-diff/upatch-dynrela.h index 7d10378a25ab1ab4e7b99b403a7c7a22607b763e..d31c9f0b5e159e913a9cb95fd278abe2b253cacb 100644 --- a/upatch-diff/upatch-dynrela.h +++ b/upatch-diff/upatch-dynrela.h @@ -37,4 +37,4 @@ struct upatch_relocation { struct upatch_symbol *sym; }; -#endif /* __UPATCH_DYN_RELA_H_ */ \ No newline at end of file +#endif /* __UPATCH_DYN_RELA_H_ */ diff --git a/upatch-diff/upatch-elf.c b/upatch-diff/upatch-elf.c index fc4396a9cadc663c698e15257143a0a1769643db..331504bb76af7020e82c803f5b6740d23e53d377 100644 --- a/upatch-diff/upatch-elf.c +++ b/upatch-diff/upatch-elf.c @@ -40,272 +40,321 @@ static void create_section_list(struct upatch_elf *uelf) { - size_t shstrndx, sections_nr; - - struct section *sec; - Elf_Scn *scn = NULL; - - if (elf_getshdrnum(uelf->elf, §ions_nr)) - ERROR("elf_getshdrnum with error %s", elf_errmsg(0)); - - sections_nr --; + size_t shstrndx = 0; + if (elf_getshdrstrndx(uelf->elf, &shstrndx) != 0) { + ERROR("Failed to get section header string index"); + } - if (elf_getshdrstrndx(uelf->elf, &shstrndx)) - ERROR("elf_getshdrstrndx with error %s", elf_errmsg(0)); + Elf_Scn *scn = elf_nextscn(uelf->elf, NULL); + while (scn != NULL) { + GElf_Section index = (GElf_Section)elf_ndxscn(scn); + struct section *sec = NULL; - log_debug("=== section list (%zu) === \n", sections_nr); - while (sections_nr --) { ALLOC_LINK(sec, &uelf->sections); + if (gelf_getshdr(scn, &sec->sh) == NULL) { + ERROR("Failed to parse section, index=%d", index); + } - scn = elf_nextscn(uelf->elf, scn); - if (!scn) - ERROR("elf_nextscn with error %s", elf_errmsg(0)); - - if (!gelf_getshdr(scn, &sec->sh)) - ERROR("gelf_getshdr with error %s", elf_errmsg(0)); - + sec->index = (GElf_Section)index; sec->name = elf_strptr(uelf->elf, shstrndx, sec->sh.sh_name); - if (!sec->name) - ERROR("elf_strptr with error %s", elf_errmsg(0)); - + if (sec->name == NULL) { + ERROR("Failed to get section name, index=%d", index); + } sec->data = elf_getdata(scn, NULL); - if (!sec->data) - ERROR("elf_getdata with error %s", elf_errmsg(0)); + if (sec->data == NULL) { + ERROR("Failed to get section '%s' data, index=%d", + sec->name, index); + } - sec->index = (unsigned int)elf_ndxscn(scn); - /* found extended section header */ - if (sec->sh.sh_type == SHT_SYMTAB_SHNDX) - uelf->symtab_shndx = sec->data; /* correct ? */ + sec->name_source = DATA_SOURCE_REF; + sec->data_source = DATA_SOURCE_REF; + sec->dbuf_source = DATA_SOURCE_REF; - log_debug("ndx %02d, data %p, size %zu, name %s\n", - sec->index, sec->data->d_buf, sec->data->d_size, sec->name); - } + INIT_LIST_HEAD(&sec->relas); - if (elf_nextscn(uelf->elf, scn)) - ERROR("elf_nextscn with error %s", elf_errmsg(0)); + scn = elf_nextscn(uelf->elf, scn); + } } static void create_symbol_list(struct upatch_elf *uelf) { - struct section *symtab; - unsigned int symbols_nr; - Elf32_Word shndx; - struct symbol *sym; - unsigned int index = 0; - - /* consider type first */ - symtab = find_section_by_name(&uelf->sections, ".symtab"); - if (!symtab) - ERROR("can't find symbol table"); + struct section *symtab = find_section_by_type(&uelf->sections, SHT_SYMTAB); + if (symtab == NULL) { + ERROR("Cannot find symbol table"); + } - symbols_nr = (unsigned int)(symtab->sh.sh_size / symtab->sh.sh_entsize); + GElf_Word count = (GElf_Word)(symtab->sh.sh_size / symtab->sh.sh_entsize); + for (GElf_Word i = 0; i < count; i++) { + struct symbol *sym = NULL; - log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr); - while (symbols_nr --) { ALLOC_LINK(sym, &uelf->symbols); - INIT_LIST_HEAD(&sym->children); - - sym->index = index; - if (!gelf_getsym(symtab->data, index, &sym->sym)) - ERROR("gelf_getsym with error %s", elf_errmsg(0)); - - index ++; + if (gelf_getsym(symtab->data, (int)i, &sym->sym) == NULL) { + ERROR("Failed to parse symbol, index=%d", i); + } + sym->index = i; sym->name = elf_strptr(uelf->elf, symtab->sh.sh_link, sym->sym.st_name); - if (!sym->name) - ERROR("elf_strptr with error %s", elf_errmsg(0)); - - sym->type = GELF_ST_TYPE(sym->sym.st_info); - sym->bind = GELF_ST_BIND(sym->sym.st_info); - - shndx = sym->sym.st_shndx; - /* releated section located in extended header */ - if (shndx == SHN_XINDEX && - !gelf_getsymshndx(symtab->data, uelf->symtab_shndx, - sym->index, &sym->sym, &shndx)) - ERROR("gelf_getsymshndx with error %s", elf_errmsg(0)); + if (sym->name == NULL) { + ERROR("Failed to get symbol name, index=%d", i); + } - if ((sym->sym.st_shndx > SHN_UNDEF && sym->sym.st_shndx < SHN_LORESERVE) || - sym->sym.st_shndx == SHN_XINDEX) { + sym->name_source = DATA_SOURCE_REF; + sym->bind = GELF_ST_BIND(sym->sym.st_info); + sym->type = GELF_ST_TYPE(sym->sym.st_info); + GElf_Section shndx = sym->sym.st_shndx; + if ((sym->sym.st_shndx > SHN_UNDEF) && + (sym->sym.st_shndx < SHN_LORESERVE)) { sym->sec = find_section_by_index(&uelf->sections, shndx); - if (!sym->sec) - ERROR("no releated section found for symbol %s \n", sym->name); - - /* this symbol is releated with a section */ + if (sym->sec == NULL) { + ERROR("Failed to find symbol '%s' section, index=%d, shndx=%d", + sym->name, i, shndx); + } if (sym->type == STT_SECTION) { - /* secsym must be the bundleable symbol */ - sym->sec->secsym = sym; - + /* sym must be the bundleable symbol */ + sym->sec->sym = sym; /* use section name as symbol name */ sym->name = sym->sec->name; } } - log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s", - sym->index, sym->type, sym->bind, sym->sym.st_shndx, - sym->name); - if (sym->sec) - log_debug(" -> %s", sym->sec->name); - log_debug("\n"); + + INIT_LIST_HEAD(&sym->children); + INIT_LIST_HEAD(&sym->subfunction_node); } } -static void create_rela_list(struct upatch_elf *uelf, struct section *relasec) +static void create_rela_list(struct upatch_elf *uelf, struct section *sec) { - unsigned long rela_nr; - unsigned int symndx; - struct rela *rela; - int index = 0, skip = 0; + /* for relocation sections, sh_info is the index which these info apply */ + sec->base = (struct section *)sec->info; + if (sec->base == NULL) { + ERROR("Cannot find section '%s' base section, index=%d", + sec->name, sec->index); + } + sec->base->rela = sec; + + GElf_Word count = (GElf_Word)(sec->sh.sh_size / sec->sh.sh_entsize); + for (GElf_Word i = 0; i < count; i++) { + struct rela *rela = NULL; - /* for relocation sections, sh_info is the index which these informations apply */ - relasec->base = find_section_by_index(&uelf->sections, relasec->sh.sh_info); - if (!relasec->base) - ERROR("no base section found for relocation section %s", relasec->name); + ALLOC_LINK(rela, &sec->relas); + if (gelf_getrela(sec->data, (int)i, &rela->rela) == NULL) { + ERROR("Failed to parse rela, index=%d", i); + } - relasec->base->rela = relasec; - rela_nr = relasec->sh.sh_size / relasec->sh.sh_entsize; + GElf_Word symndx = (GElf_Word)GELF_R_SYM(rela->rela.r_info); + rela->sym = find_symbol_by_index(&uelf->symbols, symndx); + if (rela->sym == NULL) { + ERROR("Cannot find rela symbol, index=%d, symndx=%d", i, symndx); + } + rela->type = GELF_R_TYPE(rela->rela.r_info); + rela->addend = rela->rela.r_addend; + rela->offset = rela->rela.r_offset; - log_debug("\n=== rela list for %s (%ld entries) === \n", - relasec->base->name, rela_nr); + if (is_string_section(rela->sym->sec)) { + void *data = rela->sym->sec->data->d_buf; + GElf_Addr addr = rela->sym->sym.st_value; + long offset = rela_target_offset(uelf, sec, rela); - if (is_debug_section(relasec)) { - log_debug("skipping rela listing for .debug_* section \n"); - skip = 1; + rela->string = data + addr + offset; + if (rela->string == NULL) { + ERROR("Cannot find rela string %s+%ld", + rela->sym->name, rela->addend); + } + } } +} + +static void destroy_rela_list(struct section *sec) +{ + struct rela *rela = NULL; + struct rela *safe = NULL; - if (is_note_section(relasec)) { - log_debug("skipping rela listing for .note_* section \n"); - skip = 1; + list_for_each_entry_safe(rela, safe, &sec->relas, list) { + list_del(&rela->list); + free(rela); } - while (rela_nr --) { - ALLOC_LINK(rela, &relasec->relas); + INIT_LIST_HEAD(&sec->relas); +} - /* use index because we need to keep the order of rela */ - if (!gelf_getrela(relasec->data, index, &rela->rela)) - ERROR("gelf_getrela with error %s", elf_errmsg(0)); - index++; +static void destroy_section_list(struct upatch_elf *uelf) +{ + struct section *sec = NULL; + struct section *safesec = NULL; - rela->type = GELF_R_TYPE(rela->rela.r_info); - rela->addend = rela->rela.r_addend; - rela->offset = (unsigned int)rela->rela.r_offset; - symndx = (unsigned int)GELF_R_SYM(rela->rela.r_info); - rela->sym = find_symbol_by_index(&uelf->symbols, symndx); - if (!rela->sym) - ERROR("no rela entry symbol found \n"); - - if (rela->sym->sec && is_string_section(rela->sym->sec)) { - rela->string = rela->sym->sec->data->d_buf + - rela->sym->sym.st_value + - rela_target_offset(uelf, relasec, rela); - if (!rela->string) - ERROR("could not lookup rela string for %s+%ld", - rela->sym->name, rela->addend); + list_for_each_entry_safe(sec, safesec, &uelf->sections, list) { + if (sec->twin) { + sec->twin->twin = NULL; } - if (skip) - continue; + if ((sec->name != NULL) && (sec->name_source == DATA_SOURCE_ALLOC)) { + free(sec->name); + sec->name = NULL; + } + + if (sec->data != NULL) { + if (sec->dbuf_source == DATA_SOURCE_ALLOC) { + free(sec->data->d_buf); + sec->data->d_buf = NULL; + } + if (sec->data_source == DATA_SOURCE_ALLOC) { + free(sec->data); + sec->data = NULL; + } + } + + if (is_rela_section(sec)) { + destroy_rela_list(sec); + } - log_debug("offset %d, type %d, %s %s %ld", rela->offset, - rela->type, rela->sym->name, - (rela->addend < 0) ? "-" : "+", labs(rela->addend)); - if (rela->string) // rela->string is not utf8 - log_debug(" string = %s", rela->string); - log_debug("\n"); + list_del(&sec->list); + free(sec); } + + INIT_LIST_HEAD(&uelf->sections); } -void upatch_elf_open(struct upatch_elf *uelf, const char *name) +static void destroy_symbol_list(struct upatch_elf *uelf) { - GElf_Ehdr ehdr; - struct section *relasec; - Elf *elf = NULL; - int fd = 1; + struct symbol *sym = NULL; + struct symbol *safesym = NULL; + + list_for_each_entry_safe(sym, safesym, &uelf->symbols, list) { + if (sym->twin) { + sym->twin->twin = NULL; + } - fd = open(name, O_RDONLY); - if (fd == -1) - ERROR("open %s failed with errno %d \n", name, errno); + list_del(&sym->list); + free(sym); + } + + INIT_LIST_HEAD(&uelf->symbols); +} + +static void destroy_string_list(struct upatch_elf *uelf) +{ + struct string *str = NULL; + struct string *safestr = NULL; - elf = elf_begin(fd, ELF_C_RDWR, NULL); - if (!elf) - ERROR("open elf %s failed with error %s \n", name, elf_errmsg(0)); + list_for_each_entry_safe(str, safestr, &uelf->strings, list) { + list_del(&str->list); + free(str); + } - memset(uelf, 0, sizeof(*uelf)); + INIT_LIST_HEAD(&uelf->strings); +} + +static void parse_section_metadata(struct upatch_elf *uelf) +{ + struct section *sec; + list_for_each_entry(sec, &uelf->sections, list) { + /* find sh_link */ + if (sec->sh.sh_link != SHN_UNDEF) { + sec->link = find_section_by_index(&uelf->sections, + (GElf_Section)sec->sh.sh_link); + if (sec->link == NULL) { + ERROR("Cannot find '%s' link section, sh_link=%d", + sec->name, sec->sh.sh_link); + } + } + /* find sh_info */ + if ((sec->sh.sh_type == SHT_REL) || (sec->sh.sh_type == SHT_RELA)) { + sec->info = find_section_by_index(&uelf->sections, + (GElf_Section)sec->sh.sh_info); + if (sec->link == NULL) { + ERROR("Cannot find '%s' info section, sh_info=%d", + sec->name, sec->sh.sh_link); + } + } else if (sec->sh.sh_type == SHT_GROUP) { + sec->info = find_symbol_by_index(&uelf->symbols, sec->sh.sh_info); + if (sec->link == NULL) { + ERROR("Cannot find '%s' info symbol, sh_info=%d", + sec->name, sec->sh.sh_link); + } + } + /* handle rela section */ + if (sec->sh.sh_type == SHT_RELA) { + create_rela_list(uelf, sec); + } + } +} + +void uelf_open(struct upatch_elf *uelf, const char *name) +{ + GElf_Ehdr ehdr; + + if (uelf == NULL) { + return; + } INIT_LIST_HEAD(&uelf->sections); INIT_LIST_HEAD(&uelf->symbols); INIT_LIST_HEAD(&uelf->strings); - uelf->elf = elf; + int fd = open(name, O_RDONLY); + if (fd == -1) { + ERROR("Failed to open '%s', %s", name, strerror(errno)); + } uelf->fd = fd; - if (!gelf_getehdr(uelf->elf, &ehdr)) - ERROR("get file %s elf header failed with error %s \n", - name, elf_errmsg(0)); + Elf *elf = elf_begin(fd, ELF_C_READ, NULL); + if (!elf) { + ERROR("Failed to read file '%s', %s", name, elf_errmsg(0)); + } + uelf->elf = elf; - /* TODO: check ELF type here, we only handle object file */ - if (ehdr.e_type != ET_REL) - ERROR("only handles relocatable files \n"); + if (!gelf_getehdr(uelf->elf, &ehdr)) { + ERROR("Failed to read file '%s' elf header, %s", name, elf_errmsg(0)); + } + if (ehdr.e_type != ET_REL) { + ERROR("File '%s' is not object file", name); + } /* * Main problem here is stack check, for kernel, only x86 is support * Not sure how to handle userspace, but let us handle x86 first here */ switch (ehdr.e_machine) { - case EM_AARCH64: - uelf->arch = AARCH64; - break; - case EM_X86_64: - uelf->arch = X86_64; - break; - default: - ERROR("unsupported architecture here"); + case EM_AARCH64: + uelf->arch = AARCH64; + break; + case EM_X86_64: + uelf->arch = X86_64; + break; + case EM_RISCV: + /* + | Val | Macros | Description | + | 1 | ELFCLASS32 | riscv32 | + | 2 | ELFCLASS64 | riscv64 | + */ + if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + uelf->arch = RISCV64; + } + break; + default: + ERROR("Unsupported architecture"); } create_section_list(uelf); create_symbol_list(uelf); - - list_for_each_entry(relasec, &uelf->sections, list) { - if (!is_rela_section(relasec)) - continue; - INIT_LIST_HEAD(&relasec->relas); - - create_rela_list(uelf, relasec); - } + parse_section_metadata(uelf); } -void upatch_elf_teardown(struct upatch_elf *uelf) +void uelf_close(struct upatch_elf *uelf) { - struct section *sec, *safesec; - struct symbol *sym, *safesym; - struct rela *rela, *saferela; - - list_for_each_entry_safe(sec, safesec, &uelf->sections, list) { - if (sec->twin) - sec->twin->twin = NULL; - if (is_rela_section(sec)) { - list_for_each_entry_safe(rela, saferela, &sec->relas, list) { - memset(rela, 0, sizeof(*rela)); - free(rela); - } - } - memset(sec, 0, sizeof(*sec)); - free(sec); + if (uelf == NULL) { + return; } + destroy_section_list(uelf); + destroy_symbol_list(uelf); + destroy_string_list(uelf); - list_for_each_entry_safe(sym, safesym, &uelf->symbols, list) { - if (sym->twin) - sym->twin->twin = NULL; - memset(sym, 0, sizeof(*sym)); - free(sym); + if (uelf->elf) { + elf_end(uelf->elf); } - - INIT_LIST_HEAD(&uelf->sections); - INIT_LIST_HEAD(&uelf->symbols); -} - -void upatch_elf_free(struct upatch_elf *uelf) -{ - elf_end(uelf->elf); - close(uelf->fd); - memset(uelf, 0, sizeof(*uelf)); + if (uelf->fd > 0) { + close(uelf->fd); + } + uelf->elf = NULL; + uelf->fd = -1; } diff --git a/upatch-diff/upatch-elf.h b/upatch-diff/upatch-elf.h index b2d038bfde472c0f380930b03f05e678b606cb87..b34c1af68562bb6e67355453cff717b714945b08 100644 --- a/upatch-diff/upatch-elf.h +++ b/upatch-diff/upatch-elf.h @@ -26,11 +26,10 @@ #ifndef __UPATCH_ELF_H_ #define __UPATCH_ELF_H_ -#include #include +#include #include "list.h" -#include "running-elf.h" extern char *g_relf_name; @@ -39,101 +38,116 @@ struct section; struct rela; struct symbol; -enum status { - NEW, - CHANGED, - SAME +enum architecture { + X86_64 = 0x1 << 0, + AARCH64 = 0x1 << 1, + RISCV64 = 0x1 << 2, }; -enum symbol_strip { - SYMBOL_DEFAULT, - SYMBOL_USED, - SYMBOL_STRIP, +enum data_source { + DATA_SOURCE_REF, + DATA_SOURCE_ALLOC, }; -struct string { - struct list_head list; - char *name; +enum status { + SAME, + CHANGED, + NEW, }; -struct section { - struct list_head list; - struct section *twin; - char *name; - Elf_Data *data; - GElf_Shdr sh; - int ignore; - int include; - int grouped; - unsigned int index; - enum status status; - union { - // section with relocation information - struct { - struct section *base; - struct list_head relas; - }; - // other function or data section - struct { - struct section *rela; - struct symbol *sym; - struct symbol *secsym; - }; - }; +struct upatch_elf { + int fd; + Elf *elf; + enum architecture arch; + struct list_head sections; + struct list_head symbols; + struct list_head strings; }; -struct rela { - struct list_head list; - GElf_Rela rela; - struct symbol *sym; - unsigned int type; - unsigned int offset; - long addend; - char *string; - bool need_dynrela; +struct section { + struct list_head list; + GElf_Shdr sh; + + GElf_Section index; + char *name; + Elf_Data *data; + + /* data source */ + enum data_source name_source; + enum data_source data_source; + enum data_source dbuf_source; + + /* section info */ + struct section *link; + void *info; + + /* symbol reference */ + struct symbol *sym; + struct symbol *bundle_sym; + + /* reloc reference */ + struct section *base; + struct section *rela; + struct list_head relas; + + /* diff metadata */ + struct section *twin; + enum status status; + bool grouped; + bool ignored; + bool include; }; struct symbol { - struct list_head list; - struct symbol *twin; - struct symbol *parent; - struct list_head children; - struct list_head subfunction_node; - struct section *sec; - GElf_Sym sym; - char *name; - struct debug_symbol *relf_sym; - unsigned int index; - unsigned char bind; - unsigned char type; - enum status status; - union { - int include; /* used in the patched elf */ - enum symbol_strip strip; /* used in the output elf */ - }; -}; + struct list_head list; + GElf_Sym sym; -enum architecture { - X86_64 = 0x1 << 0, - AARCH64 = 0x1 << 1, -}; + GElf_Word index; + char *name; -struct upatch_elf { - Elf *elf; - enum architecture arch; - struct list_head sections; - struct list_head symbols; - struct list_head strings; - Elf_Data *symtab_shndx; - int fd; + /* data source */ + enum data_source name_source; + + /* symbol info */ + unsigned char bind; + unsigned char type; + + /* section reference */ + struct section *sec; + + /* subfunction reference */ + struct symbol *parent; + struct list_head children; + struct list_head subfunction_node; + + /* diff metadata */ + struct symbol *twin; + enum status status; + bool include; /* used in the patched elf */ + bool strip; /* used in the output elf */ }; -// init a upatch_elf from a path -void upatch_elf_open(struct upatch_elf *, const char *); +struct rela { + struct list_head list; + GElf_Rela rela; + + /* symbol reference */ + struct symbol *sym; + + /* rela info */ + GElf_Word type; + GElf_Off offset; + GElf_Sxword addend; + + char *string; +}; -// Destory upatch_elf struct -void upatch_elf_teardown(struct upatch_elf *); +struct string { + struct list_head list; + char *name; +}; -void upatch_elf_free(struct upatch_elf *); +void uelf_open(struct upatch_elf *uelf, const char *name); +void uelf_close(struct upatch_elf *uelf); #endif diff --git a/upatch-diff/upatch-patch.h b/upatch-diff/upatch-patch.h index 5e2abce5e9eb1825ffa7f6cc46fbb9759211f9d7..ec0148fc3fe4fb0e171c9c3666118508a22d3f59 100644 --- a/upatch-diff/upatch-patch.h +++ b/upatch-diff/upatch-patch.h @@ -23,7 +23,7 @@ #ifndef __UPATCH_PATCH_H_ #define __UPATCH_PATCH_H_ -#define SYM_OTHER 0x40 +#define SYM_OTHER 0x40 struct upatch_patch_func { unsigned long new_addr; @@ -34,4 +34,4 @@ struct upatch_patch_func { char *name; }; -#endif /* __UPATCH_PATCH_H_ */ \ No newline at end of file +#endif /* __UPATCH_PATCH_H_ */ diff --git a/upatch-helper/Cargo.toml b/upatch-helper/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..b131b6629eca18c8ca1216b9b83a42f7c7cebf55 --- /dev/null +++ b/upatch-helper/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "upatch-helper" +version = "1.2.2" +authors = ["renoseven "] +description = "Upatch compiler helper" +license = "MulanPSL-2.0" +edition = "2018" +rust-version = "1.60" +build = "build.rs" + +[dependencies] +anyhow = { version = "1.0" } +uuid = { version = "0.8", features = ["v4"] } diff --git a/upatchd/build.rs b/upatch-helper/build.rs similarity index 43% rename from upatchd/build.rs rename to upatch-helper/build.rs index 3809c543d8f68d5a8586f1d02c0d28dbac9e5be6..dccde68f8cdc85e2e02b2c8829840554c44b5ccf 100644 --- a/upatchd/build.rs +++ b/upatch-helper/build.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Mulan PSL v2 /* * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. + * upatch-helper is licensed under Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * http://license.coscl.org.cn/MulanPSL2 @@ -12,26 +12,25 @@ * See the Mulan PSL v2 for more details. */ -use std::{env, process::Command}; +use std::{env, ffi::OsStr, os::unix::ffi::OsStrExt, process::Command}; fn rewrite_version() { - const ENV_VERSION_NAME: &str = "BUILD_VERSION"; - const PKG_VERSION_NAME: &str = "CARGO_PKG_VERSION"; + const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); + const ENV_VERSION: Option<&str> = option_env!("BUILD_VERSION"); - let version = env::var(ENV_VERSION_NAME).unwrap_or_else(|_| { - let pkg_version = env::var(PKG_VERSION_NAME).expect("Failed to fetch package version"); - let git_output = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|output| String::from_utf8(output.stdout).expect("Failed to fetch git version")); - - match git_output { - Ok(git_version) => format!("{}-g{}", pkg_version, git_version), - Err(_) => pkg_version, - } - }); - - println!("cargo:rustc-env={}={}", PKG_VERSION_NAME, version); + println!( + "cargo:rustc-env=CARGO_PKG_VERSION={}", + ENV_VERSION.map(String::from).unwrap_or_else(|| { + Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|output| { + let git_version = OsStr::from_bytes(&output.stdout).to_string_lossy(); + format!("{}-g{}", PKG_VERSION, git_version) + }) + .unwrap_or_else(|_| PKG_VERSION.to_string()) + }) + ); } fn main() { diff --git a/upatch-helper/src/main.rs b/upatch-helper/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..7c7b2b8b73ec1935fc6f2d2e241160f4ceaddd42 --- /dev/null +++ b/upatch-helper/src/main.rs @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: Mulan PSL v2 +/* + * Copyright (c) 2024 Huawei Technologies Co., Ltd. + * upatch-helper is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +use std::{ + ffi::{OsStr, OsString}, + os::unix::{ffi::OsStrExt, process::CommandExt}, + path::{Path, PathBuf}, + process::Command, +}; + +use anyhow::{bail, Context, Result}; +use uuid::Uuid; + +const COMPILER_KEYWORDS_CC: &[&str] = &["cc", "clang"]; +const COMPILER_KEYWORDS_CXX: &[&str] = &["++", "xx"]; + +const COMPILER_EXCLUDE_FLAGS: &[&str] = &[ + "-E", // Preprocess only; does not compile. + "--version", // Print compiler version and exit. + "--help", // Print help message and exit. + "--target-help", // Print target-specific help and exit. + "-dumpversion", // Print compiler version string (e.g., "11.2.0") and exit. + "-dumpmachine", // Print compiler target machine (e.g., "x86_64-linux-gnu") and exit. + "-###", // Dry run: print commands that would be executed, but do not run them. +]; +const COMPILER_EXCLUDE_FLAG_PREFIXES: &[&str] = &["--print-"]; +const COMPILER_COMPILE_SIGNAL_FLAGS: &[&str] = &["-x"]; +const COMPILER_SPECIAL_SOURCE_FILES: &[&str] = &["-", "@args.txt"]; +const COMPILER_SOURCE_FILE_EXTENSIONS: &[&str] = &["c", "cc", "cpp", "cxx", "s", "S"]; + +const HELPER_ENV_NAME_CC: &str = "UPATCH_HELPER_CC"; +const HELPER_ENV_NAME_CXX: &str = "UPATCH_HELPER_CXX"; +const HELPER_ENV_NAMES: &[(&[&str], &str)] = &[ + (COMPILER_KEYWORDS_CC, HELPER_ENV_NAME_CC), + (COMPILER_KEYWORDS_CXX, HELPER_ENV_NAME_CXX), +]; + +const COMPILE_OPTIONS_GNU: &[&str] = &[ + "-gdwarf", // generate dwarf debuginfo + "-ffunction-sections", // generate corresponding section for each function + "-fdata-sections", // generate corresponding section for each data + "-fmerge-constants", // merge constants with same value into one + "-fno-common", // avoid generating common block for uninitialized global variables + "-fno-tree-slp-vectorize", // avoid converting scalar operations into SIMD instructions +]; +const COMPILE_OPTIONS_CLANG: &[&str] = &[ + "-gdwarf", + "-ffunction-sections", + "-fdata-sections", + "-fno-common", + "-fno-slp-vectorize", // avoid converting scalar operations into SIMD instructions + "-fno-integrated-as", // avoid using built-in llvm-as, which doesn't support "--defsym" + "-Werror=uninitialized", // uninitialized variable may generate unexpected code under O2 optimization +]; + +const UPATCH_ID_PREFIX: &str = ".upatch_"; + +#[inline(always)] +fn is_compilation(args: &[OsString]) -> bool { + /* check exclude flags */ + for arg in args.iter().skip(1) { + if COMPILER_EXCLUDE_FLAGS + .iter() + .any(|&flag| arg == OsStr::new(flag)) + { + return false; + } + if COMPILER_EXCLUDE_FLAG_PREFIXES + .iter() + .any(|&prefix| arg.as_bytes().starts_with(prefix.as_bytes())) + { + return false; + } + } + + /* check compile flag & source file */ + for arg in args.iter().skip(1) { + if COMPILER_COMPILE_SIGNAL_FLAGS + .iter() + .any(|&name| arg == OsStr::new(name)) + { + return true; + } + if COMPILER_SPECIAL_SOURCE_FILES + .iter() + .any(|&name| arg == OsStr::new(name)) + { + return true; + } + if let Some(src_ext) = Path::new(arg).extension() { + if COMPILER_SOURCE_FILE_EXTENSIONS + .iter() + .any(|&ext| src_ext == OsStr::new(ext)) + { + return true; + } + } + } + + false +} + +#[inline(always)] +fn find_compiler(arg0: &OsStr) -> Result { + let file_name = Path::new(arg0).file_name().unwrap_or_default(); + + // match compiler by file name + let env_entry = HELPER_ENV_NAMES.iter().find(|(keys, _)| { + keys.iter().any(|str| { + let key_bytes = str.as_bytes(); + file_name + .as_bytes() + .windows(key_bytes.len()) + .any(|window| window == key_bytes) + }) + }); + if let Some((_, env_name)) = env_entry { + return std::env::var_os(env_name) + .map(PathBuf::from) + .with_context(|| format!("Environment variable '{}' was not set", env_name)); + } + + // exec name matched, read environment variable directly + let exec_path = std::env::current_exe()?; + let exec_name = exec_path.file_name().unwrap_or_default(); + if exec_name == file_name { + return HELPER_ENV_NAMES + .iter() + .rev() + .find_map(|&(_, env_name)| std::env::var_os(env_name).map(PathBuf::from)) + .with_context(|| { + format!( + "Environment variables '{}' and '{}' were not set", + HELPER_ENV_NAME_CC, HELPER_ENV_NAME_CXX + ) + }); + } + + bail!("No compiler found"); +} + +#[inline(always)] +fn add_compile_options(command: &mut Command) { + let prog_name = Path::new(command.get_program()) + .file_name() + .unwrap_or_default(); + let clang_name_bytes = COMPILER_KEYWORDS_CC[1].as_bytes(); + let is_clang = prog_name + .as_bytes() + .windows(clang_name_bytes.len()) + .any(|window| window == clang_name_bytes); + + let compiler_args = match is_clang { + true => COMPILE_OPTIONS_CLANG, + false => COMPILE_OPTIONS_GNU, + }; + let assembler_arg = format!("-Wa,--defsym,{}{}=0", UPATCH_ID_PREFIX, Uuid::new_v4()); + + command.args(compiler_args); + command.arg(assembler_arg); +} + +fn main() -> Result<()> { + let args: Vec<_> = std::env::args_os().collect(); + let compiler = self::find_compiler(&args[0])?; + + let mut command = Command::new(&compiler); + command.args(&args[1..]); + if self::is_compilation(&args) { + self::add_compile_options(&mut command); + } + + let err = command.exec(); + bail!( + "Failed to execute '{}', {}", + compiler.display(), + err.to_string().to_lowercase() + ); +} diff --git a/upatch-hijacker/CMakeLists.txt b/upatch-hijacker/CMakeLists.txt deleted file mode 100644 index 77d10105ca4683bec99a1ee00a659895ca4188c3..0000000000000000000000000000000000000000 --- a/upatch-hijacker/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Add common header directory -include_directories(${CMAKE_CURRENT_LIST_DIR}) - -# Build components -add_subdirectory(ko) -add_subdirectory(hijacker) diff --git a/upatch-hijacker/hijacker/CMakeLists.txt b/upatch-hijacker/hijacker/CMakeLists.txt deleted file mode 100644 index e42ffb3b163229294f0b5cd73c1d90e1d0c2c6c9..0000000000000000000000000000000000000000 --- a/upatch-hijacker/hijacker/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# Build hijackers -add_executable(gnu-as-hijacker gnu-as-hijacker.c) -add_executable(gnu-compiler-hijacker gnu-compiler-hijacker.c) - -# Generate hijackers -add_custom_target(generate-upatch-hijackers ALL - COMMENT "Generating upatch hijackers..." - COMMAND ln -f gnu-as-hijacker as-hijacker - COMMAND ln -f gnu-compiler-hijacker gcc-hijacker - COMMAND ln -f gnu-compiler-hijacker g++-hijacker - COMMAND ln -f gnu-compiler-hijacker cc-hijacker - COMMAND ln -f gnu-compiler-hijacker c++-hijacker - DEPENDS - gnu-as-hijacker - gnu-compiler-hijacker - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) - -# Install hijackers -install( - PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/gnu-as-hijacker - ${CMAKE_CURRENT_BINARY_DIR}/gnu-compiler-hijacker - ${CMAKE_CURRENT_BINARY_DIR}/as-hijacker - ${CMAKE_CURRENT_BINARY_DIR}/gcc-hijacker - ${CMAKE_CURRENT_BINARY_DIR}/g++-hijacker - ${CMAKE_CURRENT_BINARY_DIR}/cc-hijacker - ${CMAKE_CURRENT_BINARY_DIR}/c++-hijacker - PERMISSIONS - OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ - WORLD_READ WORLD_EXECUTE - DESTINATION - ${SYSCARE_LIBEXEC_DIR} -) diff --git a/upatch-hijacker/hijacker/gnu-as-hijacker.c b/upatch-hijacker/hijacker/gnu-as-hijacker.c deleted file mode 100644 index 789ddc2357ca664d909394f48a35483acec7774b..0000000000000000000000000000000000000000 --- a/upatch-hijacker/hijacker/gnu-as-hijacker.c +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * gnu-as-hijacker is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#define _GNU_SOURCE - -#include -#include - -#include -#include - -#include "hijacker.h" - -#ifndef SYS_gettid -#error "SYS_gettid is unavailable on this system" -#endif - -#define DEFSYM_MAX 64 - -static const char *DEFSYM_FLAG = "--defsym"; -static const char *DEFSYM_VALUE = ".upatch_0x%x="; -static const int APPEND_ARG_LEN = 2; - -static const char *OUTPUT_PATH = "%s/0x%x.o"; -static const char *NULL_DEV_PATH = "/dev/null"; - -static char g_defsym[DEFSYM_MAX] = { 0 }; -static char g_new_output_file[PATH_MAX] = { 0 }; - -/* - * The whole part: - * 1. Someone called execve() to run a compiler (inode). - * 2. If the inode was registered, under layer would rewrite argv[0] to hijacker path. - * 3. Hijacker would add some arguments and calls execve() again. - * 4. Under layer redirects argv[0] to original path. - * Pid would keep same. - */ -int main(int argc, char *argv[], char *envp[]) -{ - // Try to get executable path - const char *filename = get_current_exec(); - if (filename == NULL) { - return -ENOENT; - } - - // If there is no env, stop hijack - const char *output_dir = get_hijacker_env(); - if (output_dir == NULL) { - return execve(filename, argv, envp); - } - - // If output dir is not a directory, stop hijack - struct stat output_dir_stat; - if ((stat(output_dir, &output_dir_stat) != 0) || - (!S_ISDIR(output_dir_stat.st_mode))) { - return execve(filename, argv, envp); - } - - // If there is no output, stop hijack - int output_index = find_output_flag(argc, argv); - if (output_index < 0) { - return execve(filename, argv, envp); - } - output_index += 1; - - // If the output is null device, stop hijack - const char *output_file = argv[output_index]; - if (strncmp(output_file, NULL_DEV_PATH, strlen(NULL_DEV_PATH)) == 0) { - return execve(filename, argv, envp); - } - - int new_argc = argc + APPEND_ARG_LEN + 1; // include terminator NULL - const char **new_argv = calloc(1, new_argc * sizeof(char *)); - if (new_argv == NULL) { - return execve(filename, argv, envp); - } - - // Copy original arguments - new_argc = 0; - for (int i = 0; i < argc; i++) { - if (argv[i] == NULL) { - break; - } - new_argv[new_argc++] = argv[i]; - } - - // Write new arguments - pid_t tid = (pid_t)syscall(SYS_gettid); - char *defsym_value = (char *)g_defsym; - char *new_output_file = (char *)g_new_output_file; - - snprintf(defsym_value, DEFSYM_MAX, DEFSYM_VALUE, tid); - new_argv[new_argc++] = DEFSYM_FLAG; - new_argv[new_argc++] = defsym_value; - new_argv[new_argc] = NULL; - - // Handle output file - snprintf(new_output_file, PATH_MAX, OUTPUT_PATH, output_dir, tid); - new_argv[output_index] = new_output_file; - - if (access(output_file, F_OK) == 0) { - (void)unlink(output_file); - } - - if (symlink(new_output_file, output_file) != 0) { - return execve(filename, argv, envp); - } - - return execve(filename, (char* const*)new_argv, envp); -} diff --git a/upatch-hijacker/hijacker/gnu-compiler-hijacker.c b/upatch-hijacker/hijacker/gnu-compiler-hijacker.c deleted file mode 100644 index d0410a220f2ece63351a8312b6b83448f2ac6bb4..0000000000000000000000000000000000000000 --- a/upatch-hijacker/hijacker/gnu-compiler-hijacker.c +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * gnu-compiler-hijacker is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#include - -#include "hijacker.h" - -static const char* APPEND_ARGS[] = { - "-gdwarf", /* obatain debug information */ - "-ffunction-sections", - "-fdata-sections", - "-frecord-gcc-switches", -}; -static const int APPEND_ARG_LEN = (int)(sizeof(APPEND_ARGS) / sizeof(char *)); - -/* - * The whole part: - * 1. Someone called execve() to run a compiler (inode). - * 2. If the inode was registered, under layer would rewrite argv[0] to hijacker path. - * 3. Hijacker would add some arguments and calls execve() again. - * 4. Under layer redirects argv[0] to original path. - * Pid would keep same. - */ -int main(int argc, char *argv[], char *envp[]) -{ - // Try to get executable path - const char *filename = get_current_exec(); - if (filename == NULL) { - return -ENOENT; - } - - // If there is no env, stop hijack - const char *hijacker_env = get_hijacker_env(); - if (hijacker_env == NULL) { - return execve(filename, argv, envp); - } - - // If there is no output, stop hijack - if (find_output_flag(argc, argv) < 0) { - return execve(filename, argv, envp); - } - - int new_argc = argc + APPEND_ARG_LEN + 1; // include terminator NULL - const char **new_argv = calloc(1, new_argc * sizeof(char *)); - if (new_argv == NULL) { - return execve(filename, argv, envp); - } - - // Copy original arguments - new_argc = 0; - for (int i = 0; i < argc; i++) { - if (argv[i] == NULL) { - break; - } - new_argv[new_argc++] = argv[i]; - } - - // Write new arguments - for (int i = 0; i < APPEND_ARG_LEN; i++) { - new_argv[new_argc++] = APPEND_ARGS[i]; - } - new_argv[new_argc] = NULL; - - return execve(filename, (char* const*)new_argv, envp); -} diff --git a/upatch-hijacker/hijacker/hijacker.h b/upatch-hijacker/hijacker/hijacker.h deleted file mode 100644 index 2a41ab2e30ba89a89ee982a629b47e23487feb20..0000000000000000000000000000000000000000 --- a/upatch-hijacker/hijacker/hijacker.h +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * gnu-compiler-hijacker is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -#ifndef __UPATCH_HIJACKER_COMMON_H -#define __UPATCH_HIJACKER_COMMON_H - -#include -#include -#include -#include - -#include - -static const char *UPATCH_ENV_NAME = "UPATCH_HIJACKER"; -static const char *EXEC_SELF_PATH = "/proc/self/exe"; -static const char *OUTPUT_FLAG_NAME = "-o"; - -static char g_filename[PATH_MAX] = { 0 }; - -static inline char* get_current_exec() -{ - ssize_t path_len = readlink(EXEC_SELF_PATH, (char *)g_filename, PATH_MAX); - if (path_len == -1) { - return NULL; - } - g_filename[path_len] = '\0'; - - return (char *)g_filename; -} - -static inline const char* get_hijacker_env() -{ - return getenv(UPATCH_ENV_NAME); -} - -static inline int find_output_flag(int argc, char* const argv[]) -{ - for (int i = 0; i < argc; i++) { - if (argv[i] == NULL) { - break; - } - if (strncmp(argv[i], OUTPUT_FLAG_NAME, 2) == 0) { - return i; - } - } - - return -EINVAL; -} - -#endif /* __UPATCH_HIJACKER_COMMON_H */ diff --git a/upatch-hijacker/ko/CMakeLists.txt b/upatch-hijacker/ko/CMakeLists.txt deleted file mode 100644 index 9d3c67cecff76dfac1faaa74f0b03ad43c9a4ae5..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Build upatch-hijacker kernel module - -# Set target -set(UPATCH_HIJACKER_KMOD "upatch_hijacker.ko") - -# Detect kernel source path -if (DEFINED KERNEL_VERSION) - set(KERNEL_SOURCE_PATH "/lib/modules/${KERNEL_VERSION}/build") - set(UPATCH_HIJACKER_KMOD_BUILD_CMD make module_version=${BUILD_VERSION} kernel=${KERNEL_SOURCE_PATH}) -else() - set(UPATCH_HIJACKER_KMOD_BUILD_CMD make module_version=${BUILD_VERSION}) -endif() - -# Build kernel module -add_custom_target(upatch-hijacker-kmod ALL - COMMENT "Building kernel module upatch-hijacker..." - BYPRODUCTS ${UPATCH_HIJACKER_KMOD} - COMMAND ${UPATCH_HIJACKER_KMOD_BUILD_CMD} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -# Install kernel module -install( - FILES - ${UPATCH_HIJACKER_KMOD} - PERMISSIONS - OWNER_WRITE OWNER_READ - GROUP_READ - WORLD_READ - DESTINATION - ${SYSCARE_LIBEXEC_DIR} -) diff --git a/upatch-hijacker/ko/LICENSE b/upatch-hijacker/ko/LICENSE deleted file mode 100644 index d159169d1050894d3ea3b98e1c965c4058208fe1..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/upatch-hijacker/ko/Makefile b/upatch-hijacker/ko/Makefile deleted file mode 100644 index ebf6314cfe14773b1ea74370859798be24c88344..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -module_name ?= upatch_hijacker -module_version ?= "1.0-dev" -kernel ?= /lib/modules/$(shell uname -r)/build - -ccflags-y += -O2 -ccflags-y += -DBUILD_VERSION=\"$(module_version)\" -ccflags-y += -I$(PWD)/.. -ccflags-y += -Werror -Wall -ccflags-y += -fstack-protector-strong -ccflags-y += -Wl,-Bsymbolic -Wl,-no-undefined -Wl,-z,now -Wl,-z,noexecstack - -obj-m += $(module_name).o - -$(module_name)-objs := map.o records.o context.o -$(module_name)-objs += cache.o uprobe.o ioctl.o main.o - -all: - make -C $(kernel) M=$(shell pwd) modules - -clean: - make -C $(kernel) M=$(shell pwd) clean diff --git a/upatch-hijacker/ko/cache.c b/upatch-hijacker/ko/cache.c deleted file mode 100644 index b5af6bfb73152299065857bab2e5212ea8e58bc0..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/cache.c +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "cache.h" - -#include - -#include "log.h" - -static const char *CACHE_SLAB_NAME = "upatch_hijacker"; - -static struct kmem_cache *g_path_cache = NULL; - -int cache_init(void) -{ - g_path_cache = kmem_cache_create_usercopy(CACHE_SLAB_NAME, - PATH_MAX, 0, SLAB_MEM_SPREAD | SLAB_ACCOUNT | SLAB_RECLAIM_ACCOUNT, - 0, PATH_MAX, NULL); - if (g_path_cache == NULL) { - pr_err("failed to create slab '%s'\n", CACHE_SLAB_NAME); - return -ENOMEM; - } - return 0; -} - -void cache_exit(void) -{ - kmem_cache_destroy(g_path_cache); -} - -char *path_buf_alloc(void) -{ - return kmem_cache_alloc(g_path_cache, GFP_KERNEL); -} - -void path_buf_free(char *buff) -{ - if (buff == NULL) { - return; - } - kmem_cache_free(g_path_cache, buff); -} diff --git a/upatch-hijacker/ko/cache.h b/upatch-hijacker/ko/cache.h deleted file mode 100644 index e26a49c76d52370ac3dcf1ab5be77f34eaa4867b..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/cache.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_CACHE_H -#define _UPATCH_HIJACKER_KO_CACHE_H - -int cache_init(void); -void cache_exit(void); - -char *path_buf_alloc(void); -void path_buf_free(char *buff); - -#endif /* _UPATCH_HIJACKER_KO_CACHE_H */ diff --git a/upatch-hijacker/ko/context.c b/upatch-hijacker/ko/context.c deleted file mode 100644 index 2e406f45d84772e307614c18e255f071459b9a75..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/context.c +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "context.h" - -#include -#include -#include -#include - -#include "log.h" -#include "map.h" -#include "records.h" -#include "uprobe.h" -#include "utils.h" - -struct context { - struct pid_namespace *ns; - struct uprobe_record *uprobe; - struct map *hijacker_map; -}; - -static bool find_hijacker_context(const struct context *context, - const struct pid_namespace *ns); -static void free_hijacker_context(struct context *context); - -static const struct map_ops HIJACK_MAP_OPS = { - .find_value = (find_value_fn)find_hijacker_record, - .free_value = (free_value_fn)free_hijacker_record, -}; -static const struct map_ops CONTEXT_MAP_OPS = { - .find_value = (find_value_fn)find_hijacker_context, - .free_value = (free_value_fn)free_hijacker_context, -}; - -static const size_t MAX_CONTEXT_NUM = 1024; -static const size_t HIJACKER_PER_CONTEXT = 16; - -static struct map *g_context_map = NULL; - -/* Context private interface */ -static int create_hijacker_context(struct context **context, - struct pid_namespace *ns, const char *path, loff_t offset) -{ - struct context *new_context = NULL; - struct uprobe_record *uprobe = NULL; - struct map *hijacker_map = NULL; - int ret = 0; - - new_context = kzalloc(sizeof(struct context), GFP_KERNEL); - if (new_context == NULL) { - pr_err("failed to alloc context\n"); - return -ENOMEM; - } - - ret = new_map(&hijacker_map, HIJACKER_PER_CONTEXT, &HIJACK_MAP_OPS); - if (ret != 0) { - pr_err("failed to create hijacker map, ret=%d\n", ret); - kfree(new_context); - return ret; - } - - ret = new_uprobe_record(&uprobe, handle_uprobe, path, offset); - if (ret != 0) { - pr_err("failed to create uprobe record, ret=%d\n", ret); - free_map(hijacker_map); - kfree(new_context); - return ret; - } - - ret = uprobe_register(uprobe->inode, uprobe->offset, uprobe->uc); - if (ret != 0) { - pr_err("failed to register uprobe, inode=%lu, offset=0x%llx, ret=%d\n", - uprobe->inode->i_ino, uprobe->offset, ret); - free_uprobe_record(uprobe); - free_map(hijacker_map); - kfree(new_context); - return ret; - } - - new_context->ns = get_pid_ns(ns); - new_context->uprobe = uprobe; - new_context->hijacker_map = hijacker_map; - - *context = new_context; - return 0; -} - -static void free_hijacker_context(struct context *context) -{ - if (context == NULL) { - return; - } - - uprobe_unregister(context->uprobe->inode, context->uprobe->offset, - context->uprobe->uc); - - put_pid_ns(context->ns); - free_uprobe_record(context->uprobe); - free_map(context->hijacker_map); - kfree(context); -} - -static bool find_hijacker_context(const struct context *context, - const struct pid_namespace *ns) -{ - return ns_equal(context->ns, ns); -} - -/* Context public interface */ -int context_init(void) -{ - int ret = 0; - - ret = new_map(&g_context_map, MAX_CONTEXT_NUM, &CONTEXT_MAP_OPS); - if (ret != 0) { - pr_err("failed to create context map, ret=%d\n", ret); - return ret; - } - - return 0; -} - -void context_exit(void) -{ - free_map(g_context_map); -} - -int build_hijacker_context(const char *path, loff_t offset) -{ - struct pid_namespace *ns = task_active_pid_ns(current); - struct context *context = NULL; - int ret = 0; - - if ((path == NULL) || (offset == 0)) { - return -EINVAL; - } - - ret = create_hijacker_context(&context, ns, path, offset); - if (ret != 0) { - pr_err("failed to create hijacker context, ret=%d\n", ret); - return ret; - } - - pr_debug("hijacker context, addr=0x%lx\n", (unsigned long)context); - ret = map_insert(g_context_map, context); - if (ret != 0) { - pr_err("failed to register hijacker context, ret=%d\n", ret); - return ret; - } - - return 0; -} - -void destroy_hijacker_context(void) -{ - pr_debug("destroy hijacker context\n"); - map_remove(g_context_map, task_active_pid_ns(current)); -} - -size_t hijacker_context_count(void) -{ - return map_size(g_context_map); -} - -struct map *get_hijacker_map(void) -{ - struct pid_namespace *ns = task_active_pid_ns(current); - struct context *context = (struct context *)map_get(g_context_map, ns); - - return (context != NULL) ? context->hijacker_map : NULL; -} diff --git a/upatch-hijacker/ko/context.h b/upatch-hijacker/ko/context.h deleted file mode 100644 index 6b5a50f6adb7cd35156e0c3ac9b1fb0854bad296..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/context.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_CONTEXT_H -#define _UPATCH_HIJACKER_KO_CONTEXT_H - -#include - -struct map; - -int context_init(void); -void context_exit(void); - -int build_hijacker_context(const char *path, loff_t offset); -void destroy_hijacker_context(void); -size_t hijacker_context_count(void); - -struct map *get_hijacker_map(void); - -#endif /* _UPATCH_HIJACKER_KO_CONTEXT_H */ diff --git a/upatch-hijacker/ko/ioctl.c b/upatch-hijacker/ko/ioctl.c deleted file mode 100644 index f76b5fb14aa909184a2a7c4665002e6fc6029ba1..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/ioctl.c +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "ioctl.h" - -#include -#include -#include -#include -#include - -#include "log.h" -#include "map.h" -#include "records.h" -#include "context.h" -#include "utils.h" - -static const struct file_operations HIJACKER_DEV_FOPS = { - .owner = THIS_MODULE, - .unlocked_ioctl = handle_ioctl, -}; - -static struct miscdevice g_hijacker_dev = { - .minor = MISC_DYNAMIC_MINOR, - .mode = UPATCH_HIJACKER_DEV_MODE, - .name = UPATCH_HIJACKER_DEV_NAME, - .fops = &HIJACKER_DEV_FOPS, -}; - -static inline int handle_enable_hijacker(void __user *arg) -{ - int ret = 0; - upatch_enable_request_t *msg = NULL; - - msg = kzalloc(sizeof(upatch_enable_request_t), GFP_KERNEL); - if (msg == NULL) { - pr_err("failed to alloc message\n"); - return -ENOMEM; - } - - ret = copy_from_user(msg, arg, sizeof(upatch_enable_request_t)); - if (ret != 0) { - pr_err("failed to copy message from user space\n"); - kfree(msg); - return -EFAULT; - } - - pr_debug("enable hijacker, path=%s, offset=0x%llx\n", msg->path, msg->offset); - ret = build_hijacker_context(msg->path, msg->offset); - if (ret != 0) { - pr_err("failed to build hijacker context, ret=%d\n", ret); - kfree(msg); - } - - kfree(msg); - return 0; -} - -static inline void handle_disable_hijacker(void) -{ - pr_debug("disable hijacker\n"); - destroy_hijacker_context(); -} - -static inline int handle_register_hijacker(void __user *arg) -{ - upatch_register_request_t *msg = NULL; - struct map *hijacker_map = get_hijacker_map(); - struct hijacker_record *record = NULL; - int ret = 0; - - if (hijacker_map == NULL) { - pr_err("failed to get hijacker map\n"); - return -EFAULT; - } - - msg = kzalloc(sizeof(upatch_register_request_t), GFP_KERNEL); - if (msg == NULL) { - pr_err("failed to alloc message\n"); - return -ENOMEM; - } - - ret = copy_from_user(msg, arg, sizeof(upatch_register_request_t)); - if (ret != 0) { - pr_err("failed to copy message from user space\n"); - kfree(msg); - return -EFAULT; - } - - ret = create_hijacker_record(&record, msg->exec_path, msg->jump_path); - if (ret != 0) { - pr_err("failed to create hijacker record [%s -> %s], ret=%d\n", - msg->exec_path, msg->jump_path, ret); - kfree(msg); - return ret; - } - - pr_debug("register hijacker, inode=%lu, addr=0x%lx\n", - record->exec_inode->i_ino, (unsigned long)record); - ret = map_insert(get_hijacker_map(), record); - if (ret != 0) { - pr_err("failed to register hijacker record [%s -> %s], ret=%d\n", - msg->exec_path, msg->jump_path, ret); - free_hijacker_record(record); - kfree(msg); - return ret; - } - - kfree(msg); - return 0; -} - -static inline int handle_unregister_hijacker(void __user *arg) -{ - upatch_register_request_t *msg = NULL; - struct map *hijacker_map = get_hijacker_map(); - struct inode *inode = NULL; - - int ret = 0; - - if (hijacker_map == NULL) { - pr_err("failed to get hijacker map\n"); - return -EFAULT; - } - - - msg = kzalloc(sizeof(upatch_register_request_t), GFP_KERNEL); - if (msg == NULL) { - pr_err("failed to alloc message\n"); - return -ENOMEM; - } - - ret = copy_from_user(msg, arg, sizeof(upatch_register_request_t)); - if (ret != 0) { - pr_err("failed to copy message from user space\n"); - kfree(msg); - return -EFAULT; - } - - inode = path_inode(msg->exec_path); - if (inode == NULL) { - pr_err("failed to get file inode, path=%s\n", msg->exec_path); - kfree(msg); - return -ENOENT; - } - - pr_debug("remove hijacker, inode=%lu\n", inode->i_ino); - map_remove(hijacker_map, inode); - - kfree(msg); - return 0; -} - -int ioctl_init(void) -{ - int ret = 0; - - ret = misc_register(&g_hijacker_dev); - if (ret != 0) { - pr_err("failed to register misc device, ret=%d\n", ret); - } - - return ret; -} - -void ioctl_exit(void) -{ - misc_deregister(&g_hijacker_dev); -} - -long handle_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret = 0; - - if (_IOC_TYPE(cmd) != UPATCH_HIJACKER_IOC_MAGIC) { - pr_info("invalid command\n"); - return -EBADMSG; - } - - switch (cmd) { - case UPATCH_HIJACKER_ENABLE: - ret = handle_enable_hijacker((void __user *)arg); - break; - case UPATCH_HIJACKER_DISABLE: - handle_disable_hijacker(); - break; - case UPATCH_HIJACKER_REGISTER: - ret = handle_register_hijacker((void __user *)arg); - break; - case UPATCH_HIJACKER_UNREGISTER: - ret = handle_unregister_hijacker((void __user *)arg); - break; - default: - ret = -EBADMSG; - break; - } - - return (long)ret; -} diff --git a/upatch-hijacker/ko/ioctl.h b/upatch-hijacker/ko/ioctl.h deleted file mode 100644 index dbcd12e42346d0ca89db1804e1276e900c9874ed..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/ioctl.h +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_IOCTL_H -#define _UPATCH_HIJACKER_KO_IOCTL_H - -#include -#include - -#define UPATCH_HIJACKER_DEV_NAME "upatch-hijacker" -#define UPATCH_HIJACKER_DEV_MODE 0600 - -#define UPATCH_HIJACKER_IOC_MAGIC 0xE5 -#define UPATCH_HIJACKER_ENABLE _IOW(UPATCH_HIJACKER_IOC_MAGIC, 0x1, \ - upatch_enable_request_t) -#define UPATCH_HIJACKER_DISABLE _IO(UPATCH_HIJACKER_IOC_MAGIC, 0x2) -#define UPATCH_HIJACKER_REGISTER _IOW(UPATCH_HIJACKER_IOC_MAGIC, 0x3, \ - upatch_register_request_t) -#define UPATCH_HIJACKER_UNREGISTER _IOW(UPATCH_HIJACKER_IOC_MAGIC, 0x4, \ - upatch_register_request_t) - -typedef struct { - char path[PATH_MAX]; - loff_t offset; -} upatch_enable_request_t; - -typedef struct { - char exec_path[PATH_MAX]; - char jump_path[PATH_MAX]; -} upatch_register_request_t; - -struct file; - -int ioctl_init(void); -void ioctl_exit(void); -long handle_ioctl(struct file *file, unsigned int cmd, unsigned long arg); - -#endif /* _UPATCH_HIJACKER_KO_IOCTL_H */ diff --git a/upatch-hijacker/ko/log.h b/upatch-hijacker/ko/log.h deleted file mode 100644 index 2aede0183b0b1c6dacfd2bc38195b1a9141304f6..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/log.h +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_LOG_H -#define _UPATCH_HIJACKER_KO_LOG_H - -#include -#include - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#define pr_fmt(fmt) "%s: " fmt, THIS_MODULE->name - -#endif /* _UPATCH_HIJACKER_KO_LOG_H */ diff --git a/upatch-hijacker/ko/main.c b/upatch-hijacker/ko/main.c deleted file mode 100644 index e14796e60f331913f64ce97fa08ff5936b4193f8..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/main.c +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include - -#include "log.h" -#include "cache.h" -#include "context.h" -#include "ioctl.h" - -static int __init upatch_hijacker_init(void) -{ - int ret = 0; - - ret = context_init(); - if (ret != 0) { - pr_err("failed to init context, ret=%d\n", ret); - return ret; - } - - ret = cache_init(); - if (ret != 0) { - pr_err("failed to init cache, ret=%d\n", ret); - return ret; - } - - ret = ioctl_init(); - if (ret != 0) { - pr_err("failed to init ioctl, ret=%d\n", ret); - return ret; - } - - pr_info("%s %s initialized\n", THIS_MODULE->name, THIS_MODULE->version); - return 0; -} - -static void __exit upatch_hijacker_exit(void) -{ - ioctl_exit(); - cache_exit(); - context_exit(); -} - -module_init(upatch_hijacker_init); -module_exit(upatch_hijacker_exit); - -MODULE_AUTHOR("renoseven (dev@renoseven.net)"); -MODULE_DESCRIPTION("upatch compiler hijacker"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(BUILD_VERSION); diff --git a/upatch-hijacker/ko/map.c b/upatch-hijacker/ko/map.c deleted file mode 100644 index 3049556705d9b3ffbb606099ab24b0bbbc04e32d..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/map.c +++ /dev/null @@ -1,237 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "map.h" - -#include -#include -#include - -#include "log.h" - -struct map_entry { - struct map *parent; - void *value; - struct kref ref; -}; - -struct map { - struct mutex lock; - size_t length; - size_t capacity; - const struct map_ops *ops; - struct map_entry entries[]; -}; - -/* Map private interface */ -static inline void insert_entry(struct map_entry *entry, struct map *parent, - void *value) -{ - pr_debug("insert map entry, map=0x%lx, index=%lu, value=0x%lx\n", - (unsigned long)parent, (entry - &parent->entries[0]), - (unsigned long)value); - entry->parent = parent; - entry->value = value; - kref_init(&entry->ref); -} - -static inline void remove_entry(struct map_entry *entry) -{ - struct map *parent = entry->parent; - void *value = entry->value; - - if (value == NULL) { - return; - } - - pr_debug("remove map entry, map=0x%lx, index=%lu\n", (unsigned long)parent, - (entry - &parent->entries[0])); - entry->parent = NULL; - entry->value = NULL; - parent->ops->free_value(value); -} - -static inline void release_entry(struct kref *kref) -{ - remove_entry(container_of(kref, struct map_entry, ref)); -} - -static inline struct map_entry *lookup_entry(struct map *map, const void *param) -{ - struct map_entry *entry = NULL; - size_t i = 0; - - for (i = 0; i < map->capacity; i++) { - entry = &map->entries[i]; - if (entry->value == NULL) { - continue; - } - if (map->ops->find_value(entry->value, param)) { - return entry; - } - } - - return NULL; -} - -static inline struct map_entry *lookup_free_entry(struct map *map) -{ - size_t i = 0; - - for (i = 0; i < map->capacity; i++) { - if (map->entries[i].value == NULL) { - return &map->entries[i]; - } - } - - return NULL; -} - -/* Map public interface */ -int new_map(struct map **map, size_t capacity, const struct map_ops *ops) -{ - struct map *new_map = NULL; - size_t map_size = 0; - - if ((map == NULL) || (capacity == 0) || (ops == NULL)) { - return -EINVAL; - } - - map_size += sizeof(struct map); - map_size += sizeof(struct map_entry) * capacity; - - new_map = kzalloc(map_size, GFP_KERNEL); - if (new_map == NULL) { - return -ENOMEM; - } - - mutex_init(&new_map->lock); - new_map->ops = ops; - new_map->capacity = capacity; - - *map = new_map; - return 0; -} - -void free_map(struct map *map) -{ - size_t capacity = 0; - size_t i = 0; - - if (map == NULL) { - return; - } - - mutex_lock(&map->lock); - - capacity = map->capacity; - map->length = 0; - map->capacity = 0; - for (i = 0; i < capacity; i++) { - remove_entry(&map->entries[i]); - } - - mutex_unlock(&map->lock); - mutex_destroy(&map->lock); - - kfree(map); -} - -int map_insert(struct map *map, void *value) -{ - struct map_entry *entry = NULL; - - if ((map == NULL) || (value == NULL)) { - return -EINVAL; - } - - /* - * try to find the record - * if found, increase refence - * if not found, create a new entry - */ - mutex_lock(&map->lock); - - entry = lookup_entry(map, value); - if (entry != NULL) { - mutex_unlock(&map->lock); - kref_get(&entry->ref); - return 0; - } - - entry = lookup_free_entry(map); - if (entry == NULL) { - mutex_unlock(&map->lock); - return -ENOBUFS; - } - - insert_entry(entry, map, value); - map->length++; - - mutex_unlock(&map->lock); - - return 0; -} - -void map_remove(struct map *map, const void *param) -{ - struct map_entry *entry = NULL; - - if ((map == NULL) || (param == NULL)) { - return; - } - - mutex_lock(&map->lock); - - entry = lookup_entry(map, param); - if (entry == NULL) { - mutex_unlock(&map->lock); - return; - } - - // decrease reference and try to free - if (kref_put(&entry->ref, release_entry)) { - map->length--; - }; - - mutex_unlock(&map->lock); -} - -void *map_get(struct map *map, const void *param) -{ - struct map_entry *entry = NULL; - - if ((map == NULL) || (param == NULL)) { - return NULL; - } - - mutex_lock(&map->lock); - - entry = lookup_entry(map, param); - - mutex_unlock(&map->lock); - - return (entry != NULL) ? entry->value : NULL; -} - -size_t map_size(const struct map *map) -{ - return (map != NULL) ? map->length : 0; -} diff --git a/upatch-hijacker/ko/map.h b/upatch-hijacker/ko/map.h deleted file mode 100644 index 37b522bbeb4a7ee0f819a51f458814cb2d04d911..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/map.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_MAP_H -#define _UPATCH_HIJACKER_KO_MAP_H - -#include - -typedef bool (*find_value_fn)(const void *value, const void *param); -typedef void (*free_value_fn)(void *value); - -struct map_ops { - find_value_fn find_value; - free_value_fn free_value; -}; -struct map; - -int new_map(struct map **map, size_t capacity, const struct map_ops *ops); -void free_map(struct map *map); - -int map_insert(struct map *map, void *value); -void map_remove(struct map *map, const void *param); -void *map_get(struct map *map, const void *param); -size_t map_size(const struct map *map); - -#endif /* _UPATCH_HIJACKER_KO_MAP_H */ diff --git a/upatch-hijacker/ko/records.c b/upatch-hijacker/ko/records.c deleted file mode 100644 index ef1e3fd4a3008cf4c1c5cc5ed46ede225b3b428a..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/records.c +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "records.h" - -#include -#include -#include - -#include "log.h" -#include "map.h" -#include "utils.h" - -int new_uprobe_record(struct uprobe_record **record, uprobe_handler handler, - const char *path, loff_t offset) -{ - struct inode *inode = NULL; - struct uprobe_consumer *uc = NULL; - struct uprobe_record *new_record = NULL; - - if ((record == NULL) || (handler == NULL) || - (path == NULL) || (offset == 0)) { - return -EINVAL; - } - - inode = path_inode(path); - if (inode == NULL) { - pr_err("failed to get file inode, path=%s\n", path); - return -ENOENT; - } - - new_record = kzalloc(sizeof(struct uprobe_record), GFP_KERNEL); - if (new_record == NULL) { - return -ENOMEM; - } - - uc = kzalloc(sizeof(struct uprobe_consumer), GFP_KERNEL); - if (uc == NULL) { - kfree(new_record); - return -ENOMEM; - } - uc->handler = handler; - - new_record->inode = igrab(inode); - new_record->offset = offset; - new_record->uc = uc; - - *record = new_record; - return 0; -} - -void free_uprobe_record(struct uprobe_record *record) -{ - if (record == NULL) { - return; - } - iput(record->inode); - kfree(record->uc); - kfree(record); -} - -int create_hijacker_record(struct hijacker_record **record, - const char *exec_path, const char *jump_path) -{ - struct hijacker_record *new_record = NULL; - struct inode *exec_inode = NULL; - struct inode *jump_inode = NULL; - - if ((record == NULL) || (exec_path == NULL) || (jump_path == NULL)) { - return -EINVAL; - } - - exec_inode = path_inode(exec_path); - if (exec_inode == NULL) { - pr_err("failed to get file inode, path=%s\n", exec_path); - return -ENOENT; - } - - jump_inode = path_inode(jump_path); - if (jump_inode == NULL) { - pr_err("failed to get file inode, path=%s\n", jump_path); - return -ENOENT; - } - - new_record = kzalloc(sizeof(struct hijacker_record), GFP_KERNEL); - if (record == NULL) { - return -ENOMEM; - } - - new_record->exec_inode = igrab(exec_inode); - new_record->jump_inode = igrab(jump_inode); - strlcpy(new_record->exec_path, exec_path, PATH_MAX); - strlcpy(new_record->jump_path, jump_path, PATH_MAX); - - *record = new_record; - return 0; -} - -void free_hijacker_record(struct hijacker_record *record) -{ - if (record == NULL) { - return; - } - - iput(record->exec_inode); - iput(record->jump_inode); - kfree(record); -} - -bool find_hijacker_record(const struct hijacker_record *record, - const struct inode *inode) -{ - return (inode_equal(record->exec_inode, inode) || - inode_equal(record->jump_inode, inode)); -} diff --git a/upatch-hijacker/ko/records.h b/upatch-hijacker/ko/records.h deleted file mode 100644 index 759ed5e3d5c90dc6412d2997e9fd7a45e15c0130..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/records.h +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_ENTITY_H -#define _UPATCH_HIJACKER_KO_ENTITY_H - -#include -#include - -struct inode; -struct uprobe_consumer; -struct pt_regs; - -typedef int (*uprobe_handler)(struct uprobe_consumer *uc, struct pt_regs *regs); - -struct uprobe_record { - struct inode *inode; - loff_t offset; - struct uprobe_consumer *uc; -}; - -struct hijacker_record { - struct inode *exec_inode; - struct inode *jump_inode; - char exec_path[PATH_MAX]; - char jump_path[PATH_MAX]; -}; - -int new_uprobe_record(struct uprobe_record **record, - uprobe_handler handler, const char *path, loff_t offset); -void free_uprobe_record(struct uprobe_record *record); - -int create_hijacker_record(struct hijacker_record **record, - const char *exec_path, const char *jump_path); -void free_hijacker_record(struct hijacker_record *record); -bool find_hijacker_record(const struct hijacker_record *record, - const struct inode *inode); - -#endif /* _UPATCH_HIJACKER_KO_ENTITY_H */ diff --git a/upatch-hijacker/ko/uprobe.c b/upatch-hijacker/ko/uprobe.c deleted file mode 100644 index bc3c639aafbaedce422a1cdd636da9bf256ed5b6..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/uprobe.c +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "uprobe.h" - -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "map.h" -#include "context.h" -#include "records.h" -#include "cache.h" -#include "utils.h" - -#ifdef __x86_64__ -#define _reg_argv0 regs->di -#endif - -#ifdef __aarch64__ -#define _reg_argv0 regs->regs[0] -#endif - -/* Uprobe private interface */ -static inline char* read_user_str(char *dst, const char __user *src, size_t count) -{ - size_t len = strncpy_from_user(dst, src, (long)count); - if (len <= 0) { - pr_err("failed to read from user space\n"); - return NULL; - } - dst[len] = '\0'; - - return dst; -} - -static inline const char __user *new_user_str(const char *src, size_t len) -{ - unsigned long addr = vm_mmap(NULL, 0, len, - PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0); - - if (addr == 0) { - pr_err_ratelimited("failed to alloc in userspace\n"); - return NULL; - } - - if (copy_to_user((void *)addr, src, len) != 0) { - pr_err_ratelimited("failed to write to userspace\n"); - (void)vm_munmap(addr, len); - return NULL; - } - - return (const char __user *)addr; -} - -static inline const char *select_jump_path(const struct hijacker_record *record, - const struct inode *inode) -{ - if (inode_equal(inode, record->exec_inode)) { - return record->jump_path; - } - if (inode_equal(inode, record->jump_inode)) { - return record->exec_path; - } - return NULL; -} - -/* Uprobe public interface */ -int handle_uprobe(struct uprobe_consumer *self, struct pt_regs *regs) -{ - const char __user *argv0 = (const char __user *)_reg_argv0; - const char __user *new_argv0 = NULL; - - struct map *hijacker_map = get_hijacker_map(); - const struct hijacker_record *record = NULL; - - const char *elf_path = NULL; - const char *jump_path = NULL; - - const struct inode *inode = NULL; - char *path_buff = NULL; - size_t path_len = 0; - - if ((argv0 == NULL) || (hijacker_context_count() == 0)) { - return 0; - } - - if (map_size(hijacker_map) == 0) { - return 0; - } - - path_buff = path_buf_alloc(); - if (path_buff == NULL) { - pr_err_ratelimited("failed to alloc path cache\n"); - return 0; - } - - elf_path = read_user_str(path_buff, argv0, PATH_MAX); - if (elf_path == NULL) { - pr_err_ratelimited("failed to read execve argument from userspace\n"); - path_buf_free(path_buff); - return 0; - } - - inode = path_inode(elf_path); - if (inode == NULL) { - path_buf_free(path_buff); - return 0; - } - - record = (const struct hijacker_record *)map_get(hijacker_map, inode); - if (record == NULL) { - pr_debug("record not found, elf_path=%s\n", elf_path); - path_buf_free(path_buff); - return 0; - } - - jump_path = select_jump_path(record, inode); - if (jump_path == NULL) { - pr_err_ratelimited("failed to find jump path, elf_path=%s\n", elf_path); - path_buf_free(path_buff); - return 0; - } - path_len = strnlen(jump_path, PATH_MAX) + 1; - pr_debug("[hijacked] elf_path=%s, jump_path=%s\n", elf_path, jump_path); - - new_argv0 = new_user_str(jump_path, path_len); - if (new_argv0 == NULL) { - pr_err_ratelimited("failed to write new execve argument\n"); - path_buf_free(path_buff); - return 0; - } - - path_buf_free(path_buff); - - // We won't free new allocated userspace memory - // since it would be used by execve - _reg_argv0 = (unsigned long)new_argv0; - - return 0; // always return 0, so that execve would never fail -} diff --git a/upatch-hijacker/ko/uprobe.h b/upatch-hijacker/ko/uprobe.h deleted file mode 100644 index 06564d50662f2210220bb95bfac91e4004362cfd..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/uprobe.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_UPROBE_H -#define _UPATCH_HIJACKER_KO_UPROBE_H - -#include - -struct uprobe_consumer; -struct pt_regs; - -int handle_uprobe(struct uprobe_consumer *self, struct pt_regs *regs); - -#endif /* _UPATCH_HIJACKER_KO_UPROBE_H */ diff --git a/upatch-hijacker/ko/utils.h b/upatch-hijacker/ko/utils.h deleted file mode 100644 index 5e2f7eda707f50c7fc6f16bfcd7e20dbd24f5780..0000000000000000000000000000000000000000 --- a/upatch-hijacker/ko/utils.h +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-hijacker kernel module - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef _UPATCH_HIJACKER_KO_UTILS_H -#define _UPATCH_HIJACKER_KO_UTILS_H - -#include -#include -#include -#include - -static inline struct inode* path_inode(const char *path) -{ - struct path kpath; - - if (kern_path(path, LOOKUP_NO_SYMLINKS, &kpath) != 0) { - return NULL; - } - return kpath.dentry->d_inode; -} - -static inline bool inode_equal(const struct inode *lhs, const struct inode *rhs) -{ - return (lhs->i_ino == rhs->i_ino); -} - -static inline bool ns_equal(const struct pid_namespace *lhs, - const struct pid_namespace *rhs) -{ - return (lhs->ns.inum == rhs->ns.inum); -} - -#endif /* _UPATCH_HIJACKER_KO_UTILS_H */ diff --git a/upatch-manage/CMakeLists.txt b/upatch-manage/CMakeLists.txt index 850a308e49ab210c27542a46e7be20749c1d8f5f..1f7ecd04c2ff1932e37f332774692aa68f85231a 100644 --- a/upatch-manage/CMakeLists.txt +++ b/upatch-manage/CMakeLists.txt @@ -2,10 +2,6 @@ set(UPATCH_MANAGE "upatch-manage") -EXECUTE_PROCESS(COMMAND uname -m - OUTPUT_VARIABLE ARCH - OUTPUT_STRIP_TRAILING_WHITESPACE) - set(ARCH_PATH arch/${ARCH}) include_directories( @@ -25,9 +21,8 @@ install( TARGETS ${UPATCH_MANAGE} PERMISSIONS - OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ - WORLD_READ WORLD_EXECUTE + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE DESTINATION ${SYSCARE_LIBEXEC_DIR} ) diff --git a/upatch-manage/arch/aarch64/insn.c b/upatch-manage/arch/aarch64/insn.c index 8f78ae105fb19eabcda32ae98b16ba9221b9cecb..9a775a5a93d040a648c62565873bc69f45182aea 100644 --- a/upatch-manage/arch/aarch64/insn.c +++ b/upatch-manage/arch/aarch64/insn.c @@ -11,120 +11,120 @@ #include "insn.h" static int aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type, - u32 *maskp, int *shiftp) + u32 *maskp, int *shiftp) { - u32 mask; - int shift; - - switch (type) { - case AARCH64_INSN_IMM_26: - mask = BIT(26) - 1; - shift = 0; - break; - case AARCH64_INSN_IMM_19: - mask = BIT(19) - 1; - shift = 5; - break; - case AARCH64_INSN_IMM_16: - mask = BIT(16) - 1; - shift = 5; - break; - case AARCH64_INSN_IMM_14: - mask = BIT(14) - 1; - shift = 5; - break; - case AARCH64_INSN_IMM_12: - mask = BIT(12) - 1; - shift = 10; - break; - case AARCH64_INSN_IMM_9: - mask = BIT(9) - 1; - shift = 12; - break; - case AARCH64_INSN_IMM_7: - mask = BIT(7) - 1; - shift = 15; - break; - case AARCH64_INSN_IMM_6: - case AARCH64_INSN_IMM_S: - mask = BIT(6) - 1; - shift = 10; - break; - case AARCH64_INSN_IMM_R: - mask = BIT(6) - 1; - shift = 16; - break; - case AARCH64_INSN_IMM_N: - mask = 1; - shift = 22; - break; - default: - return -EINVAL; - } - - *maskp = mask; - *shiftp = shift; - - return 0; + u32 mask; + int shift; + + switch (type) { + case AARCH64_INSN_IMM_26: + mask = BIT(26) - 1; + shift = 0; + break; + case AARCH64_INSN_IMM_19: + mask = BIT(19) - 1; + shift = 5; + break; + case AARCH64_INSN_IMM_16: + mask = BIT(16) - 1; + shift = 5; + break; + case AARCH64_INSN_IMM_14: + mask = BIT(14) - 1; + shift = 5; + break; + case AARCH64_INSN_IMM_12: + mask = BIT(12) - 1; + shift = 10; + break; + case AARCH64_INSN_IMM_9: + mask = BIT(9) - 1; + shift = 12; + break; + case AARCH64_INSN_IMM_7: + mask = BIT(7) - 1; + shift = 15; + break; + case AARCH64_INSN_IMM_6: + case AARCH64_INSN_IMM_S: + mask = BIT(6) - 1; + shift = 10; + break; + case AARCH64_INSN_IMM_R: + mask = BIT(6) - 1; + shift = 16; + break; + case AARCH64_INSN_IMM_N: + mask = 1; + shift = 22; + break; + default: + return -EINVAL; + } + + *maskp = mask; + *shiftp = shift; + + return 0; } -u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, u32 insn, - u64 imm) +u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, + u32 insn, u64 imm) { - u32 immlo, immhi, mask; - int shift; - - if (insn == AARCH64_BREAK_FAULT) - return AARCH64_BREAK_FAULT; - - switch (type) { - case AARCH64_INSN_IMM_ADR: - shift = 0; - immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; - imm >>= ADR_IMM_HILOSPLIT; - immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; - imm = immlo | immhi; - mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | - (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); - break; - default: - if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { - log_error("upatch: unknown immediate encoding %d\n", - type); - return AARCH64_BREAK_FAULT; - } - } - - /* Update the immediate field. */ - insn &= ~(mask << shift); - insn |= (imm & mask) << shift; - - return insn; + u32 immlo; + u32 immhi; + u32 mask; + + int shift; + + if (insn == AARCH64_BREAK_FAULT) { + return AARCH64_BREAK_FAULT; + } + + switch (type) { + case AARCH64_INSN_IMM_ADR: + shift = 0; + immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; + imm >>= ADR_IMM_HILOSPLIT; + immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; + imm = immlo | immhi; + mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | + (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); + break; + default: + if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { + log_error("upatch: unknown immediate encoding %d\n", type); + return AARCH64_BREAK_FAULT; + } + } + + /* Update the immediate field. */ + insn &= ~(mask << shift); + insn |= (u32)(imm & mask) << shift; + + return insn; } -u64 extract_insn_imm(s64 sval, int len, int lsb) +s64 extract_insn_imm(s64 sval, int len, int lsb) { - u64 imm, imm_mask; + s64 imm; + s64 imm_mask; - imm = sval >> lsb; - imm_mask = (BIT(lsb + len) - 1) >> lsb; - imm = imm & imm_mask; + imm = sval >> lsb; + imm_mask = (s64)((BIT(lsb + len) - 1) >> lsb); + imm = imm & imm_mask; - log_debug("upatch: extract imm, X=0x%lx, X[%d:%d]=0x%lx\n", sval, - (len + lsb - 1), lsb, imm); - return imm; + log_debug("imm: insn=0x%lx, insn[%d:%d]=0x%lx\n", + sval, (len + lsb - 1), lsb, imm); + return imm; } -u32 insert_insn_imm(enum aarch64_insn_imm_type imm_type, void *place, u64 imm) +s32 insert_insn_imm(enum aarch64_insn_imm_type imm_type, void *place, u64 imm) { - u32 insn, new_insn; + u32 insn = le32_to_cpu(*(__le32 *)place); + u32 new_insn = aarch64_insn_encode_immediate(imm_type, insn, imm); - insn = le32_to_cpu(*(__le32 *)place); - new_insn = aarch64_insn_encode_immediate(imm_type, insn, imm); - - log_debug( - "upatch: insert imm, P=0x%lx, insn=0x%x, imm_type=%d, imm=0x%lx, " - "new_insn=0x%x\n", - (u64)place, insn, imm_type, imm, new_insn); - return new_insn; + log_debug("insn: insn=0x%x, imm_type=%d, imm=0x%lx, new_insn=0x%x\n", + insn, imm_type, imm, new_insn); + return (s32)new_insn; } diff --git a/upatch-manage/arch/aarch64/insn.h b/upatch-manage/arch/aarch64/insn.h index 2b97e8daef1c029359247670743ab6853871453b..8efc81b12e0935122bfb3bb7e545d7924c5fcafa 100644 --- a/upatch-manage/arch/aarch64/insn.h +++ b/upatch-manage/arch/aarch64/insn.h @@ -15,19 +15,19 @@ #include "upatch-relocation.h" enum aarch64_insn_imm_type { - AARCH64_INSN_IMM_ADR, - AARCH64_INSN_IMM_26, - AARCH64_INSN_IMM_19, - AARCH64_INSN_IMM_16, - AARCH64_INSN_IMM_14, - AARCH64_INSN_IMM_12, - AARCH64_INSN_IMM_9, - AARCH64_INSN_IMM_7, - AARCH64_INSN_IMM_6, - AARCH64_INSN_IMM_S, - AARCH64_INSN_IMM_R, - AARCH64_INSN_IMM_N, - AARCH64_INSN_IMM_MAX + AARCH64_INSN_IMM_ADR, + AARCH64_INSN_IMM_26, + AARCH64_INSN_IMM_19, + AARCH64_INSN_IMM_16, + AARCH64_INSN_IMM_14, + AARCH64_INSN_IMM_12, + AARCH64_INSN_IMM_9, + AARCH64_INSN_IMM_7, + AARCH64_INSN_IMM_6, + AARCH64_INSN_IMM_S, + AARCH64_INSN_IMM_R, + AARCH64_INSN_IMM_N, + AARCH64_INSN_IMM_MAX }; #define SZ_2M 0x00200000 @@ -61,11 +61,11 @@ enum aarch64_insn_imm_type { #define cpu_to_le32(val) bswap_32(val) #endif -u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, u32 insn, - u64 imm); +u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, + u32 insn, u64 imm); -u64 extract_insn_imm(s64, int, int); +s64 extract_insn_imm(s64 sval, int len, int lsb); -u32 insert_insn_imm(enum aarch64_insn_imm_type, void *, u64); +s32 insert_insn_imm(enum aarch64_insn_imm_type imm_type, void *place, u64 imm); #endif /* _ARCH_AARCH64_INSN_H */ diff --git a/upatch-manage/arch/aarch64/process.h b/upatch-manage/arch/aarch64/process.h deleted file mode 100644 index 8acf04bcdb4dd118bddf78fc92aa7a560487e543..0000000000000000000000000000000000000000 --- a/upatch-manage/arch/aarch64/process.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * upatch-manage - * Copyright (C) 2024 Huawei Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef __PROCESS__ -#define __PROCESS__ - -#ifndef MAX_DISTANCE -#define MAX_DISTANCE 0x8000000 -#endif - -#endif \ No newline at end of file diff --git a/upatch-manage/arch/aarch64/ptrace.c b/upatch-manage/arch/aarch64/ptrace.c index c51a236a70edde2bddf3467ef7494409d3392d6d..131e0dd34f101c268180a33f543e2150865e4159 100644 --- a/upatch-manage/arch/aarch64/ptrace.c +++ b/upatch-manage/arch/aarch64/ptrace.c @@ -21,182 +21,201 @@ #include #include #include +#include #include "insn.h" #include "upatch-ptrace.h" #define ORIGIN_INSN_LEN 16 -int upatch_arch_syscall_remote(struct upatch_ptrace_ctx *pctx, int nr, - unsigned long arg1, unsigned long arg2, - unsigned long arg3, unsigned long arg4, - unsigned long arg5, unsigned long arg6, - unsigned long *res) +#define UPATCH_INSN_LEN 8 +#define UPATCH_ADDR_LEN 8 + +int upatch_arch_reg_init(int pid, unsigned long *sp, unsigned long *pc) { - struct user_regs_struct regs; - unsigned char syscall[] = { - 0x01, 0x00, 0x00, 0xd4, // 0xd4000001 svc #0 = syscall - 0xa0, 0x00, 0x20, 0xd4, // 0xd42000a0 brk #5 = int3 - }; - int ret; - - log_debug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); - regs.regs[8] = (unsigned long)nr; - regs.regs[0] = arg1; - regs.regs[1] = arg2; - regs.regs[2] = arg3; - regs.regs[3] = arg4; - regs.regs[4] = arg5; - regs.regs[5] = arg6; - - ret = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); - if (ret == 0) - *res = regs.regs[0]; - - return ret; + struct iovec regs_iov; + struct user_regs_struct regs; + + regs_iov.iov_base = ®s; + regs_iov.iov_len = sizeof(regs); + + if (ptrace(PTRACE_GETREGSET, pid, + (void *)NT_PRSTATUS, (void *)®s_iov) < 0) { + log_error("Cannot get regs from %d\n", pid); + return -1; + } + *sp = (unsigned long)regs.sp; + *pc = (unsigned long)regs.pc; + + return 0; +} +long upatch_arch_syscall_remote(struct upatch_ptrace_ctx *pctx, int nr, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5, unsigned long arg6, + unsigned long *res) +{ + struct user_regs_struct regs; + unsigned char syscall[] = { + 0x01, 0x00, 0x00, 0xd4, // 0xd4000001 svc #0 = syscall + 0xa0, 0x00, 0x20, 0xd4, // 0xd42000a0 brk #5 = int3 + }; + long ret; + + log_debug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); + regs.regs[8] = (unsigned long long)nr; + regs.regs[0] = arg1; + regs.regs[1] = arg2; + regs.regs[2] = arg3; + regs.regs[3] = arg4; + regs.regs[4] = arg5; + regs.regs[5] = arg6; + + ret = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); + if (ret == 0) { + *res = regs.regs[0]; + } + + return ret; } -int upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, - const unsigned char *code, size_t codelen, - struct user_regs_struct *pregs, - int (*func)(struct upatch_ptrace_ctx *pctx, - const void *data), - const void *data) +long upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, const void *data), + const void *data) { - struct user_regs_struct orig_regs, regs; - struct iovec orig_regs_iov, regs_iov; - - orig_regs_iov.iov_base = &orig_regs; - orig_regs_iov.iov_len = sizeof(orig_regs); - regs_iov.iov_base = ®s; - regs_iov.iov_len = sizeof(regs); - - unsigned char orig_code[codelen]; - int ret; - struct upatch_process *proc = pctx->proc; - unsigned long libc_base = proc->libc_base; - - ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void *)NT_PRSTATUS, - (void *)&orig_regs_iov); - if (ret < 0) { - log_error("can't get regs - %d\n", pctx->pid); - return -1; - } - ret = upatch_process_mem_read(proc, libc_base, - (unsigned long *)orig_code, codelen); - if (ret < 0) { - log_error("can't peek original code - %d\n", pctx->pid); - return -1; - } - ret = upatch_process_mem_write(proc, (unsigned long *)code, libc_base, - codelen); - if (ret < 0) { - log_error("can't poke syscall code - %d\n", pctx->pid); - goto poke_back; - } - - regs = orig_regs; - regs.pc = libc_base; - - copy_regs(®s, pregs); - - ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void *)NT_PRSTATUS, - (void *)®s_iov); - if (ret < 0) { - log_error("can't set regs - %d\n", pctx->pid); - goto poke_back; - } - - ret = func(pctx, data); - if (ret < 0) { - log_error("failed call to func\n"); - goto poke_back; - } - - ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void *)NT_PRSTATUS, - (void *)®s_iov); - if (ret < 0) { - log_error("can't get updated regs - %d\n", pctx->pid); - goto poke_back; - } - - ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void *)NT_PRSTATUS, - (void *)&orig_regs_iov); - if (ret < 0) { - log_error("can't restore regs - %d\n", pctx->pid); - goto poke_back; - } - - *pregs = regs; + long ret; + + struct user_regs_struct orig_regs; + struct user_regs_struct regs; + + struct iovec orig_regs_iov; + struct iovec regs_iov; + + struct upatch_process *proc = pctx->proc; + unsigned long libc_base = proc->libc_base; + + unsigned char *orig_code = (unsigned char *)malloc( + sizeof(*orig_code) * codelen); + if (orig_code == NULL) { + log_error("Malloc orig_code failed\n"); + return -1; + } + + orig_regs_iov.iov_base = &orig_regs; + orig_regs_iov.iov_len = sizeof(orig_regs); + regs_iov.iov_base = ®s; + regs_iov.iov_len = sizeof(regs); + + ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void *)NT_PRSTATUS, + (void *)&orig_regs_iov); + if (ret < 0) { + log_error("can't get regs - %d\n", pctx->pid); + free(orig_code); + return -1; + } + ret = upatch_process_mem_read(proc, libc_base, + (unsigned long *)orig_code, codelen); + if (ret < 0) { + log_error("can't peek original code - %d\n", pctx->pid); + free(orig_code); + return -1; + } + ret = upatch_process_mem_write(proc, code, + libc_base, codelen); + if (ret < 0) { + log_error("can't poke syscall code - %d\n", pctx->pid); + goto poke_back; + } + + regs = orig_regs; + regs.pc = libc_base; + copy_regs(®s, pregs); + + ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void *)NT_PRSTATUS, + (void *)®s_iov); + if (ret < 0) { + log_error("can't set regs - %d\n", pctx->pid); + goto poke_back; + } + + ret = func(pctx, data); + if (ret < 0) { + log_error("failed call to func\n"); + goto poke_back; + } + + ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void *)NT_PRSTATUS, + (void *)®s_iov); + if (ret < 0) { + log_error("can't get updated regs - %d\n", pctx->pid); + goto poke_back; + } + + ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void *)NT_PRSTATUS, + (void *)&orig_regs_iov); + if (ret < 0) { + log_error("can't restore regs - %d\n", pctx->pid); + goto poke_back; + } + + *pregs = regs; poke_back: - upatch_process_mem_write(proc, (unsigned long *)orig_code, libc_base, - codelen); - return ret; + upatch_process_mem_write(proc, (unsigned long *)orig_code, + libc_base, codelen); + + free(orig_code); + return ret; } void copy_regs(struct user_regs_struct *dst, struct user_regs_struct *src) { #define COPY_REG(x) dst->x = src->x - COPY_REG(regs[0]); - COPY_REG(regs[1]); - COPY_REG(regs[2]); - COPY_REG(regs[3]); - COPY_REG(regs[4]); - COPY_REG(regs[5]); - COPY_REG(regs[8]); - COPY_REG(regs[29]); - - COPY_REG(regs[9]); - COPY_REG(regs[10]); - COPY_REG(regs[11]); - COPY_REG(regs[12]); - COPY_REG(regs[13]); - COPY_REG(regs[14]); - COPY_REG(regs[15]); - COPY_REG(regs[16]); - COPY_REG(regs[17]); - COPY_REG(regs[18]); - COPY_REG(regs[19]); - COPY_REG(regs[20]); + COPY_REG(regs[0]); + COPY_REG(regs[1]); + COPY_REG(regs[2]); + COPY_REG(regs[3]); + COPY_REG(regs[4]); + COPY_REG(regs[5]); + COPY_REG(regs[8]); + COPY_REG(regs[29]); + + COPY_REG(regs[9]); + COPY_REG(regs[10]); + COPY_REG(regs[11]); + COPY_REG(regs[12]); + COPY_REG(regs[13]); + COPY_REG(regs[14]); + COPY_REG(regs[15]); + COPY_REG(regs[16]); + COPY_REG(regs[17]); + COPY_REG(regs[18]); + COPY_REG(regs[19]); + COPY_REG(regs[20]); #undef COPY_REG } -size_t get_origin_insn_len() +size_t get_origin_insn_len(void) { - return ORIGIN_INSN_LEN; -} -#define UPATCH_INSN_LEN 8 -#define UPATCH_ADDR_LEN 8 -size_t get_upatch_insn_len() -{ - return UPATCH_INSN_LEN; + return ORIGIN_INSN_LEN; } -size_t get_upatch_addr_len() +size_t get_upatch_insn_len(void) { - return UPATCH_ADDR_LEN; + return UPATCH_INSN_LEN; } -// for long jumper -unsigned long get_new_insn(struct object_file *obj, unsigned long old_addr, - unsigned long new_addr) +size_t get_upatch_addr_len(void) { - unsigned int insn0 = 0x58000051; // ldr x17, #8 - unsigned int insn4 = 0xd61f0220; // br x17 - return (unsigned long)(insn0 | ((unsigned long)insn4 << 32)); + return UPATCH_ADDR_LEN; } -#if 0 -unsigned long get_new_insn(struct object_file *obj, unsigned long old_addr, - unsigned long new_addr) +// for long jumper +unsigned long get_new_insn(void) { - unsigned char b_insn[] = { 0x00, 0x00, 0x00, 0x00 }; /* ins: b IMM */ - - *(unsigned int *)(b_insn) = (unsigned int)(new_addr - old_addr) / 4; - b_insn[3] &= 0x3; - b_insn[3] |= 0x14; - - return *(unsigned int *)b_insn; + unsigned int insn0 = 0x58000051; // ldr x17, #8 + unsigned int insn4 = 0xd61f0220; // br x17 + return (unsigned long)(insn0 | ((unsigned long)insn4 << 32)); } -#endif diff --git a/upatch-manage/arch/aarch64/relocation.c b/upatch-manage/arch/aarch64/relocation.c index 0019388efec4c44f4fb2c07e65d1f9f3b50cfe85..7dd82ebd651037cfd0573ac28545cdad4377e1f5 100644 --- a/upatch-manage/arch/aarch64/relocation.c +++ b/upatch-manage/arch/aarch64/relocation.c @@ -24,300 +24,315 @@ #include "upatch-relocation.h" #include "upatch-resolve.h" -#define TCB_SIZE 2 * sizeof(void *) +#define TCB_SIZE (2 * sizeof(void *)) enum aarch64_reloc_op { - RELOC_OP_NONE, - RELOC_OP_ABS, - RELOC_OP_PREL, - RELOC_OP_PAGE, + RELOC_OP_NONE, + RELOC_OP_ABS, + RELOC_OP_PREL, + RELOC_OP_PAGE, }; static inline s64 calc_reloc(enum aarch64_reloc_op op, void *place, u64 val) { - s64 sval; - switch (op) { - case RELOC_OP_ABS: - // S + A - sval = val; - break; - case RELOC_OP_PREL: - // S + A - P - sval = val - (u64)place; - break; - case RELOC_OP_PAGE: - // Page(S + A) - Page(P) - sval = (val & ~0xfff) - ((u64)place & ~0xfff); - break; - default: - log_error("upatch: unknown relocation operation %d\n", op); - break; - } + s64 sval; + switch (op) { + case RELOC_OP_ABS: + // S + A + sval = (s64)val; + break; + case RELOC_OP_PREL: + // S + A - P + sval = (s64)(val - (u64)place); + break; + case RELOC_OP_PAGE: + // Page(S + A) - Page(P) + sval = (s64)((val & ~(u64)0xfff) - ((u64)place & ~(u64)0xfff)); + break; + default: + log_error("upatch: unknown relocation operation %d\n", op); + break; + } - log_debug("upatch: reloc, S+A=0x%lx, P=0x%lx, X=0x%lx\n", val, - (u64)place, sval); - return sval; + log_debug("reloc: S+A=0x%lx, P=0x%lx, X=0x%lx\n", val, (u64)place, sval); + return sval; } int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, - unsigned int relsec) + unsigned int relsec) { - unsigned int i; - GElf_Sym *sym; - char const *sym_name; - void *loc; - void *uloc; - u64 val; - s64 result; - GElf_Shdr *shdrs = (void *)uelf->info.shdrs; - GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; + unsigned int i; + GElf_Sym *sym; + char const *sym_name; + void *loc; + void *uloc; + u64 val; + u64 got; + s64 result = 0; + GElf_Shdr *shdrs = (void *)uelf->info.shdrs; + GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; - for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { - /* loc corresponds to P in the kernel space */ - loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + - rel[i].r_offset; + for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { + /* loc corresponds to P in the kernel space */ + loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + rel[i].r_offset; - // /* uloc corresponds P in user space */ - uloc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + - rel[i].r_offset; + /* uloc corresponds P in user space */ + uloc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + + rel[i].r_offset; - /* sym is the ELF symbol we're referring to */ - sym = (GElf_Sym *)shdrs[symindex].sh_addr + - GELF_R_SYM(rel[i].r_info); - if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && - sym->st_shndx < uelf->info.hdr->e_shnum) - sym_name = uelf->info.shstrtab + - shdrs[sym->st_shndx].sh_name; - else - sym_name = uelf->strtab + sym->st_name; + /* sym is the ELF symbol we're referring to */ + sym = (GElf_Sym *)shdrs[symindex].sh_addr + GELF_R_SYM(rel[i].r_info); + if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && + sym->st_shndx < uelf->info.hdr->e_shnum) { + sym_name = uelf->info.shstrtab + shdrs[sym->st_shndx].sh_name; + } else { + sym_name = uelf->strtab + sym->st_name; + } - /* val corresponds to (S + A) */ - val = (s64)(sym->st_value + rel[i].r_addend); - log_debug( - "upatch: reloc symbol, name=%s, k_addr=0x%lx, u_addr=0x%lx, " - "r_offset=0x%lx, st_value=0x%lx, r_addend=0x%lx \n", - sym_name, shdrs[shdrs[relsec].sh_info].sh_addr, - shdrs[shdrs[relsec].sh_info].sh_addralign, - rel[i].r_offset, sym->st_value, rel[i].r_addend); + /* val corresponds to (S + A) */ + val = (unsigned long)sym->st_value + (unsigned long)rel[i].r_addend; + log_debug( + "\nsymbol='%s', k_addr=0x%lx, u_addr=0x%lx, " + "r_offset=0x%lx, st_value=0x%lx, r_addend=0x%lx\n", + sym_name, shdrs[shdrs[relsec].sh_info].sh_addr, + shdrs[shdrs[relsec].sh_info].sh_addralign, + rel[i].r_offset, sym->st_value, rel[i].r_addend); - /* Perform the static relocation. */ - switch (GELF_R_TYPE(rel[i].r_info)) { - case R_AARCH64_NONE: - break; - /* Data relocations. */ - case R_AARCH64_ABS64: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - *(s64 *)loc = result; - break; - case R_AARCH64_ABS32: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) - goto overflow; - *(s32 *)loc = result; - break; - case R_AARCH64_ABS16: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) - goto overflow; - *(s16 *)loc = result; - break; - case R_AARCH64_PREL64: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - *(s64 *)loc = result; - break; - case R_AARCH64_PREL32: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) - goto overflow; - *(s32 *)loc = result; - break; - case R_AARCH64_PREL16: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) - goto overflow; - *(s16 *)loc = result; - break; - /* Immediate instruction relocations. */ - case R_AARCH64_LD_PREL_LO19: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) - goto overflow; - result = extract_insn_imm(result, 19, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_PREL_LO21: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) - goto overflow; - result = extract_insn_imm(result, 21, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_PREL_PG_HI21: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) - goto overflow; - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_PREL_PG_HI21_NC: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADD_ABS_LO12_NC: - case R_AARCH64_LDST8_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 12, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST16_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 11, 1); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST32_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 10, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST64_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 9, 3); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST128_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 8, 4); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TSTBR14: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < -(s64)BIT(15) || result >= (s64)BIT(15)) - goto overflow; - result = extract_insn_imm(result, 14, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_14, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_CONDBR19: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - result = extract_insn_imm(result, 19, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_JUMP26: - case R_AARCH64_CALL26: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < -(s64)BIT(27) || result >= (s64)BIT(27)) { - log_debug( - "R_AARCH64_CALL26 overflow: result = 0x%lx, uloc = " - "0x%lx, val = 0x%lx\n", - result, (unsigned long)uloc, val); - val = search_insert_plt_table(uelf, val, - (u64)&val); - log_debug( - "R_AARCH64_CALL26 overflow: plt.addr = 0x%lx\n", - val); - if (!val) - goto overflow; - result = calc_reloc(RELOC_OP_PREL, uloc, val); - } - result = extract_insn_imm(result, 26, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_26, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_GOT_PAGE: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) - goto overflow; - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LD64_GOT_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - // don't check result & 7 == 0. - // sometimes, result & 7 != 0, it works fine. - result = extract_insn_imm(result, 9, 3); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSLE_ADD_TPREL_HI12: - result = ALIGN(TCB_SIZE, uelf->relf->tls_align) + val; - if (result < 0 || result >= BIT(24)) - goto overflow; - result = extract_insn_imm(result, 12, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: - result = ALIGN(TCB_SIZE, uelf->relf->tls_align) + val; - result = extract_insn_imm(result, 12, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_ADR_PAGE21: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) - goto overflow; - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_LD64_LO12: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - // don't check result & 7 == 0. - result = extract_insn_imm(result, 9, 3); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_ADD_LO12: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 12, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, - result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_CALL: - // this is a blr instruction, don't need to modify - break; - - default: - log_error("upatch: unsupported RELA relocation: %lu\n", - GELF_R_TYPE(rel[i].r_info)); - return -ENOEXEC; - } - } - return 0; + /* Perform the static relocation. */ + switch (GELF_R_TYPE(rel[i].r_info)) { + case R_AARCH64_NONE: + break; + /* Data relocations. */ + case R_AARCH64_ABS64: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + *(s64 *)loc = result; + break; + case R_AARCH64_ABS32: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) { + goto overflow; + } + *(s32 *)loc = (s32)result; + break; + case R_AARCH64_ABS16: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) { + goto overflow; + } + *(s16 *)loc = (s16)result; + break; + case R_AARCH64_PREL64: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + *(s64 *)loc = result; + break; + case R_AARCH64_PREL32: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) { + goto overflow; + } + *(s32 *)loc = (s32)result; + break; + case R_AARCH64_PREL16: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) { + goto overflow; + } + *(s16 *)loc = (s16)result; + break; + /* Immediate instruction relocations. */ + case R_AARCH64_LD_PREL_LO19: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) { + goto overflow; + } + result = extract_insn_imm(result, 19, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_ADR_PREL_LO21: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) { + goto overflow; + } + result = extract_insn_imm(result, 21, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_ADR_PREL_PG_HI21: + result = calc_reloc(RELOC_OP_PAGE, uloc, val); + if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) { + goto overflow; + } + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_ADR_PREL_PG_HI21_NC: + result = calc_reloc(RELOC_OP_PAGE, uloc, val); + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 12, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_LDST16_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 11, 1); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_LDST32_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 10, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 9, 3); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_LDST128_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 8, 4); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_TSTBR14: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(15) || result >= (s64)BIT(15)) { + goto overflow; + } + result = extract_insn_imm(result, 14, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_14, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_CONDBR19: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + result = extract_insn_imm(result, 19, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(27) || result >= (s64)BIT(27)) { + log_debug( + "R_AARCH64_CALL26 overflow: " + "result = 0x%lx, uloc = 0x%lx, val = 0x%lx\n", + result, (unsigned long)uloc, val); + val = search_insert_plt_table(uelf, val, (u64)&val); + log_debug("R_AARCH64_CALL26 overflow: " + "plt.addr = 0x%lx\n", val); + if (!val) { + goto overflow; + } + result = calc_reloc(RELOC_OP_PREL, uloc, val); + } + result = extract_insn_imm(result, 26, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_26, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_ADR_GOT_PAGE: + got = (u64)setup_got_table(uelf, val, 0); + if (got == 0) { + goto overflow; + } + result = calc_reloc(RELOC_OP_PAGE, uloc, got); + if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) { + goto overflow; + } + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_LD64_GOT_LO12_NC: + got = (u64)setup_got_table(uelf, val, 0); + if (got == 0) { + goto overflow; + } + result = calc_reloc(RELOC_OP_ABS, uloc, got); + // don't check result & 7 == 0. + // sometimes, result & 7 != 0, it works fine. + result = extract_insn_imm(result, 9, 3); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + result = (long)(ALIGN(TCB_SIZE, uelf->relf->tls_align) + val); + if (result < 0 || result >= (s64)BIT(24)) { + goto overflow; + } + result = extract_insn_imm(result, 12, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + result = (long)(ALIGN(TCB_SIZE, uelf->relf->tls_align) + val); + result = extract_insn_imm(result, 12, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + result = calc_reloc(RELOC_OP_PAGE, uloc, val); + if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) { + goto overflow; + } + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_TLSDESC_LD64_LO12: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + // don't check result & 7 == 0. + result = extract_insn_imm(result, 9, 3); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_TLSDESC_ADD_LO12: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 12, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + (unsigned long)result); + *(__le32 *)loc = cpu_to_le32((__le32)result); + break; + case R_AARCH64_TLSDESC_CALL: + // this is a blr instruction, don't need to modify + break; + default: + log_error("upatch: unsupported RELA relocation: %lu\n", + GELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; overflow: - log_error("upatch: overflow in relocation type %d val %lx reloc %lx\n", - (int)GELF_R_TYPE(rel[i].r_info), val, result); - return -ENOEXEC; + log_error("upatch: overflow in relocation type %d val %lx reloc %lx\n", + (int)GELF_R_TYPE(rel[i].r_info), val, result); + return -ENOEXEC; } diff --git a/upatch-manage/arch/aarch64/resolve.c b/upatch-manage/arch/aarch64/resolve.c index 99fa307d13b64215927ef7c23033ecc7127e1ae3..63ebb0fa30af0c680db23e3c3fec466930cf783f 100644 --- a/upatch-manage/arch/aarch64/resolve.c +++ b/upatch-manage/arch/aarch64/resolve.c @@ -34,133 +34,131 @@ #define AARCH64_JUMP_TABLE_JMP2 0xffffffffd61f0220 struct upatch_jmp_table_entry { - unsigned long inst[2]; - unsigned long addr[2]; + unsigned long inst[2]; + unsigned long addr[2]; }; unsigned int get_jmp_table_entry() { - return sizeof(struct upatch_jmp_table_entry); + return sizeof(struct upatch_jmp_table_entry); } static unsigned long setup_jmp_table(struct upatch_elf *uelf, - unsigned long jmp_addr, - unsigned long origin_addr) + unsigned long jmp_addr, unsigned long origin_addr) { - struct upatch_jmp_table_entry *table = - uelf->core_layout.kbase + uelf->jmp_offs; - unsigned int index = uelf->jmp_cur_entry; - if (index >= uelf->jmp_max_entry) { - log_error("jmp table overflow\n"); - return 0; - } - - table[index].inst[0] = AARCH64_JUMP_TABLE_JMP1; - table[index].inst[1] = AARCH64_JUMP_TABLE_JMP2; - table[index].addr[0] = jmp_addr; - table[index].addr[1] = origin_addr; - uelf->jmp_cur_entry++; - return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + - index * sizeof(struct upatch_jmp_table_entry)); + struct upatch_jmp_table_entry *table = uelf->core_layout.kbase + + uelf->jmp_offs; + + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("jmp table overflow\n"); + return 0; + } + + table[index].inst[0] = AARCH64_JUMP_TABLE_JMP1; + table[index].inst[1] = AARCH64_JUMP_TABLE_JMP2; + table[index].addr[0] = jmp_addr; + table[index].addr[1] = origin_addr; + uelf->jmp_cur_entry++; + + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); } -static unsigned long setup_got_table(struct upatch_elf *uelf, - unsigned long jmp_addr, - unsigned long tls_addr) +unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long jmp_addr, unsigned long tls_addr) { - struct upatch_jmp_table_entry *table = - uelf->core_layout.kbase + uelf->jmp_offs; - unsigned int index = uelf->jmp_cur_entry; - - if (index >= uelf->jmp_max_entry) { - log_error("got table overflow\n"); - return 0; - } - - table[index].inst[0] = jmp_addr; - table[index].inst[1] = tls_addr; - table[index].addr[0] = 0xffffffff; - table[index].addr[1] = 0xffffffff; - uelf->jmp_cur_entry++; - return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + - index * sizeof(struct upatch_jmp_table_entry)); + struct upatch_jmp_table_entry *table = uelf->core_layout.kbase + + uelf->jmp_offs; + + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("got table overflow\n"); + return 0; + } + + table[index].inst[0] = jmp_addr; + table[index].inst[1] = tls_addr; + table[index].addr[0] = 0xffffffff; + table[index].addr[1] = 0xffffffff; + uelf->jmp_cur_entry++; + + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); } unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, - unsigned long r_type, unsigned long addr) + unsigned long r_type, unsigned long addr) { - unsigned long jmp_addr = 0xffffffff; - unsigned long tls_addr = 0xffffffff; - unsigned long elf_addr = 0; - - if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, - sizeof(jmp_addr))) { - log_error("copy address failed\n"); - goto out; - } - - if (r_type == R_AARCH64_TLSDESC && - upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), - &tls_addr, sizeof(tls_addr))) { - log_error("copy address failed\n"); - goto out; - } - - if (r_type == R_AARCH64_TLSDESC) - elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); - else - elf_addr = setup_jmp_table(uelf, jmp_addr, (unsigned long)addr); - - log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", elf_addr, - jmp_addr, tls_addr); - + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } + + if (r_type == R_AARCH64_TLSDESC && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed\n"); + goto out; + } + + if (r_type == R_AARCH64_TLSDESC) { + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + } else { + elf_addr = setup_jmp_table(uelf, jmp_addr, (unsigned long)addr); + } + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", + elf_addr, jmp_addr, tls_addr); out: - return elf_addr; + return elf_addr; } unsigned long insert_got_table(struct upatch_elf *uelf, struct object_file *obj, - unsigned long r_type, unsigned long addr) + unsigned long r_type, unsigned long addr) { - unsigned long jmp_addr = 0xffffffff; - unsigned long tls_addr = 0xffffffff; - unsigned long elf_addr = 0; - - if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, - sizeof(jmp_addr))) { - log_error("copy address failed\n"); - goto out; - } - - if (r_type == R_AARCH64_TLSDESC && - upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), - &tls_addr, sizeof(tls_addr))) { - log_error("copy address failed\n"); - goto out; - } - - elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); - - log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", elf_addr, - jmp_addr, tls_addr); + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } + if (r_type == R_AARCH64_TLSDESC && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed\n"); + goto out; + } + + elf_addr = jmp_addr; + if (r_type != R_AARCH64_GLOB_DAT) { + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + } + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", elf_addr, + jmp_addr, tls_addr); out: - return elf_addr; + return elf_addr; } unsigned long search_insert_plt_table(struct upatch_elf *uelf, - unsigned long jmp_addr, - unsigned long origin_addr) + unsigned long jmp_addr, unsigned long origin_addr) { - struct upatch_jmp_table_entry *table = - uelf->core_layout.kbase + uelf->jmp_offs; - unsigned int i = 0; - - for (i = 0; i < uelf->jmp_cur_entry; ++i) { - if (table[i].addr[0] != jmp_addr) - continue; - return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + - i * sizeof(struct upatch_jmp_table_entry)); - } - - return setup_jmp_table(uelf, jmp_addr, origin_addr); -} \ No newline at end of file + struct upatch_jmp_table_entry *table = uelf->core_layout.kbase + + uelf->jmp_offs; + + for (unsigned int i = 0; i < uelf->jmp_cur_entry; ++i) { + if (table[i].addr[0] != jmp_addr) { + continue; + } + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + i * sizeof(struct upatch_jmp_table_entry)); + } + + return setup_jmp_table(uelf, jmp_addr, origin_addr); +} diff --git a/upatch-manage/arch/riscv64/insn.h b/upatch-manage/arch/riscv64/insn.h new file mode 100644 index 0000000000000000000000000000000000000000..8ab4daf7ca4091d3f259663df98e4f53ba789834 --- /dev/null +++ b/upatch-manage/arch/riscv64/insn.h @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 laokz + */ + +#ifndef _ARCH_RISCV64_INSN_H +#define _ARCH_RISCV64_INSN_H + +static inline unsigned set_utype_imm(unsigned ins, unsigned long imm) +{ + /* + imm[11] to counteract lo12 sign extension in next instruction */ + unsigned long temp_imm = imm; + temp_imm += (temp_imm & 0x800) << 1; + return (temp_imm & 0xfffff000) | (ins & 0xfff); +} + +static inline unsigned set_itype_imm(unsigned ins, unsigned long imm) +{ + return ((imm & 0xfff) << 20) | (ins & 0xfffff); +} + +static inline unsigned set_stype_imm(unsigned ins, unsigned long imm) +{ + /* rs2 rs1 func opcode + ins: imm[11-8,7-5] 1,1111, 1111,1 111, imm[4-1,0] 111,1111 + ins mask 0 1 f f f 0 7 f + + imm bit no. 11-----5 4---0 + 1111,111 1,1111 + imm mask fe0 1f + + ==>imm bit no. 31----25 11--7 + */ + return (ins & 0x1fff07f) | + ((imm & 0xfe0) << (31 - 11)) | ((imm & 0x1f) << (11 - 4)); +} + +static inline unsigned set_jtype_imm(unsigned ins, unsigned long imm) +{ + /* + imm bit no. 20 19------12 11 10---------1 + 1, 1111,1111, 1 111,1111,111 0 + mask 100000 ff000 800 7fe + + ==>imm bit no. 31 19------12 20 30---------21 + */ + return (ins & 0xfff) | + ((imm & 0x100000) << (31 - 20)) | (imm & 0xff000) | + ((imm & 0x800) << (20 - 11)) | ((imm & 0x7fe) << (30 - 10)); +} + +static inline unsigned set_btype_imm(unsigned ins, unsigned long imm) +{ + /* rs2 rs1 func opcode + ins: imm[12 10-8,7-5] 1,1111, 1111,1 111, imm[4-1,11] 111,1111 + ins mask 0 1 f f f 0 7 f + + imm bit no. 12 11 10----5 4--1 + 1, 1 111,111 1,111 0 + imm mask 1000 800 7e0 1e + + ==>imm bit no. 31 7 30---25 11-8 + */ + return (ins & 0x01fff07f) | + ((imm & 0x1000) << (31 - 12)) | ((imm & 0x800) >> (11 - 7)) | + ((imm & 0x7e0) << (30 - 10)) | ((imm & 0x1e) << (11 - 4)); +} + +static inline unsigned short set_cjtype_imm(unsigned short ins, unsigned long imm) +{ + /* funct3 imm opcode + ins: 111 offset[11,4 9 8 10, 6 7 3 2, 1 5] 11 + ins mask e 0 0 3 + + imm bit no. 11 10 9-8 7 6 5 4 3-1 + 1 1 11, 1 1 1 1, 111 0 + imm mask 800 400 300 80 40 20 10 e + + ==>imm bit no. 12 8 10-9 6 7 2 11 5-3 + */ + return (ins & 0xe003) | + ((imm & 0x800) << (12 - 11)) | ((imm & 0x400) >> (10 - 8)) | + ((imm & 0x300) << (10 - 9)) | ((imm & 0x80) >> (7 - 6)) | + ((imm & 0x40) << (7 - 6)) | ((imm & 0x20) >> (5 - 2)) | + ((imm & 0x10) << (11 - 4)) | ((imm & 0xe) << (5 - 3)); +} + +/* only support C.LUI */ +static inline unsigned short set_citype_imm(unsigned short ins, unsigned long imm) +{ + /* funct3 imm[17] rd imm[16:12] opcode + ins: 111 imm[17], 1111,1 imm[16-14,13-12] 11 + ins mask e f 8 3 + + imm bit no. 17 16--12 + 1 1,1111 + imm mask 20000 1f000 + + ==>imm bit no. 12 6---2 + */ + return (ins & 0xef83) | + ((imm & 0x20000) >> (17 - 12)) | ((imm & 0x1f000) >> (16 - 6)); +} + +/* only support C.BEQZ C.BNEZ */ +static inline unsigned short set_cbtype_imm(unsigned short ins, unsigned long imm) +{ + /* funct3 imm[8 4 3] rs imm[7 6 2 1 5] opcode + ins: 111 0,0 0 11,1 0 0 0,0 0 11 + ins mask e 3 8 3 + + imm bit no. 8 , 7 6 5 4,3 2 1 0 + imm mask 100 c0 20 18 6 + + ==>imm bit no. 12 6-5 2 11-10 4-3 + */ + return (ins & 0xe383) | + ((imm & 0x100) << (12 - 8)) | ((imm & 0xc0) >> (7 - 6)) | + ((imm & 0x20) >> (5 - 2)) | ((imm & 0x18) << (11 - 4)) | + ((imm & 0x6) << (4 - 2)); +} + +#endif diff --git a/upatch-manage/arch/x86_64/process.h b/upatch-manage/arch/riscv64/process.h similarity index 93% rename from upatch-manage/arch/x86_64/process.h rename to upatch-manage/arch/riscv64/process.h index 5de8fc34ed9fb965acb7ed9bcebe994c7be1de85..91926fa22c2788503711dac2a38094d18e6ea349 100644 --- a/upatch-manage/arch/x86_64/process.h +++ b/upatch-manage/arch/riscv64/process.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * upatch-manage - * Copyright (C) 2024 Huawei Technologies Co., Ltd. + * Copyright (C) 2024 ISCAS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,4 +25,4 @@ #define MAX_DISTANCE 0x80000000 #endif -#endif \ No newline at end of file +#endif diff --git a/upatch-manage/arch/riscv64/ptrace.c b/upatch-manage/arch/riscv64/ptrace.c new file mode 100644 index 0000000000000000000000000000000000000000..2ccdc9efec070dc764ad013385f4bb1521d14a40 --- /dev/null +++ b/upatch-manage/arch/riscv64/ptrace.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage + * Copyright (C) 2024 ISCAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include "upatch-ptrace.h" + +int upatch_arch_reg_init(int pid, unsigned long *sp, unsigned long *pc) +{ + struct iovec regs_iov; + struct user_regs_struct regs; + + regs_iov.iov_base = ®s; + regs_iov.iov_len = sizeof(regs); + + if (ptrace(PTRACE_GETREGSET, pid, + (void *)NT_PRSTATUS, (void *)®s_iov) < 0) { + log_error("Cannot get regs from %d\n", pid); + return -1; + } + *sp = (unsigned long)regs.sp; + *pc = (unsigned long)regs.pc; + return 0; +} + +static long read_gregs(int pid, struct user_regs_struct *regs) +{ + struct iovec data = {regs, sizeof(*regs)}; + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &data) == -1) { + log_error("ptrace(PTRACE_GETREGSET)"); + return -1; + } + return 0; +} + +static long write_gregs(int pid, struct user_regs_struct *regs) +{ + struct iovec data = {regs, sizeof(*regs)}; + if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &data) == -1) { + log_error("ptrace(PTRACE_SETREGSET)"); + return -1; + } + return 0; +} + +long upatch_arch_syscall_remote(struct upatch_ptrace_ctx *pctx, int nr, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5, unsigned long arg6, + unsigned long *res) +{ + struct user_regs_struct regs; + unsigned char syscall[] = { + 0x73, 0x00, 0x00, 0x00, // ecall + 0x73, 0x00, 0x10, 0x00, // ebreak + }; + long ret; + + log_debug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); + regs.a7 = (unsigned long)nr; + regs.a0 = arg1; + regs.a1 = arg2; + regs.a2 = arg3; + regs.a3 = arg4; + regs.a4 = arg5; + regs.a5 = arg6; + + ret = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); + if (ret == 0) + *res = regs.a0; + + return ret; +} + +long upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, + const void *data), + const void *data) +{ + struct user_regs_struct orig_regs, regs; + if (!codelen) { + log_error("Invalid codelen\n"); + return -1; + } + unsigned char *orig_code = (unsigned char *)malloc(sizeof(*orig_code) * codelen); + if (orig_code == NULL) { + log_error("Malloc orig_code failed\n"); + return -1; + } + long ret; + struct upatch_process *proc = pctx->proc; + unsigned long libc_base = proc->libc_base; + + ret = read_gregs(pctx->pid, &orig_regs); + if (ret < 0) { + free(orig_code); + return -1; + } + ret = upatch_process_mem_read(proc, libc_base, + (unsigned long *)orig_code, codelen); + if (ret < 0) { + log_error("can't peek original code - %d\n", pctx->pid); + free(orig_code); + return -1; + } + ret = upatch_process_mem_write(proc, (const unsigned long *)code, libc_base, + codelen); + if (ret < 0) { + log_error("can't poke syscall code - %d\n", pctx->pid); + goto poke_back; + } + + regs = orig_regs; + regs.pc = libc_base; + + copy_regs(®s, pregs); + + ret = write_gregs(pctx->pid, ®s); + if (ret < 0) { + goto poke_back; + } + + ret = func(pctx, data); + if (ret < 0) { + log_error("failed call to func\n"); + goto poke_back; + } + + ret = read_gregs(pctx->pid, ®s); + if (ret < 0) { + goto poke_back; + } + + ret = write_gregs(pctx->pid, &orig_regs); + if (ret < 0) { + goto poke_back; + } + + *pregs = regs; + +poke_back: + upatch_process_mem_write(proc, (unsigned long *)orig_code, libc_base, + codelen); + free(orig_code); + return ret; +} + +void copy_regs(struct user_regs_struct *dst, struct user_regs_struct *src) +{ +#define COPY_REG(x) dst->x = src->x + COPY_REG(a0); + COPY_REG(a1); + COPY_REG(a2); + COPY_REG(a3); + COPY_REG(a4); + COPY_REG(a5); + COPY_REG(a6); + COPY_REG(a7); +#undef COPY_REG +} + +#define UPATCH_INSN_LEN 8 +#define UPATCH_ADDR_LEN 8 +#define ORIGIN_INSN_LEN (UPATCH_INSN_LEN + UPATCH_ADDR_LEN) + +size_t get_origin_insn_len() +{ + return ORIGIN_INSN_LEN; +} + +size_t get_upatch_insn_len() +{ + return UPATCH_INSN_LEN; +} + +size_t get_upatch_addr_len() +{ + return UPATCH_ADDR_LEN; +} + +/* + * On RISC-V, there must be 3 instructors(12 bytes) to jump to + * arbitrary address. The core upatch-manage limit jump instructor + * to one long(8 bytes), for us is +-2G range. + */ +unsigned long get_new_insn(unsigned long old_addr, unsigned long new_addr) +{ + unsigned long offset; + unsigned int insn0, insn4; + + offset = new_addr - old_addr; + offset += (offset & 0x800) << 1; + insn0 = 0xf97 | (offset & 0xfffff000); // auipc t6, off[20] + insn4 = 0xf8067 | ((offset & 0xfff) << 20); // jalr zero, off[12](t6) + return (unsigned long)(insn0 | ((unsigned long)insn4 << 32)); +} diff --git a/upatch-manage/arch/riscv64/relocation.c b/upatch-manage/arch/riscv64/relocation.c new file mode 100644 index 0000000000000000000000000000000000000000..ba825992d38ddab819dd899483b784369fdc4f7e --- /dev/null +++ b/upatch-manage/arch/riscv64/relocation.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage + * Copyright (C) 2024 ISCAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "insn.h" +#include "upatch-relocation.h" +#include "upatch-resolve.h" + +/* + * In PCREL_LO12 relocation entity, its corresponding symbol's value + * points to the ..._HI20 instruction, where the LO12 part of the + * immediate is part of the ..._HI20 symbol value. + */ +static unsigned long find_pcrel_hi_value(GElf_Rela *r, int idx, GElf_Sym *st, unsigned long v) +{ + int i = idx; + r--; + for (; i > 0; i--, r--) { + if ((r->r_offset == v) && + ((GELF_R_TYPE(r->r_info) == R_RISCV_PCREL_HI20) || + (GELF_R_TYPE(r->r_info) == R_RISCV_TLS_GOT_HI20) || + (GELF_R_TYPE(r->r_info) == R_RISCV_TLS_GD_HI20) || + (GELF_R_TYPE(r->r_info) == R_RISCV_GOT_HI20))) + return st[GELF_R_SYM(r->r_info)].st_value; + } + + /* should never happen */ + log_error("Not found no. %d rela's corresponding HI20\n", idx); + return 0; +} + +/* + * The patch is a .o file, has only static relocations, all symbols + * have been resolved with our jump table act as got/plt. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" +int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, + unsigned int relsec) +{ + unsigned int i; + GElf_Sym *sym, *symtab; + char const *sym_name; + unsigned long uloc_sec; + void *loc; + void *uloc; + u64 val; + GElf_Shdr *shdrs = (void *)uelf->info.shdrs; + GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; + + symtab = (GElf_Sym *)shdrs[symindex].sh_addr; + for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { + /* loc corresponds to P in the kernel space */ + loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + + /* uloc corresponds P in user space */ + uloc_sec = shdrs[shdrs[relsec].sh_info].sh_addralign; + uloc = (void *)uloc_sec + rel[i].r_offset; + + /* sym is the ELF symbol we're referring to */ + sym = symtab + GELF_R_SYM(rel[i].r_info); + if (GELF_ST_TYPE(sym->st_info) == STT_SECTION && + sym->st_shndx < uelf->info.hdr->e_shnum) + sym_name = uelf->info.shstrtab + + shdrs[sym->st_shndx].sh_name; + else + sym_name = uelf->strtab + sym->st_name; + + /* val corresponds to (S + A) */ + val = (s64)(sym->st_value + rel[i].r_addend); + log_debug( + "upatch: reloc symbol, name=%s, k_addr=0x%lx, u_addr=0x%lx, " + "r_offset=0x%lx, st_value=0x%lx, r_addend=0x%lx \n", + sym_name, shdrs[shdrs[relsec].sh_info].sh_addr, + uloc_sec, rel[i].r_offset, sym->st_value, rel[i].r_addend); + + /* Perform the static relocation. */ + switch (GELF_R_TYPE(rel[i].r_info)) { + case R_RISCV_NONE: + case R_RISCV_TPREL_ADD: + break; + + case R_RISCV_64: + *(unsigned long *)loc = val; + break; + + /* seems no need to recalculate as it should confined in the same func */ + case R_RISCV_BRANCH: + val -= (unsigned long)uloc; + if ((signed)val >= 4096 || (signed)val < -4096) + goto overflow; + *(unsigned *)loc = set_btype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_JAL: + val -= (unsigned long)uloc; + if ((signed)val >= (1 << 20) || (signed)val < -(1 << 20)) + goto overflow; + *(unsigned *)loc = set_jtype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + // in our jump table, must not overflow + val -= (unsigned long)uloc; + *(unsigned *)loc = set_utype_imm(*(unsigned *)loc, val); + *(unsigned *)(loc + 4) = set_itype_imm(*(unsigned *)(loc + 4), val); + break; + + case R_RISCV_GOT_HI20: + case R_RISCV_TLS_GOT_HI20: + case R_RISCV_TLS_GD_HI20: + case R_RISCV_PCREL_HI20: + val -= (unsigned long)uloc; // fall through + case R_RISCV_HI20: + case R_RISCV_TPREL_HI20: + if ((long)val != (long)(int)val) + goto overflow; + *(unsigned *)loc = set_utype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_PCREL_LO12_I: + val = find_pcrel_hi_value(rel + i, i, symtab, sym->st_value - uloc_sec); + if (val == 0) + goto overflow; + val -= sym->st_value; // fall through + case R_RISCV_LO12_I: + case R_RISCV_TPREL_LO12_I: + *(unsigned *)loc = set_itype_imm(*(unsigned *)loc, val); + break; + + case R_RISCV_PCREL_LO12_S: + val = find_pcrel_hi_value(rel + i, i, symtab, sym->st_value - uloc_sec); + if (val == 0) + goto overflow; + val -= sym->st_value; // fall through + case R_RISCV_LO12_S: + case R_RISCV_TPREL_LO12_S: + *(unsigned *)loc = set_stype_imm(*(unsigned *)loc, val); + break; + + /* inner function label calculation, must not overflow */ + case R_RISCV_ADD8: + *(char *)loc += val; + break; + case R_RISCV_ADD16: + *(short *)loc += val; + break; + case R_RISCV_ADD32: + *(int *)loc += val; + break; + case R_RISCV_ADD64: + *(long *)loc += val; + break; + + case R_RISCV_SUB8: + *(char *)loc -= val; + break; + case R_RISCV_SUB16: + *(short *)loc -= val; + break; + case R_RISCV_SUB32: + *(int *)loc -= val; + break; + case R_RISCV_SUB64: + *(long *)loc -= val; + break; + + case R_RISCV_RVC_BRANCH: + val -= (unsigned long)uloc; + if ((signed)val >= 256 || (signed)val < -256) + goto overflow; + *(unsigned short *)loc = set_cbtype_imm(*(unsigned short *)loc, val); + break; + + case R_RISCV_RVC_JUMP: + val -= (unsigned long)uloc; + if ((signed)val >= 2048 || (signed)val < -2048) + goto overflow; + *(unsigned short *)loc = set_cjtype_imm(*(unsigned short *)loc, val); + break; + + case R_RISCV_RVC_LUI: + if ((signed)val >= (1 << 17) || (signed)val < -(1 << 17) || (val & 0x3f000) == 0) + goto overflow; + *(unsigned short *)loc = set_citype_imm(*(unsigned short *)loc, val); + break; + + case R_RISCV_SET8: + *(char *)loc = val; + break; + case R_RISCV_SET16: + *(short *)loc = val; + break; + case R_RISCV_32_PCREL: + case R_RISCV_PLT32: + val -= (unsigned long)uloc; // fall through + case R_RISCV_32: + case R_RISCV_SET32: + if ((long)val != (long)(int)val) + goto overflow; + *(int *)loc = val; + break; + + case R_RISCV_SUB6: + char w6 = (*(char *)loc - (char)val) & 0x3f; + *(char *)loc = (*(char *)loc & 0xc0) | w6; + break; + case R_RISCV_SET6: + *(char *)loc = (*(char *)loc & 0xc0) | (val & 0x3f); + break; + + default: + log_error("upatch: unsupported RELA relocation: %lu\n", + GELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; + +overflow: + log_error("upatch: overflow in relocation type %d val %lx\n", + (int)GELF_R_TYPE(rel[i].r_info), val); + return -ENOEXEC; +} +#pragma GCC diagnostic pop diff --git a/upatch-manage/arch/riscv64/resolve.c b/upatch-manage/arch/riscv64/resolve.c new file mode 100644 index 0000000000000000000000000000000000000000..0b4aa8babd0e3d66aaad3e428cadfc2db4e13eea --- /dev/null +++ b/upatch-manage/arch/riscv64/resolve.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * upatch-manage + * Copyright (C) 2024 ISCAS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#include "log.h" +#include "upatch-ptrace.h" +#include "upatch-resolve.h" + +/* + * auipc t6,0x0 + * ld t6,16(t6) # addr + * jr t6 + * undefined + */ +#define RISCV64_JMP_TABLE_JUMP0 0x010fbf8300000f97 +#define RISCV64_JMP_TABLE_JUMP1 0x000f8067 + +struct upatch_jmp_table_entry { + unsigned long inst[2]; + unsigned long addr[2]; +}; + +unsigned int get_jmp_table_entry() +{ + return sizeof(struct upatch_jmp_table_entry); +} + +static unsigned long setup_jmp_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long origin_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("jmp table overflow\n"); + return 0; + } + + table[index].inst[0] = RISCV64_JMP_TABLE_JUMP0; + table[index].inst[1] = RISCV64_JMP_TABLE_JUMP1; + table[index].addr[0] = jmp_addr; + table[index].addr[1] = origin_addr; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long tls_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + + if (index >= uelf->jmp_max_entry) { + log_error("got table overflow\n"); + return 0; + } + + table[index].inst[0] = jmp_addr; + table[index].inst[1] = tls_addr; + table[index].addr[0] = 0xffffffff; + table[index].addr[1] = 0xffffffff; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type __attribute__((unused)), unsigned long addr) +{ + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } + + elf_addr = setup_jmp_table(uelf, jmp_addr, (unsigned long)addr); + + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", elf_addr, + jmp_addr, tls_addr); + +out: + return elf_addr; +} + +unsigned long insert_got_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type, unsigned long addr) +{ + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } + + /* + * Addr with this type means the symbol is a dynamic TLS variable. + * Addr points to a GOT entry(16 bytes) having type + * + * typedef struct { + * unsigned long int ti_module; + * unsigned long int ti_offset; + * } tls_index; + * + * We also need copy ti_offset to our jump table. + * + * The corresponding symbol will associate with TLS_GD_HI20 + * relocation type, using this tls_index as argument to call + * `void *__tls_get_addr (tls_index *ti)` to resolve the real address. + */ + if (r_type == R_RISCV_TLS_DTPMOD64 && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed\n"); + goto out; + } + + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx\n", elf_addr, + jmp_addr, tls_addr); + +out: + return elf_addr; +} diff --git a/upatch-manage/arch/x86_64/ptrace.c b/upatch-manage/arch/x86_64/ptrace.c index d824d92b1516999f38cd5a2bf469f6a3d7b2661e..2a84a2f45888eacd46e3a53f464c86a7311bddfe 100644 --- a/upatch-manage/arch/x86_64/ptrace.c +++ b/upatch-manage/arch/x86_64/ptrace.c @@ -19,6 +19,7 @@ */ #include +#include #include #include @@ -26,132 +27,159 @@ #include "upatch-ptrace.h" -int upatch_arch_syscall_remote(struct upatch_ptrace_ctx *pctx, int nr, - unsigned long arg1, unsigned long arg2, - unsigned long arg3, unsigned long arg4, - unsigned long arg5, unsigned long arg6, - unsigned long *res) +#define UPATCH_INSN_LEN 6 +#define UPATCH_ADDR_LEN 8 +#define ORIGIN_INSN_LEN (UPATCH_INSN_LEN + UPATCH_ADDR_LEN) + +int upatch_arch_reg_init(int pid, unsigned long *sp, unsigned long *pc) { - struct user_regs_struct regs; - - unsigned char syscall[] = { - 0x0f, 0x05, /* syscall */ - 0xcc, /* int3 */ - }; - int ret; - - memset(®s, 0, sizeof(struct user_regs_struct)); - log_debug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); - regs.rax = (unsigned long)nr; - regs.rdi = arg1; - regs.rsi = arg2; - regs.rdx = arg3; - regs.r10 = arg4; - regs.r8 = arg5; - regs.r9 = arg6; - - ret = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); - if (ret == 0) - *res = regs.rax; - - return ret; + struct user_regs_struct regs; + + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) < 0) { + log_error("Cannot get regs from %d\n", pid); + return -1; + } + *sp = (unsigned long)regs.rsp; + *pc = (unsigned long)regs.rip; + return 0; } -int upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, - const unsigned char *code, size_t codelen, - struct user_regs_struct *pregs, - int (*func)(struct upatch_ptrace_ctx *pctx, - const void *data), - const void *data) +long upatch_arch_syscall_remote(struct upatch_ptrace_ctx *pctx, int nr, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5, unsigned long arg6, + unsigned long *res) { - struct user_regs_struct orig_regs, regs; - unsigned char orig_code[codelen]; - int ret; - struct upatch_process *proc = pctx->proc; - unsigned long libc_base = proc->libc_base; - - ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, &orig_regs); - if (ret < 0) { - log_error("can't get regs - %d\n", pctx->pid); - return -1; - } - ret = upatch_process_mem_read(proc, libc_base, - (unsigned long *)orig_code, codelen); - if (ret < 0) { - log_error("can't peek original code - %d\n", pctx->pid); - return -1; - } - ret = upatch_process_mem_write(proc, (unsigned long *)code, libc_base, - codelen); - if (ret < 0) { - log_error("can't poke syscall code - %d\n", pctx->pid); - goto poke_back; - } - - regs = orig_regs; - regs.rip = libc_base; - - copy_regs(®s, pregs); - - ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); - if (ret < 0) { - log_error("can't set regs - %d\n", pctx->pid); - goto poke_back; - } - - ret = func(pctx, data); - if (ret < 0) { - log_error("failed call to func\n"); - goto poke_back; - } - - ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); - if (ret < 0) { - log_error("can't get updated regs - %d\n", pctx->pid); - goto poke_back; - } - - ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, &orig_regs); - if (ret < 0) { - log_error("can't restore regs - %d\n", pctx->pid); - goto poke_back; - } - - *pregs = regs; + struct user_regs_struct regs; + + unsigned char syscall[] = { + 0x0f, 0x05, /* syscall */ + 0xcc, /* int3 */ + }; + long ret; + + memset(®s, 0, sizeof(struct user_regs_struct)); + log_debug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); + regs.rax = (unsigned long long)nr; + regs.rdi = arg1; + regs.rsi = arg2; + regs.rdx = arg3; + regs.r10 = arg4; + regs.r8 = arg5; + regs.r9 = arg6; + + ret = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); + if (ret == 0) { + *res = regs.rax; + } + + return ret; +} + +long upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, const void *data), + const void *data) +{ + long ret; + + struct user_regs_struct orig_regs; + struct user_regs_struct regs; + + struct upatch_process *proc = pctx->proc; + unsigned long libc_base = proc->libc_base; + + unsigned char *orig_code = (unsigned char *)malloc( + sizeof(*orig_code) * codelen); + if (orig_code == NULL) { + log_error("Malloc orig_code failed\n"); + return -1; + } + + ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, &orig_regs); + if (ret < 0) { + log_error("can't get regs - %d\n", pctx->pid); + free(orig_code); + return -1; + } + + ret = upatch_process_mem_read(proc, libc_base, + (unsigned long *)orig_code, codelen); + if (ret < 0) { + log_error("can't peek original code - %d\n", pctx->pid); + free(orig_code); + return -1; + } + + ret = upatch_process_mem_write(proc, code, libc_base, codelen); + if (ret < 0) { + log_error("can't poke syscall code - %d\n", pctx->pid); + goto poke_back; + } + + regs = orig_regs; + regs.rip = libc_base; + copy_regs(®s, pregs); + + ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); + if (ret < 0) { + log_error("can't set regs - %d\n", pctx->pid); + goto poke_back; + } + + ret = func(pctx, data); + if (ret < 0) { + log_error("failed call to func\n"); + goto poke_back; + } + + ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); + if (ret < 0) { + log_error("can't get updated regs - %d\n", pctx->pid); + goto poke_back; + } + + ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, &orig_regs); + if (ret < 0) { + log_error("can't restore regs - %d\n", pctx->pid); + goto poke_back; + } + + *pregs = regs; poke_back: - upatch_process_mem_write(proc, (unsigned long *)orig_code, libc_base, - codelen); - return ret; + upatch_process_mem_write(proc, (unsigned long *)orig_code, + libc_base, codelen); + free(orig_code); + + return ret; } void copy_regs(struct user_regs_struct *dst, struct user_regs_struct *src) { #define COPY_REG(x) dst->x = src->x - COPY_REG(r15); - COPY_REG(r14); - COPY_REG(r13); - COPY_REG(r12); - COPY_REG(rbp); - COPY_REG(rbx); - COPY_REG(r11); - COPY_REG(r10); - COPY_REG(r9); - COPY_REG(r8); - COPY_REG(rax); - COPY_REG(rcx); - COPY_REG(rdx); - COPY_REG(rsi); - COPY_REG(rdi); + COPY_REG(r15); + COPY_REG(r14); + COPY_REG(r13); + COPY_REG(r12); + COPY_REG(rbp); + COPY_REG(rbx); + COPY_REG(r11); + COPY_REG(r10); + COPY_REG(r9); + COPY_REG(r8); + COPY_REG(rax); + COPY_REG(rcx); + COPY_REG(rdx); + COPY_REG(rsi); + COPY_REG(rdi); #undef COPY_REG } -#define UPATCH_INSN_LEN 6 -#define UPATCH_ADDR_LEN 8 -#define ORIGIN_INSN_LEN (UPATCH_INSN_LEN + UPATCH_ADDR_LEN) size_t get_origin_insn_len() { - return ORIGIN_INSN_LEN; + return ORIGIN_INSN_LEN; } size_t get_upatch_insn_len() @@ -164,23 +192,8 @@ size_t get_upatch_addr_len() return UPATCH_ADDR_LEN; } - -unsigned long get_new_insn(struct object_file *obj, unsigned long old_addr, - unsigned long new_addr) -{ - char jmp_insn[] = { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00}; - return *(unsigned long *)jmp_insn; -} - -#if 0 -unsigned long get_new_insn(struct object_file *obj, unsigned long old_addr, - unsigned long new_addr) +unsigned long get_new_insn(void) { - char jmp_insn[] = { 0xe9, 0x00, 0x00, 0x00, 0x00 }; /* jmp IMM */ - - *(unsigned int *)(jmp_insn + 1) = - (unsigned int)(new_addr - old_addr - 5); - - return *(unsigned long *)jmp_insn; + // ASM: jmp word ptr [di] (FF25 0000 0000 0000) + return 0x25FF; } -#endif diff --git a/upatch-manage/arch/x86_64/relocation.c b/upatch-manage/arch/x86_64/relocation.c index bb9cf32c8d3b2afd7b4aae3bc2d3a46136bbb036..e492b1e94fd462cdd25c8741154ab07627a6d7f3 100644 --- a/upatch-manage/arch/x86_64/relocation.c +++ b/upatch-manage/arch/x86_64/relocation.c @@ -25,115 +25,121 @@ #include "upatch-relocation.h" int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, - unsigned int relsec) + unsigned int relsec) { - unsigned int i; - GElf_Sym *sym; - void *loc, *real_loc; - u64 val; - const char *sym_name; - GElf_Xword tls_size; - GElf_Shdr *shdrs = (void *)uelf->info.shdrs; - GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; + unsigned int i; + GElf_Sym *sym; + void *loc; + void *real_loc; + u64 val; + const char *sym_name; + GElf_Xword tls_size; + GElf_Shdr *shdrs = (void *)uelf->info.shdrs; + GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; - log_debug("Applying relocate section %u to %u\n", relsec, - shdrs[relsec].sh_info); + log_debug("Applying relocate section %u to %u\n", relsec, + shdrs[relsec].sh_info); - for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { - /* This is where to make the change, calculate it from kernel address */ - loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + - rel[i].r_offset; + for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change, calculate it from kernel address */ + loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + rel[i].r_offset; + real_loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + + rel[i].r_offset; - real_loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + - rel[i].r_offset; + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (GElf_Sym *)shdrs[symindex].sh_addr + GELF_R_SYM(rel[i].r_info); + if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && + sym->st_shndx < uelf->info.hdr->e_shnum) { + sym_name = uelf->info.shstrtab + shdrs[sym->st_shndx].sh_name; + } else { + sym_name = uelf->strtab + sym->st_name; + } - /* This is the symbol it is referring to. Note that all - undefined symbols have been resolved. */ - sym = (GElf_Sym *)shdrs[symindex].sh_addr + - GELF_R_SYM(rel[i].r_info); + log_debug("type %d st_value %lx r_addend %lx loc %lx\n", + (int)GELF_R_TYPE(rel[i].r_info), sym->st_value, + rel[i].r_addend, (u64)loc); - if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && - sym->st_shndx < uelf->info.hdr->e_shnum) - sym_name = uelf->info.shstrtab + - shdrs[sym->st_shndx].sh_name; - else - sym_name = uelf->strtab + sym->st_name; - - log_debug("type %d st_value %lx r_addend %lx loc %lx\n", - (int)GELF_R_TYPE(rel[i].r_info), sym->st_value, - rel[i].r_addend, (u64)loc); - - val = sym->st_value + rel[i].r_addend; - switch (GELF_R_TYPE(rel[i].r_info)) { - case R_X86_64_NONE: - break; - case R_X86_64_64: - if (*(u64 *)loc != 0) - goto invalid_relocation; - memcpy(loc, &val, 8); - break; - case R_X86_64_32: - if (*(u32 *)loc != 0) - goto invalid_relocation; - memcpy(loc, &val, 4); - if (val != *(u32 *)loc && - (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) - goto overflow; - break; - case R_X86_64_32S: - if (*(s32 *)loc != 0) - goto invalid_relocation; - memcpy(loc, &val, 4); - if ((s64)val != *(s32 *)loc && - (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) - goto overflow; - break; - case R_X86_64_TLSGD: - case R_X86_64_GOTTPOFF: - case R_X86_64_GOTPCRELX: - case R_X86_64_REX_GOTPCRELX: - if (sym->st_value == 0) - goto overflow; - /* G + GOT + A*/ - val = sym->st_value + rel[i].r_addend; - case R_X86_64_PC32: - case R_X86_64_PLT32: - if (*(u32 *)loc != 0) - goto invalid_relocation; - val -= (u64)real_loc; - memcpy(loc, &val, 4); - break; - case R_X86_64_PC64: - if (*(u64 *)loc != 0) - goto invalid_relocation; - val -= (u64)real_loc; - memcpy(loc, &val, 8); - break; - case R_X86_64_TPOFF32: - tls_size = ALIGN(uelf->relf->tls_size, - uelf->relf->tls_align); - // %fs + val - tls_size - if (val >= tls_size) - goto overflow; - val -= (u64)tls_size; - memcpy(loc, &val, 4); - break; - default: - log_error("Unknown rela relocation: %lu\n", - GELF_R_TYPE(rel[i].r_info)); - return -ENOEXEC; - } - } - return 0; + val = sym->st_value + (unsigned long)rel[i].r_addend; + switch (GELF_R_TYPE(rel[i].r_info)) { + case R_X86_64_NONE: + break; + case R_X86_64_64: + if (*(u64 *)loc != 0) { + goto invalid_relocation; + } + memcpy(loc, &val, 8); + break; + case R_X86_64_32: + if (*(u32 *)loc != 0) { + goto invalid_relocation; + } + memcpy(loc, &val, 4); + if (val != *(u32 *)loc && + (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) { + goto overflow; + } + break; + case R_X86_64_32S: + if (*(s32 *)loc != 0) { + goto invalid_relocation; + } + memcpy(loc, &val, 4); + if ((s64)val != *(s32 *)loc && + (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) { + goto overflow; + } + break; + case R_X86_64_TLSGD: + case R_X86_64_GOTTPOFF: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + if (sym->st_value == 0) { + goto overflow; + } + /* G + GOT + A */ + val = sym->st_value + (unsigned long)rel[i].r_addend; + /* fall through */ + case R_X86_64_PC32: + case R_X86_64_PLT32: + if (*(u32 *)loc != 0) { + goto invalid_relocation; + } + val -= (u64)real_loc; + memcpy(loc, &val, 4); + break; + case R_X86_64_PC64: + if (*(u64 *)loc != 0) { + goto invalid_relocation; + } + val -= (u64)real_loc; + memcpy(loc, &val, 8); + break; + case R_X86_64_TPOFF32: + tls_size = ALIGN(uelf->relf->tls_size, uelf->relf->tls_align); + // %fs + val - tls_size + if (val >= tls_size) { + goto overflow; + } + val -= (u64)tls_size; + memcpy(loc, &val, 4); + break; + default: + log_error("Unknown rela relocation: %lu\n", + GELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; invalid_relocation: - log_error("upatch: Skipping invalid relocation target, \ + log_error("upatch: Skipping invalid relocation target, \ existing value is nonzero for type %d, loc %p, name %s\n", - (int)GELF_R_TYPE(rel[i].r_info), loc, sym_name); - return -ENOEXEC; + (int)GELF_R_TYPE(rel[i].r_info), loc, sym_name); + return -ENOEXEC; overflow: - log_error("upatch: overflow in relocation type %d name %s\n", - (int)GELF_R_TYPE(rel[i].r_info), sym_name); - return -ENOEXEC; + log_error("upatch: overflow in relocation type %d name %s\n", + (int)GELF_R_TYPE(rel[i].r_info), sym_name); + return -ENOEXEC; } diff --git a/upatch-manage/arch/x86_64/resolve.c b/upatch-manage/arch/x86_64/resolve.c index 5432b207dfbe93c924dd2c9448925d24e8fe887b..405c90c8e4b28922bc2dab88e732d10f8926e32b 100644 --- a/upatch-manage/arch/x86_64/resolve.c +++ b/upatch-manage/arch/x86_64/resolve.c @@ -26,31 +26,33 @@ #define X86_64_JUMP_TABLE_JMP 0x90900000000225ff /* jmp [rip+2]; nop; nop */ struct upatch_jmp_table_entry { - unsigned long inst; - unsigned long addr; + unsigned long inst; + unsigned long addr; }; -unsigned int get_jmp_table_entry() +unsigned int get_jmp_table_entry(void) { - return sizeof(struct upatch_jmp_table_entry); + return sizeof(struct upatch_jmp_table_entry); } static unsigned long setup_jmp_table(struct upatch_elf *uelf, - unsigned long jmp_addr) + unsigned long jmp_addr) { - struct upatch_jmp_table_entry *table = - uelf->core_layout.kbase + uelf->jmp_offs; - unsigned int index = uelf->jmp_cur_entry; - if (index >= uelf->jmp_max_entry) { - log_error("jmp table overflow\n"); - return 0; - } - - table[index].inst = X86_64_JUMP_TABLE_JMP; - table[index].addr = jmp_addr; - uelf->jmp_cur_entry++; - return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + - index * sizeof(struct upatch_jmp_table_entry)); + struct upatch_jmp_table_entry *table = uelf->core_layout.kbase + + uelf->jmp_offs; + + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("jmp table overflow\n"); + return 0; + } + + table[index].inst = X86_64_JUMP_TABLE_JMP; + table[index].addr = jmp_addr; + uelf->jmp_cur_entry++; + + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); } /* @@ -59,84 +61,80 @@ static unsigned long setup_jmp_table(struct upatch_elf *uelf, * GOT only need record address and resolve it by [got_addr]. * To simplify design, use same table for both jmp table and GOT. */ -static unsigned long setup_got_table(struct upatch_elf *uelf, - unsigned long jmp_addr, - unsigned long tls_addr) +unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long jmp_addr, unsigned long tls_addr) { - struct upatch_jmp_table_entry *table = - uelf->core_layout.kbase + uelf->jmp_offs; - unsigned int index = uelf->jmp_cur_entry; - if (index >= uelf->jmp_max_entry) { - log_error("got table overflow\n"); - return 0; - } - - table[index].inst = jmp_addr; - table[index].addr = tls_addr; - uelf->jmp_cur_entry++; - return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + - index * sizeof(struct upatch_jmp_table_entry)); + struct upatch_jmp_table_entry *table = uelf->core_layout.kbase + + uelf->jmp_offs; + + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("got table overflow\n"); + return 0; + } + + table[index].inst = jmp_addr; + table[index].addr = tls_addr; + uelf->jmp_cur_entry++; + + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); } unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, - unsigned long r_type, unsigned long addr) + unsigned long r_type, unsigned long addr) { - unsigned long jmp_addr; - unsigned long elf_addr = 0; + unsigned long jmp_addr; + unsigned long elf_addr = 0; - if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, - sizeof(jmp_addr))) { - log_error("copy address failed\n"); - goto out; - } + (void)r_type; + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } - elf_addr = setup_jmp_table(uelf, jmp_addr); - - log_debug("0x%lx: jmp_addr=0x%lx\n", elf_addr, jmp_addr); + elf_addr = setup_jmp_table(uelf, jmp_addr); + log_debug("0x%lx: jmp_addr=0x%lx\n", elf_addr, jmp_addr); out: - return elf_addr; + return elf_addr; } unsigned long insert_got_table(struct upatch_elf *uelf, struct object_file *obj, - unsigned long r_type, unsigned long addr) + unsigned long r_type, unsigned long addr) { - unsigned long jmp_addr; - unsigned long tls_addr = 0xffffffff; - unsigned long elf_addr = 0; - - if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, - sizeof(jmp_addr))) { - log_error("copy address failed\n"); - goto out; - } - - /* - * R_X86_64_TLSGD: allocate two contiguous entries in the GOT to hold a - * tls_index structure tls_index has two unsigned long, the first one is - * R_X86_64_DTPMOD64. - */ - if (r_type == R_X86_64_DTPMOD64 && - upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), - &tls_addr, sizeof(tls_addr))) { - log_error("copy address failed\n"); - goto out; - } - - if (uelf->relf->info.is_dyn && !uelf->relf->info.is_pie) { - elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); - - log_debug("0x%lx: jmp_addr=0x%lx\n", elf_addr, jmp_addr); - - } else { - /* - * For non-dynamic library files, global variables are not placed in the GOT table - */ - elf_addr = jmp_addr; - log_debug("For non-dynamic library: jmp_addr=0x%lx\n", jmp_addr); - } - + unsigned long jmp_addr; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, sizeof(jmp_addr))) { + log_error("copy address failed\n"); + goto out; + } + + /* + * R_X86_64_TLSGD: allocate two contiguous entries in the GOT to hold a + * tls_index structure tls_index has two unsigned long, the first one is + * R_X86_64_DTPMOD64. + */ + if (r_type == R_X86_64_DTPMOD64 && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed\n"); + goto out; + } + + if (uelf->relf->info.is_dyn && !uelf->relf->info.is_pie) { + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + log_debug("0x%lx: jmp_addr=0x%lx\n", elf_addr, jmp_addr); + } else { + /* + * For non-dynamic library files, global variables are not placed in the GOT table + */ + elf_addr = jmp_addr; + log_debug("For non-dynamic library: jmp_addr=0x%lx\n", jmp_addr); + } out: - return elf_addr; + return elf_addr; } diff --git a/upatch-manage/list.h b/upatch-manage/list.h index 7e1e16f8d91a1c775b5204882b210fc4e2af668c..9e55a75802ac34e620bc4f4c306ed4469ac2b422 100644 --- a/upatch-manage/list.h +++ b/upatch-manage/list.h @@ -34,29 +34,29 @@ */ #define container_of(ptr, type, member) \ - ((type *)(((char *)(ptr)) - offsetof(type, member))) + ((type *)(((void *)(ptr)) - offsetof(type, member))) struct list_head { - struct list_head *next, *prev; + struct list_head *next, *prev; }; -#define LIST_HEAD_INIT(name) \ - { \ - &(name), &(name) \ - } +#define LIST_HEAD_INIT(name) \ + { \ + &(name), &(name) \ + } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) -#define INIT_LIST_HEAD(ptr) \ - do { \ - (ptr)->next = (ptr); \ - (ptr)->prev = (ptr); \ - } while (0) +#define INIT_LIST_HEAD(ptr) \ + do { \ + (ptr)->next = (ptr); \ + (ptr)->prev = (ptr); \ + } while (0) static inline void list_init(struct list_head *list) { - list->next = list; - list->prev = list; + list->next = list; + list->prev = list; } /* @@ -66,12 +66,12 @@ static inline void list_init(struct list_head *list) * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, - struct list_head *next) + struct list_head *next) { - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; } /** @@ -84,7 +84,7 @@ static inline void __list_add(struct list_head *new, struct list_head *prev, */ static inline void list_add_head(struct list_head *new, struct list_head *head) { - __list_add(new, head, head->next); + __list_add(new, head, head->next); } /** @@ -97,7 +97,7 @@ static inline void list_add_head(struct list_head *new, struct list_head *head) */ static inline void list_add(struct list_head *new, struct list_head *head) { - __list_add(new, head->prev, head); + __list_add(new, head->prev, head); } /* @@ -109,8 +109,8 @@ static inline void list_add(struct list_head *new, struct list_head *head) */ static inline void __list_del(struct list_head *prev, struct list_head *next) { - next->prev = prev; - prev->next = next; + next->prev = prev; + prev->next = next; } /** @@ -121,27 +121,27 @@ static inline void __list_del(struct list_head *prev, struct list_head *next) */ static inline void __list_del_entry(struct list_head *entry) { - __list_del(entry->prev, entry->next); + __list_del(entry->prev, entry->next); } static inline void list_del(struct list_head *entry) { - __list_del_entry(entry); + __list_del_entry(entry); } /** * list_replace - replace old entry by new one - * @old : the element to be replaced - * @new : the new element to insert + * @old: the element to be replaced + * @new: the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, struct list_head *new) { - new->next = old->next; - new->next->prev = new; - new->prev = old->prev; - new->prev->next = new; + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; } /** @@ -151,8 +151,8 @@ static inline void list_replace(struct list_head *old, struct list_head *new) */ static inline void list_move(struct list_head *list, struct list_head *head) { - __list_del_entry(list); - list_add(list, head); + __list_del_entry(list); + list_add(list, head); } /** @@ -161,10 +161,10 @@ static inline void list_move(struct list_head *list, struct list_head *head) * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, - struct list_head *head) + struct list_head *head) { - __list_del_entry(list); - list_add(list, head); + __list_del_entry(list); + list_add(list, head); } /** @@ -173,9 +173,9 @@ static inline void list_move_tail(struct list_head *list, * @head: the head of the list */ static inline int list_is_last(const struct list_head *list, - const struct list_head *head) + const struct list_head *head) { - return list->next == head; + return list->next == head; } /** @@ -184,7 +184,7 @@ static inline int list_is_last(const struct list_head *list, */ static inline int list_empty(const struct list_head *head) { - return head->next == head; + return head->next == head; } /** @@ -202,8 +202,8 @@ static inline int list_empty(const struct list_head *head) */ static inline int list_empty_careful(const struct list_head *head) { - struct list_head *next = head->next; - return (next == head) && (next == head->prev); + struct list_head *next = head->next; + return (next == head) && (next == head->prev); } /** @@ -212,12 +212,12 @@ static inline int list_empty_careful(const struct list_head *head) */ static inline void list_rotate_left(struct list_head *head) { - struct list_head *first; + struct list_head *first; - if (!list_empty(head)) { - first = head->next; - list_move_tail(first, head); - } + if (!list_empty(head)) { + first = head->next; + list_move_tail(first, head); + } } /** @@ -226,33 +226,33 @@ static inline void list_rotate_left(struct list_head *head) */ static inline int list_is_singular(const struct list_head *head) { - return !list_empty(head) && (head->next == head->prev); + return !list_empty(head) && (head->next == head->prev); } static inline void __list_cut_position(struct list_head *list, - struct list_head *head, - struct list_head *entry) + struct list_head *head, struct list_head *entry) { - struct list_head *new_first = entry->next; - list->next = head->next; - list->next->prev = list; - list->prev = entry; - entry->next = list; - head->next = new_first; - new_first->prev = head; + struct list_head *new_first = entry->next; + + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; } static inline void __list_splice(const struct list_head *list, - struct list_head *prev, struct list_head *next) + struct list_head *prev, struct list_head *next) { - struct list_head *first = list->next; - struct list_head *last = list->prev; + struct list_head *first = list->next; + struct list_head *last = list->prev; - first->prev = prev; - prev->next = first; + first->prev = prev; + prev->next = first; - last->next = next; - next->prev = last; + last->next = next; + next->prev = last; } /** @@ -261,10 +261,11 @@ static inline void __list_splice(const struct list_head *list, * @head: the place to add it in the first list. */ static inline void list_splice(const struct list_head *list, - struct list_head *head) + struct list_head *head) { - if (!list_empty(list)) - __list_splice(list, head, head->next); + if (!list_empty(list)) { + __list_splice(list, head, head->next); + } } /** @@ -273,36 +274,37 @@ static inline void list_splice(const struct list_head *list, * @head: the place to add it in the first list. */ static inline void list_splice_tail(struct list_head *list, - struct list_head *head) + struct list_head *head) { - if (!list_empty(list)) - __list_splice(list, head->prev, head); + if (!list_empty(list)) { + __list_splice(list, head->prev, head); + } } /** * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. */ #define list_entry(ptr, type, member) container_of(ptr, type, member) /** * list_first_entry - get the first element from a list - * @ptr: the list head to take the element from. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ - list_entry((ptr)->next, type, member) + list_entry((ptr)->next, type, member) /** * list_last_entry - get the last element from a list - * @ptr: the list head to take the element from. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. * * Note, that list is expected to be not empty. */ @@ -310,220 +312,250 @@ static inline void list_splice_tail(struct list_head *list, /** * list_first_entry_or_null - get the first element from a list - * @ptr: the list head to take the element from. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. * * Note that if the list is empty, it returns NULL. */ -#define list_first_entry_or_null(ptr, type, member) \ - ({ \ - struct list_head *head__ = (ptr); \ - struct list_head *pos__ = READ_ONCE(head__->next); \ - pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ - }) +#define list_first_entry_or_null(ptr, type, member) \ + ({ \ + struct list_head *head__ = (ptr); \ + struct list_head *pos__ = READ_ONCE(head__->next); \ + pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ + }) /** * list_next_entry - get the next element in list - * @pos: the type * to cursor - * @member: the name of the list_head within the struct. + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. */ #define list_next_entry(pos, member) \ - list_entry((pos)->member.next, typeof(*(pos)), member) + list_entry((pos)->member.next, typeof(*(pos)), member) /** * list_prev_entry - get the prev element in list - * @pos: the type * to cursor - * @member: the name of the list_head within the struct. + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. */ #define list_prev_entry(pos, member) \ - list_entry((pos)->member.prev, typeof(*(pos)), member) + list_entry((pos)->member.prev, typeof(*(pos)), member) /** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. */ #define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) + for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) /** - * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; pos != (head); pos = pos->prev) + for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) +#define list_for_each_safe(pos, n, head) \ + for ( \ + (pos) = (head)->next, (n) = (pos)->next; \ + (pos) != (head); \ + (pos) = (n), (n) = (pos)->next \ + ) /** * list_for_each_prev_safe - iterate over a list backwards safe against removal * of list entry - * @pos: the &struct list_head to use as a loop cursor. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. */ -#define list_for_each_prev_safe(pos, n, head) \ - for (pos = (head)->prev, n = pos->prev; pos != (head); \ - pos = n, n = pos->prev) +#define list_for_each_prev_safe(pos, n, head) \ + for ( \ + (pos) = (head)->prev, (n) = (pos)->prev; \ + (pos) != (head); \ + (pos) = (n), (n) = (pos)->prev \ + ) /** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_first_entry(head, typeof(*pos), member); \ - &pos->member != (head); pos = list_next_entry(pos, member)) +#define list_for_each_entry(pos, head, member) \ + for ( \ + (pos) = list_first_entry(head, typeof(*(pos)), member); \ + &((pos)->member) != (head); \ + (pos) = list_next_entry(pos, member) \ + ) /** * list_for_each_entry_reverse - iterate backwards over list of given type. - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. */ -#define list_for_each_entry_reverse(pos, head, member) \ - for (pos = list_last_entry(head, typeof(*pos), member); \ - &pos->member != (head); pos = list_prev_entry(pos, member)) +#define list_for_each_entry_reverse(pos, head, member) \ + for ( \ + (pos) = list_last_entry(head, typeof(*(pos)), member); \ + &((pos)->member) != (head); \ + (pos) = list_prev_entry(pos, member) \ + ) /** * list_prepare_entry - prepare a pos entry for use in * list_for_each_entry_continue() - * @pos: the type * to use as a start point - * @head: the head of the list - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_head within the struct. * * Prepares a pos entry for use as a start point in * list_for_each_entry_continue(). */ #define list_prepare_entry(pos, head, member) \ - ((pos) ?: list_entry(head, typeof(*pos), member)) + ((pos) ?: list_entry(head, typeof(*(pos)), member)) /** * list_for_each_entry_continue - continue iteration over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. * * Continue to iterate over list of given type, continuing after * the current position. */ -#define list_for_each_entry_continue(pos, head, member) \ - for (pos = list_next_entry(pos, member); &pos->member != (head); \ - pos = list_next_entry(pos, member)) +#define list_for_each_entry_continue(pos, head, member) \ + for ( \ + (pos) = list_next_entry(pos, member); \ + &((pos)->member) != (head); \ + (pos) = list_next_entry(pos, member) \ + ) /** * list_for_each_entry_continue_reverse - iterate backwards from the given point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. * * Start to iterate over list of given type backwards, continuing after * the current position. */ -#define list_for_each_entry_continue_reverse(pos, head, member) \ - for (pos = list_prev_entry(pos, member); &pos->member != (head); \ - pos = list_prev_entry(pos, member)) +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for ( \ + (pos) = list_prev_entry(pos, member); \ + &((pos)->member) != (head); \ + (pos) = list_prev_entry(pos, member) \ + ) /** * list_for_each_entry_from - iterate over list of given type from the current * point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. * * Iterate over list of given type, continuing from current position. */ #define list_for_each_entry_from(pos, head, member) \ - for (; &pos->member != (head); pos = list_next_entry(pos, member)) + for (; &((pos)->member) != (head); (pos) = list_next_entry(pos, member)) /** * list_for_each_entry_from_reverse - iterate backwards over list of given type * from the current point - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. * * Iterate backwards over list of given type, continuing from current position. */ #define list_for_each_entry_from_reverse(pos, head, member) \ - for (; &pos->member != (head); pos = list_prev_entry(pos, member)) + for (; &((pos)->member) != (head); (pos) = list_prev_entry(pos, member)) /** * list_for_each_entry_safe - iterate over list of given type safe against * removal of list entry - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_first_entry(head, typeof(*pos), member), \ - n = list_next_entry(pos, member); \ - &pos->member != (head); pos = n, n = list_next_entry(n, member)) +#define list_for_each_entry_safe(pos, n, head, member) \ + for ( \ + (pos) = list_first_entry(head, typeof(*(pos)), member), \ + (n) = list_next_entry(pos, member); \ + &((pos)->member) != (head); \ + (pos) = (n), (n) = list_next_entry(n, member) \ + ) /** * list_for_each_entry_safe_continue - continue list iteration safe against * removal - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. * * Iterate over list of given type, continuing after current point, * safe against removal of list entry. */ #define list_for_each_entry_safe_continue(pos, n, head, member) \ - for (pos = list_next_entry(pos, member), \ - n = list_next_entry(pos, member); \ - &pos->member != (head); pos = n, n = list_next_entry(n, member)) + for ( \ + (pos) = list_next_entry(pos, member), \ + (n) = list_next_entry(pos, member); \ + &((pos)->member) != (head); \ + (pos) = (n), (n) = list_next_entry(n, member) \ + ) /** * list_for_each_entry_safe_from - iterate over list from current point safe * against removal - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. * * Iterate over list of given type from current point, safe against * removal of list entry. */ -#define list_for_each_entry_safe_from(pos, n, head, member) \ - for (n = list_next_entry(pos, member); &pos->member != (head); \ - pos = n, n = list_next_entry(n, member)) +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for ( \ + (n) = list_next_entry(pos, member); \ + &((pos)->member) != (head); \ + (pos) = (n), (n) = list_next_entry(n, member) \ + ) /** * list_for_each_entry_safe_reverse - iterate backwards over list safe against * removal - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_head within the struct. + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. * * Iterate backwards over list of given type, safe against removal * of list entry. */ -#define list_for_each_entry_safe_reverse(pos, n, head, member) \ - for (pos = list_last_entry(head, typeof(*pos), member), \ - n = list_prev_entry(pos, member); \ - &pos->member != (head); pos = n, n = list_prev_entry(n, member)) +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for ( \ + (pos) = list_last_entry(head, typeof(*(pos)), member), \ + (n) = list_prev_entry(pos, member); \ + &((pos)->member) != (head); \ + (pos) = (n), (n) = list_prev_entry(n, member) \ + ) /** * list_safe_reset_next - reset a stale list_for_each_entry_safe loop - * @pos: the loop cursor used in the list_for_each_entry_safe loop - * @n: temporary storage used in list_for_each_entry_safe - * @member: the name of the list_head within the struct. + * @pos: the loop cursor used in the list_for_each_entry_safe loop + * @n: temporary storage used in list_for_each_entry_safe + * @member: the name of the list_head within the struct. * * list_safe_reset_next is not safe to use in general if the list may be * modified concurrently (eg. the lock is dropped in the loop body). An @@ -531,6 +563,6 @@ static inline void list_splice_tail(struct list_head *list, * and list_safe_reset_next is called after re-taking the lock and before * completing the current iteration of the loop body. */ -#define list_safe_reset_next(pos, n, member) n = list_next_entry(pos, member) +#define list_safe_reset_next(pos, n, member) (n) = list_next_entry(pos, member) #endif diff --git a/upatch-manage/log.h b/upatch-manage/log.h index 32e9c56e02f11d6b2177d8162f9f2c2360ec464c..a109958b51dc8506b386882adf31b2185bda6cb5 100644 --- a/upatch-manage/log.h +++ b/upatch-manage/log.h @@ -25,29 +25,22 @@ #ifndef __UPATCH_LOG_H_ #define __UPATCH_LOG_H_ -#include #include +#include -/* Files that include log.h must define loglevel and logprefix */ -extern enum loglevel loglevel; -extern char *logprefix; +/* Files that include log.h must define g_loglevel and g_logprefix */ +extern enum log_level g_loglevel; +extern char *g_logprefix; enum exit_status { - EXIT_STATUS_SUCCESS = 0, - EXIT_STATUS_ERROR = 1, - EXIT_STATUS_DIFF_FATAL = 2, - EXIT_STATUS_NO_CHANGE = 3, + EXIT_STATUS_SUCCESS = 0, + EXIT_STATUS_ERROR = 1, }; -/* Since upatch-build is an one-shot program, we do not care about failure - * handler */ -#define ERROR(format, ...) \ - error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, logprefix, \ - __FUNCTION__, __LINE__, ##__VA_ARGS__) - -#define DIFF_FATAL(format, ...) \ - error(EXIT_STATUS_DIFF_FATAL, 0, "ERROR: %s: %s: %d: " format, \ - logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) +/* Since upatch-build is an one-shot program, we do not care about failure handler */ +#define ERROR(format, ...) \ + error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, \ + g_logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) /* it is time cost */ #define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__) @@ -55,22 +48,26 @@ enum exit_status { #define log_warn(format, ...) log(WARN, format, ##__VA_ARGS__) #define log_error(format, ...) log(ERR, format, ##__VA_ARGS__) -#define log(level, format, ...) \ - ({ \ - if (loglevel <= (level)) \ - printf(format, ##__VA_ARGS__); \ - }) +#define log(level, format, ...) \ + do { \ + if (g_loglevel <= (level)) { \ + printf(format, ##__VA_ARGS__); \ + } \ + } while (0) -#define REQUIRE(COND, message) \ - do \ - if (!(COND)) \ - ERROR(message); \ - while (0) +#define REQUIRE(COND, message) \ + do { \ + if (!(COND)) { \ + ERROR(message); \ + } \ + } \ + while (0) -enum loglevel { - DEBUG, - NORMAL, - WARN, - ERR, +enum log_level { + DEBUG, + NORMAL, + WARN, + ERR, }; + #endif diff --git a/upatch-manage/upatch-common.c b/upatch-manage/upatch-common.c index 0161350937f1dff518d3cff1c54c279787472a5f..f9076ee85b6ca2d2089f77235e879e83de7f3a0d 100644 --- a/upatch-manage/upatch-common.c +++ b/upatch-manage/upatch-common.c @@ -24,5 +24,5 @@ bool streql(const char *a, const char *b) { - return strlen(a) == strlen(b) && !strncmp(a, b, strlen(a)); -} \ No newline at end of file + return strlen(a) == strlen(b) && !strncmp(a, b, strlen(a)); +} diff --git a/upatch-manage/upatch-common.h b/upatch-manage/upatch-common.h index ab4084a1efc4376ba4bd5175e8e1401ae09169db..9b5a0e6d7a8bb318d5a3a506402df3368b92943e 100644 --- a/upatch-manage/upatch-common.h +++ b/upatch-manage/upatch-common.h @@ -22,27 +22,30 @@ #define __UPATCH_COMMON__ #include +#include -#define ALLOC_LINK(_new, _list) \ - { \ - (_new) = calloc(1, sizeof(*(_new))); \ - if (!(_new)) \ - ERROR("calloc"); \ - INIT_LIST_HEAD(&(_new)->list); \ - if (_list) \ - list_add(&(_new)->list, (_list)); \ - } +#define ALLOC_LINK(_new, _list) \ + do { \ + (_new) = calloc(1, sizeof(*(_new))); \ + if (!(_new)) { \ + ERROR("calloc"); \ + } \ + INIT_LIST_HEAD(&(_new)->list); \ + if (_list) { \ + list_add(&(_new)->list, (_list)); \ + } \ + } while (0) -static inline int page_shift(int n) +static inline int page_shift(long n) { - int res = -1; + int res = -1; - while (n) { - res++; - n >>= 1; - } + while (n) { + res++; + n >>= 1; + } - return res; + return res; } #ifndef PAGE_SIZE @@ -50,15 +53,26 @@ static inline int page_shift(int n) #define PAGE_MASK (~(PAGE_SIZE - 1)) #define PAGE_SHIFT page_shift(PAGE_SIZE) #endif + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#define ALIGN(x, a) (((x) + (a)-1) & (~((a)-1))) -#define PAGE_ALIGN(x) ALIGN((x), PAGE_SIZE) +#define ALIGN(x, a) (((x) + (a) - 1) & (~((a) - 1))) +#define PAGE_ALIGN(x) ALIGN((x), (unsigned long)PAGE_SIZE) -#define ROUND_DOWN(x, m) ((x) & ~((m)-1)) -#define ROUND_UP(x, m) (((x) + (m)-1) & ~((m)-1)) +#define ROUND_DOWN(x, m) ((x) & ~((m) - 1)) +#define ROUND_UP(x, m) (((x) + (m) - 1) & ~((m) - 1)) #define BIT(x) (1UL << (x)) +#define SEC2MICRO 1000000 + +static inline long get_microseconds(struct timeval *start, struct timeval *end) +{ + long sec = end->tv_sec - start->tv_sec; + long usec = end->tv_usec - start->tv_usec; + + return sec * SEC2MICRO + usec; +} + bool streql(const char *, const char *); -#endif \ No newline at end of file +#endif diff --git a/upatch-manage/upatch-elf.c b/upatch-manage/upatch-elf.c index 78c7fd7a5cd170b7a48b6d37488ce62a170b7dec..f3773f90d7a82c9fa9edd99f8c145e58cbed5d41 100644 --- a/upatch-manage/upatch-elf.c +++ b/upatch-manage/upatch-elf.c @@ -30,14 +30,14 @@ #include "upatch-elf.h" #include "upatch-ptrace.h" -static int read_from_offset(int fd, void **buf, int len, off_t offset) +static int read_from_offset(int fd, void **buf, unsigned long len, off_t offset) { *buf = malloc(len); if (*buf == NULL) { return -errno; } - int size = pread(fd, *buf, len, offset); + ssize_t size = pread(fd, *buf, len, offset); if (size == -1) { return -errno; } @@ -47,8 +47,8 @@ static int read_from_offset(int fd, void **buf, int len, off_t offset) static int open_elf(struct elf_info *einfo, const char *name) { - int ret = 0, fd = -1, i; - char *sec_name; + int ret = 0; + int fd = -1; struct stat st; fd = open(name, O_RDONLY); @@ -65,7 +65,8 @@ static int open_elf(struct elf_info *einfo, const char *name) goto out; } - ret = read_from_offset(fd, (void **)&einfo->patch_buff, st.st_size, 0); + ret = read_from_offset(fd, (void **)&einfo->patch_buff, + (unsigned long)st.st_size, 0); if (ret != 0) { log_error("Failed to read file '%s'\n", name); goto out; @@ -73,32 +74,20 @@ static int open_elf(struct elf_info *einfo, const char *name) einfo->name = name; einfo->inode = st.st_ino; - einfo->patch_size = st.st_size; + einfo->patch_size = (unsigned long)st.st_size; einfo->hdr = (void *)einfo->patch_buff; einfo->shdrs = (void *)einfo->hdr + einfo->hdr->e_shoff; - einfo->shstrtab = (void *)einfo->hdr + einfo->shdrs[einfo->hdr->e_shstrndx].sh_offset; + einfo->shstrtab = (void *)einfo->hdr + + einfo->shdrs[einfo->hdr->e_shstrndx].sh_offset; void *einfo_eof = einfo->hdr + einfo->patch_size; - if ((void *)einfo->shdrs > einfo_eof || (void *)einfo->shstrtab > einfo_eof) { + if ((void *)einfo->shdrs > einfo_eof || + (void *)einfo->shstrtab > einfo_eof) { log_error("File '%s' is not a valid elf\n", name); ret = -ENOEXEC; goto out; } - for (i = 0; i < einfo->hdr->e_shnum; ++i) { - sec_name = einfo->shstrtab + einfo->shdrs[i].sh_name; - if (streql(sec_name, BUILD_ID_NAME) && einfo->shdrs[i].sh_type == SHT_NOTE) { - einfo->num_build_id = i; - break; - } - } - - if (einfo->num_build_id == 0) { - ret = -EINVAL; - log_error("Cannot find section '%s'\n", BUILD_ID_NAME); - goto out; - } - ret = 0; out: @@ -116,16 +105,18 @@ int upatch_init(struct upatch_elf *uelf, const char *name) return ret; } - for (int i = 1; i < uelf->info.hdr->e_shnum; ++i) { + for (unsigned int i = 1; i < uelf->info.hdr->e_shnum; ++i) { char *sec_name = uelf->info.shstrtab + uelf->info.shdrs[i].sh_name; if (uelf->info.shdrs[i].sh_type == SHT_SYMTAB) { uelf->num_syms = uelf->info.shdrs[i].sh_size / sizeof(GElf_Sym); uelf->index.sym = i; uelf->index.str = uelf->info.shdrs[i].sh_link; uelf->strtab = (char *)uelf->info.hdr + - uelf->info.shdrs[uelf->info.shdrs[i].sh_link].sh_offset; + uelf->info.shdrs[uelf->info.shdrs[i].sh_link].sh_offset; } else if (streql(sec_name, UPATCH_FUNC_NAME)) { uelf->index.upatch_funcs = i; + } else if (streql(sec_name, UPATCH_FUNC_STRING)) { + uelf->index.upatch_string = i; } } @@ -136,25 +127,27 @@ static bool is_pie_elf(struct running_elf *relf) { GElf_Shdr *shdr = &relf->info.shdrs[relf->index.dynamic]; GElf_Dyn *dyns = (void *)relf->info.hdr + shdr->sh_offset; + if (relf->index.dynamic == 0) { return false; } + for (Elf64_Xword i = 0; i < shdr->sh_size / sizeof(GElf_Dyn); i++) { log_debug("Syminfo %lx, %lx\n", dyns[i].d_tag, dyns[i].d_un.d_val); if (dyns[i].d_tag == DT_FLAGS_1) { - if ((dyns[i].d_un.d_val & DF_1_PIE) != 0) + if ((dyns[i].d_un.d_val & DF_1_PIE) != 0) { return true; + } break; } } + return false; } -static bool is_dyn_elf(struct running_elf *relf) +static inline bool is_dyn_elf(struct running_elf *relf) { - GElf_Ehdr *ehdr = relf->info.hdr; - - return ehdr->e_type == ET_DYN; + return relf->info.hdr->e_type == ET_DYN; } int binary_init(struct running_elf *relf, const char *name) @@ -165,7 +158,7 @@ int binary_init(struct running_elf *relf, const char *name) return ret; } - for (int i = 1; i < relf->info.hdr->e_shnum; i++) { + for (unsigned int i = 1; i < relf->info.hdr->e_shnum; i++) { char *sec_name = relf->info.shstrtab + relf->info.shdrs[i].sh_name; if (relf->info.shdrs[i].sh_type == SHT_SYMTAB) { log_debug("Found section '%s', idx=%d\n", SYMTAB_NAME, i); @@ -173,22 +166,22 @@ int binary_init(struct running_elf *relf, const char *name) relf->index.sym = i; relf->index.str = relf->info.shdrs[i].sh_link; relf->strtab = (char *)relf->info.hdr + - relf->info.shdrs[relf->info.shdrs[i].sh_link].sh_offset; + relf->info.shdrs[relf->info.shdrs[i].sh_link].sh_offset; } else if (relf->info.shdrs[i].sh_type == SHT_DYNSYM) { log_debug("Found section '%s', idx=%d\n", DYNSYM_NAME, i); relf->index.dynsym = i; relf->index.dynstr = relf->info.shdrs[i].sh_link; relf->dynstrtab = (char *)relf->info.hdr + - relf->info.shdrs[relf->info.shdrs[i].sh_link].sh_offset; + relf->info.shdrs[relf->info.shdrs[i].sh_link].sh_offset; } else if (relf->info.shdrs[i].sh_type == SHT_DYNAMIC) { - log_debug("Found section '%s', idx=%d\n", DYNAMIC_NAME, i); + log_debug("Found section '%s', idx=%d\n", DYNAMIC_NAME, i); relf->index.dynamic = i; } else if (streql(sec_name, PLT_RELA_NAME) && - relf->info.shdrs[i].sh_type == SHT_RELA) { + relf->info.shdrs[i].sh_type == SHT_RELA) { log_debug("Found section '%s', idx=%d\n", PLT_RELA_NAME, i); relf->index.rela_plt = i; } else if (streql(sec_name, GOT_RELA_NAME) && - relf->info.shdrs[i].sh_type == SHT_RELA) { + relf->info.shdrs[i].sh_type == SHT_RELA) { log_debug("Found section '%s' idx=%d\n", GOT_RELA_NAME, i); relf->index.rela_dyn = i; } @@ -199,50 +192,33 @@ int binary_init(struct running_elf *relf, const char *name) if (relf->phdrs[i].p_type == PT_TLS) { relf->tls_size = relf->phdrs[i].p_memsz; relf->tls_align = relf->phdrs[i].p_align; - log_debug("Found TLS size = %ld, align = %ld\n", relf->tls_size, relf->tls_align); + log_debug("Found TLS size = %ld, align = %ld\n", + relf->tls_size, relf->tls_align); break; } } - + relf->info.is_pie = is_pie_elf(relf); relf->info.is_dyn = is_dyn_elf(relf); - return 0; -} - -bool check_build_id(struct elf_info *uelf, struct elf_info *relf) -{ - if (uelf->shdrs[uelf->num_build_id].sh_size != relf->shdrs[relf->num_build_id].sh_size) { - return false; - } - - void* uelf_build_id = (void *)uelf->hdr + uelf->shdrs[uelf->num_build_id].sh_offset; - void* relf_build_id = (void *)relf->hdr + relf->shdrs[relf->num_build_id].sh_offset; - size_t build_id_len = uelf->shdrs[uelf->num_build_id].sh_size; - if (memcmp(uelf_build_id, relf_build_id, build_id_len) != 0) { - return false; - } - return true; + return 0; } void binary_close(struct running_elf *relf) { - // TODO: free relf if (relf->info.patch_buff) { free(relf->info.patch_buff); - } + } } void upatch_close(struct upatch_elf *uelf) { - // TODO: free uelf if (uelf->info.patch_buff) { free(uelf->info.patch_buff); - } - + } if (uelf->core_layout.kbase) { free(uelf->core_layout.kbase); - } + } } bool is_upatch_section(const char *name) diff --git a/upatch-manage/upatch-elf.h b/upatch-manage/upatch-elf.h index 481fec982911cac427e0a35448c34e4b6e427356..6f62c7df0e108cebd70e8258f86cb9f919a49e9d 100644 --- a/upatch-manage/upatch-elf.h +++ b/upatch-manage/upatch-elf.h @@ -35,112 +35,120 @@ #define PLT_RELA_NAME ".rela.plt" #define BUILD_ID_NAME ".note.gnu.build-id" #define UPATCH_FUNC_NAME ".upatch.funcs" +#define UPATCH_FUNC_STRING ".upatch.strings" #define TDATA_NAME ".tdata" #define TBSS_NAME ".tbss" -#define JMP_TABLE_MAX_ENTRY 100 +#define JMP_TABLE_MAX_ENTRY 4096 #define UPATCH_HEADER "UPATCH" #define UPATCH_HEADER_LEN 6 #define UPATCH_ID_LEN 40 +struct upatch_func_addr { + unsigned long new_addr; + unsigned long new_size; + unsigned long old_addr; + unsigned long old_size; +}; + struct upatch_info_func { - unsigned long old_addr; - unsigned long new_addr; - unsigned long old_insn[3]; - unsigned long new_insn; + struct upatch_func_addr addr; + unsigned long old_insn[2]; + unsigned long new_insn; + char *name; }; struct upatch_info { - char magic[7]; // upatch magic - char id[UPATCH_ID_LEN + 1]; // upatch id - unsigned long size; // upatch_info and upatch_info_func size - unsigned long start; // upatch vma start - unsigned long end; // upatch vma end - unsigned int changed_func_num; - // upatch_header_func + char magic[7]; // upatch magic + char id[UPATCH_ID_LEN + 1]; // upatch id + unsigned long size; // upatch_info and upatch_info_func size + unsigned long start; // upatch vma start + unsigned long end; // upatch vma end + unsigned long changed_func_num; + struct upatch_info_func *funcs; + char *func_names; + unsigned long func_names_size; }; struct upatch_layout { - /* The actual code + data. */ - void *kbase; - void *base; - /* Total size. */ - unsigned int size; - /* The size of the executable code. */ - unsigned int text_size; - /* Size of RO section of the module (text+rodata) */ - unsigned int ro_size; - /* Size of RO after init section, not use it now */ - unsigned int ro_after_init_size; - /* The size of the info. */ - unsigned int info_size; + /* The actual code + data. */ + void *kbase; + void *base; + /* Total size. */ + unsigned long size; + /* The size of the executable code. */ + unsigned long text_size; + /* Size of RO section of the module (text+rodata) */ + unsigned long ro_size; + /* Size of RO after init section, not use it now */ + unsigned long ro_after_init_size; + /* The size of the info. */ + unsigned long info_size; }; struct upatch_patch_func { - unsigned long new_addr; - unsigned long new_size; - unsigned long old_addr; - unsigned long old_size; - unsigned long sympos; /* handle local symbols */ - char *name; + struct upatch_func_addr addr; + unsigned long sympos; /* handle local symbols */ + char *name; }; struct elf_info { - const char *name; - ino_t inode; - void *patch_buff; - size_t patch_size; - - GElf_Ehdr *hdr; - GElf_Shdr *shdrs; - char *shstrtab; - - unsigned int num_build_id; - bool is_pie; - bool is_dyn; + const char *name; + ino_t inode; + void *patch_buff; + size_t patch_size; + + GElf_Ehdr *hdr; + GElf_Shdr *shdrs; + char *shstrtab; + + unsigned int num_build_id; + bool is_pie; + bool is_dyn; }; struct running_elf { - struct elf_info info; + struct elf_info info; - unsigned long num_syms; - char *strtab; - char *dynstrtab; + unsigned long num_syms; + char *strtab; + char *dynstrtab; - GElf_Phdr *phdrs; - GElf_Xword tls_size; - GElf_Xword tls_align; + GElf_Phdr *phdrs; + GElf_Xword tls_size; + GElf_Xword tls_align; - struct { - unsigned int sym, str; - unsigned int rela_dyn, rela_plt; - unsigned int dynsym, dynstr, dynamic; - } index; + struct { + unsigned int sym, str; + unsigned int rela_dyn, rela_plt; + unsigned int dynsym, dynstr, dynamic; + } index; - /* load bias, used to handle ASLR */ - unsigned long load_bias; - unsigned long load_start; + /* load bias, used to handle ASLR */ + unsigned long load_bias; + unsigned long load_start; }; struct upatch_elf { - struct elf_info info; + struct elf_info info; - unsigned long num_syms; - char *strtab; + unsigned long num_syms; + char *strtab; - struct { - unsigned int sym, str; - unsigned int upatch_funcs; - } index; + struct { + unsigned int sym, str; + unsigned int upatch_funcs; + unsigned int upatch_string; + } index; - unsigned long symoffs, stroffs, core_typeoffs; - unsigned long jmp_offs; - unsigned int jmp_cur_entry, jmp_max_entry; + unsigned long symoffs, stroffs, core_typeoffs; + unsigned long jmp_offs; + unsigned int jmp_cur_entry, jmp_max_entry; - /* memory layout for patch */ - struct upatch_layout core_layout; + /* memory layout for patch */ + struct upatch_layout core_layout; - struct running_elf *relf; + struct running_elf *relf; }; int upatch_init(struct upatch_elf *, const char *); @@ -148,8 +156,6 @@ int binary_init(struct running_elf *, const char *); void upatch_close(struct upatch_elf *); void binary_close(struct running_elf *); -bool check_build_id(struct elf_info *, struct elf_info *); - bool is_upatch_section(const char *); bool is_note_section(GElf_Word); diff --git a/upatch-manage/upatch-manage.c b/upatch-manage/upatch-manage.c index 8a7ba60711bfd1d04258a3fe9331fa53e04d3916..98868b717a108384e0938e13e5e85a3231f9a9c3 100644 --- a/upatch-manage/upatch-manage.c +++ b/upatch-manage/upatch-manage.c @@ -29,198 +29,205 @@ #include "log.h" #include "upatch-elf.h" #include "upatch-patch.h" +#include "upatch-stack-check.h" #define PROG_VERSION "upatch-manage "BUILD_VERSION #define COMMAND_SIZE 4 -enum loglevel loglevel = NORMAL; -char *logprefix; +enum log_level g_loglevel = NORMAL; +char *g_logprefix; char *command[COMMAND_SIZE] = { "", "patch", "unpatch", "info" }; enum Command { - DEFAULT, - PATCH, - UNPATCH, - INFO, + DEFAULT, + PATCH, + UNPATCH, + INFO, }; struct arguments { - int cmd; - int pid; - char *upatch; - char *binary; - char *uuid; - bool verbose; + int cmd; + int pid; + char *upatch; + char *binary; + char *uuid; + bool verbose; }; static struct argp_option options[] = { - { "verbose", 'v', NULL, 0, "Show verbose output" }, - { "uuid", 'U', "uuid", 0, "the uuid of the upatch" }, - { "pid", 'p', "pid", 0, "the pid of the user-space process" }, - { "upatch", 'u', "upatch", 0, "the upatch file" }, - { "binary", 'b', "binary", 0, "the binary file" }, - { "cmd", 0, "patch", 0, "Apply a upatch file to a user-space process" }, - { "cmd", 0, "unpatch", 0, - "Unapply a upatch file to a user-space process" }, - { NULL } + { "cmd", 0, "patch", 0, "Apply a upatch file to a process", 0 }, + { "cmd", 0, "unpatch", 0, "Unapply a upatch file to a process", 0 }, + { "pid", 'p', "pid", 0, "the pid of the user-space process", 0 }, + { "uuid", 'U', "uuid", 0, "the uuid of the upatch", 0 }, + { "upatch", 'u', "upatch", 0, "the upatch file", 0 }, + { "binary", 'b', "binary", 0, "the binary file", 0 }, + { "verbose", 'v', NULL, 0, "Show verbose output", 0 }, + { NULL } }; -static char program_doc[] = "Operate a upatch file on the user-space process"; +static char program_doc[] = "Operate a upatch file on the process"; -static char args_doc[] = - " --pid --upatch --binary --uuid "; +static char args_doc[] = " --pid --uuid " + "--upatch --binary "; const char *argp_program_version = PROG_VERSION; static error_t check_opt(struct argp_state *state) { - struct arguments *arguments = state->input; - - if (arguments->cmd == DEFAULT) { - argp_usage(state); - return ARGP_ERR_UNKNOWN; - } - switch (arguments->cmd) { - case PATCH: - case UNPATCH: - case INFO: - if (!arguments->pid || arguments->upatch == NULL || - arguments->binary == NULL || arguments->uuid == NULL) { - argp_usage(state); - return ARGP_ERR_UNKNOWN; - } - default: - break; - } - return 0; + struct arguments *arguments = state->input; + + if (arguments->cmd == DEFAULT) { + argp_usage(state); + return ARGP_ERR_UNKNOWN; + } + + switch (arguments->cmd) { + case PATCH: + case UNPATCH: + case INFO: + if (!arguments->pid || arguments->upatch == NULL || + arguments->binary == NULL || arguments->uuid == NULL) { + argp_usage(state); + return ARGP_ERR_UNKNOWN; + } + default: + break; + } + + return 0; } static error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct arguments *arguments = state->input; - - switch (key) { - case 'v': - arguments->verbose = true; - break; - case 'p': - arguments->pid = atoi(arg); - break; - case 'u': - arguments->upatch = arg; - break; - case 'b': - arguments->binary = arg; - break; - case 'U': - arguments->uuid = arg; - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) - argp_usage(state); - if (arguments->cmd != DEFAULT) - argp_usage(state); - for (int i = 1; i < COMMAND_SIZE; ++i) { - if (!strcmp(arg, command[i])) { - arguments->cmd = i; - break; - } - } - break; - case ARGP_KEY_END: - return check_opt(state); - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + struct arguments *arguments = state->input; + + switch (key) { + case 'v': + arguments->verbose = true; + break; + case 'p': + arguments->pid = atoi(arg); + break; + case 'u': + arguments->upatch = arg; + break; + case 'b': + arguments->binary = arg; + break; + case 'U': + arguments->uuid = arg; + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + argp_usage(state); + } + if (arguments->cmd != DEFAULT) { + argp_usage(state); + } + for (int i = 1; i < COMMAND_SIZE; ++i) { + if (!strcmp(arg, command[i])) { + arguments->cmd = i; + break; + } + } + break; + case ARGP_KEY_END: + return check_opt(state); + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } -static struct argp argp = { options, parse_opt, args_doc, program_doc }; +static struct argp argp = { + options, parse_opt, args_doc, program_doc, NULL, NULL, NULL +}; -int patch_upatch(const char *uuid, const char *binary_path, const char *upatch_path, int pid) +int patch_upatch(const char *uuid, const char *binary_path, + const char *upatch_path, int pid) { - struct upatch_elf uelf; - struct running_elf relf; - memset(&uelf, 0, sizeof(struct upatch_elf)); - memset(&relf, 0, sizeof(struct running_elf)); - - int ret = upatch_init(&uelf, upatch_path); - if (ret) { - log_error("Failed to initialize patch, pid=%d, ret=%d\n", pid, ret); - goto out; - } - - ret = process_patch(pid, &uelf, &relf, uuid, binary_path); - if (ret) { - log_error("Failed to patch process, pid=%d, ret=%d\n", pid, ret); - goto out; - } + struct upatch_elf uelf; + struct running_elf relf; + memset(&uelf, 0, sizeof(struct upatch_elf)); + memset(&relf, 0, sizeof(struct running_elf)); + + int ret = upatch_init(&uelf, upatch_path); + if (ret) { + log_error("Failed to initialize patch, pid=%d, ret=%d\n", pid, ret); + goto out; + } + + ret = process_patch(pid, &uelf, &relf, uuid, binary_path); + if (ret) { + log_error("Failed to patch process, pid=%d, ret=%d\n", pid, ret); + goto out; + } out: - upatch_close(&uelf); - binary_close(&relf); + upatch_close(&uelf); + binary_close(&relf); - return ret; + return ret; } -int unpatch_upatch(const char *uuid, const char *binary_path, const char *upatch_path, int pid) +int unpatch_upatch(const char *uuid, int pid) { - int ret = 0; + int ret = 0; - ret = process_unpatch(pid, uuid); - if (ret) { - log_error("Failed to unpatch process, pid=%d, ret=%d\n", pid, ret); - return ret; - } + ret = process_unpatch(pid, uuid); + if (ret) { + log_error("Failed to unpatch process, pid=%d, ret=%d\n", pid, ret); + return ret; + } - return 0; + return 0; } -int info_upatch(const char *binary_path, const char *upatch_path, int pid) +int info_upatch(int pid) { - int ret = process_info(pid); - if (ret != 0) { - log_error("Failed to get patch info, pid=%d, ret=%d\n", pid, ret); - return ret; - } + int ret = process_info(pid); + if (ret != 0) { + log_error("Failed to get patch info, pid=%d, ret=%d\n", pid, ret); + return ret; + } - return 0; + return 0; } int main(int argc, char *argv[]) { - struct arguments args; - int ret; - - memset(&args, 0, sizeof(struct arguments)); - argp_parse(&argp, argc, argv, 0, NULL, &args); - if (args.verbose) { - loglevel = DEBUG; - } - - logprefix = basename(args.upatch); - log_debug("PID: %d\n", args.pid); - log_debug("UUID: %s\n", args.uuid); - log_debug("Patch: %s\n", args.upatch); - log_debug("Binary: %s\n", args.binary); - - args.pid = args.pid & INT32_MAX; - switch (args.cmd) { - case PATCH: - ret = patch_upatch(args.uuid, args.binary, args.upatch, args.pid); - break; - case UNPATCH: - ret = unpatch_upatch(args.uuid, args.binary, args.upatch, args.pid); - break; - case INFO: - ret = info_upatch(args.binary, args.upatch, args.pid); - break; - default: - ERROR("Unknown command"); - ret = EINVAL; - break; - } - - (ret == 0) ? log_normal("SUCCESS\n\n") : log_error("FAILED\n\n"); - return abs(ret); + int ret; + + struct arguments args; + + memset(&args, 0, sizeof(struct arguments)); + argp_parse(&argp, argc, argv, 0, NULL, &args); + if (args.verbose) { + g_loglevel = DEBUG; + } + + g_logprefix = basename(args.upatch); + log_debug("PID: %d\n", args.pid); + log_debug("UUID: %s\n", args.uuid); + log_debug("Patch: %s\n", args.upatch); + log_debug("Binary: %s\n", args.binary); + + args.pid = args.pid & INT32_MAX; + switch (args.cmd) { + case PATCH: + ret = patch_upatch(args.uuid, args.binary, args.upatch, args.pid); + break; + case UNPATCH: + ret = unpatch_upatch(args.uuid, args.pid); + break; + case INFO: + ret = info_upatch(args.pid); + break; + default: + ERROR("Unknown command"); + ret = EINVAL; + break; + } + + return abs(ret); } diff --git a/upatch-manage/upatch-patch.c b/upatch-manage/upatch-patch.c index ab972ac1c08012c5fb9c962e3d184dc6672c162c..fd2c5f22ed732d4113bbab7c08aca2bb15bbc36e 100644 --- a/upatch-manage/upatch-patch.c +++ b/upatch-manage/upatch-patch.c @@ -33,9 +33,7 @@ #include "upatch-ptrace.h" #include "upatch-relocation.h" #include "upatch-resolve.h" - -#define GET_MICROSECONDS(a, b) \ - ((a.tv_sec - b.tv_sec) * 1000000 + (a.tv_usec - b.tv_usec)) +#include "upatch-stack-check.h" #ifndef ARCH_SHF_SMALL #define ARCH_SHF_SMALL 0 @@ -48,182 +46,169 @@ #define BITS_PER_LONG sizeof(unsigned long) * 8 static GElf_Off calculate_load_address(struct running_elf *relf, - bool check_code) + bool check_code) { - int i; - GElf_Off min_addr = -1; - - /* TODO: for ET_DYN, consider check PIE */ - if (relf->info.hdr->e_type != ET_EXEC && - relf->info.hdr->e_type != ET_DYN) { - log_error("invalid elf type, it should be ET_EXEC or ET_DYN\n"); - goto out; - } - - for (i = 0; i < relf->info.hdr->e_phnum; ++i) { - if (relf->phdrs[i].p_type != PT_LOAD) - continue; - if (!check_code || - (check_code && (relf->phdrs[i].p_flags & PF_X))) - min_addr = (min_addr > relf->phdrs[i].p_vaddr) ? - relf->phdrs[i].p_vaddr : - min_addr; - // min_addr = min(min_addr, relf->phdrs[i].p_vaddr); - } + GElf_Off min_addr = (unsigned long)-1; + + /* TODO: for ET_DYN, consider check PIE */ + if (relf->info.hdr->e_type != ET_EXEC && + relf->info.hdr->e_type != ET_DYN) { + log_error("invalid elf type, it should be ET_EXEC or ET_DYN\n"); + goto out; + } + + for (int i = 0; i < relf->info.hdr->e_phnum; ++i) { + if (relf->phdrs[i].p_type != PT_LOAD) { + continue; + } + if (!check_code || + (check_code && (relf->phdrs[i].p_flags & PF_X))) { + min_addr = (min_addr > relf->phdrs[i].p_vaddr) ? + relf->phdrs[i].p_vaddr : min_addr; + } + } out: - return min_addr; + return min_addr; } static unsigned long calculate_mem_load(struct object_file *obj) { - struct obj_vm_area *ovma; - unsigned long load_addr = -1; - - list_for_each_entry(ovma, &obj->vma, list) { - if (ovma->inmem.prot & PROT_EXEC) { - load_addr = (load_addr > ovma->inmem.start) ? - ovma->inmem.start : - load_addr; - } - } - - return load_addr; + struct obj_vm_area *ovma; + unsigned long load_addr = (unsigned long)-1; + + list_for_each_entry(ovma, &obj->vma, list) { + if (ovma->inmem.prot & PROT_EXEC) { + load_addr = (load_addr > ovma->inmem.start) ? + ovma->inmem.start : load_addr; + } + } + + return load_addr; } static int rewrite_section_headers(struct upatch_elf *uelf) { - unsigned int i; - - /* Handle SHF_ALLOC in this part */ - - /* This should always be true, but let's be sure. */ - uelf->info.shdrs[0].sh_addr = 0; - uelf->info.shdrs[0].sh_addralign = 0; - - for (i = 1; i < uelf->info.hdr->e_shnum; i++) { - GElf_Shdr *shdr = &uelf->info.shdrs[i]; - if (shdr->sh_type != SHT_NOBITS && - uelf->info.patch_size < shdr->sh_offset + shdr->sh_size) { - log_error("upatch len %lu truncated\n", - uelf->info.patch_size); - return -ENOEXEC; - } - - /* Mark all sections sh_addr with their address in the - temporary image. */ - shdr->sh_addr = (size_t)uelf->info.hdr + shdr->sh_offset; - log_debug("section %s at 0x%lx\n", - uelf->info.shstrtab + shdr->sh_name, shdr->sh_addr); - } - - return 0; -} + unsigned int i; + /* Handle SHF_ALLOC in this part */ + + /* This should always be true, but let's be sure. */ + uelf->info.shdrs[0].sh_addr = 0; + uelf->info.shdrs[0].sh_addralign = 0; + + for (i = 1; i < uelf->info.hdr->e_shnum; i++) { + GElf_Shdr *shdr = &uelf->info.shdrs[i]; + if (shdr->sh_type != SHT_NOBITS && + uelf->info.patch_size < shdr->sh_offset + shdr->sh_size) { + log_error("upatch len %lu truncated\n", uelf->info.patch_size); + return -ENOEXEC; + } + + /* Mark all sections sh_addr with their address in the + temporary image. */ + shdr->sh_addr = (size_t)uelf->info.hdr + shdr->sh_offset; + log_debug("section %s at 0x%lx\n", + uelf->info.shstrtab + shdr->sh_name, shdr->sh_addr); + } -/* Additional bytes needed by arch in front of individual sections */ -unsigned int arch_mod_section_prepend(struct upatch_elf *uelf, - unsigned int section) -{ - /* default implementation just returns zero */ - return 0; + return 0; } -static long get_offset(struct upatch_elf *uelf, unsigned int *size, - GElf_Shdr *sechdr, unsigned int section) +static unsigned long get_offset(unsigned long *size, GElf_Shdr *sechdr) { - long ret; + unsigned long ret; + + ret = ALIGN(*size, (unsigned long)(sechdr->sh_addralign ?: 1)); + *size = (unsigned long)ret + sechdr->sh_size; - *size += arch_mod_section_prepend(uelf, section); - ret = ALIGN(*size, sechdr->sh_addralign ?: 1); - *size = ret + sechdr->sh_size; - return ret; + return ret; } static void layout_upatch_info(struct upatch_elf *uelf) { - GElf_Shdr *upatch_func = uelf->info.shdrs + uelf->index.upatch_funcs; - int num = upatch_func->sh_size / sizeof(struct upatch_patch_func); - - uelf->core_layout.info_size = uelf->core_layout.size; - uelf->core_layout.size += sizeof(struct upatch_info) + - num * sizeof(struct upatch_info_func); - uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + GElf_Shdr *upatch_func = uelf->info.shdrs + uelf->index.upatch_funcs; + unsigned long num = upatch_func->sh_size / sizeof(struct upatch_patch_func); + GElf_Shdr *upatch_string = uelf->info.shdrs + uelf->index.upatch_string; + + uelf->core_layout.info_size = uelf->core_layout.size; + uelf->core_layout.size += sizeof(struct upatch_info) + + num * sizeof(struct upatch_info_func) + upatch_string->sh_size; + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); } static void layout_jmptable(struct upatch_elf *uelf) { - uelf->jmp_cur_entry = 0; - uelf->jmp_max_entry = JMP_TABLE_MAX_ENTRY; - uelf->jmp_offs = ALIGN(uelf->core_layout.size, sizeof(unsigned long)); - uelf->core_layout.size = - uelf->jmp_offs + uelf->jmp_max_entry * get_jmp_table_entry(); - uelf->core_layout.text_size = uelf->core_layout.size; + uelf->jmp_cur_entry = 0; + uelf->jmp_max_entry = JMP_TABLE_MAX_ENTRY; + uelf->jmp_offs = ALIGN(uelf->core_layout.size, sizeof(unsigned long)); + uelf->core_layout.size = uelf->jmp_offs + + uelf->jmp_max_entry * get_jmp_table_entry(); + uelf->core_layout.text_size = uelf->core_layout.size; } static void layout_sections(struct upatch_elf *uelf) { - static unsigned long const masks[][2] = { - /* NOTE: all executable code must be the last section - * in this array; otherwise modify the text_size - * finder in the two loops below */ - { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL }, - { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL }, - { SHF_RO_AFTER_INIT | SHF_ALLOC, ARCH_SHF_SMALL }, - { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL }, - { ARCH_SHF_SMALL | SHF_ALLOC, 0 } - }; - unsigned int m, i; - - for (i = 0; i < uelf->info.hdr->e_shnum; i++) - uelf->info.shdrs[i].sh_entsize = ~0UL; - - log_debug("upatch section allocation order:\n"); - for (m = 0; m < ARRAY_SIZE(masks); ++m) { - for (i = 0; i < uelf->info.hdr->e_shnum; ++i) { - GElf_Shdr *s = &uelf->info.shdrs[i]; - const char *sname = uelf->info.shstrtab + s->sh_name; - - if ((s->sh_flags & masks[m][0]) != masks[m][0] || - (s->sh_flags & masks[m][1]) || - s->sh_entsize != ~0UL) - continue; - - s->sh_entsize = - get_offset(uelf, &uelf->core_layout.size, s, i); - log_debug("\tm = %d; %s: sh_entsize: 0x%lx\n", m, sname, - s->sh_entsize); - } - switch (m) { - case 0: /* executable */ - uelf->core_layout.size = - PAGE_ALIGN(uelf->core_layout.size); - uelf->core_layout.text_size = uelf->core_layout.size; - break; - case 1: /* RO: text and ro-data */ - uelf->core_layout.size = - PAGE_ALIGN(uelf->core_layout.size); - uelf->core_layout.ro_size = uelf->core_layout.size; - break; - case 2: /* RO after init */ - uelf->core_layout.size = - PAGE_ALIGN(uelf->core_layout.size); - uelf->core_layout.ro_after_init_size = - uelf->core_layout.size; - break; - case 3: /* whole core */ - uelf->core_layout.size = - PAGE_ALIGN(uelf->core_layout.size); - break; - } - } + static unsigned long const masks[][2] = { + /* NOTE: all executable code must be the last section + * in this array; otherwise modify the text_size + * finder in the two loops below */ + { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL }, + { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL }, + { SHF_RO_AFTER_INIT | SHF_ALLOC, ARCH_SHF_SMALL }, + { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL }, + { ARCH_SHF_SMALL | SHF_ALLOC, 0 } + }; + unsigned int m; + unsigned int i; + + for (i = 0; i < uelf->info.hdr->e_shnum; i++) { + uelf->info.shdrs[i].sh_entsize = ~0UL; + } + + log_debug("upatch section allocation order:\n"); + for (m = 0; m < ARRAY_SIZE(masks); ++m) { + for (i = 0; i < uelf->info.hdr->e_shnum; ++i) { + GElf_Shdr *s = &uelf->info.shdrs[i]; + const char *sname = uelf->info.shstrtab + s->sh_name; + + if ((s->sh_flags & masks[m][0]) != masks[m][0] || + (s->sh_flags & masks[m][1]) || s->sh_entsize != ~0UL) { + continue; + } + + s->sh_entsize = get_offset(&uelf->core_layout.size, s); + log_debug("\tm = %d; %s: sh_entsize: 0x%lx\n", m, sname, + s->sh_entsize); + } + switch (m) { + case 0: /* executable */ + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + uelf->core_layout.text_size = uelf->core_layout.size; + break; + case 1: /* RO: text and ro-data */ + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + uelf->core_layout.ro_size = uelf->core_layout.size; + break; + case 2: /* RO after init */ + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + uelf->core_layout.ro_after_init_size = + uelf->core_layout.size; + break; + case 3: /* whole core */ + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + break; + default: + break; + } + } } /* TODO: only included used symbol */ -static bool is_upatch_symbol(const GElf_Sym *src, const GElf_Shdr *sechdrs, - unsigned int shnum) +static bool is_upatch_symbol(void) { - return true; + return true; } + /* * We only allocate and copy the strings needed by the parts of symtab * we keep. This is simple, but has the effect of making multiple @@ -233,743 +218,822 @@ static bool is_upatch_symbol(const GElf_Sym *src, const GElf_Shdr *sechdrs, */ static void layout_symtab(struct upatch_elf *uelf) { - GElf_Shdr *symsect = uelf->info.shdrs + uelf->index.sym; - GElf_Shdr *strsect = uelf->info.shdrs + uelf->index.str; - /* TODO: only support same arch as kernel now */ - const GElf_Sym *src; - unsigned int i, nsrc, ndst, strtab_size = 0; - - /* Put symbol section at end of init part of module. */ - symsect->sh_flags |= SHF_ALLOC; - symsect->sh_entsize = get_offset(uelf, &uelf->core_layout.size, symsect, - uelf->index.sym); - log_debug("\t%s\n", uelf->info.shstrtab + symsect->sh_name); - - src = (void *)uelf->info.hdr + symsect->sh_offset; - nsrc = symsect->sh_size / sizeof(*src); - - /* Compute total space required for the symbols' strtab. */ - for (ndst = i = 0; i < nsrc; i++) { - if (i == 0 || is_upatch_symbol(src + i, uelf->info.shdrs, - uelf->info.hdr->e_shnum)) { - strtab_size += - strlen(&uelf->strtab[src[i].st_name]) + 1; - ndst++; - } - } - - /* Append room for core symbols at end of core part. */ - uelf->symoffs = - ALIGN(uelf->core_layout.size, symsect->sh_addralign ?: 1); - uelf->stroffs = uelf->core_layout.size = - uelf->symoffs + ndst * sizeof(GElf_Sym); - uelf->core_layout.size += strtab_size; - uelf->core_typeoffs = uelf->core_layout.size; - uelf->core_layout.size += ndst * sizeof(char); - uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); - - /* Put string table section at end of init part of module. */ - strsect->sh_flags |= SHF_ALLOC; - strsect->sh_entsize = get_offset(uelf, &uelf->core_layout.size, strsect, - uelf->index.str); - uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); - log_debug("\t%s\n", uelf->info.shstrtab + strsect->sh_name); + GElf_Shdr *symsect = uelf->info.shdrs + uelf->index.sym; + GElf_Shdr *strsect = uelf->info.shdrs + uelf->index.str; + /* TODO: only support same arch as kernel now */ + const GElf_Sym *src; + unsigned long i; + unsigned long nsrc; + unsigned long ndst; + unsigned long strtab_size = 0; + + /* Put symbol section at end of init part of module. */ + symsect->sh_flags |= SHF_ALLOC; + symsect->sh_entsize = get_offset(&uelf->core_layout.size, symsect); + log_debug("\t%s\n", uelf->info.shstrtab + symsect->sh_name); + + src = (void *)uelf->info.hdr + symsect->sh_offset; + nsrc = symsect->sh_size / sizeof(*src); + + /* Compute total space required for the symbols' strtab. */ + for (ndst = i = 0; i < nsrc; i++) { + if (i == 0 || is_upatch_symbol()) { + strtab_size += strlen(&uelf->strtab[src[i].st_name]) + 1; + ndst++; + } + } + + /* Append room for core symbols at end of core part. */ + uelf->symoffs = ALIGN(uelf->core_layout.size, symsect->sh_addralign ?: 1); + uelf->stroffs = uelf->core_layout.size = + uelf->symoffs + ndst * sizeof(GElf_Sym); + uelf->core_layout.size += strtab_size; + uelf->core_typeoffs = uelf->core_layout.size; + uelf->core_layout.size += ndst * sizeof(char); + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + + /* Put string table section at end of init part of module. */ + strsect->sh_flags |= SHF_ALLOC; + strsect->sh_entsize = get_offset(&uelf->core_layout.size, strsect); + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + log_debug("\t%s\n", uelf->info.shstrtab + strsect->sh_name); } -static void *upatch_alloc(struct object_file *obj, size_t sz) +static void *upatch_alloc(struct object_file *obj, size_t len) { - int ret; - unsigned long addr; - struct vm_hole *hole = NULL; - - addr = object_find_patch_region_nolimit(obj, sz, &hole); - if (!addr) - return NULL; - - addr = upatch_mmap_remote(proc2pctx(obj->proc), addr, sz, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, - 0); - if (addr == 0) { - return NULL; - } - - log_debug("Allocated 0x%lx bytes at 0x%lx of '%s'\n", sz, addr, obj->name); - - // log_debug("Marking this space as busy\n"); - ret = vm_hole_split(hole, addr, addr + sz); - if (ret) { - // TODO: clear - log_error("Failed to split vm hole\n"); - return NULL; - } - - return (void *)addr; + struct upatch_ptrace_ctx *pctx = proc2pctx(obj->proc); + if (pctx == NULL) { + log_error("Failed to find process context\n"); + return NULL; + } + + log_debug("Finding patch region for '%s', len=0x%lx\n", obj->name, len); + struct vm_hole *hole = find_patch_region(obj, len); + if (hole == NULL) { + log_error("Failed to find patch region for '%s'\n", obj->name); + return NULL; + } + + uintptr_t addr = PAGE_ALIGN(hole->start); + log_debug("Found patch region at 0x%lx, size=0x%lx\n", addr, len); + + addr = upatch_mmap_remote(pctx, addr, len, + PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, + (unsigned long)-1, 0); + if (addr == 0) { + log_error("Failed to map patch region, ret=%d\n", errno); + return NULL; + } + + int ret = vm_hole_split(hole, addr, (addr + len)); + if (ret != 0) { + log_error("Failed to split vm hole, ret=%d\n", ret); + return NULL; + } + + return (void *)addr; } -static void upatch_free(struct object_file *obj, void *base, - unsigned int size) +static void upatch_free(struct object_file *obj, void *base, unsigned long size) { - log_debug("Free patch memory %p\n", base); - if (upatch_munmap_remote(proc2pctx(obj->proc), (unsigned long)base, size)) { - log_error("Failed to free patch memory %p\n", base); - } + log_debug("Free patch memory %p\n", base); + if (upatch_munmap_remote(proc2pctx(obj->proc), (unsigned long)base, size)) { + log_error("Failed to free patch memory %p\n", base); + } } -static int __alloc_memory(struct object_file *obj_file, - struct upatch_layout *layout) +static int alloc_memory(struct upatch_elf *uelf, struct object_file *obj) { - /* Do the allocs. */ - layout->base = upatch_alloc(obj_file, layout->size); - if (!layout->base) { - return -errno; - } + struct upatch_layout *layout = &uelf->core_layout; - layout->kbase = malloc(layout->size); - if (!layout->kbase) { - upatch_free(obj_file, layout->base, layout->size); - return -errno; - } + layout->base = upatch_alloc(obj, layout->size); + if (layout->base == NULL) { + log_error("Failed to alloc patch memory\n"); + return ENOMEM; + } - memset(layout->kbase, 0, layout->size); + layout->kbase = calloc(1, layout->size); + if (!layout->kbase) { + log_error("Failed to alloc memory\n"); + upatch_free(obj, layout->base, layout->size); + return ENOMEM; + } + + /* Transfer each section which specifies SHF_ALLOC */ + log_debug("Final section addresses:\n"); + for (int i = 0; i < uelf->info.hdr->e_shnum; i++) { + GElf_Shdr *shdr = &uelf->info.shdrs[i]; + + if (!(shdr->sh_flags & SHF_ALLOC)) { + continue; + } + + void *dest = layout->base + shdr->sh_entsize; + void *kdest = layout->kbase + shdr->sh_entsize; + if (shdr->sh_type != SHT_NOBITS) { + memcpy(kdest, (void *)shdr->sh_addr, shdr->sh_size); + } + + shdr->sh_addr = (uintptr_t)kdest; + /* overuse this attr to record user address */ + shdr->sh_addralign = (uintptr_t)dest; + log_debug("\t0x%lx %s <- 0x%lx\n", (uintptr_t)kdest, + uelf->info.shstrtab + shdr->sh_name, (uintptr_t)dest); + } - return 0; + return 0; } -static int alloc_memory(struct upatch_elf *uelf, struct object_file *obj) +static int post_memory(struct upatch_elf *uelf, struct object_file *obj) { - int i, ret; - - /* Do the allocs. */ - ret = __alloc_memory(obj, &uelf->core_layout); - if (ret) { - return ret; - } - - /* Transfer each section which specifies SHF_ALLOC */ - log_debug("Final section addresses:\n"); - for (i = 0; i < uelf->info.hdr->e_shnum; i++) { - void *kdest; - void *dest; - GElf_Shdr *shdr = &uelf->info.shdrs[i]; - - if (!(shdr->sh_flags & SHF_ALLOC)) { - continue; - } - - kdest = uelf->core_layout.kbase + shdr->sh_entsize; - dest = uelf->core_layout.base + shdr->sh_entsize; - - if (shdr->sh_type != SHT_NOBITS) { - memcpy(kdest, (void *)shdr->sh_addr, shdr->sh_size); - } - - shdr->sh_addr = (unsigned long)kdest; - /* overuse this attr to record user address */ - shdr->sh_addralign = (unsigned long)dest; - log_debug("\t0x%lx %s <- 0x%lx\n", (long)kdest, - uelf->info.shstrtab + shdr->sh_name, (long)dest); - } - - return 0; + log_debug("Post memory 0x%lx to 0x%lx, len=0x%lx\n", + (uintptr_t)uelf->core_layout.kbase, (uintptr_t)uelf->core_layout.base, + uelf->core_layout.size); + + int ret = upatch_process_mem_write(obj->proc, + uelf->core_layout.kbase, (unsigned long)uelf->core_layout.base, + uelf->core_layout.size); + if (ret) { + log_error("Failed to write process memory, ret=%d\n", ret); + } + + return ret; } -static int post_memory(struct upatch_elf *uelf, struct object_file *obj) +static int upatch_info_alloc(struct upatch_elf *uelf, struct upatch_info *uinfo) { - int ret = 0; - - log_debug("Post kbase %lx(%x) to base %lx\n", - (unsigned long)uelf->core_layout.kbase, - uelf->core_layout.size, - (unsigned long)uelf->core_layout.base); - ret = upatch_process_mem_write(obj->proc, uelf->core_layout.kbase, - (unsigned long)uelf->core_layout.base, - uelf->core_layout.size); - if (ret) { - log_error("Failed to move kbase to base, ret=%d\n", ret); - goto out; - } + GElf_Shdr *upatch_funcs = &uelf->info.shdrs[uelf->index.upatch_funcs]; + size_t num = upatch_funcs->sh_size / sizeof(struct upatch_patch_func); -out: - return ret; + uinfo->funcs = (void *)malloc(num * sizeof(*uinfo->funcs)); + if (uinfo->funcs == NULL) { + log_error("Failed to malloc uinfo->funcs\n"); + return -ENOMEM; + } + return 0; } -static int complete_info(struct upatch_elf *uelf, struct object_file *obj, const char *uuid) +static void upatch_info_init(struct upatch_elf *uelf, struct upatch_info *uinfo) { - int ret = 0, i; - struct upatch_info *uinfo = - (void *)uelf->core_layout.kbase + uelf->core_layout.info_size; - struct upatch_patch_func *upatch_funcs_addr = - (void *)uelf->info.shdrs[uelf->index.upatch_funcs].sh_addr; - - // TODO: uinfo->id - memcpy(uinfo, UPATCH_HEADER, strlen(UPATCH_HEADER)); - uinfo->size = uelf->core_layout.size - uelf->core_layout.info_size; - uinfo->start = (unsigned long)uelf->core_layout.base; - uinfo->end = - (unsigned long)uelf->core_layout.base + uelf->core_layout.size; - uinfo->changed_func_num = - uelf->info.shdrs[uelf->index.upatch_funcs].sh_size / - sizeof(struct upatch_patch_func); - memcpy(uinfo->id, uuid, strlen(uuid)); - - log_normal("Changed insn:\n"); - for (i = 0; i < uinfo->changed_func_num; ++i) { - struct upatch_info_func *upatch_func = - (void *)uelf->core_layout.kbase + - uelf->core_layout.info_size + - sizeof(struct upatch_info) + - i * sizeof(struct upatch_info_func); - - upatch_func->old_addr = - upatch_funcs_addr[i].old_addr + uelf->relf->load_bias; - upatch_func->new_addr = upatch_funcs_addr[i].new_addr; - ret = upatch_process_mem_read(obj->proc, upatch_func->old_addr, - &upatch_func->old_insn, - get_origin_insn_len()); - if (ret) { - log_error("can't read origin insn at 0x%lx - %d\n", - upatch_func->old_addr, ret); - goto out; - } - - upatch_func->new_insn = get_new_insn(obj, upatch_func->old_addr, - upatch_func->new_addr); - - log_normal("\t0x%lx(0x%lx -> 0x%lx)\n", upatch_func->old_addr, - upatch_func->old_insn[0], upatch_func->new_insn); - } + GElf_Shdr *ufuncs = &uelf->info.shdrs[uelf->index.upatch_funcs]; + GElf_Shdr *ustring = &uelf->info.shdrs[uelf->index.upatch_string]; + struct upatch_patch_func *funcs = (void *)uelf->info.hdr + + ufuncs->sh_offset; + char *names = (void *)uelf->info.hdr + ustring->sh_offset; + + uinfo->changed_func_num = ufuncs->sh_size / + sizeof(struct upatch_patch_func); + uinfo->func_names_size = ustring->sh_size; + uinfo->func_names = names; + + for (unsigned long i = 0; i < uinfo->changed_func_num; i++) { + uinfo->funcs[i].addr = funcs[i].addr; + uinfo->funcs[i].addr.old_addr += uelf->relf->load_bias; + uinfo->funcs[i].name = names; + names += strlen(names) + 1; + } +} + +static int upatch_active_stack_check(struct upatch_elf *uelf, + struct upatch_process *proc) +{ + struct upatch_info uinfo; + + int ret = upatch_info_alloc(uelf, &uinfo); + if (ret < 0) { + return ret; + } + + upatch_info_init(uelf, &uinfo); + ret = upatch_stack_check(&uinfo, proc, ACTIVE); + + free(uinfo.funcs); + return ret; +} + +static struct object_file *upatch_find_obj(struct upatch_elf *uelf, + struct upatch_process *proc) +{ + struct object_file *obj = NULL; + GElf_Off min_addr; + + list_for_each_entry(obj, &proc->objs, list) { + if (obj->inode == uelf->relf->info.inode) { + min_addr = calculate_load_address(uelf->relf, true); + uelf->relf->load_start = calculate_mem_load(obj); + uelf->relf->load_bias = uelf->relf->load_start - min_addr; + + log_debug("load_bias = %lx\n", uelf->relf->load_bias); + return obj; + } + } + + log_error("Cannot find inode %lu in pid %d, file is not loaded\n", + uelf->relf->info.inode, proc->pid); + return NULL; +} +static int complete_info(struct upatch_elf *uelf, struct object_file *obj, + const char *uuid) +{ + int ret = 0; + + struct upatch_info *uinfo = (void *)uelf->core_layout.kbase + + uelf->core_layout.info_size; + struct upatch_patch_func *upatch_funcs_addr = + (void *)uelf->info.shdrs[uelf->index.upatch_funcs].sh_addr; + GElf_Shdr *upatch_string = &uelf->info.shdrs[uelf->index.upatch_string]; + + memcpy(uinfo->magic, UPATCH_HEADER, strlen(UPATCH_HEADER)); + memcpy(uinfo->id, uuid, strlen(uuid)); + + uinfo->size = uelf->core_layout.size - uelf->core_layout.info_size; + uinfo->start = (unsigned long)uelf->core_layout.base; + uinfo->end = (unsigned long)uelf->core_layout.base + + uelf->core_layout.size; + uinfo->changed_func_num = + uelf->info.shdrs[uelf->index.upatch_funcs].sh_size / + sizeof(struct upatch_patch_func); + + uinfo->func_names = (void *)uinfo + sizeof(*uinfo); + uinfo->func_names_size = upatch_string->sh_size; + uinfo->funcs = (void *)uinfo->func_names + uinfo->func_names_size; + + memcpy(uinfo->func_names, (void *)upatch_string->sh_addr, + upatch_string->sh_size); + + unsigned long offset = 0; + for (unsigned long i = 0; i < uinfo->changed_func_num; ++i) { + char *name = (char *)uinfo->func_names + offset; + + uinfo->funcs[i].name = name; + offset += strlen(name) + 1; + } + + log_debug("Changed function:\n"); + for (unsigned int i = 0; i < uinfo->changed_func_num; ++i) { + struct upatch_info_func *upatch_func = &uinfo->funcs[i]; + + upatch_func->addr = upatch_funcs_addr[i].addr; + upatch_func->addr.old_addr += uelf->relf->load_bias; + +#ifdef __riscv +#define RISCV_MAX_JUMP_RANGE (1L << 31) + /* + * On RISC-V, to jump to arbitrary address, there must be + * at least 12 bytes to hold 3 instructors. Struct upatch_info + * new_insn field is only 8 bytes. We can only jump into + * +-2G ranges. Here do the check. + */ + long riscv_offset = (long)(upatch_func->addr.new_addr - upatch_func->addr.old_addr); + if (riscv_offset >= RISCV_MAX_JUMP_RANGE || riscv_offset < -RISCV_MAX_JUMP_RANGE) { + log_error("new_addr=%lx old_addr=%lx exceed +-2G range\n", + upatch_func->addr.new_addr, upatch_func->addr.old_addr); + goto out; + } +#endif + + ret = upatch_process_mem_read(obj->proc, upatch_func->addr.old_addr, + &upatch_func->old_insn, get_origin_insn_len()); + if (ret) { + log_error("can't read origin insn at 0x%lx - %d\n", + upatch_func->addr.old_addr, ret); + goto out; + } + +#ifdef __riscv + upatch_func->new_insn = get_new_insn( + upatch_func->addr.old_addr, upatch_func->addr.new_addr); +#else + upatch_func->new_insn = get_new_insn(); +#endif + log_debug("\taddr: 0x%lx -> 0x%lx, insn: 0x%lx -> 0x%lx, name: '%s'\n", + upatch_func->addr.old_addr, upatch_func->addr.new_addr, + upatch_func->old_insn[0], upatch_func->new_insn, + upatch_func->name); + } out: - return ret; + return ret; } static int unapply_patch(struct object_file *obj, - struct upatch_info_func *funcs, - unsigned int changed_func_num) + struct upatch_info_func *funcs, unsigned long changed_func_num) { - log_normal("Changed insn:\n"); - for (int i = 0; i < changed_func_num; ++i) { - log_normal("\t0x%lx(0x%lx -> 0x%lx)\n", funcs[i].old_addr, - funcs[i].new_insn, funcs[i].old_insn[0]); - - int ret = upatch_process_mem_write(obj->proc, &funcs[i].old_insn, - (unsigned long)funcs[i].old_addr, get_origin_insn_len()); - - if (ret) { - log_error("Failed to write old insn at 0x%lx, ret=%d\n", - funcs[i].old_addr, ret); - return ret; - } - } - return 0; + log_debug("Changed function:\n"); + for (unsigned int i = 0; i < changed_func_num; ++i) { + struct upatch_info_func *upatch_func = &funcs[i]; + + log_debug("\taddr: 0x%lx -> 0x%lx, insn: 0x%lx -> 0x%lx, name: '%s'\n", + upatch_func->addr.new_addr, upatch_func->addr.old_addr, + upatch_func->new_insn, upatch_func->old_insn[0], + upatch_func->name); + int ret = upatch_process_mem_write(obj->proc, &funcs[i].old_insn, + (unsigned long)funcs[i].addr.old_addr, get_origin_insn_len()); + if (ret) { + log_error("Failed to write old insn at 0x%lx, ret=%d\n", + funcs[i].addr.old_addr, ret); + return ret; + } + } + return 0; } static int apply_patch(struct upatch_elf *uelf, struct object_file *obj) { - int ret = 0, i; - struct upatch_info *uinfo = - (void *)uelf->core_layout.kbase + uelf->core_layout.info_size; - - for (i = 0; i < uinfo->changed_func_num; ++i) { - struct upatch_info_func *upatch_func = - (void *)uelf->core_layout.kbase + - uelf->core_layout.info_size + - sizeof(struct upatch_info) + - i * sizeof(struct upatch_info_func); - - // write jumper insn to first 8 bytes - ret = upatch_process_mem_write(obj->proc, &upatch_func->new_insn, - (unsigned long)upatch_func->old_addr, get_upatch_insn_len()); - if (ret) { - log_error( - "Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", - upatch_func->old_addr, upatch_func->new_insn, - ret); - goto out; - } - // write 64bit new addr to second 8 bytes - ret = upatch_process_mem_write(obj->proc, &upatch_func->new_addr, - (unsigned long)upatch_func->old_addr + get_upatch_insn_len(), - get_upatch_addr_len()); - if (ret) { - log_error( - "Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", - upatch_func->old_addr + get_upatch_insn_len(), - upatch_func->new_addr, - ret); - goto out; - } - } + int ret = 0; + unsigned int i; + + struct upatch_info *uinfo = (void *)uelf->core_layout.kbase + + uelf->core_layout.info_size; + for (i = 0; i < uinfo->changed_func_num; ++i) { + struct upatch_info_func *upatch_func = &uinfo->funcs[i]; + + // write jumper insn to first 8 bytes + ret = upatch_process_mem_write(obj->proc, &upatch_func->new_insn, + (unsigned long)upatch_func->addr.old_addr, get_upatch_insn_len()); + if (ret) { + log_error( + "Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", + upatch_func->addr.old_addr, upatch_func->new_insn, + ret); + goto out; + } + // write 64bit new addr to second 8 bytes + ret = upatch_process_mem_write(obj->proc, &upatch_func->addr.new_addr, + (unsigned long)upatch_func->addr.old_addr + get_upatch_insn_len(), + get_upatch_addr_len()); + if (ret) { + log_error( + "Failed to ptrace upatch func at 0x%lx(0x%lx) - %d\n", + upatch_func->addr.old_addr + get_upatch_insn_len(), + upatch_func->addr.new_addr, ret); + goto out; + } + } out: - if (ret) { - unapply_patch(obj, - (void *)uelf->core_layout.kbase + - uelf->core_layout.info_size + - sizeof(struct upatch_info), - i); - } - return ret; + if (ret) { + unapply_patch(obj, uinfo->funcs, uinfo->changed_func_num); + } + return ret; } static int upatch_mprotect(struct upatch_elf *uelf, struct object_file *obj) { - int ret; - - if (uelf->core_layout.text_size > 0) { - ret = upatch_mprotect_remote( - proc2pctx(obj->proc), - (unsigned long)uelf->core_layout.base, - uelf->core_layout.text_size, PROT_READ | PROT_EXEC); - if (ret < 0) { - log_error("Failed to change upatch text protection to r-x"); - return ret; - } - } - - if (uelf->core_layout.ro_size > uelf->core_layout.text_size) { - ret = upatch_mprotect_remote( - proc2pctx(obj->proc), - (unsigned long)uelf->core_layout.base + uelf->core_layout.text_size, - uelf->core_layout.ro_size - uelf->core_layout.text_size, - PROT_READ); - if (ret < 0) { - log_error("Failed to change upatch ro protection to r--"); - return ret; - } - } - - if (uelf->core_layout.ro_after_init_size > uelf->core_layout.ro_size) { - ret = upatch_mprotect_remote( - proc2pctx(obj->proc), - (unsigned long)uelf->core_layout.base + uelf->core_layout.ro_size, - uelf->core_layout.ro_after_init_size - uelf->core_layout.ro_size, - PROT_READ); - if (ret < 0) { - log_error("Failed to change upatch ro init protection to r--"); - return ret; - } - } - - if (uelf->core_layout.info_size > - uelf->core_layout.ro_after_init_size) { - ret = upatch_mprotect_remote( - proc2pctx(obj->proc), - (unsigned long)uelf->core_layout.base + uelf->core_layout.ro_after_init_size, - uelf->core_layout.info_size - uelf->core_layout.ro_after_init_size, - PROT_READ | PROT_WRITE); - if (ret < 0) { - log_error("Failed to change upatch rw protection to rw-"); - return ret; - } - } - - if (uelf->core_layout.size > uelf->core_layout.info_size) { - ret = upatch_mprotect_remote( - proc2pctx(obj->proc), - (unsigned long)uelf->core_layout.base + uelf->core_layout.info_size, - uelf->core_layout.size - uelf->core_layout.info_size, - PROT_READ); - if (ret < 0) { - log_error("Failed to change upatch info protection to r--"); - return ret; - } - } - - return 0; + int ret; + + if (uelf->core_layout.text_size > 0) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base, + uelf->core_layout.text_size, PROT_READ | PROT_EXEC); + if (ret < 0) { + log_error("Failed to change upatch text protection to r-x"); + return ret; + } + } + + if (uelf->core_layout.ro_size > uelf->core_layout.text_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + uelf->core_layout.text_size, + uelf->core_layout.ro_size - uelf->core_layout.text_size, + PROT_READ); + if (ret < 0) { + log_error("Failed to change upatch ro protection to r--"); + return ret; + } + } + + if (uelf->core_layout.ro_after_init_size > uelf->core_layout.ro_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + uelf->core_layout.ro_size, + uelf->core_layout.ro_after_init_size - uelf->core_layout.ro_size, + PROT_READ); + if (ret < 0) { + log_error("Failed to change upatch ro init protection to r--"); + return ret; + } + } + + if (uelf->core_layout.info_size > + uelf->core_layout.ro_after_init_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + uelf->core_layout.ro_after_init_size, + uelf->core_layout.info_size - uelf->core_layout.ro_after_init_size, + PROT_READ | PROT_WRITE); + if (ret < 0) { + log_error("Failed to change upatch rw protection to rw-"); + return ret; + } + } + + if (uelf->core_layout.size > uelf->core_layout.info_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + uelf->core_layout.info_size, + uelf->core_layout.size - uelf->core_layout.info_size, + PROT_READ); + if (ret < 0) { + log_error("Failed to change upatch info protection to r--"); + return ret; + } + } + + return 0; } -static int upatch_apply_patches(struct upatch_process *proc, - struct upatch_elf *uelf, const char *uuid) +static int upatch_apply_patches(struct object_file *obj, + struct upatch_elf *uelf, const char *uuid) { - int ret = 0; - struct object_file *obj = NULL; - GElf_Off min_addr; - bool found = false; - - list_for_each_entry(obj, &proc->objs, list) { - if (obj->inode == uelf->relf->info.inode) { - found = true; - break; - } - } - - if (!found) { - ret = -1; - log_debug("Cannot find inode %lu in pid %d, file is not loaded\n", - uelf->relf->info.inode, proc->pid); - goto out; - } - - min_addr = calculate_load_address(uelf->relf, true); - uelf->relf->load_start = calculate_mem_load(obj); - uelf->relf->load_bias = uelf->relf->load_start - min_addr; - log_debug("load_bias = %lx\n", uelf->relf->load_bias); - - ret = rewrite_section_headers(uelf); - if (ret) - goto free; - - // Caculate upatch mem size - layout_jmptable(uelf); - layout_sections(uelf); - layout_symtab(uelf); - layout_upatch_info(uelf); - - log_debug("calculate core layout = %x\n", uelf->core_layout.size); - log_debug( - "Core layout: text_size = %x, ro_size = %x, ro_after_init_size = " - "%x, info = %x, size = %x\n", - uelf->core_layout.text_size, uelf->core_layout.ro_size, - uelf->core_layout.ro_after_init_size, - uelf->core_layout.info_size, uelf->core_layout.size); - - /* - * Map patch as close to the original code as possible. - * Otherwise we can't use 32-bit jumps. - */ - ret = alloc_memory(uelf, obj); - if (ret) { - log_error("Failed to alloc patch memory\n"); - goto free; - } - - ret = upatch_mprotect(uelf, obj); - if (ret) { - log_error("Failed to set patch memory permission\n"); - goto free; - } - - /* Fix up syms, so that st_value is a pointer to location. */ - ret = simplify_symbols(uelf, obj); - if (ret) { - goto free; - } - - /* upatch new address will be updated */ - ret = apply_relocations(uelf); - if (ret) { - goto free; - } - - /* upatch upatch info */ - ret = complete_info(uelf, obj, uuid); - if (ret) { - goto free; - } - - ret = post_memory(uelf, obj); - if (ret) { - goto free; - } - - ret = apply_patch(uelf, obj); - if (ret) { - goto free; - } - - ret = 0; - goto out; + int ret = 0; + + ret = rewrite_section_headers(uelf); + if (ret) { + return ret; + } + + // Caculate upatch mem size + layout_jmptable(uelf); + layout_sections(uelf); + layout_symtab(uelf); + layout_upatch_info(uelf); + + log_debug("calculate core layout = %lx\n", uelf->core_layout.size); + log_debug( + "Core layout: text_size = %lx, ro_size = %lx, ro_after_init_size = " + "%lx, info = %lx, size = %lx\n", + uelf->core_layout.text_size, uelf->core_layout.ro_size, + uelf->core_layout.ro_after_init_size, + uelf->core_layout.info_size, uelf->core_layout.size); + + /* + * Map patch as close to the original code as possible. + * Otherwise we can't use 32-bit jumps. + */ + ret = alloc_memory(uelf, obj); + if (ret) { + goto free; + } + + ret = upatch_mprotect(uelf, obj); + if (ret) { + goto free; + } + + /* Fix up syms, so that st_value is a pointer to location. */ + ret = simplify_symbols(uelf, obj); + if (ret) { + goto free; + } + + /* upatch new address will be updated */ + ret = apply_relocations(uelf); + if (ret) { + goto free; + } + + /* upatch upatch info */ + ret = complete_info(uelf, obj, uuid); + if (ret) { + goto free; + } + + ret = post_memory(uelf, obj); + if (ret) { + goto free; + } + + ret = apply_patch(uelf, obj); + if (ret) { + goto free; + } + + ret = 0; + goto out; // TODO: clear free: - upatch_free(obj, uelf->core_layout.base, uelf->core_layout.size); + upatch_free(obj, uelf->core_layout.base, uelf->core_layout.size); out: - return ret; + return ret; } -static void upatch_time_tick(int pid) { - static struct timeval start_tv; - static struct timeval end_tv; - - if ((end_tv.tv_sec != 0) || (end_tv.tv_usec != 0)) { - memset(&start_tv, 0, sizeof(struct timeval)); - memset(&end_tv, 0, sizeof(struct timeval)); - } - - if ((start_tv.tv_sec == 0) && (start_tv.tv_usec == 0)) { - gettimeofday(&start_tv, NULL); - } else { - gettimeofday(&end_tv, NULL); - } - - if ((start_tv.tv_sec == 0) || (start_tv.tv_usec == 0) || - (end_tv.tv_sec == 0) || (end_tv.tv_usec == 0)) { - return; - } - - unsigned long frozen_time = GET_MICROSECONDS(end_tv, start_tv); - log_normal("Process %d frozen time is %ld microsecond(s)\n", - pid, frozen_time); +static void upatch_time_tick(int pid) +{ + static struct timeval start_tv; + static struct timeval end_tv; + + if ((end_tv.tv_sec != 0) || (end_tv.tv_usec != 0)) { + memset(&start_tv, 0, sizeof(struct timeval)); + memset(&end_tv, 0, sizeof(struct timeval)); + } + + if ((start_tv.tv_sec == 0) && (start_tv.tv_usec == 0)) { + gettimeofday(&start_tv, NULL); + } else { + gettimeofday(&end_tv, NULL); + } + + if ((start_tv.tv_sec == 0) || (start_tv.tv_usec == 0) || + (end_tv.tv_sec == 0) || (end_tv.tv_usec == 0)) { + return; + } + + long frozen_time = get_microseconds(&start_tv, &end_tv); + log_debug("Process %d frozen time is %ld microsecond(s)\n", + pid, frozen_time); } -int upatch_process_uuid_exist(struct upatch_process *proc, const char *uuid) +static struct object_patch *upatch_find_patch(struct upatch_process *proc, + const char *uuid) { - struct object_file *obj; - struct object_patch *patch; - list_for_each_entry(obj, &proc->objs, list) { - if (!obj->is_patch) { - continue; - } - list_for_each_entry(patch, &obj->applied_patch, list) { - if (strncmp(patch->uinfo->id, uuid, UPATCH_ID_LEN) == 0) { - return -EEXIST; - } - } - } - return 0; + struct object_file *obj = NULL; + struct object_patch *patch = NULL; + + // Traverse all mapped memory and find all upatch memory + list_for_each_entry(obj, &proc->objs, list) { + if (!obj->is_patch) { + continue; + } + list_for_each_entry(patch, &obj->applied_patch, list) { + if (strncmp(patch->uinfo->id, uuid, UPATCH_ID_LEN) == 0) { + return patch; + } + } + } + return NULL; } -int process_patch(int pid, struct upatch_elf *uelf, struct running_elf *relf, const char *uuid, const char *binary_path) +static int upatch_apply_prepare(struct upatch_elf *uelf, + struct upatch_process *proc, struct object_file **obj) { - struct upatch_process proc; - - // 查看process的信息,pid: maps, mem, cmdline, exe - int ret = upatch_process_init(&proc, pid); - if (ret < 0) { - log_error("Failed to init process\n"); - goto out; - } - - printf("Patch '%s' to ", uuid); - upatch_process_print_short(&proc); - - ret = upatch_process_mem_open(&proc, MEM_READ); - if (ret < 0) { - log_error("Failed to open process memory\n"); - goto out_free; - } - - // use uprobe to hack function. the program has been executed to the entry - // point - - /* - * For each object file that we want to patch (either binary itself or - * shared library) we need its ELF structure to perform relocations. - * Because we know uniq BuildID of the object the section addresses - * stored in the patch are valid for the original object. - */ - // 解析process的mem-maps,获得各个块的内存映射以及phdr - ret = upatch_process_map_object_files(&proc, NULL); - if (ret < 0) { - log_error("Failed to read process memory mapping\n"); - goto out_free; - } - ret = upatch_process_uuid_exist(&proc, uuid); - if (ret != 0) { - ret = 0; - log_error("Patch '%s' already exists\n", uuid); - goto out_free; - } - ret = binary_init(relf, binary_path); + int ret = 0; + + for (int i = 0; i < STACK_CHECK_RETRY_TIMES; i++) { + ret = upatch_process_attach(proc); + if (ret < 0) { + log_error("Failed to attach process\n"); + goto detach; + } + + *obj = upatch_find_obj(uelf, proc); + if (*obj == NULL) { + ret = -ENODATA; + goto detach; + } + + ret = upatch_active_stack_check(uelf, proc); + if (ret != -EBUSY) { + return ret; + } + upatch_process_detach(proc); + sleep(1); + } +detach: + upatch_process_detach(proc); + return ret; +} + +int process_patch(int pid, struct upatch_elf *uelf, struct running_elf *relf, + const char *uuid, const char *binary_path) +{ + struct upatch_process proc; + struct object_file *obj = NULL; + + // 查看process的信息,pid: maps, mem, cmdline, exe + int ret = upatch_process_init(&proc, pid); + if (ret < 0) { + log_error("Failed to init process\n"); + goto out; + } + + log_debug("Patch '%s' to ", uuid); + upatch_process_print_short(&proc); + + ret = upatch_process_mem_open(&proc, MEM_READ); + if (ret < 0) { + log_error("Failed to open process memory\n"); + goto out_free; + } + + // use uprobe to interpose function. the program has been executed to the + // entry point + + /* + * For each object file that we want to patch (either binary itself or + * shared library) we need its ELF structure to perform relocations. + * Because we know uniq BuildID of the object the section addresses + * stored in the patch are valid for the original object. + */ + // 解析process的mem-maps,获得各个块的内存映射以及phdr + ret = upatch_process_map_object_files(&proc); + if (ret < 0) { + log_error("Failed to read process memory mapping\n"); + goto out_free; + } + struct object_patch *patch = upatch_find_patch(&proc, uuid); + if (patch != NULL) { + log_error("Patch '%s' already exists\n", uuid); + goto out_free; + } + ret = binary_init(relf, binary_path); if (ret) { log_error("Failed to load binary\n"); goto out_free; } uelf->relf = relf; - upatch_time_tick(pid); - - /* Finally, attach to process */ - ret = upatch_process_attach(&proc); - if (ret < 0) { - log_error("Failed to attach process\n"); - goto out_free; - } - - // TODO: 栈解析 - // 应用 - ret = upatch_apply_patches(&proc, uelf, uuid); - if (ret < 0) { - log_error("Failed to apply patch\n"); - goto out_free; - } + upatch_time_tick(pid); -out_free: - upatch_process_detach(&proc); - upatch_time_tick(pid); - - upatch_process_destroy(&proc); + ret = upatch_apply_prepare(uelf, &proc, &obj); + if (ret < 0) { + goto out_free; + } + // 应用 + ret = upatch_apply_patches(obj, uelf, uuid); + if (ret < 0) { + log_error("Failed to apply patch\n"); + goto out_free; + } +out_free: + upatch_process_detach(&proc); + upatch_time_tick(pid); + upatch_process_destroy(&proc); out: - return ret; + return ret; } -static int upatch_unapply_patches(struct upatch_process *proc, const char *uuid) +static int upatch_unapply_patches(struct object_file *obj, + struct upatch_info *uinfo) { - int ret = 0; - struct object_file *obj = NULL; - struct object_patch *patch = NULL; - bool found = false; - - // Traverse all mapped memory and find all upatch memory - list_for_each_entry(obj, &proc->objs, list) { - if (!obj->is_patch) { - continue; - } - // For eatch patch, check it's id and do remove - list_for_each_entry(patch, &obj->applied_patch, list) { - if (strncmp(patch->uinfo->id, uuid, UPATCH_ID_LEN) != 0) { - continue; - } - found = true; - - ret = unapply_patch(obj, patch->funcs, patch->uinfo->changed_func_num); - if (ret) { - goto out; - } - - log_debug("munmap upatch layout core:\n"); - upatch_free(obj, - (void *)patch->uinfo->start, - patch->uinfo->end - patch->uinfo->start - ); - - break; - } - } - - if (!found) { - log_warn("Patch '%s' is not found\n", uuid); - goto out; - } + int ret = 0; -out: - return ret; + ret = unapply_patch(obj, uinfo->funcs, uinfo->changed_func_num); + if (ret) { + return ret; + } + + log_debug("munmap upatch layout core:\n"); + upatch_free(obj, (void *)uinfo->start, uinfo->end - uinfo->start); + return ret; +} + +static int upatch_unapply_prepare(struct upatch_process *proc, + const char *uuid, struct object_patch **patch) +{ + int ret = 0; + + for (int i = 0; i < STACK_CHECK_RETRY_TIMES; i++) { + ret = upatch_process_attach(proc); + if (ret < 0) { + log_error("Failed to attach process\n"); + goto detach; + } + *patch = upatch_find_patch(proc, uuid); + if (*patch == NULL) { + log_error("Patch '%s' is not found\n", uuid); + ret = -ENODATA; + goto detach; + } + ret = upatch_stack_check((*patch)->uinfo, proc, DEACTIVE); + if (ret != -EBUSY) { + return ret; + } + upatch_process_detach(proc); + sleep(1); + } +detach: + upatch_process_detach(proc); + return ret; } int process_unpatch(int pid, const char *uuid) { - struct upatch_process proc; - - // TODO: check build id - // TODO: 栈解析 - // 查看process的信息,pid: maps, mem, cmdline, exe - int ret = upatch_process_init(&proc, pid); - if (ret < 0) { - log_error("Failed to init process\n"); - goto out; - } - - printf("Unpatch '%s' from ", uuid); - upatch_process_print_short(&proc); - - ret = upatch_process_mem_open(&proc, MEM_READ); - if (ret < 0) { - log_error("Failed to open process memory\n"); - goto out_free; - } - - // use uprobe to hack function. the program has been executed to the entry - // point - - /* - * For each object file that we want to patch (either binary itself or - * shared library) we need its ELF structure to perform relocations. - * Because we know uniq BuildID of the object the section addresses - * stored in the patch are valid for the original object. - */ - // 解析process的mem-maps,获得各个块的内存映射以及phdr - ret = upatch_process_map_object_files(&proc, NULL); - if (ret < 0) { - log_error("Failed to read process memory mapping\n"); - goto out_free; - } - - upatch_time_tick(pid); - - /* Finally, attach to process */ - ret = upatch_process_attach(&proc); - if (ret < 0) { - log_error("Failed to attach process\n"); - goto out_free; - } - - // 应用 - ret = upatch_unapply_patches(&proc, uuid); - if (ret < 0) { - log_error("Failed to remove patch\n"); - goto out_free; - } + struct upatch_process proc; + struct object_patch *patch = NULL; + + // 查看process的信息,pid: maps, mem, cmdline, exe + int ret = upatch_process_init(&proc, pid); + if (ret < 0) { + log_error("Failed to init process\n"); + goto out; + } -out_free: - upatch_process_detach(&proc); - upatch_time_tick(pid); + log_debug("Unpatch '%s' from ", uuid); + upatch_process_print_short(&proc); + + ret = upatch_process_mem_open(&proc, MEM_READ); + if (ret < 0) { + log_error("Failed to open process memory\n"); + goto out_free; + } + + // use uprobe to interpose function. the program has been executed to the + // entry point + + /* + * For each object file that we want to patch (either binary itself or + * shared library) we need its ELF structure to perform relocations. + * Because we know uniq BuildID of the object the section addresses + * stored in the patch are valid for the original object. + */ + // 解析process的mem-maps,获得各个块的内存映射以及phdr + ret = upatch_process_map_object_files(&proc); + if (ret < 0) { + log_error("Failed to read process memory mapping\n"); + goto out_free; + } + + upatch_time_tick(pid); + ret = upatch_unapply_prepare(&proc, uuid, &patch); + if (ret < 0) { + goto out_free; + } + // 应用 + ret = upatch_unapply_patches(patch->obj, patch->uinfo); + if (ret < 0) { + log_error("Failed to remove patch\n"); + goto out_free; + } - upatch_process_destroy(&proc); +out_free: + upatch_process_detach(&proc); + upatch_time_tick(pid); + upatch_process_destroy(&proc); out: - return ret; + return ret; } static int upatch_info(struct upatch_process *proc) { - struct object_file *obj = NULL; - struct object_patch *patch = NULL; - bool found = false; - - list_for_each_entry(obj, &proc->objs, list) { - if (obj->is_patch) { - found = true; - break; - } - } - - if (!found) - return found; - - found = false; - list_for_each_entry(patch, &obj->applied_patch, list) { - // TODO: check upatch_info id - found = true; - break; - } - - return found; + struct object_file *obj = NULL; + struct object_patch *patch = NULL; + bool found = false; + + list_for_each_entry(obj, &proc->objs, list) { + if (obj->is_patch) { + found = true; + break; + } + } + + if (!found) { + return found; + } + + found = false; + list_for_each_entry(patch, &obj->applied_patch, list) { + found = true; + break; + } + + return found; } int process_info(int pid) { - int ret; - struct upatch_process proc; - char *status = "error"; - - // TODO: check build id - // TODO: 栈解析 - // 查看process的信息,pid: maps, mem, cmdline, exe - ret = upatch_process_init(&proc, pid); - if (ret < 0) { - log_error("Failed to init process\n"); - goto out; - } - - ret = upatch_process_mem_open(&proc, MEM_READ); - if (ret < 0) { - log_error("Failed to open process memory\n"); - goto out_free; - } - - ret = upatch_process_map_object_files(&proc, NULL); - if (ret < 0) { - log_error("Failed to read process memory mapping\n"); - goto out_free; - } - - ret = upatch_info(&proc); - if (ret) { - status = "actived"; - } - else { - status = "removed"; - } - - ret = 0; + int ret; + struct upatch_process proc; + char *status = "error"; + + // TODO: check build id + // TODO: 栈解析 + // 查看process的信息,pid: maps, mem, cmdline, exe + ret = upatch_process_init(&proc, pid); + if (ret < 0) { + log_error("Failed to init process\n"); + goto out; + } + + ret = upatch_process_mem_open(&proc, MEM_READ); + if (ret < 0) { + log_error("Failed to open process memory\n"); + goto out_free; + } + + ret = upatch_process_map_object_files(&proc); + if (ret < 0) { + log_error("Failed to read process memory mapping\n"); + goto out_free; + } + + ret = upatch_info(&proc); + if (ret) { + status = "actived"; + } else { + status = "removed"; + } + ret = 0; out_free: - upatch_process_destroy(&proc); + upatch_process_destroy(&proc); out: - log_normal("%s\n", status); - return ret; + log_debug("%s\n", status); + return ret; } diff --git a/upatch-manage/upatch-process.c b/upatch-manage/upatch-process.c index c3681650eb7a9664b7a859e1c45076bcff382bfe..8e1882f8a41f3d1542d2ff2998cbe19f198b62c3 100644 --- a/upatch-manage/upatch-process.c +++ b/upatch-manage/upatch-process.c @@ -33,7 +33,6 @@ #include "list.h" #include "log.h" -#include "process.h" #include "upatch-common.h" #include "upatch-elf.h" #include "upatch-process.h" @@ -49,850 +48,871 @@ static const int MAX_ATTACH_ATTEMPTS = 3; */ static int lock_process(int pid) { - int fd; - char path[128]; + int fd; + char path[128]; - log_debug("Locking PID %d...", pid); - snprintf(path, sizeof(path), "/proc/%d/maps", pid); + log_debug("Locking PID %d...", pid); + (void) snprintf(path, sizeof(path), "/proc/%d/maps", pid); - fd = open(path, O_RDONLY); - if (fd < 0) { - log_error("Failed to open file '%s'\n", path); - return -1; - } - log_debug("OK\n"); + fd = open(path, O_RDONLY); + if (fd < 0) { + log_error("Failed to open file '%s'\n", path); + return -1; + } + log_debug("OK\n"); - return fd; + return fd; } -static void unlock_process(int pid, int fdmaps) +static void unlock_process(int fdmaps) { - int errsv = errno; - close(fdmaps); - errno = errsv; + int errsv = errno; + close(fdmaps); + errno = errsv; } // TODO: get addr_space static int upatch_coroutines_init(struct upatch_process *proc) { - INIT_LIST_HEAD(&proc->coro.coros); + INIT_LIST_HEAD(&proc->coro.coros); - return 0; + return 0; } static int process_get_comm(struct upatch_process *proc) { - char path[128]; - char realpath[PATH_MAX]; - char *bn, *c; - ssize_t ret; - - snprintf(path, sizeof(path), "/proc/%d/exe", proc->pid); - log_debug("Reading from '%s'...", path); - - ret = readlink(path, realpath, sizeof(realpath)); - if (ret < 0) { - return -1; - } - - realpath[ret] = '\0'; - bn = basename(realpath); - strncpy(path, bn, sizeof(path) - 1); - if ((c = strstr(path, " (deleted)"))) { - *c = '\0'; - } - - proc->comm[sizeof(proc->comm) - 1] = '\0'; - memcpy(proc->comm, path, sizeof(proc->comm) - 1); - // TODO: the comm is ldxxx - log_debug("OK\n"); - - return 0; + char path[128]; + char realpath[PATH_MAX]; + char *bn; + char *c; + ssize_t ret; + + (void) snprintf(path, sizeof(path), "/proc/%d/exe", proc->pid); + log_debug("Reading from '%s'...", path); + + ret = readlink(path, realpath, sizeof(realpath)); + if (ret < 0) { + return -1; + } + + realpath[ret] = '\0'; + bn = basename(realpath); + strncpy(path, bn, sizeof(path) - 1); + if ((c = strstr(path, " (deleted)"))) { + *c = '\0'; + } + + proc->comm[sizeof(proc->comm) - 1] = '\0'; + memcpy(proc->comm, path, sizeof(proc->comm) - 1); + // TODO: the comm is ldxxx + log_debug("OK\n"); + + return 0; } int upatch_process_init(struct upatch_process *proc, int pid) { - int fdmaps; + int fdmaps; - fdmaps = lock_process(pid); - if (fdmaps < 0) { - goto out_err; - } + fdmaps = lock_process(pid); + if (fdmaps < 0) { + goto out_err; + } - memset(proc, 0, sizeof(*proc)); + memset(proc, 0, sizeof(*proc)); - proc->pid = pid; - proc->fdmaps = fdmaps; - proc->memfd = -1; + proc->pid = pid; + proc->fdmaps = fdmaps; + proc->memfd = -1; - INIT_LIST_HEAD(&proc->ptrace.pctxs); - INIT_LIST_HEAD(&proc->objs); - INIT_LIST_HEAD(&proc->vmaholes); - proc->num_objs = 0; + INIT_LIST_HEAD(&proc->ptrace.pctxs); + INIT_LIST_HEAD(&proc->objs); + INIT_LIST_HEAD(&proc->vma_holes); + proc->num_objs = 0; - if (upatch_coroutines_init(proc)) { - goto out_unlock; - } + if (upatch_coroutines_init(proc)) { + goto out_unlock; + } - if (process_get_comm(proc)) { - goto out_unlock; - } + if (process_get_comm(proc)) { + goto out_unlock; + } - return 0; + return 0; out_unlock: - unlock_process(pid, fdmaps); + unlock_process(fdmaps); out_err: - return -1; + return -1; } static void upatch_object_memfree(struct object_file *obj) { - struct object_patch *opatch, *opatch_safe; - struct obj_vm_area *ovma, *ovma_safe; - - if (obj->name) { - free(obj->name); - } - - list_for_each_entry_safe(opatch, opatch_safe, &obj->applied_patch, list) { - if (opatch->uinfo) { - free(opatch->uinfo); - } - if (opatch->funcs) { - free(opatch->funcs); - } - free(opatch); - } - - list_for_each_entry_safe(ovma, ovma_safe, &obj->vma, list) { - free(ovma); - } + struct object_patch *opatch; + struct object_patch *opatch_safe; + struct obj_vm_area *ovma; + struct obj_vm_area *ovma_safe; + + if (obj->name) { + free(obj->name); + } + + list_for_each_entry_safe(opatch, opatch_safe, &obj->applied_patch, list) { + if (opatch->uinfo) { + if (opatch->uinfo->funcs) { + free(opatch->uinfo->funcs); + } + free(opatch->uinfo); + } + free(opatch); + } + + list_for_each_entry_safe(ovma, ovma_safe, &obj->vma, list) { + free(ovma); + } } static void upatch_process_memfree(struct upatch_process *proc) { - struct upatch_ptrace_ctx *p, *p_safe; - struct object_file *obj, *obj_safe; - struct vm_hole *hole, *hole_safe; - - list_for_each_entry_safe(p, p_safe, &proc->ptrace.pctxs, list) { - free(p); - } - - list_for_each_entry_safe(hole, hole_safe, &proc->vmaholes, list) { - free(hole); - } - - list_for_each_entry_safe(obj, obj_safe, &proc->objs, list) { - upatch_object_memfree(obj); - free(obj); - } + struct upatch_ptrace_ctx *p; + struct upatch_ptrace_ctx *p_safe; + struct object_file *obj; + struct object_file *obj_safe; + struct vm_hole *hole; + struct vm_hole *hole_safe; + + list_for_each_entry_safe(p, p_safe, &proc->ptrace.pctxs, list) { + free(p); + } + + list_for_each_entry_safe(hole, hole_safe, &proc->vma_holes, list) { + free(hole); + } + + list_for_each_entry_safe(obj, obj_safe, &proc->objs, list) { + upatch_object_memfree(obj); + free(obj); + } } void upatch_process_destroy(struct upatch_process *proc) { - unlock_process(proc->pid, proc->fdmaps); - upatch_process_memfree(proc); + unlock_process(proc->fdmaps); + upatch_process_memfree(proc); } static void process_print_cmdline(struct upatch_process *proc) { - char buf[PATH_MAX]; - ssize_t i, rv; - - snprintf(buf, PATH_MAX, "/proc/%d/cmdline", proc->pid); - int fd = open(buf, O_RDONLY); - if (fd == -1) { - log_error("Failed to open file '%s'\n", buf); - return; - } - - while (1) { - rv = read(fd, buf, sizeof(buf)); - - if (rv == -1) { - if (errno == EINTR) { - continue; - } - log_error("Failed to read cmdline\n"); - goto err_close; - } - - if (rv == 0) { - break; - } - - for (i = 0; i < rv; i++) { - if (isprint(buf[i])) { - printf("%c", buf[i]); - } - else { - printf(" "); - } - } - } + char buf[PATH_MAX]; + ssize_t i; + ssize_t rv; + + (void) snprintf(buf, PATH_MAX, "/proc/%d/cmdline", proc->pid); + + int fd = open(buf, O_RDONLY); + if (fd == -1) { + log_error("Failed to open file '%s'\n", buf); + return; + } + + while (1) { + rv = read(fd, buf, sizeof(buf)); + if (rv == -1) { + if (errno == EINTR) { + continue; + } + log_error("Failed to read cmdline\n"); + goto err_close; + } + + if (rv == 0) { + break; + } + + for (i = 0; i < rv; i++) { + if (isprint(buf[i])) { + log_debug("%c", buf[i]); + } else { + log_debug(" "); + } + } + } err_close: - close(fd); + close(fd); } void upatch_process_print_short(struct upatch_process *proc) { - printf("process %d, cmdline: ", proc->pid); - process_print_cmdline(proc); - printf("\n"); + log_debug("process %d, cmdline: ", proc->pid); + process_print_cmdline(proc); + log_debug("\n"); } int upatch_process_mem_open(struct upatch_process *proc, int mode) { - char path[PATH_MAX]; + char path[PATH_MAX]; - if (proc->memfd >= 0) { - close(proc->memfd); - } + if (proc->memfd >= 0) { + close(proc->memfd); + } - snprintf(path, sizeof(path), "/proc/%d/mem", proc->pid); - proc->memfd = open(path, mode == MEM_WRITE ? O_RDWR : O_RDONLY); - if (proc->memfd < 0) { - log_error("Failed to open file '%s'\n", path); - return -1; - } + (void) snprintf(path, sizeof(path), "/proc/%d/mem", proc->pid); + proc->memfd = open(path, mode == MEM_WRITE ? O_RDWR : O_RDONLY); + if (proc->memfd < 0) { + log_error("Failed to open file '%s'\n", path); + return -1; + } - return 0; + return 0; } static unsigned int perms2prot(const char *perms) { - unsigned int prot = 0; - - if (perms[0] == 'r') - prot |= PROT_READ; - if (perms[1] == 'w') - prot |= PROT_WRITE; - if (perms[2] == 'x') - prot |= PROT_EXEC; - /* Ignore 'p'/'s' flag, we don't need it */ - return prot; + unsigned int prot = 0; + + if (perms[0] == 'r') { + prot |= PROT_READ; + } + if (perms[1] == 'w') { + prot |= PROT_WRITE; + } + if (perms[2] == 'x') { + prot |= PROT_EXEC; + } + /* Ignore 'p'/'s' flag, we don't need it */ + return prot; } static struct vm_hole *process_add_vm_hole(struct upatch_process *proc, - unsigned long hole_start, - unsigned long hole_end) + unsigned long start, unsigned long end) { - struct vm_hole *hole; - - hole = malloc(sizeof(*hole)); - if (hole == NULL) - return NULL; - - memset(hole, 0, sizeof(*hole)); - hole->start = hole_start; - hole->end = hole_end; - - list_add(&hole->list, &proc->vmaholes); - - return hole; + struct vm_hole *hole = malloc(sizeof(*hole)); + if (hole == NULL) { + return NULL; + } + + hole->start = start; + hole->end = end; + hole->len = end - start; + list_init(&hole->list); + + list_add(&hole->list, &proc->vma_holes); + return hole; } static int process_get_object_type(struct upatch_process *proc, - struct vm_area *vma, char *name, - unsigned char *buf, size_t bufsize) + struct vm_area *vma, char *name, unsigned char *buf, size_t bufsize) { - int ret, type = OBJECT_UNKNOWN; - - ret = upatch_process_mem_read(proc, vma->start, buf, bufsize); - if (ret < 0) - return -1; - - if (vma->prot == PROT_READ && - !strncmp(name, "[anonymous]", strlen("[anonymous]")) && - !memcmp(buf, UPATCH_HEADER, UPATCH_HEADER_LEN)) { - type = OBJECT_UPATCH; - } else if (!memcmp(buf, ELFMAG, SELFMAG)) { - type = OBJECT_ELF; - } else { - type = OBJECT_UNKNOWN; - } - - return type; + int ret; + int type = OBJECT_UNKNOWN; + + ret = upatch_process_mem_read(proc, vma->start, buf, bufsize); + if (ret < 0) { + return -1; + } + + if (vma->prot == PROT_READ && + !strncmp(name, "[anonymous]", strlen("[anonymous]")) && + !memcmp(buf, UPATCH_HEADER, UPATCH_HEADER_LEN)) { + type = OBJECT_UPATCH; + } else if (!memcmp(buf, ELFMAG, SELFMAG)) { + type = OBJECT_ELF; + } else { + type = OBJECT_UNKNOWN; + } + + return type; } static int vm_area_same(struct vm_area *a, struct vm_area *b) { - return ((a->start == b->start) && (a->end == b->end) && - (a->prot == b->prot)); + return ((a->start == b->start) && + (a->end == b->end) && + (a->prot == b->prot)); } static int object_add_vm_area(struct object_file *o, struct vm_area *vma, - struct vm_hole *hole) + struct vm_hole *hole) { - struct obj_vm_area *ovma; - - if (o->previous_hole == NULL) - o->previous_hole = hole; - list_for_each_entry(ovma, &o->vma, list) { - if (vm_area_same(vma, &ovma->inmem)) - return 0; - } - ovma = malloc(sizeof(*ovma)); - if (!ovma) - return -1; - memset(ovma, 0, sizeof(*ovma)); - ovma->inmem = *vma; - list_add(&ovma->list, &o->vma); - return 0; -} + struct obj_vm_area *ovma; -static struct object_file * -process_new_object(struct upatch_process *proc, dev_t dev, int inode, - const char *name, struct vm_area *vma, struct vm_hole *hole) -{ - struct object_file *o; - - log_debug("Creating object file '%s' for %lx:%d...", name, dev, inode); - - o = malloc(sizeof(*o)); - if (!o) { - log_error("FAILED\n"); - return NULL; - } - memset(o, 0, sizeof(struct object_file)); - - INIT_LIST_HEAD(&o->list); - INIT_LIST_HEAD(&o->vma); - INIT_LIST_HEAD(&o->applied_patch); - o->num_applied_patch = 0; - o->proc = proc; - o->dev = dev; - o->inode = inode; - o->is_patch = 0; - - o->previous_hole = hole; - if (object_add_vm_area(o, vma, hole) < 0) { - log_error("Cannot add vm area for %s\n", name); - free(o); - return NULL; - } - - o->name = strdup(name); - o->is_elf = 0; - - list_add(&o->list, &proc->objs); - proc->num_objs++; - - log_debug("OK\n"); - return o; + if (o->prev_hole == NULL) { + o->prev_hole = hole; + } + + list_for_each_entry(ovma, &o->vma, list) { + if (vm_area_same(vma, &ovma->inmem)) { + return 0; + } + } + + ovma = malloc(sizeof(*ovma)); + if (!ovma) { + return -1; + } + + memset(ovma, 0, sizeof(*ovma)); + ovma->inmem = *vma; + + list_add(&ovma->list, &o->vma); + return 0; } -/** - * Returns: 0 if everything is ok, -1 on error. - */ -static int process_add_object_vma(struct upatch_process *proc, dev_t dev, - int inode, char *name, struct vm_area *vma, - struct vm_hole *hole) +static struct object_file *process_new_object(struct upatch_process *proc, + dev_t dev, ino_t inode, const char *name, + struct vm_area *vma, struct vm_hole *hole) { - int object_type; - unsigned char header_buf[1024]; - struct object_file *o; - - /* Event though process_get_object_type() return -1, - * we still need continue process. */ - object_type = process_get_object_type(proc, vma, name, header_buf, - sizeof(header_buf)); - - if (object_type != OBJECT_UPATCH) { - /* Is not a upatch, look if this is a vm_area of an already - * enlisted object. - */ - list_for_each_entry_reverse(o, &proc->objs, list) { - if ((dev && inode && o->dev == dev && - o->inode == inode) || - (dev == 0 && !strcmp(o->name, name))) { - return object_add_vm_area(o, vma, hole); - } - } - } - - o = process_new_object(proc, dev, inode, name, vma, hole); - if (o == NULL) { - return -1; - } - - if (object_type == OBJECT_UPATCH) { - struct object_patch *opatch; - - opatch = malloc(sizeof(struct object_patch)); - if (opatch == NULL) { - return -1; - } - - opatch->uinfo = malloc(sizeof(struct upatch_info)); - if (opatch->uinfo == NULL) { - return -1; - } - - memcpy(opatch->uinfo, header_buf, sizeof(struct upatch_info)); - opatch->funcs = malloc(opatch->uinfo->changed_func_num * - sizeof(struct upatch_info_func)); - if (upatch_process_mem_read( - proc, vma->start + sizeof(struct upatch_info), - opatch->funcs, - opatch->uinfo->changed_func_num * - sizeof(struct upatch_info_func))) { - log_error("can't read patch funcs at 0x%lx\n", - vma->start + sizeof(struct upatch_info)); - return -1; - } - list_add(&opatch->list, &o->applied_patch); - o->num_applied_patch++; - o->is_patch = 1; - } - if (object_type == OBJECT_ELF) { - o->is_elf = 1; - } - - return 0; + struct object_file *o; + + log_debug("Creating object file '%s' for %lx:%lu...", name, dev, inode); + + o = malloc(sizeof(*o)); + if (!o) { + log_error("FAILED\n"); + return NULL; + } + memset(o, 0, sizeof(struct object_file)); + + INIT_LIST_HEAD(&o->list); + INIT_LIST_HEAD(&o->vma); + INIT_LIST_HEAD(&o->applied_patch); + o->num_applied_patch = 0; + o->proc = proc; + o->dev = dev; + o->inode = inode; + o->is_patch = 0; + + o->prev_hole = hole; + if (object_add_vm_area(o, vma, hole) < 0) { + log_error("Cannot add vm area for %s\n", name); + free(o); + return NULL; + } + + o->name = strdup(name); + o->is_elf = 0; + + list_add(&o->list, &proc->objs); + proc->num_objs++; + + log_debug("OK\n"); + return o; } -int upatch_process_parse_proc_maps(struct upatch_process *proc) +static void link_funcs_name(struct upatch_info *uinfo) { - FILE *f; - int ret, is_libc_base_set = 0; - unsigned long hole_start = 0; - struct vm_hole *hole = NULL; - - /* - * 1. Create the list of all objects in the process - * 2. Check whether we have patch for any of them - * 3. If we have at least one patch, create files for all - * of the object (we might have references to them - * in the patch). - */ - int fd = dup(proc->fdmaps); - if (fd < 0) { - log_error("unable to dup fd %d", proc->fdmaps); - return -1; - } - - lseek(fd, 0, SEEK_SET); - f = fdopen(fd, "r"); - if (f == NULL) { - log_error("unable to fdopen %d", fd); - close(fd); - return -1; - } - - do { - struct vm_area vma; - char line[1024]; - unsigned long start, end, offset; - unsigned int maj, min, inode; - char perms[5], name_[256], *name = name_; - int r; - - if (!fgets(line, sizeof(line), f)) { - break; - } - - r = sscanf(line, "%lx-%lx %s %lx %x:%x %d %255s", &start, &end, - perms, &offset, &maj, &min, &inode, name_); - if (r == EOF) { - log_error("Failed to read maps: unexpected EOF"); - goto error; - } - - if (r != 8) { - strcpy(name, "[anonymous]"); - } - - vma.start = start; - vma.end = end; - vma.offset = offset; - vma.prot = perms2prot(perms); - - /* Hole must be at least 2 pages for guardians */ - if (start - hole_start > 2 * PAGE_SIZE) { - hole = process_add_vm_hole(proc, hole_start + PAGE_SIZE, - start - PAGE_SIZE); - if (hole == NULL) { - log_error("Failed to add vma hole"); - goto error; - } - } - hole_start = end; - - name = name[0] == '/' ? basename(name) : name; - - ret = process_add_object_vma(proc, makedev(maj, min), inode, - name, &vma, hole); - if (ret < 0) { - log_error("Failed to add object vma"); - goto error; - } - - if (!is_libc_base_set && !strncmp(basename(name), "libc", 4) && - vma.prot & PROT_EXEC) { - proc->libc_base = start; - is_libc_base_set = 1; - } - - } while (1); - fclose(f); - close(fd); - - log_debug("Found %d object file(s)\n", proc->num_objs); - - if (!is_libc_base_set) { - log_error("Can't find libc_base required for manipulations: %d", - proc->pid); - return -1; - } - - return 0; + unsigned long idx = 0; -error: - fclose(f); - close(fd); - return -1; + for (unsigned long i = 0; i < uinfo->changed_func_num; i++) { + char *name = (char *)uinfo->func_names + idx; + + uinfo->funcs[i].name = name; + idx += strlen(name) + 1; + } } -int upatch_process_map_object_files(struct upatch_process *proc, - const char *patch_id) +static void free_object_patch(struct object_patch *opatch) { - // we can get plt/got table from mem's elf_segments - // Now we read them from the running file - return upatch_process_parse_proc_maps(proc); + if (opatch == NULL) { + return; + } + + if (opatch->uinfo != NULL) { + if (opatch->uinfo->funcs != NULL) { + free(opatch->uinfo->funcs); + } + if (opatch->uinfo->func_names != NULL) { + free(opatch->uinfo->func_names); + } + free(opatch->uinfo); + } + + free(opatch); } -// static int process_has_thread_pid(struct upatch_proces *proc, int pid) -// { -// struct upatch_ptrace_ctx *pctx; +static int add_upatch_object(struct upatch_process *proc, struct object_file *o, + unsigned long src, unsigned char *header_buf) +{ + struct object_patch *opatch; + + opatch = malloc(sizeof(struct object_patch)); + if (opatch == NULL) { + log_error("malloc opatch failed\n"); + return -1; + } + + opatch->obj = o; + opatch->uinfo = malloc(sizeof(struct upatch_info)); + if (opatch->uinfo == NULL) { + log_error("malloc opatch->uinfo failed\n"); + free(opatch); + return -1; + } + + memcpy(opatch->uinfo->magic, header_buf, sizeof(struct upatch_info)); + + opatch->uinfo->func_names = malloc(opatch->uinfo->func_names_size); + if (opatch->uinfo->func_names == NULL) { + log_error("Failed to malloc funcs_names\n"); + free_object_patch(opatch); + return -ENOMEM; + } + + if (upatch_process_mem_read(proc, src, + opatch->uinfo->func_names, opatch->uinfo->func_names_size)) { + log_error("Cannot read patch func names at 0x%lx\n", src); + free_object_patch(opatch); + return -1; + } + + src += opatch->uinfo->func_names_size; + opatch->uinfo->funcs = malloc(opatch->uinfo->changed_func_num * + sizeof(struct upatch_info_func)); + if (upatch_process_mem_read(proc, src, opatch->uinfo->funcs, + opatch->uinfo->changed_func_num * sizeof(struct upatch_info_func))) { + log_error("can't read patch funcs at 0x%lx\n", src); + free_object_patch(opatch); + return -1; + } + + link_funcs_name(opatch->uinfo); + list_add(&opatch->list, &o->applied_patch); + o->num_applied_patch++; + o->is_patch = 1; + + return 0; +} +/** + * Returns: 0 if everything is ok, -1 on error. + */ +static int process_add_vma(struct upatch_process *proc, + dev_t dev, ino_t inode, char *name, + struct vm_area *vma, struct vm_hole *hole) +{ + int object_type; + unsigned char header_buf[1024]; + struct object_file *o; + + /* Event though process_get_object_type() return -1, + * we still need continue process. */ + object_type = process_get_object_type(proc, vma, name, + header_buf, sizeof(header_buf)); + if (object_type != OBJECT_UPATCH) { + /* Is not a upatch, look if this is a vm_area of an already + * enlisted object. + */ + list_for_each_entry_reverse(o, &proc->objs, list) { + if ((dev && inode && o->dev == dev && + o->inode == (ino_t)inode) || + (dev == 0 && !strcmp(o->name, name))) { + return object_add_vm_area(o, vma, hole); + } + } + } + + o = process_new_object(proc, dev, inode, name, vma, hole); + if (o == NULL) { + return -1; + } + + if (object_type == OBJECT_UPATCH) { + unsigned long src = vma->start + sizeof(struct upatch_info); + if (add_upatch_object(proc, o, src, header_buf) != 0) { + return -1; + } + } + + if (object_type == OBJECT_ELF) { + o->is_elf = 1; + } + + return 0; +} -// list_for_each_entry(pctx, &proc->ptrace.pctxs, list) -// if (pctx->pid == pid) -// return 1; +int upatch_process_map_object_files(struct upatch_process *proc) +{ + int ret = 0; + + /* + * 1. Create the list of all objects in the process + * 2. Check whether we have patch for any of them + * 3. If we have at least one patch, create files for all + * of the object (we might have references to them + * in the patch). + */ + int fd = dup(proc->fdmaps); + if (fd < 0) { + log_error("unable to dup fd %d", proc->fdmaps); + return -1; + } + + lseek(fd, 0, SEEK_SET); + FILE *file = fdopen(fd, "r"); + if (file == NULL) { + log_error("unable to fdopen %d", fd); + close(fd); + return -1; + } + + unsigned long hole_start = 0; + + char line[1024]; + while (fgets(line, sizeof(line), file) != NULL) { + struct vm_area vma; + unsigned long vma_start; + unsigned long vma_end; + unsigned long offset; + unsigned int maj; + unsigned int min; + unsigned int inode; + char perms[5]; + char name_buf[256]; + char *name = name_buf; + + ret = sscanf(line, "%lx-%lx %s %lx %x:%x %u %255s", + &vma_start, &vma_end, perms, &offset, + &maj, &min, &inode, name_buf); + if (ret == EOF) { + log_error("Failed to read maps: unexpected EOF"); + goto error; + } + if (ret != 8) { + name = "[anonymous]"; + } + + vma.start = vma_start; + vma.end = vma_end; + vma.offset = offset; + vma.prot = perms2prot(perms); + + /* Hole must be at least 2 pages for guardians */ + struct vm_hole *hole = NULL; + if ((hole_start != 0) && + (vma_start - hole_start > 2 * (uintptr_t)PAGE_SIZE)) { + uintptr_t start = hole_start + (uintptr_t)PAGE_SIZE; + uintptr_t end = vma_start - (uintptr_t)PAGE_SIZE; + + hole = process_add_vm_hole(proc, start, end); + if (hole == NULL) { + log_error("Failed to add vma hole"); + goto error; + } + log_debug("vm_hole: start=0x%lx, end=0x%lx, len=0x%lx\n", + hole->start, hole->end, hole->len); + } + hole_start = vma_end; + + name = name[0] == '/' ? basename(name) : name; + ret = process_add_vma(proc, makedev(maj, min), inode, name, &vma, hole); + if (ret < 0) { + log_error("Failed to add object vma"); + goto error; + } + + if ((proc->libc_base == 0) && + (vma.prot & PROT_EXEC) && + !strncmp(basename(name), "libc", 4)) { + proc->libc_base = vma_start; + } + } + + (void)fclose(file); + (void)close(fd); + log_debug("Found %d object file(s)\n", proc->num_objs); + + if (proc->libc_base == 0) { + log_error("Cannot find libc_base, pid=%d", + proc->pid); + return -1; + } + + return 0; -// return 0; -// } +error: + (void)fclose(file); + (void)close(fd); + return -1; +} static int process_list_threads(struct upatch_process *proc, int **ppids, - size_t *npids, size_t *alloc) + size_t *npids, size_t *alloc) { - DIR *dir = NULL; - struct dirent *de; - char path[PATH_MAX]; - int *pids = *ppids; - - snprintf(path, sizeof(path), "/proc/%d/task", proc->pid); - - dir = opendir(path); - if (!dir) { - log_error("Failed to open directory '%s'\n", path); - goto dealloc; - } - - *npids = 0; - while ((de = readdir(dir))) { - int *t; - if (de->d_name[0] == '.') { - continue; - } - - if (*npids >= *alloc) { - *alloc = *alloc ? *alloc * 2 : 1; - - t = realloc(pids, *alloc * sizeof(*pids)); - if (t == NULL) { - log_error("Failed to (re)allocate memory for pids\n"); - goto dealloc; - } - - pids = t; - } - - pids[*npids] = atoi(de->d_name); - (*npids)++; - } - closedir(dir); - - *ppids = pids; - - return *npids; + DIR *dir = NULL; + struct dirent *de; + char path[PATH_MAX]; + int *pids = *ppids; + + (void) snprintf(path, sizeof(path), "/proc/%d/task", proc->pid); + dir = opendir(path); + if (!dir) { + log_error("Failed to open directory '%s'\n", path); + goto dealloc; + } + + *npids = 0; + while ((de = readdir(dir))) { + int *t; + if (de->d_name[0] == '.') { + continue; + } + if (*npids >= *alloc) { + *alloc = *alloc ? *alloc * 2 : 1; + t = realloc(pids, *alloc * sizeof(*pids)); + if (t == NULL) { + log_error("Failed to (re)allocate memory for pids\n"); + goto dealloc; + } + pids = t; + } + + pids[*npids] = atoi(de->d_name); + (*npids)++; + } + + closedir(dir); + *ppids = pids; + + return (int)*npids; dealloc: - if (dir) { - closedir(dir); - } - free(pids); - *ppids = NULL; - *alloc = *npids = 0; - return -1; + if (dir) { + closedir(dir); + } + + free(pids); + *ppids = NULL; + *alloc = *npids = 0; + return -1; } int upatch_process_attach(struct upatch_process *proc) { - int *pids = NULL, ret; - size_t i, npids = 0, alloc = 0, prevnpids = 0, nattempts; - - if (upatch_process_mem_open(proc, MEM_WRITE) < 0) { - return -1; - } - - for (nattempts = 0; nattempts < MAX_ATTACH_ATTEMPTS; nattempts++) { - ret = process_list_threads(proc, &pids, &npids, &alloc); - if (ret == -1) - goto detach; - - if (nattempts == 0) { - log_debug("Found %lu thread(s), attaching...\n", npids); - } else { - /* - * FIXME(pboldin): This is wrong, amount of threads can - * be the same because some new spawned and some old - * died - */ - if (prevnpids == npids) - break; - - log_debug("Found %lu new thread(s), attaching...\n", - prevnpids - npids); - } - - for (i = prevnpids; i < npids; i++) { - int pid = pids[i]; - - // if (process_has_thread_pid(proc, pid)) { - // log_debug("already have pid %d\n", pid); - // continue; - // } - - ret = upatch_ptrace_attach_thread(proc, pid); - if (ret < 0) - goto detach; - } - - prevnpids = npids; - } - - if (nattempts == MAX_ATTACH_ATTEMPTS) { - log_error("Unable to catch up with process, bailing\n"); - goto detach; - } - - log_debug("Attached to %lu thread(s): %d", npids, pids[0]); - for (i = 1; i < npids; i++) { - log_debug(", %d", pids[i]); - } - log_debug("\n"); - - free(pids); - return 0; + int *pids = NULL; + int ret; + + size_t i; + size_t npids = 0; + size_t alloc = 0; + size_t prevnpids = 0; + size_t nattempts; + + if (upatch_process_mem_open(proc, MEM_WRITE) < 0) { + return -1; + } + + for (nattempts = 0; nattempts < MAX_ATTACH_ATTEMPTS; nattempts++) { + ret = process_list_threads(proc, &pids, &npids, &alloc); + if (ret == -1) { + goto detach; + } + + if (nattempts == 0) { + log_debug("Found %lu thread(s), attaching...\n", npids); + } else { + /* + * FIXME(pboldin): This is wrong, amount of threads can + * be the same because some new spawned and some old + * died + */ + if (prevnpids == npids) { + break; + } + log_debug("Found %lu new thread(s), attaching...\n", + prevnpids - npids); + } + + for (i = prevnpids; i < npids; i++) { + int pid = pids[i]; + + ret = upatch_ptrace_attach_thread(proc, pid); + if ((ret != 0) && (ret != ESRCH)) { + goto detach; + } + } + + prevnpids = npids; + } + + if (nattempts == MAX_ATTACH_ATTEMPTS) { + log_error("Unable to catch up with process, bailing\n"); + goto detach; + } + + log_debug("Attached to %lu thread(s): %d", npids, pids[0]); + for (i = 1; i < npids; i++) { + log_debug(", %d", pids[i]); + } + log_debug("\n"); + + free(pids); + return 0; detach: - upatch_process_detach(proc); - free(pids); - return -1; + upatch_process_detach(proc); + free(pids); + return -1; } void upatch_process_detach(struct upatch_process *proc) { - struct upatch_ptrace_ctx *p, *ptmp; - int status; - pid_t pid; - - if (proc->memfd >= 0 && close(proc->memfd) < 0) { - log_error("Failed to close memfd"); - } - proc->memfd = -1; - - list_for_each_entry_safe(p, ptmp, &proc->ptrace.pctxs, list) { - /** - * If upatch_ptrace_detach(p) return -ESRCH, there are two situations, - * as described below: - * 1. the specified thread does not exist, it means the thread dead - * during the attach processing, so we need to wait for the thread - * to exit; - * 2. the specified thread is not currently being traced by us, - * or is not stopped, so we just ignore it; - * - * We using the running variable of the struct upatch_ptrace_ctx to - * distinguish them: - * 1. if pctx->running = 0, it means the thread is traced by us, we - * will wait for the thread to exit; - * 2. if pctx->running = 1, it means we can not sure about the status of - * the thread, we just ignore it; - */ - if (upatch_ptrace_detach(p) == -ESRCH && !p->running) { - do { - pid = waitpid(p->pid, &status, __WALL); - } while (pid > 0 && !WIFEXITED(status)); - } - // upatch_ptrace_ctx_destroy(p); - } - log_debug("Process detached\n"); + struct upatch_ptrace_ctx *p; + struct upatch_ptrace_ctx *ptmp; + int status; + pid_t pid; + + if (proc->memfd >= 0 && close(proc->memfd) < 0) { + log_error("Failed to close memfd"); + } + proc->memfd = -1; + + list_for_each_entry_safe(p, ptmp, &proc->ptrace.pctxs, list) { + /** + * If upatch_ptrace_detach(p) return -ESRCH, there are two situations, + * as described below: + * 1. the specified thread does not exist, it means the thread dead + * during the attach processing, so we need to wait for the thread + * to exit; + * 2. the specified thread is not currently being traced by us, + * or is not stopped, so we just ignore it; + * + * We using the running variable of the struct upatch_ptrace_ctx to + * distinguish them: + * 1. if pctx->running = 0, it means the thread is traced by us, we + * will wait for the thread to exit; + * 2. if pctx->running = 1, it means we can not sure about the status of + * the thread, we just ignore it; + */ + if (upatch_ptrace_detach(p) == -ESRCH && !p->running) { + do { + pid = waitpid(p->pid, &status, __WALL); + } while (pid > 0 && !WIFEXITED(status)); + } + list_del(&p->list); + free(p); + } + log_debug("Process detached\n"); } static inline struct vm_hole *next_hole(struct vm_hole *hole, - struct list_head *head) + struct list_head *head) { - if (hole == NULL || hole->list.next == head) - return NULL; + if (hole == NULL || hole->list.next == head) { + return NULL; + } - return list_entry(hole->list.next, struct vm_hole, list); + return list_entry(hole->list.next, struct vm_hole, list); } static inline struct vm_hole *prev_hole(struct vm_hole *hole, - struct list_head *head) + struct list_head *head) { - if (hole == NULL || hole->list.prev == head) - return NULL; - - return list_entry(hole->list.prev, struct vm_hole, list); -} + if (hole == NULL || hole->list.prev == head) { + return NULL; + } -static inline unsigned long hole_size(struct vm_hole *hole) -{ - if (hole == NULL) - return 0; - return hole->end - hole->start; + return list_entry(hole->list.prev, struct vm_hole, list); } -int vm_hole_split(struct vm_hole *hole, unsigned long alloc_start, - unsigned long alloc_end) +int vm_hole_split(struct vm_hole *hole, uintptr_t start, uintptr_t end) { - alloc_start = ROUND_DOWN(alloc_start, PAGE_SIZE) - PAGE_SIZE; - alloc_end = ROUND_UP(alloc_end, PAGE_SIZE) + PAGE_SIZE; + uintptr_t new_start = ROUND_DOWN(start, (uintptr_t)PAGE_SIZE) - + (uintptr_t)PAGE_SIZE; + uintptr_t new_end = ROUND_UP(end, (uintptr_t)PAGE_SIZE) + + (uintptr_t)PAGE_SIZE; - if (alloc_start > hole->start) { - struct vm_hole *left = NULL; + if (new_start > hole->start) { + struct vm_hole *left = NULL; - left = malloc(sizeof(*hole)); - if (left == NULL) { - log_error("Failed to malloc for vm hole"); - return -1; - } + left = malloc(sizeof(*hole)); + if (left == NULL) { + log_error("Failed to malloc for vm hole"); + return ENOMEM; + } - left->start = hole->start; - left->end = alloc_start; + left->start = hole->start; + left->end = new_start; - list_add(&left->list, &hole->list); - } + list_add(&left->list, &hole->list); + } - /* Reuse hole pointer as the right hole since it is pointed to by - * the `previous_hole` of some `object_file`. */ - hole->start = alloc_end; - hole->end = hole->end > alloc_end ? hole->end : alloc_end; + /* Reuse hole pointer as the right hole since it is pointed to by + * the `prev_hole` of some `object_file`. */ + hole->start = new_end; + hole->end = hole->end > new_end ? hole->end : new_end; - return 0; + return 0; } +static bool is_vm_hole_suitable(struct obj_vm_area *vma, + struct vm_hole *hole, size_t len) +{ + uintptr_t vma_start = vma->inmem.start; + uintptr_t vma_end = vma->inmem.end; + uintptr_t hole_start = PAGE_ALIGN(hole->start); + uintptr_t hole_end = PAGE_ALIGN(hole->start + len); + + log_debug("vma_start=0x%lx, vma_end=0x%lx, " + "hole_start=0x%lx, hole_end=0x%lx, hole_len=0x%lx\n", + vma_start, vma_end, hole->start, hole->end, hole->len); + if (hole->len < len) { + return false; + } + + if (hole_end < vma_start) { + // hole is on the left side of the vma + if ((vma_start - hole_start) <= MAX_DISTANCE) { + return true; + } + } else if (hole_start > vma_end) { + // hole is on the right side of the vma + if ((hole_end - vma_end) <= MAX_DISTANCE) { + return true; + } + } + + return false; +} /* - * Find region for a patch. Take object's `previous_hole` as a left candidate + * Take object's `prev_hole` as a left candidate * and the next hole as a right candidate. Pace through them until there is * enough space in the hole for the patch. * - * Since holes can be much larger than 2GiB take extra caution to allocate - * patch region inside the (-2GiB, +2GiB) range from the original object. + * Due to relocation constraints, the hole position should be whin 4GB range + * from the obj. + * eg: R_AARCH64_ADR_GOT_PAGE */ -unsigned long object_find_patch_region(struct object_file *obj, size_t memsize, - struct vm_hole **hole) -{ - struct list_head *head = &obj->proc->vmaholes; - struct vm_hole *left_hole = obj->previous_hole; - struct vm_hole *right_hole = next_hole(left_hole, head); - unsigned long max_distance = MAX_DISTANCE; - struct obj_vm_area *sovma; - - unsigned long obj_start, obj_end; - unsigned long region_start = 0, region_end = 0; - - log_debug("Looking for patch region for '%s'...\n", obj->name); - - sovma = list_first_entry(&obj->vma, struct obj_vm_area, list); - obj_start = sovma->inmem.start; - sovma = list_entry(obj->vma.prev, struct obj_vm_area, list); - obj_end = sovma->inmem.end; - - max_distance -= memsize; - - /* TODO carefully check for the holes laying between obj_start and - * obj_end, i.e. just after the executable segment of an executable - */ - while (left_hole != NULL && right_hole != NULL) { - if (right_hole != NULL && - right_hole->start - obj_start > max_distance) - right_hole = NULL; - else if (hole_size(right_hole) > memsize) { - region_start = right_hole->start; - region_end = (right_hole->end - obj_start) <= - max_distance ? - right_hole->end - memsize : - obj_start + max_distance; - *hole = right_hole; - break; - } else - right_hole = next_hole(right_hole, head); - - if (left_hole != NULL && - obj_end - left_hole->end > max_distance) - left_hole = NULL; - else if (hole_size(left_hole) > memsize) { - region_start = (obj_end - left_hole->start) <= - max_distance ? - left_hole->start : - obj_end > max_distance ? - obj_end - max_distance : - 0; - region_end = left_hole->end - memsize; - *hole = left_hole; - break; - } else - left_hole = prev_hole(left_hole, head); - } - - if (region_start == region_end) { - log_error("Cannot find suitable region for patch '%s'\n", obj->name); - return -1UL; - } - - region_start = (region_start >> PAGE_SHIFT) << PAGE_SHIFT; - log_debug("Found patch region for '%s' at 0x%lx\n", obj->name, - region_start); - - return region_start; -} -unsigned long object_find_patch_region_nolimit(struct object_file *obj, size_t memsize, - struct vm_hole **hole) +struct vm_hole *find_patch_region(struct object_file *obj, size_t len) { - struct list_head *head = &obj->proc->vmaholes; - struct vm_hole *left_hole = obj->previous_hole; - struct vm_hole *right_hole = next_hole(left_hole, head); - unsigned long region_start = 0; - - log_debug("Looking for patch region for '%s'...\n", obj->name); - - while (right_hole != NULL) { - if (hole_size(right_hole) > memsize) { - *hole = right_hole; - goto found; - } else - right_hole = next_hole(right_hole, head); - - while (left_hole != NULL) - if (hole_size(left_hole) > memsize) { - *hole = left_hole; - goto found; - } else - left_hole = prev_hole(left_hole, head); - } - - log_error("Cannot find suitable region for patch '%s'\n", obj->name); - return -1UL; -found: - region_start = ((*hole)->start >> PAGE_SHIFT) << PAGE_SHIFT; - log_debug("Found patch region for '%s' 0xat %lx\n", obj->name, - region_start); - - return region_start; + struct list_head *vma_holes = &obj->proc->vma_holes; + + struct obj_vm_area *vma = NULL; + list_for_each_entry(vma, &obj->vma, list) { + struct vm_hole *left_hole = obj->prev_hole; + struct vm_hole *right_hole = NULL; + if (left_hole) { + right_hole = next_hole(left_hole, vma_holes); + } else { + if (!list_empty(vma_holes)) { + right_hole = list_first_entry(vma_holes, struct vm_hole, list); + } + } + + while ((left_hole != NULL) || (right_hole != NULL)) { + if (left_hole != NULL) { + if (is_vm_hole_suitable(vma, left_hole, len)) { + return left_hole; + } + left_hole = prev_hole(left_hole, vma_holes); + } + if (right_hole != NULL) { + if (is_vm_hole_suitable(vma, right_hole, len)) { + return right_hole; + } + right_hole = next_hole(right_hole, vma_holes); + } + } + } + + return NULL; } diff --git a/upatch-manage/upatch-process.h b/upatch-manage/upatch-process.h index 39909db982204fe71773afbd115f77b30000cdc3..e8d5dee0a733bef5e3962f849a440c3e3433be72 100644 --- a/upatch-manage/upatch-process.h +++ b/upatch-manage/upatch-process.h @@ -33,96 +33,101 @@ #define ELFMAG "\177ELF" #define SELFMAG 4 +#ifndef MAX_DISTANCE +#define MAX_DISTANCE (1UL << 32) +#endif + enum { - MEM_READ, - MEM_WRITE, + MEM_READ, + MEM_WRITE, }; struct object_file { - struct list_head list; - struct upatch_process *proc; + struct list_head list; + struct upatch_process *proc; - /* Device the object resides on */ - dev_t dev; - ino_t inode; + /* Device the object resides on */ + dev_t dev; + ino_t inode; - /* Object name (as seen in /proc//maps) */ - char *name; + /* Object name (as seen in /proc//maps) */ + char *name; - /* List of object's VM areas */ - struct list_head vma; + /* List of object's VM areas */ + struct list_head vma; - /* Pointer to the previous hole in the patient's mapping */ - struct vm_hole *previous_hole; + /* Pointer to the previous hole in the patient's mapping */ + struct vm_hole *prev_hole; - /* Pointer to the applied patch list, if any */ - struct list_head applied_patch; - /* The number of applied patch */ - size_t num_applied_patch; + /* Pointer to the applied patch list, if any */ + struct list_head applied_patch; + /* The number of applied patch */ + size_t num_applied_patch; - /* Is that a patch for some object? */ - unsigned int is_patch; + /* Is that a patch for some object? */ + unsigned int is_patch; - /* Is it an ELF or a mmap'ed regular file? */ - unsigned int is_elf; + /* Is it an ELF or a mmap'ed regular file? */ + unsigned int is_elf; }; struct vm_area { - unsigned long start; - unsigned long end; - unsigned long offset; - unsigned int prot; + unsigned long start; + unsigned long end; + unsigned long offset; + unsigned int prot; }; struct vm_hole { - unsigned long start; - unsigned long end; - struct list_head list; + unsigned long start; + unsigned long end; + unsigned long len; + struct list_head list; }; struct obj_vm_area { - struct list_head list; - struct vm_area inmem; + struct list_head list; + struct vm_area inmem; }; struct object_patch { - struct list_head list; - struct upatch_info *uinfo; - struct upatch_info_func *funcs; + struct list_head list; + struct upatch_info *uinfo; + struct object_file *obj; }; struct upatch_process { - /* Pid of target process */ - int pid; + /* Pid of target process */ + int pid; - /* memory fd of /proc//mem */ - int memfd; + /* memory fd of /proc//mem */ + int memfd; - /* /proc//maps FD, also works as lock */ - int fdmaps; + /* /proc//maps FD, also works as lock */ + int fdmaps; - /* Process name */ - char comm[16]; + /* Process name */ + char comm[16]; - /* List of process objects */ - struct list_head objs; - int num_objs; + /* List of process objects */ + struct list_head objs; + int num_objs; - /* List ptrace contexts (one per each thread) */ - struct { - struct list_head pctxs; - } ptrace; + /* List ptrace contexts (one per each thread) */ + struct { + struct list_head pctxs; + } ptrace; - struct { - struct list_head coros; - } coro; + struct { + struct list_head coros; + } coro; - /* List of free VMA areas */ - struct list_head vmaholes; + /* List of free VMA areas */ + struct list_head vma_holes; - // TODO: other base? - /* libc's base address to use as a worksheet */ - unsigned long libc_base; + // TODO: other base? + /* libc's base address to use as a worksheet */ + unsigned long libc_base; }; int upatch_process_init(struct upatch_process *, int); @@ -133,17 +138,14 @@ void upatch_process_print_short(struct upatch_process *); int upatch_process_mem_open(struct upatch_process *, int); -int upatch_process_map_object_files(struct upatch_process *, const char *); +int upatch_process_map_object_files(struct upatch_process *); int upatch_process_attach(struct upatch_process *); void upatch_process_detach(struct upatch_process *proc); -int vm_hole_split(struct vm_hole *, unsigned long, unsigned long); +int vm_hole_split(struct vm_hole *, uintptr_t, uintptr_t); -unsigned long object_find_patch_region(struct object_file *, size_t, - struct vm_hole **); -unsigned long object_find_patch_region_nolimit(struct object_file *, size_t, - struct vm_hole **); +struct vm_hole *find_patch_region(struct object_file *obj, size_t len); #endif diff --git a/upatch-manage/upatch-ptrace.c b/upatch-manage/upatch-ptrace.c index 1309a6ecb7b37b35b1b8653a8b6117b7666b9b03..4ebe03800b64cbcec2d15f928a0ef011cffc4f56 100644 --- a/upatch-manage/upatch-ptrace.c +++ b/upatch-manage/upatch-ptrace.c @@ -25,6 +25,10 @@ #include #include +#ifdef __riscv +/* user_regs_struct defined here */ +#include +#endif #include #include @@ -33,253 +37,262 @@ /* process's memory access */ int upatch_process_mem_read(struct upatch_process *proc, unsigned long src, - void *dst, size_t size) + void *dst, size_t size) { - ssize_t r = pread(proc->memfd, dst, size, (off_t)src); + ssize_t r = pread(proc->memfd, dst, size, (off_t)src); - return r != size ? -1 : 0; + return r != (ssize_t)size ? -1 : 0; } static int upatch_process_mem_write_ptrace(struct upatch_process *proc, - void *src, unsigned long dst, size_t size) + const void *src, unsigned long dst, size_t size) { - int ret; - - while (ROUND_DOWN(size, sizeof(long)) != 0) { - ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, *(unsigned long *)src); - if (ret) { - return -1; - } - dst += sizeof(long); - src += sizeof(long); - size -= sizeof(long); - } - - if (size) { - unsigned long tmp; - - tmp = ptrace(PTRACE_PEEKDATA, proc->pid, dst, NULL); - if (tmp == (unsigned long)-1 && errno) { - return -1; - } - memcpy(&tmp, src, size); - - ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, tmp); - if (ret) { - return -1; - } - } - - return 0; + long ret; + + while (ROUND_DOWN(size, sizeof(long)) != 0) { + ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, *(const unsigned long *)src); + if (ret) { + return -1; + } + dst += sizeof(long); + src += sizeof(long); + size -= sizeof(long); + } + + if (size) { + long tmp; + + tmp = ptrace(PTRACE_PEEKDATA, proc->pid, dst, NULL); + if (tmp == -1 && errno) { + return -1; + } + memcpy(&tmp, src, size); + + ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, tmp); + if (ret) { + return -1; + } + } + + return 0; } -int upatch_process_mem_write(struct upatch_process *proc, void *src, - unsigned long dst, size_t size) +int upatch_process_mem_write(struct upatch_process *proc, const void *src, + unsigned long dst, size_t size) { - static int use_pwrite = 1; - ssize_t w; - - if (use_pwrite) { - w = pwrite(proc->memfd, src, size, (off_t)dst); - } - if (!use_pwrite || (w == -1 && errno == EINVAL)) { - use_pwrite = 0; - return upatch_process_mem_write_ptrace(proc, src, dst, size); - } - - return w != size ? -1 : 0; + static int use_pwrite = 1; + ssize_t w; + + if (use_pwrite) { + w = pwrite(proc->memfd, src, size, (off_t)dst); + } + if (!use_pwrite || (w == -1 && errno == EINVAL)) { + use_pwrite = 0; + return upatch_process_mem_write_ptrace(proc, src, dst, size); + } + + return w != (ssize_t)size ? -1 : 0; } static struct upatch_ptrace_ctx* upatch_ptrace_ctx_alloc( - struct upatch_process *proc) + struct upatch_process *proc) { - struct upatch_ptrace_ctx *p; + struct upatch_ptrace_ctx *p; - p = malloc(sizeof(*p)); - if (!p) { - return NULL; - } + p = malloc(sizeof(*p)); + if (!p) { + return NULL; + } - memset(p, 0, sizeof(*p)); + memset(p, 0, sizeof(*p)); - p->execute_until = 0UL; - p->running = 1; - p->proc = proc; + p->execute_until = 0UL; + p->running = 1; + p->proc = proc; - INIT_LIST_HEAD(&p->list); - list_add(&p->list, &proc->ptrace.pctxs); + INIT_LIST_HEAD(&p->list); + list_add(&p->list, &proc->ptrace.pctxs); - return p; + return p; } int upatch_ptrace_attach_thread(struct upatch_process *proc, int tid) { - struct upatch_ptrace_ctx *pctx = upatch_ptrace_ctx_alloc(proc); - if (pctx == NULL) { - log_error("Failed to alloc ptrace context"); - return -1; - } - - pctx->pid = tid; - log_debug("Attaching to %d...", tid); - - long ret = ptrace(PTRACE_ATTACH, tid, NULL, NULL); - if (ret < 0) { - log_error("Failed to attach thread, pid=%d, ret=%ld\n", tid, ret); - return -1; - } - - do { - int status = 0; - - ret = waitpid(tid, &status, __WALL); - if (ret < 0) { - log_error("Failed to wait thread, tid=%d, ret=%ld\n", tid, ret); - return -1; - } - - /* We are expecting SIGSTOP */ - if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) { - break; - } - - /* If we got SIGTRAP because we just got out of execve, wait - * for the SIGSTOP - */ - if (WIFSTOPPED(status)) { - status = (WSTOPSIG(status) == SIGTRAP) ? 0 : WSTOPSIG(status); - } else if (WIFSIGNALED(status)) { - /* Resend signal */ - status = WTERMSIG(status); - } - - ret = ptrace(PTRACE_CONT, tid, NULL, (void *)(uintptr_t)status); - if (ret < 0) { - log_error("Failed to continue thread, tid=%d, ret=%ld\n", tid, ret); - return -1; - } - } while (1); - - pctx->running = 0; - - log_debug("OK\n"); - return 0; + struct upatch_ptrace_ctx *pctx = upatch_ptrace_ctx_alloc(proc); + if (pctx == NULL) { + log_error("Failed to alloc ptrace context"); + return ENOMEM; + } + + pctx->pid = tid; + log_debug("Attaching to %d...", tid); + + long ret = ptrace(PTRACE_ATTACH, tid, NULL, NULL); + if (ret < 0) { + log_error("Failed to attach thread, pid=%d, ret=%ld\n", tid, ret); + return errno; + } + + do { + int status = 0; + + ret = waitpid(tid, &status, __WALL); + if (ret < 0) { + log_error("Failed to wait thread, tid=%d, ret=%ld\n", tid, ret); + return errno; + } + + /* We are expecting SIGSTOP */ + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) { + break; + } + + /* If we got SIGTRAP because we just got out of execve, wait + * for the SIGSTOP + */ + if (WIFSTOPPED(status)) { + status = (WSTOPSIG(status) == SIGTRAP) ? 0 : WSTOPSIG(status); + } else if (WIFSIGNALED(status)) { + /* Resend signal */ + status = WTERMSIG(status); + } + + ret = ptrace(PTRACE_CONT, tid, NULL, (void *)(uintptr_t)status); + if (ret < 0) { + log_error("Failed to continue thread, tid=%d, ret=%ld\n", tid, ret); + return errno; + } + } while (1); + + pctx->running = 0; + + log_debug("OK\n"); + return 0; } int wait_for_stop(struct upatch_ptrace_ctx *pctx, const void *data) { - int ret, status = 0, pid = (int)(uintptr_t)data ?: pctx->pid; - log_debug("wait_for_stop(pctx->pid=%d, pid=%d)\n", pctx->pid, pid); - - while (1) { - ret = ptrace(PTRACE_CONT, pctx->pid, NULL, (void *)(uintptr_t)status); - if (ret < 0) { - log_error("Cannot start tracee %d, ret=%d\n", pctx->pid, ret); - return -1; - } - - ret = waitpid(pid, &status, __WALL); - if (ret < 0) { - log_error("Cannot wait tracee %d, ret=%d\n", pid, ret); - return -1; - } - - if (WIFSTOPPED(status)) { - if (WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTRAP) { - break; - } - status = WSTOPSIG(status); - continue; - } - - status = WIFSIGNALED(status) ? WTERMSIG(status) : 0; - } - - return 0; + long ret; + + int status = 0; + int pid = (int)(uintptr_t)data ?: pctx->pid; + log_debug("wait_for_stop(pctx->pid=%d, pid=%d)\n", pctx->pid, pid); + + while (1) { + ret = ptrace(PTRACE_CONT, pctx->pid, NULL, (void *)(uintptr_t)status); + if (ret < 0) { + log_error("Cannot start tracee %d, ret=%ld\n", pctx->pid, ret); + return -1; + } + + ret = waitpid(pid, &status, __WALL); + if (ret < 0) { + log_error("Cannot wait tracee %d, ret=%ld\n", pid, ret); + return -1; + } + + if (WIFSTOPPED(status)) { + if (WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTRAP) { + break; + } + status = WSTOPSIG(status); + continue; + } + + status = WIFSIGNALED(status) ? WTERMSIG(status) : 0; + } + + return 0; } int upatch_ptrace_detach(struct upatch_ptrace_ctx *pctx) { - if (!pctx->pid) { - return 0; - } - - log_debug("Detaching from %d...", pctx->pid); - long ret = ptrace(PTRACE_DETACH, pctx->pid, NULL, NULL); - if (ret < 0) { - log_error("Failed to detach from process, pid=%d, ret=%ld\n", pctx->pid, ret); - return -errno; - } - log_debug("OK\n"); - - pctx->running = 1; - pctx->pid = 0; - return 0; + if (!pctx->pid) { + return 0; + } + + log_debug("Detaching from %d...", pctx->pid); + long ret = ptrace(PTRACE_DETACH, pctx->pid, NULL, NULL); + if (ret < 0) { + log_error("Failed to detach from process, pid=%d, ret=%ld\n", pctx->pid, ret); + return -errno; + } + log_debug("OK\n"); + + pctx->running = 1; + pctx->pid = 0; + return 0; } -int upatch_execute_remote(struct upatch_ptrace_ctx *pctx, - const unsigned char *code, size_t codelen, - struct user_regs_struct *pregs) +long upatch_execute_remote(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs) { - return upatch_arch_execute_remote_func( - pctx, code, codelen, pregs, wait_for_stop, NULL); + return upatch_arch_execute_remote_func(pctx, code, codelen, pregs, + wait_for_stop, NULL); } unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx *pctx, - unsigned long addr, size_t length, int prot, - int flags, int fd, off_t offset) + unsigned long addr, size_t length, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long offset) { - int ret; - unsigned long res = 0; - - log_debug("mmap_remote: 0x%lx+%lx, %x, %x, %d, %lx\n", addr, length, - prot, flags, fd, offset); - ret = upatch_arch_syscall_remote(pctx, __NR_mmap, (unsigned long)addr, - length, prot, flags, fd, offset, &res); - if (ret < 0) { - return 0; - } - if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { - errno = -(long)res; - return 0; - } - return res; + long ret; + unsigned long res = 0; + + log_debug("mmap_remote: 0x%lx+%lx, %lx, %lx, %lu, %lx\n", addr, length, + prot, flags, fd, offset); + ret = upatch_arch_syscall_remote(pctx, __NR_mmap, + (unsigned long)addr, length, prot, flags, fd, offset, &res); + if (ret < 0) { + return 0; + } + + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { + errno = -(int)res; + return 0; + } + + return res; } int upatch_mprotect_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, - size_t length, int prot) + size_t length, unsigned long prot) { - int ret; - unsigned long res; - - log_debug("mprotect_remote: 0x%lx+%lx\n", addr, length); - ret = upatch_arch_syscall_remote(pctx, __NR_mprotect, - (unsigned long)addr, length, prot, 0, - 0, 0, &res); - if (ret < 0) - return -1; - if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { - errno = -(long)res; - return -1; - } - - return 0; + long ret; + unsigned long res; + + log_debug("mprotect_remote: 0x%lx+%lx\n", addr, length); + ret = upatch_arch_syscall_remote(pctx, __NR_mprotect, + (unsigned long)addr, length, prot, 0, 0, 0, &res); + if (ret < 0) { + return -1; + } + + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { + errno = -(int)res; + return -1; + } + + return 0; } int upatch_munmap_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, - size_t length) + size_t length) { - int ret; - unsigned long res; - - log_debug("munmap_remote: 0x%lx+%lx\n", addr, length); - ret = upatch_arch_syscall_remote(pctx, __NR_munmap, (unsigned long)addr, - length, 0, 0, 0, 0, &res); - if (ret < 0) - return -1; - if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { - errno = -(long)res; - return -1; - } - return 0; + long ret; + unsigned long res; + + log_debug("munmap_remote: 0x%lx+%lx\n", addr, length); + ret = upatch_arch_syscall_remote(pctx, __NR_munmap, + (unsigned long)addr, length, 0, 0, 0, 0, &res); + if (ret < 0) { + return -1; + } + + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { + errno = -(int)res; + return -1; + } + + return 0; } diff --git a/upatch-manage/upatch-ptrace.h b/upatch-manage/upatch-ptrace.h index 0c8843458c6686cc1e107ea9d29df00098e0a5a0..f2503cfd98d246dc8d86157b86a7c9ff385a2627 100644 --- a/upatch-manage/upatch-ptrace.h +++ b/upatch-manage/upatch-ptrace.h @@ -22,6 +22,9 @@ #define __UPATCH_PTRACE__ #include +#ifdef __riscv +#include +#endif #include "upatch-process.h" #include "list.h" @@ -30,21 +33,21 @@ #define MAX_ERRNO 4095 struct upatch_ptrace_ctx { - int pid; - int running; - unsigned long execute_until; - struct upatch_process *proc; - struct list_head list; + int pid; + int running; + unsigned long execute_until; + struct upatch_process *proc; + struct list_head list; }; #define proc2pctx(proc) \ - list_first_entry(&(proc)->ptrace.pctxs, struct upatch_ptrace_ctx, list) + list_first_entry(&(proc)->ptrace.pctxs, struct upatch_ptrace_ctx, list) int upatch_process_mem_read(struct upatch_process *proc, unsigned long src, - void *dst, size_t size); + void *dst, size_t size); -int upatch_process_mem_write(struct upatch_process *, void *, unsigned long, - size_t); +int upatch_process_mem_write(struct upatch_process *, const void *, + unsigned long, size_t); int upatch_ptrace_attach_thread(struct upatch_process *, int); @@ -54,31 +57,36 @@ int wait_for_stop(struct upatch_ptrace_ctx *, const void *); void copy_regs(struct user_regs_struct *, struct user_regs_struct *); -int upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, - const unsigned char *code, size_t codelen, - struct user_regs_struct *pregs, - int (*func)(struct upatch_ptrace_ctx *pctx, - const void *data), - const void *data); +long upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, const void *data), + const void *data); -int upatch_arch_syscall_remote(struct upatch_ptrace_ctx *, int, unsigned long, - unsigned long, unsigned long, unsigned long, - unsigned long, unsigned long, unsigned long *); +long upatch_arch_syscall_remote(struct upatch_ptrace_ctx *, int, unsigned long, + unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long *); -unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx *, unsigned long, - size_t, int, int, int, off_t); +unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx *pctx, + unsigned long addr, size_t length, unsigned long prot, + unsigned long flags, unsigned long fd, unsigned long offset); -int upatch_mprotect_remote(struct upatch_ptrace_ctx *, unsigned long, size_t, - int); +int upatch_mprotect_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, + size_t length, unsigned long prot); int upatch_munmap_remote(struct upatch_ptrace_ctx *, unsigned long, size_t); -int upatch_execute_remote(struct upatch_ptrace_ctx *, const unsigned char *, - size_t, struct user_regs_struct *); +long upatch_execute_remote(struct upatch_ptrace_ctx *, + const unsigned char *, size_t, struct user_regs_struct *); + +size_t get_origin_insn_len(void); +size_t get_upatch_insn_len(void); +size_t get_upatch_addr_len(void); -size_t get_origin_insn_len(); -size_t get_upatch_insn_len(); -size_t get_upatch_addr_len(); -unsigned long get_new_insn(struct object_file *, unsigned long, unsigned long); +#ifdef __riscv +unsigned long get_new_insn(unsigned long old_addr, unsigned long new_addr); +#else +unsigned long get_new_insn(void); +#endif #endif diff --git a/upatch-manage/upatch-relocation.c b/upatch-manage/upatch-relocation.c index a4200b6980fe638844483742b8498caa5746303c..e9b79dd86173e7c191befafdcd62b1ad0157c7f1 100644 --- a/upatch-manage/upatch-relocation.c +++ b/upatch-manage/upatch-relocation.c @@ -25,32 +25,36 @@ int apply_relocations(struct upatch_elf *uelf) { - unsigned int i; - int err = 0; - - /* Now do relocations. */ - for (i = 1; i < uelf->info.hdr->e_shnum; i++) { - unsigned int infosec = uelf->info.shdrs[i].sh_info; - const char *name = - uelf->info.shstrtab + uelf->info.shdrs[i].sh_name; - - /* Not a valid relocation section? */ - if (infosec >= uelf->info.hdr->e_shnum) - continue; - - /* Don't bother with non-allocated sections */ - if (!(uelf->info.shdrs[infosec].sh_flags & SHF_ALLOC)) - continue; - - log_debug("Relocate '%s'\n", name); - if (uelf->info.shdrs[i].sh_type == SHT_REL) { - return -EPERM; - } else if (uelf->info.shdrs[i].sh_type == SHT_RELA) { - err = apply_relocate_add(uelf, uelf->index.sym, i); - } - - if (err < 0) - break; - } - return err; -} \ No newline at end of file + unsigned int i; + int err = 0; + + /* Now do relocations. */ + for (i = 1; i < uelf->info.hdr->e_shnum; i++) { + unsigned int infosec = uelf->info.shdrs[i].sh_info; + const char *name = uelf->info.shstrtab + uelf->info.shdrs[i].sh_name; + + /* Not a valid relocation section? */ + if (infosec >= uelf->info.hdr->e_shnum) { + continue; + } + + /* Don't bother with non-allocated sections */ + if (!(uelf->info.shdrs[infosec].sh_flags & SHF_ALLOC)) { + continue; + } + + log_debug("Relocate section '%s':\n", name); + if (uelf->info.shdrs[i].sh_type == SHT_REL) { + return -EPERM; + } else if (uelf->info.shdrs[i].sh_type == SHT_RELA) { + err = apply_relocate_add(uelf, uelf->index.sym, i); + } + log_debug("\n"); + + if (err < 0) { + break; + } + } + + return err; +} diff --git a/upatch-manage/upatch-relocation.h b/upatch-manage/upatch-relocation.h index bbb19626e8e6bdb6eeef6e55f799df80b960db30..804656ee57a74515a13e4065896357c440f32542 100644 --- a/upatch-manage/upatch-relocation.h +++ b/upatch-manage/upatch-relocation.h @@ -39,4 +39,4 @@ int apply_relocate_add(struct upatch_elf *, unsigned int, unsigned int); int apply_relocations(struct upatch_elf *); -#endif \ No newline at end of file +#endif diff --git a/upatch-manage/upatch-resolve.c b/upatch-manage/upatch-resolve.c index 0e992da8360ea3a10acfba24a12388886479f23d..53debe6018ffba8d037332ae27db1659f8c3d12d 100644 --- a/upatch-manage/upatch-resolve.c +++ b/upatch-manage/upatch-resolve.c @@ -50,11 +50,10 @@ static unsigned long resolve_rela_dyn(struct upatch_elf *uelf, * some rela don't have the symbol index, use the symbol's value and * rela's addend to find the symbol. for example, R_X86_64_IRELATIVE. */ - if (rela_dyn[i].r_addend != patch_sym->st_value) { + if (rela_dyn[i].r_addend != (long)patch_sym->st_value) { continue; } - } - else { + } else { char *sym_name = relf->dynstrtab + dynsym[sym_idx].st_name; char *sym_splitter = NULL; @@ -100,8 +99,9 @@ static unsigned long resolve_rela_plt(struct upatch_elf *uelf, for (Elf64_Xword i = 0; i < rela_plt_shdr->sh_size / sizeof(GElf_Rela); i++) { unsigned long sym_idx = GELF_R_SYM(rela_plt[i].r_info); unsigned long sym_type = GELF_ST_TYPE(dynsym[sym_idx].st_info); - - if (sym_type != STT_FUNC && sym_type != STT_TLS) { + if ((sym_type == STT_NOTYPE) && + (sym_type != STT_FUNC) && + (sym_type != STT_TLS)) { continue; } @@ -110,7 +110,7 @@ static unsigned long resolve_rela_plt(struct upatch_elf *uelf, * some rela don't have the symbol index, use the symbol's value and * rela's addend to find the symbol. for example, R_X86_64_IRELATIVE. */ - if (rela_plt[i].r_addend != patch_sym->st_value) { + if (rela_plt[i].r_addend != (long)patch_sym->st_value) { continue; } } else { @@ -140,7 +140,7 @@ static unsigned long resolve_rela_plt(struct upatch_elf *uelf, } static unsigned long resolve_dynsym(struct upatch_elf *uelf, - struct object_file *obj, const char *name, GElf_Sym *patch_sym) + struct object_file *obj, const char *name) { unsigned long elf_addr = 0; struct running_elf *relf = uelf->relf; @@ -178,8 +178,7 @@ static unsigned long resolve_dynsym(struct upatch_elf *uelf, return elf_addr; } -static unsigned long resolve_sym(struct upatch_elf *uelf, - struct object_file *obj, const char *name, GElf_Sym *patch_sym) +static unsigned long resolve_sym(struct upatch_elf *uelf, const char *name) { unsigned long elf_addr = 0; struct running_elf *relf = uelf->relf; @@ -217,7 +216,7 @@ static unsigned long resolve_sym(struct upatch_elf *uelf, } static unsigned long resolve_patch_sym(struct upatch_elf *uelf, - struct object_file *obj, const char *name, GElf_Sym *patch_sym) + const char *name, GElf_Sym *patch_sym) { unsigned long elf_addr = 0; struct running_elf *relf = uelf->relf; @@ -255,32 +254,28 @@ static unsigned long resolve_symbol(struct upatch_elf *uelf, * Approach 3 is more general, but difficulty to implement. */ - /* resolve from got */ - elf_addr = resolve_rela_dyn(uelf, obj, name, &patch_sym); - - /* resolve from plt */ + /* resolve from plt */ + elf_addr = resolve_rela_plt(uelf, obj, name, &patch_sym); + /* resolve from got */ if (!elf_addr) { - elf_addr = resolve_rela_plt(uelf, obj, name, &patch_sym); + elf_addr = resolve_rela_dyn(uelf, obj, name, &patch_sym); } - - /* resolve from dynsym */ + /* resolve from dynsym */ if (!elf_addr) { - elf_addr = resolve_dynsym(uelf, obj, name, &patch_sym); + elf_addr = resolve_dynsym(uelf, obj, name); } - - /* resolve from sym */ + /* resolve from sym */ if (!elf_addr) { - elf_addr = resolve_sym(uelf, obj, name, &patch_sym); + elf_addr = resolve_sym(uelf, name); } - - /* resolve from patch sym */ + /* resolve from patch sym */ if (!elf_addr) { - elf_addr = resolve_patch_sym(uelf, obj, name, &patch_sym); + elf_addr = resolve_patch_sym(uelf, name, &patch_sym); } - if (!elf_addr) { log_error("Cannot resolve symbol '%s'\n", name); } + return elf_addr; } @@ -296,40 +291,41 @@ int simplify_symbols(struct upatch_elf *uelf, struct object_file *obj) const char *name; if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && - sym[i].st_shndx < uelf->info.hdr->e_shnum) - name = uelf->info.shstrtab + uelf->info.shdrs[sym[i].st_shndx].sh_name; - else + sym[i].st_shndx < uelf->info.hdr->e_shnum) { + name = uelf->info.shstrtab + + uelf->info.shdrs[sym[i].st_shndx].sh_name; + } else { name = uelf->strtab + sym[i].st_name; - + } switch (sym[i].st_shndx) { - case SHN_COMMON: - log_debug("Unsupported common symbol '%s'\n", name); - ret = -ENOEXEC; - break; - case SHN_ABS: - break; - case SHN_UNDEF: - elf_addr = resolve_symbol(uelf, obj, name, sym[i]); - if (!elf_addr) { + case SHN_COMMON: + log_debug("Unsupported common symbol '%s'\n", name); ret = -ENOEXEC; + break; + case SHN_ABS: + break; + case SHN_UNDEF: + elf_addr = resolve_symbol(uelf, obj, name, sym[i]); + if (!elf_addr) { + ret = -ENOEXEC; + } + sym[i].st_value = elf_addr; + log_debug("Resolved symbol '%s' at 0x%lx\n", + name, (unsigned long)sym[i].st_value); + break; + case SHN_LIVEPATCH: + sym[i].st_value += uelf->relf->load_bias; + log_debug("Resolved livepatch symbol '%s' at 0x%lx\n", + name, (unsigned long)sym[i].st_value); + break; + default: + /* use real address to calculate secbase */ + secbase = uelf->info.shdrs[sym[i].st_shndx].sh_addralign; + sym[i].st_value += secbase; + log_debug("Symbol '%s' at 0x%lx\n", + name, (unsigned long)sym[i].st_value); + break; } - sym[i].st_value = elf_addr; - log_debug("Resolved symbol '%s' at 0x%lx\n", - name, (unsigned long)sym[i].st_value); - break; - case SHN_LIVEPATCH: - sym[i].st_value += uelf->relf->load_bias; - log_debug("Resolved livepatch symbol '%s' at 0x%lx\n", - name, (unsigned long)sym[i].st_value); - break; - default: - /* use real address to calculate secbase */ - secbase = uelf->info.shdrs[sym[i].st_shndx].sh_addralign; - sym[i].st_value += secbase; - log_debug("Symbol '%s' at 0x%lx\n", - name, (unsigned long)sym[i].st_value); - break; - } } return ret; diff --git a/upatch-manage/upatch-resolve.h b/upatch-manage/upatch-resolve.h index 324c49e7c626586f361272b52d1f5456d94b1e8b..62efc13b1abc01451c84cf461421cfdf50a05f94 100644 --- a/upatch-manage/upatch-resolve.h +++ b/upatch-manage/upatch-resolve.h @@ -29,16 +29,18 @@ /* jmp table, solve limit for the jmp instruction, Used for both PLT/GOT */ struct upatch_jmp_table_entry; -unsigned int get_jmp_table_entry(); +unsigned int get_jmp_table_entry(void); unsigned long insert_plt_table(struct upatch_elf *, struct object_file *, - unsigned long, unsigned long); + unsigned long, unsigned long); unsigned long insert_got_table(struct upatch_elf *, struct object_file *, - unsigned long, unsigned long); + unsigned long, unsigned long); -unsigned long search_insert_plt_table(struct upatch_elf *, unsigned long, - unsigned long); +unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long, unsigned long); +unsigned long search_insert_plt_table(struct upatch_elf *, + unsigned long, unsigned long); int simplify_symbols(struct upatch_elf *, struct object_file *); -#endif \ No newline at end of file +#endif diff --git a/upatch-manage/upatch-stack-check.c b/upatch-manage/upatch-stack-check.c new file mode 100644 index 0000000000000000000000000000000000000000..474d857983b2019cf02fc1b2b66099ac65da285d --- /dev/null +++ b/upatch-manage/upatch-stack-check.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include + +#include "upatch-elf.h" +#include "upatch-stack-check.h" +#include "upatch-ptrace.h" +#include "upatch-common.h" +#include "log.h" + +static int stack_check(struct upatch_info *uinfo, unsigned long pc, upatch_action_t action) +{ + unsigned long start; + unsigned long end; + + for (size_t i = 0; i < uinfo->changed_func_num; i++) { + struct upatch_func_addr addr = uinfo->funcs[i].addr; + + if (action == ACTIVE) { + start = addr.old_addr; + end = addr.old_addr + addr.old_size; + } else if (action == DEACTIVE) { + start = addr.new_addr; + end = addr.new_addr + addr.new_size; + } else { + log_error("Unknown upatch action\n"); + return -1; + } + if (pc >= start && pc <= end) { + log_error("Failed to check stack, running function: %s\n", + uinfo->funcs[i].name); + return -1; + } + } + return 0; +} + +static unsigned long *stack_alloc(size_t *size) +{ + struct rlimit rl; + unsigned long *stack = NULL; + + if (getrlimit(RLIMIT_STACK, &rl) != 0) { + log_error("Failed to get system stack size config\n"); + return 0; + } + + *size = rl.rlim_cur; + stack = (unsigned long *)malloc(*size); + if (stack == NULL) { + log_error("Failed to malloc stack\n"); + } + + return stack; +} + +static size_t read_stack(struct upatch_process *proc, + unsigned long *stack, size_t size, unsigned long sp) +{ + return (size_t)pread(proc->memfd, (void *)stack, size, (off_t)sp); +} + +static int stack_check_each_pid(struct upatch_process *proc, + struct upatch_info *uinfo, int pid, upatch_action_t action) +{ + unsigned long sp, pc; + unsigned long *stack = NULL; + size_t stack_size = 0; + int ret = 0; + + if (upatch_arch_reg_init(pid, &sp, &pc) < 0) { + return -1; + } + ret = stack_check(uinfo, pc, action); + if (ret < 0) { + return ret; + } + + stack = stack_alloc(&stack_size); + if (stack == NULL) { + return -1; + } + + stack_size = read_stack(proc, stack, stack_size, sp); + log_debug("[%d] Stack size %lu, region [0x%lx, 0x%lx]\n", + pid, stack_size, sp, sp + stack_size); + + for (size_t i = 0; i < stack_size / sizeof(*stack); i++) { + if (stack[i] == 0 || stack[i] == -1UL) { + continue; + } + + ret = stack_check(uinfo, stack[i], action); + if (ret < 0) { + goto free; + } + } +free: + free(stack); + return ret; +} + +int upatch_stack_check(struct upatch_info *uinfo, struct upatch_process *proc, + upatch_action_t action) +{ + struct upatch_ptrace_ctx *pctx; + struct timeval start, end; + + if (gettimeofday(&start, NULL) < 0) { + log_error("Failed to get stack check start time\n"); + } + + list_for_each_entry(pctx, &proc->ptrace.pctxs, list) { + if (stack_check_each_pid(proc, uinfo, pctx->pid, action) < 0) { + return -EBUSY; + } + } + + if (gettimeofday(&end, NULL) < 0) { + log_error("Failed to get stack check end time\n"); + } + + log_debug("Stack check time %ld microseconds\n", + get_microseconds(&start, &end)); + return 0; +} diff --git a/upatch-manage/upatch-stack-check.h b/upatch-manage/upatch-stack-check.h new file mode 100644 index 0000000000000000000000000000000000000000..49be6e3cd21661df048d6d72efc39df3f52cdd51 --- /dev/null +++ b/upatch-manage/upatch-stack-check.h @@ -0,0 +1,17 @@ +#ifndef __UPATCH_STACK_CHECK_H +#define __UPATCH_STACK_CHECK_H + +#include "upatch-elf.h" +#include "upatch-process.h" + +#define STACK_CHECK_RETRY_TIMES 3 + +typedef enum { + ACTIVE, + DEACTIVE, +} upatch_action_t; + +int upatch_arch_reg_init(int pid, unsigned long *sp, unsigned long *pc); +int upatch_stack_check(struct upatch_info *uinfo, struct upatch_process *proc, + upatch_action_t action); +#endif diff --git a/upatchd/Cargo.toml b/upatchd/Cargo.toml deleted file mode 100644 index fea08599ca28e09ed6dcaa2c60f8dd400e41d968..0000000000000000000000000000000000000000 --- a/upatchd/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "upatchd" -version = "1.2.1" -authors = ["renoseven "] -description = "Syscare upatch daemon" -license = "MulanPSL-2.0" -edition = "2018" -rust-version = "1.60" -build = "build.rs" - -[dependencies] -syscare-common = { package = "syscare-common", path = "../syscare-common" } -anyhow = { version = "1.0" } -clap = { version = "3.2", features = ["cargo", "derive"] } -daemonize = { version = "0.5" } -flexi_logger = { version = "0.24", features = ["compress"] } -indexmap = { version = "1.9", features = ["serde"] } -jsonrpc-core = { version = "18.0" } -jsonrpc-derive = { version = "18.0" } -jsonrpc-ipc-server = { version = "18.0" } -log = { version = "0.4" } -nix = { version = "0.26" } -object = { version = "0.29" } -serde = { version = "1.0", features = ["derive"] } -serde_yaml = { version = "0.8" } -signal-hook = { version = "0.3" } diff --git a/upatchd/src/args.rs b/upatchd/src/args.rs deleted file mode 100644 index 0b9029b1ef44a4b776a653c138579f2ef53b36b0..0000000000000000000000000000000000000000 --- a/upatchd/src/args.rs +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::PathBuf; - -use anyhow::Result; -use clap::{AppSettings, ColorChoice, Parser}; -use log::LevelFilter; - -use syscare_common::fs; - -use super::{DAEMON_ABOUT, DAEMON_NAME, DAEMON_VERSION}; - -const DEFAULT_CONFIG_DIR: &str = "/etc/syscare"; -const DEFAULT_WORK_DIR: &str = "/var/run/syscare"; -const DEFAULT_LOG_DIR: &str = "/var/log/syscare"; -const DEFAULT_LOG_LEVEL: &str = "info"; - -#[derive(Debug, Clone, Parser)] -#[clap( - bin_name = DAEMON_NAME, - version = DAEMON_VERSION, - about = DAEMON_ABOUT, - color(ColorChoice::Never), - global_setting(AppSettings::DeriveDisplayOrder), - term_width(120), -)] - -pub struct Arguments { - /// Run as a daemon - #[clap(short, long)] - pub daemon: bool, - - /// Daemon config directory - #[clap(long, default_value=DEFAULT_CONFIG_DIR)] - pub config_dir: PathBuf, - - /// Daemon working directory - #[clap(long, default_value=DEFAULT_WORK_DIR)] - pub work_dir: PathBuf, - - #[clap(long, default_value=DEFAULT_LOG_DIR)] - /// Daemon logging directory - pub log_dir: PathBuf, - - /// Set the logging level ("trace"|"debug"|"info"|"warn"|"error") - #[clap(short, long, default_value=DEFAULT_LOG_LEVEL)] - pub log_level: LevelFilter, -} - -impl Arguments { - pub fn new() -> Result { - Self::parse().normalize_path() - } - - fn normalize_path(mut self) -> Result { - self.config_dir = fs::normalize(&self.config_dir)?; - self.work_dir = fs::normalize(self.work_dir)?; - self.log_dir = fs::normalize(&self.log_dir)?; - - Ok(self) - } -} - -impl std::fmt::Display for Arguments { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{:?}", self)) - } -} diff --git a/upatchd/src/config.rs b/upatchd/src/config.rs deleted file mode 100644 index 125770d27606c9c31e466309e82f83cb5d5eaac1..0000000000000000000000000000000000000000 --- a/upatchd/src/config.rs +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::Path; - -use anyhow::{anyhow, Result}; -use serde::{Deserialize, Serialize}; -use syscare_common::fs; - -use crate::hijacker::HijackerConfig; - -const DEFAULT_SOCKET_UID: u32 = 0; -const DEFAULT_SOCKET_GID: u32 = 0; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct SocketConfig { - pub uid: u32, - pub gid: u32, -} - -impl Default for SocketConfig { - fn default() -> Self { - Self { - uid: DEFAULT_SOCKET_UID, - gid: DEFAULT_SOCKET_GID, - } - } -} - -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct DaemonConfig { - pub socket: SocketConfig, -} - -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct Config { - pub daemon: DaemonConfig, - pub hijacker: HijackerConfig, -} - -impl Config { - pub fn parse>(path: P) -> Result { - let config_path = path.as_ref(); - let instance = serde_yaml::from_reader(fs::open_file(config_path)?) - .map_err(|_| anyhow!("Failed to parse config {}", config_path.display()))?; - - Ok(instance) - } - - pub fn write>(&self, path: P) -> Result<()> { - let config_path = path.as_ref(); - let config_file = fs::create_file(config_path)?; - serde_yaml::to_writer(config_file, self) - .map_err(|_| anyhow!("Failed to write config {}", config_path.display()))?; - - Ok(()) - } -} - -#[test] -fn test() -> Result<()> { - use anyhow::{ensure, Context}; - use std::path::PathBuf; - - let tmp_file = PathBuf::from("/tmp/upatchd.yaml"); - - let orig_cfg = Config::default(); - println!("{:#?}", orig_cfg); - - orig_cfg - .write(&tmp_file) - .context("Failed to write config")?; - - let new_cfg = Config::parse(tmp_file).context("Failed to read config")?; - println!("{:#?}", new_cfg); - - ensure!(orig_cfg == new_cfg, "Config does not match"); - - Ok(()) -} diff --git a/upatchd/src/hijacker/config.rs b/upatchd/src/hijacker/config.rs deleted file mode 100644 index 5f97fb189bac71cfeb04e670382937910b2d28ff..0000000000000000000000000000000000000000 --- a/upatchd/src/hijacker/config.rs +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::PathBuf; - -use indexmap::{indexmap, IndexMap}; -use serde::{Deserialize, Serialize}; - -const CC_BINARY: &str = "/usr/bin/cc"; -const CXX_BINARY: &str = "/usr/bin/c++"; -const GCC_BINARY: &str = "/usr/bin/gcc"; -const GXX_BINARY: &str = "/usr/bin/g++"; -const AS_BINARY: &str = "/usr/bin/as"; - -const CC_HIJACKER: &str = "/usr/libexec/syscare/cc-hijacker"; -const CXX_HIJACKER: &str = "/usr/libexec/syscare/c++-hijacker"; -const GCC_HIJACKER: &str = "/usr/libexec/syscare/gcc-hijacker"; -const GXX_HIJACKER: &str = "/usr/libexec/syscare/g++-hijacker"; -const AS_HIJACKER: &str = "/usr/libexec/syscare/as-hijacker"; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct HijackerConfig { - pub mapping: IndexMap, -} - -impl Default for HijackerConfig { - fn default() -> Self { - Self { - mapping: indexmap! { - PathBuf::from(CC_BINARY) => PathBuf::from(CC_HIJACKER), - PathBuf::from(CXX_BINARY) => PathBuf::from(CXX_HIJACKER), - PathBuf::from(GCC_BINARY) => PathBuf::from(GCC_HIJACKER), - PathBuf::from(GXX_BINARY) => PathBuf::from(GXX_HIJACKER), - PathBuf::from(AS_BINARY) => PathBuf::from(AS_HIJACKER), - }, - } - } -} diff --git a/upatchd/src/hijacker/elf_resolver.rs b/upatchd/src/hijacker/elf_resolver.rs deleted file mode 100644 index 9b62b0028e62415afc65310c0c977724e18ced6d..0000000000000000000000000000000000000000 --- a/upatchd/src/hijacker/elf_resolver.rs +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{fs, path::PathBuf}; - -use anyhow::Result; -use object::{NativeFile, Object, ObjectSymbol}; -use syscare_common::{ffi::OsStrExt, os, process::Command}; - -const LDD_BIN: &str = "ldd"; - -pub struct ElfResolver<'a> { - elf: NativeFile<'a, &'a [u8]>, -} - -impl<'a> ElfResolver<'a> { - pub fn new(data: &'a [u8]) -> Result { - Ok(Self { - elf: NativeFile::parse(data)?, - }) - } -} - -impl ElfResolver<'_> { - pub fn dependencies(&self) -> Result> { - let mut paths = Vec::new(); - let output = Command::new(LDD_BIN) - .arg(os::process::path()) - .run_with_output()?; - - output.exit_ok()?; - - let lines = output.stdout.lines(); - for line in lines { - let words = line.split_whitespace().collect::>(); - if let Some(path) = words.get(2) { - if let Ok(path) = fs::canonicalize(path) { - paths.push(path); - } - } - } - - Ok(paths) - } - - pub fn find_symbol_addr(&self, symbol_name: &str) -> Result> { - let symbols = self.elf.dynamic_symbols(); - for sym in symbols { - if let Ok(sym_name) = sym.name() { - if sym_name == symbol_name { - return Ok(Some(sym.address())); - } - } - } - - Ok(None) - } -} diff --git a/upatchd/src/hijacker/ioctl.rs b/upatchd/src/hijacker/ioctl.rs deleted file mode 100644 index b1879791526c475a7e1552e98a7e43e9ba358452..0000000000000000000000000000000000000000 --- a/upatchd/src/hijacker/ioctl.rs +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{fs::File, io::Write, os::unix::io::AsRawFd, path::Path}; - -use anyhow::{anyhow, Result}; -use nix::{ioctl_none, ioctl_write_ptr, libc::PATH_MAX}; -use syscare_common::{ffi::OsStrExt, fs}; - -const KMOD_IOCTL_MAGIC: u16 = 0xE5; - -ioctl_write_ptr!( - ioctl_enable_hijacker, - KMOD_IOCTL_MAGIC, - 0x1, - UpatchEnableRequest -); -ioctl_none!(ioctl_disable_hijacker, KMOD_IOCTL_MAGIC, 0x2); -ioctl_write_ptr!( - ioctl_register_hijacker, - KMOD_IOCTL_MAGIC, - 0x3, - UpatchRegisterRequest -); -ioctl_write_ptr!( - ioctl_unregister_hijacker, - KMOD_IOCTL_MAGIC, - 0x4, - UpatchRegisterRequest -); - -#[repr(C)] -pub struct UpatchEnableRequest { - path: [u8; PATH_MAX as usize], - offset: u64, -} - -pub struct UpatchRegisterRequest { - exec_path: [u8; PATH_MAX as usize], - jump_path: [u8; PATH_MAX as usize], -} - -pub struct HijackerIoctl { - dev: File, -} - -impl HijackerIoctl { - pub fn new>(dev_path: P) -> Result { - Ok(Self { - dev: fs::open_file(dev_path)?, - }) - } - - pub fn enable_hijacker>(&self, lib_path: P, offset: u64) -> Result<()> { - let mut msg = UpatchEnableRequest { - path: [0; PATH_MAX as usize], - offset: 0, - }; - - msg.path - .as_mut() - .write_all(lib_path.as_ref().to_cstring()?.to_bytes_with_nul())?; - msg.offset = offset; - - unsafe { - ioctl_enable_hijacker(self.dev.as_raw_fd(), &msg) - .map_err(|e| anyhow!("Ioctl error, ret={}", e))? - }; - - Ok(()) - } - - pub fn disable_hijacker(&self) -> Result<()> { - unsafe { - ioctl_disable_hijacker(self.dev.as_raw_fd()) - .map_err(|e| anyhow!("Ioctl error, ret={}", e))? - }; - - Ok(()) - } - - pub fn register_hijacker(&self, exec_path: P, jump_path: Q) -> Result<()> - where - P: AsRef, - Q: AsRef, - { - let mut msg = UpatchRegisterRequest { - exec_path: [0; PATH_MAX as usize], - jump_path: [0; PATH_MAX as usize], - }; - - msg.exec_path - .as_mut() - .write_all(exec_path.as_ref().to_cstring()?.to_bytes_with_nul())?; - msg.jump_path - .as_mut() - .write_all(jump_path.as_ref().to_cstring()?.to_bytes_with_nul())?; - - unsafe { - ioctl_register_hijacker(self.dev.as_raw_fd(), &msg) - .map_err(|e| anyhow!("Ioctl error, {}", e.desc()))? - }; - - Ok(()) - } - - pub fn unregister_hijacker(&self, exec_path: P, jump_path: Q) -> Result<()> - where - P: AsRef, - Q: AsRef, - { - let mut msg = UpatchRegisterRequest { - exec_path: [0; PATH_MAX as usize], - jump_path: [0; PATH_MAX as usize], - }; - - msg.exec_path - .as_mut() - .write_all(exec_path.as_ref().to_cstring()?.to_bytes_with_nul())?; - msg.jump_path - .as_mut() - .write_all(jump_path.as_ref().to_cstring()?.to_bytes_with_nul())?; - - unsafe { - ioctl_unregister_hijacker(self.dev.as_raw_fd(), &msg) - .map_err(|e| anyhow!("Ioctl error, {}", e.desc()))? - }; - - Ok(()) - } -} diff --git a/upatchd/src/hijacker/kmod.rs b/upatchd/src/hijacker/kmod.rs deleted file mode 100644 index fc89f5fceb108f6035589532eaf228313ac7e016..0000000000000000000000000000000000000000 --- a/upatchd/src/hijacker/kmod.rs +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{ - ffi::{CString, OsString}, - path::{Path, PathBuf}, -}; - -use anyhow::{Context, Result}; -use log::{error, info}; -use nix::kmod; -use syscare_common::{fs, os}; - -const KMOD_SYS_PATH: &str = "/sys/module"; - -/// An RAII guard of the kernel module. -pub struct HijackerKmodGuard { - kmod_name: String, - kmod_path: PathBuf, - sys_path: PathBuf, -} - -impl HijackerKmodGuard { - pub fn new, P: AsRef>(name: S, kmod_path: P) -> Result { - let instance = Self { - kmod_name: name.as_ref().to_string(), - kmod_path: kmod_path.as_ref().to_path_buf(), - sys_path: Path::new(KMOD_SYS_PATH).join(name.as_ref()), - }; - instance.selinux_relabel_kmod()?; - instance.install_kmod()?; - - Ok(instance) - } -} - -impl HijackerKmodGuard { - fn selinux_relabel_kmod(&self) -> Result<()> { - const KMOD_SECURITY_TYPE: &str = "modules_object_t"; - - if os::selinux::get_status()? != os::selinux::Status::Enforcing { - return Ok(()); - } - - info!("Relabeling kernel module '{}'...", self.kmod_name); - let mut sec_context = os::selinux::get_security_context(&self.kmod_path)?; - if sec_context.kind != KMOD_SECURITY_TYPE { - sec_context.kind = OsString::from(KMOD_SECURITY_TYPE); - os::selinux::set_security_context(&self.kmod_path, sec_context)?; - } - - Ok(()) - } - - fn install_kmod(&self) -> Result<()> { - if self.sys_path.exists() { - return Ok(()); - } - - info!("Installing kernel module '{}'...", self.kmod_name); - let ko_file = fs::open_file(&self.kmod_path)?; - kmod::finit_module( - &ko_file, - CString::new("")?.as_c_str(), - kmod::ModuleInitFlags::MODULE_INIT_IGNORE_VERMAGIC, - ) - .with_context(|| format!("Failed to install kernel module '{}'", self.kmod_name)) - } - - fn remove_kmod(&self) -> Result<()> { - if !self.sys_path.exists() { - return Ok(()); - } - - info!("Removing kernel module '{}'...", self.kmod_name); - kmod::delete_module( - CString::new(self.kmod_name.as_str())?.as_c_str(), - kmod::DeleteModuleFlags::O_NONBLOCK, - ) - .with_context(|| format!("Failed to remove kernel module '{}'", self.kmod_name)) - } -} - -impl Drop for HijackerKmodGuard { - fn drop(&mut self) { - if let Err(e) = self.remove_kmod() { - error!("{:?}", e); - } - } -} diff --git a/upatchd/src/hijacker/mod.rs b/upatchd/src/hijacker/mod.rs deleted file mode 100644 index d0f2c4d38e0ba871cc8defb3d9e126ccd7e3aee0..0000000000000000000000000000000000000000 --- a/upatchd/src/hijacker/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::{Path, PathBuf}; - -use anyhow::{bail, Context, Result}; -use log::{debug, error, info}; - -use syscare_common::{fs::MappedFile, os}; - -mod config; -mod elf_resolver; -mod ioctl; -mod kmod; - -pub use config::HijackerConfig; -use elf_resolver::ElfResolver; -use ioctl::HijackerIoctl; -use kmod::HijackerKmodGuard; - -const KMOD_NAME: &str = "upatch_hijacker"; -const KMOD_DEV_PATH: &str = "/dev/upatch-hijacker"; -const KMOD_PATH: &str = "/usr/libexec/syscare/upatch_hijacker.ko"; - -const HIJACK_SYMBOL_NAME: &str = "execve"; - -pub struct Hijacker { - config: HijackerConfig, - ioctl: HijackerIoctl, - _kmod: HijackerKmodGuard, // need to ensure this drops last -} - -impl Hijacker { - fn find_symbol_addr(symbol_name: &str) -> Result<(PathBuf, u64)> { - let exec_file = MappedFile::open(os::process::path())?; - let exec_resolver = ElfResolver::new(exec_file.as_bytes())?; - - for lib_path in exec_resolver.dependencies()? { - let lib_file = MappedFile::open(&lib_path)?; - let lib_resolver = ElfResolver::new(lib_file.as_bytes())?; - - if let Ok(Some(addr)) = lib_resolver.find_symbol_addr(symbol_name) { - return Ok((lib_path, addr)); - } - } - - bail!("Failed to find symbol '{}'", symbol_name); - } -} - -impl Hijacker { - pub fn new(config: HijackerConfig) -> Result { - debug!("Initializing hijacker kernel module..."); - let kmod = HijackerKmodGuard::new(KMOD_NAME, KMOD_PATH)?; - - debug!("Initializing hijacker ioctl channel..."); - let ioctl = HijackerIoctl::new(KMOD_DEV_PATH)?; - - debug!("Initializing hijacker hooks..."); - let (lib_path, offset) = Self::find_symbol_addr(HIJACK_SYMBOL_NAME)?; - info!( - "Hooking library: {}, offset: {:#x}", - lib_path.display(), - offset - ); - ioctl.enable_hijacker(lib_path, offset)?; - - Ok(Self { - config, - _kmod: kmod, - ioctl, - }) - } -} - -impl Hijacker { - fn get_hijacker>(&self, exec_path: P) -> Result<&Path> { - let hijacker = self - .config - .mapping - .get(exec_path.as_ref()) - .with_context(|| format!("Cannot find hijacker for {}", exec_path.as_ref().display()))? - .as_path(); - - Ok(hijacker) - } - - pub fn register>(&self, elf_path: P) -> Result<()> { - let exec_path = elf_path.as_ref(); - let jump_path = self.get_hijacker(exec_path)?; - - self.ioctl.register_hijacker(exec_path, jump_path) - } - - pub fn unregister>(&self, elf_path: P) -> Result<()> { - let exec_path = elf_path.as_ref(); - let jump_path = self.get_hijacker(exec_path)?; - - self.ioctl.unregister_hijacker(exec_path, jump_path) - } -} - -impl Drop for Hijacker { - fn drop(&mut self) { - if let Err(e) = self.ioctl.disable_hijacker() { - error!("{:?}", e); - } - } -} diff --git a/upatchd/src/main.rs b/upatchd/src/main.rs deleted file mode 100644 index 066e53e3e818ebe462906567c943ef1dab46464a..0000000000000000000000000000000000000000 --- a/upatchd/src/main.rs +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::{fs::Permissions, os::unix::fs::PermissionsExt, panic, process}; - -use anyhow::{ensure, Context, Result}; -use daemonize::Daemonize; -use flexi_logger::{ - Age, Cleanup, Criterion, DeferredNow, Duplicate, FileSpec, LogSpecification, Logger, Naming, - WriteMode, -}; -use jsonrpc_core::IoHandler; -use jsonrpc_ipc_server::{Server, ServerBuilder}; -use log::{debug, error, info, warn, LevelFilter, Record}; -use nix::unistd::{chown, Gid, Uid}; -use signal_hook::{consts::TERM_SIGNALS, iterator::Signals, low_level::signal_name}; - -use syscare_common::{fs, os}; - -mod args; -mod config; -mod hijacker; -mod rpc; - -use args::Arguments; -use config::Config; -use rpc::{Skeleton, SkeletonImpl}; - -const DAEMON_NAME: &str = env!("CARGO_PKG_NAME"); -const DAEMON_VERSION: &str = env!("CARGO_PKG_VERSION"); -const DAEMON_ABOUT: &str = env!("CARGO_PKG_DESCRIPTION"); -const DAEMON_UMASK: u32 = 0o077; - -const CONFIG_FILE_NAME: &str = "upatchd.yaml"; -const PID_FILE_NAME: &str = "upatchd.pid"; -const SOCKET_FILE_NAME: &str = "upatchd.sock"; - -const CONFIG_DIR_PERM: u32 = 0o700; -const WORK_DIR_PERM: u32 = 0o755; -const LOG_DIR_PERM: u32 = 0o700; -const SOCKET_FILE_PERM: u32 = 0o660; -const SOCKET_FILE_PERM_STRICT: u32 = 0o600; - -const MAIN_THREAD_NAME: &str = "main"; -const UNNAMED_THREAD_NAME: &str = ""; -const LOG_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f"; - -struct Daemon { - args: Arguments, - config: Config, -} - -impl Daemon { - fn format_log( - w: &mut dyn std::io::Write, - now: &mut DeferredNow, - record: &Record, - ) -> std::io::Result<()> { - thread_local! { - static THREAD_NAME: String = std::thread::current().name().and_then(|name| { - if name == MAIN_THREAD_NAME { - return os::process::name().to_str(); - } - Some(name) - }) - .unwrap_or(UNNAMED_THREAD_NAME) - .to_string(); - } - - THREAD_NAME.with(|thread_name| { - write!( - w, - "[{}] [{}] [{}] {}", - now.format(LOG_FORMAT), - record.level(), - thread_name, - &record.args() - ) - }) - } - - fn new() -> Result { - // Check root permission - ensure!( - os::user::id() == 0, - "This command has to be run with superuser privileges (under the root user on most systems)." - ); - - // Initialize arguments & prepare environments - os::umask::set_umask(DAEMON_UMASK); - - let args = Arguments::new()?; - fs::create_dir_all(&args.config_dir)?; - fs::create_dir_all(&args.work_dir)?; - fs::create_dir_all(&args.log_dir)?; - fs::set_permissions(&args.config_dir, Permissions::from_mode(CONFIG_DIR_PERM))?; - fs::set_permissions(&args.work_dir, Permissions::from_mode(WORK_DIR_PERM))?; - fs::set_permissions(&args.log_dir, Permissions::from_mode(LOG_DIR_PERM))?; - - std::env::set_current_dir(&args.work_dir).with_context(|| { - format!( - "Failed to change current directory to {}", - args.work_dir.display() - ) - })?; - - // Initialize logger - let max_level = args.log_level; - let stdout_level = match args.daemon { - true => LevelFilter::Off, - false => max_level, - }; - let log_spec = LogSpecification::builder().default(max_level).build(); - let file_spec = FileSpec::default() - .directory(&args.log_dir) - .use_timestamp(false); - Logger::with(log_spec) - .log_to_file(file_spec) - .format(Self::format_log) - .duplicate_to_stdout(Duplicate::from(stdout_level)) - .rotate( - Criterion::Age(Age::Day), - Naming::Timestamps, - Cleanup::KeepCompressedFiles(30), - ) - .write_mode(WriteMode::Direct) - .start() - .context("Failed to initialize logger")?; - - // Initialize config - debug!("Initializing configuation..."); - let config_file = args.config_dir.join(CONFIG_FILE_NAME); - let config = match Config::parse(&config_file) { - Ok(config) => config, - Err(e) => { - warn!("{:?}", e); - info!("Using default configuration..."); - let config = Config::default(); - config.write(&config_file)?; - - config - } - }; - - // Print panic to log incase it really happens - panic::set_hook(Box::new(|info| error!("{}", info))); - Ok(Self { args, config }) - } -} - -impl Daemon { - fn daemonize(&self) -> Result<()> { - if !self.args.daemon { - return Ok(()); - } - - let pid_file = self.args.work_dir.join(PID_FILE_NAME); - Daemonize::new() - .umask(DAEMON_UMASK) - .working_directory(&self.args.work_dir) - .pid_file(pid_file) - .start() - .context("Daemonize failed") - } - - fn initialize_skeleton(&self) -> Result { - let config = self.config.hijacker.clone(); - let methods = SkeletonImpl::new(config)?.to_delegate(); - - let mut io_handler = IoHandler::new(); - io_handler.extend_with(methods); - - Ok(io_handler) - } - - fn start_rpc_server(&self, io_handler: IoHandler) -> Result { - let socket_file = self.args.work_dir.join(SOCKET_FILE_NAME); - let builder = ServerBuilder::new(io_handler).set_client_buffer_size(1); - let server = builder.start( - socket_file - .to_str() - .context("Failed to convert socket path to string")?, - )?; - - let socket_owner = Uid::from_raw(self.config.daemon.socket.uid); - let socket_group = Gid::from_raw(self.config.daemon.socket.gid); - chown(&socket_file, Some(socket_owner), Some(socket_group))?; - - fs::set_permissions( - &socket_file, - match socket_owner.as_raw() == socket_group.as_raw() { - true => Permissions::from_mode(SOCKET_FILE_PERM_STRICT), - false => Permissions::from_mode(SOCKET_FILE_PERM), - }, - )?; - - Ok(server) - } - - fn run(&self) -> Result<()> { - info!("================================"); - info!("Upatch Daemon - {}", DAEMON_VERSION); - info!("================================"); - info!("Start with {:#?}", self.args); - info!("Using {:#?}", self.config); - self.daemonize()?; - - info!("Initializing skeleton..."); - let io_handler = self - .initialize_skeleton() - .context("Failed to initialize skeleton")?; - - info!("Starting remote procedure call server..."); - let server = self - .start_rpc_server(io_handler) - .context("Failed to create remote procedure call server")?; - - info!("Daemon is running..."); - let mut signals = - Signals::new(TERM_SIGNALS).context("Failed to initialize signal handler")?; - if let Some(signal) = signals.forever().next() { - info!( - "Received {} signal", - signal_name(signal).unwrap_or("UNKNOWN") - ); - } - - info!("Shutting down..."); - server.close(); - - Ok(()) - } -} - -fn main() { - let daemon = match Daemon::new() { - Ok(instance) => instance, - Err(e) => { - eprintln!("Error: {:?}", e); - process::exit(-1); - } - }; - - if let Err(e) = daemon.run() { - error!("Error: {:?}", e); - error!("Daemon exited unsuccessfully"); - - drop(daemon); - process::exit(-1); - } - - info!("Daemon exited"); -} diff --git a/upatchd/src/rpc/function.rs b/upatchd/src/rpc/function.rs deleted file mode 100644 index 87a32387480a96ccf16d4f6286592b24c7735cbb..0000000000000000000000000000000000000000 --- a/upatchd/src/rpc/function.rs +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -pub use jsonrpc_core::Result as RpcResult; -use jsonrpc_core::{Error, ErrorCode}; -pub use jsonrpc_derive::rpc; -use log::error; - -const RPC_OP_ERROR: i64 = -1; - -pub struct RpcFunction; - -impl RpcFunction { - pub fn call(f: F) -> RpcResult - where - F: FnOnce() -> anyhow::Result, - { - (f)().map_err(|e| { - error!("{:?}", e); - Error { - code: ErrorCode::ServerError(RPC_OP_ERROR), - message: format!("{:?}", e), - data: None, - } - }) - } -} diff --git a/upatchd/src/rpc/mod.rs b/upatchd/src/rpc/mod.rs deleted file mode 100644 index 8d37c05c7f56d886ae784a46b01c6aecfadb0897..0000000000000000000000000000000000000000 --- a/upatchd/src/rpc/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -mod function; -mod skeleton; -mod skeleton_impl; - -pub use skeleton::*; -pub use skeleton_impl::*; diff --git a/upatchd/src/rpc/skeleton.rs b/upatchd/src/rpc/skeleton.rs deleted file mode 100644 index 9972fc1069a68e4537faafbb659956a577b146f1..0000000000000000000000000000000000000000 --- a/upatchd/src/rpc/skeleton.rs +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::PathBuf; - -use super::function::{rpc, RpcResult}; - -#[rpc(server)] -pub trait Skeleton { - #[rpc(name = "enable_hijack")] - fn enable_hijack(&self, exec_path: PathBuf) -> RpcResult<()>; - - #[rpc(name = "disable_hijack")] - fn disable_hijack(&self, exec_path: PathBuf) -> RpcResult<()>; -} diff --git a/upatchd/src/rpc/skeleton_impl.rs b/upatchd/src/rpc/skeleton_impl.rs deleted file mode 100644 index d725166b7a8461afcf95c3cb31a7c5394f77ebd2..0000000000000000000000000000000000000000 --- a/upatchd/src/rpc/skeleton_impl.rs +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: Mulan PSL v2 -/* - * Copyright (c) 2024 Huawei Technologies Co., Ltd. - * upatchd is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -use std::path::PathBuf; - -use anyhow::{Context, Result}; -use log::{debug, info}; - -use crate::hijacker::{Hijacker, HijackerConfig}; - -use super::{ - function::{RpcFunction, RpcResult}, - skeleton::Skeleton, -}; - -pub struct SkeletonImpl { - hijacker: Hijacker, -} - -impl SkeletonImpl { - pub fn new(config: HijackerConfig) -> Result { - debug!("Initializing hijacker..."); - Ok(Self { - hijacker: Hijacker::new(config).context("Failed to initialize hijacker")?, - }) - } -} - -impl Skeleton for SkeletonImpl { - fn enable_hijack(&self, elf_path: PathBuf) -> RpcResult<()> { - RpcFunction::call(|| { - info!("Enable hijack: {}", elf_path.display()); - self.hijacker - .register(&elf_path) - .with_context(|| format!("Failed to register hijack {}", elf_path.display())) - }) - } - - fn disable_hijack(&self, elf_path: PathBuf) -> RpcResult<()> { - RpcFunction::call(|| { - info!("Disable hijack: {}", elf_path.display()); - self.hijacker - .unregister(&elf_path) - .with_context(|| format!("Failed to unregister hijack {}", elf_path.display())) - }) - } -}