A execve(3)
(and exec family of functions) wrapper to fix two problems with exec-ing files in Termux.
Android 10 started blocking executing files under the app data directory, as that is a W^X violation - files should be either writeable or executable, but not both. Resources:
- Google Android issue
- Termux: No more exec from data folder on targetAPI >= Android Q
- Termux: Revisit the Android W^X problem
While there is merit in that general principle, this prevents using Termux and Android as a general computing device, where it should be possible for users to create executable scripts and binaries.
Create an exec
interceptor using LD_PRELOAD,
that instead of executing an ELF file directly, executes /system/bin/linker64 /path/to/elf
.
Explanation follows below.
On Linux, the kernel is normally responsible for loading both the executable and the
dynamic linker. The executable is invoked
by file path with the execve system call.
The kernel loads the executable into the process, and looks for a PT_INTERP
entry in
its ELF program header table
of the file - this specifies the path to the dynamic linker (/system/bin/linker64
for 64-bit Android).
There is another way to load the two ELF objects:
since 2018
the dynamic linker can be invoked directly with exec
.
If passed the filename of an executable, the dynamic linker will load and run the executable itself.
So, instead of executing path/to/mybinary
, it's possible to execute
/system/bin/linker64 /absolute/path/to/mybinary
(the linker needs an absolute path).
This is what termux-exec
does to circumvent the block on executing files in the data
directory - the kernel sees only /system/bin/linker64
being executed.
This also means that we need to extract shebangs. So for example, a call to execute:
./path/to/myscript.sh <args>
where the script has a #!/path/to/interpreter
shebang, is replaced with:
/system/bin/linker64 /path/to/interpreter ./path/to/myscript.sh <args>
Implications:
-
It's important that
LD_PRELOAD
is kept - see e.g. this change in sshd. We could also consider patching this exec interception into the build process of termux packages, soLD_PRELOAD
would not be necessary for packages built by the termux-packages repository. -
The executable will be
/system/bin/linker64
. So some programs that inspects the executable name (on itself or other programs) using/proc/${PID}/exec
or/proc/${PID}/comm
(where$(PID}
could beself
, for the current process) needs to be changed to instead inspectargv0
of the process instead. See this llvm driver change and this pgrep/pkill change. -
Statically linked binaries will not work. These are rare in Android and Termux, but zig currently produces statically linked binaries against musl libc.
-
The interception using
LD_PRELOAD
will only work for programs using the C library wrappers for executing a new process, not when using theexecve
system call directly. Luckily most programs do use this. Programs using raw system calls needs to be patched or be run under proot.
NOTE: The above example used /system/bin/linker64
- on 32-bit systems, the corresponding
path is /system/bin/linker
.
A lot of Linux software is written with the assumption that /bin/sh
, /usr/bin/env
and similar file exists. This is not the case on Android where neither /bin/
nor /usr/
exists.
When building packages for Termux those hard-coded assumptions are patched away - but this does not help with installing scripts and programs from other sources than Termux packages.
Create an execve()
wrapper that rewrites calls to execute files under /bin/
and /usr/bin
into the matching Termux executables under $PREFIX/bin/
and inject that into processes
using LD_PRELOAD
.
This termux-exec
package comes preinstalled as an essential package in Termux. Each time a
process is spawned by the app, the LD_PRELOAD=$PREFIX/lib/libtermux-exec.so
environment variable
is setup by the Android app. This environment variable needs to be kept at all times when executing
files inside Termux.
In the future the linker --wrap
functionaliy might be used to avoid having to rely on LD_PRELOAD
always being present.