⛏️ index : haiku.git

/*
 * 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;


// pragma mark - CoreFileTeamInfo


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;
}


// pragma mark - CoreFileAreaInfo


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)
{
}


// pragma mark - CoreFileImageInfo


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;
}


// pragma mark - CoreFileSymbolsInfo

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;
}


// pragma mark - CoreFileThreadInfo


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;
}


// pragma mark - CoreFile


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)
{
	// get the needed data
	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;
	}

	// create a data source for the text segment
	ElfSymbolLookupSource* source = fElfFile.CreateSymbolLookupSource(
		textSegment->FileOffset(), textSegment->FileSize(),
		textSegment->LoadAddress());
	if (source == NULL)
		return B_NO_MEMORY;

	// get the symbol table entry size
	// TODO: This is not actually correct, since at least theoretically the
	// entry size may differ (cf. DT_SYMENT in the dynamic segment).
	size_t symbolTableEntrySize = fElfFile.Is64Bit()
		? sizeof(ElfClass64::Sym) : sizeof(ElfClass32::Sym);

	// create the symbol lookup
	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());
	// TODO: Verify that we actually read something!
	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)
{
	// read the whole segment into memory
	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;
	}

	// iterate through notes
	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:
				// not needed
				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);

	// check, if args are null-terminated
	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;

	// check entry size and area count
	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;
	}

	// check, if strings are null-terminated
	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++) {
		// get entry values
		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);

		// get name
		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;

		// create and add area
		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;

	// check entry size and image count
	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;
	}

	// check, if strings are null-terminated
	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++) {
		// get entry values
		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);

		// get name
		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;

		// create and add image
		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;

	// get the corresponding image
	CoreFileImageInfo* imageInfo = _ImageInfoForId(imageId);
	if (imageInfo == NULL) {
		WARNING("Symbols note: image (ID %" B_PRId32 ") not found\n",
			entrySize);
		return B_BAD_DATA;
	}

	// check entry size and symbol count
	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;

	// check, if the string table is null-terminated
	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;

	// check entry size and thread count
	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;
	}

	// check, if strings are null-terminated
	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++) {
		// get entry values
		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);

		// get name
		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;

		// create and add thread
		CoreFileThreadInfo* thread = new(std::nothrow) CoreFileThreadInfo(id,
			state, priority, stackBase, stackEnd, copiedName);
		if (thread == NULL || !fThreadInfos.AddItem(thread)) {
			delete thread;
			return B_NO_MEMORY;
		}

		// get CPU state
		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;
}