Full Code of gamozolabs/elfloader for AI

main 4e8118eb9e8d cached
15 files
23.8 KB
6.8k tokens
18 symbols
1 requests
Download .txt
Repository: gamozolabs/elfloader
Branch: main
Commit: 4e8118eb9e8d
Files: 15
Total size: 23.8 KB

Directory structure:
gitextract_lpxhwkla/

├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── example_program_with_data/
│   ├── .cargo/
│   │   └── config
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── Makefile
│   └── src/
│       └── main.rs
├── example_small_program/
│   ├── .cargo/
│   │   └── config
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── Makefile
│   └── src/
│       └── main.rs
└── src/
    └── main.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/target


================================================
FILE: Cargo.toml
================================================
[package]
name = "elfloader"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 gamozolabs

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.


================================================
FILE: README.md
================================================
# Summary

`elfloader` is a super simple loader for ELF files that generates a flat
in-memory representation of the ELF.

Pair this with Rust and now you can write your shellcode in a proper, safe,
high-level language. Any target that LLVM can target can be used, including
custom target specifications for really exotic platforms and ABIs. Enjoy using
things like `u64`s on 32-bit systems, bounds checked arrays, drop handling of
allocations, etc :)

It simply concatenates all `LOAD` sections together, using zero-padding if
there are gaps, into one big flat file.

This file includes zero-initialization of `.bss` sections, and thus can be used
directly as a shellcode payload.

If you don't want to waste time with fail-open linker scripts, this is probably
a great way to go.

This doesn't handle any relocations, it's on you to make sure the original ELF
is based at the address you want it to be at.

# Usage

To use this tool, simply:

```
Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>
    --binary      - Don't output a FELF, output the raw loaded image with no
                    metadata
    --perms       - Create a FELF0002 which includes permission data, overrides
                    --binary
    --base=<addr> - Force the output to start at `<addr>`, zero padding from
                    the base to the start of the first LOAD segment if needed.
                    `<addr>` is default hex, can be overrided with `0d`, `0b`,
                    `0x`, or `0o` prefixes.
                    Warning: This does not _relocate_ to base, it simply starts
                    the output at `<addr>` (adding zero bytes such that the
                    output image can be loaded at `<addr>` instead of the
                    original ELF base)
    <input ELF>   - Path to input ELF
    <output>      - Path to output file
```

To install this tool run:

`cargo install --path .`

Now you can use `elfloader` from anywhere in your shell!

# Dev

This project was developed live here:

https://www.youtube.com/watch?v=x0V-CEmXQCQ

# Example

There's an example in `example_small_program`, simply run `make` or `nmake`
and this should generate an `example.bin` which is 8 bytes.

```
pleb@gamey ~/elfloader/example_small_program $ make
cargo build --release
    Finished release [optimized] target(s) in 0.03s
elfloader --binary target/aarch64-unknown-none/release/example_small_program example.bin
pleb@gamey ~/elfloader/example_small_program $ ls -l ./example.bin 
-rw-r--r-- 1 pleb pleb 8 Nov  8 12:27 ./example.bin

pleb@gamey ~/elfloader/example_small_program $ objdump -d target/aarch64-unknown-none/release/example_small_program

target/aarch64-unknown-none/release/example_small_program:     file format elf64-littleaarch64


Disassembly of section .text:

00000000133700b0 <_start>:
    133700b0:   8b000020        add     x0, x1, x0
    133700b4:   d65f03c0        ret
```

Now you can write your shellcode in Rust, and you don't have to worry about
whether you emit `.data`, `.rodata`, `.bss`, etc. This will handle it all for
you!

There's also an example with `.bss` and `.rodata`

