From d56f8dfa756e17555440863fa15b8073ccc0fa8f Mon Sep 17 00:00:00 2001 From: wenxuanjun Date: Sun, 3 Nov 2024 07:06:07 +0800 Subject: [PATCH] Init project --- .cargo/config.toml | 6 +++ .github/workflows/release.yml | 53 ++++++++++++++++++++ .gitignore | 5 ++ Cargo.lock | 25 ++++++++++ Cargo.toml | 22 ++++++++ LICENSE | 21 ++++++++ README.md | 26 ++++++++++ cbindgen.toml | 5 ++ i686-unknown-none.json | 15 ++++++ src/lib.rs | 94 +++++++++++++++++++++++++++++++++++ 10 files changed, 272 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cbindgen.toml create mode 100644 i686-unknown-none.json create mode 100644 src/lib.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..4718b08 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +[unstable] +build-std-features = ["compiler-builtins-mem"] +build-std = ["core", "compiler_builtins", "alloc"] + +[build] +target = ["i686-unknown-none.json", "x86_64-unknown-none"] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..76f8859 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,53 @@ +name: Build and Release + +permissions: + contents: write + +on: + push: + branches: [ "main" ] + paths-ignore: + - 'README.md' + - '.gitignore' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: nightly + override: true + + - name: Add x86_64-unknown-none target + run: rustup target add x86_64-unknown-none + + - name: Install cbindgen + run: cargo install cbindgen + + - name: Add rust-src component + run: rustup component add rust-src + + - name: Build general release + run: | + cargo build --release + mv target/x86_64-unknown-none/release/libelf_parse.a libelf_parse-x86_64.a + mv target/i686-unknown-none/release/libelf_parse.a libelf_parse-i686.a + + - name: Generate header + run: cbindgen --output elf_parse.h + + - name: Release artifacts + uses: softprops/action-gh-release@v2 + with: + name: Nightly build + tag_name: release + files: | + libelf_parse-x86_64.a + libelf_parse-i686.a + elf_parse.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a266633 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Cbindgen files +*.h + +# Build cache +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4abb117 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "libelf_parse" +version = "0.0.0" +dependencies = [ + "object", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..25be964 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "libelf_parse" +edition = "2021" + +[lib] +name = "elf_parse" +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +opt-level = 3 +strip = true +codegen-units = 1 + +[dependencies.object] +version = "0.36.5" +features = ["read_core", "elf", "unaligned"] +default-features = false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c33e483 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023-2024 wenxuanjun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f8dff6 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# libelf-parse + +C binding of `object` crate for x86 or x86_64 OS to parse ELF files easily. + +## Usage + +Download the header file and lib from [releases](https://github.com/plos-clan/libelf-parse/releases/tag/release). + +Link the library to your project. + +## Build + +Build directly to get the two target files: + +```bash +cargo build --release +``` + +The production build will be in `target/release//` directory. + +And use `cbindgen` to generate the header file: + +```bash +cargo install cbindgen +cbindgen --output elf_parse.h +``` diff --git a/cbindgen.toml b/cbindgen.toml new file mode 100644 index 0000000..b634677 --- /dev/null +++ b/cbindgen.toml @@ -0,0 +1,5 @@ +language = "C" +pragma_once = true +no_includes = true +cpp_compat = true +usize_is_size_t = true diff --git a/i686-unknown-none.json b/i686-unknown-none.json new file mode 100644 index 0000000..2ae6b7c --- /dev/null +++ b/i686-unknown-none.json @@ -0,0 +1,15 @@ +{ + "llvm-target": "i686-unknown-none", + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", + "arch": "x86", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float" +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..03f46b2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,94 @@ +#![no_std] +#![no_main] +#![feature(alloc_error_handler)] + +extern crate alloc; + +use core::alloc::{GlobalAlloc, Layout}; +use core::ffi::c_void; +use core::panic::PanicInfo; +use core::slice::from_raw_parts; +use object::{File, Object, ObjectSegment}; + +#[panic_handler] +unsafe fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[global_allocator] +static ALLOCATOR: Allocator = Allocator; + +#[alloc_error_handler] +fn alloc_error_handler(layout: Layout) -> ! { + panic!("Allocation error: {:?}", layout); +} + +struct Allocator; + +static mut MALLOC: Option *mut c_void> = None; +static mut FREE: Option = None; + +unsafe impl GlobalAlloc for Allocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + MALLOC.unwrap()(layout.size()) as *mut u8 + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + FREE.unwrap()(ptr as *mut c_void); + } +} + +#[repr(C)] +pub struct Segment { + address: u64, + size: u64, + data: *const u8, +} + +impl Segment { + pub fn new(address: u64, size: u64, data: *const u8) -> Self { + Self { address, size, data } + } +} + +#[repr(C)] +pub enum ParseElfError { + None = 0, + InvalidElfData, + FailedToGetSegmentData, + AllocFunctionNotProvided, +} + +#[no_mangle] +pub unsafe extern "C" fn parse_elf( + elf_data: *const u8, + elf_size: usize, + callback: extern "C" fn(segment: Segment), + malloc: Option *mut c_void>, + free: Option, +) -> ParseElfError { + if malloc.is_none() || free.is_none() { + return ParseElfError::AllocFunctionNotProvided; + } + + MALLOC = malloc; + FREE = free; + + let buffer = from_raw_parts(elf_data, elf_size); + + let binary = match File::parse(buffer) { + Ok(file) => file, + Err(_) => return ParseElfError::InvalidElfData, + }; + + for segment in binary.segments() { + let data = match segment.data() { + Ok(d) => d, + Err(_) => return ParseElfError::FailedToGetSegmentData, + }; + + callback(Segment::new(segment.address(), segment.size(), data.as_ptr())); + } + + ParseElfError::None +}