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