Bug 788974 - Don't disable elfhack when enabling profiling on platforms supporting dl_iterate_phdr. r=glandium.

This commit is contained in:
Julian Seward 2014-11-10 09:31:09 +01:00
parent d0efa48997
commit 63a104efba
5 changed files with 101 additions and 77 deletions

View File

@ -238,13 +238,13 @@ template<typename ElfClass>
bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
const typename ElfClass::Shdr* exidx_section,
const typename ElfClass::Shdr* extab_section,
uint32_t loading_addr,
uintptr_t text_bias,
uintptr_t rx_avma, size_t rx_size,
SecMap* smap,
void (*log)(const char*)) {
// To do this properly we need to know:
// * the bounds of the .ARM.exidx section in the mapped image
// * the bounds of the .ARM.extab section in the mapped image
// * the bounds of the .ARM.exidx section in the process image
// * the bounds of the .ARM.extab section in the process image
// * the vma of the last byte in the text section associated with the .exidx
// The first two are easy. The third is a bit tricky. If we can't
// figure out what it is, just pass in zero.
@ -258,18 +258,46 @@ bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
// though .extab is missing, the range checks done by GET_EX_U32 in
// ExceptionTableInfo::ExtabEntryExtract should prevent any invalid
// memory accesses, and cause the .extab to be rejected as invalid.
const char *exidx_img
= GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset);
size_t exidx_size = exidx_section->sh_size;
const char *extab_img
= extab_section
? GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset)
: nullptr;
size_t extab_size = extab_section ? extab_section->sh_size : 0;
uintptr_t exidx_svma = exidx_section->sh_addr;
uintptr_t exidx_avma = exidx_svma + text_bias;
size_t exidx_size = exidx_section->sh_size;
uintptr_t extab_svma = 0;
uintptr_t extab_avma = 0;
size_t extab_size = 0;
if (extab_section) {
extab_svma = extab_section->sh_addr;
extab_avma = extab_svma + text_bias;
extab_size = extab_section->sh_size;
}
// Because we are reading EXIDX directly out of the executing image,
// we need to be careful to check that the relevant sections have
// really been mapped with r permissions, so as to guarantee that
// reading them won't segfault. Do this by checking that rx mapped
// area covers the exidx and extab as mapped in.
if (rx_size == 0)
// This seems sufficiently bogus that we shouldn't proceed further.
return false;
if (exidx_size == 0)
// There's no EXIDX data. No point in continuing.
return false;
if (!(exidx_avma >= rx_avma && exidx_avma + exidx_size <= rx_avma + rx_size))
// The mapped .exidx isn't entirely inside the rx area.
return false;
if (extab_section &&
!(extab_avma >= rx_avma && extab_avma + extab_size <= rx_avma + rx_size))
// There an .extab section, but it isn't entirely inside the rx area.
return false;
// The sh_link field of the exidx section gives the section number
// for the associated text section.
uint32_t exidx_text_last_svma = 0;
uint32_t exidx_text_last_avma = 0;
int exidx_text_sno = exidx_section->sh_link;
typedef typename ElfClass::Shdr Shdr;
// |sections| points to the section header table
@ -279,17 +307,18 @@ bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) {
const Shdr* exidx_text_shdr = &sections[exidx_text_sno];
if (exidx_text_shdr->sh_size > 0) {
exidx_text_last_svma
uint32_t exidx_text_last_svma
= exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1;
exidx_text_last_avma
= exidx_text_last_svma + text_bias;
}
}
lul::ARMExToModule handler(smap, log);
lul::ExceptionTableInfo
parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma,
&handler,
reinterpret_cast<const char*>(elf_header),
loading_addr, text_bias, log);
parser(reinterpret_cast<const char*>(exidx_avma), exidx_size,
reinterpret_cast<const char*>(extab_avma), extab_size,
exidx_text_last_avma, &handler, log);
parser.Start();
return true;
}
@ -417,7 +446,7 @@ bool LoadSymbols(const string& obj_file,
const bool read_gnu_debug_link,
LoadSymbolsInfo<ElfClass>* info,
SecMap* smap,
void* rx_avma,
void* rx_avma, size_t rx_size,
void (*log)(const char*)) {
typedef typename ElfClass::Phdr Phdr;
typedef typename ElfClass::Shdr Shdr;
@ -523,7 +552,9 @@ bool LoadSymbols(const string& obj_file,
info->LoadedSection(".ARM.extab");
bool result = LoadARMexidx<ElfClass>(elf_header,
arm_exidx_section, arm_extab_section,
loading_addr, text_bias, smap, log);
text_bias,
reinterpret_cast<uintptr_t>(rx_avma),
rx_size, smap, log);
found_usable_info = found_usable_info || result;
if (result)
log("LoadSymbols: read EXIDX from .ARM.{exidx,extab}");
@ -590,7 +621,7 @@ template<typename ElfClass>
bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
const string& obj_filename,
const vector<string>& debug_dirs,
SecMap* smap, void* rx_avma,
SecMap* smap, void* rx_avma, size_t rx_size,
void (*log)(const char*)) {
typedef typename ElfClass::Ehdr Ehdr;
@ -621,7 +652,7 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
LoadSymbolsInfo<ElfClass> info(debug_dirs);
if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
!debug_dirs.empty(), &info,
smap, rx_avma, log)) {
smap, rx_avma, rx_size, log)) {
const string debuglink_file = info.debuglink_file();
if (debuglink_file.empty())
return false;
@ -660,7 +691,7 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian,
debug_elf_header, false, &info,
smap, rx_avma, log)) {
smap, rx_avma, rx_size, log)) {
return false;
}
}
@ -676,7 +707,7 @@ namespace lul {
bool ReadSymbolDataInternal(const uint8_t* obj_file,
const string& obj_filename,
const vector<string>& debug_dirs,
SecMap* smap, void* rx_avma,
SecMap* smap, void* rx_avma, size_t rx_size,
void (*log)(const char*)) {
if (!IsValidElf(obj_file)) {
@ -688,12 +719,12 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file,
if (elfclass == ELFCLASS32) {
return ReadSymbolDataElfClass<ElfClass32>(
reinterpret_cast<const Elf32_Ehdr*>(obj_file),
obj_filename, debug_dirs, smap, rx_avma, log);
obj_filename, debug_dirs, smap, rx_avma, rx_size, log);
}
if (elfclass == ELFCLASS64) {
return ReadSymbolDataElfClass<ElfClass64>(
reinterpret_cast<const Elf64_Ehdr*>(obj_file),
obj_filename, debug_dirs, smap, rx_avma, log);
obj_filename, debug_dirs, smap, rx_avma, rx_size, log);
}
return false;
@ -701,7 +732,7 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file,
bool ReadSymbolData(const string& obj_file,
const vector<string>& debug_dirs,
SecMap* smap, void* rx_avma,
SecMap* smap, void* rx_avma, size_t rx_size,
void (*log)(const char*)) {
MmapWrapper map_wrapper;
void* elf_header = NULL;
@ -709,7 +740,8 @@ bool ReadSymbolData(const string& obj_file,
return false;
return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header),
obj_file, debug_dirs, smap, rx_avma, log);
obj_file, debug_dirs,
smap, rx_avma, rx_size, log);
}

