Fix Android native Git child exit handling so committed levels validate and Git calls avoid SIGSEGV status 139
This commit is contained in:
@@ -450,7 +450,7 @@ build_android_git_for_abi() {
|
||||
local output_dir="$ANDROID_JNI_DIR/$abi"
|
||||
local output_binary="$output_dir/libgit.so"
|
||||
local stamp="$output_dir/.source-fingerprint"
|
||||
local fingerprint="$3:android:$abi:$cc:$ANDROID_API"
|
||||
local fingerprint="$3:android:$abi:$cc:$ANDROID_API:githug-embedded-main"
|
||||
|
||||
require_path "$ANDROID_TOOLBIN/$cc"
|
||||
|
||||
@@ -470,6 +470,7 @@ build_android_git_for_abi() {
|
||||
NO_PTHREADS=YesPlease \
|
||||
NO_LIBGEN_H=YesPlease \
|
||||
HAVE_DEV_TTY=YesPlease \
|
||||
CFLAGS_APPEND=-DGITHUG_EMBEDDED_MAIN \
|
||||
CC="$cc" \
|
||||
AR=llvm-ar \
|
||||
RANLIB=llvm-ranlib \
|
||||
|
||||
@@ -155,13 +155,13 @@ Git build outputs are stamped with a Git source fingerprint. Re-running `--test`
|
||||
|
||||
Git manpage assets are also refreshed from the checked-out Git source whenever Git is compiled or an app artifact is built.
|
||||
|
||||
Git source patches can live under `patches/git/` and are applied by `AndroidProjectTooling.sh` after the Git source checkout is cloned or reused. These patches are part of the source fingerprint, so changing a patch forces the host and Android Git binaries to rebuild. The current runtime does not require a Git source patch: it resolves Git's existing exported `init_git` and `cmd_main` symbols through JNI and calls them with Git-style `argc`/`argv`.
|
||||
Git source patches live under `patches/git/` and are applied by `AndroidProjectTooling.sh` after the Git source checkout is cloned or reused. These patches are part of the source fingerprint, so changing a patch forces Git binaries to rebuild. The Android Git build enables a small embedded-main patch that exports `githug_git_main(argc, argv)`. That wrapper still delegates command dispatch and parsing to Git, but routes Git's process-exit path through `_exit()` in the forked child so Android does not run libc/JVM inherited exit handlers after native Git completes.
|
||||
|
||||
## Runtime Architecture
|
||||
|
||||
The command engine has one app-facing runtime:
|
||||
|
||||
- **Native Git path**: the packaged Git binary for the device ABI is loaded by the JNI bridge and every `git ...` command invokes Git's existing `init_git(argv)` and `cmd_main(argc, argv)` path in a real per-level repository sandbox in app-private storage.
|
||||
- **Native Git path**: the packaged Git binary for the device ABI is loaded by the JNI bridge and every `git ...` command invokes Git's patched embedded entry point, `githug_git_main(argc, argv)`, in a forked child process inside a real per-level repository sandbox in app-private storage.
|
||||
|
||||
The runtime exposes a `RepoState` surface to validators. In addition to files, commits, branches, tags, remotes, and config, the model tracks learning-relevant effects such as stashes, fetched remote refs, pushed branches/tags, submodules, and repository maintenance actions.
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ android {
|
||||
applicationId = "solutions.tretter.githugandroid"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 177
|
||||
versionName = "0.1.176"
|
||||
versionCode = 178
|
||||
versionName = "0.1.177"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define GIT_COMMAND_TIMEOUT_MS 30000
|
||||
|
||||
typedef int (*git_main_fn)(int argc, const char **argv);
|
||||
typedef int (*githug_git_main_fn)(int argc, const char **argv);
|
||||
typedef void (*git_init_fn)(const char **argv);
|
||||
|
||||
struct output_buffer {
|
||||
@@ -27,6 +28,7 @@ struct output_buffer {
|
||||
static pthread_mutex_t git_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static void *git_handle = NULL;
|
||||
static git_main_fn git_main = NULL;
|
||||
static githug_git_main_fn githug_git_main = NULL;
|
||||
static git_init_fn git_init = NULL;
|
||||
|
||||
static int append_output(struct output_buffer *buffer, const char *data, size_t length) {
|
||||
@@ -116,6 +118,7 @@ static int load_git(const char *library_path) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "dlsym init_git failed: %s", dlerror());
|
||||
return -1;
|
||||
}
|
||||
githug_git_main = (githug_git_main_fn)dlsym(git_handle, "githug_git_main");
|
||||
git_main = (git_main_fn)dlsym(git_handle, "cmd_main");
|
||||
if (git_main == NULL) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "dlsym cmd_main failed: %s", dlerror());
|
||||
@@ -274,8 +277,13 @@ Java_solutions_tretter_githugandroid_NativeGitBridge_runGitMainNative(
|
||||
(void)saved_env;
|
||||
chdir(working_directory_chars);
|
||||
|
||||
int child_exit_code;
|
||||
if (githug_git_main != NULL) {
|
||||
child_exit_code = githug_git_main(argc, (const char **)argv);
|
||||
} else {
|
||||
git_init((const char **)argv);
|
||||
int child_exit_code = git_main(argc, (const char **)argv);
|
||||
child_exit_code = git_main(argc, (const char **)argv);
|
||||
}
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
_exit(child_exit_code);
|
||||
|
||||
@@ -196,7 +196,7 @@ class GitRepositoryRuntime private constructor(
|
||||
addAll(GitSandboxEngine.commandReferenceLines())
|
||||
add("Native Git runtime:")
|
||||
add(" binary path: nativeLibraryDir/libgit.so")
|
||||
add(" invocation: init_git(argv), then cmd_main(argc, argv)")
|
||||
add(" invocation: githug_git_main(argc, argv)")
|
||||
add(" selected ABI: ${Build.SUPPORTED_ABIS.firstOrNull() ?: "unknown"}")
|
||||
add(" helper commands: ls/dir, pwd, cat, sh <script>, ./<script>, touch, mkdir/md, cd.., rm/del, echo")
|
||||
add(" visual editors: vi, vim, nano, emacs, ed, ex, edit, notepad")
|
||||
|
||||
49
patches/git/0001-githug-embedded-git-main.patch
Normal file
49
patches/git/0001-githug-embedded-git-main.patch
Normal file
@@ -0,0 +1,49 @@
|
||||
diff --git a/common-main.c b/common-main.c
|
||||
index 0000000000..0000000000 100644
|
||||
--- a/common-main.c
|
||||
+++ b/common-main.c
|
||||
@@ -1,6 +1,27 @@
|
||||
#include "git-compat-util.h"
|
||||
#include "common-init.h"
|
||||
|
||||
+#ifdef GITHUG_EMBEDDED_MAIN
|
||||
+void githug_exit(int code)
|
||||
+{
|
||||
+ code &= 0xff;
|
||||
+ fflush(stdout);
|
||||
+ fflush(stderr);
|
||||
+ _exit(code);
|
||||
+}
|
||||
+
|
||||
+int githug_git_main(int argc, const char **argv)
|
||||
+{
|
||||
+ int result;
|
||||
+
|
||||
+ init_git(argv);
|
||||
+ result = cmd_main(argc, argv);
|
||||
+
|
||||
+ /* Match Git's real main(), but return instead of calling libc exit(). */
|
||||
+ return common_exit(__FILE__, __LINE__, result);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int result;
|
||||
diff --git a/git-compat-util.h b/git-compat-util.h
|
||||
index 0000000000..0000000000 100644
|
||||
--- a/git-compat-util.h
|
||||
+++ b/git-compat-util.h
|
||||
@@ -1047,7 +1047,12 @@ int cmd_main(int, const char **);
|
||||
* optionally emit a message before calling the real exit().
|
||||
*/
|
||||
int common_exit(const char *file, int line, int code);
|
||||
+#ifdef GITHUG_EMBEDDED_MAIN
|
||||
+NORETURN void githug_exit(int code);
|
||||
+#define exit(code) githug_exit(common_exit(__FILE__, __LINE__, (code)))
|
||||
+#else
|
||||
#define exit(code) exit(common_exit(__FILE__, __LINE__, (code)))
|
||||
+#endif
|
||||
|
||||
/*
|
||||
* This include must come after system headers, since it introduces macros that
|
||||
Reference in New Issue
Block a user