Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/assembler-inl.h"
6 : #include "src/handles-inl.h"
7 : #include "src/macro-assembler-inl.h"
8 : #include "src/simulator.h"
9 : #include "test/cctest/cctest.h"
10 : #include "test/common/assembler-tester.h"
11 :
12 : namespace v8 {
13 : namespace internal {
14 : namespace test_icache {
15 :
16 : using F0 = int(int);
17 :
18 : #define __ masm.
19 :
20 : static constexpr int kNumInstr = 100;
21 : static constexpr int kNumIterations = 5;
22 : static constexpr int kBufferSize = 8 * KB;
23 :
24 60 : static void FloodWithInc(Isolate* isolate, TestingAssemblerBuffer* buffer) {
25 120 : MacroAssembler masm(isolate, CodeObjectRequired::kYes, buffer->CreateView());
26 : #if V8_TARGET_ARCH_IA32
27 : __ mov(eax, Operand(esp, kSystemPointerSize));
28 : for (int i = 0; i < kNumInstr; ++i) {
29 : __ add(eax, Immediate(1));
30 : }
31 : #elif V8_TARGET_ARCH_X64
32 : __ movl(rax, arg_reg_1);
33 12060 : for (int i = 0; i < kNumInstr; ++i) {
34 : __ addl(rax, Immediate(1));
35 : }
36 : #elif V8_TARGET_ARCH_ARM64
37 : for (int i = 0; i < kNumInstr; ++i) {
38 : __ Add(x0, x0, Operand(1));
39 : }
40 : #elif V8_TARGET_ARCH_ARM
41 : for (int i = 0; i < kNumInstr; ++i) {
42 : __ add(r0, r0, Operand(1));
43 : }
44 : #elif V8_TARGET_ARCH_MIPS
45 : __ mov(v0, a0);
46 : for (int i = 0; i < kNumInstr; ++i) {
47 : __ Addu(v0, v0, Operand(1));
48 : }
49 : #elif V8_TARGET_ARCH_MIPS64
50 : __ mov(v0, a0);
51 : for (int i = 0; i < kNumInstr; ++i) {
52 : __ Addu(v0, v0, Operand(1));
53 : }
54 : #elif V8_TARGET_ARCH_PPC
55 : for (int i = 0; i < kNumInstr; ++i) {
56 : __ addi(r3, r3, Operand(1));
57 : }
58 : #elif V8_TARGET_ARCH_S390
59 : for (int i = 0; i < kNumInstr; ++i) {
60 : __ agfi(r2, Operand(1));
61 : }
62 : #else
63 : #error Unsupported architecture
64 : #endif
65 60 : __ Ret();
66 60 : CodeDesc desc;
67 : masm.GetCode(isolate, &desc);
68 60 : }
69 :
70 60 : static void FloodWithNop(Isolate* isolate, TestingAssemblerBuffer* buffer) {
71 120 : MacroAssembler masm(isolate, CodeObjectRequired::kYes, buffer->CreateView());
72 : #if V8_TARGET_ARCH_IA32
73 : __ mov(eax, Operand(esp, kSystemPointerSize));
74 : #elif V8_TARGET_ARCH_X64
75 : __ movl(rax, arg_reg_1);
76 : #elif V8_TARGET_ARCH_MIPS
77 : __ mov(v0, a0);
78 : #elif V8_TARGET_ARCH_MIPS64
79 : __ mov(v0, a0);
80 : #endif
81 12060 : for (int i = 0; i < kNumInstr; ++i) {
82 6000 : __ nop();
83 : }
84 60 : __ Ret();
85 60 : CodeDesc desc;
86 : masm.GetCode(isolate, &desc);
87 60 : }
88 :
89 : // Order of operation for this test case:
90 : // exec -> perm(RW) -> patch -> flush -> perm(RX) -> exec
91 26643 : TEST(TestFlushICacheOfWritable) {
92 : Isolate* isolate = CcTest::i_isolate();
93 : HandleScope handles(isolate);
94 :
95 44 : for (int i = 0; i < kNumIterations; ++i) {
96 : auto buffer = AllocateAssemblerBuffer(kBufferSize);
97 :
98 : // Allow calling the function from C++.
99 20 : auto f = GeneratedCode<F0>::FromBuffer(isolate, buffer->start());
100 :
101 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
102 : buffer->size(), v8::PageAllocator::kReadWrite));
103 20 : FloodWithInc(isolate, buffer.get());
104 40 : FlushInstructionCache(buffer->start(), buffer->size());
105 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
106 : buffer->size(), v8::PageAllocator::kReadExecute));
107 20 : CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code.
108 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
109 : buffer->size(), v8::PageAllocator::kReadWrite));
110 20 : FloodWithNop(isolate, buffer.get());
111 40 : FlushInstructionCache(buffer->start(), buffer->size());
112 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
113 : buffer->size(), v8::PageAllocator::kReadExecute));
114 20 : CHECK_EQ(23, f.Call(23)); // Call into generated code.
115 : }
116 4 : }
117 :
118 : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64
119 : // Note that this order of operations is not supported on ARM32/64 because on
120 : // some older ARM32/64 kernels there is a bug which causes an access error on
121 : // cache flush instructions to trigger access error on non-writable memory.
122 : // See https://bugs.chromium.org/p/v8/issues/detail?id=8157
123 : //
124 : // Also note that this requires {kBufferSize == 8 * KB} to reproduce.
125 : //
126 : // The order of operations in V8 is akin to {TestFlushICacheOfWritable} above.
127 : // It is hence OK to disable the below test on some architectures. Only the
128 : // above test case should remain enabled on all architectures.
129 : #define CONDITIONAL_TEST DISABLED_TEST
130 : #else
131 : #define CONDITIONAL_TEST TEST
132 : #endif
133 :
134 : // Order of operation for this test case:
135 : // exec -> perm(RW) -> patch -> perm(RX) -> flush -> exec
136 26643 : CONDITIONAL_TEST(TestFlushICacheOfExecutable) {
137 : Isolate* isolate = CcTest::i_isolate();
138 : HandleScope handles(isolate);
139 :
140 44 : for (int i = 0; i < kNumIterations; ++i) {
141 : auto buffer = AllocateAssemblerBuffer(kBufferSize);
142 :
143 : // Allow calling the function from C++.
144 20 : auto f = GeneratedCode<F0>::FromBuffer(isolate, buffer->start());
145 :
146 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
147 : buffer->size(), v8::PageAllocator::kReadWrite));
148 20 : FloodWithInc(isolate, buffer.get());
149 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
150 : buffer->size(), v8::PageAllocator::kReadExecute));
151 40 : FlushInstructionCache(buffer->start(), buffer->size());
152 20 : CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code.
153 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
154 : buffer->size(), v8::PageAllocator::kReadWrite));
155 20 : FloodWithNop(isolate, buffer.get());
156 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
157 : buffer->size(), v8::PageAllocator::kReadExecute));
158 40 : FlushInstructionCache(buffer->start(), buffer->size());
159 20 : CHECK_EQ(23, f.Call(23)); // Call into generated code.
160 : }
161 4 : }
162 :
163 : #undef CONDITIONAL_TEST
164 :
165 : // Order of operation for this test case:
166 : // perm(RWX) -> exec -> patch -> flush -> exec
167 26643 : TEST(TestFlushICacheOfWritableAndExecutable) {
168 : Isolate* isolate = CcTest::i_isolate();
169 : HandleScope handles(isolate);
170 :
171 44 : for (int i = 0; i < kNumIterations; ++i) {
172 : auto buffer = AllocateAssemblerBuffer(kBufferSize);
173 :
174 : // Allow calling the function from C++.
175 20 : auto f = GeneratedCode<F0>::FromBuffer(isolate, buffer->start());
176 :
177 40 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
178 : buffer->size(), v8::PageAllocator::kReadWriteExecute));
179 20 : FloodWithInc(isolate, buffer.get());
180 40 : FlushInstructionCache(buffer->start(), buffer->size());
181 20 : CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code.
182 20 : FloodWithNop(isolate, buffer.get());
183 40 : FlushInstructionCache(buffer->start(), buffer->size());
184 20 : CHECK_EQ(23, f.Call(23)); // Call into generated code.
185 : }
186 4 : }
187 :
188 : #undef __
189 :
190 : } // namespace test_icache
191 : } // namespace internal
192 79917 : } // namespace v8
|