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