dwarf_x86/
lib.rs

1#[macro_use]
2extern crate enum_primitive;
3extern crate elf;
4extern crate gimli;
5extern crate leb128;
6
7mod dwarf_utils;
8mod consts;
9
10pub use dwarf_utils::ArgumentLocation;
11pub use consts::X86Register;
12
13use std::path;
14
15#[derive(Debug, Clone)]
16pub struct Argument {
17    pub name: String,
18    pub location: ArgumentLocation,
19}
20
21#[derive(Debug)]
22pub struct Function {
23    pub name: String,
24    pub arguments: Vec<Argument>,
25    pub start_address: u64,
26}
27
28#[derive(Debug)]
29pub struct Executable {
30    path: path::PathBuf,
31    file: elf::File,
32    endianess: gimli::RunTimeEndian,
33}
34
35fn process_function_child(
36    entry: &gimli::DebuggingInformationEntry<gimli::EndianBuf<gimli::RunTimeEndian>>,
37    strings: &gimli::DebugStr<gimli::EndianBuf<gimli::RunTimeEndian>>,
38) -> Option<Argument> {
39    if entry.tag() != gimli::DW_TAG_formal_parameter {
40        return None;
41    }
42    let mut arg_name: Result<String, &'static str> = Err("Argument doesn't have a name attribute");
43    let mut location: Result<ArgumentLocation, &'static str> =
44        Err("Argument doesn't have a location attribute");
45
46    let mut attrs = entry.attrs();
47    while let Some(attr) = attrs.next().unwrap() {
48        match attr.name() {
49            gimli::DW_AT_name => {
50                let arg_name_str = attr.string_value(strings).unwrap().to_string_lossy();
51                arg_name = Ok(arg_name_str.into_owned());
52            }
53            gimli::DW_AT_location => {
54                match attr.value() {
55                    gimli::AttributeValue::Exprloc(expr) => {
56                        let buf = expr.0.buf();
57                        location = Ok(dwarf_utils::convert_dw_at_location(buf));
58                    }
59                    _ => panic!("Unexpected type for argument location"),
60                }
61            }
62            _ => (),
63        }
64    }
65
66    return Some(Argument {
67        name: arg_name.unwrap(),
68        location: location.unwrap(),
69    });
70}
71
72fn get_function_from_dwarf_entry(
73    node: gimli::EntriesTreeNode<gimli::EndianBuf<gimli::RunTimeEndian>>,
74    strings: &gimli::DebugStr<gimli::EndianBuf<gimli::RunTimeEndian>>,
75) -> Function {
76    let mut func_name: Result<String, &'static str> = Err("Function doesn't have a name attribute");
77    let mut start_address: Result<u64, &'static str> =
78        Err("Function doesn't have starting address");
79
80    let entry = {
81        node.entry().clone()
82    };
83    let mut attrs = entry.attrs();
84    while let Some(attr) = attrs.next().unwrap() {
85        match attr.name() {
86            gimli::DW_AT_name => {
87                let func_name_str = attr.string_value(strings).unwrap().to_string_lossy();
88                func_name = Ok(func_name_str.into_owned());
89            }
90            gimli::DW_AT_low_pc => {
91                match attr.value() {
92                    gimli::AttributeValue::Addr(val) => start_address = Ok(val),
93                    _ => panic!("Invalid type for function start address"),
94                }
95            }
96            _ => (),
97        };
98    }
99
100    let mut args: Vec<Argument> = Vec::new();
101
102    // iterate over the children to get the arguments
103    let mut children = node.children();
104
105    while let Some(child) = children.next().unwrap() {
106        let argument = process_function_child(child.entry(), strings);
107        match argument {
108            Some(argument) => {
109                args.push(argument);
110            }
111            _ => {}
112        }
113    }
114
115    return Function {
116        name: func_name.unwrap(),
117        arguments: args,
118        start_address: start_address.unwrap(),
119    };
120}
121
122impl Executable {
123    pub fn debug_info<'a>(
124        &'a self,
125    ) -> gimli::DebugInfo<gimli::EndianBuf<'a, gimli::RunTimeEndian>> {
126        let debug_info = self.file.get_section(".debug_info").unwrap();
127        return gimli::DebugInfo::new(&debug_info.data, self.endianess);
128    }
129
130    pub fn debug_abbrev<'a>(
131        &'a self,
132    ) -> gimli::DebugAbbrev<gimli::EndianBuf<'a, gimli::RunTimeEndian>> {
133        let debug_abbrev = self.file.get_section(".debug_abbrev").unwrap();
134        return gimli::DebugAbbrev::new(&debug_abbrev.data, self.endianess);
135    }
136
137    pub fn debug_str<'a>(&'a self) -> gimli::DebugStr<gimli::EndianBuf<'a, gimli::RunTimeEndian>> {
138        let debug_str = self.file.get_section(".debug_str").unwrap();
139        return gimli::DebugStr::new(&debug_str.data, self.endianess);
140    }
141
142    pub fn get_functions(self) -> Vec<Function> {
143        let mut functions: Vec<Function> = Vec::new();
144
145        let debug_info = self.debug_info();
146        let debug_abbrev = self.debug_abbrev();
147        let debug_str = self.debug_str();
148
149        let mut units = debug_info.units();
150        while let Some(unit) = units.next().unwrap() {
151            let abbrev = unit.abbreviations(&debug_abbrev).unwrap();
152
153            let mut tree = unit.entries_tree(&abbrev, None).unwrap();
154            let mut tree = tree.root().unwrap().children();
155
156            while let Some(child) = tree.next().unwrap() {
157                let entry = {
158                    child.entry().clone()
159                };
160                if entry.tag() == gimli::DW_TAG_subprogram {
161                    let func = get_function_from_dwarf_entry(child, &debug_str);
162
163                    functions.push(func);
164                }
165            }
166        }
167
168        return functions;
169    }
170}
171
172#[derive(Debug)]
173pub enum ExecutableLoadError {
174    Parse(elf::ParseError),
175    InvalidFile(&'static str),
176    MissingSection(&'static str),
177}
178
179pub fn load_executable(path: &path::Path) -> Result<Executable, ExecutableLoadError> {
180    let elf_file = try!(elf::File::open_path(&path).map_err(
181        ExecutableLoadError::Parse,
182    ));
183
184    let endianess = match elf_file.ehdr.data {
185        elf::types::ELFDATA2LSB => gimli::RunTimeEndian::Little,
186        elf::types::ELFDATA2MSB => gimli::RunTimeEndian::Big,
187        _ => {
188            return Err(ExecutableLoadError::InvalidFile(
189                "Invalid ELF file, not little endian or big endian",
190            ))
191        }
192    };
193
194    if elf_file.get_section(".debug_info").is_none() {
195        return Err(ExecutableLoadError::MissingSection(
196            "Missing debug_info section",
197        ));
198    }
199    if elf_file.get_section(".debug_abbrev").is_none() {
200        return Err(ExecutableLoadError::MissingSection(
201            "Missing debug_abbrev section",
202        ));
203    }
204    if elf_file.get_section(".debug_str").is_none() {
205        return Err(ExecutableLoadError::MissingSection(
206            "Missing debug_str section",
207        ));
208    }
209
210    return Ok(Executable {
211        path: path::PathBuf::from(path),
212        file: elf_file,
213        endianess: endianess,
214    });
215}