├── .gitignore ├── media ├── example-eh.png ├── example-fn.png ├── example-ph.png ├── example-sh.png ├── example-sym.png ├── example-eh-sym.png ├── example-fn-cfi.png ├── example-header.png ├── example-sections.png ├── example-sh-strtab.png └── example-sh-eh_frame_hdr.png ├── Cargo.toml ├── src ├── elf.rs ├── main.rs ├── print.rs ├── args.rs ├── header.rs ├── sym.rs ├── sections.rs ├── func.rs └── eh.rs ├── README.md ├── Cargo.lock └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /media/example-eh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-eh.png -------------------------------------------------------------------------------- /media/example-fn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-fn.png -------------------------------------------------------------------------------- /media/example-ph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-ph.png -------------------------------------------------------------------------------- /media/example-sh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-sh.png -------------------------------------------------------------------------------- /media/example-sym.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-sym.png -------------------------------------------------------------------------------- /media/example-eh-sym.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-eh-sym.png -------------------------------------------------------------------------------- /media/example-fn-cfi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-fn-cfi.png -------------------------------------------------------------------------------- /media/example-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-header.png -------------------------------------------------------------------------------- /media/example-sections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-sections.png -------------------------------------------------------------------------------- /media/example-sh-strtab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-sh-strtab.png -------------------------------------------------------------------------------- /media/example-sh-eh_frame_hdr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevin-lesenechal/elf-info/HEAD/media/example-sh-eh_frame_hdr.png -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Copyright © 2023 Kévin Lesénéchal # 3 | # This file is part of the elf-info CLI tool. # 4 | # # 5 | # elf-info is free software; you can redistribute it and/or modify it under # 6 | # the terms of the GNU General Public License as published by the Free # 7 | # Software Foundation; either version 3 of the License, or (at your option) # 8 | # any later version. See LICENSE file for more information. # 9 | ############################################################################## 10 | 11 | [package] 12 | name = "elf-info" 13 | version = "0.3.0" 14 | description = "Inspect and dissect an ELF file with pretty formatting." 15 | authors = ["Kévin Lesénéchal "] 16 | edition = "2021" 17 | license = "GPL-3.0" 18 | repository = "https://github.com/kevin-lesenechal/elf-info" 19 | categories = ["development-tools", "command-line-utilities"] 20 | keywords = ["elf", "disassembly", "objdump", "readelf"] 21 | 22 | [[bin]] 23 | name = "elf" 24 | path = "src/main.rs" 25 | 26 | [dependencies] 27 | clap = { version = "4.0", features = ["derive", "env"] } 28 | goblin = { version = "0.6.1", features = ["elf64"] } 29 | gimli = "0.27.2" 30 | anyhow = "1.0" 31 | memmap2 = "0.5.7" 32 | rustc-demangle = "0.1.21" 33 | regex = "1.6" 34 | iced-x86 = "1.17" 35 | byteorder = "1.4.3" 36 | -------------------------------------------------------------------------------- /src/elf.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use goblin::elf::{Elf, ProgramHeader, Sym, Symtab}; 12 | use goblin::strtab::Strtab; 13 | use rustc_demangle::demangle; 14 | 15 | pub fn symbol_file_offset(elf: &Elf, sym_name: &str) -> Option { 16 | // TODO: handle stripped binaries 17 | let sym = find_symbol(&elf.syms, &elf.strtab, sym_name) 18 | .or_else(|| find_symbol(&elf.dynsyms, &elf.dynstrtab, sym_name))?; 19 | let ph = ph_by_vaddr(elf, sym.st_value)?; 20 | 21 | Some(ph.p_offset + (sym.st_value - ph.p_vaddr)) 22 | } 23 | 24 | pub fn find_symbol(tab: &Symtab, strtab: &Strtab, name: &str) -> Option { 25 | tab.iter() 26 | .find(|sym| { 27 | strtab.get_at(sym.st_name).map(|n| n == name).unwrap_or(false) 28 | }) 29 | .or_else(|| tab.iter().find(|sym| { 30 | strtab.get_at(sym.st_name) 31 | .map(|n| demangle(n).to_string() == name) 32 | .unwrap_or(false) 33 | })) 34 | } 35 | 36 | pub fn find_symbol_by_addr(tab: &Symtab, addr: u64) -> Option { 37 | tab.iter() 38 | .find(|sym| { 39 | (sym.st_value..(sym.st_value + sym.st_size)).contains(&addr) 40 | }) 41 | } 42 | 43 | pub fn ph_by_vaddr<'a>(elf: &'a Elf, vaddr: u64) -> Option<&'a ProgramHeader> { 44 | elf.program_headers.iter() 45 | .find(|&ph| (ph.p_vaddr..(ph.p_vaddr + ph.p_memsz)).contains(&vaddr)) 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elf-info: ergonomic CLI ELF inspection tool # 2 | 3 | `elf-info` can inspect and display information about ELF binaries, the executable 4 | format mostly used on Linux. This CLI tools aims at being ergonomic, without the 5 | need of using two different commands (`objdump` and `readelf`) that have impossible 6 | to remember options. 7 | 8 | `elf-info` commands and options are easy to remember yet short and easy to type. 9 | Efforts are made on the presentation of the data, using colors and ASCII tables. 10 | 11 | For better ergonomics, you can set a `ELF` environment variable so not to have 12 | to repeat the file name on each command. 13 | 14 | ## Quick and ergonomic disassembly ## 15 | 16 | The `fn` subcommand is my favorite: give it a function’s name and its full 17 | pretty-formatted disassembly is displayed. 18 | 19 | ![Disassembly of main](media/example-fn.png) 20 | 21 | By passing `--cfi` you can superimpose call frame information (CFI): 22 | 23 | ![Disassembly of main with CFI](media/example-fn-cfi.png) 24 | 25 | ## Symbol tables ## 26 | 27 | You can quickly list all symbols, and demangle the names. 28 | 29 | ![List of all symbols](media/example-sym.png) 30 | 31 | Pass `--no-demangle` for not demangling.\ 32 | Pass `-l` / `-g` / `-w` / `-d` to only display local / global / weak / defined symbols.\ 33 | Pass `-t ` to filter by symbol type.\ 34 | Pass `-D` to only display dynamic symbols.\ 35 | Pass `-f` to filter by symbol name using a regular expression. 36 | 37 | ## Sections ## 38 | 39 | ### List all sections ### 40 | 41 | ![List of all sections](media/example-sections.png) 42 | 43 | ### Show specific section content ### 44 | 45 | ![One specific ELF section](media/example-sh.png) 46 | 47 | ### String table sections ### 48 | 49 | For some type of sections, `elf-info` is able to interpret its content and 50 | display it in a more usable way. For example, for a table of strings, all strings 51 | are listed. You can add the option `-x` or `--hexdump` to always display a hexdump. 52 | 53 | ![.strtab section](media/example-sh-strtab.png) 54 | 55 | ### .eh_frame_hdr section ### 56 | 57 | In the same way, `elf-info` can pretty print the content of `.eh_frame_hdr`. 58 | 59 | ![.strtab section](media/example-sh-eh_frame_hdr.png) 60 | 61 | ## ELF header ## 62 | 63 | ![ELF header](media/example-header.png) 64 | 65 | ## Program headers ## 66 | 67 | ![Program headers](media/example-ph.png) 68 | 69 | ## Clear and structured exception-handling information ## 70 | 71 | ![Exception-handling information](media/example-eh.png) 72 | 73 | Quickly find EH information for a specific symbol with `-s`: 74 | 75 | ![Exception-handling information for a symbol](media/example-eh-sym.png) 76 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use std::fs::File; 12 | use clap::Parser; 13 | use goblin::Object; 14 | use goblin::elf::Elf; 15 | use memmap2::Mmap; 16 | use anyhow::{anyhow, bail, Context, Result}; 17 | 18 | use crate::args::{Options, Command, SymbolsArgs}; 19 | use crate::eh::eh; 20 | use crate::func::do_fn; 21 | use crate::header::{header, program_headers}; 22 | use crate::print::{PairTable, print_header, SizePrint}; 23 | use crate::sections::{all_sections, one_section}; 24 | use crate::sym::all_symbols; 25 | 26 | mod args; 27 | mod print; 28 | mod sections; 29 | mod func; 30 | mod sym; 31 | mod header; 32 | mod elf; 33 | mod eh; 34 | 35 | fn main() { 36 | let args = Options::parse(); 37 | 38 | if let Err(e) = run(&args) { 39 | eprintln!("\x1b[1;31merror\x1b[0m: {e:#}"); 40 | std::process::exit(1); 41 | } 42 | } 43 | 44 | fn run(args: &Options) -> Result<()> { 45 | let elf_path = args.elf.clone().or_else( 46 | || std::env::var_os("ELF").map(|s| s.into()) 47 | ).ok_or_else( 48 | || anyhow!("No ELF file provided either from the command line nor via the `ELF` env variable.") 49 | )?; 50 | 51 | let f = File::open(&elf_path) 52 | .with_context(|| 53 | format!("{}: couldn't open ELF", elf_path.display()) 54 | )?; 55 | let map = unsafe { Mmap::map(&f) } 56 | .with_context(|| format!("{}: couldn't mmap ELF", elf_path.display()))?; 57 | let bytes = &*map; 58 | 59 | let obj = Object::parse(bytes) 60 | .with_context(|| 61 | format!("{}: failed to parse ELF", elf_path.display()) 62 | )?; 63 | let elf = match obj { 64 | Object::Elf(elf) => Box::new(elf), 65 | _ => bail!("{}: unsupported ELF format", elf_path.display()), 66 | }; 67 | 68 | match args.command.as_ref().unwrap_or(&Command::Summary) { 69 | Command::Summary => summary(&elf), 70 | Command::Header => header(&elf), 71 | Command::ProgramHeader => program_headers(&elf), 72 | Command::Sections => all_sections(&elf), 73 | Command::Section(opts) => one_section(&elf, bytes, opts)?, 74 | Command::Symbols(opts) => all_symbols(&elf, opts), 75 | Command::Fn(opts) => do_fn(&elf, bytes, opts)?, 76 | Command::Eh(opts) => eh(&elf, bytes, opts.clone())?, 77 | _ => todo!(), 78 | } 79 | 80 | Ok(()) 81 | } 82 | 83 | fn summary(elf: &Elf) { 84 | header(elf); 85 | println!(); 86 | program_headers(elf); 87 | println!(); 88 | all_sections(elf); 89 | } 90 | -------------------------------------------------------------------------------- /src/print.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use std::fmt::{Display, Formatter}; 12 | use goblin::container::Container; 13 | 14 | pub fn print_header(name: &str) { 15 | print!("\x1b[1;96m───┤ {name} ├"); 16 | println!("{:─w$}\x1b[0m │ ", w = self.0); 24 | } 25 | } 26 | 27 | #[derive(Copy, Clone)] 28 | pub struct SizePrint { 29 | size: Container, 30 | } 31 | 32 | impl SizePrint { 33 | pub fn new(size: Container) -> Self { 34 | Self { size } 35 | } 36 | 37 | pub fn hex(&self, value: u64) -> SizePrintHex { 38 | SizePrintHex { size: self.size, value } 39 | } 40 | } 41 | 42 | pub struct SizePrintHex { 43 | size: Container, 44 | value: u64, 45 | } 46 | 47 | impl Display for SizePrintHex { 48 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 49 | match self.size { 50 | Container::Little => { 51 | write!(f, "0x{:04x}", &(self.value >> 16))?; 52 | write!(f, "'")?; 53 | write!(f, "{:04x}", &(self.value & 0xffff)) 54 | }, 55 | Container::Big => { 56 | write!(f, "0x{:08x}", &(self.value >> 32))?; 57 | write!(f, "'")?; 58 | write!(f, "{:08x}", &(self.value & 0xffffffff)) 59 | }, 60 | } 61 | } 62 | } 63 | 64 | pub struct BinSize(pub u64); 65 | 66 | impl Display for BinSize { 67 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 68 | let size; 69 | let unit; 70 | 71 | if self.0 < 1024 { 72 | size = self.0 as f64; 73 | unit = "B"; 74 | } else if self.0 < 1024 * 1024 { 75 | size = self.0 as f64 / 1024.0; 76 | unit = "KiB"; 77 | } else if self.0 < 1024 * 1024 * 1024 { 78 | size = self.0 as f64 / 1024.0 / 1024.0; 79 | unit = "MiB"; 80 | } else { 81 | size = self.0 as f64 / 1024.0 / 1024.0 / 1024.0; 82 | unit = "GiB"; 83 | } 84 | 85 | let w = f.width().unwrap_or(0); 86 | let uw = if w == 0 { unit.len() } else { 3 }; 87 | let nw = w.saturating_sub(uw + 1); 88 | 89 | if unit == "B" { 90 | write!(f, "{:nw$} {:= b' ' && b <= b'~' { 125 | print!("{}", b as char); 126 | } else { 127 | print!("\x1b[90m╳\x1b[0m"); 128 | } 129 | } else { 130 | print!("\x1b[90m─\x1b[0m"); 131 | } 132 | } 133 | 134 | print!("\x1b[97m│\x1b[0m\n"); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use std::path::PathBuf; 12 | use clap::{Args, Parser, Subcommand, ValueEnum}; 13 | use regex::Regex; 14 | 15 | #[derive(Parser, Debug)] 16 | #[command(author, version, about, long_about = None)] 17 | pub struct Options { 18 | #[arg()] 19 | pub elf: Option, 20 | 21 | #[command(subcommand)] 22 | pub command: Option, 23 | } 24 | 25 | #[derive(Subcommand, Debug)] 26 | pub enum Command { 27 | /// Give a brief summary of the ELF: file header, program headers, and 28 | /// sections' header. 29 | Summary, 30 | 31 | /// Display information in ELF's header. 32 | #[clap(alias = "h")] 33 | Header, 34 | 35 | /// List all program headers. 36 | #[clap(alias = "ph")] 37 | ProgramHeader, 38 | 39 | /// List all sections. 40 | Sections, 41 | 42 | /// Display detailed information of one specific section, including its 43 | /// content. The formatting used depends on the type of section. 44 | #[clap(alias = "sh")] 45 | Section(SectionArgs), 46 | 47 | /// List all symbols. 48 | #[clap(alias = "sym")] 49 | Symbols(SymbolsArgs), 50 | 51 | /// Disassemble a function. 52 | Fn(FnArgs), 53 | 54 | /// List all relocation entries. 55 | #[clap(alias = "rel")] 56 | Relocations, 57 | 58 | /// Display call frame information for exception handling. 59 | Eh(EhArgs), 60 | } 61 | 62 | #[derive(Args, Debug)] 63 | pub struct SymbolsArgs { 64 | #[arg(long)] 65 | pub no_demangle: bool, 66 | 67 | /// Display dynamic symbols. 68 | #[arg(short = 'D', long)] 69 | pub dynamic: bool, 70 | 71 | /// Try to filter out symbols generated for the Rust's standard, core, and 72 | /// alloc libraries. 73 | #[arg(long)] 74 | pub no_rust_std: bool, 75 | 76 | /// Only show symbols that matches a PCRE regex. 77 | #[arg(short = 'f', long)] 78 | pub filter: Option, 79 | 80 | /// Only display local symbols. 81 | #[arg(long, short = 'l')] 82 | pub local: bool, 83 | 84 | /// Only display global symbols, this includes undefined symbols. 85 | #[arg(long, short = 'g')] 86 | pub global: bool, 87 | 88 | /// Only display weak symbols. 89 | #[arg(long, short = 'w')] 90 | pub weak: bool, 91 | 92 | /// Only display symbols with default visibility. 93 | #[arg(long, short = 'v')] 94 | pub visible: bool, 95 | 96 | /// Only display defined symbols. 97 | #[arg(long, short = 'd')] 98 | pub defined: bool, 99 | 100 | /// Only display symbols of a specific type. 101 | #[arg(long, short = 't')] 102 | pub r#type: Option, 103 | } 104 | 105 | #[derive(Clone, ValueEnum, Debug)] 106 | pub enum SymbolType { 107 | None, 108 | Func, 109 | Section, 110 | Object, 111 | File, 112 | Common, 113 | Tls, 114 | Num, 115 | } 116 | 117 | impl SymbolType { 118 | pub fn to_st_type(&self) -> u8 { 119 | use goblin::elf::sym::*; 120 | match self { 121 | Self::None => STT_NOTYPE, 122 | Self::Func => STT_FUNC, 123 | Self::Section => STT_SECTION, 124 | Self::Object => STT_OBJECT, 125 | Self::File => STT_FILE, 126 | Self::Common => STT_COMMON, 127 | Self::Tls => STT_TLS, 128 | Self::Num => STT_NUM, 129 | } 130 | } 131 | } 132 | 133 | #[derive(Args, Debug)] 134 | pub struct SectionArgs { 135 | /// The section name, starting with a period. 136 | #[arg()] 137 | pub name: Option, 138 | 139 | /// Write the entire section's content into a file. 140 | #[arg(short = 'o', long)] 141 | pub output: Option, 142 | 143 | /// Always display the content has a hexdump, regardless of the section's 144 | /// type. 145 | #[arg(long, short = 'x')] 146 | pub hexdump: bool, 147 | 148 | /// The maximum number of bytes to export or dump as hexdump. 149 | #[arg(short = 'n', long)] 150 | pub size: Option, 151 | 152 | /// A number of bytes to skip for export or hexdump. 153 | #[arg(short = 's', long)] 154 | pub skip: Option, 155 | } 156 | 157 | #[derive(Clone, Copy, ValueEnum, Debug, Default)] 158 | pub enum Syntax { 159 | /// GNU assembler (AT&T) 160 | #[default] 161 | Att, 162 | /// Intel 163 | Intel, 164 | } 165 | 166 | #[derive(Args, Debug)] 167 | pub struct FnArgs { 168 | #[arg()] 169 | pub name: String, 170 | 171 | /// The parameter is a memory address, not a symbol name. The address is 172 | /// always parsed as hexadecimal, and can start with `0x` or not. 173 | #[arg(long, short = 'a')] 174 | pub address: bool, 175 | 176 | /// Superimpose call-frame information extracted from `.eh_frame`. 177 | #[arg(long)] 178 | pub cfi: bool, 179 | 180 | /// Syntax to use to format the disassembly. 181 | #[arg(long, value_enum, env = "ELF_INFO_ASM_SYNTAX", default_value_t)] 182 | pub syntax: Syntax, 183 | } 184 | 185 | #[derive(Args, Debug, Clone, Default)] 186 | pub struct EhArgs { 187 | /// A specific section name to parse for call frame information entries. 188 | /// If none is specified, elf-info will look for a `.eh_frame` section, or 189 | /// `.debug_frame` section. 190 | #[arg(long)] 191 | pub section: Option, 192 | 193 | /// Only display FDEs that contains the address of this symbol. 194 | #[arg(long, short = 's')] 195 | pub symbol: Option, 196 | 197 | /// Only display FDEs that contains this address. 198 | #[arg(long)] 199 | pub address: Option, 200 | } 201 | -------------------------------------------------------------------------------- /src/header.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use goblin::container::{Container, Endian}; 12 | use goblin::elf::Elf; 13 | use goblin::elf::program_header::pt_to_str; 14 | 15 | use crate::{PairTable, print_header, SizePrint}; 16 | 17 | pub fn header(elf: &Elf) { 18 | let h = &elf.header; 19 | let container = elf.header.container().unwrap_or(Container::Big); 20 | let sp = SizePrint::new(container); 21 | let table = PairTable(22); 22 | 23 | print_header("ELF HEADER"); 24 | 25 | table.field("Version"); 26 | println!("{}", h.e_version); 27 | 28 | table.field("Type"); 29 | use goblin::elf::header::*; 30 | match h.e_type { 31 | ET_NONE => println!("None"), 32 | ET_REL => println!("Relocatable"), 33 | ET_EXEC => println!("Executable"), 34 | ET_DYN => println!("Shared object"), 35 | ET_CORE => println!("Core file"), 36 | n if (ET_LOOS..ET_HIOS).contains(&n) => 37 | println!("OS-specific ({n:#06x})"), 38 | n if (ET_LOPROC..ET_HIPROC).contains(&n) => 39 | println!("Processor-specific ({n:#06x})"), 40 | n => println!("Unknown ({n:#06x})"), 41 | } 42 | 43 | table.field("Ident's class"); 44 | match h.container() { 45 | Ok(Container::Little) => println!("ELF32"), 46 | Ok(Container::Big) => println!("ELF64"), 47 | Err(_) => println!("\x1b[93m[warning: invalid ident class]\x1b[0m"), 48 | } 49 | 50 | table.field("Ident's data"); 51 | match h.endianness() { 52 | Ok(Endian::Little) => println!("Little-endian"), 53 | Ok(Endian::Big) => println!("Big-endian"), 54 | Err(_) => println!("\x1b[93m[warning: invalid ident data]\x1b[0m"), 55 | } 56 | 57 | table.field("Machine"); 58 | println!("{}", machine_to_str(h.e_machine)); 59 | 60 | table.field("Entry point address"); 61 | if h.e_entry == 0 { 62 | print!("\x1b[90m"); 63 | } 64 | println!("{}", sp.hex(h.e_entry)); 65 | 66 | table.field("Flags"); 67 | println!("{:#010x}", h.e_flags); 68 | 69 | if let Some(interpreter) = elf.interpreter { 70 | table.field("Interpreter"); 71 | println!("{interpreter}"); 72 | } 73 | 74 | if let Some(soname) = elf.soname { 75 | table.field("SO name"); 76 | println!("{soname}"); 77 | } 78 | 79 | table.field(""); 80 | println!(); 81 | 82 | table.field("Nr. prog. headers"); 83 | println!("{:>16}", h.e_phnum); 84 | 85 | table.field("Prog. headers offset"); 86 | println!("{:>16} B", h.e_phoff); 87 | 88 | table.field("Prog. header size"); 89 | println!("{:>16} B", h.e_phentsize); 90 | 91 | table.field("Nr. section headers"); 92 | println!("{:>16}", h.e_shnum); 93 | 94 | table.field("Section headers offset"); 95 | println!("{:>16} B", h.e_shoff); 96 | 97 | table.field("Section header size"); 98 | println!("{:>16} B", h.e_shentsize); 99 | } 100 | 101 | pub fn program_headers(elf: &Elf) { 102 | let container = elf.header.container().unwrap_or(Container::Big); 103 | let sp = SizePrint::new(container); 104 | 105 | print_header( 106 | &format!("PROGRAM HEADERS ({})", elf.program_headers.len()) 107 | ); 108 | 109 | let colw = match container { 110 | Container::Big => 19, 111 | Container::Little => 11, 112 | }; 113 | 114 | println!( 115 | "\x1b[37m{:>12} \x1b[97m│ \x1b[37m{: "\x1b[93m[unknown]\x1b[0m", 129 | s => s.strip_prefix("PT_").unwrap_or(s), 130 | }; 131 | 132 | println!( 133 | "\x1b[97m{0:─<13}┼{0:─12} \x1b[97m│\x1b[0m {vaddr} \x1b[97m╶┼>\x1b[0m {paddr} ", 138 | vaddr = sp.hex(ph.p_vaddr), 139 | paddr = sp.hex(ph.p_paddr), 140 | ); 141 | println!( 142 | "\x1b[97m│\x1b[0m {}{}{}{}{} \x1b[97m│\x1b[0m", 143 | if ph.is_read() { "r" } else { "-" }, 144 | if ph.is_write() { "w" } else { "-" }, 145 | if ph.is_executable() { "x" } else { "-" }, 146 | if container.is_big() { " " } else { " " }, 147 | if ph.p_align == 0 { 148 | if container.is_big() { 149 | " --- ".to_owned() 150 | } else { 151 | "--- ".to_owned() 152 | } 153 | } else { 154 | format!("2^{:12} \x1b[97m│\x1b[0m {msize} \x1b[97m│\x1b[0m {fsize} ", 160 | "", 161 | msize = sp.hex(ph.p_memsz), 162 | fsize = sp.hex(ph.p_filesz), 163 | ); 164 | println!( 165 | "\x1b[97m│\x1b[0m {foff} \x1b[97m│\x1b[0m", 166 | foff = sp.hex(ph.p_offset), 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/sym.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use goblin::container::Container; 12 | use goblin::elf::{Elf, Sym, Symtab}; 13 | use regex::Regex; 14 | use rustc_demangle::demangle; 15 | 16 | use crate::{print_header, SizePrint, SymbolsArgs}; 17 | 18 | pub fn all_symbols(elf: &Elf, opts: &SymbolsArgs) { 19 | let (syms, strtab) = if opts.dynamic { 20 | (&elf.dynsyms, &elf.dynstrtab) 21 | } else { 22 | (&elf.syms, &elf.strtab) 23 | }; 24 | let container = elf.header.container().unwrap_or(Container::Big); 25 | let sp = SizePrint::new(container); 26 | 27 | print_header( 28 | &format!( 29 | "{} ({})", 30 | if opts.dynamic { "DYNAMIC SYMBOLS" } else { "SYMBOLS" }, 31 | syms.len() 32 | ) 33 | ); 34 | 35 | let colw = match container { 36 | Container::Big => 19, 37 | Container::Little => 11, 38 | }; 39 | println!( 40 | "\x1b[97m{:>colw$} │ {:7} │ {:10} │ {}\x1b[0m", 41 | "Value", "Type VB", "Size", "Name", 42 | ); 43 | println!( 44 | "\x1b[97m{0:─ 0; 72 | 73 | if let Some(ref filter) = opts.filter { 74 | if !filter.is_match(&name) { 75 | continue; 76 | } 77 | } 78 | 79 | use goblin::elf::sym::*; 80 | let typ = match sym.st_type() { 81 | STT_NOTYPE => "\x1b[90mNONE\x1b[0m", 82 | STT_OBJECT => " \x1b[34mOBJ\x1b[0m", 83 | STT_FUNC => "\x1b[32mFUNC\x1b[0m", 84 | STT_SECTION => "\x1b[31mSECT\x1b[0m", 85 | STT_FILE => "\x1b[36mFILE\x1b[0m", 86 | STT_COMMON => "COMM", 87 | STT_TLS => " \x1b[35mTLS\x1b[0m", 88 | STT_NUM => " NUM", 89 | _ => " ", 90 | }; 91 | let vis = match sym.st_visibility() { 92 | STV_DEFAULT => "+", 93 | STV_INTERNAL => "i", 94 | STV_HIDDEN => "\x1b[31m−\x1b[0m", 95 | STV_PROTECTED => "\x1b[33m#\x1b[0m", 96 | STV_EXPORTED => "x", 97 | STV_SINGLETON => "s", 98 | STV_ELIMINATE => "e", 99 | _ => "?", 100 | }; 101 | let bind = match sym.st_bind() { 102 | STB_LOCAL => "\x1b[90ml\x1b[0m", 103 | STB_GLOBAL => if defined { "\x1b[97mG\x1b[0m" } 104 | else { "\x1b[91mU\x1b[0m" }, 105 | STB_WEAK => "\x1b[36mW\x1b[0m", 106 | STB_NUM => "\x1b[35mN\x1b[0m", 107 | STB_GNU_UNIQUE => "\x1b[31mu\x1b[0m", 108 | _ => "\x1b[93m?\x1b[0m", 109 | }; 110 | let size = match sym.st_size { 111 | 0 => " ".to_string(), 112 | n => format!("{n:#010x}"), 113 | }; 114 | 115 | if !defined { 116 | print!("\x1b[30m"); 117 | } 118 | 119 | println!( 120 | "{v} \x1b[97m│\x1b[0m {typ} {vis}{bind} \x1b[97m│\x1b[0m {size} \x1b[97m│\x1b[0m {name}", 121 | v = sp.hex(sym.st_value), 122 | ); 123 | } 124 | 125 | println!(); 126 | println!("\x1b[97mVisibility [V]: Binding [B]:\x1b[0m"); 127 | println!(" + Default \x1b[90ml\x1b[0m Local"); 128 | println!(" \x1b[33m#\x1b[0m Protected \x1b[97mG\x1b[0m Global"); 129 | println!(" \x1b[31m−\x1b[0m Hidden \x1b[91mU\x1b[0m Global (undefined)"); 130 | println!(" i Internal \x1b[36mW\x1b[0m Weak"); 131 | println!(" x Exported \x1b[35mN\x1b[0m Number of defined types"); 132 | println!(" s Singleton \x1b[31mu\x1b[0m GNU unique"); 133 | println!(" e Eliminate"); 134 | } 135 | 136 | fn is_std_sym(sym: &str) -> bool { 137 | sym.starts_with("core::") 138 | || sym.starts_with("std::") 139 | || sym.starts_with("alloc::") 140 | || Regex::new("^<(std|core|alloc)::.+ as .+>").unwrap().is_match(sym) 141 | || Regex::new("^<.+ as (std|core|alloc)::.+>").unwrap().is_match(sym) 142 | } 143 | 144 | pub fn sym_type(typ: u8) -> &'static str { 145 | use goblin::elf::sym::*; 146 | match typ { 147 | STT_NOTYPE => "\x1b[90mNONE\x1b[0m", 148 | STT_OBJECT => "\x1b[34mOBJECT\x1b[0m", 149 | STT_FUNC => "\x1b[32mFUNCTION\x1b[0m", 150 | STT_SECTION => "\x1b[31mSECTION\x1b[0m", 151 | STT_FILE => "\x1b[36mFILE\x1b[0m", 152 | STT_COMMON => "COMMON", 153 | STT_TLS => "\x1b[35mTLS\x1b[0m", 154 | STT_NUM => "NUM", 155 | _ => "???", 156 | } 157 | } 158 | 159 | pub fn addr_to_sym(symtab: &Symtab, addr: u64) -> Option { 160 | let mut iter = symtab.iter(); 161 | let mut curr_sym = iter.next()?; 162 | 163 | for sym in iter { 164 | if sym.st_value <= addr && sym.st_value > curr_sym.st_value { 165 | curr_sym = sym; 166 | } 167 | } 168 | 169 | if curr_sym.st_value > addr { 170 | None 171 | } else { 172 | Some(curr_sym) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/sections.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use std::fs::File; 12 | use std::io::Write; 13 | use goblin::container::{Container}; 14 | use goblin::elf::{Elf, SectionHeader}; 15 | use goblin::elf::section_header::sht_to_str; 16 | use goblin::elf32::section_header::SHT_STRTAB; 17 | use goblin::strtab::Strtab; 18 | use anyhow::{anyhow, Context, Result}; 19 | 20 | use crate::{PairTable, print_header, SizePrint}; 21 | use crate::args::SectionArgs; 22 | use crate::eh::{eh_frame, eh_frame_hdr}; 23 | use crate::print::{BinSize, hexdump}; 24 | 25 | pub fn all_sections(elf: &Elf) { 26 | let container = elf.header.container().unwrap_or(Container::Big); 27 | let sp = SizePrint::new(container); 28 | 29 | print_header( 30 | &format!("SECTIONS ({})", elf.section_headers.len()) 31 | ); 32 | 33 | let colw = match container { 34 | Container::Big => 19, 35 | Container::Little => 11, 36 | }; 37 | println!( 38 | "\x1b[97m{:2} │ {:20} │ {:12} │ {:colw$} │ {:22} │\x1b[0m", 39 | "No", "Name", "Type", "Virt. addr.", "Size", 40 | ); 41 | println!( 42 | "\x1b[97m{0:─<3}┼{0:─<22}┼{0:─<14}┼{0:─10} \x1b[97m│\x1b[0m", BinSize(sh.sh_size)); 66 | 67 | println!(); 68 | } 69 | } 70 | 71 | pub fn one_section(elf: &Elf, bytes: &[u8], opts: &SectionArgs) -> Result<()> { 72 | if opts.name.is_none() { 73 | return Ok(all_sections(elf)); 74 | } 75 | 76 | let name = opts.name.as_ref().unwrap(); 77 | let sh = find_section(elf, name) 78 | .ok_or_else(|| anyhow!("couldn't find section {name:?}"))?; 79 | 80 | let mut index_range = sh.file_range(); 81 | if let Some(ref mut index_range) = index_range { 82 | if let Some(skip) = opts.skip { 83 | if skip >= index_range.len() { 84 | println!("\x1b[93mWarning: skipping more bytes than in section\x1b[0m"); 85 | } 86 | index_range.start += skip; 87 | } 88 | if let Some(maxlen) = opts.size { 89 | if index_range.len() > maxlen { 90 | index_range.end -= index_range.len() - maxlen; 91 | } 92 | } 93 | if index_range.len() == 0 { 94 | *index_range = 0..0; 95 | } 96 | } else { 97 | if opts.skip.is_some() { 98 | println!("\x1b[93mWarning: byte skipping specified on a NOBITS section\x1b[0m"); 99 | } 100 | if opts.size.is_some() { 101 | println!("\x1b[93mWarning: number of bytes specified on a NOBITS section\x1b[0m"); 102 | } 103 | } 104 | 105 | if let Some(ref output) = opts.output { 106 | let index_range = index_range.ok_or_else(|| 107 | anyhow!("section {name:?} is NOBITS and, therefor, has no content to export.") 108 | )?; 109 | 110 | let mut fh = File::create(output).with_context(|| 111 | format!("couldn't export content to file '{}'", output.display()) 112 | )?; 113 | fh.write_all(&bytes[index_range]).with_context(|| 114 | format!("couldn't write content to file '{}'", output.display()) 115 | )?; 116 | 117 | println!("Section {name:?} has been saved to \"{}\"", output.display()); 118 | 119 | return Ok(()); 120 | } 121 | 122 | print_header(&format!("SECTION {name:?}")); 123 | 124 | let table = PairTable(18); 125 | let (type_c, type_n) = section_type(sh.sh_type); 126 | let container = elf.header.container().unwrap_or(Container::Big); 127 | let sp = SizePrint::new(container); 128 | 129 | table.field("Name"); 130 | println!("{name}"); 131 | 132 | table.field("Type"); 133 | println!("\x1b[{type_c}{type_n}\x1b[0m ({:#010x})", sh.sh_type); 134 | 135 | table.field("Virtual address"); 136 | if sh.sh_addr == 0 { 137 | print!("\x1b[90m"); 138 | } 139 | println!("{}", sp.hex(sh.sh_addr)); 140 | 141 | table.field("Offset in ELF"); 142 | println!("{} B", sp.hex(sh.sh_offset)); 143 | 144 | table.field("Size"); 145 | println!("{} B ({})", sp.hex(sh.sh_size), BinSize(sh.sh_size)); 146 | 147 | table.field("Alignment"); 148 | println!("{} B", sp.hex(sh.sh_addralign)); 149 | 150 | table.field("Entry size"); 151 | if sh.sh_entsize == 0 { 152 | print!("\x1b[90m"); 153 | } 154 | println!("{} B\x1b[0m", sp.hex(sh.sh_entsize)); 155 | 156 | if let Some(index_range) = index_range { 157 | println!(); 158 | let content = &bytes[index_range]; 159 | 160 | if opts.hexdump { 161 | hexdump(content); 162 | } else { 163 | if sh.sh_type == SHT_STRTAB { 164 | strtab(content)?; 165 | } else if name == ".eh_frame_hdr" { 166 | eh_frame_hdr(elf, sh.sh_addr, content); 167 | } else if name == ".eh_frame" { 168 | eh_frame(elf, sh.sh_addr, content, &Default::default())?; 169 | } else { 170 | hexdump(content); 171 | } 172 | } 173 | } 174 | 175 | Ok(()) 176 | } 177 | 178 | pub fn find_section<'a>(elf: &'a Elf, name: &str) -> Option<&'a SectionHeader> { 179 | elf.section_headers 180 | .iter() 181 | .find(|&s| { 182 | elf.shdr_strtab.get_at(s.sh_name) 183 | .map(|n| n == name) 184 | .unwrap_or(false) 185 | }) 186 | } 187 | 188 | fn section_type(typ: u32) -> (&'static str, &'static str) { 189 | match sht_to_str(typ) { 190 | "UNKNOWN_SHT" => ("93m", "[unknown]"), 191 | "SHT_PROGBITS" => ("34m", "PROGBITS"), 192 | "SHT_NULL" => ("90m", "NULL"), 193 | "SHT_NOBITS" => ("90m", "NOBITS"), 194 | "SHT_STRTAB" => ("32m", "STRTAB"), 195 | "SHT_DYNAMIC" => ("35m", "DYNAMIC"), 196 | "SHT_DYNSYM" => ("95m", "DYNSYM"), 197 | "SHT_RELA" => ("33m", "RELA"), 198 | "SHT_SYMTAB" => ("31m", "SYMTAB"), 199 | "SHT_NOTE" => ("36m", "NOTE"), 200 | s => ("0m", s.strip_prefix("SHT_").unwrap_or(s)), 201 | } 202 | } 203 | 204 | fn strtab(content: &[u8]) -> Result<()> { 205 | let strtab = Strtab::new(content, 0).to_vec()?; 206 | for (i, s) in strtab.into_iter().enumerate() { 207 | println!("{i:4} {s:?}"); 208 | } 209 | 210 | Ok(()) 211 | } 212 | -------------------------------------------------------------------------------- /src/func.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use std::cell::RefCell; 12 | use std::collections::HashMap; 13 | use goblin::elf::Elf; 14 | use anyhow::{anyhow, Context, Result}; 15 | use gimli::{BaseAddresses, CallFrameInstruction, EhFrame, EndianSlice, 16 | FrameDescriptionEntry, LittleEndian, Register, SectionBaseAddresses, 17 | UnwindSection}; 18 | use goblin::container::Container; 19 | use goblin::elf::sym::STT_FUNC; 20 | use iced_x86::{Decoder, DecoderOptions, Formatter, FormatterOutput, 21 | FormatterTextKind, GasFormatter, Instruction, IntelFormatter, 22 | SymbolResolver, SymbolResult}; 23 | use rustc_demangle::demangle; 24 | 25 | use crate::args::{FnArgs, Syntax}; 26 | use crate::eh::EhInstrContext; 27 | use crate::elf::{find_symbol, find_symbol_by_addr, symbol_file_offset}; 28 | use crate::print::SizePrint; 29 | use crate::sections::find_section; 30 | use crate::sym::sym_type; 31 | 32 | pub fn do_fn(elf: &Elf, bytes: &[u8], args: &FnArgs) -> Result<()> { 33 | let (sym, strtab) = if args.address { 34 | let addr = u64::from_str_radix(args.name.trim_start_matches("0x"), 16) 35 | .context(anyhow!("couldn't parse memory address '{}'", args.name))?; 36 | find_symbol_by_addr(&elf.syms, addr).zip(Some(&elf.strtab)) 37 | } else { 38 | find_symbol(&elf.syms, &elf.strtab, &args.name).zip(Some(&elf.strtab)) 39 | .or_else(|| find_symbol(&elf.dynsyms, &elf.dynstrtab, &args.name).zip(Some(&elf.dynstrtab))) 40 | }.ok_or_else(|| 41 | anyhow!("couldn't find any symbol matching {:?}", args.name) 42 | )?; 43 | 44 | let sym_name = strtab.get_at(sym.st_name).unwrap(); 45 | 46 | if sym.st_type() != STT_FUNC { 47 | println!( 48 | "\x1b[93mwarning\x1b[0m: Symbol {sym_name:?} has type {}", sym_type(sym.st_type()) 49 | ); 50 | } 51 | 52 | let file_off = symbol_file_offset(elf, sym_name).ok_or_else(|| 53 | anyhow!("couldn't find the file offset") 54 | )? as usize; 55 | if sym.st_size == 0 { 56 | unimplemented!("symbol is required"); 57 | } 58 | 59 | let content = &bytes[file_off..(file_off + sym.st_size as usize)]; 60 | 61 | println!("\x1b[97m{sym_name}:\x1b[0m"); 62 | 63 | let opts = DisassOptions { 64 | cfi: args.cfi, 65 | syntax: args.syntax, 66 | }; 67 | disassemble(elf, bytes, sym.st_value, content, opts); 68 | 69 | Ok(()) 70 | } 71 | 72 | struct SymResolver { 73 | syms: HashMap, 74 | demangle: bool, 75 | sym_name: String, 76 | } 77 | 78 | impl SymbolResolver for SymResolver { 79 | fn symbol( 80 | &mut self, 81 | _instruction: &Instruction, 82 | _operand: u32, 83 | _instruction_operand: Option, 84 | address: u64, 85 | _address_size: u32, 86 | ) -> Option> { 87 | if let Some(name) = self.syms.get(&address) { 88 | if self.demangle { 89 | self.sym_name = demangle(&name).to_string(); 90 | } else { 91 | self.sym_name = name.clone(); 92 | }; 93 | Some(SymbolResult::with_str(address, &self.sym_name)) 94 | } else { 95 | None 96 | } 97 | } 98 | } 99 | 100 | struct ColorOutput; 101 | 102 | impl FormatterOutput for ColorOutput { 103 | fn write(&mut self, text: &str, kind: FormatterTextKind) { 104 | use FormatterTextKind::*; 105 | 106 | match kind { 107 | Number => print!("\x1b[36m"), 108 | Register => print!("\x1b[32m"), 109 | Mnemonic => print!("\x1b[33m"), 110 | FunctionAddress => print!("\x1b[94m"), 111 | LabelAddress => print!("\x1b[34m"), 112 | _=> print!("\x1b[0m"), 113 | } 114 | 115 | print!("{text}"); 116 | } 117 | } 118 | 119 | #[derive(Default)] 120 | struct DisassOptions { 121 | syntax: Syntax, 122 | cfi: bool, 123 | } 124 | 125 | fn disassemble(elf: &Elf, bytes: &[u8], ip: u64, content: &[u8], opts: DisassOptions) { 126 | let container = elf.header.container().unwrap_or(Container::Big); 127 | let bitness = match container { 128 | Container::Big => 64, 129 | Container::Little => 32, 130 | }; 131 | let sp = SizePrint::new(container); 132 | 133 | let mut decoder = Decoder::with_ip( 134 | bitness, 135 | content, 136 | ip, 137 | DecoderOptions::NONE 138 | ); 139 | 140 | let syms: HashMap = elf.syms.iter() 141 | .filter_map(|sym| 142 | elf.strtab.get_at(sym.st_name) 143 | .map(|name| (sym.st_value, name.to_owned())) 144 | ).collect(); 145 | 146 | let sym_resolver: Box = Box::new(SymResolver { 147 | syms, 148 | demangle: true, 149 | sym_name: String::new(), 150 | }); 151 | 152 | let mut output = ColorOutput; 153 | let mut formatter: Box = match opts.syntax { 154 | Syntax::Intel => Box::new(IntelFormatter::with_options(Some(sym_resolver), None)), 155 | Syntax::Att => Box::new(GasFormatter::with_options(Some(sym_resolver), None)), 156 | }; 157 | formatter.options_mut().set_first_operand_char_index(8); 158 | formatter.options_mut().set_uppercase_hex(false); 159 | formatter.options_mut().set_space_after_operand_separator(true); 160 | formatter.options_mut().set_space_between_memory_add_operators(true); 161 | formatter.options_mut().set_gas_space_after_memory_operand_comma(true); 162 | 163 | let mut eh = opts.cfi.then(|| EhFnCtx::new(elf, bytes, ip)).flatten(); 164 | 165 | while decoder.can_decode() { 166 | let instr = decoder.decode(); 167 | let start_index = (instr.ip() - ip) as usize; 168 | let bytes = &content[start_index..(start_index + instr.len())]; 169 | 170 | if let Some(ref mut eh) = eh { 171 | eh.at_ip(instr.ip()); 172 | } 173 | 174 | print!("{} \x1b[97m│\x1b[0m ", sp.hex(instr.ip())); 175 | 176 | let col_w; 177 | 178 | if bytes.len() > 12 { 179 | for &byte in bytes { 180 | print!("{byte:02x}"); 181 | } 182 | col_w = bytes.len() * 2; 183 | } else { 184 | for &byte in bytes { 185 | print!("{byte:02x} "); 186 | } 187 | col_w = bytes.len() * 3; 188 | } 189 | 190 | print!( 191 | "{:w$} \x1b[97m│\x1b[0m ", "", 192 | w = 24usize.saturating_sub(col_w) 193 | ); 194 | 195 | formatter.format(&instr, &mut output); 196 | println!("\x1b[0m"); 197 | } 198 | } 199 | 200 | struct EhFnCtx<'a> { 201 | base_addrs: &'static BaseAddresses, 202 | eh: EhFrame>, 203 | fde: FrameDescriptionEntry>, 204 | instr_ctx: RefCell, 205 | curr_loc: RefCell, 206 | cie_shown: bool, 207 | instr_index: usize, 208 | } 209 | 210 | impl<'a> EhFnCtx<'a> { 211 | fn new(elf: &Elf, bytes: &'a [u8], ip: u64) -> Option { 212 | let container = elf.header.container().unwrap_or(Container::Big); 213 | let sp = SizePrint::new(container); 214 | 215 | let eh_frame = find_section(elf, ".eh_frame")?; 216 | let eh = EhFrame::new(&bytes[eh_frame.file_range()?], LittleEndian); 217 | 218 | let base_addrs = Box::leak(Box::new(BaseAddresses { 219 | eh_frame_hdr: SectionBaseAddresses::default(), 220 | eh_frame: SectionBaseAddresses { 221 | section: Some(eh_frame.sh_addr), 222 | text: None, 223 | data: None, 224 | }, 225 | })); 226 | 227 | let fde = eh.fde_for_address( 228 | &base_addrs, 229 | ip, 230 | |section, bases, offset| section.cie_from_offset(bases, offset), 231 | ).ok()?; 232 | 233 | let instr_ctx = EhInstrContext { 234 | cfa_reg: Register(0), 235 | cfa_off: 0, 236 | loc: fde.initial_address(), 237 | data_align: fde.cie().data_alignment_factor(), 238 | sp, 239 | }; 240 | let curr_loc = instr_ctx.loc; 241 | 242 | Some(EhFnCtx { 243 | base_addrs, 244 | eh, 245 | fde, 246 | instr_ctx: RefCell::new(instr_ctx), 247 | curr_loc: RefCell::new(curr_loc), 248 | cie_shown: false, 249 | instr_index: 0, 250 | }) 251 | } 252 | 253 | fn at_ip(&mut self, ip: u64) { 254 | if !self.cie_shown { 255 | let mut iter = self.fde.cie().instructions(&self.eh, self.base_addrs); 256 | while let Ok(Some(instr)) = iter.next() { 257 | self.print_instr(instr); 258 | } 259 | self.cie_shown = true; 260 | } 261 | 262 | let mut iter = self.fde.instructions(&self.eh, self.base_addrs); 263 | for _ in 0..self.instr_index { 264 | if iter.next().ok().flatten().is_none() { 265 | return; 266 | } 267 | } 268 | 269 | while let Ok(Some(instr)) = iter.next() { 270 | if ip < *self.curr_loc.borrow() { 271 | break; 272 | } 273 | self.print_instr(instr); 274 | self.instr_index += 1; 275 | } 276 | } 277 | 278 | fn print_instr( 279 | &self, 280 | instr: CallFrameInstruction>, 281 | ) { 282 | match instr { 283 | CallFrameInstruction::Nop => (), 284 | CallFrameInstruction::AdvanceLoc { delta } => { 285 | *self.curr_loc.borrow_mut() += delta as u64; 286 | }, 287 | _ => { 288 | print!("\x1b[35m[CFI]\x1b[0m "); 289 | self.instr_ctx.borrow_mut().print(instr.clone()); 290 | } 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.19" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.66" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" 19 | 20 | [[package]] 21 | name = "atty" 22 | version = "0.2.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 25 | dependencies = [ 26 | "hermit-abi", 27 | "libc", 28 | "winapi", 29 | ] 30 | 31 | [[package]] 32 | name = "autocfg" 33 | version = "1.1.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.3.2" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 42 | 43 | [[package]] 44 | name = "byteorder" 45 | version = "1.4.3" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 48 | 49 | [[package]] 50 | name = "cfg-if" 51 | version = "1.0.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 54 | 55 | [[package]] 56 | name = "clap" 57 | version = "4.0.18" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" 60 | dependencies = [ 61 | "atty", 62 | "bitflags", 63 | "clap_derive", 64 | "clap_lex", 65 | "once_cell", 66 | "strsim", 67 | "termcolor", 68 | ] 69 | 70 | [[package]] 71 | name = "clap_derive" 72 | version = "4.0.18" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" 75 | dependencies = [ 76 | "heck", 77 | "proc-macro-error", 78 | "proc-macro2", 79 | "quote", 80 | "syn", 81 | ] 82 | 83 | [[package]] 84 | name = "clap_lex" 85 | version = "0.3.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 88 | dependencies = [ 89 | "os_str_bytes", 90 | ] 91 | 92 | [[package]] 93 | name = "elf-info" 94 | version = "0.3.0" 95 | dependencies = [ 96 | "anyhow", 97 | "byteorder", 98 | "clap", 99 | "gimli", 100 | "goblin", 101 | "iced-x86", 102 | "memmap2", 103 | "regex", 104 | "rustc-demangle", 105 | ] 106 | 107 | [[package]] 108 | name = "fallible-iterator" 109 | version = "0.2.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 112 | 113 | [[package]] 114 | name = "gimli" 115 | version = "0.27.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" 118 | dependencies = [ 119 | "fallible-iterator", 120 | "indexmap", 121 | "stable_deref_trait", 122 | ] 123 | 124 | [[package]] 125 | name = "goblin" 126 | version = "0.6.1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "0d6b4de4a8eb6c46a8c77e1d3be942cb9a8bf073c22374578e5ba4b08ed0ff68" 129 | dependencies = [ 130 | "log", 131 | "plain", 132 | "scroll", 133 | ] 134 | 135 | [[package]] 136 | name = "hashbrown" 137 | version = "0.12.3" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 140 | 141 | [[package]] 142 | name = "heck" 143 | version = "0.4.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 146 | 147 | [[package]] 148 | name = "hermit-abi" 149 | version = "0.1.19" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 152 | dependencies = [ 153 | "libc", 154 | ] 155 | 156 | [[package]] 157 | name = "iced-x86" 158 | version = "1.17.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "158f5204401d08f91d19176112146d75e99b3cf745092e268fa7be33e09adcec" 161 | dependencies = [ 162 | "lazy_static", 163 | "static_assertions", 164 | ] 165 | 166 | [[package]] 167 | name = "indexmap" 168 | version = "1.9.3" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 171 | dependencies = [ 172 | "autocfg", 173 | "hashbrown", 174 | ] 175 | 176 | [[package]] 177 | name = "lazy_static" 178 | version = "1.4.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 181 | 182 | [[package]] 183 | name = "libc" 184 | version = "0.2.135" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" 187 | 188 | [[package]] 189 | name = "log" 190 | version = "0.4.17" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 193 | dependencies = [ 194 | "cfg-if", 195 | ] 196 | 197 | [[package]] 198 | name = "memchr" 199 | version = "2.5.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 202 | 203 | [[package]] 204 | name = "memmap2" 205 | version = "0.5.7" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" 208 | dependencies = [ 209 | "libc", 210 | ] 211 | 212 | [[package]] 213 | name = "once_cell" 214 | version = "1.15.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 217 | 218 | [[package]] 219 | name = "os_str_bytes" 220 | version = "6.3.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" 223 | 224 | [[package]] 225 | name = "plain" 226 | version = "0.2.3" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 229 | 230 | [[package]] 231 | name = "proc-macro-error" 232 | version = "1.0.4" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 235 | dependencies = [ 236 | "proc-macro-error-attr", 237 | "proc-macro2", 238 | "quote", 239 | "syn", 240 | "version_check", 241 | ] 242 | 243 | [[package]] 244 | name = "proc-macro-error-attr" 245 | version = "1.0.4" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 248 | dependencies = [ 249 | "proc-macro2", 250 | "quote", 251 | "version_check", 252 | ] 253 | 254 | [[package]] 255 | name = "proc-macro2" 256 | version = "1.0.47" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 259 | dependencies = [ 260 | "unicode-ident", 261 | ] 262 | 263 | [[package]] 264 | name = "quote" 265 | version = "1.0.21" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 268 | dependencies = [ 269 | "proc-macro2", 270 | ] 271 | 272 | [[package]] 273 | name = "regex" 274 | version = "1.6.0" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 277 | dependencies = [ 278 | "aho-corasick", 279 | "memchr", 280 | "regex-syntax", 281 | ] 282 | 283 | [[package]] 284 | name = "regex-syntax" 285 | version = "0.6.27" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 288 | 289 | [[package]] 290 | name = "rustc-demangle" 291 | version = "0.1.21" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 294 | 295 | [[package]] 296 | name = "scroll" 297 | version = "0.11.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" 300 | dependencies = [ 301 | "scroll_derive", 302 | ] 303 | 304 | [[package]] 305 | name = "scroll_derive" 306 | version = "0.11.0" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" 309 | dependencies = [ 310 | "proc-macro2", 311 | "quote", 312 | "syn", 313 | ] 314 | 315 | [[package]] 316 | name = "stable_deref_trait" 317 | version = "1.2.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 320 | 321 | [[package]] 322 | name = "static_assertions" 323 | version = "1.1.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 326 | 327 | [[package]] 328 | name = "strsim" 329 | version = "0.10.0" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 332 | 333 | [[package]] 334 | name = "syn" 335 | version = "1.0.103" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 338 | dependencies = [ 339 | "proc-macro2", 340 | "quote", 341 | "unicode-ident", 342 | ] 343 | 344 | [[package]] 345 | name = "termcolor" 346 | version = "1.1.3" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 349 | dependencies = [ 350 | "winapi-util", 351 | ] 352 | 353 | [[package]] 354 | name = "unicode-ident" 355 | version = "1.0.5" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 358 | 359 | [[package]] 360 | name = "version_check" 361 | version = "0.9.4" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 364 | 365 | [[package]] 366 | name = "winapi" 367 | version = "0.3.9" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 370 | dependencies = [ 371 | "winapi-i686-pc-windows-gnu", 372 | "winapi-x86_64-pc-windows-gnu", 373 | ] 374 | 375 | [[package]] 376 | name = "winapi-i686-pc-windows-gnu" 377 | version = "0.4.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 380 | 381 | [[package]] 382 | name = "winapi-util" 383 | version = "0.1.5" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 386 | dependencies = [ 387 | "winapi", 388 | ] 389 | 390 | [[package]] 391 | name = "winapi-x86_64-pc-windows-gnu" 392 | version = "0.4.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 395 | -------------------------------------------------------------------------------- /src/eh.rs: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright © 2023 Kévin Lesénéchal * 3 | * This file is part of the elf-info CLI tool. * 4 | * * 5 | * elf-info is free software; you can redistribute it and/or modify it under * 6 | * the terms of the GNU General Public License as published by the Free * 7 | * Software Foundation; either version 3 of the License, or (at your option) * 8 | * any later version. See LICENSE file for more information. * 9 | ******************************************************************************/ 10 | 11 | use std::collections::HashMap; 12 | use byteorder::ReadBytesExt; 13 | use gimli::{BaseAddresses, CallFrameInstruction, CieOrFde, 14 | CommonInformationEntry, EhFrame, FrameDescriptionEntry, 15 | LittleEndian, Reader, Register, SectionBaseAddresses, UnwindSection, 16 | X86_64}; 17 | use goblin::container::Container; 18 | use goblin::elf::Elf; 19 | use rustc_demangle::demangle; 20 | use anyhow::{anyhow, Context, Result}; 21 | 22 | use crate::args::EhArgs; 23 | use crate::elf::find_symbol; 24 | use crate::print::{PairTable, SizePrint}; 25 | use crate::sections::find_section; 26 | use crate::sym::addr_to_sym; 27 | 28 | pub fn eh(elf: &Elf, bytes: &[u8], mut opts: EhArgs) -> Result<()> { 29 | let sh = if let Some(ref name) = opts.section { 30 | find_section(elf, name) 31 | .ok_or_else(|| anyhow!("couldn't find section {name:?}"))? 32 | } else { 33 | find_section(elf, ".eh_frame") 34 | .or_else(|| find_section(elf, ".debug_frame")) 35 | .ok_or_else(|| anyhow!("couldn't find section `.eh_frame` or `.debug_frame`"))? 36 | }; 37 | 38 | let range = sh.file_range() 39 | .ok_or_else(|| anyhow!("section has no content"))?; 40 | 41 | if let Some(ref sym) = opts.symbol { 42 | let sym = find_symbol(&elf.syms, &elf.strtab, sym) 43 | .or_else(|| find_symbol(&elf.dynsyms, &elf.dynstrtab, sym)) 44 | .ok_or_else(|| 45 | anyhow!("couldn't find any symbol matching {:?}", sym) 46 | )?; 47 | opts.address = Some(sym.st_value); 48 | } 49 | 50 | eh_frame(elf, sh.sh_addr, &bytes[range], &opts)?; 51 | 52 | Ok(()) 53 | } 54 | 55 | pub fn eh_frame( 56 | elf: &Elf, 57 | vaddr: u64, 58 | content: &[u8], 59 | opts: &EhArgs, 60 | ) -> Result<()> { 61 | let container = elf.header.container().unwrap_or(Container::Big); 62 | let sp = SizePrint::new(container); 63 | let eh = EhFrame::new(content, LittleEndian); // TODO: endianness 64 | 65 | let mut cies = HashMap::new(); 66 | let mut instr_ctx = EhInstrContext { 67 | cfa_reg: Register(0), 68 | cfa_off: 0, 69 | loc: 0, 70 | data_align: 1, 71 | sp, 72 | }; 73 | 74 | let base_addrs = BaseAddresses { 75 | eh_frame_hdr: SectionBaseAddresses::default(), 76 | eh_frame: SectionBaseAddresses { 77 | section: Some(vaddr), 78 | text: None, 79 | data: None, 80 | }, 81 | }; 82 | let mut entries = eh.entries(&base_addrs); 83 | 84 | while let Some(entry) = entries.next() 85 | .with_context(|| anyhow!("failed to parse entry"))? { 86 | match entry { 87 | CieOrFde::Cie(cie) => { 88 | print_cie_header(&cie, sp); 89 | let mut instr_iter = cie.instructions(&eh, &base_addrs); 90 | instr_ctx.data_align = cie.data_alignment_factor(); 91 | while let Some(instr) = instr_iter.next().unwrap_or(None) { 92 | print!("│ ├──⮞ "); 93 | instr_ctx.print(instr); 94 | } 95 | cies.insert(cie.offset(), cie); 96 | }, 97 | CieOrFde::Fde(fde_unparsed) => { 98 | let fde = fde_unparsed.parse(|_, _, offset| { 99 | Ok(cies[&offset.0].clone()) 100 | }).unwrap(); 101 | if let Some(addr) = opts.address { 102 | if !fde.contains(addr) { 103 | continue; 104 | } 105 | } 106 | print_fde_header(&fde, elf); 107 | instr_ctx.loc = fde.initial_address(); 108 | let mut instr_iter = fde.instructions(&eh, &base_addrs); 109 | while let Some(instr) = instr_iter.next().unwrap_or(None) { 110 | print!("│ │ ├──⮞ "); 111 | instr_ctx.print(instr); 112 | } 113 | }, 114 | } 115 | } 116 | 117 | Ok(()) 118 | } 119 | 120 | fn print_cie_header>( 121 | cie: &CommonInformationEntry, 122 | sp: SizePrint 123 | ) { 124 | let table = PairTable(20); 125 | 126 | println!("│"); 127 | println!("├╴ \x1b[97mCIE\x1b[0m offset={}", sp.hex(cie.offset() as u64)); 128 | print!("│ ├╴"); 129 | table.field("Version"); 130 | println!("{}", cie.version()); 131 | 132 | print!("│ ├╴"); 133 | table.field("Length"); 134 | println!("{}", cie.entry_len()); 135 | 136 | print!("│ ├╴"); 137 | table.field("Augmentation"); 138 | println!(); 139 | 140 | print!("│ ├╴"); 141 | table.field("Code alignment"); 142 | println!("{}", cie.code_alignment_factor()); 143 | 144 | print!("│ ├╴"); 145 | table.field("Data alignment"); 146 | println!("{}", cie.data_alignment_factor()); 147 | 148 | print!("│ ├╴"); 149 | table.field("Return addr register"); 150 | println!("{} (%{})", cie.return_address_register().0, 151 | register_name(cie.return_address_register())); 152 | } 153 | 154 | fn print_fde_header>( 155 | fde: &FrameDescriptionEntry, 156 | elf: &Elf, 157 | ) { 158 | let container = elf.header.container().unwrap_or(Container::Big); 159 | let sp = SizePrint::new(container); 160 | let table = PairTable(10); 161 | 162 | println!("│ │"); 163 | println!( 164 | "│ ├╴ \x1b[97mFDE\x1b[0m offset={} CIE={}", 165 | sp.hex(fde.offset() as u64), sp.hex(fde.cie().offset() as u64) 166 | ); 167 | print!("│ │ ├╴"); 168 | table.field("PC range"); 169 | println!( 170 | "{}..{}", 171 | sp.hex(fde.initial_address()), 172 | sp.hex(fde.initial_address() + fde.len()) 173 | ); 174 | 175 | if let Some(sym) = addr_to_sym(&elf.syms, fde.initial_address()) { 176 | print!("│ │ ├╴"); 177 | table.field("Symbol"); 178 | let name = elf.strtab.get_at(sym.st_name).unwrap_or("???"); 179 | let name = demangle(name).to_string(); 180 | println!("{name} + {:#x}", fde.initial_address() - sym.st_value); 181 | } 182 | } 183 | 184 | pub struct EhInstrContext { 185 | pub cfa_reg: Register, 186 | pub cfa_off: u64, 187 | pub loc: u64, 188 | pub data_align: i64, 189 | pub sp: SizePrint, 190 | } 191 | 192 | impl EhInstrContext { 193 | pub fn print(&mut self, instr: CallFrameInstruction) { 194 | use CallFrameInstruction::*; 195 | 196 | match instr { 197 | SetLoc { address } => { 198 | println!( 199 | "{:30} loc = {address}", 200 | format!("DW_CFA_set_loc({address})") 201 | ); 202 | }, 203 | AdvanceLoc { delta } => { 204 | self.loc += delta as u64; 205 | println!( 206 | "{:30} loc += {delta}\tloc = {}", 207 | format!("DW_CFA_advance_loc({delta})"), 208 | self.sp.hex(self.loc), 209 | ); 210 | }, 211 | DefCfa { register, offset } => { 212 | println!( 213 | "{:30} cfa = %{} + {offset}", 214 | format!("DW_CFA_def_cfa({}, {offset})", register.0), 215 | register_name(register), 216 | ); 217 | self.cfa_reg = register; 218 | self.cfa_off = offset; 219 | }, 220 | DefCfaSf { register, factored_offset } => { 221 | println!( 222 | "DW_CFA_def_cfa_sf({}, {factored_offset})", register.0 223 | ); 224 | }, 225 | DefCfaRegister { register } => { 226 | println!( 227 | "{:30} cfa = %{} + \x1b[90m{}\x1b[0m", 228 | format!("DW_CFA_def_cfa_register({})", register.0), 229 | register_name(register), self.cfa_off, 230 | ); 231 | self.cfa_reg = register; 232 | }, 233 | DefCfaOffset { offset } => { 234 | println!( 235 | "{:30} cfa = \x1b[90m%{}\x1b[0m + {offset}", 236 | format!("DW_CFA_def_cfa_offset({offset})"), 237 | register_name(self.cfa_reg), 238 | ); 239 | self.cfa_off = offset; 240 | }, 241 | DefCfaOffsetSf { factored_offset } => { 242 | println!("DW_CFA_def_cfa_offset_sf({factored_offset})"); 243 | }, 244 | DefCfaExpression { expression } => { 245 | println!("DW_CFA_def_cfa_expression({:02x?})", expression.0.to_slice().unwrap()); 246 | }, 247 | Undefined { register } => { 248 | println!( 249 | "{:30} %{} @ ??? (unrecoverable)", 250 | format!("DW_CFA_undefined({})", register.0), 251 | register_name(register), 252 | ); 253 | }, 254 | SameValue { register } => { 255 | println!( 256 | "{:30} %{} untouched", 257 | format!("DW_CFA_same_value({})", register.0), 258 | register_name(register), 259 | ); 260 | }, 261 | Offset { register, factored_offset } => { 262 | let off = factored_offset as i64 * self.data_align; 263 | println!( 264 | "{:30} %{} @ cfa {} {}", 265 | format!("DW_CFA_offset({}, {factored_offset})", register.0), 266 | register_name(register), 267 | if off < 0 { "−" } else { "+" }, 268 | off.abs(), 269 | ); 270 | }, 271 | OffsetExtendedSf { register, factored_offset } => { 272 | println!( 273 | "DW_CFA_offset_extended_sf({}, {factored_offset})", register.0 274 | ); 275 | }, 276 | ValOffset { register, factored_offset } => { 277 | println!( 278 | "DW_CFA_val_offset({}, {factored_offset})", register.0 279 | ); 280 | }, 281 | ValOffsetSf { register, factored_offset } => { 282 | println!( 283 | "DW_CFA_val_offset_sf({}, {factored_offset})", register.0 284 | ); 285 | }, 286 | Register { dest_register, src_register } => { 287 | println!( 288 | "{:30} %{} = %{}", 289 | format!("DW_CFA_register({}, {})", dest_register.0, src_register.0), 290 | register_name(dest_register), register_name(src_register), 291 | ); 292 | }, 293 | Expression { register, expression } => { 294 | println!( 295 | "DW_CFA_expression({}, {:02x?})\t\t%{} = ...", 296 | register.0, expression.0.to_slice().unwrap(), 297 | register_name(register), 298 | ); 299 | }, 300 | ValExpression { register, expression } => { 301 | println!( 302 | "DW_CFA_val_expression({}, {:02x?})", 303 | register.0, expression.0.to_slice().unwrap(), 304 | ); 305 | }, 306 | Restore { register } => { 307 | println!( 308 | "{:30} %{} @ (initial rule)", 309 | format!("DW_CFA_restore({})", register.0), 310 | register_name(register), 311 | ); 312 | }, 313 | RememberState => println!("DW_CFA_remember_state()"), 314 | RestoreState => println!("DW_CFA_restore_state()"), 315 | ArgsSize { size } => println!("DW_CFA_GNU_args_size({size})"), 316 | Nop => println!("\x1b[90mDW_CFA_nop()\x1b[0m"), 317 | } 318 | } 319 | } 320 | 321 | pub fn eh_frame_hdr(elf: &Elf, pc: u64, content: &[u8]) { 322 | let container = elf.header.container().unwrap_or(Container::Big); 323 | let sp = SizePrint::new(container); 324 | 325 | println!("\x1b[1;96m─── Header ───\x1b[0m"); 326 | let table = PairTable(22); 327 | 328 | table.field("Version"); 329 | println!("{}", content[0]); 330 | 331 | table.field("eh_frame_ptr encoding"); 332 | encoding(content[1]); 333 | 334 | table.field("fde_count encoding"); 335 | encoding(content[2]); 336 | 337 | table.field("Table encoding"); 338 | encoding(content[3]); 339 | 340 | let mut off = 4; 341 | table.field(".eh_frame pointer"); 342 | let (size, val) = value(content[1], &content[off..]); 343 | match val { 344 | Value::Signed(n) => print!("{n}"), 345 | Value::Unsigned(n) => print!("{n}"), 346 | } 347 | println!(" (-> {})", sp.hex(value_abs(content[1], val, pc + off as u64, pc))); 348 | off += size; 349 | 350 | table.field("Nr entries"); 351 | let (size, val) = value(content[2], &content[off..]); 352 | let nr_entries; 353 | match val { 354 | Value::Signed(n) => { nr_entries = n as usize; println!("{n}"); }, 355 | Value::Unsigned(n) => { nr_entries = n as usize; println!("{n}"); }, 356 | } 357 | off += size; 358 | 359 | println!("\n\x1b[1;96m─── Table content ───\x1b[0m"); 360 | 361 | for _ in 0..nr_entries { 362 | print!("\t"); 363 | 364 | let (size, val) = value(content[3], &content[off..]); 365 | print!("\x1b[90m("); 366 | match val { 367 | Value::Signed(n) => print!("{n:10}"), 368 | Value::Unsigned(n) => print!("{n:10}"), 369 | } 370 | off += size; 371 | print!(")\x1b[0m"); 372 | 373 | print!(" {}", sp.hex(value_abs(content[3], val, pc + off as u64, pc))); 374 | print!(" -> "); 375 | 376 | let (size, val) = value(content[3], &content[off..]); 377 | print!("{} ", sp.hex(value_abs(content[3], val, pc + off as u64, pc))); 378 | print!("\x1b[90m("); 379 | match val { 380 | Value::Signed(n) => print!("{n:10}"), 381 | Value::Unsigned(n) => print!("{n:10}"), 382 | } 383 | off += size; 384 | print!(")\x1b[0m"); 385 | 386 | println!(); 387 | } 388 | } 389 | 390 | fn encoding(n: u8) { 391 | print!("{n:#04x} "); 392 | if n == 0xff { 393 | println!("(no value)"); 394 | return; 395 | } 396 | 397 | let size = match n & 0x0f { 398 | 0x01 => "unsigned LEB128", 399 | 0x02 => "u16", 400 | 0x03 => "u32", 401 | 0x04 => "u64", 402 | 0x09 => "signed LEB128", 403 | 0x0a => "i16", 404 | 0x0b => "i32", 405 | 0x0c => "i64", 406 | _ => "???", 407 | }; 408 | let app = match n & 0xf0 { 409 | 0x00 => "as is", 410 | 0x10 => "relative to program counter", 411 | 0x30 => "relative to .eh_frame_hdr start", 412 | _ => "???", 413 | }; 414 | 415 | println!("({size}, {app})"); 416 | } 417 | 418 | fn value(enc: u8, mut d: &[u8]) -> (usize, Value) { 419 | use byteorder::LittleEndian; 420 | 421 | match enc & 0x0f { 422 | 0x01 => unimplemented!("unsigned LEB128"), // TODO: implement 423 | 0x02 => (2, Value::Unsigned(d.read_u16::().unwrap() as u64)), 424 | 0x03 => (4, Value::Unsigned(d.read_u32::().unwrap() as u64)), 425 | 0x04 => (8, Value::Unsigned(d.read_u64::().unwrap())), 426 | 0x09 => unimplemented!("signed LEB128"), // TODO: implement 427 | 0x0a => (2, Value::Signed(d.read_i16::().unwrap() as i64)), 428 | 0x0b => (4, Value::Signed(d.read_i32::().unwrap() as i64)), 429 | 0x0c => (8, Value::Signed(d.read_i64::().unwrap())), 430 | _ => panic!("invalid encoding"), // TODO: don't panic 431 | } 432 | } 433 | 434 | fn value_abs(enc: u8, v: Value, pc: u64, ehhdr: u64) -> u64 { 435 | let base = match enc & 0xf0 { 436 | 0x00 => 0, 437 | 0x10 => pc, 438 | 0x30 => ehhdr, 439 | _ => panic!("invalid encoding"), // TODO: don't panic 440 | }; 441 | 442 | match v { 443 | Value::Signed(n) => (base as i64 + n) as u64, 444 | Value::Unsigned(n) => base + n, 445 | } 446 | } 447 | 448 | #[derive(Copy, Clone)] 449 | enum Value { 450 | Signed(i64), 451 | Unsigned(u64), 452 | } 453 | 454 | fn register_name(r: Register) -> &'static str { 455 | X86_64::register_name(r).unwrap_or("???") // TODO: handle other archs 456 | } 457 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------