You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
367 lines
11 KiB
367 lines
11 KiB
//===- 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
|