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 "include/v8.h"
6 :
7 : #include "src/api-inl.h"
8 : #include "src/builtins/builtins.h"
9 : #include "src/isolate.h"
10 : #include "src/objects/code-inl.h"
11 : #include "test/cctest/cctest.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 : namespace test_unwinder {
16 :
17 : static void* unlimited_stack_base = std::numeric_limits<void*>::max();
18 :
19 28342 : TEST(Unwind_BadState_Fail) {
20 5 : UnwindState unwind_state; // Fields are intialized to nullptr.
21 : RegisterState register_state;
22 :
23 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
24 5 : unlimited_stack_base);
25 5 : CHECK(!unwound);
26 : // The register state should not change when unwinding fails.
27 5 : CHECK_NULL(register_state.fp);
28 5 : CHECK_NULL(register_state.sp);
29 5 : CHECK_NULL(register_state.pc);
30 5 : }
31 :
32 28342 : TEST(Unwind_BuiltinPCInMiddle_Success) {
33 5 : LocalContext env;
34 5 : v8::Isolate* isolate = env->GetIsolate();
35 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
36 :
37 5 : UnwindState unwind_state = isolate->GetUnwindState();
38 : RegisterState register_state;
39 :
40 : uintptr_t stack[3];
41 : void* stack_base = stack + arraysize(stack);
42 5 : stack[0] = reinterpret_cast<uintptr_t>(stack + 2); // saved FP (rbp).
43 5 : stack[1] = 202; // Return address into C++ code.
44 5 : stack[2] = 303; // The SP points here in the caller's frame.
45 :
46 5 : register_state.sp = stack;
47 5 : register_state.fp = stack;
48 :
49 : // Put the current PC inside of a valid builtin.
50 5 : Code builtin = i_isolate->builtins()->builtin(Builtins::kStringEqual);
51 : const uintptr_t offset = 40;
52 10 : CHECK_LT(offset, builtin->InstructionSize());
53 : register_state.pc =
54 5 : reinterpret_cast<void*>(builtin->InstructionStart() + offset);
55 :
56 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
57 5 : stack_base);
58 5 : CHECK(unwound);
59 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.fp);
60 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.sp);
61 5 : CHECK_EQ(reinterpret_cast<void*>(202), register_state.pc);
62 5 : }
63 :
64 : // The unwinder should be able to unwind even if we haven't properly set up the
65 : // current frame, as long as there is another JS frame underneath us (i.e. as
66 : // long as the PC isn't in JSEntry). This test puts the PC at the start
67 : // of a JS builtin and creates a fake JSEntry frame before it on the stack. The
68 : // unwinder should be able to unwind to the C++ frame before the JSEntry frame.
69 28342 : TEST(Unwind_BuiltinPCAtStart_Success) {
70 5 : LocalContext env;
71 5 : v8::Isolate* isolate = env->GetIsolate();
72 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
73 :
74 5 : UnwindState unwind_state = isolate->GetUnwindState();
75 : RegisterState register_state;
76 :
77 : const size_t code_length = 40;
78 5 : uintptr_t code[code_length] = {0};
79 5 : unwind_state.code_range.start = code;
80 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
81 :
82 : uintptr_t stack[6];
83 : void* stack_base = stack + arraysize(stack);
84 5 : stack[0] = 101;
85 : // Return address into JS code. It doesn't matter that this is not actually in
86 : // JSEntry, because we only check that for the top frame.
87 5 : stack[1] = reinterpret_cast<uintptr_t>(code + 10);
88 5 : stack[2] = reinterpret_cast<uintptr_t>(stack + 5); // saved FP (rbp).
89 5 : stack[3] = 303; // Return address into C++ code.
90 5 : stack[4] = 404;
91 5 : stack[5] = 505;
92 :
93 5 : register_state.sp = stack;
94 5 : register_state.fp = stack + 2; // FP to the JSEntry frame.
95 :
96 : // Put the current PC at the start of a valid builtin, so that we are setting
97 : // up the frame.
98 5 : Code builtin = i_isolate->builtins()->builtin(Builtins::kStringEqual);
99 5 : register_state.pc = reinterpret_cast<void*>(builtin->InstructionStart());
100 :
101 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
102 5 : stack_base);
103 :
104 5 : CHECK(unwound);
105 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 5), register_state.fp);
106 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 4), register_state.sp);
107 5 : CHECK_EQ(reinterpret_cast<void*>(303), register_state.pc);
108 5 : }
109 :
110 : const char* foo_source = R"(
111 : function foo(a, b) {
112 : let x = a * b;
113 : let y = x ^ b;
114 : let z = y / a;
115 : return x + y - z;
116 : }
117 : foo(1, 2);
118 : foo(1, 2);
119 : %OptimizeFunctionOnNextCall(foo);
120 : foo(1, 2);
121 : )";
122 :
123 : // Check that we can unwind when the pc is within an optimized code object on
124 : // the V8 heap.
125 28342 : TEST(Unwind_CodeObjectPCInMiddle_Success) {
126 5 : FLAG_allow_natives_syntax = true;
127 5 : LocalContext env;
128 5 : v8::Isolate* isolate = env->GetIsolate();
129 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
130 : HandleScope scope(i_isolate);
131 :
132 5 : UnwindState unwind_state = isolate->GetUnwindState();
133 : RegisterState register_state;
134 :
135 : uintptr_t stack[3];
136 : void* stack_base = stack + arraysize(stack);
137 5 : stack[0] = reinterpret_cast<uintptr_t>(stack + 2); // saved FP (rbp).
138 5 : stack[1] = 202; // Return address into C++ code.
139 5 : stack[2] = 303; // The SP points here in the caller's frame.
140 :
141 5 : register_state.sp = stack;
142 5 : register_state.fp = stack;
143 :
144 : // Create an on-heap code object. Make sure we run the function so that it is
145 : // compiled and not just marked for lazy compilation.
146 5 : CompileRun(foo_source);
147 : v8::Local<v8::Function> local_foo = v8::Local<v8::Function>::Cast(
148 25 : env.local()->Global()->Get(env.local(), v8_str("foo")).ToLocalChecked());
149 : Handle<JSFunction> foo =
150 5 : Handle<JSFunction>::cast(v8::Utils::OpenHandle(*local_foo));
151 :
152 : // Put the current PC inside of the created code object.
153 5 : AbstractCode abstract_code = foo->abstract_code();
154 : // We don't produce optimized code when run with --no-opt.
155 10 : if (!abstract_code->IsCode() && FLAG_opt == false) return;
156 4 : CHECK(abstract_code->IsCode());
157 :
158 4 : Code code = abstract_code->GetCode();
159 : // We don't want the offset too early or it could be the `push rbp`
160 : // instruction (which is not at the start of generated code, because the lazy
161 : // deopt check happens before frame setup).
162 4 : const uintptr_t offset = code->InstructionSize() - 20;
163 8 : CHECK_LT(offset, code->InstructionSize());
164 4 : Address pc = code->InstructionStart() + offset;
165 4 : register_state.pc = reinterpret_cast<void*>(pc);
166 :
167 : // Check that the created code is within the code range that we get from the
168 : // API.
169 4 : Address start = reinterpret_cast<Address>(unwind_state.code_range.start);
170 4 : CHECK(pc >= start && pc < start + unwind_state.code_range.length_in_bytes);
171 :
172 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
173 4 : stack_base);
174 4 : CHECK(unwound);
175 4 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.fp);
176 4 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.sp);
177 8 : CHECK_EQ(reinterpret_cast<void*>(202), register_state.pc);
178 : }
179 :
180 : // If the PC is within JSEntry but we haven't set up the frame yet, then we
181 : // cannot unwind.
182 28342 : TEST(Unwind_JSEntryBeforeFrame_Fail) {
183 5 : LocalContext env;
184 5 : v8::Isolate* isolate = env->GetIsolate();
185 :
186 5 : UnwindState unwind_state = isolate->GetUnwindState();
187 : RegisterState register_state;
188 :
189 : const size_t code_length = 40;
190 5 : uintptr_t code[code_length] = {0};
191 5 : unwind_state.code_range.start = code;
192 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
193 :
194 : // Pretend that it takes 5 instructions to set up the frame in JSEntry.
195 5 : unwind_state.js_entry_stub.code.start = code + 10;
196 5 : unwind_state.js_entry_stub.code.length_in_bytes = 10 * sizeof(uintptr_t);
197 :
198 : uintptr_t stack[10];
199 : void* stack_base = stack + arraysize(stack);
200 5 : stack[0] = 101;
201 5 : stack[1] = 111;
202 5 : stack[2] = 121;
203 5 : stack[3] = 131;
204 5 : stack[4] = 141;
205 5 : stack[5] = 151;
206 5 : stack[6] = 100; // Return address into C++ code.
207 5 : stack[7] = 303; // The SP points here in the caller's frame.
208 5 : stack[8] = 404;
209 5 : stack[9] = 505;
210 :
211 5 : register_state.sp = stack + 5;
212 5 : register_state.fp = stack + 9;
213 :
214 : // Put the current PC inside of JSEntry, before the frame is set up.
215 5 : register_state.pc = code + 12;
216 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
217 5 : stack_base);
218 5 : CHECK(!unwound);
219 : // The register state should not change when unwinding fails.
220 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
221 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 5), register_state.sp);
222 5 : CHECK_EQ(code + 12, register_state.pc);
223 :
224 : // Change the PC to a few instructions later, after the frame is set up.
225 5 : register_state.pc = code + 16;
226 : unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
227 5 : stack_base);
228 : // TODO(petermarshall): More precisely check position within JSEntry rather
229 : // than just assuming the frame is unreadable.
230 5 : CHECK(!unwound);
231 : // The register state should not change when unwinding fails.
232 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
233 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 5), register_state.sp);
234 5 : CHECK_EQ(code + 16, register_state.pc);
235 5 : }
236 :
237 28342 : TEST(Unwind_OneJSFrame_Success) {
238 5 : LocalContext env;
239 5 : v8::Isolate* isolate = env->GetIsolate();
240 :
241 5 : UnwindState unwind_state = isolate->GetUnwindState();
242 : RegisterState register_state;
243 :
244 : // Use a fake code range so that we can initialize it to 0s.
245 : const size_t code_length = 40;
246 5 : uintptr_t code[code_length] = {0};
247 5 : unwind_state.code_range.start = code;
248 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
249 :
250 : // Our fake stack has two frames - one C++ frame and one JS frame (on top).
251 : // The stack grows from high addresses to low addresses.
252 : uintptr_t stack[10];
253 : void* stack_base = stack + arraysize(stack);
254 5 : stack[0] = 101;
255 5 : stack[1] = 111;
256 5 : stack[2] = 121;
257 5 : stack[3] = 131;
258 5 : stack[4] = 141;
259 5 : stack[5] = reinterpret_cast<uintptr_t>(stack + 9); // saved FP (rbp).
260 5 : stack[6] = 100; // Return address into C++ code.
261 5 : stack[7] = 303; // The SP points here in the caller's frame.
262 5 : stack[8] = 404;
263 5 : stack[9] = 505;
264 :
265 5 : register_state.sp = stack;
266 5 : register_state.fp = stack + 5;
267 :
268 : // Put the current PC inside of the code range so it looks valid.
269 5 : register_state.pc = code + 30;
270 :
271 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
272 5 : stack_base);
273 :
274 5 : CHECK(unwound);
275 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
276 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 7), register_state.sp);
277 5 : CHECK_EQ(reinterpret_cast<void*>(100), register_state.pc);
278 5 : }
279 :
280 : // Creates a fake stack with two JS frames on top of a C++ frame and checks that
281 : // the unwinder correctly unwinds past the JS frames and returns the C++ frame's
282 : // details.
283 28342 : TEST(Unwind_TwoJSFrames_Success) {
284 5 : LocalContext env;
285 5 : v8::Isolate* isolate = env->GetIsolate();
286 :
287 5 : UnwindState unwind_state = isolate->GetUnwindState();
288 : RegisterState register_state;
289 :
290 : // Use a fake code range so that we can initialize it to 0s.
291 : const size_t code_length = 40;
292 5 : uintptr_t code[code_length] = {0};
293 5 : unwind_state.code_range.start = code;
294 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
295 :
296 : // Our fake stack has three frames - one C++ frame and two JS frames (on top).
297 : // The stack grows from high addresses to low addresses.
298 : uintptr_t stack[10];
299 : void* stack_base = stack + arraysize(stack);
300 5 : stack[0] = 101;
301 5 : stack[1] = 111;
302 5 : stack[2] = reinterpret_cast<uintptr_t>(stack + 5); // saved FP (rbp).
303 : // The fake return address is in the JS code range.
304 5 : stack[3] = reinterpret_cast<uintptr_t>(code + 10);
305 5 : stack[4] = 141;
306 5 : stack[5] = reinterpret_cast<uintptr_t>(stack + 9); // saved FP (rbp).
307 5 : stack[6] = 100; // Return address into C++ code.
308 5 : stack[7] = 303; // The SP points here in the caller's frame.
309 5 : stack[8] = 404;
310 5 : stack[9] = 505;
311 :
312 5 : register_state.sp = stack;
313 5 : register_state.fp = stack + 2;
314 :
315 : // Put the current PC inside of the code range so it looks valid.
316 5 : register_state.pc = code + 30;
317 :
318 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
319 5 : stack_base);
320 :
321 5 : CHECK(unwound);
322 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
323 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 7), register_state.sp);
324 5 : CHECK_EQ(reinterpret_cast<void*>(100), register_state.pc);
325 5 : }
326 :
327 : // If the PC is in JSEntry then the frame might not be set up correctly, meaning
328 : // we can't unwind the stack properly.
329 28342 : TEST(Unwind_JSEntry_Fail) {
330 5 : LocalContext env;
331 5 : v8::Isolate* isolate = env->GetIsolate();
332 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
333 :
334 5 : UnwindState unwind_state = isolate->GetUnwindState();
335 : RegisterState register_state;
336 :
337 5 : Code js_entry = i_isolate->heap()->builtin(Builtins::kJSEntry);
338 5 : byte* start = reinterpret_cast<byte*>(js_entry->InstructionStart());
339 5 : register_state.pc = start + 10;
340 :
341 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
342 5 : unlimited_stack_base);
343 5 : CHECK(!unwound);
344 : // The register state should not change when unwinding fails.
345 5 : CHECK_NULL(register_state.fp);
346 5 : CHECK_NULL(register_state.sp);
347 5 : CHECK_EQ(start + 10, register_state.pc);
348 5 : }
349 :
350 28342 : TEST(Unwind_StackBounds_Basic) {
351 5 : LocalContext env;
352 5 : v8::Isolate* isolate = env->GetIsolate();
353 :
354 5 : UnwindState unwind_state = isolate->GetUnwindState();
355 : RegisterState register_state;
356 :
357 : const size_t code_length = 10;
358 5 : uintptr_t code[code_length] = {0};
359 5 : unwind_state.code_range.start = code;
360 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
361 :
362 : uintptr_t stack[3];
363 5 : stack[0] = reinterpret_cast<uintptr_t>(stack + 2); // saved FP (rbp).
364 5 : stack[1] = 202; // Return address into C++ code.
365 5 : stack[2] = 303; // The SP points here in the caller's frame.
366 :
367 5 : register_state.sp = stack;
368 5 : register_state.fp = stack;
369 5 : register_state.pc = code;
370 :
371 : void* wrong_stack_base = reinterpret_cast<void*>(
372 5 : reinterpret_cast<uintptr_t>(stack) - sizeof(uintptr_t));
373 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
374 5 : wrong_stack_base);
375 5 : CHECK(!unwound);
376 :
377 : // Correct the stack base and unwinding should succeed.
378 : void* correct_stack_base = stack + arraysize(stack);
379 : unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
380 5 : correct_stack_base);
381 5 : CHECK(unwound);
382 5 : }
383 :
384 28342 : TEST(Unwind_StackBounds_WithUnwinding) {
385 5 : LocalContext env;
386 5 : v8::Isolate* isolate = env->GetIsolate();
387 :
388 5 : UnwindState unwind_state = isolate->GetUnwindState();
389 : RegisterState register_state;
390 :
391 : // Use a fake code range so that we can initialize it to 0s.
392 : const size_t code_length = 40;
393 5 : uintptr_t code[code_length] = {0};
394 5 : unwind_state.code_range.start = code;
395 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
396 :
397 : // Our fake stack has two frames - one C++ frame and one JS frame (on top).
398 : // The stack grows from high addresses to low addresses.
399 : uintptr_t stack[11];
400 : void* stack_base = stack + arraysize(stack);
401 5 : stack[0] = 101;
402 5 : stack[1] = 111;
403 5 : stack[2] = 121;
404 5 : stack[3] = 131;
405 5 : stack[4] = 141;
406 5 : stack[5] = reinterpret_cast<uintptr_t>(stack + 9); // saved FP (rbp).
407 5 : stack[6] = reinterpret_cast<uintptr_t>(code + 20); // JS code.
408 5 : stack[7] = 303; // The SP points here in the caller's frame.
409 5 : stack[8] = 404;
410 5 : stack[9] = reinterpret_cast<uintptr_t>(stack) +
411 5 : (12 * sizeof(uintptr_t)); // saved FP (OOB).
412 5 : stack[10] = reinterpret_cast<uintptr_t>(code + 20); // JS code.
413 :
414 5 : register_state.sp = stack;
415 5 : register_state.fp = stack + 5;
416 :
417 : // Put the current PC inside of the code range so it looks valid.
418 5 : register_state.pc = code + 30;
419 :
420 : // Unwind will fail because stack[9] FP points outside of the stack.
421 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
422 5 : stack_base);
423 5 : CHECK(!unwound);
424 :
425 : // Change the return address so that it is not in range.
426 5 : stack[10] = 202;
427 : unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
428 5 : stack_base);
429 5 : CHECK(!unwound);
430 5 : }
431 :
432 28342 : TEST(PCIsInV8_BadState_Fail) {
433 5 : UnwindState unwind_state;
434 : void* pc = nullptr;
435 :
436 5 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
437 5 : }
438 :
439 28342 : TEST(PCIsInV8_ValidStateNullPC_Fail) {
440 5 : LocalContext env;
441 5 : v8::Isolate* isolate = env->GetIsolate();
442 :
443 5 : UnwindState unwind_state = isolate->GetUnwindState();
444 : void* pc = nullptr;
445 :
446 5 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
447 5 : }
448 :
449 10 : void TestRangeBoundaries(const UnwindState& unwind_state, byte* range_start,
450 : size_t range_length) {
451 10 : void* pc = range_start - 1;
452 10 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
453 : pc = range_start;
454 10 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
455 10 : pc = range_start + 1;
456 10 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
457 10 : pc = range_start + range_length - 1;
458 10 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
459 10 : pc = range_start + range_length;
460 10 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
461 10 : pc = range_start + range_length + 1;
462 10 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
463 10 : }
464 :
465 28342 : TEST(PCIsInV8_InCodeOrEmbeddedRange) {
466 5 : LocalContext env;
467 5 : v8::Isolate* isolate = env->GetIsolate();
468 :
469 5 : UnwindState unwind_state = isolate->GetUnwindState();
470 :
471 : byte* code_range_start = const_cast<byte*>(
472 5 : reinterpret_cast<const byte*>(unwind_state.code_range.start));
473 5 : size_t code_range_length = unwind_state.code_range.length_in_bytes;
474 5 : TestRangeBoundaries(unwind_state, code_range_start, code_range_length);
475 :
476 : byte* embedded_range_start = const_cast<byte*>(
477 5 : reinterpret_cast<const byte*>(unwind_state.embedded_code_range.start));
478 : size_t embedded_range_length =
479 5 : unwind_state.embedded_code_range.length_in_bytes;
480 : TestRangeBoundaries(unwind_state, embedded_range_start,
481 5 : embedded_range_length);
482 5 : }
483 :
484 : // PCIsInV8 doesn't check if the PC is in JSEntrydirectly. It's assumed that the
485 : // CodeRange or EmbeddedCodeRange contain JSEntry.
486 28342 : TEST(PCIsInV8_InJSEntryRange) {
487 5 : LocalContext env;
488 5 : v8::Isolate* isolate = env->GetIsolate();
489 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
490 :
491 5 : UnwindState unwind_state = isolate->GetUnwindState();
492 :
493 5 : Code js_entry = i_isolate->heap()->builtin(Builtins::kJSEntry);
494 5 : byte* start = reinterpret_cast<byte*>(js_entry->InstructionStart());
495 5 : size_t length = js_entry->InstructionSize();
496 :
497 : void* pc = start;
498 5 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
499 5 : pc = start + 1;
500 5 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
501 5 : pc = start + length - 1;
502 5 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
503 5 : }
504 :
505 : // Large code objects can be allocated in large object space. Check that this is
506 : // inside the CodeRange.
507 28342 : TEST(PCIsInV8_LargeCodeObject) {
508 5 : FLAG_allow_natives_syntax = true;
509 5 : LocalContext env;
510 5 : v8::Isolate* isolate = env->GetIsolate();
511 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
512 : HandleScope scope(i_isolate);
513 :
514 5 : UnwindState unwind_state = isolate->GetUnwindState();
515 :
516 : // Create a big function that ends up in CODE_LO_SPACE.
517 : const int instruction_size = Page::kPageSize + 1;
518 : STATIC_ASSERT(instruction_size > kMaxRegularHeapObjectSize);
519 5 : std::unique_ptr<byte[]> instructions(new byte[instruction_size]);
520 :
521 5 : CodeDesc desc;
522 5 : desc.buffer = instructions.get();
523 5 : desc.buffer_size = instruction_size;
524 5 : desc.instr_size = instruction_size;
525 : desc.reloc_size = 0;
526 : desc.constant_pool_size = 0;
527 : desc.unwinding_info = nullptr;
528 : desc.unwinding_info_size = 0;
529 : desc.origin = nullptr;
530 : Handle<Object> self_ref;
531 : Handle<Code> foo_code =
532 5 : i_isolate->factory()->NewCode(desc, Code::WASM_FUNCTION, self_ref);
533 :
534 5 : CHECK(i_isolate->heap()->InSpace(*foo_code, CODE_LO_SPACE));
535 5 : byte* start = reinterpret_cast<byte*>(foo_code->InstructionStart());
536 :
537 : void* pc = start;
538 10 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
539 5 : }
540 :
541 : } // namespace test_unwinder
542 : } // namespace internal
543 85011 : } // namespace v8
|