[
  {
    "path": ".gitignore",
    "content": "/target\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"elfloader\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 gamozolabs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Summary\n\n`elfloader` is a super simple loader for ELF files that generates a flat\nin-memory representation of the ELF.\n\nPair this with Rust and now you can write your shellcode in a proper, safe,\nhigh-level language. Any target that LLVM can target can be used, including\ncustom target specifications for really exotic platforms and ABIs. Enjoy using\nthings like `u64`s on 32-bit systems, bounds checked arrays, drop handling of\nallocations, etc :)\n\nIt simply concatenates all `LOAD` sections together, using zero-padding if\nthere are gaps, into one big flat file.\n\nThis file includes zero-initialization of `.bss` sections, and thus can be used\ndirectly as a shellcode payload.\n\nIf you don't want to waste time with fail-open linker scripts, this is probably\na great way to go.\n\nThis doesn't handle any relocations, it's on you to make sure the original ELF\nis based at the address you want it to be at.\n\n# Usage\n\nTo use this tool, simply:\n\n```\nUsage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>\n    --binary      - Don't output a FELF, output the raw loaded image with no\n                    metadata\n    --perms       - Create a FELF0002 which includes permission data, overrides\n                    --binary\n    --base=<addr> - Force the output to start at `<addr>`, zero padding from\n                    the base to the start of the first LOAD segment if needed.\n                    `<addr>` is default hex, can be overrided with `0d`, `0b`,\n                    `0x`, or `0o` prefixes.\n                    Warning: This does not _relocate_ to base, it simply starts\n                    the output at `<addr>` (adding zero bytes such that the\n                    output image can be loaded at `<addr>` instead of the\n                    original ELF base)\n    <input ELF>   - Path to input ELF\n    <output>      - Path to output file\n```\n\nTo install this tool run:\n\n`cargo install --path .`\n\nNow you can use `elfloader` from anywhere in your shell!\n\n# Dev\n\nThis project was developed live here:\n\nhttps://www.youtube.com/watch?v=x0V-CEmXQCQ\n\n# Example\n\nThere's an example in `example_small_program`, simply run `make` or `nmake`\nand this should generate an `example.bin` which is 8 bytes.\n\n```\npleb@gamey ~/elfloader/example_small_program $ make\ncargo build --release\n    Finished release [optimized] target(s) in 0.03s\nelfloader --binary target/aarch64-unknown-none/release/example_small_program example.bin\npleb@gamey ~/elfloader/example_small_program $ ls -l ./example.bin \n-rw-r--r-- 1 pleb pleb 8 Nov  8 12:27 ./example.bin\n\npleb@gamey ~/elfloader/example_small_program $ objdump -d target/aarch64-unknown-none/release/example_small_program\n\ntarget/aarch64-unknown-none/release/example_small_program:     file format elf64-littleaarch64\n\n\nDisassembly of section .text:\n\n00000000133700b0 <_start>:\n    133700b0:   8b000020        add     x0, x1, x0\n    133700b4:   d65f03c0        ret\n```\n\nNow you can write your shellcode in Rust, and you don't have to worry about\nwhether you emit `.data`, `.rodata`, `.bss`, etc. This will handle it all for\nyou!\n\nThere's also an example with `.bss` and `.rodata`\n\n```\npleb@gamey ~/elfloader/example_program_with_data $ make\ncargo build --release\n    Finished release [optimized] target(s) in 0.04s\nelfloader --binary target/aarch64-unknown-none/release/example_program_with_data example.bin\npleb@gamey ~/elfloader/example_program_with_data $ ls -l ./example.bin\n-rw-r--r-- 1 pleb pleb 29 Nov  8 12:39 ./example.bin\npleb@gamey ~/elfloader/example_program_with_data $ objdump -d target/aarch64-unknown-none/release/example_program_with_data\n\ntarget/aarch64-unknown-none/release/example_program_with_data:     file format elf64-littleaarch64\n\n\nDisassembly of section .text:\n\n0000000013370124 <_start>:\n    13370124:   90000000        adrp    x0, 13370000 <_start-0x124>\n    13370128:   90000008        adrp    x8, 13370000 <_start-0x124>\n    1337012c:   52800029        mov     w9, #0x1                        // #1\n    13370130:   91048000        add     x0, x0, #0x120\n    13370134:   3904f109        strb    w9, [x8, #316]\n    13370138:   d65f03c0        ret\npleb@gamey ~/elfloader/example_program_with_data $ readelf -l target/aarch64-unknown-none/release/example_program_with_data\n\nElf file type is EXEC (Executable file)\nEntry point 0x13370124\nThere are 4 program headers, starting at offset 64\n\nProgram Headers:\n  Type           Offset             VirtAddr           PhysAddr\n                 FileSiz            MemSiz              Flags  Align\n  LOAD           0x0000000000000120 0x0000000013370120 0x0000000013370120\n                 0x0000000000000004 0x0000000000000004  R      0x1\n  LOAD           0x0000000000000124 0x0000000013370124 0x0000000013370124\n                 0x0000000000000018 0x0000000000000018  R E    0x4\n  LOAD           0x000000000000013c 0x000000001337013c 0x000000001337013c\n                 0x0000000000000000 0x0000000000000001  RW     0x4\n  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000\n                 0x0000000000000000 0x0000000000000000  RW     0x0\n\n Section to Segment mapping:\n  Segment Sections...\n   00     .rodata \n   01     .text \n   02     .bss \n   03     \n```\n\n# Internals\n\nThis tool doesn't care about anything except for `LOAD` sections. It determines\nthe endianness (little vs big) and bitness (32 vs 64) from the ELF header,\nand from there it creates a flat image based on program header virtual\naddresses (where it's loaded), file size (number of initialized bytes) and\nmem size (size of actual memory region). The bytes are initialized from the\nfile based on the offset and file size, and this is then extended with zeros\nuntil mem size (or truncated if mem size is smaller than file size).\n\nThese `LOAD` sections are then concatenated together with zero-byte padding\nfor gaps.\n\nThis is designed to be incredibly simple, and agnostic to the ELF input. It\ncould be an executable, object file, shared object, core dump, etc, doesn't\nreally care. It'll simply give you the flat representation of the memory,\nnothing more.\n\nThis allows you to turn any ELF into shellcode, or a simpler file format that\nis easier to load in hard-to-reach areas, like embedded devices. Personally,\nI developed this for my MIPS NT 4.0 loader which allows me to run Rust code.\n\n# FELF0001 format\n\nThis tool by default generates a FELF file format. This is a Falk ELF. This\nis a simple file format:\n\n```\nFELF0001 - Magic header\nentry    - 64-bit little endian integer of the entry point address\nbase     - 64-bit little endian integer of the base address to load the image\n<image>  - Rest of the file is the raw image, to be loaded at `base` and jumped\n           into at `entry`\n```\n\n# FELF0002 format (when --perms flag is used)\n\nThis tool by default generates a FELF file format. This is a Falk ELF. This\nis a simple file format with permissions:\n\n```\nFELF0002 - Magic header\nentry    - 64-bit little endian integer of the entry point address\nbase     - 64-bit little endian integer of the base address to load the image\n<image>  - Rest of the file is the raw image, to be loaded at `base` and jumped\n           into at `entry`\n<perms>  - Permissions, matching the bytes of <image> where the byte contains\n           the following flags bitwise or-ed together:\n           0x01 - Executable, 0x02 - Writable, 0x04 - Readable\n           Padding bytes will be 0x00, and thus have no permissions for any\n           access\n```\n\n"
  },
  {
    "path": "example_program_with_data/.cargo/config",
    "content": "[build]\ntarget = \"aarch64-unknown-none\"\n\n[target.aarch64-unknown-none]\nrustflags = [\"-Clink-arg=--nmagic\", \"-Clink-arg=--image-base=0x13370000\"]\n\n[unstable]\nbuild-std = [\"core\"]\n\n"
  },
  {
    "path": "example_program_with_data/.gitignore",
    "content": "/target\n/*.bin\n"
  },
  {
    "path": "example_program_with_data/Cargo.toml",
    "content": "[package]\nname = \"example_program_with_data\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\n\n[profile.dev]\npanic = \"abort\"\n\n[profile.release]\nopt-level = \"z\"\npanic = \"abort\"\n\n"
  },
  {
    "path": "example_program_with_data/Makefile",
    "content": "all:\n\tcargo build --release\n\telfloader --binary target/aarch64-unknown-none/release/example_program_with_data example.bin\n\n"
  },
  {
    "path": "example_program_with_data/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicBool, Ordering};\n\n#[panic_handler]\nfn panic(_panic_info: &core::panic::PanicInfo) -> ! {\n    loop {}\n}\n\n// BSS example\n#[no_mangle]\npub static BSSTHING: AtomicBool = AtomicBool::new(false);\n\n#[no_mangle]\npub extern fn _start() -> *const u8 {\n    BSSTHING.store(true, Ordering::Relaxed);\n    \"asdf\".as_ptr()\n}\n\n"
  },
  {
    "path": "example_small_program/.cargo/config",
    "content": "[build]\ntarget = \"aarch64-unknown-none\"\n\n[target.aarch64-unknown-none]\nrustflags = [\"-Clink-arg=--nmagic\", \"-Clink-arg=--image-base=0x13370000\"]\n\n[unstable]\nbuild-std = [\"core\"]\n\n"
  },
  {
    "path": "example_small_program/.gitignore",
    "content": "/target\n/*.bin\n"
  },
  {
    "path": "example_small_program/Cargo.toml",
    "content": "[package]\nname = \"example_small_program\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\n\n[profile.dev]\npanic = \"abort\"\n\n[profile.release]\nopt-level = \"z\"\npanic = \"abort\"\n\n"
  },
  {
    "path": "example_small_program/Makefile",
    "content": "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",
    "content": "#![no_std]\n#![no_main]\n\n#[panic_handler]\nfn panic(_panic_info: &core::panic::PanicInfo) -> ! {\n    loop {}\n}\n\n#[no_mangle]\npub extern fn _start(x: u64, y: u64) -> u64 {\n    x.wrapping_add(y)\n}\n\n"
  },
  {
    "path": "src/main.rs",
    "content": "use std::fs::File;\nuse std::io::{BufReader, Read, Seek, SeekFrom, BufWriter, Write};\nuse std::mem::size_of;\nuse std::path::Path;\n\n/// Friendly wrapper around [`Error`]\ntype Result<T> = std::result::Result<T, Error>;\n\n/// Error types\n#[derive(Debug)]\npub enum Error {\n    /// Failed to open the ELF file\n    Open(std::io::Error),\n\n    /// Failed to consume a field from the input\n    Consume(&'static str, std::io::Error),\n\n    /// ELF magic was missing from file\n    MissingMagic,\n\n    /// ELF indiciated a bitness which was not a valid variant\n    InvalidBitness(u8),\n\n    /// ELF indiciated an endianness which was not a valid variant\n    InvalidEndianness(u8),\n\n    /// ELF version was unknown\n    UnknownVersion(u8),\n\n    /// Expected an executable but got a different ELF type\n    ExpectedExecutable(u16),\n\n    /// Seeking to the program headers failed\n    SeekProgramHeaders(std::io::Error),\n\n    /// Seeking the initialized data for a loaded segment failed\n    LoadSeek(std::io::Error),\n\n    /// Reading initialized bytes from file failed\n    ReadInit(std::io::Error),\n\n    /// Creating the FELF failed\n    CreateFelf(std::io::Error),\n\n    /// Writing the FELF failed\n    WriteFelf(std::io::Error),\n\n    /// Truncated integer for filesz\n    IntegerTruncationFileSz,\n\n    /// Truncated integer for memsz\n    IntegerTruncationMemSz,\n\n    /// Trucated integer for offset\n    IntegerTruncationOffset,\n\n    /// Integer overflow when computing current address\n    IntegerOverflowCurrentAddress,\n\n    /// Multiple sections overlap where they are loaded\n    SectionOverlap,\n\n    /// The ELF didn't supply any segments to load\n    NoLoadSegments,\n\n    /// The `--base` provided did not parse into a `u64` correctly\n    InvalidBase(std::num::ParseIntError),\n}\n\n/// Consume bytes from a reader\nmacro_rules! consume {\n    // Consume a `u8`\n    ($reader:expr, $field:expr) => {{\n        // Create buffer\n        let mut tmp = [0u8; 1];\n        $reader.read_exact(&mut tmp).map(|_| {\n            tmp[0]\n        }).map_err(|x| Error::Consume($field, x))\n    }};\n\n    ($reader:expr, $ty:ty, $endian:expr, $field:expr) => {{\n        // Create buffer for type\n        let mut tmp = [0u8; size_of::<$ty>()];\n\n        match $endian {\n            Endianness::Little => {\n                // Read the bytes and convert\n                $reader.read_exact(&mut tmp).map(|_| {\n                    <$ty>::from_le_bytes(tmp)\n                }).map_err(|x| Error::Consume($field, x))\n            }\n            Endianness::Big => {\n                // Read the bytes and convert\n                $reader.read_exact(&mut tmp).map(|_| {\n                    <$ty>::from_be_bytes(tmp)\n                }).map_err(|x| Error::Consume($field, x))\n            }\n        }\n    }};\n\n    ($reader:expr, $size:expr, $field:expr) => {{\n        // Create buffer for type\n        let mut tmp = [0u8; $size];\n\n        // Read the bytes and convert\n        $reader.read_exact(&mut tmp).map(|_| {\n            tmp\n        }).map_err(|x| Error::Consume($field, x))\n    }};\n}\n\nmacro_rules! consume_native {\n    ($reader:expr, $bitness:ident, $endian:expr, $field:expr) => {{\n        match $bitness {\n            Bitness::Bits32 => {\n                consume!($reader, u32, $endian, $field).map(|x| x as u64)\n            },\n            Bitness::Bits64 => consume!($reader, u64, $endian, $field),\n        }\n    }};\n}\n\n/// Bitnesses for ELF files\n#[derive(Debug)]\nenum Bitness {\n    /// 32-bit ELF\n    Bits32,\n\n    /// 64-bit ELF\n    Bits64,\n}\n\nimpl TryFrom<u8> for Bitness {\n    type Error = Error;\n\n    fn try_from(val: u8) -> Result<Self> {\n        Ok(match val {\n            1 => Bitness::Bits32,\n            2 => Bitness::Bits64,\n            _ => return Err(Error::InvalidBitness(val)),\n        })\n    }\n}\n\n/// Endianness for ELF files\n#[derive(Debug)]\nenum Endianness {\n    /// Little endian\n    Little,\n\n    /// Big endian\n    Big,\n}\n\nimpl TryFrom<u8> for Endianness {\n    type Error = Error;\n\n    fn try_from(val: u8) -> Result<Self> {\n        Ok(match val {\n            1 => Endianness::Little,\n            2 => Endianness::Big,\n            _ => return Err(Error::InvalidEndianness(val)),\n        })\n    }\n}\n\n/// Loaded segment\nconst PT_LOAD: u32 = 1;\n\n/// Executable segment\nconst PF_X: u32 = 1 << 0;\n\n/// Writable segment\nconst PF_W: u32 = 1 << 1;\n\n/// Readable segment\nconst PF_R: u32 = 1 << 2;\n\n/// Load an ELF from disk\n///\n/// Returns:\n///\n/// `(entry virtual address, base address for flat map, flat map contents)`\npub fn write_file(path: impl AsRef<Path>, base: Option<u64>,\n        mut output: impl Write, binary: bool, save_perms: bool) -> Result<()> {\n    // Open the file\n    let mut reader =\n        BufReader::new(File::open(path).map_err(Error::Open)?);\n\n    // Check that this is an ELF\n    if &consume!(reader, 4, \"ELF magic\")? != b\"\\x7fELF\" {\n        return Err(Error::MissingMagic);\n    }\n\n    // Get the bitness and endianness\n    let bt = Bitness::try_from(consume!(reader, \"bitness\")?)?;\n    let en = Endianness::try_from(consume!(reader, \"endianness\")?)?;\n\n    // Make sure the ELF version matches\n    let version = consume!(reader, \"version\")?;\n    if version != 1 {\n        return Err(Error::UnknownVersion(version));\n    }\n\n    // We don't care about the ABI\n    let _abi    = consume!(reader, \"abi\")?;\n    let _abiver = consume!(reader, \"abi version\")?;\n    let _pad    = consume!(reader, 7, \"padding\")?;\n\n    let _objtyp  = consume!(reader, u16, en, \"type\")?;\n    let _machine = consume!(reader, u16, en, \"machine\")?;\n    let _elfver  = consume!(reader, u32, en, \"ELF version\")?;\n\n    let entry = consume_native!(reader, bt, en, \"entry point\")?;\n\n    let phoff  = consume_native!(reader, bt, en, \"program header offset\")?;\n    let _shoff = consume_native!(reader, bt, en, \"section header offset\")?;\n\n    let _flags  = consume!(reader, u32, en, \"flags\")?;\n    let _ehsize = consume!(reader, u16, en, \"ELF header size\")?;\n    let _phesz  = consume!(reader, u16, en, \"program header entry size\")?;\n    let phcnt   = consume!(reader, u16, en, \"program header entries\")?;\n\n    // Seek to the program headers\n    reader.seek(SeekFrom::Start(phoff))\n        .map_err(Error::SeekProgramHeaders)?;\n\n    // List of sections to load\n    let mut load = Vec::new();\n\n    // Go through each program header entry\n    for _ in 0..phcnt {\n        // Get header type\n        let typ = consume!(reader, u32, en, \"PH type\")?;\n\n        // 64-bit has flags here\n        let mut flags = if matches!(bt, Bitness::Bits64) {\n            consume!(reader, u32, en, \"PH flags\")?\n        } else { 0 };\n\n        // Parse program header\n        let offset = consume_native!(reader, bt, en, \"PH offset\")?;\n        let vaddr  = consume_native!(reader, bt, en, \"PH vaddr\")?;\n        let _paddr = consume_native!(reader, bt, en, \"PH paddr\")?;\n        let filesz = consume_native!(reader, bt, en, \"PH filesz\")?;\n        let memsz  = consume_native!(reader, bt, en, \"PH memsz\")?;\n\n        // 32-bit has flags here\n        if matches!(bt, Bitness::Bits32) {\n            flags = consume!(reader, u32, en, \"PH flags\")?\n        }\n\n        let _align = consume_native!(reader, bt, en, \"PH align\")?;\n\n        if typ == PT_LOAD {\n            // If the section is zero size, skip it entirely\n            if memsz == 0 {\n                continue;\n            }\n\n            // Read initialized bytes from file if needed\n            let mut bytes = Vec::new();\n            if filesz > 0 {\n                // Save the current position\n                let stream_pos = reader.stream_position()\n                    .map_err(Error::LoadSeek)?;\n\n                // Seek to the bytes in the file\n                reader.seek(SeekFrom::Start(offset))\n                    .map_err(Error::LoadSeek)?;\n\n                // Resize buffer\n                bytes.resize(filesz.try_into()\n                    .map_err(|_| Error::IntegerTruncationFileSz)?, 0u8);\n\n                // Read initialized bytes from file\n                reader.read_exact(&mut bytes).map_err(Error::ReadInit)?;\n\n                // Seek back to where we were\n                reader.seek(SeekFrom::Start(stream_pos))\n                    .map_err(Error::LoadSeek)?;\n            }\n\n            // Pad out with zeros until memory size\n            bytes.resize(memsz.try_into()\n                .map_err(|_| Error::IntegerTruncationMemSz)?, 0u8);\n\n            // Determine permissions for this segment\n            let r = (flags & PF_R) != 0;\n            let w = (flags & PF_W) != 0;\n            let x = (flags & PF_X) != 0;\n\n            // Save the address to load to and the bytes\n            load.push((vaddr, bytes, r, w, x));\n        }\n    }\n\n    // Sort load sections by virtual address\n    load.sort_by_key(|x| x.0);\n\n    // Start load at the specified `base`, otherwise use the lowest address of\n    // all the LOAD sections\n    let lowest_addr = base.unwrap_or(\n        load.get(0).ok_or(Error::NoLoadSegments)?.0);\n\n    if !binary {\n        // Write the FELF header\n        if !save_perms {\n            output.write_all(b\"FELF0001\").map_err(Error::WriteFelf)?;\n        } else {\n            output.write_all(b\"FELF0002\").map_err(Error::WriteFelf)?;\n        }\n\n        output\n            .write_all(&entry.to_le_bytes())\n            .map_err(Error::WriteFelf)?;\n        output\n            .write_all(&lowest_addr.to_le_bytes())\n            .map_err(Error::WriteFelf)?;\n    }\n\n    // Permissions vector\n    let mut perms = Vec::new();\n\n    // Write everything!\n    let mut cur_addr = lowest_addr;\n    for (vaddr, bytes, r, w, x) in load {\n        // Get the offset from where we are\n        let offset: usize = vaddr.checked_sub(cur_addr)\n            .ok_or(Error::SectionOverlap)?\n            .try_into()\n            .map_err(|_| Error::IntegerTruncationOffset)?;\n\n        // Pad out loaded representation until `vaddr`\n        const ZERO_BUF: [u8; 1024 * 8] = [0u8; 1024 * 8];\n\n        let mut padding = offset;\n        while padding > ZERO_BUF.len() {\n            output.write_all(&ZERO_BUF).map_err(Error::WriteFelf)?;\n            if save_perms {\n                perms.write_all(&ZERO_BUF).map_err(Error::WriteFelf)?;\n            }\n            padding -= ZERO_BUF.len();\n        }\n        output.write_all(&ZERO_BUF[..padding])\n            .map_err(Error::WriteFelf)?;\n        if save_perms {\n            perms.write_all(&ZERO_BUF[..padding])\n                .map_err(Error::WriteFelf)?;\n        }\n\n        // Place in the bytes\n        output.write_all(&bytes).map_err(Error::WriteFelf)?;\n\n        if save_perms {\n            // Place in all the permission bytes\n            let perm_flags =\n                if r { PF_R } else { 0 } |\n                if w { PF_W } else { 0 } |\n                if x { PF_X } else { 0 };\n            perms.resize(perms.len() + bytes.len(), perm_flags as u8);\n        }\n\n        // Update current address\n        cur_addr = vaddr\n            .checked_add(bytes.len() as u64)\n            .ok_or(Error::IntegerOverflowCurrentAddress)?;\n    }\n\n    if save_perms {\n        // Add permissions to file\n        output.write_all(&perms).map_err(Error::WriteFelf)?;\n    }\n\n    output.flush().map_err(Error::WriteFelf)?;\n\n    Ok(())\n}\n\n/// Entry point\nfn main() -> Result<()> {\n    // Get the command line arguments\n    let mut args = std::env::args().collect::<Vec<_>>();\n\n    // Check if the `--binary` flag was specified\n    let mut binary = false;\n    args.retain(|x| if x == \"--binary\" { binary = true; false } else { true });\n\n    // Check if the `--perms` flag was specified\n    let mut perms = false;\n    args.retain(|x| if x == \"--perms\" { perms = true; false } else { true });\n\n    // Perms flag overrides the binary flag\n    if perms {\n        binary = false;\n    }\n\n    // Check if the `--base=` flag was specified\n    let mut base = None;\n    args.retain(|x| {\n        if x.starts_with(\"--base=\") {\n            // Default to hex, skip over `--base=`\n            let mut radix = 16;\n            let mut x     = &x[7..];\n\n            if x.starts_with(\"0x\") {\n                radix = 16;\n                x = &x[2..];\n            } else if x.starts_with(\"0o\") {\n                radix = 8;\n                x = &x[2..];\n            } else if x.starts_with(\"0b\") {\n                radix = 2;\n                x = &x[2..];\n            } else if x.starts_with(\"0d\") {\n                radix = 10;\n                x = &x[2..];\n            }\n\n            // Convert to a `u64`\n            base = Some(u64::from_str_radix(x, radix)\n                .map_err(Error::InvalidBase));\n\n            // Don't keep this argument\n            false\n        } else { true }\n    });\n\n    // Can't use `?` in closure\n    let base = if let Some(base) = base {\n        Some(base?)\n    } else { None };\n\n    if args.len() != 3 {\n        println!(\nr#\"Usage: elfloader [--perms] [--binary] [--base=<addr>] <input ELF> <output>\n    --binary      - Don't output a FELF, output the raw loaded image with no\n                    metadata\n    --perms       - Create a FELF0002 which includes permission data, overrides\n                    --binary\n    --base=<addr> - Force the output to start at `<addr>`, zero padding from\n                    the base to the start of the first LOAD segment if needed.\n                    `<addr>` is default hex, can be overrided with `0d`, `0b`,\n                    `0x`, or `0o` prefixes.\n                    Warning: This does not _relocate_ to base, it simply starts\n                    the output at `<addr>` (adding zero bytes such that the\n                    output image can be loaded at `<addr>` instead of the\n                    original ELF base)\n    <input ELF>   - Path to input ELF\n    <output>      - Path to output file\"#);\n        return Ok(());\n    }\n\n    // Create the output file\n    let mut output = BufWriter::new(File::create(&args[2])\n        .map_err(Error::CreateFelf)?);\n    write_file(&args[1], base, &mut output, binary, perms)?;\n\n    Ok(())\n}\n\n"
  }
]