@@ -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" ) ]
@@ -23,13 +26,85 @@ use goblin::elf64::program_header::PT_LOAD;
2326
2427use crate :: { log_then_return, new_error, Result } ;
2528
29+ #[ cfg( feature = "unwind_guest" ) ]
30+ struct ResolvedSectionHeader {
31+ name : String ,
32+ addr : u64 ,
33+ offset : u64 ,
34+ size : u64 ,
35+ }
36+
2637pub ( crate ) struct ElfInfo {
2738 payload : Vec < u8 > ,
2839 phdrs : ProgramHeaders ,
40+ #[ cfg( feature = "unwind_guest" ) ]
41+ shdrs : Vec < ResolvedSectionHeader > ,
2942 entry : u64 ,
3043 relocs : Vec < Reloc > ,
3144}
3245
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+
33108impl ElfInfo {
34109 pub ( crate ) fn new ( bytes : & [ u8 ] ) -> Result < Self > {
35110 let elf = Elf :: parse ( bytes) ?;
@@ -44,6 +119,19 @@ impl ElfInfo {
44119 Ok ( ElfInfo {
45120 payload : bytes. to_vec ( ) ,
46121 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 ( ) ,
47135 entry : elf. entry ,
48136 relocs,
49137 } )
@@ -68,7 +156,11 @@ impl ElfInfo {
68156 . unwrap ( ) ; // guaranteed not to panic because of the check in new()
69157 ( max_phdr. p_vaddr + max_phdr. p_memsz - self . get_base_va ( ) ) as usize
70158 }
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 > {
72164 let base_va = self . get_base_va ( ) ;
73165 for phdr in self . phdrs . iter ( ) . filter ( |phdr| phdr. p_type == PT_LOAD ) {
74166 let start_va = ( phdr. p_vaddr - base_va) as usize ;
@@ -108,6 +200,20 @@ impl ElfInfo {
108200 }
109201 }
110202 }
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+ }
112218 }
113219}
0 commit comments