```
pleb@gamey ~/elfloader/example_program_with_data $ make
cargo build --release
    Finished release [optimized] target(s) in 0.04s
elfloader --binary target/aarch64-unknown-none/release/example_program_with_data example.bin
pleb@gamey ~/elfloader/example_program_with_data $ ls -l ./example.bin
-rw-r--r-- 1 pleb pleb 29 Nov  8 12:39 ./example.bin
pleb@gamey ~/elfloader/example_program_with_data $ objdump -d target/aarch64-unknown-none/release/example_program_with_data

target/aarch64-unknown-none/release/example_program_with_data:     file format elf64-littleaarch64


Disassembly of section .text:

0000000013370124 <_start>:
    13370124:   90000000        adrp    x0, 13370000 <_start-0x124>
    13370128:   90000008        adrp    x8, 13370000 <_start-0x124>
    1337012c:   52800029        mov     w9, #0x1                        // #1
    13370130:   91048000        add     x0, x0, #0x120
    13370134:   3904f109        strb    w9, [x8, #316]
    13370138:   d65f03c0        ret
pleb@gamey ~/elfloader/example_program_with_data $ readelf -l target/aarch64-unknown-none/release/example_program_with_data

Elf file type is EXEC (Executable file)
Entry point 0x13370124
There are 4 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000120 0x0000000013370120 0x0000000013370120
                 0x0000000000000004 0x0000000000000004  R      0x1
  LOAD           0x0000000000000124 0x0000000013370124 0x0000000013370124
                 0x0000000000000018 0x0000000000000018  R E    0x4
  LOAD           0x000000000000013c 0x000000001337013c 0x000000001337013c
                 0x0000000000000000 0x0000000000000001  RW     0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x0

 Section to Segment mapping:
  Segment Sections...
   00     .rodata 
   01     .text 
   02     .bss 
   03     
```

# Internals

