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