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 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}