This tool doesn't care about anything except for `LOAD` sections. It determines
the endianness (little vs big) and bitness (32 vs 64) from the ELF header,
and from there it creates a flat image based on program header virtual
addresses (where it's loaded), file size (number of initialized bytes) and
mem size (size of actual memory region). The bytes are initialized from the
file based on the offset and file size, and this is then extended with zeros
until mem size (or truncated if mem size is smaller than file size).

These `LOAD` sections are then concatenated together with zero-byte padding
for gaps.

This is designed to be incredibly simple, and agnostic to the ELF input. It
could be an executable, object file, shared object, core dump, etc, doesn't
really care. It'll simply give you the flat representation of the memory,
nothing more.

This allows you to turn any ELF into shellcode, or a simpler file format that
is easier to load in hard-to-reach areas, like embedded devices. Personally,
I developed this for my MIPS NT 4.0 loader which allows me to run Rust code.

# FELF0001 format

This tool by default generates a FELF file format. This is a Falk ELF. This
is a simple file format:

```
FELF0001 - Magic header
entry    - 64-bit little endian integer of the entry point address
base     - 64-bit little endian integer of the base address to load the image
<image>  - Rest of the file is the raw image, to be loaded at `base` and jumped
           into at `entry`
```

# FELF0002 format (when --perms flag is used)

This tool by default generates a FELF file format. This is a Falk ELF. This
is a simple file format with permissions:

```
FELF0002 - Magic header
entry    - 64-bit little endian integer of the entry point address
base     - 64-bit little endian integer of the base address to load the image
<image>  - Rest of the file is the raw image, to be loaded at `base` and jumped
           into at `entry`
<perms>  - Permissions, matching the bytes of <image> where the byte contains
           the following flags bitwise or-ed together:
           0x01 - Executable, 0x02 - Writable, 0x04 - Readable
           Padding bytes will be 0x00, and thus have no permissions for any
           access
```



================================================
FILE: example_program_with_data/.cargo/config
================================================
[build]
target = "aarch64-unknown-none"

[target.aarch64-unknown-none]
rustflags = ["-Clink-arg=--nmagic", "-Clink-arg=--image-base=0x13370000"]

[unstable]
build-std = ["core"]



================================================
FILE: example_program_with_data/.gitignore
================================================
/target
/*.bin


================================================
FILE: example_program_with_data/Cargo.toml
================================================
[package]
name = "example_program_with_data"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[profile.dev]
panic = "abort"

[profile.release]
opt-level = "z"
panic = "abort"



================================================
FILE: example_program_with_data/Makefile
================================================
all:
	cargo build --release
	elfloader --binary target/aarch64-unknown-none/release/example_program_with_data example.bin



================================================
FILE: example_program_with_data/src/main.rs
================================================
#![no_std]
#![no_main]

use core::sync::atomic::{AtomicBool, Ordering};

#[panic_handler]
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

// BSS example
#[no_mangle]
pub static BSSTHING: AtomicBool = AtomicBool::new(false);

#[no_mangle]
pub extern fn _start() -> *const u8 {
    BSSTHING.store(true, Ordering::Relaxed);
    "asdf".as_ptr()
}



================================================
FILE: example_small_program/.cargo/config
================================================
[build]
target = "aarch64-unknown-none"

[target.aarch64-unknown-none]
rustflags = ["-Clink-arg=--nmagic", "-Clink-arg=--image-base=0x13370000"]

[unstable]
build-std = ["core"]



================================================
FILE: example_small_program/.gitignore
================================================
/target
/*.bin


================================================
FILE: example_small_program/Cargo.toml
================================================
[package]
name = "example_small_program"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[profile.dev]
panic = "abort"

[profile.release]
opt-level = "z"
panic = "abort"



================================================
FILE: example_small_program/Makefile
================================================
all:
	cargo build --release
	elfloader --binary target/aarch64-unknown-none/release/example_small_program example.bin



================================================
FILE: example_small_program/src/main.rs
================================================
#![no_std]
#![no_main]

#[panic_handler]
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
pub extern fn _start(x: u64, y: u64) -> u64 {
    x.wrapping_add(y)
}



================================================
FILE: src/main.rs
================================================
use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom, BufWriter, Write};
use std::mem::size_of;
use std::path::Path;

/// Friendly wrapper around [`Error`]
type Result<T> = std::result::Result<T, Error>;

/// Error types
#[derive(Debug)]
pub enum Error {
    /// Failed to open the ELF file
    Open(std::io::Error),

    /// Failed to consume a field from the input
    Consume(&'static str, std::io::Error),

    /// ELF magic was missing from file
    MissingMagic,

    /// ELF indiciated a bitness which was not a valid variant
    InvalidBitness(u8),

    /// ELF indiciated an endianness which was not a valid variant
    InvalidEndianness(u8),

    /// ELF version was unknown
    UnknownVersion(u8),

    /// Expected an executable but got a different ELF type
    ExpectedExecutable(u16),

    /// Seeking to the program headers failed
    SeekProgramHeaders(std::io::Error),

    /// Seeking the initialized data for a loaded segment failed
    LoadSeek(std::io::Error),

    /// Reading initialized bytes from file failed
    ReadInit(std::io::Error),

    /// Creating the FELF failed
    CreateFelf(std::io::Error),

    /// Writing the FELF failed
    WriteFelf(std::io::Error),

    /// Truncated integer for filesz
    IntegerTruncationFileSz,

    /// Truncated integer for memsz
    IntegerTruncationMemSz,

    /// Trucated integer for offset
    IntegerTruncationOffset,

    /// Integer overflow when computing current address
    IntegerOverflowCurrentAddress,

    /// Multiple sections overlap where they are loaded
    SectionOverlap,

    /// The ELF didn't supply any segments to load
    NoLoadSegments,

    /// The `--base` provided did not parse into a `u64` correctly
    InvalidBase(std::num::ParseIntError),
}

/// Consume bytes from a reader
macro_rules! consume {
    // Consume a `u8`
    ($reader:expr, $field:expr) => {{
        // Create buffer
        let mut tmp = [0u8; 1];
        $reader.read_exact(&mut tmp).map(|_| {
            tmp[0]
        }).map_err(|x| Error::Consume($field, x))
    }};

    ($reader:expr, $ty:ty, $endian:expr, $field:expr) => {{
        // Create buffer for type
        let mut tmp = [0u8; size_of::<$ty>()];

        match $endian {
            Endianness::Little => {
                // Read the bytes and convert
                $reader.read_exact(&mut tmp).map(|_| {
                    <$ty>::from_le_bytes(tmp)
                }).map_err(|x| Error::Consume($field, x))
            }
            Endianness::Big => {
                // Read the bytes and convert
                $reader.read_exact(&mut tmp).map(|_| {
                    <$ty>::from_be_bytes(tmp)
                }).map_err(|x| Error::Consume($field, x))
            }
        }
    }};

    ($reader:expr, $size:expr, $field:expr) => {{
        // Create buffer for type
        let mut tmp = [0u8; $size];

        // Read the bytes and convert
        $reader.read_exact(&mut tmp).map(|_| {
            tmp
        }).map_err(|x| Error::Consume($field, x))
    }};
}

macro_rules! consume_native {
    ($reader:expr, $bitness:ident, $endian:expr, $field:expr) => {{
        match $bitness {
            Bitness::Bits32 => {
                consume!($reader, u32, $endian, $field).map(|x| x as u64)
            },
            Bitness::Bits64 => consume!($reader, u64, $endian, $field),
        }
    }};
}

/// Bitnesses for ELF files
#[derive(Debug)]
enum Bitness {
    /// 32-bit ELF
    Bits32,

    /// 64-bit ELF
    Bits64,
}

impl TryFrom<u8> for Bitness {
    type Error = Error;

    fn try_from(val: u8) -> Result<Self> {
        Ok(match val {
            1 => Bitness::Bits32,
            2 => Bitness::Bits64,
            _ => return Err(Error::InvalidBitness(val)),
        })
    }
}

/// Endianness for ELF files
#[derive(Debug)]
enum Endianness {
    /// Little endian
    Little,

    /// Big endian
    Big,
}

impl TryFrom<u8> for Endianness {
    type Error = Error;

    fn try_from(val: u8) -> Result<Self> {
        Ok(match val {
            1 => Endianness::Little,
            2 => Endianness::Big,
            _ => return Err(Error::InvalidEndianness(val)),
        })
    }
}

/// Loaded segment
const PT_LOAD: u32 = 1;

/// Executable segment
const PF_X: u32 = 1 << 0;

/// Writable segment
const PF_W: u32 = 1 << 1;

/// Readable segment
const PF_R: u32 = 1 << 2;

/// Load an ELF from disk
///
/// Returns:
///
/// `(entry virtual address, base address for flat map, flat map contents)`
pub fn write_file(path: impl AsRef<Path>, base: Option<u64>,
        mut output: impl Write, binary: bool, save_perms: bool) -> Result<()> {
    // Open the file
    let mut reader =
        BufReader::new(File::open(path).map_err(Error::Open)?);

    // Check that this is an ELF
    if &consume!(reader, 4, "ELF magic")? != b"\x7fELF" {
        return Err(Error::MissingMagic);
    }

    // Get the bitness and endianness
    let bt = Bitness::try_from(consume!(reader, "bitness")?)?;
    let en = Endianness::try_from(consume!(reader, "endianness")?)?;

    // Make sure the ELF version matches
    let version = consume!(reader, "version")?;
    if version != 1 {
        return Err(Error::UnknownVersion(version));
    }

    // We don't care about the ABI
    let _abi    = consume!(reader, "abi")?;
    let _abiver = consume!(reader, "abi version")?;
    let _pad    = consume!(reader, 7, "padding")?;

    let _objtyp  = consume!(reader, u16, en, "type")?;
    let _machine = consume!(reader, u16, en, "machine")?;
    let _elfver  = consume!(reader, u32, en, "ELF version")?;

    let entry = consume_native!(reader, bt, en, "entry point")?;

    let phoff  = consume_native!(reader, bt, en, "program header offset")?;
    let _shoff = consume_native!(reader, bt, en, "section header offset")?;

    let _flags  = consume!(reader, u32, en, "flags")?;
    let _ehsize = consume!(reader, u16, en, "ELF header size")?;
    let _phesz  = consume!(reader, u16, en, "program header entry size")?;
    let phcnt   = consume!(reader, u16, en, "program header entries")?;

    // Seek to the program headers
    reader.seek(SeekFrom::Start(phoff))
        .map_err(Error::SeekProgramHeaders)?;

    // List of sections to load
    let mut load = Vec::new();

    // Go through each program header entry
    for _ in 0..phcnt {
        // Get header type
        let typ = consume!(reader, u32, en, "PH type")?;

        // 64-bit has flags here
        let mut flags = if matches!(bt, Bitness::Bits64) {
            consume!(reader, u32, en, "PH flags")?
        } else { 0 };

        // Parse program header
        let offset = consume_native!(reader, bt, en, "PH offset")?;
        let vaddr  = consume_native!(reader, bt, en, "PH vaddr")?;
        let _paddr = consume_native!(reader, bt, en, "PH paddr")?;
        let filesz = consume_native!(reader, bt, en, "PH filesz")?;
        let memsz  = consume_native!(reader, bt, en, "PH memsz")?;

        // 32-bit has flags here
        if matches!(bt, Bitness::Bits32) {
            flags = consume!(reader, u32, en, "PH flags")?
        }

        let _align = consume_native!(reader, bt, en, "PH align")?;

        if typ == PT_LOAD {
            // If the section is zero size, skip it entirely
            if memsz == 0 {
                continue;
            }

            // Read initialized bytes from file if needed
            let mut bytes = Vec::new();
            if filesz > 0 {
                // Save the current position
                let stream_pos = reader.stream_position()
                    .map_err(Error::LoadSeek)?;

                // Seek to the bytes in the file
                reader.seek(SeekFrom::Start(offset))
                    .map_err(Error::LoadSeek)?;

                // Resize buffer
                bytes.resize(filesz.try_into()
                    .map_err(|_| Error::IntegerTruncationFileSz)?, 0u8);

                // Read initialized bytes from file
                reader.read_exact(&mut bytes).map_err(Error::ReadInit)?;

                // Seek back to where we were
                reader.seek(SeekFrom::Start(stream_pos))
                    .map_err(Error::LoadSeek)?;
            }

            // Pad out with zeros until memory size
            bytes.resize(memsz.try_into()
                .map_err(|_| Error::IntegerTruncationMemSz)?, 0u8);

            // Determine permissions for this segment
            let r = (flags & PF_R) != 0;
            let w = (flags & PF_W) != 0;
            let x = (flags & PF_X) != 0;

            // Save the address to load to and the bytes
            load.push((vaddr, bytes, r, w, x));
        }
    }

    // Sort load sections by virtual address
    load.sort_by_key(|x| x.0);

    // Start load at the specified `base`, otherwise use the lowest address of
    // all the LOAD sections
    let lowest_addr = base.unwrap_or(
        load.get(0).ok_or(Error::NoLoadSegments)?.0);

    if !binary {
        // Write the FELF header
        if !save_perms {
            output.write_all(b"FELF0001").map_err(Error::WriteFelf)?;
        } else {
            output.write_all(b"FELF0002").map_err(Error::WriteFelf)?;
        }

        output
            .write_all(&entry.to_le_bytes())
            .map_err(Error::WriteFelf)?;
        output
            .write_all(&lowest_addr.to_le_bytes())
            .map_err(Error::WriteFelf)?;
    }

    // Permissions vector
    let mut perms = Vec::new();

    // Write everything!
    let mut cur_addr = lowest_addr;
    for (vaddr, bytes, r, w, x) in load {
        // Get the offset from where we are
        let offset: usize = vaddr.checked_sub(cur_addr)
            .ok_or(Error::SectionOverlap)?
            .try_into()
            .map_err(|_| Error::IntegerTruncationOffset)?;

        // Pad out loaded representation until `vaddr`
        const ZERO_BUF: [u8; 1024 * 8] = [0u8; 1024 * 8];

        let mut padding = offset;
        while padding > ZERO_BUF.len() {
            output.write_all(&ZERO_BUF).map_err(Error::WriteFelf)?;
            if save_perms {
                perms.write_all(&ZERO_BUF).map_err(Error::WriteFelf)?;
            }
            padding -= ZERO_BUF.len();
        }
        output.write_all(&ZERO_BUF[..padding])
            .map_err(Error::WriteFelf)?;
        if save_perms {
            perms.write_all(&ZERO_BUF[..padding])
                .map_err(Error::WriteFelf)?;
        }

        // Place in the bytes
        output.write_all(&bytes).map_err(Error::WriteFelf)?;

        if save_perms {
            // Place in all the permission bytes
            let perm_flags =
                if r { PF_R } else { 0 } |
                if w { PF_W } else { 0 } |
                if x { PF_X } else { 0 };
            perms.resize(perms.len() + bytes.len(), perm_flags as u8);
        }

        // Update current address
        cur_addr = vaddr
            .checked_add(bytes.len() as u64)
            .ok_or(Error::IntegerOverflowCurrentAddress)?;
    }

    if save_perms {
        // Add permissions to file
        output.write_all(&perms).map_err(Error::WriteFelf)?;
    }

    output.flush().map_err(Error::WriteFelf)?;

    Ok(())
}

/// Entry point
fn main() -> Result<()> {
    // Get the command line arguments
    let mut args = std::env::args().collect::<Vec<_>>();

    // Check if the `--binary` flag was specified
    let mut binary = false;
    args.retain(|x| if x == "--binary" { binary = true; false } else { true });

    // Check if the `--perms` flag was specified
    let mut perms = false;
    args.retain(|x| if x == "--perms" { perms = true; false } else { true });

    // Perms flag overrides the binary flag
    if perms {
        binary = false;
    }

    // Check if the `--base=` flag was specified
    let mut base = None;
    args.retain(|x| {
        if x.starts_with("--base=") {
            // Default to hex, skip over `--base=`
            let mut radix = 16;
            let mut x     = &x[7..];

            if x.starts_with("0x") {
                radix = 16;
                x = &x[2..];
            } else if x.starts_with("0o") {
                radix = 8;
                x = &x[2..];
            } else if x.starts_with("0b") {
                radix = 2;
                x = &x[2..];
            } else if x.starts_with("0d") {
                radix = 10;
                x = &x[2..];
            }

            // Convert to a `u64`
            base = Some(u64::from_str_radix(x, radix)
                .map_err(Error::InvalidBase));

            // Don't keep this argument
            false
        } else { true }
    });

    // Can't use `?` in closure
    let base = if let Some(base) = base {
        Some(base?)
    } else { None };

    if args.len() != 3 {
        println!(
r#"Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>
    --binary      - Don't output a FELF, output the raw loaded image with no
                    metadata
    --perms       - Create a FELF0002 which includes permission data, overrides
                    --binary
    --base=<addr> - Force the output to start at `<addr>`, zero padding from
                    the base to the start of the first LOAD segment if needed.
                    `<addr>` is default hex, can be overrided with `0d`, `0b`,
                    `0x`, or `0o` prefixes.
                    Warning: This does not _relocate_ to base, it simply starts
                    the output at `<addr>` (adding zero bytes such that the
                    output image can be loaded at `<addr>` instead of the
                    original ELF base)
    <input ELF>   - Path to input ELF
    <output>      - Path to output file"#);
        return Ok(());
    }

    // Create the output file
    let mut output = BufWriter::new(File::create(&args[2])
        .map_err(Error::CreateFelf)?);
    write_file(&args[1], base, &mut output, binary, perms)?;

    Ok(())
}

Download .txt
gitextract_lpxhwkla/

├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── example_program_with_data/
│   ├── .cargo/
│   │   └── config
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── Makefile
│   └── src/
│       └── main.rs
├── example_small_program/
│   ├── .cargo/
│   │   └── config
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── Makefile
│   └── src/
│       └── main.rs
└── src/
    └── main.rs
Download .txt
SYMBOL INDEX (18 symbols across 3 files)

FILE: example_program_with_data/src/main.rs
  function panic (line 7) | fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
  function _start (line 16) | pub extern fn _start() -> *const u8 {

FILE: example_small_program/src/main.rs
  function panic (line 5) | fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
  function _start (line 10) | pub extern fn _start(x: u64, y: u64) -> u64 {

FILE: src/main.rs
  type Result (line 7) | type Result<T> = std::result::Result<T, Error>;
  type Error (line 11) | pub enum Error {
  type Bitness (line 125) | enum Bitness {
    type Error (line 134) | type Error = Error;
    method try_from (line 136) | fn try_from(val: u8) -> Result<Self> {
  type Endianness (line 147) | enum Endianness {
    type Error (line 156) | type Error = Error;
    method try_from (line 158) | fn try_from(val: u8) -> Result<Self> {
  constant PT_LOAD (line 168) | const PT_LOAD: u32 = 1;
  constant PF_X (line 171) | const PF_X: u32 = 1 << 0;
  constant PF_W (line 174) | const PF_W: u32 = 1 << 1;
  constant PF_R (line 177) | const PF_R: u32 = 1 << 2;
  function write_file (line 184) | pub fn write_file(path: impl AsRef<Path>, base: Option<u64>,
  function main (line 381) | fn main() -> Result<()> {
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (26K chars).
[
  {
    "path": ".gitignore",
    "chars": 8,
    "preview": "/target\n"
  },
  {
    "path": "Cargo.toml",
    "chars": 178,
    "preview": "[package]\nname = \"elfloader\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.ru"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2022 gamozolabs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "README.md",
    "chars": 7448,
    "preview": "# Summary\n\n`elfloader` is a super simple loader for ELF files that generates a flat\nin-memory representation of the ELF."
  },
  {
    "path": "example_program_with_data/.cargo/config",
    "chars": 179,
    "preview": "[build]\ntarget = \"aarch64-unknown-none\"\n\n[target.aarch64-unknown-none]\nrustflags = [\"-Clink-arg=--nmagic\", \"-Clink-arg=-"
  },
  {
    "path": "example_program_with_data/.gitignore",
    "chars": 15,
    "preview": "/target\n/*.bin\n"
  },
  {
    "path": "example_program_with_data/Cargo.toml",
    "chars": 277,
    "preview": "[package]\nname = \"example_program_with_data\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions a"
  },
  {
    "path": "example_program_with_data/Makefile",
    "chars": 123,
    "preview": "all:\n\tcargo build --release\n\telfloader --binary target/aarch64-unknown-none/release/example_program_with_data example.bi"
  },
  {
    "path": "example_program_with_data/src/main.rs",
    "chars": 365,
    "preview": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicBool, Ordering};\n\n#[panic_handler]\nfn panic(_panic_info: &core::p"
  },
  {
    "path": "example_small_program/.cargo/config",
    "chars": 179,
    "preview": "[build]\ntarget = \"aarch64-unknown-none\"\n\n[target.aarch64-unknown-none]\nrustflags = [\"-Clink-arg=--nmagic\", \"-Clink-arg=-"
  },
  {
    "path": "example_small_program/.gitignore",
    "chars": 15,
    "preview": "/target\n/*.bin\n"
  },
  {
    "path": "example_small_program/Cargo.toml",
    "chars": 273,
    "preview": "[package]\nname = \"example_small_program\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at ht"
  },
  {
    "path": "example_small_program/Makefile",
    "chars": 119,
    "preview": "all:\n\tcargo build --release\n\telfloader --binary target/aarch64-unknown-none/release/example_small_program example.bin\n\n"
  },
  {
    "path": "example_small_program/src/main.rs",
    "chars": 194,
    "preview": "#![no_std]\n#![no_main]\n\n#[panic_handler]\nfn panic(_panic_info: &core::panic::PanicInfo) -> ! {\n    loop {}\n}\n\n#[no_mangl"
  },
  {
    "path": "src/main.rs",
    "chars": 13910,
    "preview": "use std::fs::File;\nuse std::io::{BufReader, Read, Seek, SeekFrom, BufWriter, Write};\nuse std::mem::size_of;\nuse std::pat"
  }
]

About this extraction

This page contains the full source code of the gamozolabs/elfloader GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (23.8 KB), approximately 6.8k tokens, and a symbol index with 18 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!