Files
haiku-beta6/src/kits/debug/SymbolLookup.cpp
T
Murai Takashi 992ae400ec Fix catching by value
Pointed out by LGTM.
Change-Id: I223655c728305f6f45b3738553da5b7d7a69e8c8
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2134
Reviewed-by: Adrien Destugues <[email protected]>
2020-01-21 19:26:22 +00:00

697 lines
16 KiB
C++

/*
* Copyright 2005-2009, Ingo Weinhold, [email protected].
* Copyright 2013, Rene Gollent, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "SymbolLookup.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <new>
#include <runtime_loader.h>
#include <syscalls.h>
#include "Image.h"
#undef TRACE
//#define TRACE_DEBUG_SYMBOL_LOOKUP
#ifdef TRACE_DEBUG_SYMBOL_LOOKUP
# define TRACE(x) printf x
#else
# define TRACE(x) ;
#endif
using namespace BPrivate::Debug;
// PrepareAddress
const void *
Area::PrepareAddress(const void *address)
{
TRACE(("Area::PrepareAddress(%p): area: %" B_PRId32 "\n", address, fRemoteID));
// clone the area, if not done already
if (fLocalID < 0) {
fLocalID = clone_area("cloned area", &fLocalAddress, B_ANY_ADDRESS,
B_READ_AREA, fRemoteID);
if (fLocalID < 0) {
TRACE(("Area::PrepareAddress(): Failed to clone area %" B_PRId32
": %s\n", fRemoteID, strerror(fLocalID)));
throw Exception(fLocalID);
}
}
// translate the address
const void *result = (const void*)((addr_t)address - (addr_t)fRemoteAddress
+ (addr_t)fLocalAddress);
TRACE(("Area::PrepareAddress(%p) done: %p\n", address, result));
return result;
}
// #pragma mark -
// constructor
RemoteMemoryAccessor::RemoteMemoryAccessor(team_id team)
: fTeam(team),
fAreas()
{
}
// destructor
RemoteMemoryAccessor::~RemoteMemoryAccessor()
{
// delete the areas
while (Area *area = fAreas.Head()) {
fAreas.Remove(area);
delete area;
}
}
// Init
status_t
RemoteMemoryAccessor::Init()
{
// If the team is the kernel team, we don't try to clone the areas. Only
// SymbolLookup's image file functionality will be available.
if (fTeam == B_SYSTEM_TEAM)
return B_OK;
// get a list of the team's areas
area_info areaInfo;
ssize_t cookie = 0;
status_t error;
while ((error = get_next_area_info(fTeam, &cookie, &areaInfo)) == B_OK) {
TRACE(("area %" B_PRId32 ": address: %p, size: %ld, name: %s\n",
areaInfo.area, areaInfo.address, areaInfo.size, areaInfo.name));
Area *area = new(std::nothrow) Area(areaInfo.area, areaInfo.address,
areaInfo.size);
if (!area)
return B_NO_MEMORY;
fAreas.Add(area);
}
if (fAreas.IsEmpty())
return error;
return B_OK;
}
// PrepareAddress
const void *
RemoteMemoryAccessor::PrepareAddress(const void *remoteAddress,
int32 size) const
{
TRACE(("RemoteMemoryAccessor::PrepareAddress(%p, %" B_PRId32 ")\n",
remoteAddress, size));
if (!remoteAddress) {
TRACE(("RemoteMemoryAccessor::PrepareAddress(): Got null address!\n"));
throw Exception(B_BAD_VALUE);
}
return _FindArea(remoteAddress, size).PrepareAddress(remoteAddress);
}
const void *
RemoteMemoryAccessor::PrepareAddressNoThrow(const void *remoteAddress,
int32 size) const
{
if (remoteAddress == NULL)
return NULL;
Area* area = _FindAreaNoThrow(remoteAddress, size);
if (area == NULL)
return NULL;
return area->PrepareAddress(remoteAddress);
}
// AreaForLocalAddress
Area*
RemoteMemoryAccessor::AreaForLocalAddress(const void* address) const
{
if (address == NULL)
return NULL;
for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
Area* area = it.Next();
if (area->ContainsLocalAddress(address))
return area;
}
return NULL;
}
// _FindArea
Area &
RemoteMemoryAccessor::_FindArea(const void *address, int32 size) const
{
TRACE(("RemoteMemoryAccessor::_FindArea(%p, %" B_PRId32 ")\n", address,
size));
for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
Area *area = it.Next();
if (area->ContainsAddress(address, size))
return *area;
}
TRACE(("RemoteMemoryAccessor::_FindArea(): No area found for address %p\n",
address));
throw Exception(B_ENTRY_NOT_FOUND);
}
// _FindAreaNoThrow
Area*
RemoteMemoryAccessor::_FindAreaNoThrow(const void *address, int32 size) const
{
for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
Area *area = it.Next();
if (area->ContainsAddress(address, size))
return area;
}
return NULL;
}
// #pragma mark -
class SymbolLookup::LoadedImage : public Image {
public:
LoadedImage(SymbolLookup* symbolLookup,
const image_t* image, int32 symbolCount);
virtual ~LoadedImage();
virtual const elf_sym* LookupSymbol(addr_t address,
addr_t* _baseAddress,
const char** _symbolName,
size_t *_symbolNameLen,
bool *_exactMatch) const;
virtual status_t NextSymbol(int32& iterator,
const char** _symbolName,
size_t* _symbolNameLen,
addr_t* _symbolAddress, size_t* _symbolSize,
int32* _symbolType) const;
private:
SymbolLookup* fSymbolLookup;
const image_t* fImage;
int32 fSymbolCount;
size_t fTextDelta;
};
// #pragma mark -
// constructor
SymbolLookup::SymbolLookup(team_id team, image_id image)
:
RemoteMemoryAccessor(team),
fDebugArea(NULL),
fImages(),
fImageID(image)
{
}
// destructor
SymbolLookup::~SymbolLookup()
{
while (Image* image = fImages.RemoveHead())
delete image;
}
// Init
status_t
SymbolLookup::Init()
{
TRACE(("SymbolLookup::Init()\n"));
status_t error = RemoteMemoryAccessor::Init();
if (error != B_OK)
return error;
if (fTeam != B_SYSTEM_TEAM) {
TRACE(("SymbolLookup::Init(): searching debug area...\n"));
// find the runtime loader debug area
runtime_loader_debug_area *remoteDebugArea = NULL;
ssize_t cookie = 0;
area_info areaInfo;
while (get_next_area_info(fTeam, &cookie, &areaInfo) == B_OK) {
if (strcmp(areaInfo.name, RUNTIME_LOADER_DEBUG_AREA_NAME) == 0) {
remoteDebugArea = (runtime_loader_debug_area*)areaInfo.address;
break;
}
}
if (remoteDebugArea) {
TRACE(("SymbolLookup::Init(): found debug area, translating "
"address...\n"));
} else {
TRACE(("SymbolLookup::Init(): Couldn't find debug area!\n"));
}
// translate the address
try {
if (remoteDebugArea != NULL) {
fDebugArea = &Read(*remoteDebugArea);
TRACE(("SymbolLookup::Init(): translated debug area is at: %p, "
"loaded_images: %p\n", fDebugArea, fDebugArea->loaded_images));
}
} catch (Exception& exception) {
// we can live without the debug area
}
}
image_info imageInfo;
if (fImageID < 0) {
// create a list of the team's images
int32 cookie = 0;
while (get_next_image_info(fTeam, &cookie, &imageInfo) == B_OK) {
error = _LoadImageInfo(imageInfo);
if (error != B_OK)
return error;
}
} else {
error = get_image_info(fImageID, &imageInfo);
if (error != B_OK)
return error;
error = _LoadImageInfo(imageInfo);
if (error != B_OK)
return error;
}
return B_OK;
}
// LookupSymbolAddress
status_t
SymbolLookup::LookupSymbolAddress(addr_t address, addr_t *_baseAddress,
const char **_symbolName, size_t *_symbolNameLen, const char **_imageName,
bool *_exactMatch) const
{
TRACE(("SymbolLookup::LookupSymbolAddress(%p)\n", (void*)address));
Image* image = _FindImageAtAddress(address);
if (!image)
return B_ENTRY_NOT_FOUND;
if (_imageName != NULL)
*_imageName = image->Name();
const elf_sym* symbolFound = image->LookupSymbol(address, _baseAddress,
_symbolName, _symbolNameLen, _exactMatch);
TRACE(("SymbolLookup::LookupSymbolAddress(): done: symbol: %p, image name: "
"%s, exact match: %d\n", symbolFound, image->name, exactMatch));
if (symbolFound != NULL)
return B_OK;
// symbol not found -- return the image itself
if (_baseAddress)
*_baseAddress = image->TextAddress();
if (_imageName)
*_imageName = image->Name();
if (_symbolName)
*_symbolName = NULL;
if (_exactMatch)
*_exactMatch = false;
if (_symbolNameLen != NULL)
*_symbolNameLen = 0;
return B_OK;
}
// InitSymbolIterator
status_t
SymbolLookup::InitSymbolIterator(image_id imageID,
SymbolIterator& iterator) const
{
TRACE(("SymbolLookup::InitSymbolIterator(): image ID: %" B_PRId32 "\n",
imageID));
// find the image
iterator.image = _FindImageByID(imageID);
// If that didn't work, find the loaded image.
if (iterator.image == NULL) {
TRACE(("SymbolLookup::InitSymbolIterator() done: image not "
"found\n"));
return B_ENTRY_NOT_FOUND;
}
iterator.currentIndex = -1;
return B_OK;
}
// InitSymbolIterator
status_t
SymbolLookup::InitSymbolIteratorByAddress(addr_t address,
SymbolIterator& iterator) const
{
TRACE(("SymbolLookup::InitSymbolIteratorByAddress(): base address: %#lx\n",
address));
// find the image
iterator.image = _FindImageAtAddress(address);
if (iterator.image == NULL) {
TRACE(("SymbolLookup::InitSymbolIteratorByAddress() done: image "
"not found\n"));
return B_ENTRY_NOT_FOUND;
}
iterator.currentIndex = -1;
return B_OK;
}
// NextSymbol
status_t
SymbolLookup::NextSymbol(SymbolIterator& iterator, const char** _symbolName,
size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
int32* _symbolType) const
{
return iterator.image->NextSymbol(iterator.currentIndex, _symbolName,
_symbolNameLen, _symbolAddress, _symbolSize, _symbolType);
}
// GetSymbol
status_t
SymbolLookup::GetSymbol(image_id imageID, const char* name, int32 symbolType,
void** _symbolLocation, size_t* _symbolSize, int32* _symbolType) const
{
Image* image = _FindImageByID(imageID);
if (image == NULL)
return B_ENTRY_NOT_FOUND;
return image->GetSymbol(name, symbolType, _symbolLocation, _symbolSize,
_symbolType);
}
// _FindLoadedImageAtAddress
const image_t *
SymbolLookup::_FindLoadedImageAtAddress(addr_t address) const
{
TRACE(("SymbolLookup::_FindLoadedImageAtAddress(%p)\n", (void*)address));
if (fDebugArea == NULL)
return NULL;
// iterate through the loaded images
for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
image;
image = &Read(*image->next)) {
if (image->regions[0].vmstart <= address
&& address < image->regions[0].vmstart + image->regions[0].size) {
return image;
}
}
return NULL;
}
// _FindLoadedImageByID
const image_t*
SymbolLookup::_FindLoadedImageByID(image_id id) const
{
if (fDebugArea == NULL)
return NULL;
// iterate through the images
for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
image;
image = &Read(*image->next)) {
if (image->id == id)
return image;
}
return NULL;
}
// _FindImageAtAddress
Image*
SymbolLookup::_FindImageAtAddress(addr_t address) const
{
DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
while (Image* image = it.Next()) {
addr_t textAddress = image->TextAddress();
if (address >= textAddress && address < textAddress + image->TextSize())
return image;
}
return NULL;
}
// _FindImageByID
Image*
SymbolLookup::_FindImageByID(image_id id) const
{
DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
while (Image* image = it.Next()) {
if (image->ID() == id)
return image;
}
return NULL;
}
// _SymbolNameLen
size_t
SymbolLookup::_SymbolNameLen(const char* address) const
{
Area* area = AreaForLocalAddress(address);
if (area == NULL)
return 0;
return strnlen(address, (addr_t)area->LocalAddress() + area->Size()
- (addr_t)address);
}
status_t
SymbolLookup::_LoadImageInfo(const image_info& imageInfo)
{
status_t error = B_OK;
Image* image;
if (fTeam == B_SYSTEM_TEAM) {
// kernel image
KernelImage* kernelImage = new(std::nothrow) KernelImage;
if (kernelImage == NULL)
return B_NO_MEMORY;
error = kernelImage->Init(imageInfo);
image = kernelImage;
} else if (!strcmp("commpage", imageInfo.name)) {
// commpage image
CommPageImage* commPageImage = new(std::nothrow) CommPageImage;
if (commPageImage == NULL)
return B_NO_MEMORY;
error = commPageImage->Init(imageInfo);
image = commPageImage;
} else {
// userland image -- try to load an image file
ImageFile* imageFile = new(std::nothrow) ImageFile;
if (imageFile == NULL)
return B_NO_MEMORY;
error = imageFile->Init(imageInfo);
image = imageFile;
}
if (error != B_OK) {
// initialization error -- fall back to the loaded image
delete image;
const image_t* loadedImage = _FindLoadedImageByID(imageInfo.id);
if (loadedImage == NULL)
return B_OK;
image = new(std::nothrow) LoadedImage(this, loadedImage,
Read(loadedImage->symhash[1]));
if (image == NULL)
return B_NO_MEMORY;
}
fImages.Add(image);
return B_OK;
}
// #pragma mark - LoadedImage
SymbolLookup::LoadedImage::LoadedImage(SymbolLookup* symbolLookup,
const image_t* image, int32 symbolCount)
:
fSymbolLookup(symbolLookup),
fImage(image),
fSymbolCount(symbolCount),
fTextDelta(image->regions[0].delta)
{
// init info
fInfo.id = fImage->id;
fInfo.type = fImage->type;
fInfo.sequence = 0;
fInfo.init_order = 0;
fInfo.init_routine = (void (*)())fImage->init_routine;
fInfo.term_routine = (void (*)())fImage->term_routine;
fInfo.device = -1;
fInfo.node = -1;
strlcpy(fInfo.name, fImage->path, sizeof(fInfo.name));
fInfo.text = (void*)fImage->regions[0].vmstart;
fInfo.data = (void*)fImage->regions[1].vmstart;
fInfo.text_size = fImage->regions[0].vmsize;
fInfo.data_size = fImage->regions[1].vmsize;
}
SymbolLookup::LoadedImage::~LoadedImage()
{
}
const elf_sym*
SymbolLookup::LoadedImage::LookupSymbol(addr_t address, addr_t* _baseAddress,
const char** _symbolName, size_t *_symbolNameLen, bool *_exactMatch) const
{
TRACE(("LoadedImage::LookupSymbol(): found image: ID: %" B_PRId32 ", text: "
"address: %p, size: %ld\n", fImage->id,
(void*)fImage->regions[0].vmstart, fImage->regions[0].size));
// search the image for the symbol
const elf_sym *symbolFound = NULL;
addr_t deltaFound = INT_MAX;
bool exactMatch = false;
const char *symbolName = NULL;
int32 symbolCount = fSymbolLookup->Read(fImage->symhash[1]);
const elf_region_t *textRegion = fImage->regions; // local
for (int32 i = 0; i < symbolCount; i++) {
const elf_sym *symbol = &fSymbolLookup->Read(fImage->syms[i]);
// The symbol table contains not only symbols referring to functions
// and data symbols within the shared object, but also referenced
// symbols of other shared objects, as well as section and file
// references. We ignore everything but function and data symbols
// that have an st_value != 0 (0 seems to be an indication for a
// symbol defined elsewhere -- couldn't verify that in the specs
// though).
if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
|| symbol->st_value == 0
|| symbol->st_value + symbol->st_size + textRegion->delta
> textRegion->vmstart + textRegion->size) {
continue;
}
// skip symbols starting after the given address
addr_t symbolAddress = symbol->st_value + textRegion->delta;
if (symbolAddress > address)
continue;
addr_t symbolDelta = address - symbolAddress;
if (!symbolFound || symbolDelta < deltaFound) {
symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
SYMNAME(fImage, symbol), 1);
if (symbolName == NULL)
continue;
deltaFound = symbolDelta;
symbolFound = symbol;
if (symbolDelta >= 0 && symbolDelta < symbol->st_size) {
// exact match
exactMatch = true;
break;
}
}
}
TRACE(("LoadedImage::LookupSymbol(): done: symbol: %p, image name: "
"%s, exact match: %d\n", symbolFound, fImage->name, exactMatch));
if (symbolFound != NULL) {
if (_baseAddress)
*_baseAddress = symbolFound->st_value + textRegion->delta;
if (_symbolName)
*_symbolName = symbolName;
if (_exactMatch)
*_exactMatch = exactMatch;
if (_symbolNameLen != NULL)
*_symbolNameLen = fSymbolLookup->_SymbolNameLen(symbolName);
}
return symbolFound;
}
status_t
SymbolLookup::LoadedImage::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
= &fSymbolLookup->Read(fImage->syms[iterator]);
if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
|| symbol->st_value == 0) {
continue;
}
*_symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
SYMNAME(fImage, symbol), 1);
*_symbolNameLen = fSymbolLookup->_SymbolNameLen(*_symbolName);
*_symbolAddress = symbol->st_value + fTextDelta;
*_symbolSize = symbol->st_size;
*_symbolType = symbol->Type() == STT_FUNC ? B_SYMBOL_TYPE_TEXT
: B_SYMBOL_TYPE_DATA;
return B_OK;
}
}