mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +09:00
[Feat] (Build, TraceApp): ship and exec a second native binary on android
- PLAN-B.md §11 P0 lists spike A (the Android delivery chain) as a P0 deliverable, inherited verbatim from PLAN.md §15 P0; §8.1 inherits PLAN.md §11.1-§11.6, whose Android path needs a second process. Android gives an application no writable exec-able directory, so the only supported route is to name the binary lib*.so, let the packager put it in lib/<abi>/, and exec it out of getApplicationInfo().nativeLibraryDir. This builds that route end to end so the spike can be answered with evidence instead of folklore. - New root option MOBILEGL_BUILD_SERVER_SPIKE (OFF, ANDROID-only) adds the MobileGLServer target from tools/spikes/server_stub/main.cpp with PREFIX "lib" / SUFFIX ".so" and -fPIE/-pie: an .so name does not exempt the file from Android's PIE requirement. Its RUNTIME_OUTPUT_DIRECTORY is pointed at CMAKE_LIBRARY_OUTPUT_DIRECTORY, because AGP packages what lands in the per-ABI library output directory and CMake would otherwise put an executable elsewhere. - The option is opt-in on both sides. The plugin flavour cannot turn it on at all, and the trace flavour builds it only when asked, with `-Pmobilegl.buildServerSpike=ON` or MOBILEGL_BUILD_SERVER_SPIKE=ON in the environment; a flavour that silently carries an executable nothing loads is the kind of thing nobody notices until it ships. Verified both ways: assembleTraceDebug -Pmobilegl.buildServerSpike=ON packages lib/arm64-v8a/libMobileGLServer.so and `file` reports "ELF 64-bit LSB pie executable, ARM aarch64 ... interpreter /system/bin/linker64, for Android 26"; the same task with no property packages only libMobileGL.so and libtrace_replay_runner.so. - The stub prints one line to stdout and writes the same line to the file named by argv[1], then exits 0. The line carries pid/ppid/uid/gid and, decisively, the child's own /proc/self/attr/current: only `u:r:untrusted_app:...` proves an ordinary app process did the exec. An `adb run-as` shell runs in a different SELinux domain, so a success there would prove nothing. - RunSpawnSpike() starts the stub with argv [serverPath, markerPath], redirects the child's stdout/stderr into a captured file (an app process has stdout on /dev/null, so a printed line would otherwise vanish), waits for it, and reports exit status, signal, the exec errno, the parent's own SELinux context, the marker content and the captured stdout - to logcat, to the returned string, and to a <marker>.report file, because the Activity finishes immediately afterwards. - The child reports the errno of a REFUSED execve through a close-on-exec pipe. Without it the one datum the spike exists to produce is lost: the parent only ever sees a wait status, in which every reason has already been flattened into one exit code, and EACCES (SELinux, or a noexec mount) versus ENOEXEC (a packager that mangled the file) are opposite verdicts for the design. A successful exec closes the write end for free, so the parent reads EOF and reports execErrno=0. - fork/execve only. The earlier draft also carried a posix_spawn arm behind `__ANDROID_API__ >= 28`, which was dead code in every configuration this repo can build - bionic declares posix_spawn from API 28 and the root CMakeLists.txt pins MOBILEGL_ANDROID_API_LEVEL to 26 and refuses to configure lower - and would have silently become the production path, untested, on a minSdk bump. Keeping the arm that actually ships means the spike measures the code the server would really use. Nothing happens between fork and execve except open/dup2/execve/write/_exit, all async-signal-safe, because the parent is a multi-threaded JVM process. - The spike lives in its own TU, spawn_spike.cpp/.hpp, listed only by the trace APK's CMakeLists. Its sibling trace_replay_core.cpp is compiled verbatim by the DESKTOP mobilegl_trace_replay runner (tools/trace_replay/CMakeLists.txt names the same file), where <android/log.h> does not exist, so nothing Android-only may live there; spawn_spike.cpp carries an #error for anyone who adds it to that list. - The Activity runs the spike, and nothing else, when launched with the `mobilegl_spike_spawn` intent extra; that mode needs no trace, no golden and no render surface. It is a separate JNI entry point rather than another parameter on the 30-argument replay call, which it shares nothing with. - Not yet run on a device: both device locks are held by another campaign. The on-device verdict is the coordinator's step.
This commit is contained in:
@@ -21,6 +21,7 @@ option(MOBILEGL_IOS "Build MobileGL for iOS instead of macOS when
|
||||
# That emptiness is one of the two byte-level equalities the plan's validation
|
||||
# gates keep (section 10.3).
|
||||
option(MOBILEGL_BUILD_DISAGGREGATED "Build the MG_Remote transport layer (two-process shape)" OFF)
|
||||
option(MOBILEGL_BUILD_SERVER_SPIKE "Build the P0 spike-A MobileGLServer delivery-chain executable (Android only)" OFF)
|
||||
set(MOBILEGL_LOG_ACTIVE_LEVEL "MOBILEGL_LOG_LEVEL_INFO" CACHE STRING "MobileGL active log level macro")
|
||||
set(MOBILEGL_VULKAN_LIBRARY "" CACHE FILEPATH "Vulkan loader/MoltenVK library to link for iOS builds")
|
||||
|
||||
@@ -760,3 +761,43 @@ endif()
|
||||
if (ANDROID AND MOBILEGL_BUILD_INTEGRATION_TEST)
|
||||
add_subdirectory(MobileGL/MG_IntegrationTest)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# P0 spike A: the Android delivery chain for a second native executable.
|
||||
#
|
||||
# The disaggregated design needs a server process on Android (PLAN-B.md §8.1,
|
||||
# inheriting PLAN.md §11.1-§11.6). An APK's only exec-able install location is
|
||||
# lib/<abi>/, and the packager only puts a file there if it is named lib*.so -
|
||||
# so a second executable has to be built with an .so name and exec'd out of
|
||||
# getApplicationInfo().nativeLibraryDir. This target is the stub that proves the
|
||||
# chain end to end: it is packaged like a library, exec'd from the app's own
|
||||
# untrusted_app process, and writes a marker the parent reads back.
|
||||
#
|
||||
# Off by default and ANDROID-only, so no shipping configuration builds it. The
|
||||
# trace flavour of the plugin APK turns it on (android-plugin/build.gradle).
|
||||
# ---------------------------------------------------------------------------
|
||||
if (ANDROID AND MOBILEGL_BUILD_SERVER_SPIKE)
|
||||
add_executable(MobileGLServer
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tools/spikes/server_stub/main.cpp)
|
||||
|
||||
# An executable that is named like a shared library still has to be a real
|
||||
# PIE executable: Android has refused non-PIE executables since API 21, and
|
||||
# the name alone does not change what the loader demands of the file.
|
||||
set_target_properties(MobileGLServer PROPERTIES
|
||||
PREFIX "lib"
|
||||
SUFFIX ".so"
|
||||
OUTPUT_NAME "MobileGLServer"
|
||||
POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_options(MobileGLServer PRIVATE -fPIE)
|
||||
target_link_options(MobileGLServer PRIVATE -pie)
|
||||
|
||||
# AGP packages what the external native build drops into the per-ABI output
|
||||
# directory, and it selects by the .so extension. CMake puts executables in
|
||||
# CMAKE_RUNTIME_OUTPUT_DIRECTORY, which is not the directory AGP hands to
|
||||
# CMAKE_LIBRARY_OUTPUT_DIRECTORY, so point this target's runtime output at
|
||||
# the library directory when the generator gave us one.
|
||||
if (CMAKE_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set_target_properties(MobileGLServer PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user