1
1
//! Parsing of GCC-style Language-Specific Data Area (LSDA)
2
2
//! For details see:
3
3
//! * <https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html>
4
+ //! * <https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html>
4
5
//! * <https://itanium-cxx-abi.github.io/cxx-abi/exceptions.pdf>
5
6
//! * <https://www.airs.com/blog/archives/460>
6
7
//! * <https://www.airs.com/blog/archives/464>
@@ -37,17 +38,19 @@ pub const DW_EH_PE_indirect: u8 = 0x80;
37
38
38
39
#[ derive( Copy , Clone ) ]
39
40
pub struct EHContext < ' a > {
40
- pub ip : usize , // Current instruction pointer
41
- pub func_start : usize , // Address of the current function
42
- pub get_text_start : & ' a dyn Fn ( ) -> usize , // Get address of the code section
43
- pub get_data_start : & ' a dyn Fn ( ) -> usize , // Get address of the data section
41
+ pub ip : * const u8 , // Current instruction pointer
42
+ pub func_start : * const u8 , // Pointer to the current function
43
+ pub get_text_start : & ' a dyn Fn ( ) -> * const u8 , // Get pointer to the code section
44
+ pub get_data_start : & ' a dyn Fn ( ) -> * const u8 , // Get pointer to the data section
44
45
}
45
46
47
+ /// Landing pad.
48
+ type LPad = * const u8 ;
46
49
pub enum EHAction {
47
50
None ,
48
- Cleanup ( usize ) ,
49
- Catch ( usize ) ,
50
- Filter ( usize ) ,
51
+ Cleanup ( LPad ) ,
52
+ Catch ( LPad ) ,
53
+ Filter ( LPad ) ,
51
54
Terminate ,
52
55
}
53
56
@@ -81,22 +84,24 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
81
84
let ip = context. ip ;
82
85
83
86
if !USING_SJLJ_EXCEPTIONS {
87
+ // read the callsite table
84
88
while reader. ptr < action_table {
85
- let cs_start = read_encoded_pointer ( & mut reader, context, call_site_encoding) ?;
86
- let cs_len = read_encoded_pointer ( & mut reader, context, call_site_encoding) ?;
87
- let cs_lpad = read_encoded_pointer ( & mut reader, context, call_site_encoding) ?;
89
+ // these are offsets rather than pointers;
90
+ let cs_start = read_encoded_offset ( & mut reader, call_site_encoding) ?;
91
+ let cs_len = read_encoded_offset ( & mut reader, call_site_encoding) ?;
92
+ let cs_lpad = read_encoded_offset ( & mut reader, call_site_encoding) ?;
88
93
let cs_action_entry = reader. read_uleb128 ( ) ;
89
94
// Callsite table is sorted by cs_start, so if we've passed the ip, we
90
95
// may stop searching.
91
- if ip < func_start + cs_start {
96
+ if ip < func_start. wrapping_add ( cs_start) {
92
97
break ;
93
98
}
94
- if ip < func_start + cs_start + cs_len {
99
+ if ip < func_start. wrapping_add ( cs_start + cs_len) {
95
100
if cs_lpad == 0 {
96
101
return Ok ( EHAction :: None ) ;
97
102
} else {
98
- let lpad = lpad_base + cs_lpad;
99
- return Ok ( interpret_cs_action ( action_table as * mut u8 , cs_action_entry, lpad) ) ;
103
+ let lpad = lpad_base. wrapping_add ( cs_lpad) ;
104
+ return Ok ( interpret_cs_action ( action_table, cs_action_entry, lpad) ) ;
100
105
}
101
106
}
102
107
}
@@ -106,30 +111,31 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
106
111
// SjLj version:
107
112
// The "IP" is an index into the call-site table, with two exceptions:
108
113
// -1 means 'no-action', and 0 means 'terminate'.
109
- match ip as isize {
114
+ match ip. addr ( ) as isize {
110
115
-1 => return Ok ( EHAction :: None ) ,
111
116
0 => return Ok ( EHAction :: Terminate ) ,
112
117
_ => ( ) ,
113
118
}
114
- let mut idx = ip;
119
+ let mut idx = ip. addr ( ) ;
115
120
loop {
116
121
let cs_lpad = reader. read_uleb128 ( ) ;
117
122
let cs_action_entry = reader. read_uleb128 ( ) ;
118
123
idx -= 1 ;
119
124
if idx == 0 {
120
125
// Can never have null landing pad for sjlj -- that would have
121
126
// been indicated by a -1 call site index.
122
- let lpad = ( cs_lpad + 1 ) as usize ;
123
- return Ok ( interpret_cs_action ( action_table as * mut u8 , cs_action_entry, lpad) ) ;
127
+ // FIXME(strict provenance)
128
+ let lpad = ptr:: from_exposed_addr ( ( cs_lpad + 1 ) as usize ) ;
129
+ return Ok ( interpret_cs_action ( action_table, cs_action_entry, lpad) ) ;
124
130
}
125
131
}
126
132
}
127
133
}
128
134
129
135
unsafe fn interpret_cs_action (
130
- action_table : * mut u8 ,
136
+ action_table : * const u8 ,
131
137
cs_action_entry : u64 ,
132
- lpad : usize ,
138
+ lpad : LPad ,
133
139
) -> EHAction {
134
140
if cs_action_entry == 0 {
135
141
// If cs_action_entry is 0 then this is a cleanup (Drop::drop). We run these
@@ -138,7 +144,7 @@ unsafe fn interpret_cs_action(
138
144
} else {
139
145
// If lpad != 0 and cs_action_entry != 0, we have to check ttype_index.
140
146
// If ttype_index == 0 under the condition, we take cleanup action.
141
- let action_record = ( action_table as * mut u8 ) . offset ( cs_action_entry as isize - 1 ) ;
147
+ let action_record = action_table. offset ( cs_action_entry as isize - 1 ) ;
142
148
let mut action_reader = DwarfReader :: new ( action_record) ;
143
149
let ttype_index = action_reader. read_sleb128 ( ) ;
144
150
if ttype_index == 0 {
@@ -157,22 +163,24 @@ fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
157
163
if align. is_power_of_two ( ) { Ok ( ( unrounded + align - 1 ) & !( align - 1 ) ) } else { Err ( ( ) ) }
158
164
}
159
165
160
- unsafe fn read_encoded_pointer (
161
- reader : & mut DwarfReader ,
162
- context : & EHContext < ' _ > ,
163
- encoding : u8 ,
164
- ) -> Result < usize , ( ) > {
165
- if encoding == DW_EH_PE_omit {
166
+ /// Read a offset (`usize`) from `reader` whose encoding is described by `encoding`.
167
+ ///
168
+ /// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext].
169
+ /// In addition the upper ("application") part must be zero.
170
+ ///
171
+ /// # Errors
172
+ /// Returns `Err` if `encoding`
173
+ /// * is not a valid DWARF Exception Header Encoding,
174
+ /// * is `DW_EH_PE_omit`, or
175
+ /// * has a non-zero application part.
176
+ ///
177
+ /// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
178
+ unsafe fn read_encoded_offset ( reader : & mut DwarfReader , encoding : u8 ) -> Result < usize , ( ) > {
179
+ if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 {
166
180
return Err ( ( ) ) ;
167
181
}
168
-
169
- // DW_EH_PE_aligned implies it's an absolute pointer value
170
- if encoding == DW_EH_PE_aligned {
171
- reader. ptr = reader. ptr . with_addr ( round_up ( reader. ptr . addr ( ) , mem:: size_of :: < usize > ( ) ) ?) ;
172
- return Ok ( reader. read :: < usize > ( ) ) ;
173
- }
174
-
175
- let mut result = match encoding & 0x0F {
182
+ let result = match encoding & 0x0F {
183
+ // despite the name, LLVM also uses absptr for offsets instead of pointers
176
184
DW_EH_PE_absptr => reader. read :: < usize > ( ) ,
177
185
DW_EH_PE_uleb128 => reader. read_uleb128 ( ) as usize ,
178
186
DW_EH_PE_udata2 => reader. read :: < u16 > ( ) as usize ,
@@ -184,25 +192,66 @@ unsafe fn read_encoded_pointer(
184
192
DW_EH_PE_sdata8 => reader. read :: < i64 > ( ) as usize ,
185
193
_ => return Err ( ( ) ) ,
186
194
} ;
195
+ Ok ( result)
196
+ }
197
+
198
+ /// Read a pointer from `reader` whose encoding is described by `encoding`.
199
+ ///
200
+ /// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext].
201
+ ///
202
+ /// # Errors
203
+ /// Returns `Err` if `encoding`
204
+ /// * is not a valid DWARF Exception Header Encoding,
205
+ /// * is `DW_EH_PE_omit`, or
206
+ /// * combines `DW_EH_PE_absptr` or `DW_EH_PE_aligned` application part with an integer encoding
207
+ /// (not `DW_EH_PE_absptr`) in the value format part.
208
+ ///
209
+ /// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
210
+ unsafe fn read_encoded_pointer (
211
+ reader : & mut DwarfReader ,
212
+ context : & EHContext < ' _ > ,
213
+ encoding : u8 ,
214
+ ) -> Result < * const u8 , ( ) > {
215
+ if encoding == DW_EH_PE_omit {
216
+ return Err ( ( ) ) ;
217
+ }
187
218
188
- result + = match encoding & 0x70 {
189
- DW_EH_PE_absptr => 0 ,
219
+ let base_ptr = match encoding & 0x70 {
220
+ DW_EH_PE_absptr => core :: ptr :: null ( ) ,
190
221
// relative to address of the encoded value, despite the name
191
- DW_EH_PE_pcrel => reader. ptr . expose_addr ( ) ,
222
+ DW_EH_PE_pcrel => reader. ptr ,
192
223
DW_EH_PE_funcrel => {
193
- if context. func_start == 0 {
224
+ if context. func_start . is_null ( ) {
194
225
return Err ( ( ) ) ;
195
226
}
196
227
context. func_start
197
228
}
198
229
DW_EH_PE_textrel => ( * context. get_text_start ) ( ) ,
199
230
DW_EH_PE_datarel => ( * context. get_data_start ) ( ) ,
231
+ // aligned means the value is aligned to the size of a pointer
232
+ DW_EH_PE_aligned => {
233
+ reader. ptr =
234
+ reader. ptr . with_addr ( round_up ( reader. ptr . addr ( ) , mem:: size_of :: < * const u8 > ( ) ) ?) ;
235
+ core:: ptr:: null ( )
236
+ }
200
237
_ => return Err ( ( ) ) ,
201
238
} ;
202
239
240
+ let mut ptr = if base_ptr. is_null ( ) {
241
+ // any value encoding other than absptr would be nonsensical here;
242
+ // there would be no source of pointer provenance
243
+ if encoding & 0x0F != DW_EH_PE_absptr {
244
+ return Err ( ( ) ) ;
245
+ }
246
+ reader. read :: < * const u8 > ( )
247
+ } else {
248
+ let offset = read_encoded_offset ( reader, encoding & 0x0F ) ?;
249
+ base_ptr. wrapping_add ( offset)
250
+ } ;
251
+
203
252
if encoding & DW_EH_PE_indirect != 0 {
204
- result = * ptr:: from_exposed_addr :: < usize > ( result ) ;
253
+ ptr = * ( ptr. cast :: < * const u8 > ( ) ) ;
205
254
}
206
255
207
- Ok ( result )
256
+ Ok ( ptr )
208
257
}
0 commit comments