1 /*
2 * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2013, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7 #include "SymbolLookup.h"
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 #include <new>
14
15 #include <runtime_loader.h>
16 #include <syscalls.h>
17
18 #include "Image.h"
19
20
21 #undef TRACE
22 //#define TRACE_DEBUG_SYMBOL_LOOKUP
23 #ifdef TRACE_DEBUG_SYMBOL_LOOKUP
24 # define TRACE(x) printf x
25 #else
26 # define TRACE(x) ;
27 #endif
28
29
30 using namespace BPrivate::Debug;
31
32
33 // PrepareAddress
34 const void *
PrepareAddress(const void * address)35 Area::PrepareAddress(const void *address)
36 {
37 TRACE(("Area::PrepareAddress(%p): area: %" B_PRId32 "\n", address, fRemoteID));
38
39 // clone the area, if not done already
40 if (fLocalID < 0) {
41 fLocalID = clone_area("cloned area", &fLocalAddress, B_ANY_ADDRESS,
42 B_READ_AREA, fRemoteID);
43 if (fLocalID < 0) {
44 TRACE(("Area::PrepareAddress(): Failed to clone area %" B_PRId32
45 ": %s\n", fRemoteID, strerror(fLocalID)));
46 throw Exception(fLocalID);
47 }
48 }
49
50 // translate the address
51 const void *result = (const void*)((addr_t)address - (addr_t)fRemoteAddress
52 + (addr_t)fLocalAddress);
53
54 TRACE(("Area::PrepareAddress(%p) done: %p\n", address, result));
55
56 return result;
57 }
58
59
60 // #pragma mark -
61
62 // constructor
RemoteMemoryAccessor(team_id team)63 RemoteMemoryAccessor::RemoteMemoryAccessor(team_id team)
64 : fTeam(team),
65 fAreas()
66 {
67 }
68
69 // destructor
~RemoteMemoryAccessor()70 RemoteMemoryAccessor::~RemoteMemoryAccessor()
71 {
72 // delete the areas
73 while (Area *area = fAreas.Head()) {
74 fAreas.Remove(area);
75 delete area;
76 }
77 }
78
79 // Init
80 status_t
Init()81 RemoteMemoryAccessor::Init()
82 {
83 // If the team is the kernel team, we don't try to clone the areas. Only
84 // SymbolLookup's image file functionality will be available.
85 if (fTeam == B_SYSTEM_TEAM)
86 return B_OK;
87
88 // get a list of the team's areas
89 area_info areaInfo;
90 ssize_t cookie = 0;
91 status_t error;
92 while ((error = get_next_area_info(fTeam, &cookie, &areaInfo)) == B_OK) {
93 TRACE(("area %" B_PRId32 ": address: %p, size: %ld, name: %s\n",
94 areaInfo.area, areaInfo.address, areaInfo.size, areaInfo.name));
95
96 Area *area = new(std::nothrow) Area(areaInfo.area, areaInfo.address,
97 areaInfo.size);
98 if (!area)
99 return B_NO_MEMORY;
100
101 fAreas.Add(area);
102 }
103
104 if (fAreas.IsEmpty())
105 return error;
106
107 return B_OK;
108 }
109
110 // PrepareAddress
111 const void *
PrepareAddress(const void * remoteAddress,int32 size) const112 RemoteMemoryAccessor::PrepareAddress(const void *remoteAddress,
113 int32 size) const
114 {
115 TRACE(("RemoteMemoryAccessor::PrepareAddress(%p, %" B_PRId32 ")\n",
116 remoteAddress, size));
117
118 if (!remoteAddress) {
119 TRACE(("RemoteMemoryAccessor::PrepareAddress(): Got null address!\n"));
120 throw Exception(B_BAD_VALUE);
121 }
122
123 return _FindArea(remoteAddress, size).PrepareAddress(remoteAddress);
124 }
125
126
127 const void *
PrepareAddressNoThrow(const void * remoteAddress,int32 size) const128 RemoteMemoryAccessor::PrepareAddressNoThrow(const void *remoteAddress,
129 int32 size) const
130 {
131 if (remoteAddress == NULL)
132 return NULL;
133
134 Area* area = _FindAreaNoThrow(remoteAddress, size);
135 if (area == NULL)
136 return NULL;
137
138 return area->PrepareAddress(remoteAddress);
139 }
140
141
142 // AreaForLocalAddress
143 Area*
AreaForLocalAddress(const void * address) const144 RemoteMemoryAccessor::AreaForLocalAddress(const void* address) const
145 {
146 if (address == NULL)
147 return NULL;
148
149 for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
150 Area* area = it.Next();
151 if (area->ContainsLocalAddress(address))
152 return area;
153 }
154
155 return NULL;
156 }
157
158
159 // _FindArea
160 Area &
_FindArea(const void * address,int32 size) const161 RemoteMemoryAccessor::_FindArea(const void *address, int32 size) const
162 {
163 TRACE(("RemoteMemoryAccessor::_FindArea(%p, %" B_PRId32 ")\n", address,
164 size));
165
166 for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
167 Area *area = it.Next();
168 if (area->ContainsAddress(address, size))
169 return *area;
170 }
171
172 TRACE(("RemoteMemoryAccessor::_FindArea(): No area found for address %p\n",
173 address));
174 throw Exception(B_ENTRY_NOT_FOUND);
175 }
176
177
178 // _FindAreaNoThrow
179 Area*
_FindAreaNoThrow(const void * address,int32 size) const180 RemoteMemoryAccessor::_FindAreaNoThrow(const void *address, int32 size) const
181 {
182 for (AreaList::ConstIterator it = fAreas.GetIterator(); it.HasNext();) {
183 Area *area = it.Next();
184 if (area->ContainsAddress(address, size))
185 return area;
186 }
187
188 return NULL;
189 }
190
191
192 // #pragma mark -
193
194
195 class SymbolLookup::LoadedImage : public Image {
196 public:
197 LoadedImage(SymbolLookup* symbolLookup,
198 const image_t* image, int32 symbolCount);
199 virtual ~LoadedImage();
200
201 virtual const elf_sym* LookupSymbol(addr_t address,
202 addr_t* _baseAddress,
203 const char** _symbolName,
204 size_t *_symbolNameLen,
205 bool *_exactMatch) const;
206 virtual status_t NextSymbol(int32& iterator,
207 const char** _symbolName,
208 size_t* _symbolNameLen,
209 addr_t* _symbolAddress, size_t* _symbolSize,
210 int32* _symbolType) const;
211
212 private:
213 SymbolLookup* fSymbolLookup;
214 const image_t* fImage;
215 int32 fSymbolCount;
216 size_t fTextDelta;
217 };
218
219
220 // #pragma mark -
221
222
223 // constructor
SymbolLookup(team_id team,image_id image)224 SymbolLookup::SymbolLookup(team_id team, image_id image)
225 :
226 RemoteMemoryAccessor(team),
227 fDebugArea(NULL),
228 fImages(),
229 fImageID(image)
230 {
231 }
232
233
234 // destructor
~SymbolLookup()235 SymbolLookup::~SymbolLookup()
236 {
237 while (Image* image = fImages.RemoveHead())
238 delete image;
239 }
240
241
242 // Init
243 status_t
Init()244 SymbolLookup::Init()
245 {
246 TRACE(("SymbolLookup::Init()\n"));
247
248 status_t error = RemoteMemoryAccessor::Init();
249 if (error != B_OK)
250 return error;
251
252 if (fTeam != B_SYSTEM_TEAM) {
253 TRACE(("SymbolLookup::Init(): searching debug area...\n"));
254
255 // find the runtime loader debug area
256 runtime_loader_debug_area *remoteDebugArea = NULL;
257 ssize_t cookie = 0;
258 area_info areaInfo;
259 while (get_next_area_info(fTeam, &cookie, &areaInfo) == B_OK) {
260 if (strcmp(areaInfo.name, RUNTIME_LOADER_DEBUG_AREA_NAME) == 0) {
261 remoteDebugArea = (runtime_loader_debug_area*)areaInfo.address;
262 break;
263 }
264 }
265
266 if (remoteDebugArea) {
267 TRACE(("SymbolLookup::Init(): found debug area, translating "
268 "address...\n"));
269 } else {
270 TRACE(("SymbolLookup::Init(): Couldn't find debug area!\n"));
271 }
272
273 // translate the address
274 try {
275 if (remoteDebugArea != NULL) {
276 fDebugArea = &Read(*remoteDebugArea);
277
278 TRACE(("SymbolLookup::Init(): translated debug area is at: %p, "
279 "loaded_images: %p\n", fDebugArea, fDebugArea->loaded_images));
280 }
281 } catch (Exception& exception) {
282 // we can live without the debug area
283 }
284 }
285
286 image_info imageInfo;
287 if (fImageID < 0) {
288 // create a list of the team's images
289 int32 cookie = 0;
290 while (get_next_image_info(fTeam, &cookie, &imageInfo) == B_OK) {
291 error = _LoadImageInfo(imageInfo);
292 if (error != B_OK)
293 return error;
294 }
295 } else {
296 error = get_image_info(fImageID, &imageInfo);
297 if (error != B_OK)
298 return error;
299
300 error = _LoadImageInfo(imageInfo);
301 if (error != B_OK)
302 return error;
303 }
304
305 return B_OK;
306 }
307
308
309 // LookupSymbolAddress
310 status_t
LookupSymbolAddress(addr_t address,addr_t * _baseAddress,const char ** _symbolName,size_t * _symbolNameLen,const char ** _imageName,bool * _exactMatch) const311 SymbolLookup::LookupSymbolAddress(addr_t address, addr_t *_baseAddress,
312 const char **_symbolName, size_t *_symbolNameLen, const char **_imageName,
313 bool *_exactMatch) const
314 {
315 TRACE(("SymbolLookup::LookupSymbolAddress(%p)\n", (void*)address));
316
317 Image* image = _FindImageAtAddress(address);
318 if (!image)
319 return B_ENTRY_NOT_FOUND;
320
321 if (_imageName != NULL)
322 *_imageName = image->Name();
323
324 const elf_sym* symbolFound = image->LookupSymbol(address, _baseAddress,
325 _symbolName, _symbolNameLen, _exactMatch);
326
327 TRACE(("SymbolLookup::LookupSymbolAddress(): done: symbol: %p, image name: "
328 "%s, exact match: %d\n", symbolFound, image->name, exactMatch));
329
330 if (symbolFound != NULL)
331 return B_OK;
332
333 // symbol not found -- return the image itself
334
335 if (_baseAddress)
336 *_baseAddress = image->TextAddress();
337
338 if (_imageName)
339 *_imageName = image->Name();
340
341 if (_symbolName)
342 *_symbolName = NULL;
343
344 if (_exactMatch)
345 *_exactMatch = false;
346
347 if (_symbolNameLen != NULL)
348 *_symbolNameLen = 0;
349
350 return B_OK;
351 }
352
353
354 // InitSymbolIterator
355 status_t
InitSymbolIterator(image_id imageID,SymbolIterator & iterator) const356 SymbolLookup::InitSymbolIterator(image_id imageID,
357 SymbolIterator& iterator) const
358 {
359 TRACE(("SymbolLookup::InitSymbolIterator(): image ID: %" B_PRId32 "\n",
360 imageID));
361
362 // find the image
363 iterator.image = _FindImageByID(imageID);
364
365 // If that didn't work, find the loaded image.
366 if (iterator.image == NULL) {
367 TRACE(("SymbolLookup::InitSymbolIterator() done: image not "
368 "found\n"));
369 return B_ENTRY_NOT_FOUND;
370 }
371
372 iterator.currentIndex = -1;
373
374 return B_OK;
375 }
376
377
378 // InitSymbolIterator
379 status_t
InitSymbolIteratorByAddress(addr_t address,SymbolIterator & iterator) const380 SymbolLookup::InitSymbolIteratorByAddress(addr_t address,
381 SymbolIterator& iterator) const
382 {
383 TRACE(("SymbolLookup::InitSymbolIteratorByAddress(): base address: %#lx\n",
384 address));
385
386 // find the image
387 iterator.image = _FindImageAtAddress(address);
388 if (iterator.image == NULL) {
389 TRACE(("SymbolLookup::InitSymbolIteratorByAddress() done: image "
390 "not found\n"));
391 return B_ENTRY_NOT_FOUND;
392 }
393
394 iterator.currentIndex = -1;
395
396 return B_OK;
397 }
398
399
400 // NextSymbol
401 status_t
NextSymbol(SymbolIterator & iterator,const char ** _symbolName,size_t * _symbolNameLen,addr_t * _symbolAddress,size_t * _symbolSize,int32 * _symbolType) const402 SymbolLookup::NextSymbol(SymbolIterator& iterator, const char** _symbolName,
403 size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
404 int32* _symbolType) const
405 {
406 return iterator.image->NextSymbol(iterator.currentIndex, _symbolName,
407 _symbolNameLen, _symbolAddress, _symbolSize, _symbolType);
408 }
409
410
411 // GetSymbol
412 status_t
GetSymbol(image_id imageID,const char * name,int32 symbolType,void ** _symbolLocation,size_t * _symbolSize,int32 * _symbolType) const413 SymbolLookup::GetSymbol(image_id imageID, const char* name, int32 symbolType,
414 void** _symbolLocation, size_t* _symbolSize, int32* _symbolType) const
415 {
416 Image* image = _FindImageByID(imageID);
417 if (image == NULL)
418 return B_ENTRY_NOT_FOUND;
419
420 return image->GetSymbol(name, symbolType, _symbolLocation, _symbolSize,
421 _symbolType);
422 }
423
424
425 // _FindLoadedImageAtAddress
426 const image_t *
_FindLoadedImageAtAddress(addr_t address) const427 SymbolLookup::_FindLoadedImageAtAddress(addr_t address) const
428 {
429 TRACE(("SymbolLookup::_FindLoadedImageAtAddress(%p)\n", (void*)address));
430
431 if (fDebugArea == NULL)
432 return NULL;
433
434 // iterate through the loaded images
435 for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
436 image;
437 image = &Read(*image->next)) {
438 if (image->regions[0].vmstart <= address
439 && address < image->regions[0].vmstart + image->regions[0].size) {
440 return image;
441 }
442 }
443
444 return NULL;
445 }
446
447
448 // _FindLoadedImageByID
449 const image_t*
_FindLoadedImageByID(image_id id) const450 SymbolLookup::_FindLoadedImageByID(image_id id) const
451 {
452 if (fDebugArea == NULL)
453 return NULL;
454
455 // iterate through the images
456 for (const image_t *image = &Read(*Read(fDebugArea->loaded_images->head));
457 image;
458 image = &Read(*image->next)) {
459 if (image->id == id)
460 return image;
461 }
462
463 return NULL;
464 }
465
466
467 // _FindImageAtAddress
468 Image*
_FindImageAtAddress(addr_t address) const469 SymbolLookup::_FindImageAtAddress(addr_t address) const
470 {
471 DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
472 while (Image* image = it.Next()) {
473 addr_t textAddress = image->TextAddress();
474 if (address >= textAddress && address < textAddress + image->TextSize())
475 return image;
476 }
477
478 return NULL;
479 }
480
481
482 // _FindImageByID
483 Image*
_FindImageByID(image_id id) const484 SymbolLookup::_FindImageByID(image_id id) const
485 {
486 DoublyLinkedList<Image>::ConstIterator it = fImages.GetIterator();
487 while (Image* image = it.Next()) {
488 if (image->ID() == id)
489 return image;
490 }
491
492 return NULL;
493 }
494
495
496 // _SymbolNameLen
497 size_t
_SymbolNameLen(const char * address) const498 SymbolLookup::_SymbolNameLen(const char* address) const
499 {
500 Area* area = AreaForLocalAddress(address);
501 if (area == NULL)
502 return 0;
503
504 return strnlen(address, (addr_t)area->LocalAddress() + area->Size()
505 - (addr_t)address);
506 }
507
508
509 status_t
_LoadImageInfo(const image_info & imageInfo)510 SymbolLookup::_LoadImageInfo(const image_info& imageInfo)
511 {
512 status_t error = B_OK;
513
514 Image* image;
515 if (fTeam == B_SYSTEM_TEAM) {
516 // kernel image
517 KernelImage* kernelImage = new(std::nothrow) KernelImage;
518 if (kernelImage == NULL)
519 return B_NO_MEMORY;
520
521 error = kernelImage->Init(imageInfo);
522 image = kernelImage;
523 } else if (!strcmp("commpage", imageInfo.name)) {
524 // commpage image
525 CommPageImage* commPageImage = new(std::nothrow) CommPageImage;
526 if (commPageImage == NULL)
527 return B_NO_MEMORY;
528
529 error = commPageImage->Init(imageInfo);
530 image = commPageImage;
531 } else {
532 // userland image -- try to load an image file
533 ImageFile* imageFile = new(std::nothrow) ImageFile;
534 if (imageFile == NULL)
535 return B_NO_MEMORY;
536
537 error = imageFile->Init(imageInfo);
538 image = imageFile;
539 }
540
541 if (error != B_OK) {
542 // initialization error -- fall back to the loaded image
543 delete image;
544
545 const image_t* loadedImage = _FindLoadedImageByID(imageInfo.id);
546 if (loadedImage == NULL)
547 return B_OK;
548
549 image = new(std::nothrow) LoadedImage(this, loadedImage,
550 Read(loadedImage->symhash[1]));
551 if (image == NULL)
552 return B_NO_MEMORY;
553
554 }
555
556 fImages.Add(image);
557
558 return B_OK;
559 }
560
561 // #pragma mark - LoadedImage
562
563
LoadedImage(SymbolLookup * symbolLookup,const image_t * image,int32 symbolCount)564 SymbolLookup::LoadedImage::LoadedImage(SymbolLookup* symbolLookup,
565 const image_t* image, int32 symbolCount)
566 :
567 fSymbolLookup(symbolLookup),
568 fImage(image),
569 fSymbolCount(symbolCount),
570 fTextDelta(image->regions[0].delta)
571 {
572 // init info
573 fInfo.id = fImage->id;
574 fInfo.type = fImage->type;
575 fInfo.sequence = 0;
576 fInfo.init_order = 0;
577 fInfo.init_routine = (void (*)())fImage->init_routine;
578 fInfo.term_routine = (void (*)())fImage->term_routine;
579 fInfo.device = -1;
580 fInfo.node = -1;
581 strlcpy(fInfo.name, fImage->path, sizeof(fInfo.name));
582 fInfo.text = (void*)fImage->regions[0].vmstart;
583 fInfo.data = (void*)fImage->regions[1].vmstart;
584 fInfo.text_size = fImage->regions[0].vmsize;
585 fInfo.data_size = fImage->regions[1].vmsize;
586 }
587
588
~LoadedImage()589 SymbolLookup::LoadedImage::~LoadedImage()
590 {
591 }
592
593
594 const elf_sym*
LookupSymbol(addr_t address,addr_t * _baseAddress,const char ** _symbolName,size_t * _symbolNameLen,bool * _exactMatch) const595 SymbolLookup::LoadedImage::LookupSymbol(addr_t address, addr_t* _baseAddress,
596 const char** _symbolName, size_t *_symbolNameLen, bool *_exactMatch) const
597 {
598 TRACE(("LoadedImage::LookupSymbol(): found image: ID: %" B_PRId32 ", text: "
599 "address: %p, size: %ld\n", fImage->id,
600 (void*)fImage->regions[0].vmstart, fImage->regions[0].size));
601
602 // search the image for the symbol
603 const elf_sym *symbolFound = NULL;
604 addr_t deltaFound = INT_MAX;
605 bool exactMatch = false;
606 const char *symbolName = NULL;
607
608 int32 symbolCount = fSymbolLookup->Read(fImage->symhash[1]);
609 const elf_region_t *textRegion = fImage->regions; // local
610
611 for (int32 i = 0; i < symbolCount; i++) {
612 const elf_sym *symbol = &fSymbolLookup->Read(fImage->syms[i]);
613
614 // The symbol table contains not only symbols referring to functions
615 // and data symbols within the shared object, but also referenced
616 // symbols of other shared objects, as well as section and file
617 // references. We ignore everything but function and data symbols
618 // that have an st_value != 0 (0 seems to be an indication for a
619 // symbol defined elsewhere -- couldn't verify that in the specs
620 // though).
621 if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
622 || symbol->st_value == 0
623 || symbol->st_value + symbol->st_size + textRegion->delta
624 > textRegion->vmstart + textRegion->size) {
625 continue;
626 }
627
628 // skip symbols starting after the given address
629 addr_t symbolAddress = symbol->st_value + textRegion->delta;
630
631 if (symbolAddress > address)
632 continue;
633 addr_t symbolDelta = address - symbolAddress;
634
635 if (!symbolFound || symbolDelta < deltaFound) {
636 symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
637 SYMNAME(fImage, symbol), 1);
638 if (symbolName == NULL)
639 continue;
640
641 deltaFound = symbolDelta;
642 symbolFound = symbol;
643
644 if (symbolDelta >= 0 && symbolDelta < symbol->st_size) {
645 // exact match
646 exactMatch = true;
647 break;
648 }
649 }
650 }
651
652 TRACE(("LoadedImage::LookupSymbol(): done: symbol: %p, image name: "
653 "%s, exact match: %d\n", symbolFound, fImage->name, exactMatch));
654
655 if (symbolFound != NULL) {
656 if (_baseAddress)
657 *_baseAddress = symbolFound->st_value + textRegion->delta;
658 if (_symbolName)
659 *_symbolName = symbolName;
660 if (_exactMatch)
661 *_exactMatch = exactMatch;
662 if (_symbolNameLen != NULL)
663 *_symbolNameLen = fSymbolLookup->_SymbolNameLen(symbolName);
664 }
665
666 return symbolFound;
667 }
668
669
670 status_t
NextSymbol(int32 & iterator,const char ** _symbolName,size_t * _symbolNameLen,addr_t * _symbolAddress,size_t * _symbolSize,int32 * _symbolType) const671 SymbolLookup::LoadedImage::NextSymbol(int32& iterator, const char** _symbolName,
672 size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize,
673 int32* _symbolType) const
674 {
675 while (true) {
676 if (++iterator >= fSymbolCount)
677 return B_ENTRY_NOT_FOUND;
678
679 const elf_sym* symbol
680 = &fSymbolLookup->Read(fImage->syms[iterator]);
681 if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT)
682 || symbol->st_value == 0) {
683 continue;
684 }
685
686 *_symbolName = (const char*)fSymbolLookup->PrepareAddressNoThrow(
687 SYMNAME(fImage, symbol), 1);
688 *_symbolNameLen = fSymbolLookup->_SymbolNameLen(*_symbolName);
689 *_symbolAddress = symbol->st_value + fTextDelta;
690 *_symbolSize = symbol->st_size;
691 *_symbolType = symbol->Type() == STT_FUNC ? B_SYMBOL_TYPE_TEXT
692 : B_SYMBOL_TYPE_DATA;
693
694 return B_OK;
695 }
696 }
697