blob: b0fb8e2c998e93672d22b6cb7a17580d98d14078 [file] [log] [blame]
// Copyright (c) 2019, 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
// @dart = 2.9
import 'dart:ffi';
import 'dart:io';
import 'package:expect/expect.dart';
import 'ffi_test_helpers.dart';
// void* mmap(void* addr, size_t length,
// int prot, int flags,
// int fd, off_t offset)
typedef MMapNative = Pointer<Uint8> Function(Pointer<Uint8> address, IntPtr len,
IntPtr prot, IntPtr flags, IntPtr fd, IntPtr offset);
typedef MMap = Pointer<Uint8> Function(
Pointer<Uint8> address, int len, int prot, int flags, int fd, int offset);
final mmap = processSymbols.lookupFunction<MMapNative, MMap>('mmap');
// int munmap(void *addr, size_t length)
typedef MUnMapNative = IntPtr Function(Pointer<Uint8> address, IntPtr len);
typedef MUnMap = int Function(Pointer<Uint8> address, int len);
final munmap = processSymbols.lookupFunction<MUnMapNative, MUnMap>('munmap');
final processSymbols = DynamicLibrary.process();
const int kProtRead = 1;
const int kProtWrite = 2;
const int kMapPrivate = 2;
const int kMapFixed = 16;
final int kMapAnonymous = Platform.isMacOS ? 0x1000 : 0x20;
const int kMapFailed = -1;
// On 32-bit platforms the upper 4 bytes should be ignored.
const int kIgnoreBytesPositive = 0x1122334400000000;
const int kIgnoreBytesNegative = 0xffddccbb00000000;
void swapBytes(
Pointer<Uint8> memoryView, int indexOffset, int indexA, int indexB) {
final int oldA = memoryView[indexOffset + indexA];
memoryView[indexOffset + indexA] = memoryView[indexOffset + indexB];
memoryView[indexOffset + indexB] = oldA;
}
void testLoadsAndStores(int indexOffset, Pointer<Uint8> memory) {
for (int i = 0; i < 10; ++i) {
memory[indexOffset + i] = 10 + i;
}
Expect.listEquals(<int>[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
memory.offsetBy(indexOffset).asTypedList(10));
for (int i = 0; i < 9; ++i) {
swapBytes(memory, indexOffset + 0, i, i + 1);
}
Expect.listEquals(<int>[11, 12, 13, 14, 15, 16, 17, 18, 19, 10],
memory.offsetBy(indexOffset).asTypedList(10));
for (int i = 0; i < 9; ++i) {
swapBytes(memory, indexOffset + kIgnoreBytesPositive, i, i + 1);
}
Expect.listEquals(<int>[12, 13, 14, 15, 16, 17, 18, 19, 10, 11],
memory.offsetBy(indexOffset).asTypedList(10));
for (int i = 0; i < 9; ++i) {
swapBytes(memory, indexOffset + kIgnoreBytesNegative, i, i + 1);
}
Expect.listEquals(<int>[13, 14, 15, 16, 17, 18, 19, 10, 11, 12],
memory.offsetBy(indexOffset).asTypedList(10));
}
void testOnHighOrLowMemory(Pointer<Uint8> memory, int indexOffset) {
testLoadsAndStores(indexOffset, memory);
testLoadsAndStores(indexOffset, memory.offsetBy(5));
testLoadsAndStores(indexOffset, memory.offsetBy(-5));
testLoadsAndStores(indexOffset, memory.offsetBy(kIgnoreBytesPositive + 5));
testLoadsAndStores(indexOffset, memory.offsetBy(kIgnoreBytesNegative + 5));
testLoadsAndStores(indexOffset, memory.offsetBy(kIgnoreBytesPositive - 5));
testLoadsAndStores(indexOffset, memory.offsetBy(kIgnoreBytesNegative - 5));
final m2 = Pointer<Uint8>.fromAddress(kIgnoreBytesPositive + memory.address);
Expect.equals(memory, m2); //# 01: ok
Expect.equals(memory.address, m2.address); //# 01: ok
testLoadsAndStores(indexOffset, m2);
final m3 = Pointer<Uint8>.fromAddress(kIgnoreBytesNegative + memory.address);
Expect.equals(memory, m3); //# 01: ok
Expect.equals(memory.address, m3.address); //# 01: ok
testLoadsAndStores(indexOffset, m3);
}
const int kPageSize = 4096;
withMMapedAddress(
Pointer<Uint8> fixedAddress, void fun(Pointer<Uint8> address)) {
final result = mmap(fixedAddress, kPageSize, kProtRead | kProtWrite,
kMapAnonymous | kMapFixed | kMapPrivate, 0, 0);
if (result.address == kMapFailed) {
throw 'Could not mmap @0x${fixedAddress.address.toRadixString(16)}!';
}
Expect.equals(fixedAddress, result); //# 01: ok
Expect.equals(fixedAddress.address, result.address); //# 01: ok
try {
fun(result);
} finally {
if (munmap(result, kPageSize) != 0) {
throw 'Failed to unmap memory!';
}
}
}
main() {
final bool is32BitProcess = sizeOf<Pointer<Uint8>>() == 4;
// User space processes usually have
// * the lower 3 GB available on Linux/Android on 32bit OS
// * full 4 GB available on Linux/Android on 64bit OS
// * full 4 GB available on MacOS
// So we choose high and low addresses that fall into lower 3 GB.
if (is32BitProcess && !Platform.isWindows) {
final highMemoryAddress = Pointer<Uint8>.fromAddress(0xaaaa0000);
final lowMemoryAddress = Pointer<Uint8>.fromAddress(0x11110000);
withMMapedAddress(lowMemoryAddress, (Pointer<Uint8> lowMemory) {
withMMapedAddress(highMemoryAddress, (Pointer<Uint8> highMemory) {
testOnHighOrLowMemory(lowMemory, 2048);
testOnHighOrLowMemory(highMemory, 2048);
testOnHighOrLowMemory(
lowMemory, 2048 + highMemory.address - lowMemory.address);
testOnHighOrLowMemory(
highMemory, 2048 + lowMemory.address - highMemory.address);
});
});
}
}