fingerprintd/packaging/make-libqcomtee.sh
Jorijn van der Graaf a91fb2ff58 Reach QTEE: credentials, client env and the app loader, with no QCBOR
fingerprintd's own code now talks to QTEE. On the phone:

    root object on /dev/tee0
    client env obtained (uid 0, 13-byte credentials)
    QSEECOM-compat app loader (UID 122) opened

The credentials object is ours rather than libqcomtee's. Upstream's exists only
to build a thirteen-byte CBOR map and drags in QCBOR to do it, so
packaging/make-libqcomtee.sh compiles the two sources that matter and drops
credentials_obj.c entirely -- nothing else references it, and the library then
has no dependency beyond libc. The map is built in Fingerprintd:Tee where it is
pinned byte-for-byte against the string verified on-device, and the object's
two-op read protocol is served here.

Three interop details, all of which cost a build cycle:

  * libqcomtee's headers carry no extern "C" guard, having only ever been
    consumed from C, so everything came out C++-mangled. They also pull in
    <stdatomic.h> and <stdio.h>, which under libc++ drag in templates that may
    not appear inside extern "C" -- so those are included first.
  * tee_call_t's second parameter is unsigned long on glibc and int on musl.
    The native build is glibc and the phone is musl; both forms are compiled.
  * On the callback path a UBUF_OUTPUT param arrives with addr = NULL. The
    dispatcher supplies the buffer, so a handler POINTS the param at its own
    storage rather than writing through the incoming address. Doing the latter
    is a null dereference that takes the supplicant thread with it, which is
    how the first run against real QTEE ended -- with the correct behaviour
    already spelled out in the module comment above the code that ignored it.
    That comment now says so in as many words.
2026-09-02 18:02:28 +02:00

82 lines
3.1 KiB
Shell
Executable file

#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
# make-libqcomtee.sh — build libqcomtee for a target, without QCBOR.
#
# packaging/make-libqcomtee.sh [--target=<triple> --sysroot=<dir>] [outdir]
#
# libqcomtee is Qualcomm's BSD-3 userspace client for QTEE (quic/quic-teec).
# Upstream's CMake does find_package(QCBOR REQUIRED) and falls back to libcbor,
# but that dependency exists for exactly one file, src/objects/credentials_obj.c,
# which builds the {AttrUid, AttrSystemTime} CBOR map.
#
# fingerprintd builds that map itself in Fingerprintd:Tee, where it is thirteen
# bytes under test rather than a library dependency, and implements the
# credentials object's two-op read protocol in its own shell. So only two of
# the three sources are needed:
#
# src/qcomtee_object.c the object/marshalling core
# src/objects/mem_obj.c memory objects (CAPTURE_IMAGE needs a real region)
#
# Nothing in either references credentials_obj, so dropping it leaves the
# library with no dependency beyond libc.
set -eu
REPO="${QUIC_TEEC_REPO:-https://github.com/quic/quic-teec.git}"
# Pinned. Upstream is small and moves rarely; an unpinned clone would change
# the marshalling under us without notice.
COMMIT="${QUIC_TEEC_COMMIT:-736419e}"
SRC="${QUIC_TEEC_DIR:-$HOME/.cache/fingerprintd/quic-teec}"
TARGET=""
SYSROOT=""
MARCH=""
OUT=""
for a in "$@"; do
case "$a" in
--target=*) TARGET="${a#--target=}" ;;
--sysroot=*) SYSROOT="${a#--sysroot=}" ;;
--march=*) MARCH="${a#--march=}" ;;
-*) echo "unknown option: $a" >&2; exit 1 ;;
*) OUT="$a" ;;
esac
done
[ -n "$OUT" ] || OUT="$HOME/.cache/fingerprintd/libqcomtee${TARGET:+-$TARGET}"
if [ ! -d "$SRC/.git" ]; then
echo ">> cloning $REPO -> $SRC"
mkdir -p "$(dirname "$SRC")"
git clone -q "$REPO" "$SRC"
git -C "$SRC" checkout -q "$COMMIT"
elif [ -n "${QUIC_TEEC_DIR:-}" ]; then
# A checkout the caller pointed us at is THEIRS. Never move its HEAD --
# silently checking out a pin in someone's working tree is how you lose
# uncommitted work. Report the mismatch and let them decide.
have=$(git -C "$SRC" rev-parse --short HEAD)
case "$COMMIT" in
"$have"*) : ;;
*) echo "!! $SRC is at $have, not the pinned $COMMIT." >&2
echo "!! Building from it anyway; unset QUIC_TEEC_DIR to use a pinned clone." >&2 ;;
esac
else
git -C "$SRC" fetch -q --all 2>/dev/null || true
git -C "$SRC" checkout -q "$COMMIT"
fi
mkdir -p "$OUT"
set -- -O2 -Wall -fPIC
[ -n "$TARGET" ] && set -- "$@" --target="$TARGET"
[ -n "$SYSROOT" ] && set -- "$@" --sysroot="$SYSROOT"
[ -n "$MARCH" ] && set -- "$@" -march="$MARCH"
echo ">> building libqcomtee.a (no QCBOR) for ${TARGET:-native}"
for f in src/qcomtee_object.c src/objects/mem_obj.c; do
clang "$@" -I"$SRC/libqcomtee/include" -I"$SRC/libqcomtee/src" \
-c "$SRC/libqcomtee/$f" -o "$OUT/$(basename "$f" .c).o"
done
rm -f "$OUT/libqcomtee.a"
llvm-ar rcs "$OUT/libqcomtee.a" "$OUT"/*.o
mkdir -p "$OUT/include"
cp "$SRC/libqcomtee/include/"*.h "$OUT/include/"
echo ">> done: $OUT/libqcomtee.a"