@@ -14,6 +14,9 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
+ #[ cfg( feature = "unwind_guest" ) ]
18
+ use std:: sync:: Arc ;
19
+
17
20
#[ cfg( target_arch = "aarch64" ) ]
18
21
use goblin:: elf:: reloc:: { R_AARCH64_NONE , R_AARCH64_RELATIVE } ;
19
22
#[ cfg( target_arch = "x86_64" ) ]
@@ -23,13 +26,85 @@ use goblin::elf64::program_header::PT_LOAD;
23
26
24
27
use crate :: { log_then_return, new_error, Result } ;
25
28
29
+ #[ cfg( feature = "unwind_guest" ) ]
30
+ struct ResolvedSectionHeader {
31
+ name : String ,
32
+ addr : u64 ,
33
+ offset : u64 ,
34
+ size : u64 ,
35
+ }
36
+
26
37
pub ( crate ) struct ElfInfo {
27
38
payload : Vec < u8 > ,
28
39
phdrs : ProgramHeaders ,
40
+ #[ cfg( feature = "unwind_guest" ) ]
41
+ shdrs : Vec < ResolvedSectionHeader > ,
29
42
entry : u64 ,
30
43
relocs : Vec < Reloc > ,
31
44
}
32
45
46
+ #[ cfg( feature = "unwind_guest" ) ]
47
+ struct UnwindInfo {
48
+ payload : Vec < u8 > ,
49
+ load_addr : u64 ,
50
+ va_size : u64 ,
51
+ base_svma : u64 ,
52
+ shdrs : Vec < ResolvedSectionHeader > ,
53
+ }
54
+
55
+ #[ cfg( feature = "unwind_guest" ) ]
56
+ impl super :: exe:: UnwindInfo for UnwindInfo {
57
+ fn as_module ( & self ) -> framehop:: Module < Vec < u8 > > {
58
+ framehop:: Module :: new (
59
+ // TODO: plumb through a name from from_file if this
60
+ // came from a file
61
+ "guest" . to_string ( ) ,
62
+ self . load_addr ..self . load_addr + self . va_size ,
63
+ self . load_addr ,
64
+ self ,
65
+ )
66
+ }
67
+ fn hash ( & self ) -> blake3:: Hash {
68
+ blake3:: hash ( & self . payload )
69
+ }
70
+ }
71
+
72
+ #[ cfg( feature = "unwind_guest" ) ]
73
+ impl UnwindInfo {
74
+ fn resolved_section_header ( & self , name : & [ u8 ] ) -> Option < & ResolvedSectionHeader > {
75
+ self . shdrs
76
+ . iter ( )
77
+ . find ( |& sh| sh. name . as_bytes ( ) [ 0 ..core:: cmp:: min ( name. len ( ) , sh. name . len ( ) ) ] == * name)
78
+ }
79
+ }
80
+
81
+ #[ cfg( feature = "unwind_guest" ) ]
82
+ impl framehop:: ModuleSectionInfo < Vec < u8 > > for & UnwindInfo {
83
+ fn base_svma ( & self ) -> u64 {
84
+ self . base_svma
85
+ }
86
+ fn section_svma_range ( & mut self , name : & [ u8 ] ) -> Option < std:: ops:: Range < u64 > > {
87
+ let shdr = self . resolved_section_header ( name) ?;
88
+ Some ( shdr. addr ..shdr. addr + shdr. size )
89
+ }
90
+ fn section_data ( & mut self , name : & [ u8 ] ) -> Option < Vec < u8 > > {
91
+ if name == b".eh_frame" && self . resolved_section_header ( b".debug_frame" ) . is_some ( ) {
92
+ /* Rustc does not always emit enough information for stack
93
+ * unwinding in .eh_frame, presumably because we use panic =
94
+ * abort in the guest. Framehop defaults to ignoring
95
+ * .debug_frame if .eh_frame exists, but we want the opposite
96
+ * behaviour here, since .debug_frame will actually contain
97
+ * frame information whereas .eh_frame often doesn't because
98
+ * of the aforementioned behaviour. Consequently, we hack
99
+ * around this by pretending that .eh_frame doesn't exist if
100
+ * .debug_frame does. */
101
+ return None ;
102
+ }
103
+ let shdr = self . resolved_section_header ( name) ?;
104
+ Some ( self . payload [ shdr. offset as usize ..( shdr. offset + shdr. size ) as usize ] . to_vec ( ) )
105
+ }
106
+ }
107
+
33
108
impl ElfInfo {
34
109
pub ( crate ) fn new ( bytes : & [ u8 ] ) -> Result < Self > {
35
110
let elf = Elf :: parse ( bytes) ?;
@@ -44,6 +119,19 @@ impl ElfInfo {
44
119
Ok ( ElfInfo {
45
120
payload : bytes. to_vec ( ) ,
46
121
phdrs : elf. program_headers ,
122
+ #[ cfg( feature = "unwind_guest" ) ]
123
+ shdrs : elf
124
+ . section_headers
125
+ . iter ( )
126
+ . filter_map ( |sh| {
127
+ Some ( ResolvedSectionHeader {
128
+ name : elf. shdr_strtab . get_at ( sh. sh_name ) ?. to_string ( ) ,
129
+ addr : sh. sh_addr ,
130
+ offset : sh. sh_offset ,
131
+ size : sh. sh_size ,
132
+ } )
133
+ } )
134
+ . collect ( ) ,
47
135
entry : elf. entry ,
48
136
relocs,
49
137
} )
@@ -68,7 +156,11 @@ impl ElfInfo {
68
156
. unwrap ( ) ; // guaranteed not to panic because of the check in new()
69
157
( max_phdr. p_vaddr + max_phdr. p_memsz - self . get_base_va ( ) ) as usize
70
158
}
71
- pub ( crate ) fn load_at ( self , load_addr : usize , target : & mut [ u8 ] ) -> Result < ( ) > {
159
+ pub ( crate ) fn load_at (
160
+ self ,
161
+ load_addr : usize ,
162
+ target : & mut [ u8 ] ,
163
+ ) -> Result < super :: exe:: LoadInfo > {
72
164
let base_va = self . get_base_va ( ) ;
73
165
for phdr in self . phdrs . iter ( ) . filter ( |phdr| phdr. p_type == PT_LOAD ) {
74
166
let start_va = ( phdr. p_vaddr - base_va) as usize ;
@@ -108,6 +200,20 @@ impl ElfInfo {
108
200
}
109
201
}
110
202
}
111
- Ok ( ( ) )
203
+ cfg_if:: cfg_if! {
204
+ if #[ cfg( feature = "unwind_guest" ) ] {
205
+ let va_size = self . get_va_size( ) as u64 ;
206
+ let base_svma = self . get_base_va( ) ;
207
+ Ok ( Arc :: new( UnwindInfo {
208
+ payload: self . payload,
209
+ load_addr: load_addr as u64 ,
210
+ va_size,
211
+ base_svma,
212
+ shdrs: self . shdrs,
213
+ } ) )
214
+ } else {
215
+ Ok ( ( ) )
216
+ }
217
+ }
112
218
}
113
219
}
0 commit comments