blob: 721f6f454538425cfcbbceb857c3841a02abeab4 [file] [log] [blame]
// Copyright (c) 2022, 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.
// ignore_for_file: camel_case_types
// ignore_for_file: deprecated_member_use
// ignore_for_file: non_constant_identifier_names
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
// int open(const char *path, int oflag, ...);
@Native<Int Function(Pointer<Utf8>, Int)>(symbol: "open")
external int open(Pointer<Utf8> filename, int flags);
// int close(int fd);
@Native<Int Function(Int)>(symbol: "close")
external int close(int fd);
// void* mmap(void* addr, size_t length,
// int prot, int flags,
// int fd, off_t offset)
@Native<Pointer<Uint8> Function(Pointer<Uint8>, Size, Int, Int, Int, IntPtr)>()
external Pointer<Uint8> mmap(
Pointer<Uint8> address, int len, int prot, int flags, int fd, int offset);
// int munmap(void *addr, size_t length)
@Native<IntPtr Function(Pointer<Uint8> address, Size len)>()
external int munmap(Pointer<Uint8> address, int len);
final DynamicLibrary processSymbols = DynamicLibrary.process();
final munmapNative = processSymbols.lookup<Void>('munmap');
final closeNative = processSymbols.lookup<Void>('close');
final freeNative = processSymbols.lookup<Void>('free');
// int mprotect(void *addr, size_t len, int prot)
@Native<Int Function(Pointer<Uint8>, Size, Int)>(symbol: "mprotect")
external int mprotect(Pointer<Uint8> addr, int len, int prot);
// DART_EXPORT Dart_Handle
// Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,
// void* data,
// intptr_t length,
// void* peer,
// intptr_t external_allocation_size,
// Dart_HandleFinalizer callback)
typedef Dart_NewExternalTypedDataWithFinalizerNative = Handle Function(
Int, Pointer<Void>, IntPtr, Pointer<Void>, IntPtr, Pointer<Void>);
typedef Dart_NewExternalTypedDataWithFinalizerDart = Object Function(
int, Pointer<Void>, int, Pointer<Void>, int, Pointer<Void>);
final Dart_NewExternalTypedDataWithFinalizer = processSymbols.lookupFunction<
Dart_NewExternalTypedDataWithFinalizerNative,
Dart_NewExternalTypedDataWithFinalizerDart>(
'Dart_NewExternalTypedDataWithFinalizer');
const int kPageSize = 4096;
const int kProtRead = 1;
const int kProtWrite = 2;
const int kProtExec = 4;
const int kMapPrivate = 2;
const int kMapAnon = 0x20;
const int kMapFailed = -1;
// #include <cstddef>
// #include <cstdint>
//
// struct PeerData {
// int (*close)(int);
// int (*munmap)(void*, size_t);
// int (*free)(void*);
// void* mapping;
// intptr_t size;
// intptr_t fd;
// };
//
// extern "C" void finalizer(void* callback_data, void* peer) {
// auto data = static_cast<PeerData*>(peer);
// data->munmap(data->mapping, data->size);
// data->close(data->fd);
// data->free(peer);
// }
//
// riscv64-linux-gnu-g++ -O2 -c -o a.o a.cc
// riscv64-linux-gnu-objcopy -O binary --only-section=.text a.o a.bin
// xxd -i a.bin
const finalizerCode = <Abi, List<int>>{
Abi.linuxX64: [
0x53, 0x48, 0x89, 0xf3, 0x48, 0x8b, 0x76, 0x20, 0x48, 0x8b, 0x7b, 0x18, //
0xff, 0x53, 0x08, 0x8b, 0x7b, 0x28, 0xff, 0x13, 0x48, 0x8b, 0x43, 0x10, //
0x48, 0x89, 0xdf, 0x5b, 0xff, 0xe0, //
],
Abi.linuxIA32: [
0x53, 0x83, 0xec, 0x10, 0x8b, 0x5c, 0x24, 0x1c, 0xff, 0x73, 0x10, 0xff, //
0x73, 0x0c, 0xff, 0x53, 0x04, 0x58, 0xff, 0x73, 0x14, 0xff, 0x13, 0x89, //
0x5c, 0x24, 0x20, 0x8b, 0x43, 0x08, 0x83, 0xc4, 0x18, 0x5b, 0xff, 0xe0, //
],
Abi.linuxArm64: [
0xfd, 0x7b, 0xbe, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0x22, 0x04, 0x40, 0xf9, //
0xf3, 0x0b, 0x00, 0xf9, 0xf3, 0x03, 0x01, 0xaa, 0x20, 0x84, 0x41, 0xa9, //
0x40, 0x00, 0x3f, 0xd6, 0x61, 0x02, 0x40, 0xf9, 0x60, 0x2a, 0x40, 0xb9, //
0x20, 0x00, 0x3f, 0xd6, 0x61, 0x0a, 0x40, 0xf9, 0xe0, 0x03, 0x13, 0xaa, //
0xf3, 0x0b, 0x40, 0xf9, 0xf0, 0x03, 0x01, 0xaa, 0xfd, 0x7b, 0xc2, 0xa8, //
0x00, 0x02, 0x1f, 0xd6, //
],
Abi.linuxArm: [
0x10, 0x40, 0x2d, 0xe9, 0x01, 0x40, 0xa0, 0xe1, 0x10, 0x10, 0x91, 0xe5, //
0x04, 0x30, 0x94, 0xe5, 0x0c, 0x00, 0x94, 0xe5, 0x33, 0xff, 0x2f, 0xe1, //
0x00, 0x30, 0x94, 0xe5, 0x14, 0x00, 0x94, 0xe5, 0x33, 0xff, 0x2f, 0xe1, //
0x08, 0x30, 0x94, 0xe5, 0x04, 0x00, 0xa0, 0xe1, 0x10, 0x40, 0xbd, 0xe8, //
0x13, 0xff, 0x2f, 0xe1, //
],
Abi.linuxRiscv64: [
0x41, 0x11, 0x22, 0xe0, 0x2e, 0x84, 0x1c, 0x64, 0x8c, 0x71, 0x08, 0x6c, //
0x06, 0xe4, 0x82, 0x97, 0x1c, 0x60, 0x08, 0x54, 0x82, 0x97, 0x1c, 0x68, //
0x22, 0x85, 0x02, 0x64, 0xa2, 0x60, 0x41, 0x01, 0x82, 0x87, //
],
Abi.linuxRiscv32: [
0x41, 0x11, 0x22, 0xc4, 0x2e, 0x84, 0x5c, 0x40, 0x8c, 0x49, 0x48, 0x44, //
0x06, 0xc6, 0x82, 0x97, 0x1c, 0x40, 0x48, 0x48, 0x82, 0x97, 0x1c, 0x44, //
0x22, 0x85, 0x22, 0x44, 0xb2, 0x40, 0x41, 0x01, 0x82, 0x87, //
],
};
// We need to attach the finalizer which calls close() and munmap().
final finalizerAddress = () {
// UBSAN will dereference callback-8 to get typeinfo to check for matching
// types at the call site for the finalizer callback. Make that slot
// addressable and leave it initialized to NULL.
final offset = 8;
final Pointer<Uint8> finalizerStub = mmap(nullptr, kPageSize,
kProtRead | kProtWrite, kMapPrivate | kMapAnon, -1, 0);
finalizerStub
.cast<Uint8>()
.asTypedList(kPageSize)
.setAll(offset, finalizerCode[Abi.current()]!);
if (mprotect(finalizerStub, kPageSize, kProtRead | kProtExec) != 0) {
throw 'Failed to write executable code to the memory.';
}
return (finalizerStub + offset).cast<Void>();
}();
base class PeerData extends Struct {
external Pointer<Void> close;
external Pointer<Void> munmap;
external Pointer<Void> free;
external Pointer<Uint8> mapping;
@IntPtr()
external int size;
@IntPtr()
external int fd;
}
Uint8List toExternalDataWithFinalizer(
Pointer<Uint8> memory, int size, int length, int fd) {
final Pointer<PeerData> peer = malloc.allocate<PeerData>(sizeOf<PeerData>());
peer.ref.close = closeNative;
peer.ref.munmap = munmapNative;
peer.ref.free = freeNative;
peer.ref.mapping = memory;
peer.ref.size = size;
peer.ref.fd = fd;
return Dart_NewExternalTypedDataWithFinalizer(
/*Dart_TypedData_kUint8*/ 2,
memory.cast(),
length,
peer.cast(),
size,
finalizerAddress,
) as Uint8List;
}