xref: /haiku/src/kits/debugger/elf/ElfSymbolLookup.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 
14 static const size_t kMaxSymbolNameLength = 64 * 1024;
15 static const size_t kMaxReadStringChunkSize = 1024;
16 static const size_t kCacheBufferSize = 4 * 1024;
17 
18 
19 struct 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 
73 private:
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 
120 private:
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 
133 template<typename ElfClass>
134 class ElfSymbolLookupImpl : public ElfSymbolLookup {
135 public:
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 
273 private:
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 
305 private:
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 
320 ElfSymbolLookup::~ElfSymbolLookup()
321 {
322 }
323 
324 
325 /*static*/ status_t
326 ElfSymbolLookup::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