Newer
Older
// @HEADER_COMPONENT libehp
// @HEADER_LANG C++
// @HEADER_BEGIN
int32_t handle_pcrel31(int32_t rel)
{
return (rel << 1) >> 1;
}
template <int ptrsize>
bool split_arm_eh_frame_impl_t<ptrsize>::parse(const bool is_be)
{
const auto contents = exidx_scoop->getContents(); // do not make reference, as we take a c_str() of this in a moemnt.
const auto fde_tab = reinterpret_cast<const uint32_t*>(contents.c_str());
const auto fde_end = reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(fde_tab)+exidx_scoop->getLength());
auto fde_idx=0u;
auto current_address = exidx_scoop->getStart();
vector<arm_fde_contents_t<ptrsize> > local_fdes;
while(&fde_tab[fde_idx+2] <= fde_end)
{
const auto first_entry = handle_pcrel31(fde_tab[fde_idx]);
const auto fde_start = current_address + first_entry;
const auto second_entry = fde_tab[fde_idx+1];
const auto upper_bit = second_entry >> 31;
const auto can_unwind = second_entry != 0x1; // can't unwind == 1:
const auto offset_to_start = handle_pcrel31(second_entry);
const auto contains_inline_unwind_entry = can_unwind && (second_entry>>31); // is inline if bit 31 set, and not special pattern cant_unwind.
//const auto inline_unwind_entry = second_entry & 0x7fffffff; // the EH unwind table entry itself if it can be encoded in 31 bits.
const auto lsda_addr =
!can_unwind ? 0 : // the special pattern 0x1 indicating can't unwind.
contains_inline_unwind_entry ? 0 : // no lsda addr if the entry is inline
upper_bit == 0 ? current_address + 4 + offset_to_start : // pcrel31 offset if bit 31 is clear.
throw runtime_error("Unexpected/logic error"); // report error if this is messed up.
// update the prior fde to end just before this one starts.
if(local_fdes.size() != 0)
{
local_fdes[local_fdes.size()-1].setEndAddress(fde_start-1);
}
auto fde = arm_fde_contents_t<ptrsize>{fde_start,lsda_addr,can_unwind};
/*
cout << hex ;
cout << "\tFde ("<< fde.getStartAddress();
cout << "-" << fde.getEndAddress();
cout << ")\tlsda_addr=" << fde.getLSDAAddress();
cout << "\tcan_unwind=" << boolalpha << fde.getCanUnwind() << endl;
*/
auto unwind_pgm =vector<uint8_t>();
if(lsda_addr)
{
// fetch the first word of the lsda.
throw_assert(extab_scoop->getStart() <= lsda_addr && lsda_addr <= extab_scoop->getEnd());
unwind_pgm=parse_arm_eh_pgm(lsda_addr,extab_scoop.get(),fde, is_be);
}
if(contains_inline_unwind_entry )
{
unwind_pgm=parse_arm_eh_pgm(current_address+4,exidx_scoop.get(),fde, is_be);
//cout << "\tFde ("<< fde.getStartAddress();
//cout << "Unwind pgm = " << hex << endl;
//for(auto byte : unwind_pgm)
//{
//cout << "\t" << +byte << endl;
//}
local_fdes.push_back(fde);
fde_idx += 2;
current_address += 8;
}
// last fde goes to the end of the linked section.
local_fdes[local_fdes.size()-1].setEndAddress(lnk_scoop->getEnd());
/*
for(const auto& fde: local_fdes)
{
cout << hex ;
cout << "\tFde ("<< fde.getStartAddress();
cout << "-" << fde.getEndAddress();
cout << ")\tlsda_addr=" << fde.getLSDAAddress();
cout << "\tcan_unwind=" << boolalpha << fde.getCanUnwind() << endl;
fde.getLSDA()->print();
}
*/
fdes.insert(ALLOF(local_fdes));
return true;
}
template <int ptrsize>
vector<uint8_t> split_arm_eh_frame_impl_t<ptrsize>::parse_arm_eh_pgm(const uint64_t lsda_addr, const ScoopReplacement_t *lsda_scoop, arm_fde_contents_t<ptrsize> &fde, const bool is_be)
{
auto unwind_pgm=vector<uint8_t>();
const auto fde_start=fde.getStartAddress();
// note: do not make reference as we are going to do unsafe stuff.
// and need a copy that won't change.
const auto contents_str = lsda_scoop->getContents();
const auto contents = reinterpret_cast<const uint8_t*>(contents_str.data());
const auto start_offset = lsda_addr - lsda_scoop->getStart();
// fetch 4 bytes to detect type
if(lsda_addr + sizeof(uint32_t) > lsda_scoop->getEnd())
throw out_of_range("Cannot parse lsda at " + to_hex_string(lsda_addr));
const auto first_word = *reinterpret_cast<const uint32_t*>(&contents[start_offset]);
if(first_word >> 31) // check the top bit.
{
const auto byte1 = (first_word >> 0)&0xff;
const auto byte2 = (first_word >> 8)&0xff;
const auto byte3 = (first_word >> 16)&0xff;
const auto byte4 = (first_word >> 24)&0xff;
const auto personality_index = byte4 & 0xf;
// cout << "Found arm32-specific personality routine, pr" << hex << personality_index << endl;
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
switch(personality_index)
{
case 0:
{
unwind_pgm.push_back(byte3);
unwind_pgm.push_back(byte2);
unwind_pgm.push_back(byte1);
break;
}
case 1:
case 2:
{
const auto words_following = byte3;
unwind_pgm.push_back(byte2);
unwind_pgm.push_back(byte1);
for(auto i = 0u; i < words_following; i++)
{
const auto next_word = *reinterpret_cast<const uint32_t*>(&contents[start_offset+4+i*4]);
unwind_pgm.push_back((next_word >> 24)&0xff);
unwind_pgm.push_back((next_word >> 16)&0xff);
unwind_pgm.push_back((next_word >> 8 )&0xff);
unwind_pgm.push_back((next_word >> 0 )&0xff);
}
break;
}
default:
throw new out_of_range("Unknown personality index: "+ to_string(personality_index));
}
}
else
{
// generic version.
const auto offset_to_personality_routine = handle_pcrel31(first_word);
const auto personality_routine_addr=lsda_addr+offset_to_personality_routine;
fde.setPersonality(personality_routine_addr);
// cout << "Found generic model with personality = " << hex << personality_routine_addr << endl;
const auto second_word = *reinterpret_cast<const uint32_t*>(&contents[start_offset+4]);
const auto byte1 = (second_word >> 0 )&0xff;
const auto byte2 = (second_word >> 8 )&0xff;
const auto byte3 = (second_word >> 16)&0xff;
const auto byte4 = (second_word >> 24)&0xff;
const auto words_following = byte4;
unwind_pgm.push_back(byte3);
unwind_pgm.push_back(byte2);
unwind_pgm.push_back(byte1);
for(auto i = 0u; i < words_following; i++)
{
const auto next_word = *reinterpret_cast<const uint32_t*>(&contents[start_offset+8+i*4]);
unwind_pgm.push_back((next_word >> 24)&0xff);
unwind_pgm.push_back((next_word >> 16)&0xff);
unwind_pgm.push_back((next_word >> 8 )&0xff);
unwind_pgm.push_back((next_word >> 0 )&0xff);
}
// 4 for personality routine, + 1 for a length specifier + the length in bytes.
fde.parse_lsda(lsda_addr+8+words_following*4,lsda_scoop, fde_start, is_be);
}
return unwind_pgm;
}
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
unique_ptr<const EHFrameParser_t> EHFrameParser_t::arm_factory(
uint8_t ptrsize,
EHPEndianness_t endian_type,
const string &extab_sec, const uint64_t extab_addr,
const string &exidx_sec, const uint64_t exidx_addr,
const string &lnk_sec, const uint64_t lnk_addr
)
{
const auto extab_scoop=ScoopReplacement_t(extab_sec, extab_addr);
const auto exidx_scoop=ScoopReplacement_t(exidx_sec,exidx_addr);
const auto lnk_scoop =ScoopReplacement_t(lnk_sec, lnk_addr);
auto ret_val=(EHFrameParser_t*)nullptr;
if(ptrsize==4)
ret_val=new split_arm_eh_frame_impl_t<4>(extab_scoop,exidx_scoop,lnk_scoop);
else if(ptrsize==8)
ret_val=new split_arm_eh_frame_impl_t<8>(extab_scoop,exidx_scoop,lnk_scoop);
else
throw out_of_range("ptrsize must be 4 or 8");
const auto is_big_endian = [] () -> bool
{
union
{
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
};
const auto is_be = endian_type == BIG || ( is_big_endian() && endian_type == HOST) ;
ret_val->parse(is_be);
return unique_ptr<const EHFrameParser_t>(ret_val);
}
template <int ptrsize>
const FDEVector_t* split_arm_eh_frame_impl_t<ptrsize>::getFDEs() const
{
if(fdes_cache.size()==0)
{
transform(ALLOF(fdes), back_inserter(fdes_cache), [](const arm_fde_contents_t<ptrsize> &a) { return &a; });
}
return &fdes_cache;
}
template <int ptrsize>
const CIEVector_t* split_arm_eh_frame_impl_t<ptrsize>::getCIEs() const
{
if(cies_cache.size()==0)
{
transform(ALLOF(fdes), back_inserter(cies_cache), [](const arm_fde_contents_t<ptrsize> &a) { return &a.getCIE(); });
}
return &cies_cache;
}
template <int ptrsize>
const FDEContents_t* split_arm_eh_frame_impl_t<ptrsize>::findFDE(uint64_t addr) const
{
const auto tofind=arm_fde_contents_t<ptrsize>( addr, addr+1);
const auto fde_it=fdes.find(tofind);
const auto raw_ret_ptr = (fde_it==fdes.end()) ? nullptr : &*fde_it;
return raw_ret_ptr;
}
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
template <int ptrsize>
const EHProgramInstructionVector_t* arm_eh_program_t<ptrsize>::getInstructions() const
{
if(instructions_cache.size()==0)
{
transform(ALLOF(instructions), back_inserter(instructions_cache), [](const arm_eh_program_insn_t<ptrsize> &a) { return &a;});
}
return &instructions_cache;
}
template <int ptrsize>
arm_eh_program_t<ptrsize>::arm_eh_program_t(const vector<uint8_t>& unwind_pgm)
{
auto unwind_idx=0u;
const auto unwind_pgm_data = unwind_pgm.data();
while(unwind_idx < unwind_pgm.size())
{
const auto opcode_byte1=unwind_pgm[unwind_idx];
const auto top_two=opcode_byte1>>6;
const auto top_four=opcode_byte1>>4;
const auto top_five=opcode_byte1>>3;
const auto bottom_three=opcode_byte1&0b111;
const auto bits543=(opcode_byte1>>3)&0b111;
// see https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst#frame-unwinding-instructions
if(
top_two == 0b00 || // vsp=vsp+6-bit-immed
top_two == 0b01 || // vsp=vsp-6-bit-immed
top_four==0b1001 || // set vsp=r[immed] || reserved if immed==13||15
top_four==0b1010 || // pop r4-r[4+immed] || pop r4-r[4+immed]+r14
opcode_byte1 == 0xb0 || // finish
opcode_byte1 == 0b10110100 || // Pop Return Address Authentication Code pseudo-register (see remark g)
opcode_byte1 == 0b10110101 || // Use current vsp as modifier in Return Addresss Authentication (see remark h)
opcode_byte1 == 0b10110110 || // Spare (was Pop FPA)
opcode_byte1 == 0b10110111 || // Spare (was Pop FPA)
top_five == 0b10111 || // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDX (see remark d)
(top_five == 0b11000 && bottom_three != 6 && bottom_three !=7)
|| // Intel Wireless MMX pop wR[10]-wR[10+nnn]
(top_five == 0b11001 && bottom_three != 0 && bottom_three !=1)
|| // Spare (yyy != 000, 001)
top_five == 0b10111 || // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by VPUSH (see remark d)
(top_two == 0b11&& bits543 != 0b000 && bits543 != 0b001 && bits543 != 0b010)
// Spare (xxx != 000, 001, 010)
)
{
// cout << "Found 1 byte unwind arm insn" << endl;
instructions.push_back(arm_eh_program_insn_t<ptrsize>({opcode_byte1}));
unwind_idx++;
}
else if(
top_four == 0b1000 || // 12-bit immed==0 ? refuse to unwind : pop registers indicated by immed
opcode_byte1 == 0b10110001 || // spare || pop registers
opcode_byte1 == 0b10110011 || // pop vfp double registers.
(top_five == 0b11000 && bottom_three == 6)
|| // Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e)
(top_five == 0b11000 && bottom_three == 7)
|| // Spare || Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} || Spare (xxxx != 0000)
opcode_byte1 == 0b11001000 || // Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] saved (as if) by VPUSH (see remarks d,e)
opcode_byte1 == 0b11001001 // Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by VPUSH (see remark d)
)
{
// cout << "Found 2 byte arm insn" << endl;
unwind_idx++;
if(unwind_idx>=unwind_pgm.size())
throw runtime_error("Cannot decode arm32 unwind instruction with prefix 0b1000");
const auto opcode_byte2=unwind_pgm[unwind_idx];
instructions.push_back(arm_eh_program_insn_t<ptrsize>({opcode_byte1,opcode_byte2}));
unwind_idx++;
}
else if (
opcode_byte1 == 0b10110010 // vsp += uleb128
)
{
// declare vars needed to call uleb routine.
const auto initial_pos=uint64_t{unwind_idx+1};
const auto max=initial_pos+unwind_pgm.size();
auto final_pos=initial_pos; // updated by read_uleb
auto res=uint64_t{0}; // ignore output of read_uleb, just need length
// read uleb128 and sanity check.
const auto fail = eh_frame_util_t<ptrsize>::read_uleb128(res,final_pos,unwind_pgm_data,max);
if(fail)
throw new out_of_range("Unable to read uleb128 in unwind_pgm");
// calc uleb length and record instructions..
const auto uleb_len=final_pos-initial_pos;
const auto unwind_pgm_start = unwind_pgm_data+unwind_idx;
const auto unwind_pgm_end = unwind_pgm_data+unwind_idx+1+uleb_len;
instructions.push_back(arm_eh_program_insn_t<ptrsize>(vector<uint8_t>({unwind_pgm_start,unwind_pgm_end})));
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
const auto insn_len = 1+uleb_len;
// cout << "Found multi-byte ( " << insn_len << " bytes) arm32 instructions" << endl;
unwind_idx+=insn_len;
}
else
throw new out_of_range("Cannot determine arm32 unwind instruction length");
}
}
template <int ptrsize>
void arm_eh_program_t<ptrsize>::print(const uint64_t pc, const int64_t caf) const
{
auto tmp_pc=pc;
for(const auto &insn : instructions)
insn.print(tmp_pc,caf);
}
template <int ptrsize>
void arm_eh_program_insn_t<ptrsize>::print(uint64_t &pc, int64_t caf) const
{
cout <<"arm32 unwind insn len=" << dec << program_bytes.size() << "bytes = ";
for(const auto byte : program_bytes)
cout << hex << +byte << ", ";
cout << endl;
}
template <int ptrsize>
void arm_fde_contents_t<ptrsize>::print() const
{
cout << "start_addr = " << hex << fde_start_addr << endl;
cout << "end_addr = " << hex << fde_end_addr << endl;
cout << "lsda_addr = " << hex << fde_lsda_addr << endl;
cout << "can_unwind = " << boolalpha << can_unwind << endl;
// lsda_t<ptrsize> lsda;
// arm_eh_program_t<ptrsize> eh_pgm;
// arm_cie_contents_t<ptrsize> cie;
}