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 {
CachedSymbolLookupSourceCachedSymbolLookupSource20 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
~CachedSymbolLookupSourceCachedSymbolLookupSource35 ~CachedSymbolLookupSource()
36 {
37 delete[] fBuffer[0];
38
39 fSource->ReleaseReference();
40 }
41
InitCachedSymbolLookupSource42 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
ReadCachedSymbolLookupSource53 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:
_ReadPartialCachedSymbolLookupSource74 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
_ReadCachedCachedSymbolLookupSource87 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
_CacheCachedSymbolLookupSource102 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
ElfSymbolLookupImpl(ElfSymbolLookupSource * source,uint64 symbolTable,uint64 symbolHash,uint64 stringTable,uint32 symbolCount,uint32 symbolTableEntrySize,uint64 textDelta,bool swappedByteOrder)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
~ElfSymbolLookupImpl()154 ~ElfSymbolLookupImpl()
155 {
156 SetSource(NULL);
157 }
158
159 template<typename Value>
Get(const Value & value) const160 Value Get(const Value& value) const
161 {
162 return ElfFile::StaticGet(value, fSwappedByteOrder);
163 }
164
SetSource(ElfSymbolLookupSource * source)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
Init(bool cacheSource)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
NextSymbolInfo(uint32 & index,SymbolInfo & _info)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
GetSymbolInfo(const char * name,uint32 symbolType,SymbolInfo & _info)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:
_ReadString(uint64 address,size_t size,BString & _string)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
~ElfSymbolLookup()320 ElfSymbolLookup::~ElfSymbolLookup()
321 {
322 }
323
324
325 /*static*/ status_t
Create(ElfSymbolLookupSource * source,uint64 symbolTable,uint64 symbolHash,uint64 stringTable,uint32 symbolCount,uint32 symbolTableEntrySize,uint64 textDelta,bool is64Bit,bool swappedByteOrder,bool cacheSource,ElfSymbolLookup * & _lookup)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