/* * 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 "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::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::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; } }