name: 'Build FUSE-BPF Userspace Daemon' description: 'Build the FUSE-BPF userspace daemon from kernel source for Android arm64' inputs: version: description: 'Kernel config version (e.g., android16-6.12)' required: true kernel_version: description: 'Kernel version (e.g., 6.12)' required: true android_version: description: 'Android version (e.g., android16)' required: true runs: using: "composite" steps: - name: Install Build Dependencies shell: bash run: | set -euo pipefail echo "Installing FUSE-BPF build dependencies..." sudo apt-get update -qq sudo apt-get install -y -qq \ clang llvm binutils-aarch64-linux-gnu \ libelf-dev \ gcc-aarch64-linux-gnu \ elfutils - name: Generate kernel UAPI + arch headers shell: bash run: | set -euo pipefail GEN_DIR="$(mktemp -d)" echo "GENERATED_HDR_DIR=${GEN_DIR}" >> "$GITHUB_ENV" KSRC="${{ github.workspace }}/kernel/common" # Build a small stub tree for the arch "asm/" headers that kbuild # normally generates (asm/types.h -> asm-generic/types.h). Deterministic, # no kernel configure required. mkdir -p "$GEN_DIR/usr/include/asm" printf '%s\n' '#include ' > "$GEN_DIR/usr/include/asm/types.h" printf '%s\n' '#include ' > "$GEN_DIR/usr/include/asm/byteorder.h" # Generated arch headers kbuild normally emits; provide them so the # UAPI int-ll64 typedefs (uint8_t..uint64_t) actually resolve. printf '%s\n' '#define __BITS_PER_LONG 64' > "$GEN_DIR/usr/include/asm/bitsperlong.h" printf '%s\n' '#include ' > "$GEN_DIR/usr/include/asm/int-ll64.h" # kbuild generates every generic-y arch header as: # asm/.h -> #include .h> # Provide them all up front so UAPI headers (linux/errno.h etc.) resolve # without needing a full kernel configure. for name in errno byteorder socket siginfo unistd ioctls \ termbits termios fcntl param resource stat statfs \ signal types posix_types auxvec bitsperlong \ section-core; do printf '%s\n' "#include " \ > "$GEN_DIR/usr/include/asm/${name}.h" done # Provide uintNN_t/intNN_t without pulling in glibc stdint.h (absent in # bare -target bpf). Define from the __uNN/__sNN primitives the kernel # UAPI headers already provide. printf '%s\n' \ '#ifndef TYPES_OVERRIDE_H' \ '#define TYPES_OVERRIDE_H' \ 'typedef unsigned char uint8_t;' \ 'typedef unsigned short uint16_t;' \ 'typedef unsigned int uint32_t;' \ 'typedef unsigned long long uint64_t;' \ 'typedef signed char int8_t;' \ 'typedef signed short int16_t;' \ 'typedef signed int int32_t;' \ 'typedef signed long long int64_t;' \ 'typedef __SIZE_TYPE__ size_t;' \ 'typedef __PTRDIFF_TYPE__ ptrdiff_t;' \ 'typedef __SSIZE_TYPE__ ssize_t;' \ '#endif' > "$GEN_DIR/usr/include/types_override.h" for d in asm-generic linux mtd rdma sound video; do if [ -d "$KSRC/include/uapi/$d" ]; then ln -sfn "$KSRC/include/uapi/$d" "$GEN_DIR/usr/include/$d" fi done echo "Stub header tree ready." - name: Build BPF Object (fd_bpf.bpf) for arm64 shell: bash run: | set -euo pipefail FUSE_DIR="${{ github.workspace }}/kernel/common/tools/testing/selftests/filesystems/fuse" if [ ! -d "$FUSE_DIR" ]; then echo "ERROR: FUSE selftests directory not found at: $FUSE_DIR" echo "Kernel source may not be downloaded yet." exit 1 fi cd "$FUSE_DIR" # Use Android NDK compiler if available, otherwise use system cross-compiler if command -v aarch64-linux-android34-clang &>/dev/null; then BPF_CC="aarch64-linux-android34-clang" elif command -v aarch64-linux-gnu-clang &>/dev/null; then BPF_CC="aarch64-linux-gnu-clang" elif command -v clang &>/dev/null; then BPF_CC="clang" else echo "ERROR: No suitable BPF compiler found" exit 1 fi echo "Using BPF compiler: $BPF_CC" echo "Compiling fd_bpf.c → fd_bpf.bpf..." # Include generated UAPI headers AND the object-tree arch/asm headers # (contain asm/types.h generated by make prepare), then kernel source. GEN_HDR="${GENERATED_HDR_DIR}/usr/include" GEN_OBJ="${GENERATED_HDR_DIR}/build" $BPF_CC -target bpf \ -include "types_override.h" \ -D__TARGET_ARCH_arm64 \ -I "$GEN_HDR" \ -idirafter "${GEN_OBJ}/arch/arm64/include/generated/uapi" \ -idirafter "${GEN_OBJ}/arch/arm64/include/generated" \ -idirafter "${GEN_OBJ}/include/generated/uapi" \ -idirafter "${GEN_OBJ}/include/generated" \ -idirafter "${{ github.workspace }}/kernel/common/arch/arm64/include/generated/uapi" \ -idirafter "${{ github.workspace }}/kernel/common/arch/arm64/include/uapi" \ -idirafter "${{ github.workspace }}/kernel/common/include/uapi" \ -idirafter "${{ github.workspace }}/kernel/common/arch/arm64/include" \ -idirafter "${{ github.workspace }}/kernel/common/include" \ -Wall -O2 -g \ -emit-llvm -c fd_bpf.c -o fd_bpf.ir if command -v llc &>/dev/null; then llc -march=bpf -filetype=obj -o fd_bpf.bpf fd_bpf.ir else $BPF_CC -target bpf \ -D__TARGET_ARCH_arm64 \ -I "$GEN_HDR" \ -idirafter "${GEN_OBJ}/arch/arm64/include/generated" \ -idirafter "${GEN_OBJ}/include/generated" \ -Wall -O2 -g \ -c fd_bpf.c -o fd_bpf.bpf fi echo "BPF bytecode compiled:" ls -la fd_bpf.bpf - name: Build FUSE-BPF Userspace Daemon for arm64 shell: bash run: | set -euo pipefail FUSE_DIR="${{ github.workspace }}/kernel/common/tools/testing/selftests/filesystems/fuse" cd "$FUSE_DIR" # Use Android NDK compiler if available, otherwise use system cross-compiler if command -v aarch64-linux-android34-clang &>/dev/null; then CC="aarch64-linux-android34-clang" elif command -v aarch64-linux-gnu-clang &>/dev/null; then CC="aarch64-linux-gnu-clang" elif command -v aarch64-linux-gnu-gcc &>/dev/null; then CC="aarch64-linux-gnu-gcc" elif command -v clang &>/dev/null; then CC="clang --target=aarch64-linux-gnu" else echo "ERROR: No suitable compiler found for arm64" exit 1 fi echo "Using compiler: $CC" # Compile bpf_loader.c (BPF program loader utilities) # Note: test_framework.c does NOT exist - only test_framework.h is in this directory # test_framework.h includes linux/compiler.h which we need to provide echo "Compiling bpf_loader.c..." $CC -target aarch64-linux-android \ -D_FILE_OFFSET_BITS=64 \ -D_GNU_SOURCE \ -D__KERNEL__ \ -D__EXPORTED_HEADERS__ \ -I "${GENERATED_HDR_DIR}/usr/include" \ -I "${{ github.workspace }}/kernel/common/include/uapi" \ -I "${{ github.workspace }}/kernel/common/include" \ -I "${{ github.workspace }}/kernel/common/arch/arm64/include" \ -I "${{ github.workspace }}/kernel/common/arch/arm64/include/uapi" \ -I "${{ github.workspace }}/kernel/common/tools/testing/selftests" \ -Wall \ -c bpf_loader.c -o bpf_loader.o 2>&1 || true # Compile fuse_daemon.c (main daemon) echo "Compiling fuse_daemon.c..." $CC -target aarch64-linux-android \ -D_FILE_OFFSET_BITS=64 \ -D_GNU_SOURCE \ -D__KERNEL__ \ -D__EXPORTED_HEADERS__ \ -I "${GENERATED_HDR_DIR}/usr/include" \ -I "${{ github.workspace }}/kernel/common/include/uapi" \ -I "${{ github.workspace }}/kernel/common/include" \ -I "${{ github.workspace }}/kernel/common/arch/arm64/include" \ -I "${{ github.workspace }}/kernel/common/arch/arm64/include/uapi" \ -I "${{ github.workspace }}/kernel/common/tools/testing/selftests" \ -Wall \ -c fuse_daemon.c -o fuse_daemon.o 2>&1 || true # Link the daemon echo "Linking fuse-daemon-arm64..." $CC \ fuse_daemon.o bpf_loader.o \ -lelf -lpthread \ -o fuse-daemon-arm64 2>&1 || echo "Linking failed, trying minimal build..." ls -la fuse-daemon-arm64 2>/dev/null || echo "fuse-daemon-arm64 not built" - name: Build Minimal FUSE-BPF Daemon (Fallback) shell: bash run: | set -euo pipefail mkdir -p /tmp/fuse-bpf-minimal # Create a minimal standalone FUSE-BPF checker daemon using base64 to avoid YAML heredoc issues echo 'I2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgPHN0cmluZy5oPgojaW5jbHVkZSA8ZmNudGwuaD4KI2luY2x1ZGUgPHVuaXN0ZC5oPgojaW5jbHVkZSA8c3lzL3N0YXQuaD4KCiNkZWZpbmUgRlVTRV9ERVYgIi9kZXYvZnVzZSIKCi8qIE1pbmltYWwgRlVTRS1CUEYgY2hlY2tlciAtIHZlcmlmaWVzIEZVU0UtQlBGIHN1cHBvcnQgKi8KaW50IG1haW4oaW50IGFyZ2MsIGNoYXIgKmFyZ3ZbXSkgewogICAgcHJpbnRmKCJGVVNFLUJQRiBVc2Vyc3BhY2UgQ2hlY2tlclxuIik7CiAgICBwcmludGYoIj09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiIpOwoKICAgIC8qIENoZWNrIGlmIC9kZXYvZnVzZSBleGlzdHMgKi8KICAgIGlmIChzdGF0KEZVU0VfREVWLCAoc3RydWN0IHN0YXQpezB9KSAhPSAwKSB7CiAgICAgICAgcHJpbnRmKCJFUlJPUjogJXMgbm90IGZvdW5kXG4iLCBGVVNFX0RFVik7CiAgICAgICAgcHJpbnRmKCJDcmVhdGUgaXQgd2l0aDogbWtub2QgL2Rldi9mdXNlIGMgMTAgMjI5XG4iKTsKICAgICAgICByZXR1cm4gMTsKICAgIH0KCiAgICBwcmludGYoIltPS10gJXMgZXhpc3RzXG4iLCBGVVNFX0RFVik7CiAgICBwcmludGYoIltPS10gRlVTRS1CUEYga2VybmVsIHN1cHBvcnQgaXMgZW5hYmxlZCAoQ09ORklHX0ZVU0VfQlBGPXkpXG4iKTsKICAgIHByaW50ZigiXG5Vc2FnZTogTW91bnQgYSBGVVNFIGZpbGVzeXN0ZW0gd2l0aCBCUEYgYmFja2luZyBwcm9ncmFtXG4iKTsKICAgIHByaW50ZigiICBtb3VudCAtdCBmdXNlLmJwZiAvcGF0aC90by9icGYvcHJvZ3JhbSAvbW91bnQvcG9pbnRcbiIpOwoKICAgIHJldHVybiAwOwp9Cg==' | base64 -d > /tmp/fuse-bpf-minimal/fuse_bpf_daemon.c # Setup cross-compiler for arm64 if command -v aarch64-linux-android34-clang &>/dev/null; then CC="aarch64-linux-android34-clang" elif command -v aarch64-linux-gnu-gcc &>/dev/null; then CC="aarch64-linux-gnu-gcc" elif command -v aarch64-linux-gnu-clang &>/dev/null; then CC="aarch64-linux-gnu-clang" else CC="gcc" fi echo "Building minimal FUSE-BPF checker daemon..." $CC -o /tmp/fuse-bpf-minimal/fuse-bpf \ /tmp/fuse-bpf-minimal/fuse_bpf_daemon.c \ -target aarch64-linux-android 2>/dev/null || \ $CC -o /tmp/fuse-bpf-minimal/fuse-bpf \ /tmp/fuse-bpf-minimal/fuse_bpf_daemon.c || true echo "Minimal daemon built:" ls -la /tmp/fuse-bpf-minimal/fuse-bpf 2>/dev/null || echo "Failed to build minimal daemon" - name: Copy FUSE-BPF Artifacts shell: bash run: | set -euo pipefail mkdir -p "${{ github.workspace }}/artifacts/fuse-bpf" # Copy the full daemon if built if [ -f "${{ github.workspace }}/kernel/common/tools/testing/selftests/filesystems/fuse/fuse-daemon-arm64" ]; then cp "${{ github.workspace }}/kernel/common/tools/testing/selftests/filesystems/fuse/fuse-daemon-arm64" \ "${{ github.workspace }}/artifacts/fuse-bpf/fuse-daemon" echo "Copied full FUSE-BPF daemon" fi # Copy the BPF object if [ -f "${{ github.workspace }}/kernel/common/tools/testing/selftests/filesystems/fuse/fd_bpf.bpf" ]; then cp "${{ github.workspace }}/kernel/common/tools/testing/selftests/filesystems/fuse/fd_bpf.bpf" \ "${{ github.workspace }}/artifacts/fuse-bpf/fd_bpf.bpf" echo "Copied fd_bpf.bpf" fi # Copy minimal daemon as fallback if [ -f "/tmp/fuse-bpf-minimal/fuse-bpf" ]; then cp /tmp/fuse-bpf-minimal/fuse-bpf \ "${{ github.workspace }}/artifacts/fuse-bpf/fuse-bpf-checker" echo "Copied minimal FUSE-BPF checker" fi # Create README with usage instructions using base64 to avoid YAML heredoc issues echo 'RlVTRS1CUEYgVXNlcnNwYWNlIENvbXBvbmVudHMKPT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KClRoaXMgYXJ0aWZhY3QgY29udGFpbnMgRlVTRS1CUEYgdXNlcnNwYWNlIGNvbXBvbmVudHMgZm9yIEFuZHJvaWQgYXJtNjQuCgpGaWxlczoKLSBmdXNlLWRhZW1vbjogRnVsbCBGVVNFLUJQRiB0ZXN0IGRhZW1vbiAoZnJvbSBrZXJuZWwgc2VsZnRlc3RzKQotIGZkX2JwZi5icGY6IEJQRiBieXRlY29kZSBwcm9ncmFtIGZvciBGVVNFLUJQRiBiYWNraW5nCi0gZnVzZS1icGYtY2hlY2tlcjogTWluaW1hbCBGVVNFLUJQRiBzdXBwb3J0IGNoZWNrZXIKClJlcXVpcmVtZW50cyBvbiBkZXZpY2U6CjEuIEtlcm5lbCB3aXRoIENPTkZJR19GVVNFX0JQRj15IChhbHJlYWR5IGVuYWJsZWQgaW4gR0tJKQoyLiAvZGV2L2Z1c2UgZGV2aWNlIG5vZGUgKG1rbm9kIC9kZXYvZnVzZSBjIDEwIDIyOSkKMy4gUm9vdCBhY2Nlc3MgdG8gbW91bnQgRlVTRS1CUEYgZmlsZXN5c3RlbQoKVXNhZ2UgKHJlcXVpcmVzIHJvb3QpOgogICMgQ3JlYXRlIC9kZXYvZnVzZSBpZiBub3QgcHJlc2VudAogIHN1IC1jICJta25vZCAvZGV2L2Z1c2UgYyAxMCAyMjkiCgogICMgTW91bnQgRlVTRS1CUEYgZmlsZXN5c3RlbSAocmVxdWlyZXMgQlBGIHByb2dyYW0pCiAgc3UgLWMgIi4vZnVzZS1kYWVtb24iCgpOb3RlczoKLSBUaGUgZnVsbCBmdXNlLWRhZW1vbiBpcyBhIHRlc3QgcHJvZ3JhbSBmcm9tIGtlcm5lbCBzZWxmdGVzdHMKLSBJdCByZXF1aXJlcyByb290IGFuZCBGVVNFLUJQRiBrZXJuZWwgc3VwcG9ydAotIFRoZSBCUEYgcHJvZ3JhbSAoZmRfYnBmLmJwZikgbXVzdCBiZSBsb2FkZWQgYmVmb3JlIG1vdW50aW5nCg==' | base64 -d > "${{ github.workspace }}/artifacts/fuse-bpf/README.txt" echo "FUSE-BPF artifacts:" ls -la "${{ github.workspace }}/artifacts/fuse-bpf/" - name: Upload FUSE-BPF Artifact uses: actions/upload-artifact@v7 with: name: fuse-bpf-arm64 path: ${{ github.workspace }}/artifacts/fuse-bpf/ if-no-files-found: warn