* Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "Image.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <new>
#include <runtime_loader.h>
#include <syscalls.h>
using namespace BPrivate::Debug;
Image::Image()
{
}
Image::~Image()
{
}
status_t
Image::GetSymbol(const char* name, int32 symbolType, void** _symbolLocation,
size_t* _symbolSize, int32* _symbolType) const
{
int32 iterator = 0;
const char* foundName;
size_t foundNameLen;
addr_t foundAddress;
size_t foundSize;
int32 foundType;
while (NextSymbol(iterator, &foundName, &foundNameLen, &foundAddress,
&foundSize, &foundType) == B_OK) {
if ((symbolType == B_SYMBOL_TYPE_ANY || symbolType == foundType)
&& strcmp(name, foundName) == 0) {
if (_symbolLocation != NULL)
*_symbolLocation = (void*)foundAddress;
if (_symbolSize != NULL)
*_symbolSize = foundSize;
if (_symbolType != NULL)
*_symbolType = foundType;
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
SymbolTableBasedImage::SymbolTableBasedImage()
:
fLoadDelta(0),
fSymbolTable(NULL),
fStringTable(NULL),
fSymbolCount(0),
fStringTableSize(0)
{
}
SymbolTableBasedImage::~SymbolTableBasedImage()
{
}
const elf_sym*
SymbolTableBasedImage::LookupSymbol(addr_t address, addr_t* _baseAddress,
const char** _symbolName, size_t *_symbolNameLen, bool *_exactMatch) const
{
const elf_sym* symbolFound = NULL;
const char* symbolName = NULL;
bool exactMatch = false;
addr_t deltaFound = ~(addr_t)0;
for (int32 i = 0; i < fSymbolCount; i++) {
const elf_sym* symbol = &fSymbolTable[i];
if (symbol->st_value == 0
|| symbol->st_size >= (size_t)fInfo.text_size + fInfo.data_size) {
continue;
}
addr_t symbolAddress = symbol->st_value + fLoadDelta;
if (symbolAddress > address)
continue;
addr_t symbolDelta = address - symbolAddress;
if (symbolDelta >= 0 && symbolDelta < symbol->st_size)
exactMatch = true;
if (exactMatch || symbolDelta < deltaFound) {
deltaFound = symbolDelta;
symbolFound = symbol;
symbolName = fStringTable + symbol->st_name;
if (exactMatch)
break;
}
}
if (symbolFound != NULL) {
if (_baseAddress != NULL)
*_baseAddress = symbolFound->st_value + fLoadDelta;
if (_symbolName != NULL)
*_symbolName = symbolName;
if (_exactMatch != NULL)
*_exactMatch = exactMatch;
if (_symbolNameLen != NULL)
*_symbolNameLen = _SymbolNameLen(symbolName);
}
return symbolFound;
}
status_t
SymbolTableBasedImage::NextSymbol(int32& iterator, const char** _symbolName,
size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
int32* _symbolType) const
{
while (true) {
if (++iterator >= fSymbolCount)
return B_ENTRY_NOT_FOUND;
const elf_sym* symbol = &fSymbolTable[iterator];
if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
|| symbol->st_value == 0) {
continue;
}
*_symbolName = fStringTable + symbol->st_name;
*_symbolNameLen = _SymbolNameLen(*_symbolName);
*_symbolAddress = symbol->st_value + fLoadDelta;
*_symbolSize = symbol->st_size;
*_symbolType = symbol->Type() == STT_FUNC ? B_SYMBOL_TYPE_TEXT
: B_SYMBOL_TYPE_DATA;
return B_OK;
}
}
size_t
SymbolTableBasedImage::_SymbolNameLen(const char* symbolName) const
{
if (symbolName == NULL || (addr_t)symbolName < (addr_t)fStringTable
|| (addr_t)symbolName >= (addr_t)fStringTable + fStringTableSize) {
return 0;
}
return strnlen(symbolName,
(addr_t)fStringTable + fStringTableSize - (addr_t)symbolName);
}
ImageFile::ImageFile()
:
fFD(-1),
fFileSize(0),
fMappedFile((uint8*)MAP_FAILED)
{
}
ImageFile::~ImageFile()
{
if (fMappedFile != MAP_FAILED)
munmap(fMappedFile, fFileSize);
if (fFD >= 0)
close(fFD);
}
status_t
ImageFile::Init(const image_info& info)
{
fInfo = info;
addr_t textAddress;
size_t textSize;
addr_t dataAddress;
size_t dataSize;
status_t error = _LoadFile(info.name, &textAddress, &textSize, &dataAddress,
&dataSize);
if (error != B_OK)
return error;
fLoadDelta = (addr_t)fInfo.text - textAddress;
return B_OK;
}
status_t
ImageFile::Init(const char* path)
{
addr_t textAddress;
size_t textSize;
addr_t dataAddress;
size_t dataSize;
status_t error = _LoadFile(path, &textAddress, &textSize, &dataAddress,
&dataSize);
if (error != B_OK)
return error;
fInfo.id = -1;
fInfo.type = B_LIBRARY_IMAGE;
fInfo.sequence = 0;
fInfo.init_order = 0;
fInfo.init_routine = 0;
fInfo.term_routine = 0;
fInfo.device = -1;
fInfo.node = -1;
strlcpy(fInfo.name, path, sizeof(fInfo.name));
fInfo.text = (void*)textAddress;
fInfo.data = (void*)dataAddress;
fInfo.text_size = textSize;
fInfo.data_size = dataSize;
fLoadDelta = 0;
return B_OK;
}
status_t
ImageFile::_LoadFile(const char* path, addr_t* _textAddress, size_t* _textSize,
addr_t* _dataAddress, size_t* _dataSize)
{
fFD = open(path, O_RDONLY);
if (fFD < 0)
return errno;
struct stat st;
if (fstat(fFD, &st) < 0)
return errno;
fFileSize = st.st_size;
if (fFileSize < (off_t)sizeof(elf_ehdr))
return B_NOT_AN_EXECUTABLE;
fMappedFile = (uint8*)mmap(NULL, fFileSize, PROT_READ, MAP_PRIVATE, fFD, 0);
if (fMappedFile == MAP_FAILED)
return errno;
elf_ehdr* elfHeader = (elf_ehdr*)fMappedFile;
if (memcmp(elfHeader->e_ident, ELFMAG, 4) != 0)
return B_NOT_AN_EXECUTABLE;
if (elfHeader->e_ident[4] != ELF_CLASS)
return B_NOT_AN_EXECUTABLE;
int32 programHeaderCount = elfHeader->e_phnum;
if (elfHeader->e_phoff < sizeof(elf_ehdr)
|| elfHeader->e_phentsize < sizeof(elf_phdr)
|| (off_t)(elfHeader->e_phoff + programHeaderCount
* elfHeader->e_phentsize)
> fFileSize) {
return B_NOT_AN_EXECUTABLE;
}
elf_phdr* programHeaders
= (elf_phdr*)(fMappedFile + elfHeader->e_phoff);
int32 sectionCount = elfHeader->e_shnum;
if (elfHeader->e_shoff < sizeof(elf_ehdr)
|| elfHeader->e_shentsize < sizeof(elf_shdr)
|| (off_t)(elfHeader->e_shoff + sectionCount * elfHeader->e_shentsize)
> fFileSize) {
return B_NOT_AN_EXECUTABLE;
}
*_textAddress = 0;
*_textSize = 0;
*_dataAddress = 0;
*_dataSize = 0;
for (int32 i = 0; i < programHeaderCount; i++) {
elf_phdr* header = (elf_phdr*)
((uint8*)programHeaders + i * elfHeader->e_phentsize);
if (header->p_type == PT_LOAD) {
if ((header->p_flags & PF_WRITE) == 0) {
*_textAddress = header->p_vaddr;
*_textSize = header->p_memsz;
} else {
*_dataAddress = header->p_vaddr;
*_dataSize = header->p_memsz;
break;
}
}
}
status_t error = _FindTableInSection(elfHeader, SHT_SYMTAB);
if (error != B_OK)
error = _FindTableInSection(elfHeader, SHT_DYNSYM);
return error;
}
status_t
ImageFile::_FindTableInSection(elf_ehdr* elfHeader, uint16 sectionType)
{
elf_shdr* sectionHeaders
= (elf_shdr*)(fMappedFile + elfHeader->e_shoff);
for (int32 i = 0; i < elfHeader->e_shnum; i++) {
elf_shdr* sectionHeader = (elf_shdr*)
((uint8*)sectionHeaders + i * elfHeader->e_shentsize);
if (sectionHeader->sh_type == sectionType) {
elf_shdr& stringHeader = *(elf_shdr*)
((uint8*)sectionHeaders
+ sectionHeader->sh_link * elfHeader->e_shentsize);
if (stringHeader.sh_type != SHT_STRTAB)
return B_BAD_DATA;
if ((off_t)(sectionHeader->sh_offset + sectionHeader->sh_size)
> fFileSize
|| (off_t)(stringHeader.sh_offset + stringHeader.sh_size)
> fFileSize) {
return B_BAD_DATA;
}
fSymbolTable = (elf_sym*)(fMappedFile + sectionHeader->sh_offset);
fStringTable = (char*)(fMappedFile + stringHeader.sh_offset);
fSymbolCount = sectionHeader->sh_size / sizeof(elf_sym);
fStringTableSize = stringHeader.sh_size;
return B_OK;
}
}
return B_BAD_DATA;
}
KernelImage::KernelImage()
{
}
KernelImage::~KernelImage()
{
delete[] fSymbolTable;
delete[] fStringTable;
}
status_t
KernelImage::Init(const image_info& info)
{
fInfo = info;
fSymbolCount = 0;
fStringTableSize = 0;
status_t error = _kern_read_kernel_image_symbols(fInfo.id,
NULL, &fSymbolCount, NULL, &fStringTableSize, NULL);
if (error != B_OK)
return error;
fSymbolTable = new(std::nothrow) elf_sym[fSymbolCount];
fStringTable = new(std::nothrow) char[fStringTableSize];
if (fSymbolTable == NULL || fStringTable == NULL)
return B_NO_MEMORY;
return _kern_read_kernel_image_symbols(fInfo.id,
fSymbolTable, &fSymbolCount, fStringTable, &fStringTableSize,
&fLoadDelta);
}
CommPageImage::CommPageImage()
{
}
CommPageImage::~CommPageImage()
{
delete[] fSymbolTable;
delete[] fStringTable;
}
status_t
CommPageImage::Init(const image_info& info)
{
image_id commPageID = -1;
image_info commPageInfo;
int32 cookie = 0;
while (_kern_get_next_image_info(B_SYSTEM_TEAM, &cookie, &commPageInfo,
sizeof(image_info)) == B_OK) {
if (!strcmp("commpage", commPageInfo.name)) {
commPageID = commPageInfo.id;
break;
}
}
if (commPageID < 0)
return B_ENTRY_NOT_FOUND;
fInfo = commPageInfo;
fInfo.text = info.text;
fSymbolCount = 0;
fStringTableSize = 0;
status_t error = _kern_read_kernel_image_symbols(commPageID, NULL,
&fSymbolCount, NULL, &fStringTableSize, NULL);
if (error != B_OK)
return error;
fSymbolTable = new(std::nothrow) elf_sym[fSymbolCount];
fStringTable = new(std::nothrow) char[fStringTableSize];
if (fSymbolTable == NULL || fStringTable == NULL)
return B_NO_MEMORY;
error = _kern_read_kernel_image_symbols(commPageID,
fSymbolTable, &fSymbolCount, fStringTable, &fStringTableSize, NULL);
if (error != B_OK) {
delete[] fSymbolTable;
delete[] fStringTable;
return error;
}
fLoadDelta = (addr_t)info.text;
return B_OK;
}