/* * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2013, Rene Gollent, rene@gollent.com. * Distributed under the terms of the MIT License. */ #include "SymbolLookup.h" #include #include #include #include #include #include #include #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; const void * Area::TranslateAddress(const void *address) { TRACE(("Area::TranslateAddress(%p): area: %" B_PRId32 "\n", address, fLocalID)); // translate the address const void *result = (const void*)((addr_t)address - (addr_t)fRemoteAddress + (addr_t)fLocalAddress); TRACE(("Area::TranslateAddress(%p) done: %p\n", address, result)); return result; } // #pragma mark - RemoteMemoryAccessor::RemoteMemoryAccessor(debug_context* debugContext) : fDebugContext(debugContext), fAreas() { } RemoteMemoryAccessor::~RemoteMemoryAccessor() { // delete the areas while (Area *area = fAreas.Head()) { fAreas.Remove(area); delete area; } } status_t RemoteMemoryAccessor::InitCheck() const { // If we don't have a debug context, then there's nothing we can do. // SymbolLookup's image file functionality will still be available, though. if (fDebugContext == NULL || fDebugContext->nub_port < 0) return B_NO_INIT; return B_OK; } const void * RemoteMemoryAccessor::PrepareAddress(const void *remoteAddress, int32 size) { TRACE(("RemoteMemoryAccessor::PrepareAddress(%p, %" B_PRId32 ")\n", remoteAddress, size)); if (remoteAddress == NULL) { TRACE(("RemoteMemoryAccessor::PrepareAddress(): Got null address!\n")); throw Exception(B_BAD_VALUE); } return _GetArea(remoteAddress, size).TranslateAddress(remoteAddress); } const void * RemoteMemoryAccessor::PrepareAddressNoThrow(const void *remoteAddress, int32 size) { if (remoteAddress == NULL) return NULL; Area* area; status_t status = _GetAreaNoThrow(remoteAddress, size, area); if (status != B_OK) return NULL; return area->TranslateAddress(remoteAddress); } 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; } Area & RemoteMemoryAccessor::_GetArea(const void *address, int32 size) { TRACE(("RemoteMemoryAccessor::_GetArea(%p, %" B_PRId32 ")\n", address, size)); Area* area; status_t status = _GetAreaNoThrow(address, size, area); if (status != B_OK) { TRACE(("RemoteMemoryAccessor::_GetArea(): Failed to get address %p\n", address)); throw Exception(status); } return *area; } status_t RemoteMemoryAccessor::_GetAreaNoThrow(const void *address, int32 size, Area *&_area) { for (AreaList::Iterator it = fAreas.GetIterator(); it.HasNext();) { Area *area = it.Next(); if (area->ContainsAddress(address, size)) { _area = area; return B_OK; } } if (InitCheck() != B_OK) return B_NO_INIT; // we need to clone a new area debug_nub_clone_area message; message.reply_port = fDebugContext->reply_port; message.address = address; debug_nub_clone_area_reply reply; status_t error = send_debug_message(fDebugContext, B_DEBUG_MESSAGE_CLONE_AREA, &message, sizeof(message), &reply, sizeof(reply)); if (error != B_OK) return error; area_id localID = reply.area; if (localID < 0) { TRACE(("RemoteMemoryAccessor: Failed to clone area for %p: %s\n", address, strerror(localID))); return localID; } area_info areaInfo; error = get_area_info(localID, &areaInfo); if (error < 0) { TRACE(("RemoteMemoryAccessor: Failed to get info for %" B_PRId32 ": %s\n", localID, strerror(error))); return error; } const addr_t remoteBaseAddress = (addr_t)address - ((addr_t)reply.address - (addr_t)areaInfo.address); Area *area = new(std::nothrow) Area(localID, remoteBaseAddress, areaInfo.address, areaInfo.size); if (area == NULL) return B_NO_MEMORY; fAreas.Add(area); _area = area; return B_OK; } // #pragma mark - class SymbolLookup::LoadedImage : public Image { public: LoadedImage(SymbolLookup* symbolLookup, const image_info& info, 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 fLoadDelta; }; // #pragma mark - SymbolLookup::SymbolLookup(debug_context* debugContext, image_id image) : RemoteMemoryAccessor(debugContext), fDebugArea(NULL), fImages(), fImageID(image) { } SymbolLookup::~SymbolLookup() { while (Image* image = fImages.RemoveHead()) delete image; } status_t SymbolLookup::Init() { TRACE(("SymbolLookup::Init()\n")); status_t error = 0; if (RemoteMemoryAccessor::InitCheck() == B_OK) { 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(fDebugContext->team, &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(fDebugContext->team, &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; } 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 ? *_exactMatch : -1)); 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; } 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; } 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; } 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); } 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); } const image_t * SymbolLookup::_FindLoadedImageAtAddress(addr_t address) { TRACE(("SymbolLookup::_FindLoadedImageAtAddress(%p)\n", (void*)address)); if (fDebugArea == NULL) return NULL; // iterate through the loaded images const image_t *_image = Read(fDebugArea->loaded_images->head); while (_image != NULL) { const image_t *image = &Read(*_image); _image = image->next; if (image->regions[0].vmstart <= address && address < image->regions[0].vmstart + image->regions[0].size) { return image; } } return NULL; } const image_t* SymbolLookup::_FindLoadedImageByID(image_id id) { TRACE(("SymbolLookup::_FindLoadedImageByID(%" B_PRId32 ")\n", id)); if (fDebugArea == NULL) return NULL; // iterate through the loaded images const image_t *_image = Read(fDebugArea->loaded_images->head); while (_image != NULL) { const image_t *image = &Read(*_image); _image = image->next; if (image->id == id) return image; } return NULL; } Image* SymbolLookup::_FindImageAtAddress(addr_t address) const { DoublyLinkedList::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; } Image* SymbolLookup::_FindImageByID(image_id id) const { DoublyLinkedList::ConstIterator it = fImages.GetIterator(); while (Image* image = it.Next()) { if (image->ID() == id) return image; } return NULL; } 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 (fDebugContext->team == 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, imageInfo, 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_info& info, const image_t* image, int32 symbolCount) : fSymbolLookup(symbolLookup), fImage(image), fSymbolCount(symbolCount), fLoadDelta(image->regions[0].delta) { fInfo = info; } 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; for (int32 i = 0; i < fSymbolCount; 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) > (size_t)fInfo.text_size) { continue; } // skip symbols starting after the given address addr_t symbolAddress = symbol->st_value + fLoadDelta; 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 + fLoadDelta; 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 + fLoadDelta; *_symbolSize = symbol->st_size; *_symbolType = symbol->Type() == STT_FUNC ? B_SYMBOL_TYPE_TEXT : B_SYMBOL_TYPE_DATA; return B_OK; } }