OSDN Git Service

[ARM][AArch64] Workaround ARM/AArch64 peculiarity in clearing icache.
authorPeter Smith <peter.smith@linaro.org>
Tue, 28 Nov 2017 12:34:05 +0000 (12:34 +0000)
committerPeter Smith <peter.smith@linaro.org>
Tue, 28 Nov 2017 12:34:05 +0000 (12:34 +0000)
Certain ARM implementations treat icache clear instruction as a memory read,
and CPU segfaults on trying to clear cache on !PROT_READ page.
We workaround this in Memory::protectMappedMemory by adding
PROT_READ to affected pages, clearing the cache, and then setting
desired protection.

This fixes "AllocationTests/MappedMemoryTest.***/3" unit-tests on
affected hardware.

Reviewers: psmith, zatrazz, kristof.beyls, lhames

Reviewed By: lhames

Subscribers: llvm-commits, krytarowski, peter.smith, jgreenhalgh, aemerson,
             rengolin

Patch by maxim-kuvrykov!

Differential Revision: https://reviews.llvm.org/D40423

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@319166 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Support/Unix/Memory.inc

index 3148016..848548d 100644 (file)
@@ -126,8 +126,12 @@ Memory::allocateMappedMemory(size_t NumBytes,
   Result.Address = Addr;
   Result.Size = NumPages*PageSize;
 
-  if (PFlags & MF_EXEC)
-    Memory::InvalidateInstructionCache(Result.Address, Result.Size);
+  // Rely on protectMappedMemory to invalidate instruction cache.
+  if (PFlags & MF_EXEC) {
+    EC = Memory::protectMappedMemory (Result, PFlags);
+    if (EC != std::error_code())
+      return MemoryBlock();
+  }
 
   return Result;
 }
@@ -156,15 +160,31 @@ Memory::protectMappedMemory(const MemoryBlock &M, unsigned Flags) {
     return std::error_code(EINVAL, std::generic_category());
 
   int Protect = getPosixProtectionFlags(Flags);
-
   uintptr_t Start = alignAddr((uint8_t *)M.Address - PageSize + 1, PageSize);
   uintptr_t End = alignAddr((uint8_t *)M.Address + M.Size, PageSize);
+
+  bool InvalidateCache = (Flags & MF_EXEC);
+
+#if defined(__arm__) || defined(__aarch64__)
+  // Certain ARM implementations treat icache clear instruction as a memory read,
+  // and CPU segfaults on trying to clear cache on !PROT_READ page.  Therefore we need
+  // to temporarily add PROT_READ for the sake of flushing the instruction caches.
+  if (InvalidateCache && !(Protect & PROT_READ)) {
+    int Result = ::mprotect((void *)Start, End - Start, Protect | PROT_READ);
+    if (Result != 0)
+      return std::error_code(errno, std::generic_category());
+
+    Memory::InvalidateInstructionCache(M.Address, M.Size);
+    InvalidateCache = false;
+  }
+#endif
+
   int Result = ::mprotect((void *)Start, End - Start, Protect);
 
   if (Result != 0)
     return std::error_code(errno, std::generic_category());
 
-  if (Flags & MF_EXEC)
+  if (InvalidateCache)
     Memory::InvalidateInstructionCache(M.Address, M.Size);
 
   return std::error_code();