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

NativeSymbol.cpp 11KB

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