Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

library loader: use dart:ffi Abi instead of uname() for architecture detection #22

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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