@@ -14,6 +14,9 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17+ #[ cfg( feature = "unwind_guest" ) ]
18+ use std:: sync:: Arc ;
19+
1720#[ cfg( target_arch = "aarch64" ) ]
1821use goblin:: elf:: reloc:: { R_AARCH64_NONE , R_AARCH64_RELATIVE } ;
1922#[ cfg( target_arch = "x86_64" ) ]
@@ -26,13 +29,85 @@ use goblin::elf64::program_header::PT_LOAD;
2629
2730use crate :: { Result , log_then_return, new_error} ;
2831
32+ #[ cfg( feature = "unwind_guest" ) ]
33+ struct ResolvedSectionHeader {
34+ name : String ,
35+ addr : u64 ,
36+ offset : u64 ,
37+ size : u64 ,
38+ }
39+
2940pub ( crate ) struct ElfInfo {
3041 payload : Vec < u8 > ,
3142 phdrs : ProgramHeaders ,
43+ #[ cfg( feature = "unwind_guest" ) ]
44+ shdrs : Vec < ResolvedSectionHeader > ,
3245 entry : u64 ,
3346 relocs : Vec < Reloc > ,
3447}
3548
49+ #[ cfg( feature = "unwind_guest" ) ]
50+ struct UnwindInfo {
51+ payload : Vec < u8 > ,
52+ load_addr : u64 ,
53+ va_size : u64 ,
54+ base_svma : u64 ,
55+ shdrs : Vec < ResolvedSectionHeader > ,
56+ }
57+
58+ #[ cfg( feature = "unwind_guest" ) ]
59+ impl super :: exe:: UnwindInfo for UnwindInfo {
60+ fn as_module ( & self ) -> framehop:: Module < Vec < u8 > > {
61+ framehop:: Module :: new (
62+ // TODO: plumb through a name from from_file if this
63+ // came from a file
64+ "guest" . to_string ( ) ,
65+ self . load_addr ..self . load_addr + self . va_size ,
66+ self . load_addr ,
67+ self ,
68+ )
69+ }
70+ fn hash ( & self ) -> blake3:: Hash {
71+ blake3:: hash ( & self . payload )
72+ }
73+ }
74+
75+ #[ cfg( feature = "unwind_guest" ) ]
76+ impl UnwindInfo {
77+ fn resolved_section_header ( & self , name : & [ u8 ] ) -> Option < & ResolvedSectionHeader > {
78+ self . shdrs
79+ . iter ( )
80+ . find ( |& sh| sh. name . as_bytes ( ) [ 0 ..core:: cmp:: min ( name. len ( ) , sh. name . len ( ) ) ] == * name)
81+ }
82+ }
83+
84+ #[ cfg( feature = "unwind_guest" ) ]
85+ impl framehop:: ModuleSectionInfo < Vec < u8 > > for & UnwindInfo {
86+ fn base_svma ( & self ) -> u64 {
87+ self . base_svma
88+ }
89+ fn section_svma_range ( & mut self , name : & [ u8 ] ) -> Option < std:: ops:: Range < u64 > > {
90+ let shdr = self . resolved_section_header ( name) ?;
91+ Some ( shdr. addr ..shdr. addr + shdr. size )
92+ }
93+ fn section_data ( & mut self , name : & [ u8 ] ) -> Option < Vec < u8 > > {
94+ if name == b".eh_frame" && self . resolved_section_header ( b".debug_frame" ) . is_some ( ) {
95+ /* Rustc does not always emit enough information for stack
96+ * unwinding in .eh_frame, presumably because we use panic =
97+ * abort in the guest. Framehop defaults to ignoring
98+ * .debug_frame if .eh_frame exists, but we want the opposite
99+ * behaviour here, since .debug_frame will actually contain
100+ * frame information whereas .eh_frame often doesn't because
101+ * of the aforementioned behaviour. Consequently, we hack
102+ * around this by pretending that .eh_frame doesn't exist if
103+ * .debug_frame does. */
104+ return None ;
105+ }
106+ let shdr = self . resolved_section_header ( name) ?;
107+ Some ( self . payload [ shdr. offset as usize ..( shdr. offset + shdr. size ) as usize ] . to_vec ( ) )
108+ }
109+ }
110+
36111impl ElfInfo {
37112 pub ( crate ) fn new ( bytes : & [ u8 ] ) -> Result < Self > {
38113 let elf = Elf :: parse ( bytes) ?;
@@ -47,6 +122,19 @@ impl ElfInfo {
47122 Ok ( ElfInfo {
48123 payload : bytes. to_vec ( ) ,
49124 phdrs : elf. program_headers ,
125+ #[ cfg( feature = "unwind_guest" ) ]
126+ shdrs : elf
127+ . section_headers
128+ . iter ( )
129+ . filter_map ( |sh| {
130+ Some ( ResolvedSectionHeader {
131+ name : elf. shdr_strtab . get_at ( sh. sh_name ) ?. to_string ( ) ,
132+ addr : sh. sh_addr ,
133+ offset : sh. sh_offset ,
134+ size : sh. sh_size ,
135+ } )
136+ } )
137+ . collect ( ) ,
50138 entry : elf. entry ,
51139 relocs,
52140 } )
@@ -73,7 +161,11 @@ impl ElfInfo {
73161 . unwrap ( ) ;
74162 ( max_phdr. p_vaddr + max_phdr. p_memsz - self . get_base_va ( ) ) as usize
75163 }
76- pub ( crate ) fn load_at ( & mut self , load_addr : usize , target : & mut [ u8 ] ) -> Result < ( ) > {
164+ pub ( crate ) fn load_at (
165+ self ,
166+ load_addr : usize ,
167+ target : & mut [ u8 ] ,
168+ ) -> Result < super :: exe:: LoadInfo > {
77169 let base_va = self . get_base_va ( ) ;
78170 for phdr in self . phdrs . iter ( ) . filter ( |phdr| phdr. p_type == PT_LOAD ) {
79171 let start_va = ( phdr. p_vaddr - base_va) as usize ;
@@ -113,6 +205,20 @@ impl ElfInfo {
113205 }
114206 }
115207 }
116- Ok ( ( ) )
208+ cfg_if:: cfg_if! {
209+ if #[ cfg( feature = "unwind_guest" ) ] {
210+ let va_size = self . get_va_size( ) as u64 ;
211+ let base_svma = self . get_base_va( ) ;
212+ Ok ( Arc :: new( UnwindInfo {
213+ payload: self . payload,
214+ load_addr: load_addr as u64 ,
215+ va_size,
216+ base_svma,
217+ shdrs: self . shdrs,
218+ } ) )
219+ } else {
220+ Ok ( ( ) )
221+ }
222+ }
117223 }
118224}
0 commit comments