NativeSymbol.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #include "il2cpp-config.h"
  2. #include "il2cpp-class-internals.h"
  3. #include "os/Environment.h"
  4. #include "os/File.h"
  5. #include "os/Image.h"
  6. #include "os/Initialize.h"
  7. #include "os/LibraryLoader.h"
  8. #include "os/Locale.h"
  9. #include "os/Path.h"
  10. #include "NativeSymbol.h"
  11. #include "utils/Collections.h"
  12. #include "utils/PathUtils.h"
  13. #include "utils/MemoryMappedFile.h"
  14. #include "utils/Runtime.h"
  15. #include "vm-utils/MethodDefinitionKey.h"
  16. #include <string>
  17. #include <cstdlib>
  18. #if RUNTIME_TINY
  19. #include "vm/DebugMetadata.h"
  20. #endif
  21. namespace il2cpp
  22. {
  23. namespace utils
  24. {
  25. #if (IL2CPP_ENABLE_NATIVE_STACKTRACES && (!RUNTIME_TINY || IL2CPP_TINY_DEBUG_METADATA))
  26. static Il2CppMethodPointer MaskSpareBits(const Il2CppMethodPointer method)
  27. {
  28. return (Il2CppMethodPointer)((size_t)method & ~IL2CPP_POINTER_SPARE_BITS);
  29. }
  30. struct MethodInfoToMethodPointerConverter
  31. {
  32. Il2CppMethodPointer operator()(const MethodDefinitionKey& methodInfo) const
  33. {
  34. return MaskSpareBits(methodInfo.method);
  35. }
  36. };
  37. typedef il2cpp::utils::collections::ArrayValueMap<Il2CppMethodPointer, MethodDefinitionKey, MethodInfoToMethodPointerConverter> NativeMethodMap;
  38. static NativeMethodMap s_NativeMethods;
  39. struct NativeSymbolMutator
  40. {
  41. void operator()(MethodDefinitionKey* method)
  42. {
  43. // So, when a function is marked as noreturn, some compilers emit a call to that function and then
  44. // put the next function immediately after call instruction which means the return address on the stack
  45. // will appear to point to the wrong function. This messes up our stack walking as we now are confused
  46. // which method is actually on the stack. To work around this, we add "1" to each of the function addresses,
  47. // so each function appears to start 1 byte later which means the address on the stack will appear as if
  48. // it is pointing to function that called the no return function. This is okay because no function will
  49. // ever return to the first byte of another function.
  50. method->method = reinterpret_cast<Il2CppMethodPointer>(reinterpret_cast<intptr_t>(method->method) + 1);
  51. }
  52. };
  53. void NativeSymbol::RegisterMethods(const std::vector<MethodDefinitionKey>& managedMethods)
  54. {
  55. s_NativeMethods.assign(managedMethods);
  56. #if IL2CPP_MUTATE_METHOD_POINTERS
  57. NativeSymbolMutator mutator;
  58. s_NativeMethods.mutate(mutator);
  59. #endif
  60. }
  61. #pragma pack(push, p1, 4)
  62. struct SymbolInfo
  63. {
  64. uint64_t address;
  65. uint32_t length;
  66. };
  67. #pragma pack(pop, p1)
  68. static int32_t s_SymbolCount;
  69. static SymbolInfo* s_SymbolInfos;
  70. static void* s_ImageBase;
  71. #if !RUNTIME_TINY
  72. static void* LoadSymbolInfoFileFrom(const std::string& path)
  73. {
  74. int error;
  75. il2cpp::os::FileHandle* handle = il2cpp::os::File::Open(path, kFileModeOpen, kFileAccessRead, kFileShareRead, kFileOptionsNone, &error);
  76. if (error != 0)
  77. return NULL;
  78. // Note that we won't unmap this file, we'll leave it open the entire lifetime of the process.
  79. void* mappedFile = utils::MemoryMappedFile::Map(handle);
  80. il2cpp::os::File::Close(handle, &error);
  81. IL2CPP_ASSERT(error == 0);
  82. return mappedFile;
  83. }
  84. #endif //!RUNTIME_TINY
  85. static void* LoadSymbolInfoFile()
  86. {
  87. #if RUNTIME_TINY
  88. return NULL;
  89. #else
  90. #if IL2CPP_TARGET_ANDROID
  91. #if defined(__i386__)
  92. std::string symbolMapFileName = "SymbolMap-x86";
  93. #elif defined(__arm__)
  94. std::string symbolMapFileName = "SymbolMap-ARMv7";
  95. #elif defined(__aarch64__)
  96. std::string symbolMapFileName = "SymbolMap-ARM64";
  97. #elif defined(__x86_64__)
  98. std::string symbolMapFileName = "SymbolMap-x86_64";
  99. #else
  100. #error Unknown symbol map file name
  101. #endif
  102. #else
  103. #if !IL2CPP_CAN_USE_MULTIPLE_SYMBOL_MAPS
  104. std::string symbolMapFileName = "SymbolMap";
  105. #elif IL2CPP_SIZEOF_VOID_P == 4
  106. std::string symbolMapFileName = "SymbolMap-32";
  107. #elif IL2CPP_SIZEOF_VOID_P == 8
  108. std::string symbolMapFileName = "SymbolMap-64";
  109. #else
  110. #error Unknown symbol map file name
  111. #endif
  112. #endif
  113. void* result = LoadSymbolInfoFileFrom(il2cpp::utils::PathUtils::Combine(il2cpp::os::Path::GetApplicationFolder(), symbolMapFileName));
  114. if (result != NULL)
  115. return result;
  116. return LoadSymbolInfoFileFrom(il2cpp::utils::PathUtils::Combine(utils::Runtime::GetDataDir(), symbolMapFileName));
  117. #endif
  118. }
  119. static void InitializeSymbolInfos()
  120. {
  121. s_ImageBase = il2cpp::os::Image::GetImageBase();
  122. if (!il2cpp::os::Image::ManagedSectionExists())
  123. {
  124. void* fileBuffer = LoadSymbolInfoFile();
  125. if (fileBuffer == NULL)
  126. return;
  127. s_SymbolCount = *((int32_t*)fileBuffer);
  128. s_SymbolInfos = (SymbolInfo*)((uint8_t*)fileBuffer + sizeof(s_SymbolCount));
  129. }
  130. }
  131. static bool CompareEndOfSymbols(const SymbolInfo &a, const SymbolInfo &b)
  132. {
  133. return a.address + a.length < b.address + b.length;
  134. }
  135. static SymbolInfo* FindSymbolInfoForNativeMethod(Il2CppMethodPointer nativeMethod)
  136. {
  137. SymbolInfo* end = s_SymbolInfos + s_SymbolCount;
  138. // our 'key' could be anywhere within a symbol. Our comparison function compares the end address
  139. // of the symbols. By doing this, upper bound returns the first symbol whose end address is greater
  140. // than our 'key'. This is our symbol since our end is the first end above an interior value.
  141. SymbolInfo interiorSymbol = { (size_t)((char*)nativeMethod - (char*)s_ImageBase), 0 };
  142. SymbolInfo* containingSymbol = std::upper_bound(s_SymbolInfos, end, interiorSymbol, &CompareEndOfSymbols);
  143. if (containingSymbol == end)
  144. return NULL;
  145. // We only include managed methods in the symbol data. A lookup for a native method might find the
  146. // previous or next managed method in the data. This will be incorrect, so check the start and the size,
  147. // to make sure the interior symbol is really within the method found in the containing symbol.
  148. if ((interiorSymbol.address != containingSymbol->address) &&
  149. ((interiorSymbol.address < containingSymbol->address) ||
  150. (interiorSymbol.address - containingSymbol->address > containingSymbol->length)))
  151. return NULL;
  152. return containingSymbol;
  153. }
  154. static bool s_TriedToInitializeSymbolInfo = false;
  155. static bool IsInstructionPointerProbablyInManagedMethod(Il2CppMethodPointer managedMethodStart, Il2CppMethodPointer instructionPointer)
  156. {
  157. const int probableMaximumManagedMethodSizeInBytes = 5000;
  158. if (std::abs((intptr_t)managedMethodStart - (intptr_t)instructionPointer) < probableMaximumManagedMethodSizeInBytes)
  159. return true;
  160. return false;
  161. }
  162. const VmMethod* NativeSymbol::GetMethodFromNativeSymbol(Il2CppMethodPointer nativeMethod)
  163. {
  164. if (!s_TriedToInitializeSymbolInfo)
  165. {
  166. // Only attempt to initialize the symbol information once. If it is not present the first time,
  167. // it likely won't be there later either. Repeated checking can cause performance problems.
  168. s_TriedToInitializeSymbolInfo = true;
  169. InitializeSymbolInfos();
  170. }
  171. // address has to be above our base address
  172. if ((void*)nativeMethod < (void*)s_ImageBase)
  173. return NULL;
  174. if (il2cpp::os::Image::ManagedSectionExists())
  175. {
  176. if (!il2cpp::os::Image::IsInManagedSection((void*)nativeMethod))
  177. return NULL;
  178. }
  179. if (s_SymbolCount > 0)
  180. {
  181. SymbolInfo* containingSymbol = FindSymbolInfoForNativeMethod(nativeMethod);
  182. if (containingSymbol == NULL)
  183. return NULL;
  184. nativeMethod = (Il2CppMethodPointer)((char*)s_ImageBase + containingSymbol->address);
  185. // We can't assume that the map file is aligned.
  186. // We must use the same masking/no masking logic used to insert into the data structure for the lookup.
  187. // If we don't, the find will try to look up unmasked in a table full of masked values.
  188. // do exact lookup based on the symbol start address, as that is our key
  189. NativeMethodMap::iterator iter = s_NativeMethods.find_first(MaskSpareBits(nativeMethod));
  190. if (iter != s_NativeMethods.end())
  191. {
  192. #if RUNTIME_TINY
  193. return tiny::vm::DebugMetadata::GetMethodNameFromMethodDefinitionIndex(iter->methodIndex);
  194. #else
  195. return il2cpp::vm::MetadataCache::GetMethodInfoFromMethodHandle(iter->methodHandle);
  196. #endif
  197. }
  198. }
  199. else
  200. {
  201. // Get the first symbol greater than the one we want, because our instruction pointer
  202. // probably won't be at the start of the method.
  203. NativeMethodMap::iterator methodAfterNativeMethod = s_NativeMethods.upper_bound(nativeMethod);
  204. // If method are all of the managed methods are in the same custom section of the binary, then assume we
  205. // will find the proper method, so the end iterator means we found the last method in this list. If we
  206. // don't have custom sections, then we may have actually not found the method. In that case, let's not
  207. // return a method we are unsure of.
  208. if (!il2cpp::os::Image::ManagedSectionExists())
  209. {
  210. if (methodAfterNativeMethod == s_NativeMethods.end())
  211. return NULL;
  212. if (!IsInstructionPointerProbablyInManagedMethod(methodAfterNativeMethod->method, nativeMethod))
  213. return NULL;
  214. }
  215. // Go back one to get the method we actually want.
  216. if (methodAfterNativeMethod != s_NativeMethods.begin())
  217. methodAfterNativeMethod--;
  218. #if RUNTIME_TINY
  219. return tiny::vm::DebugMetadata::GetMethodNameFromMethodDefinitionIndex(methodAfterNativeMethod->methodIndex);
  220. #else
  221. return il2cpp::vm::MetadataCache::GetMethodInfoFromMethodHandle(methodAfterNativeMethod->methodHandle);
  222. #endif
  223. }
  224. return NULL;
  225. }
  226. bool NativeSymbol::GetMethodDebugInfo(const MethodInfo *method, Il2CppMethodDebugInfo* methodDebugInfo)
  227. {
  228. Il2CppMethodPointer nativeMethod = method->virtualMethodPointer;
  229. if (il2cpp::os::Image::ManagedSectionExists())
  230. {
  231. if (!il2cpp::os::Image::IsInManagedSection((void*)nativeMethod))
  232. return false;
  233. }
  234. int32_t codeSize = 0;
  235. if (s_SymbolCount > 0)
  236. {
  237. SymbolInfo* containingSymbol = FindSymbolInfoForNativeMethod(nativeMethod);
  238. if (containingSymbol == NULL)
  239. return false;
  240. codeSize = containingSymbol->length;
  241. }
  242. if (methodDebugInfo != NULL)
  243. {
  244. methodDebugInfo->methodPointer = method->virtualMethodPointer;
  245. methodDebugInfo->code_size = codeSize;
  246. methodDebugInfo->file = NULL;
  247. }
  248. return true;
  249. }
  250. #endif
  251. } /* namespace utils */
  252. } /* namespace il2cpp */