[Droid Eris Android Development] bftb0:
Static OR Dynamic Linking of Native Binaries

I was a little unsatisfied with the "hack" used in the above examples to circumvent the need to launch programs without linking to C runtime initialization using that strange code shown above, i.e.

start() { exit( main() ); }

Moreover, for purposes of debugging applications running on the phone (using "gdbserver"), it simply seems easier to link statically - it allows you to step into the syscall library routines and actually see the assembler implementations wrapped around "STI" interrupts.

So, I spent some time experimenting, and came up with a Makefile that allows me to easily toggle between building an ARM executable linked statically or dynamically. Moreover, it lets "argc" and "argv" work they way they are supposed to, and also allows main() to do a return().

Here's an example Makefile, for a program "hello.c". Make sure you read the notes which follow it.

#
# FIX THESE (to match your installation)
# Top of the Android Source/build tree (retrieved w/ repo)
ANDSRCROOT := /home/user/android/repo-root
# link dynamically against the libraries shipped on the phone!
# e.g.: $cd /opt/android/Eris_libs/ruu1_16_605_1 ; adb pull /system/lib/
PHONELIBS := /opt/android/Eris_libs/ruu1_16_605_1/lib

# Choose one or the other; static is easiest for debugging use
LINKMODE := dynamic
#LINKMODE := static

# tools
CROSS_COMPILE := arm-eabi-
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
AR := $(CROSS_COMPILE)ar
CXX := $(CROSS_COMPILE)c++
OBJDUMP := $(CROSS_COMPILE)objdump
RANLIB := $(CROSS_COMPILE)ranlib
READELF := $(CROSS_COMPILE)readelf

# Note STATLIBROOT is relative to the Android Source root (ANDSRCROOT)
STATLIBROOT := out/target/product/generic/obj/STATIC_LIBRARIES
INCLUDE := -I $(ANDSRCROOT)/bionic/libc/include \
-I $(ANDSRCROOT)/bionic/libc/arch-arm/include \
-I $(ANDSRCROOT)/bionic/libc/kernel/common \
-I $(ANDSRCROOT)/bionic/libc/kernel/arch-arm

ifeq ($(LINKMODE),dynamic)
LIBDIRS := -L $(PHONELIBS)
else
LIBDIRS := -L $(ANDSRCROOT)/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/lib/gcc/arm-eabi/4.2.1 \
-L $(ANDSRCROOT)/$(STATLIBROOT)/libc_intermediates \
-L $(ANDSRCROOT)/$(STATLIBROOT)/libc_common_intermediates
endif

ifeq ($(LINKMODE),dynamic)
CRTBEG := $(ANDSRCROOT)/out/target/product/generic/obj/lib/crtbegin_dynamic.o
LDFLAGS := -rpath /system/lib -rpath-link /system/lib --dynamic-linker /system/bin/linker
EXTLIBS := -lc -ldl
else
CRTBEG := $(ANDSRCROOT)/out/target/product/generic/obj/lib/crtbegin_static.o
LDFLAGS := -static
EXTLIBS := -lc -lgcc -lc_common
endif

CRTEND := $(ANDSRCROOT)/out/target/product/generic/obj/lib/crtend_android.o
CFLAGS := -g -Wall

%.o: %.c
$(CC) -c $(INCLUDE) \
$(CFLAGS) $< -o $@

%.s: %.c
$(CC) -S -c $(INCLUDE) \
$(CFLAGS) $< -o $@

all: hello

hello: hello.o
$(LD) $(LDFLAGS) \
-o $@ --entry=_start -nostdlib \
$(CRTBEG) \
$^ \
$(CRTEND) \
$(LIBDIRS) $(EXTLIBS)

This makefile has certain assumptions built into it:

(1) Static Linkage

If you are going to link statically, you will need to have already built the entire Android source tree - the Android NDK does not provide any static libraries (In the OP I provided instructions on how to do this download and build the Android "Cupcake" release.)

(2) Dynamic Linking

If you want to compile code which is "as close to the phone as possible" then there is really only one way to do that: compile against the headers that HTC used, and perform linking against the dynamic libraries that you literally pull off the phone ("adb pull /system/lib").

There's only one problem with this strategy: HTC did not release their copy of the bionic (or other library) source code, so you are forced to punt... the second-best choice is to use headers from the Android (Cupcake) source tree. So... you will notice that for both the dynamic-link or static-link cases in the above makefile, the $(INCLUDE) paths used during compilation come from the Android source tree.

bftb0

PS I guess there is a mechanism to configure the top-level "make" of the Android source tree for not only a target type (defaults to "arm"), but also a "machine" dependency. I didn't research that, as I have only built user-space code. I'm doubt there should be any differences at the syscall interface API - excepting perhaps things like ptrace() which have a machine dependency (register structures, etc).