* Copyright 2016, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "CoreFile.h"
#include <errno.h>
#include <algorithm>
#include <OS.h>
#include <AutoDeleter.h>
#include "ElfSymbolLookup.h"
#include "Tracing.h"
static const size_t kMaxNotesSize = 10 * 1024 * 1024;
CoreFileTeamInfo::CoreFileTeamInfo()
:
fId(-1),
fUid(-1),
fGid(-1),
fArgs()
{
}
void
CoreFileTeamInfo::Init(int32 id, int32 uid, int32 gid, const BString& args)
{
fId = id;
fUid = uid;
fGid = gid;
fArgs = args;
}
CoreFileAreaInfo::CoreFileAreaInfo(ElfSegment* segment, int32 id,
uint64 baseAddress, uint64 size, uint64 ramSize, uint32 locking,
uint32 protection, const BString& name)
:
fSegment(segment),
fBaseAddress(baseAddress),
fSize(size),
fRamSize(ramSize),
fLocking(locking),
fProtection(protection),
fId(-1)
{
}
CoreFileImageInfo::CoreFileImageInfo(int32 id, int32 type, uint64 initRoutine,
uint64 termRoutine, uint64 textBase, uint64 textSize, int64 textDelta,
uint64 dataBase, uint64 dataSize, int32 deviceId, int64 nodeId,
uint64 symbolTable, uint64 symbolHash, uint64 stringTable,
CoreFileAreaInfo* textArea, CoreFileAreaInfo* dataArea, const BString& name)
:
fId(id),
fType(type),
fInitRoutine(initRoutine),
fTermRoutine(termRoutine),
fTextBase(textBase),
fTextSize(textSize),
fTextDelta(textDelta),
fDataBase(dataBase),
fDataSize(dataSize),
fDeviceId(deviceId),
fNodeId(nodeId),
fSymbolTable(symbolTable),
fSymbolHash(symbolHash),
fStringTable(stringTable),
fTextArea(textArea),
fDataArea(dataArea),
fName(name),
fSymbolsInfo(NULL)
{
}
CoreFileImageInfo::~CoreFileImageInfo()
{
SetSymbolsInfo(NULL);
}
void
CoreFileImageInfo::SetSymbolsInfo(CoreFileSymbolsInfo* symbolsInfo)
{
if (fSymbolsInfo != NULL)
delete fSymbolsInfo;
fSymbolsInfo = symbolsInfo;
}
CoreFileSymbolsInfo::CoreFileSymbolsInfo()
:
fSymbolTable(NULL),
fStringTable(NULL),
fSymbolCount(0),
fSymbolTableEntrySize(0),
fStringTableSize(0)
{
}
CoreFileSymbolsInfo::~CoreFileSymbolsInfo()
{
free(fSymbolTable);
free(fStringTable);
}
bool
CoreFileSymbolsInfo::Init(const void* symbolTable, uint32 symbolCount,
uint32 symbolTableEntrySize, const char* stringTable,
uint32 stringTableSize)
{
fSymbolTable = malloc(symbolCount * symbolTableEntrySize);
fStringTable = (char*)malloc(stringTableSize);
if (fSymbolTable == NULL || fStringTable == NULL)
return false;
memcpy(fSymbolTable, symbolTable, symbolCount * symbolTableEntrySize);
memcpy(fStringTable, stringTable, stringTableSize);
fSymbolCount = symbolCount;
fSymbolTableEntrySize = symbolTableEntrySize;
fStringTableSize = stringTableSize;
return true;
}
CoreFileThreadInfo::CoreFileThreadInfo(int32 id, int32 state, int32 priority,
uint64 stackBase, uint64 stackEnd, const BString& name)
:
fId(id),
fState(state),
fPriority(priority),
fStackBase(stackBase),
fStackEnd(stackEnd),
fName(name),
fCpuState(NULL),
fCpuStateSize(0)
{
}
CoreFileThreadInfo::~CoreFileThreadInfo()
{
free(fCpuState);
}
bool
CoreFileThreadInfo::SetCpuState(const void* state, size_t size)
{
free(fCpuState);
fCpuState = NULL;
fCpuStateSize = 0;
if (state != NULL) {
fCpuState = malloc(size);
if (fCpuState == NULL)
return false;
memcpy(fCpuState, state, size);
fCpuStateSize = size;
}
return true;
}
CoreFile::CoreFile()
:
fElfFile(),
fTeamInfo(),
fAreaInfos(32),
fImageInfos(32),
fThreadInfos(32)
{
}
CoreFile::~CoreFile()
{
}
status_t
CoreFile::Init(const char* fileName)
{
status_t error = fElfFile.Init(fileName);
if (error != B_OK)
return error;
if (fElfFile.Is64Bit())
return _Init<ElfClass64>();
return _Init<ElfClass32>();
}
const CoreFileThreadInfo*
CoreFile::ThreadInfoForId(int32 id) const
{
int32 count = fThreadInfos.CountItems();
for (int32 i = 0; i < count; i++) {
CoreFileThreadInfo* info = fThreadInfos.ItemAt(i);
if (info->Id() == id)
return info;
}
return NULL;
}
status_t
CoreFile::CreateSymbolLookup(const CoreFileImageInfo* imageInfo,
ElfSymbolLookup*& _lookup)
{
uint64 textDelta = imageInfo->TextDelta();
uint64 symbolTable = imageInfo->SymbolTable();
uint64 symbolHash = imageInfo->SymbolHash();
uint64 stringTable = imageInfo->StringTable();
CoreFileAreaInfo* textArea = imageInfo->TextArea();
ElfSegment* textSegment = textArea != NULL ? textArea->Segment() : NULL;
if (symbolTable == 0 || symbolHash == 0 || stringTable == 0
|| textSegment == NULL) {
return B_UNSUPPORTED;
}
ElfSymbolLookupSource* source = fElfFile.CreateSymbolLookupSource(
textSegment->FileOffset(), textSegment->FileSize(),
textSegment->LoadAddress());
if (source == NULL)
return B_NO_MEMORY;
size_t symbolTableEntrySize = fElfFile.Is64Bit()
? sizeof(ElfClass64::Sym) : sizeof(ElfClass32::Sym);
return ElfSymbolLookup::Create(source, symbolTable, symbolHash, stringTable,
ElfSymbolLookup::kGetSymbolCountFromHash, symbolTableEntrySize,
textDelta, fElfFile.Is64Bit(), fElfFile.IsByteOrderSwapped(), true,
_lookup);
}
template<typename ElfClass>
status_t
CoreFile::_Init()
{
status_t error = _ReadNotes<ElfClass>();
if (error != B_OK)
return error;
printf("CoreFile::_Init(): got %" B_PRId32 " areas, %" B_PRId32 " images, %"
B_PRId32 " threads\n", CountAreaInfos(), CountImageInfos(), CountThreadInfos());
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadNotes()
{
int32 count = fElfFile.CountSegments();
for (int32 i = 0; i < count; i++) {
ElfSegment* segment = fElfFile.SegmentAt(i);
if (segment->Type() == PT_NOTE) {
status_t error = _ReadNotes<ElfClass>(segment);
if (error != B_OK)
return error;
}
}
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadNotes(ElfSegment* segment)
{
if ((uint64)segment->FileSize() > kMaxNotesSize) {
WARNING("Notes segment too large (%" B_PRIdOFF ")\n",
segment->FileSize());
return B_UNSUPPORTED;
}
size_t notesSize = (size_t)segment->FileSize();
uint8* notes = (uint8*)malloc(notesSize);
if (notes == NULL)
return B_NO_MEMORY;
MemoryDeleter notesDeleter(notes);
ssize_t bytesRead = pread(fElfFile.FD(), notes, notesSize,
(off_t)segment->FileOffset());
if (bytesRead < 0) {
WARNING("Failed to read notes segment: %s\n", strerror(errno));
return errno;
}
if ((size_t)bytesRead != notesSize) {
WARNING("Failed to read whole notes segment\n");
return B_IO_ERROR;
}
typedef typename ElfClass::Nhdr Nhdr;
while (notesSize > 0) {
if (notesSize < sizeof(Nhdr)) {
WARNING("Remaining bytes in notes segment too short for header\n");
return B_BAD_DATA;
}
const Nhdr* header = (const Nhdr*)notes;
uint32 nameSize = Get(header->n_namesz);
uint32 dataSize = Get(header->n_descsz);
uint32 type = Get(header->n_type);
notes += sizeof(Nhdr);
notesSize -= sizeof(Nhdr);
size_t alignedNameSize = (nameSize + 3) / 4 * 4;
if (alignedNameSize > notesSize) {
WARNING("Not enough bytes remaining in notes segment for note "
"name (%zu / %zu)\n", notesSize, alignedNameSize);
return B_BAD_DATA;
}
const char* name = (const char*)notes;
size_t nameLen = strnlen(name, nameSize);
if (nameLen == nameSize) {
WARNING("Unterminated note name\n");
return B_BAD_DATA;
}
notes += alignedNameSize;
notesSize -= alignedNameSize;
size_t alignedDataSize = (dataSize + 3) / 4 * 4;
if (alignedDataSize > notesSize) {
WARNING("Not enough bytes remaining in notes segment for note "
"data\n");
return B_BAD_DATA;
}
_ReadNote<ElfClass>(name, type, notes, dataSize);
notes += alignedDataSize;
notesSize -= alignedDataSize;
}
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadNote(const char* name, uint32 type, const void* data,
uint32 dataSize)
{
if (strcmp(name, ELF_NOTE_CORE) == 0) {
switch (type) {
case NT_FILE:
return B_OK;
}
} else if (strcmp(name, ELF_NOTE_HAIKU) == 0) {
switch (type) {
case NT_TEAM:
return _ReadTeamNote<ElfClass>(data, dataSize);
case NT_AREAS:
return _ReadAreasNote<ElfClass>(data, dataSize);
case NT_IMAGES:
return _ReadImagesNote<ElfClass>(data, dataSize);
case NT_SYMBOLS:
return _ReadSymbolsNote<ElfClass>(data, dataSize);
case NT_THREADS:
return _ReadThreadsNote<ElfClass>(data, dataSize);
break;
}
}
WARNING("Unsupported note type %s/%#" B_PRIx32 "\n", name, type);
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadTeamNote(const void* data, uint32 dataSize)
{
typedef typename ElfClass::NoteTeam NoteTeam;
if (dataSize < sizeof(uint32)) {
WARNING("Team note too short\n");
return B_BAD_DATA;
}
uint32 entrySize = Get(*(const uint32*)data);
data = (const uint32*)data + 1;
dataSize -= sizeof(uint32);
if (entrySize == 0 || dataSize == 0 || dataSize - 1 < entrySize) {
WARNING("Team note: too short or invalid entry size (%" B_PRIu32 ")\n",
entrySize);
return B_BAD_DATA;
}
NoteTeam note = {};
_ReadEntry(data, dataSize, note, entrySize);
const char* args = (const char*)data;
size_t argsSize = dataSize;
if (args[argsSize - 1] != '\0') {
WARNING("Team note args not terminated\n");
return B_BAD_DATA;
}
int32 id = Get(note.nt_id);
int32 uid = Get(note.nt_uid);
int32 gid = Get(note.nt_gid);
BString copiedArgs(args);
if (args[0] != '\0' && copiedArgs.Length() == 0)
return B_NO_MEMORY;
fTeamInfo.Init(id, uid, gid, copiedArgs);
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadAreasNote(const void* data, uint32 dataSize)
{
if (dataSize < 2 * sizeof(uint32)) {
WARNING("Areas note too short\n");
return B_BAD_DATA;
}
uint32 areaCount = _ReadValue<uint32>(data, dataSize);
uint32 entrySize = _ReadValue<uint32>(data, dataSize);
typedef typename ElfClass::NoteAreaEntry Entry;
if (areaCount == 0)
return B_OK;
if (entrySize == 0 || dataSize == 0 || areaCount > dataSize
|| dataSize - 1 < entrySize || areaCount * entrySize >= dataSize) {
WARNING("Areas note: too short or invalid entry size (%" B_PRIu32 ")\n",
entrySize);
return B_BAD_DATA;
}
const char* strings = (const char*)data + areaCount * entrySize;
size_t stringsSize = dataSize - areaCount * entrySize;
if (stringsSize == 0 || strings[stringsSize - 1] != '\0') {
WARNING("Areas note strings not terminated\n");
return B_BAD_DATA;
}
for (uint64 i = 0; i < areaCount; i++) {
Entry entry = {};
_ReadEntry(data, dataSize, entry, entrySize);
int32 id = Get(entry.na_id);
uint64 baseAddress = Get(entry.na_base);
uint64 size = Get(entry.na_size);
uint64 ramSize = Get(entry.na_ram_size);
uint32 lock = Get(entry.na_lock);
uint32 protection = Get(entry.na_protection);
if (stringsSize == 0) {
WARNING("Area %" B_PRIu64 " (ID %#" B_PRIx32 " @ %#" B_PRIx64
") has no name\n", i, id, baseAddress);
continue;
}
const char* name = strings;
size_t nameSize = strlen(name) + 1;
strings += nameSize;
stringsSize -= nameSize;
BString copiedName(name);
if (name[0] != '\0' && copiedName.Length() == 0)
return B_NO_MEMORY;
ElfSegment* segment = _FindAreaSegment(baseAddress);
if (segment == NULL) {
WARNING("No matching segment found for area %" B_PRIu64 " (ID %#"
B_PRIx32 " @ %#" B_PRIx64 ", name: '%s')", i, id, baseAddress,
name);
continue;
}
CoreFileAreaInfo* area = new(std::nothrow) CoreFileAreaInfo(segment, id,
baseAddress, size, ramSize, lock, protection, copiedName);
if (area == NULL || !fAreaInfos.AddItem(area)) {
delete area;
return B_NO_MEMORY;
}
}
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadImagesNote(const void* data, uint32 dataSize)
{
if (dataSize < 2 * sizeof(uint32)) {
WARNING("Images note too short\n");
return B_BAD_DATA;
}
uint32 imageCount = _ReadValue<uint32>(data, dataSize);
uint32 entrySize = _ReadValue<uint32>(data, dataSize);
typedef typename ElfClass::NoteImageEntry Entry;
if (imageCount == 0)
return B_OK;
if (entrySize == 0 || dataSize == 0 || imageCount > dataSize
|| dataSize - 1 < entrySize || imageCount * entrySize >= dataSize) {
WARNING("Images note: too short or invalid entry size (%" B_PRIu32
")\n", entrySize);
return B_BAD_DATA;
}
const char* strings = (const char*)data + imageCount * entrySize;
size_t stringsSize = dataSize - imageCount * entrySize;
if (stringsSize == 0 || strings[stringsSize - 1] != '\0') {
WARNING("Images note strings not terminated\n");
return B_BAD_DATA;
}
for (uint64 i = 0; i < imageCount; i++) {
Entry entry = {};
_ReadEntry(data, dataSize, entry, entrySize);
int32 id = Get(entry.ni_id);
int32 type = Get(entry.ni_type);
uint64 initRoutine = Get(entry.ni_init_routine);
uint64 termRoutine = Get(entry.ni_term_routine);
uint64 textBase = Get(entry.ni_text_base);
uint64 textSize = Get(entry.ni_text_size);
int64 textDelta = Get(entry.ni_text_delta);
uint64 dataBase = Get(entry.ni_data_base);
uint64 dataSize = Get(entry.ni_data_size);
int32 deviceId = Get(entry.ni_device);
int64 nodeId = Get(entry.ni_node);
uint64 symbolTable = Get(entry.ni_symbol_table);
uint64 symbolHash = Get(entry.ni_symbol_hash);
uint64 stringTable = Get(entry.ni_string_table);
if (stringsSize == 0) {
WARNING("Image %" B_PRIu64 " (ID %#" B_PRIx32 ") has no name\n",
i, id);
continue;
}
const char* name = strings;
size_t nameSize = strlen(name) + 1;
strings += nameSize;
stringsSize -= nameSize;
BString copiedName(name);
if (name[0] != '\0' && copiedName.Length() == 0)
return B_NO_MEMORY;
CoreFileAreaInfo* textArea = _FindArea(textBase);
CoreFileAreaInfo* dataArea = _FindArea(dataBase);
CoreFileImageInfo* image = new(std::nothrow) CoreFileImageInfo(id, type,
initRoutine, termRoutine, textBase, textSize, textDelta, dataBase,
dataSize, deviceId, nodeId, symbolTable, symbolHash, stringTable,
textArea, dataArea, copiedName);
if (image == NULL || !fImageInfos.AddItem(image)) {
delete image;
return B_NO_MEMORY;
}
}
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadSymbolsNote(const void* data, uint32 dataSize)
{
if (dataSize < 3 * sizeof(uint32)) {
WARNING("Symbols note too short\n");
return B_BAD_DATA;
}
int32 imageId = _ReadValue<int32>(data, dataSize);
uint32 symbolCount = _ReadValue<uint32>(data, dataSize);
uint32 entrySize = _ReadValue<uint32>(data, dataSize);
typedef typename ElfClass::Sym Sym;
if (symbolCount == 0)
return B_OK;
CoreFileImageInfo* imageInfo = _ImageInfoForId(imageId);
if (imageInfo == NULL) {
WARNING("Symbols note: image (ID %" B_PRId32 ") not found\n",
entrySize);
return B_BAD_DATA;
}
if (entrySize < sizeof(Sym) || symbolCount > dataSize
|| dataSize - 1 < entrySize
|| symbolCount * entrySize >= dataSize - 1) {
WARNING("Symbols note: too short or invalid entry size (%" B_PRIu32
")\n", entrySize);
return B_BAD_DATA;
}
uint32 symbolTableSize = symbolCount * entrySize;
uint32 stringTableSize = dataSize - symbolTableSize;
const char* stringTable = (const char*)data + symbolTableSize;
if (stringTableSize == 0 || stringTable[stringTableSize - 1] != '\0') {
WARNING("Symbols note string table not terminated\n");
return B_BAD_DATA;
}
CoreFileSymbolsInfo* symbolsInfo = new(std::nothrow) CoreFileSymbolsInfo;
if (symbolsInfo == NULL
|| !symbolsInfo->Init(data, symbolCount, entrySize, stringTable,
stringTableSize)) {
delete symbolsInfo;
return B_NO_MEMORY;
}
imageInfo->SetSymbolsInfo(symbolsInfo);
return B_OK;
}
template<typename ElfClass>
status_t
CoreFile::_ReadThreadsNote(const void* data, uint32 dataSize)
{
if (dataSize < 3 * sizeof(uint32)) {
WARNING("Threads note too short\n");
return B_BAD_DATA;
}
uint32 threadCount = _ReadValue<uint32>(data, dataSize);
uint32 entrySize = _ReadValue<uint32>(data, dataSize);
uint32 cpuStateSize = _ReadValue<uint32>(data, dataSize);
if (cpuStateSize > 1024 * 1024) {
WARNING("Threads note: unreasonable CPU state size: %" B_PRIu32 "\n",
cpuStateSize);
return B_BAD_DATA;
}
typedef typename ElfClass::NoteThreadEntry Entry;
if (threadCount == 0)
return B_OK;
size_t totalEntrySize = entrySize + cpuStateSize;
if (entrySize == 0 || dataSize == 0 || threadCount > dataSize
|| entrySize > dataSize || cpuStateSize > dataSize
|| dataSize - 1 < totalEntrySize
|| threadCount * totalEntrySize >= dataSize) {
WARNING("Threads note: too short or invalid entry size (%" B_PRIu32
")\n", entrySize);
return B_BAD_DATA;
}
const char* strings = (const char*)data + threadCount * totalEntrySize;
size_t stringsSize = dataSize - threadCount * totalEntrySize;
if (stringsSize == 0 || strings[stringsSize - 1] != '\0') {
WARNING("Threads note strings not terminated\n");
return B_BAD_DATA;
}
for (uint64 i = 0; i < threadCount; i++) {
Entry entry = {};
_ReadEntry(data, dataSize, entry, entrySize);
int32 id = Get(entry.nth_id);
int32 state = Get(entry.nth_state);
int32 priority = Get(entry.nth_priority);
uint64 stackBase = Get(entry.nth_stack_base);
uint64 stackEnd = Get(entry.nth_stack_end);
if (stringsSize == 0) {
WARNING("Thread %" B_PRIu64 " (ID %#" B_PRIx32 ") has no name\n",
i, id);
continue;
}
const char* name = strings;
size_t nameSize = strlen(name) + 1;
strings += nameSize;
stringsSize -= nameSize;
BString copiedName(name);
if (name[0] != '\0' && copiedName.Length() == 0)
return B_NO_MEMORY;
CoreFileThreadInfo* thread = new(std::nothrow) CoreFileThreadInfo(id,
state, priority, stackBase, stackEnd, copiedName);
if (thread == NULL || !fThreadInfos.AddItem(thread)) {
delete thread;
return B_NO_MEMORY;
}
if (!thread->SetCpuState(data, cpuStateSize))
return B_NO_MEMORY;
_Advance(data, dataSize, cpuStateSize);
}
return B_OK;
}
CoreFileAreaInfo*
CoreFile::_FindArea(uint64 address) const
{
int32 count = fAreaInfos.CountItems();
for (int32 i = 0; i < count; i++) {
CoreFileAreaInfo* area = fAreaInfos.ItemAt(i);
if (address >= area->BaseAddress()
&& address < area->EndAddress()) {
return area;
}
}
return NULL;
}
ElfSegment*
CoreFile::_FindAreaSegment(uint64 address) const
{
int32 count = fElfFile.CountSegments();
for (int32 i = 0; i < count; i++) {
ElfSegment* segment = fElfFile.SegmentAt(i);
if (segment->Type() == PT_LOAD && segment->LoadAddress() == address)
return segment;
}
return NULL;
}
CoreFileImageInfo*
CoreFile::_ImageInfoForId(int32 id) const
{
int32 count = fImageInfos.CountItems();
for (int32 i = 0; i < count; i++) {
CoreFileImageInfo* info = fImageInfos.ItemAt(i);
if (info->Id() == id)
return info;
}
return NULL;
}
template<typename Type>
Type
CoreFile::_ReadValue(const void*& data, uint32& dataSize)
{
Type value = Get(*(const Type*)data);
_Advance(data, dataSize, sizeof(Type));
return value;
}
template<typename Entry>
void
CoreFile::_ReadEntry(const void*& data, uint32& dataSize, Entry& entry,
size_t entrySize)
{
memcpy(&entry, data, std::min(sizeof(entry), entrySize));
_Advance(data, dataSize, entrySize);
}
void
CoreFile::_Advance(const void*& data, uint32& dataSize, size_t by)
{
data = (const uint8*)data + by;
dataSize -= by;
}