Skip to content

Commit

Permalink
library loader: use dart:ffi Abi instead of uname()
Browse files Browse the repository at this point in the history
- dart:ffi has it's own Abi class now, which you can use for
  architecture detection
- (used internally for @AbiSpecificInteger for example)
- make nativeUname() use `/proc/sys/kernel` files instead of uname binding
- make `CpuArch._internal()` use `Abi.current()` for architecture
  detection, instead of querying uname
  • Loading branch information
ardera committed May 16, 2023
1 parent c039152 commit ce352ec
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 122 deletions.
116 changes: 27 additions & 89 deletions lib/src/cpu_architecture.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import 'dart:ffi'; // For FFI
import 'dart:ffi';
import 'dart:io';

import 'package:ffi/ffi.dart';
/// Uname class, container for the Linux uname struct values.
class Uname {
String sysname;
String nodename;
String release;
String version;
String machine;
Uname(this.sysname, this.nodename, this.release, this.version, this.machine);
}

typedef NativeCall = int Function(Pointer<Int8>);
/// Calls the native uname() function.
Uname nativeUname() {
final ostype = File('/proc/sys/kernel/ostype').readAsStringSync();
final hostname = File('/proc/sys/kernel/hostname').readAsStringSync();
final osrelease = File('/proc/sys/kernel/osrelease').readAsStringSync();
final version = File('/proc/sys/kernel/version').readAsStringSync();

/// Supported CPU architectures
enum CpuArchitecture { x86, x86_64, arm, arm64, notSupported, undefined }
// Kernels since 6.1 also have `/proc/sys/kernel/arch`.
final machine = (Process.runSync('uname', ['-m']).stdout as String).trim();

final DynamicLibrary nativeAddLib = DynamicLibrary.open("libc.so.6");
NativeCall uname = nativeAddLib
.lookup<NativeFunction<Int32 Function(Pointer<Int8>)>>("uname")
.asFunction();
return Uname(ostype, hostname, osrelease, version, machine);
}

// https://en.wikipedia.org/wiki/Uname
enum CpuArchitecture { x86, x86_64, arm, arm64, notSupported, undefined }

