| // 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; |
| } |