From 9d9ecca96338018852263d5eaf145d3f896720fa Mon Sep 17 00:00:00 2001 From: Carl Johnsen Date: Thu, 12 Mar 2026 08:45:47 +0000 Subject: [PATCH] Made Linux native library calls compile like the Mac one for multiple architectures. --- proprietary/DiskImage/Disk/Linux.cs | 12 ++++-- .../Duplicati.Proprietary.DiskImage.csproj | 33 ++++++++++------ .../NativeLibraryWrappers/compile_linux.sh | 36 ++++++++++++++++++ .../libc_wrapper_arm32.so | Bin 0 -> 8324 bytes .../libc_wrapper_arm64.so | Bin 0 -> 9632 bytes .../NativeLibraryWrappers/libc_wrapper_x86.so | Bin 0 -> 14632 bytes .../libc_wrapper_x86_64.so | Bin 0 -> 15408 bytes .../linux_libc_wrapper.c | 0 8 files changed, 65 insertions(+), 16 deletions(-) create mode 100755 proprietary/DiskImage/NativeLibraryWrappers/compile_linux.sh create mode 100755 proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_arm32.so create mode 100755 proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_arm64.so create mode 100755 proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_x86.so create mode 100755 proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_x86_64.so rename proprietary/DiskImage/{Disk => NativeLibraryWrappers}/linux_libc_wrapper.c (100%) diff --git a/proprietary/DiskImage/Disk/Linux.cs b/proprietary/DiskImage/Disk/Linux.cs index b7c0100e5..5f2fa8f0c 100644 --- a/proprietary/DiskImage/Disk/Linux.cs +++ b/proprietary/DiskImage/Disk/Linux.cs @@ -41,11 +41,13 @@ namespace Duplicati.Proprietary.DiskImage.Disk // File open flags from private const int O_RDONLY = 0x0000; private const int O_RDWR = 0x0002; - // TODO Should be processor architecture agnostic. // O_DIRECT: bypass kernel page cache for unbuffered I/O - // Value is architecture-dependent: 0x4000 on x86_64, 0x10000 on aarch64 - // Using 0x4000 as the most common value (x86_64) - private const int O_DIRECT = 0x4000; + // Value is architecture-dependent: 0x4000 on x86/x86_64/arm32, 0x10000 on arm64 + private static int O_DIRECT => RuntimeInformation.IsOSPlatform(OSPlatform.Linux) switch + { + true when RuntimeInformation.OSArchitecture == Architecture.Arm64 => 0x10000, + _ => 0x4000 // Default to x86/x86_64/arm32 value + }; private readonly string m_devicePath; private int m_fileDescriptor = -1; @@ -484,6 +486,8 @@ namespace Duplicati.Proprietary.DiskImage.Disk #region P/Invoke Declarations // P/Invoke to the native wrapper library for ioctls + // The .NET runtime will automatically load the correct architecture-specific version + // from runtimes/linux-{arch}/native/libc_wrapper.so based on the current RID [LibraryImport("libc_wrapper.so", SetLastError = true)] internal static partial int ioctl_uint32(int fd, uint request, ref uint value); diff --git a/proprietary/DiskImage/Duplicati.Proprietary.DiskImage.csproj b/proprietary/DiskImage/Duplicati.Proprietary.DiskImage.csproj index 470c66803..7b3bb2489 100644 --- a/proprietary/DiskImage/Duplicati.Proprietary.DiskImage.csproj +++ b/proprietary/DiskImage/Duplicati.Proprietary.DiskImage.csproj @@ -16,26 +16,35 @@ + + + PreserveNewest runtimes/osx/native/libSystem_wrapper.dylib - - - - - - - - - - - + + PreserveNewest - libc_wrapper.so + runtimes/linux-x86/native/libc_wrapper.so + + + + PreserveNewest + runtimes/linux-x64/native/libc_wrapper.so + + + + PreserveNewest + runtimes/linux-arm/native/libc_wrapper.so + + + + PreserveNewest + runtimes/linux-arm64/native/libc_wrapper.so diff --git a/proprietary/DiskImage/NativeLibraryWrappers/compile_linux.sh b/proprietary/DiskImage/NativeLibraryWrappers/compile_linux.sh new file mode 100755 index 000000000..4f60a051d --- /dev/null +++ b/proprietary/DiskImage/NativeLibraryWrappers/compile_linux.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +# Compile Linux libc wrapper for multiple architectures +# Note: Unlike macOS, Linux does not support creating a single multi-arch .so file via standard tools. +# This script creates separate .so files for each architecture. + +# First, check if docker is available on PATH +if ! command -v docker &> /dev/null +then + echo "Docker is not installed. Please install Docker to continue." + exit 1 +fi + +echo "Compiling x86..." +docker run --rm dockcross/linux-x86 > dockcross && chmod +x dockcross +./dockcross /bin/bash -c '$CC -shared -fPIC -m32 -o libc_wrapper_x86.so linux_libc_wrapper.c' + +echo "Compiling x64..." +docker run --rm dockcross/linux-x64 > dockcross && chmod +x dockcross +./dockcross /bin/bash -c '$CC -shared -fPIC -m64 -o libc_wrapper_x86_64.so linux_libc_wrapper.c' + +echo "Compiling ARM32..." +docker run --rm dockcross/linux-armv7 > dockcross && chmod +x dockcross +./dockcross /bin/bash -c '$CC -shared -fPIC -march=armv7-a -o libc_wrapper_arm32.so linux_libc_wrapper.c' + +echo "Compiling ARM64..." +docker run --rm dockcross/linux-arm64 > dockcross && chmod +x dockcross +./dockcross /bin/bash -c '$CC -shared -fPIC -march=armv8-a -o libc_wrapper_arm64.so linux_libc_wrapper.c' + +chmod 777 libc_wrapper_x86.so +chmod 777 libc_wrapper_x86_64.so +chmod 777 libc_wrapper_arm32.so +chmod 777 libc_wrapper_arm64.so +rm dockcross +echo "Compilation complete. Generated .so files for x86, x86_64, ARM32, and ARM64." \ No newline at end of file diff --git a/proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_arm32.so b/proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_arm32.so new file mode 100755 index 0000000000000000000000000000000000000000..97a2c5b13f3638a655840612c0b231c50a6b144c GIT binary patch literal 8324 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 literal 0 HcmV?d00001 diff --git a/proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_x86.so b/proprietary/DiskImage/NativeLibraryWrappers/libc_wrapper_x86.so new file mode 100755 index 0000000000000000000000000000000000000000..462fe4cc7f02708f9f3029d36facd1074a29b77b GIT binary patch literal 14632 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