17
17
*/
18
18
19
19
use crate :: process_map;
20
- use object:: { Object , ObjectSymbol } ;
21
- use std:: collections:: HashMap ;
20
+ use object:: { Object , ObjectSegment , ObjectSymbol } ;
21
+ use std:: collections:: { BTreeMap , HashMap } ;
22
22
23
- // A list of addresses to which a particular symbol name resolves.
24
- #[ derive( Debug ) ]
25
- pub struct SymbolEntry {
26
- // The addresses.
27
- pub addresses : Vec < u64 > ,
23
+ // A reference to a function mapped into the traced process.
24
+ #[ derive( Debug , Clone ) ]
25
+ pub struct SymbolInfo {
26
+ // The name of the function.
27
+ pub name : String ,
28
+
29
+ // The address in the traced process's address space.
30
+ pub address : u64 ,
31
+
32
+ // The length of the function in bytes.
33
+ pub size : u64 ,
28
34
}
29
35
30
36
// An index of symbol names and addresses to which those symbols resolve.
31
37
#[ derive( Debug ) ]
32
38
pub struct SymbolIndex {
33
- // The map from symbol name to list of addresses.
34
- pub symbols : HashMap < String , SymbolEntry > ,
39
+ // The map from symbol name to information about the symbol.
40
+ pub symbols_by_name : HashMap < String , Vec < SymbolInfo > > ,
41
+
42
+ // A map from address to symbol info.
43
+ pub symbols_by_address : BTreeMap < u64 , SymbolInfo > ,
35
44
}
36
45
37
46
impl SymbolIndex {
38
47
// Start a new empty symbol index.
39
48
pub fn new ( ) -> SymbolIndex {
40
- let symbols = HashMap :: new ( ) ;
41
- SymbolIndex { symbols }
49
+ SymbolIndex {
50
+ symbols_by_name : HashMap :: new ( ) ,
51
+ symbols_by_address : BTreeMap :: new ( ) ,
52
+ }
42
53
}
43
54
44
55
// Check whether a particular symbol falls within the address range
45
56
// mapped by a ProcessMapEntry, and if so, then store the relevant
46
57
// address in the symbol map.
47
- fn add_symbol ( & mut self , entry : & process_map:: ProcessMapEntry , symbol : & object:: Symbol ) {
58
+ fn add_symbol (
59
+ & mut self ,
60
+ entry : & process_map:: ProcessMapEntry ,
61
+ address_offset : i64 ,
62
+ symbol : & object:: Symbol ,
63
+ ) {
48
64
match symbol. name ( ) {
49
65
Ok ( name) => {
50
- if symbol. address ( ) >= entry. offset
51
- && symbol. address ( ) < entry. offset + ( entry. end - entry. begin )
66
+ let sym_address = ( symbol. address ( ) as i64 - address_offset) as u64 ;
67
+ let size = symbol. size ( ) ;
68
+
69
+ if sym_address >= entry. offset
70
+ && sym_address < entry. offset + ( entry. end - entry. begin )
52
71
{
53
- let address = entry. begin + symbol. address ( ) - entry. offset ;
54
- if !self . symbols . contains_key ( name) {
55
- let addresses = Vec :: new ( ) ;
56
- self . symbols
57
- . insert ( name. to_owned ( ) , SymbolEntry { addresses } ) ;
72
+ let address = entry. begin + sym_address - entry. offset ;
73
+ let symbol_info = SymbolInfo {
74
+ name : name. to_owned ( ) ,
75
+ address,
76
+ size,
77
+ } ;
78
+
79
+ if !self . symbols_by_name . contains_key ( name) {
80
+ self . symbols_by_name . insert ( name. to_owned ( ) , Vec :: new ( ) ) ;
58
81
}
59
- let entry = self . symbols . get_mut ( name) . unwrap ( ) ;
60
- entry. addresses . push ( address) ;
82
+ let entry = self . symbols_by_name . get_mut ( name) . unwrap ( ) ;
83
+ entry. push ( symbol_info. clone ( ) ) ;
84
+
85
+ self . symbols_by_address . insert ( address, symbol_info) ;
61
86
}
62
87
}
63
88
Err ( _) => ( ) ,
64
89
}
65
90
}
66
91
92
+ // Add symbols from a parsed object file to the symbol index.
93
+ fn add_elf_symbols ( & mut self , entry : & process_map:: ProcessMapEntry , elf : & object:: File ) {
94
+ let mut address_offset: Option < i64 > = None ;
95
+
96
+ for segment in elf. segments ( ) {
97
+ let range = segment. file_range ( ) ;
98
+
99
+ if range. 0 == entry. offset {
100
+ address_offset = Some ( ( segment. address ( ) - range. 0 ) as i64 ) ;
101
+ }
102
+ }
103
+
104
+ if address_offset == None {
105
+ return ;
106
+ }
107
+
108
+ // Iterate through all symbols in the binary, adding
109
+ // them to the symbol map if they are in the mmap
110
+ // range.
111
+ for symbol in elf. symbols ( ) {
112
+ self . add_symbol ( entry, address_offset. unwrap ( ) , & symbol) ;
113
+ }
114
+
115
+ // Similarly, but for dynamic symbols.
116
+ for symbol in elf. dynamic_symbols ( ) {
117
+ self . add_symbol ( entry, address_offset. unwrap ( ) , & symbol) ;
118
+ }
119
+ }
120
+
67
121
// Add all the symbols for a particluar mmaped range of an executable
68
122
// which has been mapped into a traced process.
69
123
pub fn add_entry_symbols ( & mut self , entry : & process_map:: ProcessMapEntry ) {
70
124
match & entry. filename {
71
125
Some ( filename) => match std:: fs:: read ( filename. clone ( ) ) {
72
126
Ok ( elf_data) => match object:: File :: parse ( & * elf_data) {
73
127
Ok ( elf) => {
74
- // Iterate through all symbols in the binary, adding
75
- // them to the symbol map if they are in the mmap
76
- // range.
77
- for symbol in elf. symbols ( ) {
78
- self . add_symbol ( entry, & symbol) ;
79
- }
80
-
81
- // Similarly, but for dynamic symbols.
82
- for symbol in elf. dynamic_symbols ( ) {
83
- self . add_symbol ( entry, & symbol) ;
84
- }
128
+ self . add_elf_symbols ( entry, & elf) ;
85
129
}
86
130
Err ( _) => ( ) ,
87
131
} ,
@@ -98,4 +142,24 @@ impl SymbolIndex {
98
142
self . add_entry_symbols ( & entry) ;
99
143
}
100
144
}
145
+
146
+ // Get function name by address. We'll try a few symbols which start
147
+ // proir to the address we are checking, as glibc likes to leave GLIBC
148
+ // symbols near the function name.
149
+ pub fn get_function_by_address ( & self , address : u64 ) -> Option < SymbolInfo > {
150
+ let mut tries = 0 ;
151
+ let mut symbols_by_range = self . symbols_by_address . range ( ..address + 1 ) ;
152
+ while let Some ( ( _, info) ) = symbols_by_range. next_back ( ) {
153
+ if address - info. address <= info. size {
154
+ return Some ( info. clone ( ) ) ;
155
+ }
156
+
157
+ tries += 1 ;
158
+ if tries >= 4 {
159
+ break ;
160
+ }
161
+ }
162
+
163
+ return None ;
164
+ }
101
165
}
0 commit comments