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
30using namespace BPrivate::Debug;
31
32
33// PrepareAddress
34const void *
35Area::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
63RemoteMemoryAccessor::RemoteMemoryAccessor(team_id team)
64	: fTeam(team),
65	  fAreas()
66{
67}
68
69// destructor
70RemoteMemoryAccessor::~RemoteMemoryAccessor()
71{
72	// delete the areas
73	while (Area *area = fAreas.Head()) {
74		fAreas.Remove(area);
75		delete area;
76	}
77}
78
79// Init
80status_t
81RemoteMemoryAccessor::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
111const void *
112RemoteMemoryAccessor::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
127const void *
128RemoteMemoryAccessor::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
143Area*
144RemoteMemoryAccessor::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
160Area &
161RemoteMemoryAccessor::_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
179Area*
180RemoteMemoryAccessor::_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
195class SymbolLookup::LoadedImage : public Image {
196public:
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
212private:
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
224SymbolLookup::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
235SymbolLookup::~SymbolLookup()
236{
237	while (Image* image = fImages.RemoveHead())
238		delete image;
239}
240
241
242// Init
243status_t
244SymbolLookup::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
310status_t
311SymbolLookup::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
355status_t
356SymbolLookup::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
379status_t
380SymbolLookup::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
401status_t
402SymbolLookup::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
412status_t
413SymbolLookup::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
426const image_t *
427SymbolLookup::_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
449const image_t*
450SymbolLookup::_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
468Image*
469SymbolLookup::_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
483Image*
484SymbolLookup::_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
497size_t
498SymbolLookup::_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
509status_t
510SymbolLookup::_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
564SymbolLookup::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
589SymbolLookup::LoadedImage::~LoadedImage()
590{
591}
592
593
594const elf_sym*
595SymbolLookup::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
670status_t
671SymbolLookup::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