暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

DebugSymbolReader.cpp 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include "il2cpp-config.h"
  2. #include "os/File.h"
  3. #include "os/Image.h"
  4. #include "os/Path.h"
  5. #include "utils/Logging.h"
  6. #include "utils/Memory.h"
  7. #include "utils/MemoryMappedFile.h"
  8. #include "utils/PathUtils.h"
  9. #include "utils/StringView.h"
  10. #include "utils/Runtime.h"
  11. #include "vm-utils/DebugSymbolReader.h"
  12. #include "vm/GlobalMetadata.h"
  13. #include "vm/Method.h"
  14. #include "vm/Reflection.h"
  15. #include <string>
  16. #if IL2CPP_TARGET_ARM64E
  17. #include <ptrauth.h>
  18. #endif
  19. namespace il2cpp
  20. {
  21. namespace utils
  22. {
  23. struct usymliteHeader
  24. {
  25. uint32_t magic;
  26. uint32_t version;
  27. uint32_t lineCount;
  28. uint32_t id; // executable's id, offset in string table
  29. uint32_t os;
  30. uint32_t arch;
  31. };
  32. struct usymliteLine
  33. {
  34. uint64_t address;
  35. uint32_t methodIndex;
  36. uint32_t fileName; // Reference to the managed source file name in the string table
  37. uint32_t line; // Managed line number
  38. uint32_t parent;
  39. };
  40. struct Reader
  41. {
  42. void* debugSymbolData;
  43. const usymliteLine* lines;
  44. const char* strings;
  45. usymliteHeader header;
  46. std::string uuid;
  47. std::string os;
  48. std::string arch;
  49. uint64_t firstLineAddress;
  50. uint64_t lastLineAddress;
  51. uint32_t maxStringIndex;
  52. };
  53. static Reader s_usym = { 0 };
  54. const int headerSize = 24;
  55. const int lineSize = 24;
  56. const uint32_t magicUsymlite = 0x2D6D7973; // "sym-"
  57. const uint32_t noLine = 0xFFFFFFFF;
  58. static std::string GetArchFolder()
  59. {
  60. #if IL2CPP_TARGET_ARM64
  61. return PathUtils::Combine(utils::Runtime::GetDataDir(), std::string("arm64"));
  62. #elif IL2CPP_TARGET_X64
  63. return PathUtils::Combine(utils::Runtime::GetDataDir(), std::string("x64"));
  64. #else
  65. return std::string("<NotImplemented>");
  66. #endif
  67. }
  68. // Do a binary search to find the line with the given address
  69. // This is looking for the line with the closest address without going over (price is right style)
  70. usymliteLine FindLine(uint64_t address)
  71. {
  72. uint32_t head = 0;
  73. uint32_t tail = s_usym.header.lineCount - 1;
  74. while (head < tail)
  75. {
  76. uint32_t mid = (head + tail + 1) / 2;
  77. uint64_t midAddr = s_usym.lines[mid].address;
  78. if (address < midAddr)
  79. {
  80. tail = mid - 1;
  81. }
  82. else
  83. {
  84. head = mid;
  85. }
  86. }
  87. uint64_t foundAddr = s_usym.lines[head].address;
  88. // Find the last entry with this address
  89. while (head + 1 < s_usym.header.lineCount && s_usym.lines[head + 1].address == foundAddr)
  90. {
  91. head += 1;
  92. }
  93. return s_usym.lines[head];
  94. }
  95. const char* GetString(uint32_t index)
  96. {
  97. IL2CPP_ASSERT(index < s_usym.maxStringIndex);
  98. return s_usym.strings + index;
  99. }
  100. #define IL2CPP_DEBUG_DUMP_USYM_DATA 0
  101. #if IL2CPP_DEBUG_DUMP_USYM_DATA
  102. static void DumpUsymData()
  103. {
  104. // You may want to change this to be a full path so it is easy to locate.
  105. FILE* dumpFile = fopen("usymData.txt", "w");
  106. uint64_t imageBase = (uint64_t)os::Image::GetImageBase();
  107. for (uint32_t i = 0; i < s_usym.header.lineCount; i++)
  108. {
  109. if (s_usym.lines[i].methodIndex != noLine)
  110. {
  111. uint64_t address = s_usym.lines[i].address;
  112. void* actualAddress = (void*)(s_usym.lines[i].address + imageBase);
  113. const MethodInfo* methodInfo = vm::GlobalMetadata::GetMethodInfoFromMethodDefinitionIndex(s_usym.lines[i].methodIndex);
  114. uint32_t methodIndex = s_usym.lines[i].methodIndex;
  115. const char* filePath = GetString(s_usym.lines[i].fileName);
  116. uint32_t sourceCodeLineNumber = s_usym.lines[i].line;
  117. uint32_t parent = s_usym.lines[i].parent;
  118. if (methodInfo != NULL)
  119. fprintf(dumpFile, "%d [%p, %llu] Method Index: %d %s %s(%d) parent: %d\n", i, actualAddress, address, methodIndex, vm::Method::GetFullName(methodInfo).c_str(), filePath, sourceCodeLineNumber, parent);
  120. }
  121. }
  122. fclose(dumpFile);
  123. }
  124. #endif
  125. bool DebugSymbolReader::LoadDebugSymbols()
  126. {
  127. int error = 0;
  128. std::string symbolsPath;
  129. const StringView<char> symbolFileName = "il2cpp.usym";
  130. // First, look for the symbol file next to the executable.
  131. std::string applicationFolder = os::Path::GetApplicationFolder();
  132. if (!applicationFolder.empty())
  133. symbolsPath = PathUtils::Combine(applicationFolder, symbolFileName);
  134. os::FileHandle* symbolsFileHandle = NULL;
  135. if (!symbolsPath.empty())
  136. symbolsFileHandle = os::File::Open(symbolsPath.c_str(), kFileModeOpen, kFileAccessRead, kFileShareRead, kFileOptionsNone, &error);
  137. // (MacOS only) - Handle cases where the il2cpp.usym file's been dropped under an architecture specific (x64 or arm64) directory
  138. if (symbolsPath.empty() || error != 0)
  139. {
  140. std::string archFolder = GetArchFolder();
  141. if (!archFolder.empty())
  142. symbolsPath = PathUtils::Combine(archFolder, symbolFileName);
  143. if (!symbolsPath.empty())
  144. symbolsFileHandle = os::File::Open(symbolsPath.c_str(), kFileModeOpen, kFileAccessRead, kFileShareRead, kFileOptionsNone, &error);
  145. }
  146. // If we don't have a symbol path yet or there was some error opening the file next to the executable, try to
  147. // look in the data directory. For some platforms, the packaging won't allow the file to live next to the
  148. // executable.
  149. if (symbolsPath.empty() || error != 0)
  150. {
  151. symbolsPath = PathUtils::Combine(utils::Runtime::GetDataDir(), symbolFileName);
  152. symbolsFileHandle = os::File::Open(symbolsPath.c_str(), kFileModeOpen, kFileAccessRead, kFileShareRead, kFileOptionsNone, &error);
  153. if (error != 0)
  154. return false;
  155. }
  156. s_usym.debugSymbolData = utils::MemoryMappedFile::Map(symbolsFileHandle);
  157. int64_t length = os::File::GetLength(symbolsFileHandle, &error);
  158. os::File::Close(symbolsFileHandle, &error);
  159. if (error != 0)
  160. {
  161. utils::MemoryMappedFile::Unmap(s_usym.debugSymbolData);
  162. s_usym.debugSymbolData = NULL;
  163. return false;
  164. }
  165. s_usym.header = *(usymliteHeader *)((char *)s_usym.debugSymbolData);
  166. if (s_usym.header.magic != magicUsymlite || s_usym.header.lineCount == 0)
  167. {
  168. utils::MemoryMappedFile::Unmap(s_usym.debugSymbolData);
  169. s_usym.debugSymbolData = NULL;
  170. return false;
  171. }
  172. int64_t lineOffset = headerSize;
  173. int64_t stringOffset = lineOffset + (s_usym.header.lineCount * lineSize);
  174. s_usym.maxStringIndex = (uint32_t)(length - stringOffset);
  175. s_usym.lines = (const usymliteLine*)((const char *)s_usym.debugSymbolData + lineOffset);
  176. s_usym.strings = ((const char *)s_usym.debugSymbolData + stringOffset);
  177. #if IL2CPP_ENABLE_NATIVE_INSTRUCTION_POINTER_EMISSION
  178. char* our_uuid = os::Image::GetImageUUID();
  179. s_usym.uuid = std::string(GetString(s_usym.header.id));
  180. if (our_uuid == NULL || s_usym.uuid != our_uuid)
  181. {
  182. // UUID mismatch means this usymfile is not for this program
  183. il2cpp::utils::Logging::Write("Ignoring symbol file due to UUID mismatch. File contains %s but expected %s.", s_usym.uuid.c_str(), our_uuid);
  184. utils::MemoryMappedFile::Unmap(s_usym.debugSymbolData);
  185. s_usym.debugSymbolData = NULL;
  186. s_usym.lines = NULL;
  187. s_usym.strings = NULL;
  188. return false;
  189. }
  190. IL2CPP_FREE(our_uuid);
  191. #endif
  192. s_usym.os = std::string(GetString(s_usym.header.os));
  193. s_usym.arch = std::string(GetString(s_usym.header.arch));
  194. s_usym.firstLineAddress = s_usym.lines[0].address;
  195. s_usym.lastLineAddress = s_usym.lines[s_usym.header.lineCount - 1].address;
  196. #if IL2CPP_DEBUG_DUMP_USYM_DATA
  197. DumpUsymData();
  198. #endif
  199. return true;
  200. }
  201. void InsertStackFrame(usymliteLine line, std::vector<Il2CppStackFrameInfo>* stackFrames)
  202. {
  203. if (line.parent != noLine)
  204. {
  205. InsertStackFrame(s_usym.lines[line.parent], stackFrames);
  206. }
  207. const MethodInfo* methodInfo = vm::GlobalMetadata::GetMethodInfoFromMethodDefinitionIndex(line.methodIndex);
  208. Il2CppStackFrameInfo frameInfo = { 0 };
  209. frameInfo.method = methodInfo;
  210. frameInfo.raw_ip = (uintptr_t)line.address;
  211. frameInfo.filePath = GetString(line.fileName);
  212. frameInfo.sourceCodeLineNumber = line.line;
  213. stackFrames->push_back(frameInfo);
  214. }
  215. bool DebugSymbolReader::AddStackFrames(void* nativeInstructionPointer, std::vector<Il2CppStackFrameInfo>* stackFrames)
  216. {
  217. if (s_usym.debugSymbolData == NULL || nativeInstructionPointer == NULL)
  218. {
  219. return false;
  220. }
  221. // The instruction pointer points to the next address, so to get the address we came from, we subtract 1.
  222. // findLine matches the address to the closest address <= the one we give, so it finds the one we need
  223. uint64_t adjustedAddress = ((uint64_t)nativeInstructionPointer) - ((uint64_t)os::Image::GetImageBase()) - 1;
  224. #if IL2CPP_TARGET_ANDROID
  225. // We don't seem to need to subtract by one for Android
  226. // https://github.com/Unity-Technologies/unity-services-crash/commit/50611fcf29a1d876689942ed1f1cdca23e32c522
  227. adjustedAddress += 1;
  228. #endif
  229. #if IL2CPP_TARGET_ARM64E
  230. adjustedAddress = (uint64_t)ptrauth_strip((void*)adjustedAddress, ptrauth_key_return_address);
  231. #endif
  232. // Quick check to remove anything outside the range
  233. if (adjustedAddress < s_usym.firstLineAddress || s_usym.lastLineAddress < adjustedAddress)
  234. {
  235. return false;
  236. }
  237. usymliteLine line = FindLine(adjustedAddress);
  238. // End of symbol entries are placed to indicate that we're past the end of a C# function.
  239. // These EOS entries have their Line and FileName set to 0xFFFFFFFF
  240. if (line.line == noLine)
  241. {
  242. return false;
  243. }
  244. InsertStackFrame(line, stackFrames);
  245. return true;
  246. }
  247. bool DebugSymbolReader::DebugSymbolsAvailable()
  248. {
  249. #if IL2CPP_MONO_DEBUGGER
  250. return true;
  251. #else
  252. return s_usym.debugSymbolData != NULL;
  253. #endif
  254. }
  255. } /* namespace utils */
  256. } /* namespace il2cpp */