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(())
}
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
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.