View File

@ -50,7 +50,8 @@ namespace lul {
// or shared library, and add it to SMAP.
bool ReadSymbolData(const std::string& obj_file,
const std::vector<std::string>& debug_dirs,
SecMap* smap, void* rx_avma,
SecMap* smap,
void* rx_avma, size_t rx_size,
void (*log)(const char*));
// The same as ReadSymbolData, except that OBJ_FILE is assumed to
@ -58,7 +59,8 @@ bool ReadSymbolData(const std::string& obj_file,
bool ReadSymbolDataInternal(const uint8_t* obj_file,
const std::string& obj_filename,
const std::vector<std::string>& debug_dirs,
SecMap* smap, void* rx_avma,
SecMap* smap,
void* rx_avma, size_t rx_size,
void (*log)(const char*));
} // namespace lul

View File

@ -325,9 +325,9 @@ ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry,
(_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
# define GET_EXIDX_U32(_lval, _addr) \
GET_EX_U32(_lval, _addr, mr_exidx_)
GET_EX_U32(_lval, _addr, mr_exidx_avma_)
# define GET_EXTAB_U32(_lval, _addr) \
GET_EX_U32(_lval, _addr, mr_extab_)
GET_EX_U32(_lval, _addr, mr_extab_avma_)
uint32_t data;
GET_EXIDX_U32(data, &entry->data);
@ -575,10 +575,10 @@ int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size)
void ExceptionTableInfo::Start()
{
const struct exidx_entry* start
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_avma_.data());
const struct exidx_entry* end
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
+ mr_exidx_.length());
= reinterpret_cast<const struct exidx_entry*>(mr_exidx_avma_.data()
+ mr_exidx_avma_.length());
// Iterate over each of the EXIDX entries (pairs of 32-bit words).
// These occupy the entire .exidx section.
@ -586,17 +586,11 @@ void ExceptionTableInfo::Start()
// Figure out the code address range that this table entry is
// associated with.
//
// I don't claim to understand the biasing here. It appears that
// (Prel31ToAddr(&entry->addr))
// - mapping_addr_ + loading_addr_) & 0x7fffffff
// produces a SVMA. Adding the text_bias_ gives plausible AVMAs.
uint32_t svma = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
- mapping_addr_ + loading_addr_) & 0x7fffffff;
uint32_t next_svma;
uint32_t avma = reinterpret_cast<uint32_t>(Prel31ToAddr(&entry->addr));
uint32_t next_avma;
if (entry < end - 1) {
next_svma = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
- mapping_addr_ + loading_addr_) & 0x7fffffff;
next_avma
= reinterpret_cast<uint32_t>(Prel31ToAddr(&((entry + 1)->addr)));
} else {
// This is the last EXIDX entry in the sequence, so we don't
// have an address for the start of the next function, to limit
@ -604,28 +598,30 @@ void ExceptionTableInfo::Start()
// text section associated with this .exidx section, that we
// have been given. So as to avoid junking up the CFI unwind
// tables with absurdly large address ranges in the case where
// text_last_svma_ is wrong, only use the value if it is nonzero
// and within one page of |svma|. Otherwise assume a length of 1.
// text_last_avma_ is wrong, only use the value if it is nonzero
// and within one page of |avma|. Otherwise assume a length of 1.
//
// In some cases, gcc has been observed to finish the exidx
// section with an entry of length 1 marked CANT_UNWIND,
// presumably exactly for the purpose of giving a definite
// length for the last real entry, without having to look at
// text segment boundaries.
bool plausible = false;
next_svma = svma + 1;
if (text_last_svma_ != 0) {
uint32_t maybe_next_svma = text_last_svma_ + 1;
if (maybe_next_svma > svma && maybe_next_svma - svma <= 4096) {
next_svma = maybe_next_svma;
plausible = true;
}
bool plausible;
uint32_t maybe_next_avma = text_last_avma_ + 1;
if (maybe_next_avma > avma && maybe_next_avma - avma <= 4096) {
next_avma = maybe_next_avma;
plausible = true;
} else {
next_avma = avma + 1;
plausible = false;
}
if (!plausible) {
if (!plausible && avma != text_last_avma_ + 1) {
char buf[100];
snprintf(buf, sizeof(buf),
"ExceptionTableInfo: implausible EXIDX last entry size %d"
"; using 1 instead.", (int32_t)(text_last_svma_ - svma));
"; using 1 instead.", (int32_t)(text_last_avma_ - avma));
buf[sizeof(buf)-1] = 0;
log_(buf);
}
@ -672,7 +668,7 @@ void ExceptionTableInfo::Start()
// create CFI entries that Breakpad can use. This can also fail.
// First, add a new stack frame entry, into which ExtabEntryDecode
// will write the CFI entries.
handler_->AddStackFrame(svma + text_bias_, next_svma - svma);
handler_->AddStackFrame(avma, next_avma - avma);
int ret = ExtabEntryDecode(buf, buf_used);
if (ret < 0) {
handler_->DeleteStackFrame();

View File

@ -179,20 +179,15 @@ class MemoryRange {
// from .ARM.exidx and .ARM.extab sections.
class ExceptionTableInfo {
public:
ExceptionTableInfo(const char* exidx, size_t exidx_size,
const char* extab, size_t extab_size,
uint32_t text_last_svma,
ExceptionTableInfo(const char* exidx_avma, size_t exidx_size,
const char* extab_avma, size_t extab_size,
uint32_t text_last_avma,
lul::ARMExToModule* handler,
const char* mapping_addr,
uint32_t loading_addr,
uintptr_t text_bias,
void (*log)(const char*))
: mr_exidx_(lul::MemoryRange(exidx, exidx_size)),
mr_extab_(lul::MemoryRange(extab, extab_size)),
text_last_svma_(text_last_svma),
handler_(handler), mapping_addr_(mapping_addr),
loading_addr_(loading_addr),
text_bias_(text_bias),
: mr_exidx_avma_(lul::MemoryRange(exidx_avma, exidx_size)),
mr_extab_avma_(lul::MemoryRange(extab_avma, extab_size)),
text_last_avma_(text_last_avma),
handler_(handler),
log_(log) { }
~ExceptionTableInfo() { }
@ -203,13 +198,12 @@ class ExceptionTableInfo {
void Start();
private:
lul::MemoryRange mr_exidx_;
lul::MemoryRange mr_extab_;
uint32_t text_last_svma_;
// Memory ranges for the exidx and extab sections in the executing image
lul::MemoryRange mr_exidx_avma_;
lul::MemoryRange mr_extab_avma_;
// Address of the last byte of the text segment in the executing image
uint32_t text_last_avma_;
lul::ARMExToModule* handler_;
const char* mapping_addr_;
uint32_t loading_addr_;
uintptr_t text_bias_;
// debugging message sink
void (*log_)(const char*);
enum ExExtractResult {

View File

@ -974,12 +974,12 @@ LUL::NotifyAfterMap(uintptr_t aRXavma, size_t aSize,
if (!aMappedImage) {
(void)lul::ReadSymbolData(
string(aFileName), std::vector<string>(), smap,
(void*)aRXavma, mLog);
(void*)aRXavma, aSize, mLog);
} else {
(void)lul::ReadSymbolDataInternal(
(const uint8_t*)aMappedImage,
string(aFileName), std::vector<string>(), smap,
(void*)aRXavma, mLog);
(void*)aRXavma, aSize, mLog);
}
mLog("NotifyMap .. preparing entries\n");