//===- EhFrame.cpp --------------------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include <mcld/LD/EhFrame.h> #include <llvm/Support/Dwarf.h> #include <llvm/Support/Host.h> #include <mcld/MC/MCLinker.h> #include <mcld/Target/TargetLDBackend.h> #include <mcld/Support/MsgHandling.h> using namespace mcld; //========================== // EhFrame EhFrame::EhFrame() : m_fCanRecognizeAll(true) { } EhFrame::~EhFrame() { } uint64_t EhFrame::readEhFrame(Layout& pLayout, const TargetLDBackend& pBackend, SectionData& pSD, const Input& pInput, LDSection& pSection, MemoryArea& pArea) { MemoryRegion* region = pArea.request( pInput.fileOffset() + pSection.offset(), pSection.size()); // an empty .eh_frame if (NULL == region) { return 0; } ConstAddress eh_start = region->start(); ConstAddress eh_end = region->end(); ConstAddress p = eh_start; // read the Length filed uint32_t len = readVal(p, pBackend.isLittleEndian()); // This CIE is a terminator if the Length field is 0, return 0 to handled it // as an ordinary input. if (0 == len) { pArea.release(region); return 0; } if (0xffffffff == len) { debug(diag::debug_eh_unsupport) << pInput.name(); pArea.release(region); m_fCanRecognizeAll = false; return 0; } // record the order of the CIE and FDE fragments FragListType frag_list; while (p < eh_end) { if (eh_end - p < 4) { debug(diag::debug_eh_unsupport) << pInput.name(); m_fCanRecognizeAll = false; break; } // read the Length field len = readVal(p, pBackend.isLittleEndian()); p += 4; // the zero length entry should be the end of the section if (0 == len) { if (p < eh_end) { debug(diag::debug_eh_unsupport) << pInput.name(); m_fCanRecognizeAll = false; } break; } if (0xffffffff == len) { debug(diag::debug_eh_unsupport) << pInput.name(); m_fCanRecognizeAll = false; break; } if (eh_end - p < 4) { debug(diag::debug_eh_unsupport) << pInput.name(); m_fCanRecognizeAll = false; break; } // compute the section offset of this entry uint32_t ent_offset = static_cast<uint32_t>(p - eh_start - 4); // get the MemoryRegion for this entry MemoryRegion* ent_region = pArea.request( pInput.fileOffset() + pSection.offset() + ent_offset, len + 4); // create and add a CIE or FDE entry uint32_t id = readVal(p, pBackend.isLittleEndian()); // CIE if (0 == id) { if (!addCIE(*ent_region, pBackend, frag_list)) { m_fCanRecognizeAll = false; pArea.release(ent_region); break; } } // FDE else { if (!addFDE(*ent_region, pBackend, frag_list)) { m_fCanRecognizeAll = false; pArea.release(ent_region); break; } } p += len; } if (!m_fCanRecognizeAll) { debug(diag::debug_eh_unsupport) << pInput.name(); pArea.release(region); deleteFragments(frag_list, pArea); return 0; } // append all CIE and FDE fragments to Layout after we successfully read // this eh_frame size_t section_size = 0; for (FragListType::iterator it = frag_list.begin(); it != frag_list.end(); ++it) section_size += pLayout.appendFragment(**it, pSD, pSection.align()); pArea.release(region); return section_size; } bool EhFrame::addCIE(MemoryRegion& pRegion, const TargetLDBackend& pBackend, FragListType& pFragList) { ConstAddress cie_start = pRegion.start(); ConstAddress cie_end = pRegion.end(); ConstAddress p = cie_start; // skip the Length (4 byte) and CIE ID (4 byte) fields p += 8; // the version should be 1 if (1 != *p) { return false; } ++p; // get the Augumentation String ConstAddress aug_str = p; ConstAddress aug_str_end = static_cast<ConstAddress>( memchr(p, '\0', cie_end - p)); // skip the Augumentation String field p = aug_str_end + 1; // skip the Code Alignment Factor if (!skipLEB128(&p, cie_end)) { return false; } // skip the Data Alignment Factor if (!skipLEB128(&p, cie_end)) { return false; } // skip the Return Address Register if (cie_end - p < 1) { return false; } ++p; // 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 (aug_str[0] == 'e' && aug_str[1] == 'h') { return false; } // parse the Augmentation String to get the FDE encodeing if 'z' existed std::string aug_str_data; uint8_t fde_encoding = llvm::dwarf::DW_EH_PE_absptr; if (*aug_str == 'z') { aug_str_data += *aug_str; ++aug_str; // skip the Augumentation Data Length if (!skipLEB128(&p, cie_end)) { return false; } while (aug_str != aug_str_end) { switch (*aug_str) { default: return false; // LDSA encoding (1 byte) case 'L': if (cie_end - p < 1) { return false; } ++p; 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 - p < 1) { return false; } uint8_t per_encode = *p; ++p; // get the length of the second argument uint32_t per_length = 0; if (0x60 == (per_encode & 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 = pBackend.bitclass() / 8; break; } // skip the alignment if (llvm::dwarf::DW_EH_PE_aligned == (per_encode & 0xf0)) { uint32_t per_align = p - cie_end; per_align += per_length - 1; per_align &= ~(per_length -1); if (static_cast<uint32_t>(cie_end - p) < per_align) { return false; } p += per_align; } // skip the second argument if (static_cast<uint32_t>(cie_end - p) < per_length) { return false; } p += per_length; } break; // FDE encoding (1 byte) case 'R': if (cie_end - p < 1) { return false; } fde_encoding = *p; 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; } ++p; break; } // end switch aug_str_data += *aug_str; ++aug_str; } // end while } note(diag::note_eh_cie) << pRegion.size() << aug_str_data << (fde_encoding & 7); // create and push back the CIE entry CIE* entry = new CIE(pRegion, fde_encoding); m_CIEs.push_back(entry); pFragList.push_back(static_cast<Fragment*>(entry)); return true; } bool EhFrame::addFDE(MemoryRegion& pRegion, const TargetLDBackend& pBackend, FragListType& pFragList) { ConstAddress fde_start = pRegion.start(); ConstAddress fde_end = pRegion.end(); ConstAddress p = fde_start; // skip the Length (4 byte) and CIE Pointer (4 byte) fields p += 8; // get the entry offset of the PC Begin if (fde_end - p < 1) { return false; } FDE::Offset pc_offset = static_cast<FDE::Offset>(p - fde_start); note(diag::note_eh_fde) << pRegion.size() << pc_offset; // create and push back the FDE entry FDE* entry = new FDE(pRegion, **(m_CIEs.end() -1), pc_offset); m_FDEs.push_back(entry); pFragList.push_back(static_cast<Fragment*>(entry)); return true; } uint32_t EhFrame::readVal(ConstAddress pAddr, bool pIsTargetLittleEndian) { const uint32_t* p = reinterpret_cast<const uint32_t*>(pAddr); uint32_t val = *p; // byte swapping if the host and target have different endian if (llvm::sys::isLittleEndianHost() != pIsTargetLittleEndian) val = bswap32(val); return val; } bool EhFrame::skipLEB128(ConstAddress* pp, ConstAddress pend) { for (ConstAddress p = *pp; p < pend; ++p) { if (0 == (*p & 0x80)) { *pp = p + 1; return true; } } return false; } void EhFrame::deleteFragments(FragListType& pList, MemoryArea& pArea) { RegionFragment* frag = NULL; for (FragListType::iterator it = pList.begin(); it != pList.end(); ++it) { frag = static_cast<RegionFragment*>(*it); pArea.release(&(frag->getRegion())); delete *it; } pList.clear(); }