Home | History | Annotate | Download | only in LD
      1 //===- EhFrameReader.cpp --------------------------------------------------===//
      2 //
      3 //                     The MCLinker Project
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 #include "mcld/LD/EhFrameReader.h"
     10 
     11 #include "mcld/Fragment/NullFragment.h"
     12 #include "mcld/MC/Input.h"
     13 #include "mcld/LD/LDSection.h"
     14 #include "mcld/Support/MsgHandling.h"
     15 #include "mcld/Support/MemoryArea.h"
     16 
     17 #include <llvm/ADT/StringRef.h>
     18 #include <llvm/Support/Dwarf.h>
     19 #include <llvm/Support/LEB128.h>
     20 
     21 namespace mcld {
     22 
     23 //===----------------------------------------------------------------------===//
     24 // Helper Functions
     25 //===----------------------------------------------------------------------===//
     26 /// skip_LEB128 - skip the first LEB128 encoded value from *pp, update *pp
     27 /// to the next character.
     28 /// @return - false if we ran off the end of the string.
     29 static bool skip_LEB128(EhFrameReader::ConstAddress* pp,
     30                         EhFrameReader::ConstAddress pend) {
     31   for (EhFrameReader::ConstAddress p = *pp; p < pend; ++p) {
     32     if ((*p & 0x80) == 0x0) {
     33       *pp = p + 1;
     34       return true;
     35     }
     36   }
     37   return false;
     38 }
     39 
     40 //===----------------------------------------------------------------------===//
     41 // EhFrameReader
     42 //===----------------------------------------------------------------------===//
     43 template <>
     44 EhFrameReader::Token EhFrameReader::scan<true>(ConstAddress pHandler,
     45                                                uint64_t pOffset,
     46                                                llvm::StringRef pData) const {
     47   Token result;
     48   result.file_off = pOffset;
     49 
     50   const uint32_t* data = (const uint32_t*)pHandler;
     51   size_t cur_idx = 0;
     52 
     53   // Length Field
     54   uint32_t length = data[cur_idx++];
     55   if (length == 0x0) {
     56     // terminator
     57     result.kind = Terminator;
     58     result.data_off = 4;
     59     result.size = 4;
     60     return result;
     61   }
     62 
     63   // Extended Field
     64   uint64_t extended = 0x0;
     65   if (length == 0xFFFFFFFF) {
     66     extended = data[cur_idx++];
     67     extended <<= 32;
     68     extended |= data[cur_idx++];
     69     result.size = extended + 12;
     70     result.data_off = 16;
     71     // 64-bit obj file still uses 32-bit eh_frame.
     72     assert(false && "We don't support 64-bit eh_frame.");
     73   } else {
     74     result.size = length + 4;
     75     result.data_off = 8;
     76   }
     77 
     78   // ID Field
     79   uint32_t ID = data[cur_idx++];
     80   if (ID == 0x0)
     81     result.kind = CIE;
     82   else
     83     result.kind = FDE;
     84 
     85   return result;
     86 }
     87 
     88 template <>
     89 bool EhFrameReader::read<32, true>(Input& pInput, EhFrame& pEhFrame) {
     90   // Alphabet:
     91   //   {CIE, FDE, CIEt}
     92   //
     93   // Regular Expression:
     94   //   (CIE FDE*)+ CIEt
     95   //
     96   // Autometa:
     97   //   S = {Q0, Q1, Q2}, Start = Q0, Accept = Q2
     98   //
     99   //              FDE
    100   //             +---+
    101   //        CIE   \ /   CIEt
    102   //   Q0 -------> Q1 -------> Q2
    103   //    |         / \           ^
    104   //    |        +---+          |
    105   //    |         CIE           |
    106   //    +-----------------------+
    107   //              CIEt
    108   const State autometa[NumOfStates][NumOfTokenKinds] = {
    109       //     CIE     FDE    Term  Unknown
    110       {Q1, Reject, Accept, Reject},  // Q0
    111       {Q1, Q1, Accept, Reject},      // Q1
    112   };
    113 
    114   const Action transition[NumOfStates][NumOfTokenKinds] = {
    115       /*    CIE     FDE     Term Unknown */
    116       {addCIE, reject, addTerm, reject},  // Q0
    117       {addCIE, addFDE, addTerm, reject},  // Q1
    118   };
    119 
    120   LDSection& section = pEhFrame.getSection();
    121   if (section.size() == 0x0) {
    122     NullFragment* frag = new NullFragment();
    123     pEhFrame.addFragment(*frag);
    124     return true;
    125   }
    126 
    127   // get file offset and address
    128   uint64_t file_off = pInput.fileOffset() + section.offset();
    129   llvm::StringRef sect_reg =
    130       pInput.memArea()->request(file_off, section.size());
    131   ConstAddress handler = (ConstAddress)sect_reg.begin();
    132 
    133   State cur_state = Q0;
    134   while (Reject != cur_state && Accept != cur_state) {
    135     Token token = scan<true>(handler, file_off, sect_reg);
    136     llvm::StringRef entry =
    137         pInput.memArea()->request(token.file_off, token.size);
    138 
    139     if (!transition[cur_state][token.kind](pEhFrame, entry, token)) {
    140       // fail to scan
    141       debug(diag::debug_cannot_scan_eh) << pInput.name();
    142       return false;
    143     }
    144 
    145     file_off += token.size;
    146     handler += token.size;
    147 
    148     if (handler == sect_reg.end()) {
    149       cur_state = Accept;
    150     } else if (handler > sect_reg.end()) {
    151       cur_state = Reject;
    152     } else {
    153       cur_state = autometa[cur_state][token.kind];
    154     }
    155   }  // end of while
    156 
    157   if (Reject == cur_state) {
    158     // fail to parse
    159     debug(diag::debug_cannot_parse_eh) << pInput.name();
    160     return false;
    161   }
    162   return true;
    163 }
    164 
    165 bool EhFrameReader::addCIE(EhFrame& pEhFrame,
    166                            llvm::StringRef pRegion,
    167                            const EhFrameReader::Token& pToken) {
    168   // skip Length, Extended Length and CIE ID.
    169   ConstAddress handler = pRegion.begin() + pToken.data_off;
    170   ConstAddress cie_end = pRegion.end();
    171   ConstAddress handler_start = handler;
    172   uint64_t pr_ptr_data_offset = pToken.data_off;
    173 
    174   // the version should be 1 or 3
    175   uint8_t version = *handler++;
    176   if (version != 1 && version != 3) {
    177     return false;
    178   }
    179 
    180   // Set up the Augumentation String
    181   ConstAddress aug_str_front = handler;
    182   ConstAddress aug_str_back = static_cast<ConstAddress>(
    183       memchr(aug_str_front, '\0', cie_end - aug_str_front));
    184   if (aug_str_back == NULL) {
    185     return false;
    186   }
    187 
    188   // skip the Augumentation String field
    189   handler = aug_str_back + 1;
    190 
    191   // skip the Code Alignment Factor
    192   if (!skip_LEB128(&handler, cie_end)) {
    193     return false;
    194   }
    195   // skip the Data Alignment Factor
    196   if (!skip_LEB128(&handler, cie_end)) {
    197     return false;
    198   }
    199   // skip the Return Address Register
    200   if (cie_end - handler < 1) {
    201     return false;
    202   }
    203   ++handler;
    204 
    205   llvm::StringRef augment((const char*)aug_str_front);
    206 
    207   // we discard this CIE if the augumentation string is '\0'
    208   if (augment.size() == 0) {
    209     EhFrame::CIE* cie = new EhFrame::CIE(pRegion);
    210     cie->setFDEEncode(llvm::dwarf::DW_EH_PE_absptr);
    211     pEhFrame.addCIE(*cie);
    212     pEhFrame.getCIEMap().insert(std::make_pair(pToken.file_off, cie));
    213     return true;
    214   }
    215 
    216   // the Augmentation String start with 'eh' is a CIE from gcc before 3.0,
    217   // in LSB Core Spec 3.0RC1. We do not support it.
    218   if (augment.size() > 1 && augment[0] == 'e' && augment[1] == 'h') {
    219     return false;
    220   }
    221 
    222   // parse the Augmentation String to get the FDE encodeing if 'z' existed
    223   uint8_t fde_encoding = llvm::dwarf::DW_EH_PE_absptr;
    224   std::string augdata;
    225   std::string pr_ptr_data;
    226   if (augment[0] == 'z') {
    227     unsigned offset;
    228     size_t augdata_size = llvm::decodeULEB128((const uint8_t*)handler, &offset);
    229     handler += offset;
    230     augdata = std::string((const char*)handler, augdata_size);
    231 
    232     // parse the Augmentation String
    233     for (size_t i = 1; i < augment.size(); ++i) {
    234       switch (augment[i]) {
    235         // LDSA encoding (1 byte)
    236         case 'L': {
    237           if (cie_end - handler < 1) {
    238             return false;
    239           }
    240           ++handler;
    241           break;
    242         }
    243         // Two arguments, the first one represents the encoding of the second
    244         // argument (1 byte). The second one is the address of personality
    245         // routine.
    246         case 'P': {
    247           // the first argument
    248           if (cie_end - handler < 1) {
    249             return false;
    250           }
    251           uint8_t per_encode = *handler;
    252           ++handler;
    253           // get the length of the second argument
    254           uint32_t per_length = 0;
    255           if ((per_encode & 0x60) == 0x60) {
    256             return false;
    257           }
    258           switch (per_encode & 7) {
    259             default:
    260               return false;
    261             case llvm::dwarf::DW_EH_PE_udata2:
    262               per_length = 2;
    263               break;
    264             case llvm::dwarf::DW_EH_PE_udata4:
    265               per_length = 4;
    266               break;
    267             case llvm::dwarf::DW_EH_PE_udata8:
    268               per_length = 8;
    269               break;
    270             case llvm::dwarf::DW_EH_PE_absptr:
    271               per_length = 4;  // pPkg.bitclass / 8;
    272               break;
    273           }
    274           // skip the alignment
    275           if (llvm::dwarf::DW_EH_PE_aligned == (per_encode & 0xf0)) {
    276             uint32_t per_align = handler - cie_end;
    277             per_align += per_length - 1;
    278             per_align &= ~(per_length - 1);
    279             if (static_cast<uint32_t>(cie_end - handler) < per_align) {
    280               return false;
    281             }
    282             handler += per_align;
    283           }
    284           // skip the second argument
    285           if (static_cast<uint32_t>(cie_end - handler) < per_length) {
    286             return false;
    287           }
    288           pr_ptr_data_offset += handler - handler_start;
    289           pr_ptr_data = std::string((const char*)handler, per_length);
    290           handler += per_length;
    291           break;
    292         }  // end of case 'P'
    293 
    294         // FDE encoding (1 byte)
    295         case 'R': {
    296           if (cie_end - handler < 1) {
    297             return false;
    298           }
    299           fde_encoding = *handler;
    300           switch (fde_encoding & 7) {
    301             case llvm::dwarf::DW_EH_PE_udata2:
    302             case llvm::dwarf::DW_EH_PE_udata4:
    303             case llvm::dwarf::DW_EH_PE_udata8:
    304             case llvm::dwarf::DW_EH_PE_absptr:
    305               break;
    306             default:
    307               return false;
    308           }
    309           ++handler;
    310           break;
    311         }
    312         default:
    313           return false;
    314       }  // end switch
    315     }    // the rest chars.
    316   }      // first char is 'z'
    317 
    318   // create and push back the CIE entry
    319   EhFrame::CIE* cie = new EhFrame::CIE(pRegion);
    320   cie->setFDEEncode(fde_encoding);
    321   cie->setPersonalityOffset(pr_ptr_data_offset);
    322   cie->setPersonalityName(pr_ptr_data);
    323   cie->setAugmentationData(augdata);
    324   pEhFrame.addCIE(*cie);
    325   pEhFrame.getCIEMap().insert(std::make_pair(pToken.file_off, cie));
    326   return true;
    327 }
    328 
    329 bool EhFrameReader::addFDE(EhFrame& pEhFrame,
    330                            llvm::StringRef pRegion,
    331                            const EhFrameReader::Token& pToken) {
    332   if (pToken.data_off == pRegion.size())
    333     return false;
    334 
    335   const int32_t offset =
    336       *(const int32_t*)(pRegion.begin() + pToken.data_off - 4);
    337   size_t cie_offset =
    338       (size_t)((int64_t)(pToken.file_off + 4) - (int32_t)offset);
    339 
    340   EhFrame::CIEMap::iterator iter = pEhFrame.getCIEMap().find(cie_offset);
    341   if (iter == pEhFrame.getCIEMap().end())
    342     return false;
    343 
    344   // create and push back the FDE entry
    345   EhFrame::FDE* fde = new EhFrame::FDE(pRegion, *iter->second);
    346   pEhFrame.addFDE(*fde);
    347   return true;
    348 }
    349 
    350 bool EhFrameReader::addTerm(EhFrame& pEhFrame,
    351                             llvm::StringRef pRegion,
    352                             const EhFrameReader::Token& pToken) {
    353   return true;
    354 }
    355 
    356 bool EhFrameReader::reject(EhFrame& pEhFrame,
    357                            llvm::StringRef pRegion,
    358                            const EhFrameReader::Token& pToken) {
    359   return true;
    360 }
    361 
    362 }  // namespace mcld
    363