-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathlib.rs
More file actions
155 lines (142 loc) · 5.15 KB
/
Copy pathlib.rs
File metadata and controls
155 lines (142 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use std::{borrow::Cow};
use addr2line::{Context, fallible_iterator::FallibleIterator, gimli, object::{self, Endianness, Object, ObjectSection}};
use typed_arena::Arena;
use regex::Regex;
#[cfg(target_family = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg(target_family = "wasm")]
use js_sys::Array;
fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
id: gimli::SectionId,
file: &object::File<'input>,
endian: Endian,
arena_data: &'arena Arena<Cow<'input, [u8]>>,
) -> Result<gimli::EndianSlice<'arena, Endian>, object::Error> {
let name = id.name();
match file.section_by_name(name) {
Some(section) => match section.uncompressed_data()? {
Cow::Borrowed(b) => Ok(gimli::EndianSlice::new(b, endian)),
Cow::Owned(b) => Ok(gimli::EndianSlice::new(arena_data.alloc(b.into()), endian)),
},
None => Ok(gimli::EndianSlice::new(&[][..], endian)),
}
}
#[cfg(not(target_family = "wasm"))]
pub struct DecodedAddress {
pub address: u64,
pub function_name: String,
pub location: String,
}
#[cfg(target_family = "wasm")]
#[wasm_bindgen]
pub struct DecodedAddress {
pub address: u64,
function_name: String,
location: String,
}
#[cfg(target_family = "wasm")]
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
impl DecodedAddress {
#[cfg_attr(target_family = "wasm", wasm_bindgen(getter))]
pub fn function_name(&self) -> String {
self.function_name.clone()
}
#[cfg_attr(target_family = "wasm", wasm_bindgen(setter))]
pub fn set_function_name(&mut self, function_name: String) {
self.function_name = function_name;
}
#[cfg_attr(target_family = "wasm", wasm_bindgen(getter))]
pub fn location(&self) -> String {
self.location.clone()
}
#[cfg_attr(target_family = "wasm", wasm_bindgen(setter))]
pub fn set_location(&mut self, location: String) {
self.location = location;
}
}
#[cfg(target_family = "wasm")]
type ReturnType = Array;
#[cfg(not(target_family = "wasm"))]
type ReturnType = Vec<DecodedAddress>;
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
pub fn decode(bin: &[u8], dump: &str) -> ReturnType {
// Prepare vector for output data
let mut decoded_data = Vec::<DecodedAddress>::new();
#[cfg(target_family = "wasm")]
let format_return = |decoded_data: Vec::<DecodedAddress>| {
decoded_data.into_iter().map(JsValue::from).collect()
};
#[cfg(not(target_family = "wasm"))]
let format_return = |decoded_data| {
decoded_data
};
// Used to keep slices from uncompressed section data
let arena_data = Arena::new();
// Parse the binary as an object file
let object = &object::File::parse(bin);
let object = match object {
Ok(object) => object,
Err(_) => return format_return(decoded_data),
};
let endianness = match object.endianness() {
Endianness::Little => gimli::RunTimeEndian::Little,
Endianness::Big => gimli::RunTimeEndian::Big
};
// Wrapper for the Dwarf loader
let mut load_section = |id: gimli::SectionId| -> Result<_, _> {
load_file_section(id, object, endianness, &arena_data)
};
// Load the Dwarf sections
let dwarf = gimli::Dwarf::load(&mut load_section);
let dwarf = match dwarf {
Ok(dwarf) => dwarf,
Err(_) => return format_return(decoded_data),
};
let ctx = Context::from_dwarf(dwarf);
let ctx = match ctx {
Ok(ctx) => ctx,
Err(_) => return format_return(decoded_data),
};
// Match everything that looks like a program address
let re = Regex::new(r"(4[0-9a-fA-F]{7})\b").unwrap();
for cap in re.captures_iter(dump) {
let address = u64::from_str_radix(&cap[0], 16).unwrap();
// Look for frame that contains the address
let mut frames = match ctx.find_frames(address) {
Ok(frames) => frames.enumerate(),
Err(_) => continue,
};
while let Some((_, frame)) = frames.next().unwrap() {
// Skip if it doesn't point to a function
if frame.function.is_none() {
continue;
}
// Extract function name
let func = frame.function.unwrap();
let function_name = match func.raw_name() {
Ok(function_name) => String::from(addr2line::demangle_auto(function_name, func.language)),
Err(_) => "unknown_func".to_string(),
};
// Extract location
let location = match frame.location {
Some(func_location) => {
let file = func_location.file.unwrap_or("?");
let line = match func_location.line {
Some(line) => line.to_string(),
None => "?".to_string(),
};
format!("{}:{}", file, line)
},
None => "?:?".to_string(),
};
// Append the decoded address
let decoded = DecodedAddress {
address,
function_name,
location,
};
decoded_data.push(decoded);
}
}
format_return(decoded_data)
}