Android printf failes with setfault on `movaps`, caused by %rbp not being 16-bit aligned

6 days ago 8
ARTICLE AD BOX

I have 2 files:

crt.c:

const char service_interp[] __attribute__((section(".interp"))) = "${LD_SO}"; extern void _exit (int __status) __attribute__ ((__noreturn__)); int main(); void _start() { _exit(main()); }

Main.c:

#include <jni.h> #include <stdio.h> #include <unistd.h> jint Java_Main_Main(JNIEnv*,jclass); int main() { const char * str = "Hello from executable\n"; write(1,str,strlen(str)); return Java_Main_Main(NULL,NULL); } __attribute__((constructor)) void init() { printf("Hello from constructor\n"); } __attribute__((destructor)) void fin() { printf("Hello from destructor"); } jint Java_Main_Main(JNIEnv * env, jclass Main) { printf("Hello from shared object\n"); return 0; }

that I compile with

clang version 21.1.6 Target: x86_64-unknown-linux-android24 Thread model: posix InstalledDir: /data/data/com.termux/files/usr/bin sed "s/\${LD_SO}/$(./getldso.sh | sed "s/\//\\\\\//g")/g" crt.c | clang -x c - -c -fPIC -o crt.o clang Main.c -g -fPIC -o libMain.so -DLD_SO=$(./getldso.sh) crt.o -shared

getldso.sh finds and prints the path to the sysytem linker (/system/bin/linker64 here)

If I load the file using Java's System.loadLibrary or C's dlopen it correctly prints

Hello from constructor Hello from shared object Hello from destructor // only in C if I use `dlclose`

But if I try to execute it (./libMain.so) it failes:

Hello from constructor Hello from executable Segmentation fault

Upon trying to debug it, I found that it throws the segfault in the second printf call

┌──────────────────────────────────────────────────────────────┐ │ 0x7ffff7a9f6e0 push %rbp │ │ 0x7ffff7a9f6e1 mov %rsp,%rbp │ │ 0x7ffff7a9f6e4 push %r14 │ │ 0x7ffff7a9f6e6 push %rbx │ │ 0x7ffff7a9f6e7 sub $0xd0,%rsp │ │ 0x7ffff7a9f6ee mov %rdi,%rbx │ │ 0x7ffff7a9f6f1 mov %rsi,-0xd8(%rbp) │ │ 0x7ffff7a9f6f8 mov %rdx,-0xd0(%rbp) │ │ 0x7ffff7a9f6ff mov %rcx,-0xc8(%rbp) │ │ 0x7ffff7a9f706 mov %r8,-0xc0(%rbp) │ │ 0x7ffff7a9f70d mov %r9,-0xb8(%rbp) │ │ 0x7ffff7a9f714 test %al,%al │ │ 0x7ffff7a9f716 je 0x7ffff7a9f741 │ │ 0x7ffff7a9f718 movaps %xmm0,-0xb0(%rbp) │ │ 0x7ffff7a9f71f movaps %xmm1,-0xa0(%rbp) │ │ 0x7ffff7a9f726 movaps %xmm2,-0x90(%rbp) │ │ 0x7ffff7a9f72d movaps %xmm3,-0x80(%rbp) │ │ 0x7ffff7a9f731 movaps %xmm4,-0x70(%rbp) │ │ 0x7ffff7a9f735 movaps %xmm5,-0x60(%rbp) │ │ 0x7ffff7a9f739 movaps %xmm6,-0x50(%rbp) │ │ 0x7ffff7a9f73d movaps %xmm7,-0x40(%rbp) │ │ 0x7ffff7a9f741 mov %fs:0x28,%rax │ │ 0x7ffff7a9f74a mov %rax,-0x18(%rbp) │ │ 0x7ffff7a9f74e xorps %xmm0,%xmm0 │ │ >0x7ffff7a9f751 movaps %xmm0,-0x30(%rbp) │ │ 0x7ffff7a9f755 lea -0xe0(%rbp),%rax │ │ 0x7ffff7a9f75c mov %rax,-0x20(%rbp) │ │ 0x7ffff7a9f760 movabs $0x3000000008,%rax │ │ 0x7ffff7a9f76a mov %rax,-0x30(%rbp) │ │ 0x7ffff7a9f76e lea 0x10(%rbp),%rax │ │ 0x7ffff7a9f772 mov %rax,-0x28(%rbp) │ │ 0x7ffff7a9f776 mov 0x655b3(%rip),%rax # 0x7ffff7b04d30 │ │ 0x7ffff7a9f77d mov (%rax),%r14 │ │ 0x7ffff7a9f780 mov 0x58(%r14),%rdi │ │ 0x7ffff7a9f784 cmpb $0x0,0x60(%rdi) │ │ 0x7ffff7a9f788 jne 0x7ffff7a9f793 │ │ 0x7ffff7a9f78a add $0x38,%rdi │ │ 0x7ffff7a9f78e call 0x7ffff7afd9d0 │ └──────────────────────────────────────────────────────────────┘ Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7a9f751 in ?? ()

and the %rbp register has the value 0x7fffffffdad8, so %rbp-0x30 is not 16-bit aligned, which causes the segfault to be thrown.

When I compile the same code (with the exception of the ld.so's path and that I have to pass -I /usr/lib/jvm/java-25-openjdk-amd64/include -iquote /usr/lib/jvm/java-25-openjdk-amd64/include/linux when compiling) on Linux, the code executes correctly.

clang version:

$ clang -v Ubuntu clang version 18.1.3 (1ubuntu1) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin Found candidate GCC installation: /usr/bin/../lib/gcc/i686-linux-gnu/14 Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/13 Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/14 Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/14 Candidate multilib: .;@m64 Selected multilib: .;@m64

Is aligning %rsp in _start enough to make libc functions to work (it fixed printf on Android), or is it just a "hacky" fix, that does't solve the main problem and I need to do something else/more (maybe call some kind of a __libc_init function)? I would also like to know if that printf fail is considered a "bug" (in that case I would only need to align %rsp when compiling for android), or is what I'm doing implemtation defined (so I need to do it always, 'cause it might break in another version of libc on Linux).

Read Entire Article