/// Class which holds the CPU architecture of the SoC.
class CpuArch {
static CpuArch? _cpuArch;
String machine;
Expand All @@ -28,92 +39,19 @@ class CpuArch {
CpuArch._internal()
: machine = "",
cpuArch = CpuArchitecture.notSupported {
Uname uname = nativeUname();
machine = uname.machine;
switch (uname.machine) {
case 'i686':
case 'i386':
switch (Abi.current()) {
case Abi.linuxIA32:
cpuArch = CpuArchitecture.x86;
break;
case 'x86_64':
case Abi.linuxX64:
cpuArch = CpuArchitecture.x86_64;
break;
case 'aarch64':
case 'aarch64_be':
case 'arm64':
case 'armv8b':
case 'armv8l':
case Abi.linuxArm64:
cpuArch = CpuArchitecture.arm64;
break;
case 'armv':
case 'armv6l':
case 'armv7l':
case Abi.linuxArm:
cpuArch = CpuArchitecture.arm;
break;
}
}
}

/// Uname class, container for the Linux uname struct values.
class Uname {
String sysname;
String nodename;
String release;
String version;
String machine;
Uname(this.sysname, this.nodename, this.release, this.version, this.machine);
}

/// Calls the native uname() function.
Uname nativeUname() {
// allocate a memory buffer for struct utsname - size value derived from this source
// https://man7.org/linux/man-pages/man2/uname.2.html
const len = 6 * 257; // maxium size
const enumElements = 5;

Pointer<Int8> data = calloc<Int8>(len);

try {
if (uname(data) != 0) {
throw Exception('Calling uname() failed.');
}

// calculate _UTSNAME_LENGTH
var utslen = 0;
label:
for (int i = 0; i < len; ++i) {
if (data[i] == 0) {
for (int j = i + 1; j < len; ++j) {
if (data[j] != 0) {
utslen = j;
break label;
}
}
}
}

var values = <String>[];

// extract these 5 strings from the memory
//
// char sysname[]; /* Operating system name (e.g., "Linux") */
// char nodename[]; /* Name within "some implementation-defined network" */
// char release[]; /* Operating system release (e.g., "2.6.28") */
// char version[]; /* Operating system version */
// char machine[]; /* Hardware identifier */
for (int i = 0; i < enumElements; ++i) {
var start = utslen * i;
StringBuffer buf = StringBuffer();
for (int i = start; i < len; ++i) {
if (data[i] == 0) {
break;
}
buf.write(String.fromCharCode(data[i]));
}
values.add(buf.toString());
}
return Uname(values[0], values[1], values[2], values[3], values[4]);
} finally {
malloc.free(data);
}
}
63 changes: 30 additions & 33 deletions lib/src/library.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';

import 'package:dart_periphery/src/cpu_architecture.dart';

import 'native/lib_base64.dart';

const pkgName = 'dart_periphery';
Expand Down Expand Up @@ -51,43 +49,42 @@ void setCustomLibrary(String absolutePath) {
_peripheryLibPath = absolutePath;
}

const libraryNameForAbi = {
Abi.linuxIA32: 'libperiphery_x86.so',
Abi.linuxX64: 'libperiphery_x86_64.so',
Abi.linuxArm: 'libperiphery_arm.so',
Abi.linuxArm64: 'libperiphery_arm64.so',
};

bool _abiSupported(Abi abi) {
return libraryNameForAbi.containsKey(abi);
}

/// Bypasses the autodetection of the CPU architecture.
void setCPUarchitecture(CpuArchitecture arch) {
if (arch == CpuArchitecture.notSupported ||
arch == CpuArchitecture.undefined) {
void setCPUarchitecture(Abi abi) {
if (_abiSupported(abi)) {
throw LibraryException(
LibraryErrorCode.invalidParameter, "Invalid parameter");
}
var cpu = arch.toString();
cpu = cpu.substring(cpu.indexOf(".") + 1).toLowerCase();
library = 'libperiphery_$cpu.so';

library = libraryNameForAbi[abi]!;
}

String _autoDetectCPUarch() {
CpuArch arch = CpuArch();
if (arch.cpuArch == CpuArchitecture.notSupported) {
String _autoDetectCPUarch([Abi? abi]) {
abi ??= Abi.current();

if (_abiSupported(abi)) {
throw LibraryException(LibraryErrorCode.cpuArchDetectionFailed,
"Unable to detect CPU architecture, found '${arch.machine}' . Use 'setCustomLibrary(String absolutePath)' - see documentation https://github.com/pezi/dart_periphery, or create an issue https://github.com/pezi/dart_periphery/issues");
"Unable to detect CPU architecture, found '$abi' . Use 'setCustomLibrary(String absolutePath)' - see documentation https://github.com/pezi/dart_periphery, or create an issue https://github.com/pezi/dart_periphery/issues");
}
var cpu = arch.cpuArch.toString();
cpu = cpu.substring(cpu.indexOf(".") + 1).toLowerCase();
return 'libperiphery_$cpu.so';

return libraryNameForAbi[abi]!;
}

/// dart_periphery loads the library from the actual directory.
/// See [native-libraries](https://pub.dev/packages/dart_periphery#native-libraries) for details.
void useLocalLibrary([CpuArchitecture arch = CpuArchitecture.undefined]) {
if (arch == CpuArchitecture.undefined) {
_peripheryLibPath = './${_autoDetectCPUarch()}';
} else {
if (arch == CpuArchitecture.notSupported) {
throw LibraryException(
LibraryErrorCode.invalidParameter, "Invalid parameter");
}
var cpu = arch.toString();
cpu = cpu.substring(cpu.indexOf(".") + 1).toLowerCase();
_peripheryLibPath = './libperiphery_$cpu.so';
}
void useLocalLibrary([Abi? abi]) {
_peripheryLibPath = './${_autoDetectCPUarch(abi)}';
}

enum LibraryErrorCode {
Expand Down Expand Up @@ -192,18 +189,18 @@ DynamicLibrary loadPeripheryLib() {
// store the appropriate in the system temp directory

String base64EncodedLib = '';
CpuArch arch = CpuArch();
switch (arch.cpuArch) {
case CpuArchitecture.arm:
final abi = Abi.current();
switch (abi) {
case Abi.linuxArm:
base64EncodedLib = arm;
break;
case CpuArchitecture.arm64:
case Abi.linuxArm64:
base64EncodedLib = arm64;
break;
case CpuArchitecture.x86:
case Abi.linuxIA32:
base64EncodedLib = x86;
break;
case CpuArchitecture.x86_64:
case Abi.linuxX64:
base64EncodedLib = x86_64;
break;
default:
Expand Down

0 comments on commit ce352ec

Please sign in to comment.