karnel-termux 4.9.0 → 4.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ #!/data/data/com.termux/files/usr/bin/bash
2
+
3
+ UBUNTU_ROOTFS="__UBUNTU_ROOTFS__"
4
+
5
+ EXCLUDE_REGEX="^(PATH|LD_PRELOAD|LD_LIBRARY_PATH|PREFIX|HOME|PWD|OLDPWD|SHELL|IFS|_|SHLVL|PROMPT_COMMAND|TERMCAP|LS_COLORS|TERM)="
6
+
7
+ ENV_ARGS=()
8
+ while IFS= read -r line; do
9
+ if [[ -n "$line" && ! "$line" =~ $EXCLUDE_REGEX ]]; then
10
+ ENV_ARGS+=("--env" "$line")
11
+ fi
12
+ done < <(env)
13
+
14
+ ENV_ARGS+=(
15
+ "--env" "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"
16
+ "--env" "TERM=$TERM"
17
+ "--env" "HOME=/root"
18
+ )
19
+
20
+ unset LD_PRELOAD
21
+ proot-distro login \
22
+ "${ENV_ARGS[@]}" \
23
+ --termux-home \
24
+ --shared-tmp \
25
+ --bind "$UBUNTU_ROOTFS/root/.cline:/root/.cline" \
26
+ --work-dir $PWD \
27
+ ubuntu \
28
+ -- /root/.cline/bin/cline "$@"
@@ -0,0 +1,54 @@
1
+ #include <stdlib.h>
2
+ #include <unistd.h>
3
+ #include <string.h>
4
+ #include <libgen.h>
5
+ #include <limits.h>
6
+ #include <stdio.h>
7
+
8
+ int main(int argc, char** argv) {
9
+ // 1. Clear conflicting Android Bionic preloads and search paths
10
+ unsetenv("LD_PRELOAD");
11
+ unsetenv("LD_LIBRARY_PATH");
12
+
13
+ // 2. Set dynamic resolver and SSL configurations for Termux environment
14
+ setenv("SSL_CERT_FILE", "/data/data/com.termux/files/usr/etc/tls/cert.pem", 1);
15
+
16
+ // 3. Resolve current executable directory
17
+ char exec_path[PATH_MAX];
18
+ ssize_t len = readlink("/proc/self/exe", exec_path, sizeof(exec_path) - 1);
19
+ if (len == -1) {
20
+ return 1;
21
+ }
22
+ exec_path[len] = '\0';
23
+ char* dir = dirname(exec_path);
24
+
25
+ // 4. Construct paths for the glibc loader and the real binary
26
+ char* loader = "/data/data/com.termux/files/usr/glibc/lib/ld-linux-aarch64.so.1";
27
+ char real_bin[] = "/data/data/com.termux/files/home/.local/share/karnel-data/cline/cline";
28
+ char lib_path[] = "/data/data/com.termux/files/usr/glibc/lib";
29
+
30
+ // 5. Construct argument array for execv
31
+ // Format: [loader, --library-path, lib_path, real_bin, ...original_args]
32
+ char** new_argv = malloc((argc + 4) * sizeof(char*));
33
+ if (!new_argv) {
34
+ return 1;
35
+ }
36
+
37
+ new_argv[0] = loader;
38
+ new_argv[1] = "--library-path";
39
+ new_argv[2] = lib_path;
40
+ new_argv[3] = real_bin;
41
+
42
+ for (int i = 1; i < argc; i++) {
43
+ new_argv[i + 3] = argv[i];
44
+ }
45
+ new_argv[argc + 3] = NULL;
46
+
47
+ // 6. Execute the glibc loader to run the real binary
48
+ execv(loader, new_argv);
49
+
50
+ // If execv returns, an error occurred
51
+ perror("execv");
52
+ free(new_argv);
53
+ return 1;
54
+ }
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+ # Parches Termux para gentleman-guardian-angel
3
+ # Se aplica al repo upstream después del clone, antes del install.sh
4
+ set -e
5
+
6
+ REPO_DIR="$1"
7
+ [ -d "$REPO_DIR" ] || { echo "Usage: $0 <repo-dir>" >&2; exit 1; }
8
+
9
+ # --- install.sh ---
10
+ # 1) Fix shebang for Termux
11
+ sed -i '1s|#!/usr/bin/env bash|#!/data/data/com.termux/files/usr/bin/bash|' "$REPO_DIR/install.sh"
12
+
13
+ # 2) Agregar detección de Termux en detect_os()
14
+ sed -i '/^ if \[\[ -n "\${GGA_TEST_OS:-}" \]\]; then$/,/^$/{
15
+ /^$/a\
16
+ \
17
+ if [[ -n "${TERMUX_VERSION:-}" ]]; then\
18
+ echo "termux"\
19
+ return 0\
20
+ fi
21
+ }' "$REPO_DIR/install.sh"
22
+
23
+ # 3) Agregar TERMUX para INSTALL_DIR (después del bloque windows)
24
+ sed -i '/INSTALL_DIR="\$HOME\/bin"/,/elif \[\[ -w "\/usr\/local\/bin" \]\]/{
25
+ /elif \[\[ -w "\/usr\/local\/bin" \]\]/i\
26
+ elif [[ "$GGA_OS" == "termux" ]]; then\
27
+ INSTALL_DIR="${PREFIX:-/data/data/com.termux/files/usr}/bin"\
28
+ mkdir -p "$INSTALL_DIR"
29
+ }' "$REPO_DIR/install.sh"
30
+
31
+ # 4) Agregar TERMUX para LIB_INSTALL_DIR (después del bloque windows)
32
+ sed -i '/LIB_INSTALL_DIR="\$HOME\/bin\/lib\/gga"/,/else$/{
33
+ /else$/i\
34
+ elif [[ "$GGA_OS" == "termux" ]]; then\
35
+ LIB_INSTALL_DIR="${PREFIX:-/data/data/com.termux/files/usr}/share/gga/lib"
36
+ }' "$REPO_DIR/install.sh"
37
+
38
+ # --- bin/gga ---
39
+ # 5) Fix shebang for Termux (/usr/bin/env doesn't exist)
40
+ sed -i '1s|#!/usr/bin/env bash|#!/data/data/com.termux/files/usr/bin/bash|' "$REPO_DIR/bin/gga"
41
+
42
+ # 6) Agregar detección de Termux en detect_os()
43
+ sed -i '/^detect_os() {$/,/^}$/{
44
+ /^ case "\$(uname -s)" in$/i\
45
+ if [[ -n "${TERMUX_VERSION:-}" ]]; then\
46
+ echo "termux"\
47
+ return 0\
48
+ fi
49
+ }' "$REPO_DIR/bin/gga"
50
+
51
+ # --- uninstall.sh ---
52
+ # 7) Fix shebang for Termux
53
+ sed -i '1s|#!/usr/bin/env bash|#!/data/data/com.termux/files/usr/bin/bash|' "$REPO_DIR/uninstall.sh"
54
+
55
+ # 8) Agregar detección de Termux en detect_os()
56
+ sed -i '/^detect_os() {$/,/^}$/{
57
+ /^ case "\$(uname -s)" in$/i\
58
+ if [[ -n "${TERMUX_VERSION:-}" ]]; then\
59
+ echo "termux"\
60
+ return 0\
61
+ fi
62
+ }' "$REPO_DIR/uninstall.sh"
63
+
64
+ # 9) Agregar Termux path al array LOCATIONS
65
+ sed -i '/ "\$HOME\/bin\/gga"/a\ "${PREFIX:-/data/data/com.termux/files/usr}/bin/gga"' "$REPO_DIR/uninstall.sh"
66
+
67
+ # 10) Agregar Termux lib path al loop de limpieza
68
+ sed -i 's|for lib_dir in "\$HOME/.local/share/gga" "\$HOME/bin/lib/gga";|for lib_dir in "$HOME/.local/share/gga" "$HOME/bin/lib/gga" "${PREFIX:-/data/data/com.termux/files/usr}/share/gga";|' "$REPO_DIR/uninstall.sh"
69
+
70
+ echo "Termux patches applied successfully"
@@ -0,0 +1,27 @@
1
+ #!/data/data/com.termux/files/usr/bin/bash
2
+
3
+ UBUNTU_ROOTFS="__UBUNTU_ROOTFS__"
4
+
5
+ EXCLUDE_REGEX="^(PATH|LD_PRELOAD|LD_LIBRARY_PATH|PREFIX|HOME|PWD|OLDPWD|SHELL|IFS|_|SHLVL|PROMPT_COMMAND|TERMCAP|LS_COLORS|TERM)="
6
+
7
+ ENV_ARGS=()
8
+ while IFS= read -r line; do
9
+ if [[ -n "$line" && ! "$line" =~ $EXCLUDE_REGEX ]]; then
10
+ ENV_ARGS+=("--env" "$line")
11
+ fi
12
+ done < <(env)
13
+
14
+ ENV_ARGS+=(
15
+ "--env" "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"
16
+ "--env" "TERM=$TERM"
17
+ "--env" "HOME=/root"
18
+ )
19
+
20
+ unset LD_PRELOAD
21
+ proot-distro login \
22
+ "${ENV_ARGS[@]}" \
23
+ --termux-home \
24
+ --shared-tmp \
25
+ --work-dir $PWD \
26
+ ubuntu \
27
+ -- /usr/local/bin/bun "$@"
@@ -0,0 +1,481 @@
1
+ /**
2
+ * bun-android-shim.c — LD_PRELOAD shim for Bun on Android/Termux
3
+ *
4
+ * Dual purpose:
5
+ *
6
+ * 1. opendir/openat64 interception for bionic:
7
+ * Bun's project-root discovery traverses up the directory tree.
8
+ * On Termux, Android's sandbox makes /data/ and /data/data/
9
+ * readable at the syscall level but returns "Permission denied"
10
+ * or causes Bun to abort with "Cannot read directory /data/".
11
+ * This shim intercepts opendir/openat64 and returns ENOENT
12
+ * for these paths, so Bun's traversal stops at the sandbox
13
+ * boundary instead of crashing.
14
+ *
15
+ * 2. mmap fix for compiled standalone binaries:
16
+ * `bun build --compile` produces ELF binaries where the embedded
17
+ * Bun runtime contains hardcoded (non-relocated) absolute pointers
18
+ * to the `.bun` section and other data sections (e.g., 0x5730000).
19
+ * On PIE loading, these addresses are never mapped, causing SEGFAULT.
20
+ * The constructor maps critical sections at their ELF virtual addresses
21
+ * via MAP_FIXED so the pointers resolve correctly.
22
+ */
23
+
24
+ #define _GNU_SOURCE
25
+ #include <stdio.h>
26
+ #include <stdlib.h>
27
+ #include <string.h>
28
+ #include <unistd.h>
29
+ #include <fcntl.h>
30
+ #include <stdint.h>
31
+ #include <stdarg.h>
32
+ #include <sys/mman.h>
33
+ #include <sys/stat.h>
34
+ #include <errno.h>
35
+ #include <dlfcn.h>
36
+ #include <dirent.h>
37
+
38
+ /* ================================================================
39
+ * PART 1: opendir/openat64 interception
40
+ * ================================================================ */
41
+
42
+ /* CWD captured at shim load time — used as redirect for inaccessible ancestors */
43
+ static char shim_cwd[4096] = {0};
44
+ static int shim_cwd_len = 0;
45
+
46
+ /* Original functions (resolved via dlsym) */
47
+ typedef DIR *(*opendir_fn_t)(const char *);
48
+ typedef int (*open_fn_t)(const char *, int, ...);
49
+ typedef int (*openat64_fn_t)(int, const char *, int, ...);
50
+ static opendir_fn_t real_opendir = NULL;
51
+ static openat64_fn_t real_openat64 = NULL;
52
+
53
+ /* Check if a path is an inaccessible ancestor that needs redirect */
54
+ static int needs_redirect(const char *path) {
55
+ if (!path || path[0] == '\0') return 0;
56
+ /* Redirect "/", "/data/", and "/data/data/" to CWD */
57
+ if (path[0] == '/' && path[1] == '\0') return 1; /* exactly "/" */
58
+ if (strncmp(path, "/data/", 6) == 0) {
59
+ /* Don't redirect if it's the Termux sandbox itself */
60
+ if (strncmp(path, "/data/data/com.termux", 21) == 0)
61
+ return 0;
62
+ return 1;
63
+ }
64
+ return 0;
65
+ }
66
+
67
+ /* Redirect open to CWD — uses a fresh dlsym so it works from any interceptor */
68
+ static int open_cwd_redirect_impl(int flags, mode_t mode) {
69
+ if (shim_cwd_len == 0) {
70
+ errno = ENOENT;
71
+ return -1;
72
+ }
73
+ open_fn_t real = (open_fn_t)dlsym(RTLD_NEXT, "open");
74
+ if (!real) {
75
+ errno = ENOENT;
76
+ return -1;
77
+ }
78
+ return real(shim_cwd, flags, mode);
79
+ }
80
+
81
+ static int should_block(const char *path) {
82
+ /* Block exact "/data/" paths that are NOT under /data/data/com.termux */
83
+ if (path && strncmp(path, "/data/", 6) == 0) {
84
+ if (strncmp(path, "/data/data/com.termux", 21) != 0)
85
+ return 1;
86
+ }
87
+ return 0;
88
+ }
89
+
90
+ DIR *opendir(const char *path) {
91
+ if (!real_opendir) {
92
+ real_opendir = (opendir_fn_t)dlsym(RTLD_NEXT, "opendir");
93
+ }
94
+ /* Redirect blocked ancestors to CWD so bun's directory walk succeeds */
95
+ if (needs_redirect(path) && shim_cwd_len > 0) {
96
+ return real_opendir(shim_cwd);
97
+ }
98
+ if (should_block(path)) {
99
+ errno = ENOENT;
100
+ return NULL;
101
+ }
102
+ return real_opendir(path);
103
+ }
104
+
105
+ int openat64(int fd, const char *path, int flags, ...) {
106
+ if (!real_openat64) {
107
+ real_openat64 = (openat64_fn_t)dlsym(RTLD_NEXT, "openat64");
108
+ }
109
+
110
+ /* Pass mode if O_CREAT is set */
111
+ mode_t mode = 0;
112
+ if (flags & O_CREAT) {
113
+ va_list ap;
114
+ va_start(ap, flags);
115
+ mode = (mode_t)va_arg(ap, int);
116
+ va_end(ap);
117
+ }
118
+
119
+ /* Redirect blocked ancestors to CWD so bun's directory walk succeeds */
120
+ if (needs_redirect(path) && shim_cwd_len > 0) {
121
+ return real_openat64(fd, shim_cwd, flags, mode);
122
+ }
123
+ if (should_block(path)) {
124
+ errno = ENOENT;
125
+ return -1;
126
+ }
127
+ return real_openat64(fd, path, flags, mode);
128
+ }
129
+
130
+
131
+ /* ================================================================
132
+ * open/open64 interception — Android sandbox blocks open("/")
133
+ *
134
+ * Bun v1.3.14's resolver calls openat64(AT_FDCWD, "/", O_DIRECTORY)
135
+ * as part of its directory info walk. The kernel returns EACCES
136
+ * because Android's sandbox restricts root access.
137
+ *
138
+ * Returning EACCES or ENOENT both cause bun's resolver to abort
139
+ * with "CouldntReadCurrentDirectory" (bun treats ANY ancestor-open
140
+ * failure as fatal in v1.3.14 — this is fixed in a later commit,
141
+ * PR #28782, which skips permission-denied ancestors).
142
+ *
143
+ * Our fix: redirect "/" to the current working directory instead of
144
+ * failing. Bun gets a valid directory fd, completes its walk, and
145
+ * doesn't crash. The entries read will be the CWD entries (not root
146
+ * entries), but that is harmless since root never has bun config files.
147
+ * ================================================================ */
148
+
149
+ static open_fn_t real_open = NULL;
150
+
151
+ int open(const char *path, int flags, ...) {
152
+ if (!real_open)
153
+ real_open = (open_fn_t)dlsym(RTLD_NEXT, "open");
154
+
155
+ /* Redirect blocked ancestors to CWD so bun's directory walk succeeds */
156
+ if (needs_redirect(path)) {
157
+ mode_t m = 0;
158
+ if (flags & O_CREAT) {
159
+ va_list ap;
160
+ va_start(ap, flags);
161
+ m = (mode_t)va_arg(ap, int);
162
+ va_end(ap);
163
+ }
164
+ return open_cwd_redirect_impl(flags, m);
165
+ }
166
+ if (should_block(path)) {
167
+ errno = ENOENT;
168
+ return -1;
169
+ }
170
+
171
+ mode_t mode = 0;
172
+ if (flags & O_CREAT) {
173
+ va_list ap;
174
+ va_start(ap, flags);
175
+ mode = (mode_t)va_arg(ap, int);
176
+ va_end(ap);
177
+ }
178
+ return real_open(path, flags, mode);
179
+ }
180
+
181
+ int open64(const char *path, int flags, ...) {
182
+ if (!real_open)
183
+ real_open = (open_fn_t)dlsym(RTLD_NEXT, "open");
184
+
185
+ /* Redirect blocked ancestors to CWD so bun's directory walk succeeds */
186
+ if (needs_redirect(path)) {
187
+ mode_t m = 0;
188
+ if (flags & O_CREAT) {
189
+ va_list ap;
190
+ va_start(ap, flags);
191
+ m = (mode_t)va_arg(ap, int);
192
+ va_end(ap);
193
+ }
194
+ return open_cwd_redirect_impl(flags, m);
195
+ }
196
+ if (should_block(path)) {
197
+ errno = ENOENT;
198
+ return -1;
199
+ }
200
+
201
+ mode_t mode = 0;
202
+ if (flags & O_CREAT) {
203
+ va_list ap;
204
+ va_start(ap, flags);
205
+ mode = (mode_t)va_arg(ap, int);
206
+ va_end(ap);
207
+ }
208
+ return real_open(path, flags, mode);
209
+ }
210
+
211
+
212
+ /* ================================================================
213
+ * getcwd fallback — Android's getcwd() fails with
214
+ * "CouldntReadCurrentDirectory" when the process was started via
215
+ * execv() with a relative cwd. Read /proc/self/cwd as fallback.
216
+ * ================================================================ */
217
+
218
+ typedef char *(*getcwd_fn_t)(char *, size_t);
219
+ static getcwd_fn_t real_getcwd = NULL;
220
+
221
+ char *getcwd(char *buf, size_t size) {
222
+ if (!real_getcwd)
223
+ real_getcwd = (getcwd_fn_t)dlsym(RTLD_NEXT, "getcwd");
224
+
225
+ /* Try real getcwd first */
226
+ char *result = real_getcwd(buf, size);
227
+ if (result) return result;
228
+
229
+ /* Fallback: read /proc/self/cwd symlink */
230
+ ssize_t len = readlink("/proc/self/cwd", buf, size - 1);
231
+ if (len < 0) return NULL;
232
+ buf[len] = '\0';
233
+ return buf;
234
+ }
235
+
236
+
237
+ /* ================================================================
238
+ * linkat/link → copy fallback
239
+ *
240
+ * Android restricts hardlink creation across mount points and app
241
+ * sandboxes. Bun's installer tries linkat() first; when the kernel
242
+ * returns EACCES/EPERM, Bun aborts instead of falling back to copy.
243
+ * This interception converts hardlink requests into copy_file_range
244
+ * (or read+write fallback), so Bun's install flow succeeds.
245
+ * ================================================================ */
246
+
247
+ #include <sys/sendfile.h>
248
+
249
+ static int copy_file(const char *src, const char *dst) {
250
+ int fd_in = open(src, O_RDONLY);
251
+ if (fd_in < 0) return -1;
252
+
253
+ struct stat st;
254
+ if (fstat(fd_in, &st) < 0) { close(fd_in); return -1; }
255
+
256
+ int fd_out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
257
+ if (fd_out < 0) { close(fd_in); return -1; }
258
+
259
+ ssize_t sent = 0;
260
+ off_t offset = 0;
261
+ while (offset < st.st_size) {
262
+ sent = sendfile(fd_out, fd_in, &offset, st.st_size - offset);
263
+ if (sent <= 0) break;
264
+ }
265
+
266
+ close(fd_in);
267
+ close(fd_out);
268
+ return (offset == st.st_size) ? 0 : -1;
269
+ }
270
+
271
+ int linkat(int olddirfd, const char *oldpath,
272
+ int newdirfd, const char *newpath, int flags) {
273
+ /* Resolve absolute paths for copy fallback */
274
+ char src[PATH_MAX], dst[PATH_MAX];
275
+
276
+ if (oldpath && oldpath[0] != '/') {
277
+ char dir[PATH_MAX];
278
+ if (olddirfd == AT_FDCWD) {
279
+ if (getcwd(dir, sizeof(dir)) == NULL) return -1;
280
+ } else {
281
+ char fdpath[32];
282
+ snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", olddirfd);
283
+ ssize_t n = readlink(fdpath, dir, sizeof(dir) - 1);
284
+ if (n < 0) return -1;
285
+ dir[n] = '\0';
286
+ }
287
+ snprintf(src, sizeof(src), "%s/%s", dir, oldpath);
288
+ } else {
289
+ snprintf(src, sizeof(src), "%s", oldpath ? oldpath : "");
290
+ }
291
+
292
+ if (newpath && newpath[0] != '/') {
293
+ char dir[PATH_MAX];
294
+ if (newdirfd == AT_FDCWD) {
295
+ if (getcwd(dir, sizeof(dir)) == NULL) return -1;
296
+ } else {
297
+ char fdpath[32];
298
+ snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", newdirfd);
299
+ ssize_t n = readlink(fdpath, dir, sizeof(dir) - 1);
300
+ if (n < 0) return -1;
301
+ dir[n] = '\0';
302
+ }
303
+ snprintf(dst, sizeof(dst), "%s/%s", dir, newpath);
304
+ } else {
305
+ snprintf(dst, sizeof(dst), "%s", newpath ? newpath : "");
306
+ }
307
+
308
+ return copy_file(src, dst);
309
+ }
310
+
311
+ int link(const char *oldpath, const char *newpath) {
312
+ return copy_file(oldpath, newpath);
313
+ }
314
+
315
+ /* ================================================================
316
+ * PART 3: mmap fix for compiled standalone binaries
317
+ * ================================================================ */
318
+
319
+ /* ELF64 structures */
320
+ typedef struct {
321
+ unsigned char e_ident[16];
322
+ uint16_t e_type;
323
+ uint16_t e_machine;
324
+ uint32_t e_version;
325
+ uint64_t e_entry;
326
+ uint64_t e_phoff;
327
+ uint64_t e_shoff;
328
+ uint32_t e_flags;
329
+ uint16_t e_ehsize;
330
+ uint16_t e_phentsize;
331
+ uint16_t e_phnum;
332
+ uint16_t e_shentsize;
333
+ uint16_t e_shnum;
334
+ uint16_t e_shstrndx;
335
+ } Elf64_Ehdr;
336
+
337
+ typedef struct {
338
+ uint32_t sh_name;
339
+ uint32_t sh_type;
340
+ uint64_t sh_flags;
341
+ uint64_t sh_addr;
342
+ uint64_t sh_offset;
343
+ uint64_t sh_size;
344
+ uint32_t sh_link;
345
+ uint32_t sh_info;
346
+ uint64_t sh_addralign;
347
+ uint64_t sh_entsize;
348
+ } Elf64_Shdr;
349
+
350
+ /* Known hardcoded addresses in Bun standalone binaries
351
+ * These are the ELF virtual addresses that Bun's embedded runtime
352
+ * references with non-relocated absolute pointers.
353
+ * Add more as discovered from crash reports. */
354
+ static const uint64_t known_addresses[] = {
355
+ 0x5730000, /* .bun section — embedded source code metadata */
356
+ };
357
+ static const int num_known_addresses =
358
+ sizeof(known_addresses) / sizeof(known_addresses[0]);
359
+
360
+ static inline uint64_t page_align_down(uint64_t addr) {
361
+ return addr & ~(uint64_t)(0xFFF);
362
+ }
363
+
364
+ static inline uint64_t page_align_up(uint64_t addr) {
365
+ return (addr + 0xFFF) & ~(uint64_t)(0xFFF);
366
+ }
367
+
368
+ /* Find a section by name in the section header string table */
369
+ static int find_section_by_name(int fd, const Elf64_Ehdr *ehdr,
370
+ const char *name, Elf64_Shdr *out) {
371
+ if (ehdr->e_shstrndx >= ehdr->e_shnum) return -1;
372
+
373
+ /* Read section header string table */
374
+ Elf64_Shdr shstrtab;
375
+ if (lseek(fd, ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize,
376
+ SEEK_SET) < 0) return -1;
377
+ if (read(fd, &shstrtab, sizeof(shstrtab)) != sizeof(shstrtab)) return -1;
378
+
379
+ char *strtab = malloc(shstrtab.sh_size);
380
+ if (!strtab) return -1;
381
+ if (lseek(fd, shstrtab.sh_offset, SEEK_SET) < 0) { free(strtab); return -1; }
382
+ if (read(fd, strtab, shstrtab.sh_size) != (ssize_t)shstrtab.sh_size) {
383
+ free(strtab); return -1;
384
+ }
385
+
386
+ Elf64_Shdr shdr;
387
+ int found = 0;
388
+ for (int i = 0; i < ehdr->e_shnum; i++) {
389
+ if (lseek(fd, ehdr->e_shoff + i * ehdr->e_shentsize, SEEK_SET) < 0) break;
390
+ if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) break;
391
+
392
+ const char *sname = (shdr.sh_name < shstrtab.sh_size)
393
+ ? strtab + shdr.sh_name : "";
394
+ if (strcmp(sname, name) == 0) {
395
+ memcpy(out, &shdr, sizeof(shdr));
396
+ found = 1;
397
+ break;
398
+ }
399
+ }
400
+ free(strtab);
401
+ return found ? 0 : -1;
402
+ }
403
+
404
+ /* Map a section at its virtual address with MAP_FIXED */
405
+ static int map_section_at_vaddr(int fd, const Elf64_Shdr *shdr) {
406
+ uint64_t map_addr = page_align_down(shdr->sh_addr);
407
+ uint64_t map_end = page_align_up(shdr->sh_addr + shdr->sh_size);
408
+ uint64_t map_size = map_end - map_addr;
409
+
410
+ if (shdr->sh_type == 8) { /* SHT_NOBITS */
411
+ void *addr = mmap((void *)(uintptr_t)map_addr, map_size,
412
+ PROT_READ | PROT_WRITE,
413
+ MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
414
+ return (addr == MAP_FAILED) ? -1 : 0;
415
+ }
416
+
417
+ /* PROGBITS: map from file */
418
+ uint64_t file_off = shdr->sh_offset - (shdr->sh_addr - map_addr);
419
+ void *addr = mmap((void *)(uintptr_t)map_addr, map_size, PROT_READ,
420
+ MAP_PRIVATE | MAP_FIXED, fd, file_off);
421
+ return (addr == MAP_FAILED) ? -1 : 0;
422
+ }
423
+
424
+ /* Constructor: fix compiled standalone binaries */
425
+ static void fix_bun_standalone(void) {
426
+ const char *exe_path = "/proc/self/exe";
427
+ int fd = open(exe_path, O_RDONLY);
428
+ if (fd < 0) {
429
+ char cmdline[256];
430
+ int cfd = open("/proc/self/cmdline", O_RDONLY);
431
+ if (cfd >= 0) {
432
+ int n = read(cfd, cmdline, sizeof(cmdline) - 1);
433
+ close(cfd);
434
+ if (n > 0) {
435
+ cmdline[n] = '\0';
436
+ fd = open(cmdline, O_RDONLY);
437
+ }
438
+ }
439
+ if (fd < 0) return;
440
+ }
441
+
442
+ Elf64_Ehdr ehdr;
443
+ if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) { close(fd); return; }
444
+
445
+ /* Verify ELF64 */
446
+ if (ehdr.e_ident[0] != 0x7f || ehdr.e_ident[1] != 'E' ||
447
+ ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F') { close(fd); return; }
448
+ if (ehdr.e_ident[4] != 2) { close(fd); return; }
449
+ if (ehdr.e_shentsize != sizeof(Elf64_Shdr)) { close(fd); return; }
450
+
451
+ /* Try to find and map the .bun section */
452
+ Elf64_Shdr bun;
453
+ if (find_section_by_name(fd, &ehdr, ".bun", &bun) == 0) {
454
+ map_section_at_vaddr(fd, &bun);
455
+ }
456
+
457
+ /* Try known absolute addresses (fallback) */
458
+ for (int i = 0; i < num_known_addresses; i++) {
459
+ uint64_t vaddr = known_addresses[i];
460
+ /* Check if already mapped by section name above */
461
+ if (bun.sh_addr == vaddr && bun.sh_size > 0) continue;
462
+
463
+ /* Try direct mmap from file (vaddr == file offset in bun binaries) */
464
+ uint64_t pg = page_align_down(vaddr);
465
+ mmap((void *)(uintptr_t)pg, 0x1000, PROT_READ,
466
+ MAP_PRIVATE | MAP_FIXED, fd, pg);
467
+ }
468
+
469
+ close(fd);
470
+ }
471
+
472
+ /* Constructor runs at process start — captures CWD and fixes standalone bins */
473
+ __attribute__((constructor(1)))
474
+ static void init(void) {
475
+ /* Capture CWD for "/" redirect before any sandbox restriction matters */
476
+ if (getcwd(shim_cwd, sizeof(shim_cwd)) != NULL) {
477
+ shim_cwd_len = strlen(shim_cwd);
478
+ }
479
+
480
+ fix_bun_standalone();
481
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * bun-bundle — Post-process a Bun compiled standalone binary for Android Termux.
3
+ *
4
+ * Bun's `bun build --compile` produces standalone ELF binaries that crash on
5
+ * Android because the embedded Bun runtime contains hardcoded (non-relocated)
6
+ * absolute pointers. This tool wraps the compiled binary with a shell script
7
+ * that sets LD_PRELOAD to load the bun-android-shim.so fix library.
8
+ *
9
+ * Usage after bun build --compile:
10
+ * bun build --compile hello.ts
11
+ * bun-bundle hello
12
+ * ./hello # Now works!
13
+ *
14
+ * The tool renames hello -> hello.bun and creates a shell wrapper hello.
15
+ */
16
+ #define _GNU_SOURCE
17
+ #include <stdio.h>
18
+ #include <stdlib.h>
19
+ #include <string.h>
20
+ #include <unistd.h>
21
+ #include <libgen.h>
22
+ #include <errno.h>
23
+ #include <sys/stat.h>
24
+
25
+ /* Default shim path on Termux */
26
+ #define DEFAULT_SHIM "/data/data/com.termux/files/usr/lib/bun-android-shim.so"
27
+
28
+ int main(int argc, char *argv[]) {
29
+ if (argc < 2) {
30
+ fprintf(stderr, "Usage: bun-bundle <compiled-binary> [shim-path]\n");
31
+ fprintf(stderr, "\nPost-processes a Bun compiled binary to work on Android Termux.\n");
32
+ fprintf(stderr, "Renames <binary> -> <binary>.bun and creates a shell wrapper.\n");
33
+ fprintf(stderr, "\nExample:\n");
34
+ fprintf(stderr, " bun build --compile hello.ts\n");
35
+ fprintf(stderr, " bun-bundle hello\n");
36
+ fprintf(stderr, " ./hello # Works!\n");
37
+ return 1;
38
+ }
39
+
40
+ const char *binary = argv[1];
41
+ const char *shim_path = (argc >= 3) ? argv[2] : DEFAULT_SHIM;
42
+
43
+ /* Build the .bun filename: binary + ".bun" */
44
+ size_t binlen = strlen(binary);
45
+ char *bun_binary = malloc(binlen + 5);
46
+ if (!bun_binary) { perror("malloc"); return 1; }
47
+ memcpy(bun_binary, binary, binlen);
48
+ memcpy(bun_binary + binlen, ".bun", 5);
49
+
50
+ /* Check the original binary exists */
51
+ if (access(binary, F_OK) < 0) {
52
+ fprintf(stderr, "Error: %s not found\n", binary);
53
+ free(bun_binary);
54
+ return 1;
55
+ }
56
+
57
+ /* Rename binary -> binary.bun */
58
+ if (rename(binary, bun_binary) < 0) {
59
+ fprintf(stderr, "Error renaming %s -> %s: %s\n",
60
+ binary, bun_binary, strerror(errno));
61
+ free(bun_binary);
62
+ return 1;
63
+ }
64
+
65
+ /* Get basename for the wrapper */
66
+ char *bin_copy = strdup(binary);
67
+ const char *base = basename(bin_copy);
68
+
69
+ /* Create the shell wrapper */
70
+ FILE *f = fopen(binary, "w");
71
+ if (!f) {
72
+ fprintf(stderr, "Error creating wrapper %s: %s\n",
73
+ binary, strerror(errno));
74
+ rename(bun_binary, binary);
75
+ free(bun_binary);
76
+ free(bin_copy);
77
+ return 1;
78
+ }
79
+
80
+ fprintf(f, "#!/data/data/com.termux/files/usr/bin/bash\n");
81
+ fprintf(f, "# Auto-generated wrapper for Bun compiled binary\n");
82
+ fprintf(f, "# bun-bundle post-processor\n");
83
+ fprintf(f, "export LD_PRELOAD=%s\n", shim_path);
84
+ fprintf(f, "exec \"$(dirname \"$0\")/%s.bun\" \"$@\"\n", base);
85
+
86
+ fclose(f);
87
+ chmod(binary, 0755);
88
+
89
+ printf("Bundled: %s -> %s (wrapper) + %s.bun (binary)\n", binary, binary, binary);
90
+ printf("Shim: %s\n", shim_path);
91
+ printf("\nRun it: ./%s\n", base);
92
+
93
+ free(bun_binary);
94
+ free(bin_copy);
95
+ return 0;
96
+ }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- KARNEL_VERSION="4.9.0"
3
+ KARNEL_VERSION="4.9.2"
4
4
 
5
5
  # -------------------------
6
6
  # Directorios del usuario
@@ -556,6 +556,7 @@ progress_start() {
556
556
  local total=$1
557
557
  local message="${2:-Progress}"
558
558
  _progress_current=0
559
+ _progress_filled=0
559
560
  _progress_total=$total
560
561
  _progress_width=50
561
562
  printf " ${D_CYAN}%s${D_NC}" "$message"
@@ -572,15 +573,22 @@ progress_update() {
572
573
  local current=$1
573
574
  local total=$2
574
575
  local width="${_progress_width:-50}"
575
- local percentage=$((current * 100 / total))
576
- local filled=$((current * width / total))
577
- local empty=$((width - filled))
578
-
579
- local bar=""
580
- for ((i = 0; i < filled; i++)); do bar+="█"; done
581
- for ((i = 0; i < empty; i++)); do bar+="░"; done
576
+ local target_filled=$((current * width / total))
577
+
578
+ local prev="${_progress_filled:-0}"
579
+ [[ "$target_filled" -lt "$prev" ]] && prev=0
580
+
581
+ local anim i j bar pct
582
+ for ((i = prev + 1; i <= target_filled; i++)); do
583
+ bar=""
584
+ for ((j = 0; j < i; j++)); do bar="${bar}█"; done
585
+ for ((j = i; j < width; j++)); do bar="${bar}░"; done
586
+ pct=$((i * 100 / width))
587
+ printf "\r ${D_CYAN}[${D_NC}${D_GREEN}%s${D_NC}${D_CYAN}]${D_NC} %3d%%" "$bar" "$pct"
588
+ sleep 0.003 2>/dev/null || true
589
+ done
582
590
 
583
- printf "\r ${D_CYAN}[${D_NC}${D_GREEN}%s${D_NC}${D_CYAN}]${D_NC} %3d%%" "$bar" "$percentage"
591
+ _progress_filled="$target_filled"
584
592
  }
585
593
 
586
594
  progress_done() {
@@ -98,8 +98,8 @@ _get_remote_github_version() {
98
98
  tag=$(echo "$raw" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
99
99
 
100
100
  if [ -z "$tag" ]; then
101
- raw=$(_spin_capture "Checking GitHub tags" curl -fsSL "https://api.github.com/repos/$repo/tags?per_page=5" 2>/dev/null)
102
- tag=$(echo "$raw" | grep '"name":' | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
101
+ raw=$(_spin_capture "Checking GitHub tags" curl -fsSL "https://api.github.com/repos/$repo/tags?per_page=100" 2>/dev/null)
102
+ tag=$(echo "$raw" | grep '"name":' | sed -E 's/.*"([^"]+)".*/\1/' | sort -V | tail -1)
103
103
  fi
104
104
 
105
105
  _parse_version "$tag"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karnel-termux",
3
- "version": "4.9.0",
3
+ "version": "4.9.2",
4
4
  "description": "Modular Dev Environment for Termux — install languages, databases, AI agents, editors, and more with one command",
5
5
  "bin": {
6
6
  "karnel": "karnel/bin/karnel"