[vm/ffi] Adds transition information to FFI leaf calls.

This change sets `top_exit_frame_info` and `vmtag` on FFI leaf calls.
We have to set this info on the thread so that the stack walker can
interpret the frame correctly.
Without it, anyone trying to walk the stack during an FFI leaf call
- like the profiler - will misinterpret the top frame and cause
a segfault.

TEST=Added regression test.

Bug: https://github.com/dart-lang/sdk/issues/47594
Change-Id: If83aeab194aa0213aee82558bb9541cd7294a935
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/220360
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Clement Skau <cskau@google.com>
This commit is contained in:
Clement Skau 2021-11-29 10:12:36 +00:00 committed by Commit Bot
parent 3d34ec1dbf
commit bd4a27ff81
6 changed files with 109 additions and 0 deletions

View file

@ -1448,7 +1448,23 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
if (is_leaf_) {
#if !defined(PRODUCT)
// Set the thread object's top_exit_frame_info and VMTag to enable the
// profiler to determine that thread is no longer executing Dart code.
__ StoreToOffset(FPREG, THR,
compiler::target::Thread::top_exit_frame_info_offset());
__ StoreToOffset(branch, THR, compiler::target::Thread::vm_tag_offset());
#endif
__ blx(branch);
#if !defined(PRODUCT)
__ LoadImmediate(temp1, compiler::target::Thread::vm_tag_dart_id());
__ StoreToOffset(temp1, THR, compiler::target::Thread::vm_tag_offset());
__ LoadImmediate(temp1, 0);
__ StoreToOffset(temp1, THR,
compiler::target::Thread::top_exit_frame_info_offset());
#endif
} else {
// We need to copy the return address up into the dummy stack frame so the
// stack walker will know which safepoint to use.

View file

@ -1270,6 +1270,14 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
if (is_leaf_) {
#if !defined(PRODUCT)
// Set the thread object's top_exit_frame_info and VMTag to enable the
// profiler to determine that thread is no longer executing Dart code.
__ StoreToOffset(FPREG, THR,
compiler::target::Thread::top_exit_frame_info_offset());
__ StoreToOffset(branch, THR, compiler::target::Thread::vm_tag_offset());
#endif
// We are entering runtime code, so the C stack pointer must be restored
// from the stack limit to the top of the stack.
__ mov(R25, CSP);
@ -1280,6 +1288,13 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// Restore the Dart stack pointer.
__ mov(SP, CSP);
__ mov(CSP, R25);
#if !defined(PRODUCT)
__ LoadImmediate(temp1, compiler::target::Thread::vm_tag_dart_id());
__ StoreToOffset(temp1, THR, compiler::target::Thread::vm_tag_offset());
__ StoreToOffset(ZR, THR,
compiler::target::Thread::top_exit_frame_info_offset());
#endif
} else {
// We need to copy a dummy return address up into the dummy stack frame so
// the stack walker will know which safepoint to use.

View file

@ -1052,7 +1052,24 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
if (is_leaf_) {
#if !defined(PRODUCT)
// Set the thread object's top_exit_frame_info and VMTag to enable the
// profiler to determine that thread is no longer executing Dart code.
__ movl(compiler::Address(
THR, compiler::target::Thread::top_exit_frame_info_offset()),
FPREG);
__ movl(compiler::Assembler::VMTagAddress(), branch);
#endif
__ call(branch);
#if !defined(PRODUCT)
__ movl(compiler::Assembler::VMTagAddress(),
compiler::Immediate(compiler::target::Thread::vm_tag_dart_id()));
__ movl(compiler::Address(
THR, compiler::target::Thread::top_exit_frame_info_offset()),
compiler::Immediate(0));
#endif
} else {
// We need to copy a dummy return address up into the dummy stack frame so
// the stack walker will know which safepoint to use. Unlike X64, there's no

View file

@ -1240,7 +1240,24 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
if (is_leaf_) {
#if !defined(PRODUCT)
// Set the thread object's top_exit_frame_info and VMTag to enable the
// profiler to determine that thread is no longer executing Dart code.
__ movq(compiler::Address(
THR, compiler::target::Thread::top_exit_frame_info_offset()),
FPREG);
__ movq(compiler::Assembler::VMTagAddress(), target_address);
#endif
__ CallCFunction(target_address, /*restore_rsp=*/true);
#if !defined(PRODUCT)
__ movq(compiler::Assembler::VMTagAddress(),
compiler::Immediate(compiler::target::Thread::vm_tag_dart_id()));
__ movq(compiler::Address(
THR, compiler::target::Thread::top_exit_frame_info_offset()),
compiler::Immediate(0));
#endif
} else {
// We need to copy a dummy return address up into the dummy stack frame so
// the stack walker will know which safepoint to use. RIP points to the

View file

@ -0,0 +1,22 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Regression test for http://dartbug.com/47594.
// FFI leaf calls did not mark the thread for the transition and would cause
// the stack walker to segfault when it was unable to interpret the frame.
//
// VMOptions=--deterministic --enable-vm-service --profiler
import 'dart:ffi';
import 'package:ffi/ffi.dart';
final strerror = DynamicLibrary.process()
.lookupFunction<Pointer<Utf8> Function(Int32), Pointer<Utf8> Function(int)>(
'strerror',
isLeaf: true);
void main() {
for (var i = 0; i < 10000; i++) strerror(0).toDartString();
}

View file

@ -0,0 +1,22 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Regression test for http://dartbug.com/47594.
// FFI leaf calls did not mark the thread for the transition and would cause
// the stack walker to segfault when it was unable to interpret the frame.
//
// VMOptions=--deterministic --enable-vm-service --profiler
import 'dart:ffi';
import 'package:ffi/ffi.dart';
final strerror = DynamicLibrary.process()
.lookupFunction<Pointer<Utf8> Function(Int32), Pointer<Utf8> Function(int)>(
'strerror',
isLeaf: true);
void main() {
for (var i = 0; i < 10000; i++) strerror(0).toDartString();
}