Skip to content

--emit=exe and --emit=dll link for Android - #508

Merged
ASDAlexander77 merged 1 commit into
mainfrom
android-link
Oct 5, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
android-link

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

What

A triple <arch>-linux-android<api> now links with --emit=exe and --emit=dll, through the Android NDK (--android-ndk-path, or ANDROID_NDK_HOME). --emit=dll is what an app loads with System.loadLibrary; --emit=exe shares the same code and suits adb shell runs.

tslang --emit=dll -mtriple=aarch64-linux-android29 --opt -mm=gc ^
    --default-lib-path=<DefaultLib>\__build\android\arm64-v8a ^
    --gc-lib-path=3rdParty\gc\android\arm64-v8a\release\lib ^
    --tslang-lib-path=__build\tslang-runtime\release\android\arm64-v8a ^
    mylib.ts -o libmylib.so

How

  • NDK: the clang driver gets --sysroot and -resource-dir from the NDK. The host tag (windows-x86_64, linux-x86_64, …) and the clang version are found by listing directories, not hardcoded. From those, the driver itself picks Bionic's startup objects and libraries for the API level, compiler-rt's builtins and libunwind.a; tslang's clang 22 driver handles the NDK's clang 21 layout.
  • PIC by default for Android, in both obj.cpp and buildExe: executables must be PIE, and LLVM's default is static. -relocation-model=pic is no longer needed.
  • One self-contained binary. Static libc++ + libc++abi (-l:libc++.a). No -lpthread or -lrt (part of Bionic's libc) and no -lcurl (the Android default library uses http_stub.cpp). --emit=dll links the static default library: the shared one leaves GC_* to the hosting process, and an app's process (the Java VM) has no collector. The output depends only on libc.so, libm.so and libdl.so.
  • --no-undefined for an Android .so: a symbol Bionic lacks fails the link, not the app's System.loadLibrary.
  • Clear errors for a triple with no API level (the driver would silently take the oldest sysroot), no NDK, or a directory that is not an NDK.
  • checkGCLibPath/checkTslangLibPath name the library as the target does (libgc.a), not the host (gc.lib). That's the same bug class as Codegen picks the target's CRT, not the host's (Android cross-compile) #506.

Verification

  • Through tslang, for arm64-v8a and x86_64, under gc and none, both exe and dll:
    • all link;
    • the exe is PIE with /system/bin/linker64 as its interpreter;
    • the dependencies are libc, libm and libdl;
    • no strong undefined symbols remain against the API 29 Bionic stubs;
    • the dll exports the TS export function by its plain name and has .init_array (top-level code runs at load).
  • A debug (--di) dll links with DWARF.
  • New test-compile-android-codegen runs everywhere, with no NDK: it checks Android code is position independent without -relocation-model=pic, and the API-level and missing-NDK errors. The PIC check discriminates: x86_64 Linux code without PIC takes the same constant as an absolute immediate, and the test asserts that too.
  • New test-compile-android-link links an exe and a .so for x86_64/gc. It's skipped (not failed) without ANDROID_NDK_HOME and the Android builds from Android: build scripts for the collector and the async runtime #507 and DefaultLib Cannot build config tsc debug: MLIR not found #22.
  • Full Release suite on Windows (with the NDK set): 3855/3855 passed.
  • Link-only: nothing has run on a device or emulator. -mm=rc through tslang wasn't exercised (only through the earlier manual NDK link).

Known limitation: -mm=gc in an app library

GC_init and GC_enable_threads are injected into the module's global constructor (or main), so a gc .so with top-level code starts its collector at load; without either, Boehm initializes lazily. But Boehm only scans the stacks of threads registered with it. A JVM thread calling into the library (any JNI thread other than the one that loaded it) isn't registered, so under -mm=gc objects allocated or held there aren't safe yet. -mm=rc and -mm=none aren't affected. This needs a device or emulator run to confirm and fix.

Follow-ups

🤖 Generated with Claude Code

A triple <arch>-linux-android<api> now links through the Android NDK
(--android-ndk-path or ANDROID_NDK_HOME): the driver gets the NDK's sysroot
and clang resource directory, the host tag and clang version found by
listing them, and from those takes Bionic's startup objects and libraries
for the API level, compiler-rt's builtins and libunwind.

- Objects and links default to PIC for Android (obj.cpp, buildExe):
  executables must be PIE, and LLVM's default is static.
- libc++ and libc++abi are linked statically (-l:libc++.a); no -lpthread,
  -lrt (part of Bionic's libc) or -lcurl (the Android default library has
  http_stub.cpp).
- --emit=dll links the static default library, so the .so an app loads is
  self-contained: the shared one leaves GC_* to the process, and an app's
  process has no collector. It links with --no-undefined, so a symbol Bionic
  lacks is a link error rather than an UnsatisfiedLinkError in the app.
- A triple with no API level, a missing NDK and a directory that is not one
  are refused with an error that says so.
- checkGCLibPath/checkTslangLibPath name the library as the target does
  (libgc.a) rather than the host (gc.lib).

test-compile-android-codegen (no NDK needed) checks Android objects are PIC
and the two errors; test-compile-android-link links an exe and a .so for
x86_64 Android, and is skipped without the NDK and the Android libraries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit b6c8700 into main Oct 5, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the android-link branch October 5, 2026 14:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant