1/*
2 * Copyright 2016, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ElfSymbolLookup.h"
8
9#include <algorithm>
10
11#include <image.h>
12
13
14static const size_t kMaxSymbolNameLength = 64 * 1024;
15static const size_t kMaxReadStringChunkSize = 1024;
16static const size_t kCacheBufferSize = 4 * 1024;
17
18
19struct CachedSymbolLookupSource : public ElfSymbolLookupSource {
20	CachedSymbolLookupSource(ElfSymbolLookupSource* source)
21		:
22		fSource(source),
23		fBufferSize(0)
24	{
25		for (int i = 0; i < 2; i++) {
26			fBuffer[i] = 0;
27			fAddress[i] = 0;
28			fCachedSize[i] = 0;
29			fHitEnd[i] = true;
30		}
31
32		fSource->AcquireReference();
33	}
34
35	~CachedSymbolLookupSource()
36	{
37		delete[] fBuffer[0];
38
39		fSource->ReleaseReference();
40	}
41
42	status_t Init(size_t bufferSize)
43	{
44		fBuffer[0] = new(std::nothrow) uint8[bufferSize * 2];
45		if (fBuffer[0] == NULL)
46			return B_NO_MEMORY;
47
48		fBuffer[1] = fBuffer[0] + bufferSize;
49		fBufferSize = bufferSize;
50		return B_OK;
51	}
52
53	virtual ssize_t Read(uint64 address, void* _buffer, size_t size)
54	{
55		uint8* buffer = (uint8*)_buffer;
56		size_t totalRead = 0;
57
58		while (size > 0) {
59			ssize_t bytesRead = _ReadPartial(address, buffer, size);
60			if (bytesRead < 0)
61				return totalRead == 0 ? bytesRead : totalRead;
62			if (bytesRead == 0)
63				return totalRead == 0 ? B_IO_ERROR : totalRead;
64
65			totalRead += bytesRead;
66			buffer += bytesRead;
67			size -= bytesRead;
68		}
69
70		return totalRead;
71	}
72
73private:
74	ssize_t _ReadPartial(uint64 address, uint8* buffer, size_t size)
75	{
76		size_t bytesRead = _ReadCached(address, buffer, size);
77		if (bytesRead > 0)
78			return bytesRead;
79
80		status_t error = _Cache(address, size);
81		if (error != B_OK)
82			return error;
83
84		return (ssize_t)_ReadCached(address, buffer, size);
85	}
86
87	size_t _ReadCached(uint64 address, uint8* buffer, size_t size)
88	{
89		for (int i = 0; i < 2; i++) {
90			if (address >= fAddress[i]
91					&& address < fAddress[i] + fCachedSize[i]) {
92				size_t toRead = std::min(size,
93					size_t(fAddress[i] + fCachedSize[i] - address));
94				memcpy(buffer, fBuffer[i] + (address - fAddress[i]), toRead);
95				fHitEnd[i] = address + toRead == fAddress[i] + fCachedSize[i];
96				return toRead;
97			}
98		}
99		return 0;
100	}
101
102	status_t _Cache(uint64 address, size_t size)
103	{
104		int i = 0;
105		if (!fHitEnd[i])
106			i++;
107
108		ssize_t bytesRead = fSource->Read(address, fBuffer[i], fBufferSize);
109		if (bytesRead < 0)
110			return bytesRead;
111		if (bytesRead == 0)
112			return B_IO_ERROR;
113
114		fAddress[i] = address;
115		fCachedSize[i] = bytesRead;
116		fHitEnd[i] = false;
117		return B_OK;
118	}
119
120private:
121	ElfSymbolLookupSource*	fSource;
122	uint8*					fBuffer[2];
123	uint64					fAddress[2];
124	size_t					fCachedSize[2];
125	bool					fHitEnd[2];
126	size_t					fBufferSize;
127};
128
129
130// #pragma mark - ElfSymbolLookupImpl
131
132
133template<typename ElfClass>
134class ElfSymbolLookupImpl : public ElfSymbolLookup {
135public:
136	typedef typename ElfClass::Sym ElfSym;
137
138	ElfSymbolLookupImpl(ElfSymbolLookupSource* source, uint64 symbolTable,
139		uint64 symbolHash, uint64 stringTable, uint32 symbolCount,
140		uint32 symbolTableEntrySize, uint64 textDelta, bool swappedByteOrder)
141		:
142		fSource(NULL),
143		fSymbolTable(symbolTable),
144		fSymbolHash(symbolHash),
145		fStringTable(stringTable),
146		fSymbolCount(symbolCount),
147		fSymbolTableEntrySize(symbolTableEntrySize),
148		fTextDelta(textDelta),
149		fSwappedByteOrder(swappedByteOrder)
150	{
151		SetSource(source);
152	}
153
154	~ElfSymbolLookupImpl()
155	{
156		SetSource(NULL);
157	}
158
159	template<typename Value>
160	Value Get(const Value& value) const
161	{
162		return ElfFile::StaticGet(value, fSwappedByteOrder);
163	}
164
165	void SetSource(ElfSymbolLookupSource* source)
166	{
167		if (source == fSource)
168			return;
169
170		if (fSource != NULL)
171			fSource->ReleaseReference();
172
173		fSource = source;
174
175		if (fSource != NULL)
176			fSource->AcquireReference();
177	}
178
179	virtual status_t Init(bool cacheSource)
180	{
181		if (fSymbolTableEntrySize < sizeof(ElfSym))
182			return B_BAD_DATA;
183
184		// Create a cached source, if requested.
185		if (cacheSource) {
186			CachedSymbolLookupSource* cachedSource
187				= new(std::nothrow) CachedSymbolLookupSource(fSource);
188			if (cachedSource == NULL)
189				return B_NO_MEMORY;
190
191			SetSource(cachedSource);
192
193			status_t error = cachedSource->Init(kCacheBufferSize);
194			if (error != B_OK)
195				return error;
196		}
197
198		if (fSymbolCount == kGetSymbolCountFromHash) {
199			// Read the number of symbols in the symbol table from the hash
200			// table entry 1.
201			uint32 symbolCount;
202			ssize_t bytesRead = fSource->Read(fSymbolHash + 4, &symbolCount, 4);
203			if (bytesRead < 0)
204				return bytesRead;
205			if (bytesRead != 4)
206				return B_IO_ERROR;
207
208			fSymbolCount = Get(symbolCount);
209		}
210
211		return B_OK;
212	}
213
214	virtual status_t NextSymbolInfo(uint32& index, SymbolInfo& _info)
215	{
216		uint64 symbolAddress = fSymbolTable + index * fSymbolTableEntrySize;
217		for (; index < fSymbolCount;
218				index++, symbolAddress += fSymbolTableEntrySize) {
219			// read the symbol structure
220			ElfSym symbol;
221			ssize_t bytesRead = fSource->Read(symbolAddress, &symbol,
222				sizeof(symbol));
223			if (bytesRead < 0)
224				return bytesRead;
225			if ((size_t)bytesRead != sizeof(symbol))
226				return B_IO_ERROR;
227
228			// check, if it is a function or a data object and defined
229			// Note: Type() operates on a uint8, so byte order is irrelevant.
230			if ((symbol.Type() != STT_FUNC && symbol.Type() != STT_OBJECT)
231				|| symbol.st_value == 0) {
232				continue;
233			}
234
235			// get the values
236			target_addr_t address = Get(symbol.st_value) + fTextDelta;
237			target_size_t size = Get(symbol.st_size);
238			uint32 type = symbol.Type() == STT_FUNC
239				? B_SYMBOL_TYPE_TEXT : B_SYMBOL_TYPE_DATA;
240
241			// get the symbol name
242			uint64 nameAddress = fStringTable + Get(symbol.st_name);
243			BString name;
244			status_t error = _ReadString(nameAddress, kMaxSymbolNameLength,
245				name);
246			if (error != B_OK)
247				return error;
248
249			_info.SetTo(address, size, type, name);
250			index++;
251			return B_OK;
252		}
253
254		return B_ENTRY_NOT_FOUND;
255	}
256
257	virtual status_t GetSymbolInfo(const char* name, uint32 symbolType,
258		SymbolInfo& _info)
259	{
260		// TODO: Optimize this by using the hash table.
261		uint32 index = 0;
262		SymbolInfo info;
263		while (NextSymbolInfo(index, info) == B_OK) {
264			if (strcmp(name, info.Name()) == 0) {
265				_info = info;
266				return B_OK;
267			}
268		}
269
270		return B_ENTRY_NOT_FOUND;
271	}
272
273private:
274	status_t _ReadString(uint64 address, size_t size, BString& _string)
275	{
276		_string.Truncate(0);
277
278		char buffer[kMaxReadStringChunkSize];
279		while (size > 0) {
280			size_t toRead = std::min(size, sizeof(buffer));
281			ssize_t bytesRead = fSource->Read(address, buffer, toRead);
282			if (bytesRead < 0)
283				return bytesRead;
284			if (bytesRead == 0)
285				return B_IO_ERROR;
286
287			size_t chunkSize = strnlen(buffer, bytesRead);
288			int32 oldLength = _string.Length();
289			_string.Append(buffer, chunkSize);
290			if (_string.Length() <= oldLength)
291				return B_NO_MEMORY;
292
293			if (chunkSize < (size_t)bytesRead) {
294				// we found a terminating null
295				return B_OK;
296			}
297
298			address += bytesRead;
299			size -= bytesRead;
300		}
301
302		return B_BAD_DATA;
303	}
304
305private:
306	ElfSymbolLookupSource*	fSource;
307	uint64					fSymbolTable;
308	uint64					fSymbolHash;
309	uint64					fStringTable;
310	uint32					fSymbolCount;
311	uint32					fSymbolTableEntrySize;
312	uint64					fTextDelta;
313	bool					fSwappedByteOrder;
314};
315
316
317// #pragma mark - ElfSymbolLookup
318
319
320ElfSymbolLookup::~ElfSymbolLookup()
321{
322}
323
324
325/*static*/ status_t
326ElfSymbolLookup::Create(ElfSymbolLookupSource* source, uint64 symbolTable,
327	uint64 symbolHash, uint64 stringTable, uint32 symbolCount,
328	uint32 symbolTableEntrySize, uint64 textDelta, bool is64Bit,
329	bool swappedByteOrder, bool cacheSource, ElfSymbolLookup*& _lookup)
330{
331	// create
332	ElfSymbolLookup* lookup;
333	if (is64Bit) {
334		lookup = new(std::nothrow) ElfSymbolLookupImpl<ElfClass64>(source,
335			symbolTable, symbolHash, stringTable, symbolCount,
336			symbolTableEntrySize, textDelta, swappedByteOrder);
337	} else {
338		lookup = new(std::nothrow) ElfSymbolLookupImpl<ElfClass32>(source,
339			symbolTable, symbolHash, stringTable, symbolCount,
340			symbolTableEntrySize, textDelta, swappedByteOrder);
341	}
342
343	if (lookup == NULL)
344		return B_NO_MEMORY;
345
346	// init
347	status_t error = lookup->Init(cacheSource);
348	if (error == B_OK)
349		_lookup = lookup;
350	else
351		delete lookup;
352
353	return error;
354}
355