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 75 : static void FloodWithInc(Isolate* isolate, TestingAssemblerBuffer* buffer) {
25 225 : MacroAssembler masm(isolate, CodeObjectRequired::kYes, buffer->CreateView());
26 : #if V8_TARGET_ARCH_IA32
27 : __ mov(eax, Operand(esp, kPointerSize));
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 7575 : for (int i = 0; i < kNumInstr; ++i) {
34 7500 : __ 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 : __ function_descriptor();
56 : for (int i = 0; i < kNumInstr; ++i) {
57 : __ addi(r3, r3, Operand(1));
58 : }
59 : #elif V8_TARGET_ARCH_S390
60 : for (int i = 0; i < kNumInstr; ++i) {
61 : __ agfi(r2, Operand(1));
62 : }
63 : #else
64 : #error Unsupported architecture
65 : #endif
66 75 : __ Ret();
67 75 : CodeDesc desc;
68 75 : masm.GetCode(isolate, &desc);
69 75 : }
70 :
71 75 : static void FloodWithNop(Isolate* isolate, TestingAssemblerBuffer* buffer) {
72 225 : MacroAssembler masm(isolate, CodeObjectRequired::kYes, buffer->CreateView());
73 : #if V8_TARGET_ARCH_IA32
74 : __ mov(eax, Operand(esp, kPointerSize));
75 : #elif V8_TARGET_ARCH_X64
76 : __ movl(rax, arg_reg_1);
77 : #elif V8_TARGET_ARCH_MIPS
78 : __ mov(v0, a0);
79 : #elif V8_TARGET_ARCH_MIPS64
80 : __ mov(v0, a0);
81 : #elif V8_TARGET_ARCH_PPC
82 : __ function_descriptor();
83 : #endif
84 7575 : for (int i = 0; i < kNumInstr; ++i) {
85 7500 : __ nop();
86 : }
87 75 : __ Ret();
88 75 : CodeDesc desc;
89 75 : masm.GetCode(isolate, &desc);
90 75 : }
91 :
92 : // Order of operation for this test case:
93 : // exec -> perm(RW) -> patch -> flush -> perm(RX) -> exec
94 28342 : TEST(TestFlushICacheOfWritable) {
95 : Isolate* isolate = CcTest::i_isolate();
96 : HandleScope handles(isolate);
97 :
98 30 : for (int i = 0; i < kNumIterations; ++i) {
99 : auto buffer = AllocateAssemblerBuffer(kBufferSize);
100 :
101 : // Allow calling the function from C++.
102 25 : auto f = GeneratedCode<F0>::FromBuffer(isolate, buffer->start());
103 :
104 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
105 : buffer->size(), v8::PageAllocator::kReadWrite));
106 25 : FloodWithInc(isolate, buffer.get());
107 50 : Assembler::FlushICache(buffer->start(), buffer->size());
108 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
109 : buffer->size(), v8::PageAllocator::kReadExecute));
110 25 : CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code.
111 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
112 : buffer->size(), v8::PageAllocator::kReadWrite));
113 25 : FloodWithNop(isolate, buffer.get());
114 50 : Assembler::FlushICache(buffer->start(), buffer->size());
115 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
116 : buffer->size(), v8::PageAllocator::kReadExecute));
117 25 : CHECK_EQ(23, f.Call(23)); // Call into generated code.
118 : }
119 5 : }
120 :
121 : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64
122 : // Note that this order of operations is not supported on ARM32/64 because on
123 : // some older ARM32/64 kernels there is a bug which causes an access error on
124 : // cache flush instructions to trigger access error on non-writable memory.
125 : // See https://bugs.chromium.org/p/v8/issues/detail?id=8157
126 : //
127 : // Also note that this requires {kBufferSize == 8 * KB} to reproduce.
128 : //
129 : // The order of operations in V8 is akin to {TestFlushICacheOfWritable} above.
130 : // It is hence OK to disable the below test on some architectures. Only the
131 : // above test case should remain enabled on all architectures.
132 : #define CONDITIONAL_TEST DISABLED_TEST
133 : #else
134 : #define CONDITIONAL_TEST TEST
135 : #endif
136 :
137 : // Order of operation for this test case:
138 : // exec -> perm(RW) -> patch -> perm(RX) -> flush -> exec
139 28342 : CONDITIONAL_TEST(TestFlushICacheOfExecutable) {
140 : Isolate* isolate = CcTest::i_isolate();
141 : HandleScope handles(isolate);
142 :
143 30 : for (int i = 0; i < kNumIterations; ++i) {
144 : auto buffer = AllocateAssemblerBuffer(kBufferSize);
145 :
146 : // Allow calling the function from C++.
147 25 : auto f = GeneratedCode<F0>::FromBuffer(isolate, buffer->start());
148 :
149 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
150 : buffer->size(), v8::PageAllocator::kReadWrite));
151 25 : FloodWithInc(isolate, buffer.get());
152 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
153 : buffer->size(), v8::PageAllocator::kReadExecute));
154 50 : Assembler::FlushICache(buffer->start(), buffer->size());
155 25 : CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code.
156 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
157 : buffer->size(), v8::PageAllocator::kReadWrite));
158 25 : FloodWithNop(isolate, buffer.get());
159 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
160 : buffer->size(), v8::PageAllocator::kReadExecute));
161 50 : Assembler::FlushICache(buffer->start(), buffer->size());
162 25 : CHECK_EQ(23, f.Call(23)); // Call into generated code.
163 : }
164 5 : }
165 :
166 : #undef CONDITIONAL_TEST
167 :
168 : // Order of operation for this test case:
169 : // perm(RWX) -> exec -> patch -> flush -> exec
170 28342 : TEST(TestFlushICacheOfWritableAndExecutable) {
171 : Isolate* isolate = CcTest::i_isolate();
172 : HandleScope handles(isolate);
173 :
174 30 : for (int i = 0; i < kNumIterations; ++i) {
175 : auto buffer = AllocateAssemblerBuffer(kBufferSize);
176 :
177 : // Allow calling the function from C++.
178 25 : auto f = GeneratedCode<F0>::FromBuffer(isolate, buffer->start());
179 :
180 50 : CHECK(SetPermissions(GetPlatformPageAllocator(), buffer->start(),
181 : buffer->size(), v8::PageAllocator::kReadWriteExecute));
182 25 : FloodWithInc(isolate, buffer.get());
183 50 : Assembler::FlushICache(buffer->start(), buffer->size());
184 25 : CHECK_EQ(23 + kNumInstr, f.Call(23)); // Call into generated code.
185 25 : FloodWithNop(isolate, buffer.get());
186 50 : Assembler::FlushICache(buffer->start(), buffer->size());
187 25 : CHECK_EQ(23, f.Call(23)); // Call into generated code.
188 : }
189 5 : }
190 :
191 : #undef __
192 :
193 : } // namespace test_icache
194 : } // namespace internal
195 85011 : } // namespace v8
|