| //===- EhFrameReader.cpp --------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "mcld/LD/EhFrameReader.h" |
| |
| #include "mcld/Fragment/NullFragment.h" |
| #include "mcld/MC/Input.h" |
| #include "mcld/LD/LDSection.h" |
| #include "mcld/Support/MsgHandling.h" |
| #include "mcld/Support/MemoryArea.h" |
| |
| #include <llvm/ADT/StringRef.h> |
| #include <llvm/Support/Dwarf.h> |
| #include <llvm/Support/LEB128.h> |
| |
| namespace mcld { |
| |
| //===----------------------------------------------------------------------===// |
| // Helper Functions |
| //===----------------------------------------------------------------------===// |
| /// skip_LEB128 - skip the first LEB128 encoded value from *pp, update *pp |
| /// to the next character. |
| /// @return - false if we ran off the end of the string. |
| static bool skip_LEB128(EhFrameReader::ConstAddress* pp, |
| EhFrameReader::ConstAddress pend) { |
| for (EhFrameReader::ConstAddress p = *pp; p < pend; ++p) { |
| if ((*p & 0x80) == 0x0) { |
| *pp = p + 1; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // EhFrameReader |
| //===----------------------------------------------------------------------===// |
| template <> |
| EhFrameReader::Token EhFrameReader::scan<true>(ConstAddress pHandler, |
| uint64_t pOffset, |
| llvm::StringRef pData) const { |
| Token result; |
| result.file_off = pOffset; |
| |
| const uint32_t* data = (const uint32_t*)pHandler; |
| size_t cur_idx = 0; |
| |
| // Length Field |
| uint32_t length = data[cur_idx++]; |
| if (length == 0x0) { |
| // terminator |
| result.kind = Terminator; |
| result.data_off = 4; |
| result.size = 4; |
| return result; |
| } |
| |
| // Extended Field |
| uint64_t extended = 0x0; |
| if (length == 0xFFFFFFFF) { |
| extended = data[cur_idx++]; |
| extended <<= 32; |
| extended |= data[cur_idx++]; |
| result.size = extended + 12; |
| result.data_off = 16; |
| // 64-bit obj file still uses 32-bit eh_frame. |
| assert(false && "We don't support 64-bit eh_frame."); |
| } else { |
| result.size = length + 4; |
| result.data_off = 8; |
| } |
| |
| // ID Field |
| uint32_t ID = data[cur_idx++]; |
| if (ID == 0x0) |
| result.kind = CIE; |
| else |
| result.kind = FDE; |
| |
| return result; |
| } |
| |
| template <> |
| bool EhFrameReader::read<32, true>(Input& pInput, EhFrame& pEhFrame) { |
| // Alphabet: |
| // {CIE, FDE, CIEt} |
| // |
| // Regular Expression: |
| // (CIE FDE*)+ CIEt |
| // |
| // Autometa: |
| // S = {Q0, Q1, Q2}, Start = Q0, Accept = Q2 |
| // |
| // FDE |
| // +---+ |
| // CIE \ / CIEt |
| // Q0 -------> Q1 -------> Q2 |
| // | / \ ^ |
| // | +---+ | |
| // | CIE | |
| // +-----------------------+ |
| // CIEt |
| const State autometa[NumOfStates][NumOfTokenKinds] = { |
| // CIE FDE Term Unknown |
| {Q1, Reject, Accept, Reject}, // Q0 |
| {Q1, Q1, Accept, Reject}, // Q1 |
| }; |
| |
| const Action transition[NumOfStates][NumOfTokenKinds] = { |
| /* CIE FDE Term Unknown */ |
| {addCIE, reject, addTerm, reject}, // Q0 |
| {addCIE, addFDE, addTerm, reject}, // Q1 |
| }; |
| |
| LDSection& section = pEhFrame.getSection(); |
| if (section.size() == 0x0) { |
| NullFragment* frag = new NullFragment(); |
| pEhFrame.addFragment(*frag); |
| return true; |
| } |
| |
| // get file offset and address |
| uint64_t file_off = pInput.fileOffset() + section.offset(); |
| llvm::StringRef sect_reg = |
| pInput.memArea()->request(file_off, section.size()); |
| ConstAddress handler = (ConstAddress)sect_reg.begin(); |
| |
| State cur_state = Q0; |
| while (Reject != cur_state && Accept != cur_state) { |
| Token token = scan<true>(handler, file_off, sect_reg); |
| llvm::StringRef entry = |
| pInput.memArea()->request(token.file_off, token.size); |
| |
| if (!transition[cur_state][token.kind](pEhFrame, entry, token)) { |
| // fail to scan |
| debug(diag::debug_cannot_scan_eh) << pInput.name(); |
| return false; |
| } |
| |
| file_off += token.size; |
| handler += token.size; |
| |
| if (handler == sect_reg.end()) { |
| cur_state = Accept; |
| } else if (handler > sect_reg.end()) { |
| cur_state = Reject; |
| } else { |
| cur_state = autometa[cur_state][token.kind]; |
| } |
| } // end of while |
| |
| if (Reject == cur_state) { |
| // fail to parse |
| debug(diag::debug_cannot_parse_eh) << pInput.name(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool EhFrameReader::addCIE(EhFrame& pEhFrame, |
| llvm::StringRef pRegion, |
| const EhFrameReader::Token& pToken) { |
| // skip Length, Extended Length and CIE ID. |
| ConstAddress handler = pRegion.begin() + pToken.data_off; |
| ConstAddress cie_end = pRegion.end(); |
| ConstAddress handler_start = handler; |
| uint64_t pr_ptr_data_offset = pToken.data_off; |
| |
| // the version should be 1 or 3 |
| uint8_t version = *handler++; |
| if (version != 1 && version != 3) { |
| return false; |
| } |
| |
| // Set up the Augumentation String |
| ConstAddress aug_str_front = handler; |
| ConstAddress aug_str_back = static_cast<ConstAddress>( |
| memchr(aug_str_front, '\0', cie_end - aug_str_front)); |
| if (aug_str_back == NULL) { |
| return false; |
| } |
| |
| // skip the Augumentation String field |
| handler = aug_str_back + 1; |
| |
| // skip the Code Alignment Factor |
| if (!skip_LEB128(&handler, cie_end)) { |
| return false; |
| } |
| // skip the Data Alignment Factor |
| if (!skip_LEB128(&handler, cie_end)) { |
| return false; |
| } |
| // skip the Return Address Register |
| if (version == 1) { |
| if (cie_end - handler < 1) |
| return false; |
| ++handler; |
| } else { |
| if (!skip_LEB128(&handler, cie_end)) |
| return false; |
| } |
| |
| llvm::StringRef augment((const char*)aug_str_front); |
| |
| // we discard this CIE if the augumentation string is '\0' |
| if (augment.size() == 0) { |
| EhFrame::CIE* cie = new EhFrame::CIE(pRegion); |
| cie->setFDEEncode(llvm::dwarf::DW_EH_PE_absptr); |
| pEhFrame.addCIE(*cie); |
| pEhFrame.getCIEMap().insert(std::make_pair(pToken.file_off, cie)); |
| return true; |
| } |
| |
| // the Augmentation String start with 'eh' is a CIE from gcc before 3.0, |
| // in LSB Core Spec 3.0RC1. We do not support it. |
| if (augment.size() > 1 && augment[0] == 'e' && augment[1] == 'h') { |
| return false; |
| } |
| |
| // parse the Augmentation String to get the FDE encodeing if 'z' existed |
| uint8_t fde_encoding = llvm::dwarf::DW_EH_PE_absptr; |
| std::string augdata; |
| std::string pr_ptr_data; |
| if (augment[0] == 'z') { |
| unsigned offset; |
| size_t augdata_size = llvm::decodeULEB128((const uint8_t*)handler, &offset); |
| handler += offset; |
| augdata = std::string((const char*)handler, augdata_size); |
| |
| // parse the Augmentation String |
| for (size_t i = 1; i < augment.size(); ++i) { |
| switch (augment[i]) { |
| // LDSA encoding (1 byte) |
| case 'L': { |
| if (cie_end - handler < 1) { |
| return false; |
| } |
| ++handler; |
| break; |
| } |
| // Two arguments, the first one represents the encoding of the second |
| // argument (1 byte). The second one is the address of personality |
| // routine. |
| case 'P': { |
| // the first argument |
| if (cie_end - handler < 1) { |
| return false; |
| } |
| uint8_t per_encode = *handler; |
| ++handler; |
| // get the length of the second argument |
| uint32_t per_length = 0; |
| if ((per_encode & 0x60) == 0x60) { |
| return false; |
| } |
| switch (per_encode & 7) { |
| default: |
| return false; |
| case llvm::dwarf::DW_EH_PE_udata2: |
| per_length = 2; |
| break; |
| case llvm::dwarf::DW_EH_PE_udata4: |
| per_length = 4; |
| break; |
| case llvm::dwarf::DW_EH_PE_udata8: |
| per_length = 8; |
| break; |
| case llvm::dwarf::DW_EH_PE_absptr: |
| per_length = 4; // pPkg.bitclass / 8; |
| break; |
| } |
| // skip the alignment |
| if (llvm::dwarf::DW_EH_PE_aligned == (per_encode & 0xf0)) { |
| uint32_t per_align = handler - cie_end; |
| per_align += per_length - 1; |
| per_align &= ~(per_length - 1); |
| if (static_cast<uint32_t>(cie_end - handler) < per_align) { |
| return false; |
| } |
| handler += per_align; |
| } |
| // skip the second argument |
| if (static_cast<uint32_t>(cie_end - handler) < per_length) { |
| return false; |
| } |
| pr_ptr_data_offset += handler - handler_start; |
| pr_ptr_data = std::string((const char*)handler, per_length); |
| handler += per_length; |
| break; |
| } // end of case 'P' |
| |
| // FDE encoding (1 byte) |
| case 'R': { |
| if (cie_end - handler < 1) { |
| return false; |
| } |
| fde_encoding = *handler; |
| switch (fde_encoding & 7) { |
| case llvm::dwarf::DW_EH_PE_udata2: |
| case llvm::dwarf::DW_EH_PE_udata4: |
| case llvm::dwarf::DW_EH_PE_udata8: |
| case llvm::dwarf::DW_EH_PE_absptr: |
| break; |
| default: |
| return false; |
| } |
| ++handler; |
| break; |
| } |
| default: |
| return false; |
| } // end switch |
| } // the rest chars. |
| } // first char is 'z' |
| |
| // create and push back the CIE entry |
| EhFrame::CIE* cie = new EhFrame::CIE(pRegion); |
| cie->setFDEEncode(fde_encoding); |
| cie->setPersonalityOffset(pr_ptr_data_offset); |
| cie->setPersonalityName(pr_ptr_data); |
| cie->setAugmentationData(augdata); |
| pEhFrame.addCIE(*cie); |
| pEhFrame.getCIEMap().insert(std::make_pair(pToken.file_off, cie)); |
| return true; |
| } |
| |
| bool EhFrameReader::addFDE(EhFrame& pEhFrame, |
| llvm::StringRef pRegion, |
| const EhFrameReader::Token& pToken) { |
| if (pToken.data_off == pRegion.size()) |
| return false; |
| |
| const int32_t offset = |
| *(const int32_t*)(pRegion.begin() + pToken.data_off - 4); |
| size_t cie_offset = |
| (size_t)((int64_t)(pToken.file_off + 4) - (int32_t)offset); |
| |
| EhFrame::CIEMap::iterator iter = pEhFrame.getCIEMap().find(cie_offset); |
| if (iter == pEhFrame.getCIEMap().end()) |
| return false; |
| |
| // create and push back the FDE entry |
| EhFrame::FDE* fde = new EhFrame::FDE(pRegion, *iter->second); |
| pEhFrame.addFDE(*fde); |
| return true; |
| } |
| |
| bool EhFrameReader::addTerm(EhFrame& pEhFrame, |
| llvm::StringRef pRegion, |
| const EhFrameReader::Token& pToken) { |
| return true; |
| } |
| |
| bool EhFrameReader::reject(EhFrame& pEhFrame, |
| llvm::StringRef pRegion, |
| const EhFrameReader::Token& pToken) { |
| return true; |
| } |
| |
| } // namespace mcld |