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_dir="$ANDROID_JNI_DIR/$abi"
|
||||||
local output_binary="$output_dir/libgit.so"
|
local output_binary="$output_dir/libgit.so"
|
||||||
local stamp="$output_dir/.source-fingerprint"
|
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"
|
require_path "$ANDROID_TOOLBIN/$cc"
|
||||||
|
|
||||||
@@ -470,6 +470,7 @@ build_android_git_for_abi() {
|
|||||||
NO_PTHREADS=YesPlease \
|
NO_PTHREADS=YesPlease \
|
||||||
NO_LIBGEN_H=YesPlease \
|
NO_LIBGEN_H=YesPlease \
|
||||||
HAVE_DEV_TTY=YesPlease \
|
HAVE_DEV_TTY=YesPlease \
|
||||||
|
CFLAGS_APPEND=-DGITHUG_EMBEDDED_MAIN \
|
||||||
CC="$cc" \
|
CC="$cc" \
|
||||||
AR=llvm-ar \
|
AR=llvm-ar \
|
||||||
RANLIB=llvm-ranlib \
|
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 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
|
## Runtime Architecture
|
||||||
|
|
||||||
The command engine has one app-facing runtime:
|
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.
|
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"
|
applicationId = "solutions.tretter.githugandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 177
|
versionCode = 178
|
||||||
versionName = "0.1.176"
|
versionName = "0.1.177"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#define GIT_COMMAND_TIMEOUT_MS 30000
|
#define GIT_COMMAND_TIMEOUT_MS 30000
|
||||||
|
|
||||||
typedef int (*git_main_fn)(int argc, const char **argv);
|
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);
|
typedef void (*git_init_fn)(const char **argv);
|
||||||
|
|
||||||
struct output_buffer {
|
struct output_buffer {
|
||||||
@@ -27,6 +28,7 @@ struct output_buffer {
|
|||||||
static pthread_mutex_t git_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t git_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static void *git_handle = NULL;
|
static void *git_handle = NULL;
|
||||||
static git_main_fn git_main = 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 git_init_fn git_init = NULL;
|
||||||
|
|
||||||
static int append_output(struct output_buffer *buffer, const char *data, size_t length) {
|
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());
|
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "dlsym init_git failed: %s", dlerror());
|
||||||
return -1;
|
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");
|
git_main = (git_main_fn)dlsym(git_handle, "cmd_main");
|
||||||
if (git_main == NULL) {
|
if (git_main == NULL) {
|
||||||
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "dlsym cmd_main failed: %s", dlerror());
|
__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;
|
(void)saved_env;
|
||||||
chdir(working_directory_chars);
|
chdir(working_directory_chars);
|
||||||
|
|
||||||
git_init((const char **)argv);
|
int child_exit_code;
|
||||||
int child_exit_code = git_main(argc, (const char **)argv);
|
if (githug_git_main != NULL) {
|
||||||
|
child_exit_code = githug_git_main(argc, (const char **)argv);
|
||||||
|
} else {
|
||||||
|
git_init((const char **)argv);
|
||||||
|
child_exit_code = git_main(argc, (const char **)argv);
|
||||||
|
}
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
_exit(child_exit_code);
|
_exit(child_exit_code);
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ class GitRepositoryRuntime private constructor(
|
|||||||
addAll(GitSandboxEngine.commandReferenceLines())
|
addAll(GitSandboxEngine.commandReferenceLines())
|
||||||
add("Native Git runtime:")
|
add("Native Git runtime:")
|
||||||
add(" binary path: nativeLibraryDir/libgit.so")
|
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(" 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(" 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")
|
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