From f3870ce80e6b165a5599592ff98a5767e207aa00 Mon Sep 17 00:00:00 2001 From: jdh8d <jdh8d@git.zephyr-software.com> Date: Wed, 5 Apr 2017 16:42:48 +0000 Subject: [PATCH] bug fixes and first tested version of eh-rewriting. --- include/ehwrite.h | 2 +- src/ehwrite.cpp | 354 +++++++++++++++++++++++++++++++++++----------- src/elfwrite.cpp | 4 + 3 files changed, 277 insertions(+), 83 deletions(-) diff --git a/include/ehwrite.h b/include/ehwrite.h index 8a87636..357b288 100644 --- a/include/ehwrite.h +++ b/include/ehwrite.h @@ -68,7 +68,7 @@ class EhWriterImpl_t : public EhWriter_t public: LSDArepresentation_t(libIRDB::Instruction_t* insn); void extend(libIRDB::Instruction_t* insn); - bool canExtend(libIRDB::Instruction_t* insn) const { (void)insn; return true; } + bool canExtend(libIRDB::Instruction_t* insn) const; bool exists() const { return callsite_table.size() > 0; } struct call_site_t diff --git a/src/ehwrite.cpp b/src/ehwrite.cpp index 542c23a..b7f01d0 100644 --- a/src/ehwrite.cpp +++ b/src/ehwrite.cpp @@ -222,6 +222,49 @@ EhWriterImpl_t<ptrsize>::FDErepresentation_t::LSDArepresentation_t::LSDArepresen extend(insn); } + + +static const auto RelocsEqual=[](const Relocation_t* a, const Relocation_t* b) -> bool +{ + return + forward_as_tuple(a->GetType(), a->GetOffset(), a->GetWRT(), a->GetAddend()) == + forward_as_tuple(b->GetType(), b->GetOffset(), b->GetWRT(), b->GetAddend()); +}; + +template <int ptrsize> +bool EhWriterImpl_t<ptrsize>::FDErepresentation_t::LSDArepresentation_t::canExtend(Instruction_t* insn) const +{ + if(insn->GetEhCallSite() == NULL) + return true; + + const auto tt_encoding = insn->GetEhCallSite()->GetTTEncoding(); + // no type table + if(tt_encoding==0xff) // DW_EH_PE_omit + { + // but there's relocs for the type table, + // this is a conflict. + if(insn->GetEhCallSite()->GetRelocations().size() > 0 ) + return false; + } + assert(tt_encoding==0x3 || tt_encoding==0xff); // DW_EH_PE_udata4 or omit + const auto tt_entry_size=4; + + const auto mismatch_tt_entry = find_if( + insn->GetEhCallSite()->GetRelocations().begin(), + insn->GetEhCallSite()->GetRelocations().end(), + [&](const Relocation_t* candidate_reloc) + { + const auto tt_index=candidate_reloc->GetOffset()/tt_entry_size; + if(tt_index>=type_table.size()) + return false; + return !RelocsEqual(candidate_reloc, type_table.at(tt_index)); + } + ); + + // return true if we found no mismatches + return (mismatch_tt_entry == insn->GetEhCallSite()->GetRelocations().end()); +} + template <int ptrsize> void EhWriterImpl_t<ptrsize>::FDErepresentation_t::LSDArepresentation_t::extend(Instruction_t* insn) { @@ -261,11 +304,28 @@ void EhWriterImpl_t<ptrsize>::FDErepresentation_t::LSDArepresentation_t::extend( // for now, this is the only supported reloc type on a EhCallSite assert(reloc->GetType()=="type_table_entry"); cs.actions.insert(reloc); - auto tt_it=find(type_table.begin(),type_table.end(), reloc); + auto tt_it=find_if(type_table.begin(),type_table.end(), + [reloc](const Relocation_t* candidate) { return RelocsEqual(candidate,reloc); }); if(tt_it==type_table.end()) - type_table.push_back(reloc); + { + const auto tt_encoding = insn->GetEhCallSite()->GetTTEncoding(); + assert(tt_encoding==0x3); // DW_EH_PE_udata4 + const auto tt_entry_size=4; + const auto tt_index= reloc->GetOffset()/tt_entry_size; + if(tt_index>=type_table.size()) + type_table.resize(tt_index+1); + assert(type_table.at(tt_index)==NULL || RelocsEqual(type_table.at(tt_index),reloc)); + type_table[tt_index]=reloc; + } } + // if the instruction has a cleanup, and there are other entries in the action table + // add a NULL to the action table to indicate we need to insert a cleanup indicator. + // if the cs.actions.size()==0, then we won't need any action list, and we can just set the "pointer" + // to the action table entry to be "NULL" + if(insn->GetEhCallSite()->GetHasCleanup() && cs.actions.size()>0) + cs.actions.insert(NULL); + // non-duplciating insert into the action table auto at_it=find(action_table.begin(),action_table.end(), cs.actions); if(at_it==action_table.end()) @@ -298,6 +358,10 @@ bool EhWriterImpl_t<ptrsize>::FDErepresentation_t::canExtend(Instruction_t* insn if(end_addr + new_fde_thresh < insn_addr) return false; + assert(cie); + if(!cie->canSupport(insn)) + return false; // can't change the CIE. + return pgm.canExtend(insn->GetEhProgram()->GetFDEProgram()) && lsda.canExtend(insn); @@ -337,11 +401,11 @@ void EhWriterImpl_t<ptrsize>::BuildFDEs() insn_in_order[this_pair.second]=this_pair.first; - // build the fdes (and cies/lsdas for this insn, starting with a null fde in case none exist + // build the fdes (and cies/lsdas) for this insn, starting with a null fde in case none exist auto current_fde=(FDErepresentation_t*)NULL; auto insns_with_frame=0; -// for_each instruction in program order + // for_each instruction in program order for(const auto& this_pair : insn_in_order) { const auto &this_insn=this_pair.second; @@ -413,39 +477,142 @@ void EhWriterImpl_t<ptrsize>::GenerateEhOutput() const auto output_lsda=[&](const FDErepresentation_t* fde, ostream& out, const uint32_t lsda_num) -> void { const auto lsda=&fde->lsda; + out<<"LSDA"<<dec<<lsda_num<<":"<<endl; + if(!lsda->exists()) + { + return; + } + + // the encoding of the landing pad is done in two parts: + // landing_pad=landing_pad_base+landing_pad_offset. + // typically, the landing_pad_base is omitted and the fde->start_addr is used instead. + // However, sometimes the fde->start_addr is equal to the landing_pad, + // which would make the landing_pad_offset 0. + // but a landing_pad offset of 0 indicates no landing pad. + // To avoid this situation, we first detect it, + // then arbitrarily pick (and encode) a landing_pad_base that's + // not equal to any landing paad in the call site list. + const auto calc_landing_pad_base=[&]() -> uint64_t + { + // look for a landing pad that happens to match the fde->start_addr + const auto lp_base_conflict_it=find_if( + lsda->callsite_table.begin(), + lsda->callsite_table.end(), + [&](const typename FDErepresentation_t::LSDArepresentation_t::call_site_t& candidate) + { + if(candidate.landing_pad==NULL) + return false; + const auto lp_addr=zipr_obj.GetLocationMap()->at(candidate.landing_pad); + return (lp_addr==fde->start_addr); + }); + + // if not found, there's no need to adjust the landing pad base address. + if(lp_base_conflict_it==lsda->callsite_table.end()) + return fde->start_addr; + + // we pick an arbitrary landing_pad base by taking the min of all + // landing pads (and the fde_start addr), and subtracting 1. + const auto min_cs_entry_it=min_element( + lsda->callsite_table.begin(), + lsda->callsite_table.end(), + [&](const typename FDErepresentation_t::LSDArepresentation_t::call_site_t& a , const typename FDErepresentation_t::LSDArepresentation_t::call_site_t &b) + { + const auto lp_a=a.landing_pad; + const auto lp_b=b.landing_pad; + if(lp_a && lp_b) + { + const auto lp_addr_a=zipr_obj.GetLocationMap()->at(lp_a); + const auto lp_addr_b=zipr_obj.GetLocationMap()->at(lp_b); + return lp_addr_a<lp_addr_b; + } + if(!lp_a && lp_b) + return false; + if(!lp_b && lp_a) + return true; + return false; + }); + const auto &min_cs_entry=*min_cs_entry_it; + const auto min_lp_addr=zipr_obj.GetLocationMap()->at(min_cs_entry.landing_pad); + const auto min_addr=min(min_lp_addr,fde->start_addr); + return min_addr-1; + + }; + + const auto landing_pad_base=calc_landing_pad_base(); - const auto output_action=[&](const set<Relocation_t*> acts, const uint32_t act_num) -> void + // how to output actions + const auto output_action=[&](const set<Relocation_t*> action_reloc_set, const uint32_t act_num) -> void { - const auto zero_it=find_if(acts.begin(), acts.end(), [](const Relocation_t* act) { return act->GetWRT()==NULL; }); - const auto has_zero=(zero_it!=acts.end()); + // determine if cleanup is requested. + const auto cleanup_it=find(action_reloc_set.begin(), action_reloc_set.end(), (Relocation_t*)NULL); + const auto has_cleanup=(cleanup_it!=action_reloc_set.end()); + + // determine if catch all is requested. + const auto catch_all_it=find_if(action_reloc_set.begin(), action_reloc_set.end(), + [&](const Relocation_t* reloc) { return reloc && reloc->GetWRT()==NULL; }); + const auto has_catch_all=(catch_all_it!=action_reloc_set.end()); // counter for the element in this action table entry. - auto act_entry_num=acts.size()-1; + auto act_entry_num=action_reloc_set.size()-1; - if(has_zero) + // sanity + if(has_cleanup && has_catch_all) + // what? + assert(0); + + // emit any cleanup first. + if(has_cleanup) { - const auto zero_reloc=*zero_it; - const auto tt_it=find(lsda->type_table.begin(), lsda->type_table.end(), zero_reloc); + // has to be first + assert(act_entry_num==action_reloc_set.size()-1); + + // output entry. + out<<"LSDA"<<dec<<lsda_num<<"_act"<<act_num<<"_start_entry"<<act_entry_num<<":"<<endl; + out<<" .uleb128 0 #cleanup"<<endl; + + out<<" .uleb128 0 "<<endl; // always comes last in this action_table set + act_entry_num--; + + } + if(has_catch_all) + { + const auto catch_all_reloc=*catch_all_it; + const auto tt_it=find_if(lsda->type_table.begin(), lsda->type_table.end(), + [&](const Relocation_t* candidate) { return RelocsEqual(candidate, catch_all_reloc); }); assert(tt_it != lsda->type_table.end()); const auto tt_index=tt_it-lsda->type_table.begin(); + out<<"LSDA"<<dec<<lsda_num<<"_act"<<act_num<<"_start_entry"<<act_entry_num<<":"<<endl; out<<" .uleb128 "<<dec<<1+tt_index<<endl; - out<<" .uleb128 0 "<<endl; + + if(act_entry_num==action_reloc_set.size()-1) + out<<" .uleb128 0 "<<endl; + else + out<<" .uleb128 LSDA"<<lsda_num<<"_act"<<act_num<<"_start_entry"<<act_entry_num+1<<" - . "<<endl; act_entry_num--; - } - for(const auto& act : acts) + for(const auto& action_reloc : action_reloc_set) { - // do nothing for a - if(act->GetWRT()==NULL) continue; + // indicates has_cleanup -- taken care of specially above. + if(action_reloc==NULL) + { + continue; + } + + // which indicates has catch all -- taken care of specially above. + if(action_reloc->GetWRT()==NULL) + { + continue; + } - const auto tt_it=find(lsda->type_table.begin(), lsda->type_table.end(), act); + const auto tt_it=find_if(lsda->type_table.begin(), lsda->type_table.end(), + [action_reloc](const Relocation_t* candidate) { return RelocsEqual(action_reloc,candidate); } ); assert(tt_it != lsda->type_table.end()); const auto tt_index=tt_it-lsda->type_table.begin(); out<<"LSDA"<<dec<<lsda_num<<"_act"<<act_num<<"_start_entry"<<act_entry_num<<""<<":"<<endl; out<<" .uleb128 "<<dec<<1+tt_index<<endl; - if(act_entry_num==acts.size()-1) + if(act_entry_num==action_reloc_set.size()-1) out<<" .uleb128 0 "<<endl; else out<<" .uleb128 LSDA"<<lsda_num<<"_act"<<act_num<<"_start_entry"<<act_entry_num+1<<" - . "<<endl; @@ -469,7 +636,7 @@ void EhWriterImpl_t<ptrsize>::GenerateEhOutput() { const auto lp_addr=zipr_obj.GetLocationMap()->at(cs.landing_pad); out<<" # 3) the landing pad, or 0 if none exists."<<endl; - out<<" .uleb128 0x"<<hex<<lp_addr<<" - 0x"<<hex<<fde->start_addr<<endl; + out<<" .uleb128 0x"<<hex<<lp_addr<<" - 0x"<<hex<<landing_pad_base<<endl; } else { @@ -479,7 +646,8 @@ void EhWriterImpl_t<ptrsize>::GenerateEhOutput() if(cs.actions.size() > 0 ) { out<<" # 4) index into action table + 1 -- 0 indicates unwind only"<<endl; - out<<" .uleb128 1 + LSDA"<<dec<<lsda_num<<"_act"<<cs.action_table_index<<"_start_entry0 - LSDA"<<dec<<lsda_num<<"_action_tab_start"<<endl; + out<<" .uleb128 1 + LSDA"<<dec<<lsda_num<<"_act" + <<cs.action_table_index<<"_start_entry0 - LSDA"<<dec<<lsda_num<<"_action_tab_start"<<endl; } else { @@ -490,68 +658,90 @@ void EhWriterImpl_t<ptrsize>::GenerateEhOutput() }; - out<<"LSDA"<<dec<<lsda_num<<":"<<endl; - if(!lsda->exists()) - { - return; - } - out<<""<<endl; - out<<" # 1) encoding of next field "<<endl; - out<<" .byte 0xff # DW_EH_PE_omit (0xff)"<<endl; - out<<""<<endl; - out<<" # 2) landing pad base, if omitted, use FDE start addr"<<endl; - out<<" # .<fdebasetype> <fdebase> -- omitted. "<<endl; - out<<""<<endl; - out<<" # 3) encoding of type table entries"<<endl; - out<<" .byte 0x3 # DW_EH_PE_udata4"<<endl; - out<<""<<endl; - out<<" # 4) type table pointer -- always a uleb128"<<endl; - out<<" .uleb128 LSDA"<<lsda_num<<"_type_table_end - LSDA"<<lsda_num<<"_tt_ptr_end"<<endl; - out<<"LSDA"<<lsda_num<<"_tt_ptr_end:"<<endl; - out<<""<<endl; - out<<" # 5) call site table encoding"<<endl; - out<<" .byte 0x1 # DW_EH_PE_uleb128 "<<endl; - out<<""<<endl; - out<<" # 6) the length of the call site table"<<endl; - out<<" .uleb128 LSDA"<<lsda_num<<"_cs_tab_end-LSDA"<<lsda_num<<"_cs_tab_start"<<endl; - out<<"LSDA"<<lsda_num<<"_cs_tab_start:"<<endl; - - - // output call site table - auto cs_num=0; - for(const auto & cs : lsda->callsite_table) - { - output_callsite(cs,cs_num++); - } - out<<"LSDA"<<dec<<lsda_num<<"_cs_tab_end:"<<endl; - - // output action table - out<<"LSDA"<<dec<<lsda_num<<"_action_tab_start:"<<endl; - auto act_num=0; - for(const auto & act : lsda->action_table) + const auto output_header=[&]() { - output_action(act,act_num++); - } - out<<"LSDA"<<dec<<lsda_num<<"_action_tab_end:"<<endl; - + if(landing_pad_base==fde->start_addr) + { + out<<" # 1) encoding of next field "<<endl; + out<<" .byte 0xff # DW_EH_PE_omit (0xff)"<<endl; + out<<""<<endl; + out<<" # 2) landing pad base, if omitted, use FDE start addr"<<endl; + out<<" # .<fdebasetype> <fdebase> -- omitted. "<<endl; + } + else + { + out<<" # 1) encoding of next field "<<endl; + out<<" .byte 0x1b # DW_EH_PE_pcrel (0x10) |sdata4 (0xb)"<<endl; + out<<""<<endl; + out<<" # 2) landing pad base, if omitted, use FDE start addr"<<endl; + out<<" .int 0x"<<hex<<landing_pad_base<<"- . # as pcrel|sdata4 . "<<endl; + } + out<<""<<endl; + out<<" # 3) encoding of type table entries"<<endl; + out<<" .byte 0x3 # DW_EH_PE_udata4"<<endl; + out<<""<<endl; + out<<" # 4) type table pointer -- always a uleb128"<<endl; + out<<" .uleb128 LSDA"<<lsda_num<<"_type_table_end - LSDA"<<lsda_num<<"_tt_ptr_end"<<endl; + out<<"LSDA"<<lsda_num<<"_tt_ptr_end:"<<endl; + out<<""<<endl; + out<<" # 5) call site table encoding"<<endl; + out<<" .byte 0x1 # DW_EH_PE_uleb128 "<<endl; + out<<""<<endl; + out<<" # 6) the length of the call site table"<<endl; + out<<" .uleb128 LSDA"<<lsda_num<<"_cs_tab_end-LSDA"<<lsda_num<<"_cs_tab_start"<<endl; + out<<"LSDA"<<lsda_num<<"_cs_tab_start:"<<endl; + }; - out<<"LSDA"<<dec<<lsda_num<<"_type_table_start:"<<endl; - for_each( lsda->type_table.rbegin(), lsda->type_table.rend(), [&](const Relocation_t* reloc) + const auto output_call_site_table=[&]() { - if(reloc->GetWRT()==NULL) + // output call site table + auto cs_num=0; + for(const auto & cs : lsda->callsite_table) { - out<<" .int 0x0"<<endl; + output_callsite(cs,cs_num++); } - else + out<<"LSDA"<<dec<<lsda_num<<"_cs_tab_end:"<<endl; + }; + const auto output_action_table=[&]() + { + // output action table + out<<"LSDA"<<dec<<lsda_num<<"_action_tab_start:"<<endl; + auto act_num=0; + for(const auto & act : lsda->action_table) { - const auto scoop=dynamic_cast<DataScoop_t*>(reloc->GetWRT()); - assert(scoop); - const auto final_addr=scoop->GetStart()->GetVirtualOffset() + reloc->GetAddend(); - out<<" .int 0x"<<hex<<final_addr<<endl; - + output_action(act,act_num++); } - }); - out<<"LSDA"<<dec<<lsda_num<<"_type_table_end:"<<endl; + out<<"LSDA"<<dec<<lsda_num<<"_action_tab_end:"<<endl; + }; + + const auto output_type_table=[&]() + { + out<<"LSDA"<<dec<<lsda_num<<"_type_table_start:"<<endl; + for_each( lsda->type_table.rbegin(), lsda->type_table.rend(), [&](const Relocation_t* reloc) + { + if(reloc->GetWRT()==NULL) + { + // indicates a catch all + out<<" .int 0x0"<<endl; + } + else + { + // indicates a catch of a paritcular type + const auto scoop=dynamic_cast<DataScoop_t*>(reloc->GetWRT()); + assert(scoop); + const auto final_addr=scoop->GetStart()->GetVirtualOffset() + reloc->GetAddend(); + out<<" .int 0x"<<hex<<final_addr<<endl; + + } + }); + out<<"LSDA"<<dec<<lsda_num<<"_type_table_end:"<<endl; + }; + + output_header(); + output_call_site_table(); + output_action_table(); + output_type_table(); + }; const auto output_cie=[&](const CIErepresentation_t* cie, ostream& out) -> void { @@ -757,17 +947,17 @@ void EhWriterImpl_t<ptrsize>::ScoopifyEhOutput() auto to_scoop=[&](const string &secname)->void { - auto sec=ehframe_exe_elfio.sections[secname]; + const auto &sec=ehframe_exe_elfio.sections[secname]; // if sec is missing, don't scoopify. if(sec==NULL) return; - auto data=string(sec->get_data(), sec->get_size()); - auto start_vo=sec->get_address(); - auto start_addr=new AddressID_t(BaseObj_t::NOT_IN_DATABASE, BaseObj_t::NOT_IN_DATABASE, start_vo); - auto end_vo=sec->get_address()+sec->get_size()-1; - auto end_addr=new AddressID_t(BaseObj_t::NOT_IN_DATABASE, BaseObj_t::NOT_IN_DATABASE, end_vo); - auto new_scoop=new DataScoop_t(BaseObj_t::NOT_IN_DATABASE, secname, start_addr,end_addr,NULL,4,false,data); + const auto data=string(sec->get_data(), sec->get_size()); + const auto start_vo=sec->get_address(); + const auto start_addr=new AddressID_t(BaseObj_t::NOT_IN_DATABASE, BaseObj_t::NOT_IN_DATABASE, start_vo); + const auto end_vo=sec->get_address()+sec->get_size()-1; + const auto end_addr=new AddressID_t(BaseObj_t::NOT_IN_DATABASE, BaseObj_t::NOT_IN_DATABASE, end_vo); + const auto new_scoop=new DataScoop_t(BaseObj_t::NOT_IN_DATABASE, secname, start_addr,end_addr,NULL,4,false,data); zipr_obj.GetFileIR()->GetAddresses().insert(start_addr); zipr_obj.GetFileIR()->GetAddresses().insert(end_addr); diff --git a/src/elfwrite.cpp b/src/elfwrite.cpp index b75babd..9eb41ba 100644 --- a/src/elfwrite.cpp +++ b/src/elfwrite.cpp @@ -781,6 +781,8 @@ void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_ { fseek(fout,0,SEEK_END); long cur_file_pos=ftell(fout); + fseek(fout,cur_file_pos+1,SEEK_SET); + cur_file_pos=ftell(fout); StringTable_t strtab; @@ -924,10 +926,12 @@ void ElfWriterImpl<T_Elf_Ehdr,T_Elf_Phdr,T_Elf_Addr,T_Elf_Shdr,T_Elf_Sym, T_Elf_ symtab_shdr. sh_entsize =0; shdrs.push_back(symtab_shdr); + cout<<"Writing strtab at filepos="<<hex<<cur_file_pos<<endl; strtab.Write(fout); long shdr_file_pos=ftell(fout); + cout<<"Writing section headers at filepos="<<hex<<shdr_file_pos<<endl; fwrite(shdrs.data(), sizeof(T_Elf_Shdr), shdrs.size(), fout); new_ehdr.e_shentsize=sizeof(T_Elf_Shdr); -- GitLab