That was fun, but who wants a 500+ KB file that only displays something to the screen?
Recall that the –static flag links the essentials for running the application, including the input/output routines required for printing a message to the screen. If you’re thinking that there must be a better way, you’re correct; you need to link the applica- tion to existing system libraries rather than include all that code in the application’s executable file.
Listing 13.2 Build script for Hello Android, buildhello.bat
13.2.1 Android system libraries
When an application is built with the –static flag, it’s entirely self-contained, meaning that all the routines it requires are linked directly into the application. This information isn’t new to you; we’ve already discussed this. It has another important implication beyond just the size of the code: it also means that using Android resi- dent code libraries is a bigger challenge.
Let’s dig deeper to understand why. To do this, we have to look at the filesystem of Android/Linux.
System libraries in Android/Linux are stored in the directory /system/lib. This directory contains important functionality, such as OpenGL, SQLite, C standard rou- tines, Android runtime, UI routines, and much more. Figure 13.3 shows a list of the available libraries in the Android Emulator.
In short, everything that’s specific to the Android platform is found in /system/lib, so if you’re going to build an application that has any significant functionality, you can’t rely on the libraries that ship with CodeSourcery alone. You have to write an application that can interact with the Android system libraries. This calls for a side trip to discuss the functionality of the linker application.
When you’re building an application that requires the use of the linker, a few things change. First, the gcc command is
no longer responsible for invoking the linker. Instead, the –c option is used to inform the tool to simply compile the application and leave the link step to a subsequent build step. Here’s an example:
arm-none-linux-gnueabi-gcc –c hello.c -o hello.o
This command tells the compiler to compile the file hello.c and place the resulting object code into the file hello.o.
This process is repeated for as many source files as necessary for a particular appli- cation. For our sample application, you have only this single source file. But to get an executable application, you must employ the services of the linker.
Figure 13.3 Available libraries in /system/lib
Another important change in the build environment is that you have to get a copy of the Android/Linux libraries. You’re compiling on the Windows platform (or Linux if you prefer), so you need to get access to the Android Emulator’s /system/lib con- tents in order to properly link against the library files. Just how do you go about this?
You use the adb utility, of course! Listing 13.3 shows a Windows batch file used to extract the system libraries from a running instance of the Android Emulator. A few of the libraries are pointed out.
adb pull /system/lib/libdl.so m:\android\system\lib adb pull /system/lib/libthread_db.so m:\android\system\lib adb pull /system/lib/libc.so m:\android\system\lib
adb pull /system/lib/libm.so m:\android\system\lib adb pull /system/lib/libGLES_CM.so m:\android\system\lib adb pull /system/lib/libssl.so m:\android\system\lib ...
adb pull /system/lib/libhardware.so m:\android\system\lib adb pull /system/lib/libsqlite.so m:\android\system\lib many entries omitted for brevity
Figure 13.4 shows these files now copied over to the development machine.
Once these files are available on the development machine, you can proceed with the build step using the linker.
13.2.2 Building a dynamically linked application
The name for the linker is arm-none-linux-gnueabi-ld. In most Linux environ- ments, the linker is named simply ld. When you’re using the linker, many command- line options are available to you for controlling the output. There are so many options that we could write an entire book covering no other topic. Our interest in this chap- ter is writing applications, and we’re taking as streamlined an approach as possible. So although there may be other options that can get the job done, our aim here is to show you how to build an application that gives you as much flexibility as possible to employ the Android system libraries. To that end, the following listing shows the script for building a dynamic version of Hello Android.
arm-none-linux-gnueabi-gcc -c hello.c -o hello.o
arm-none-linux-gnueabi-ld -entry=main -dynamic-linker /system/bin/linker -nostdlib -rpath /system/lib -rpath-link /android/system/lib -L
/android/system/lib -l android_runtime -l c -o hellodynamic hello.o
g:\tools\adb push hellodynamic /data/ch13
g:\tools\adb shell "chmod 777 /data/ch13/hellodynamic"
This build script passes the –c compiler option when compiling the source file, hello.c. This way, gcc doesn’t attempt to link the application. The link command, arm- none-linux-gnueeabi-ld, has a number of options. These options are more fully
Listing 13.3 pullandroid.bat
Listing 13.4 Build script for dynamically linked Android application
libdl.so, dynamic loading libc.so, C runtime libm.so, math library
libGLES_CM.so, OpenGL
libsqlite.so, SQLite database
described in table 13.1. As in the previous example, adb is used to push the executable file over to the Android Emulator. The permissions are also modified to mark the application as executable.
If our application required routines from the Open GL or SQLite library, the link command would have additional parameters of –l GLES_CM or –l sqlite, respec- tively. Leaving those library options off the link command prevents the application from linking properly because certain symbols (functions, data) can’t be found.
So, did it work? The hellodynamic binary is now only 2504 bytes. That’s a great improvement. Figure 13.5 shows a listing of the two Hello Android files for a
Figure 13.4 Android libraries pulled to the development machine
remarkable comparison. Each program is run: first the static version, then the dynamic version.
This looks great, except for one little problem. Note the last line in figure 13.5, which says, “Killed.” Is there a problem with our dynamic version? Let’s look closer.
Table 13.1 Linker options
Linker option Description
-entry=main Indicates the entry point for the application, in this case, the function named main.
-dynamic-linker /system/bin/linker Tells the application where the dynamic linker appli- cation may be found at runtime. The /system/bin/
linker path is found on the Android Emulator, not the development environment.
-nostdlib Tells the linker to not include standard C libraries when attempting to resolve code during the link process.
-rpath /system/lib Tells the executable where libraries can be found at runtime. This works in a manner similar to the envi- ronment variable LD_LIBRARY_PATH.
-rpath-link /android/system/lib Tells the linker where libraries can be found when linking. For Linux, add a dot to the beginning of the line, as in ./android/system/lib.
-L /android/system/lib Tells the linker where libraries can be found. This is the linker import directory.
-l android_runtime Tells the linker that this application requires routines found in the library file libandroid_runtime.so.
-l c Tells the linker that this application requires routines
found in the library file libc.so.
-o hellodynamic Requests an output filename of hellodynamic.
hello.o Includes hello.o as an input to the link process.
Figure 13.5 Hello Android, static and dynamically linked
13.2.3 exit() vs. return()
Though our application has successfully linked with the Android system libraries of libc.so and libandroid_runtime.so and can actually run, there are missing pieces that cause the application to not properly execute. When you build an application in this manner, without letting the linker do all its magic of knitting the entire application together, you have to do a bit of housekeeping yourself. Looks like there was some- thing to that 500 KB application after all!
For one thing, if our application’s entry point is the main function, and the main function executes a return() statement, just where does it return to? Let’s replace the return() statement with an exit() call, as shown in this listing.
#include <stdio.h>
int main(int argc,char * argv[]) {
printf("Hello, Android!\n");
exit(0);
//return 0;
}
We add a call to the function exit(). This should return execution to the OS. And we comment out the call to return(). A return() call in this location causes a stack underflow because there’s nowhere within this application to return to!
This fixed the problem—no more killed messages. Look at figure 13.6, where you see that the dynamic version of Hello Android now runs just fine.
Unfortunately, you’re not finished. It turns out that the application doesn’t prop- erly interact with other libraries, nor does it properly handle the argc and argv[]
arguments to the main function. The C library (remember, you’re linking against libc.so) has certain expectations for application structure and stack location. You’re closer but still not quite ready for prime time.
What this application requires is a start routine, which is called by the operating system when the application is invoked. This function in turn calls the application’s main function. This start routine must set up the necessary structures to allow the application to properly interact with the operating system and the core C libraries.
Listing 13.5 Adding an exit() call
Figure 13.6 A better- behaving dynamic version of Hello Android
13.2.4 Startup code
We’ve surmised that the sample application is missing the proper startup code, but just what does startup code for an Android/Linux application on ARM look like?
Where do you turn to get this kind of information? Let’s look deeper into the bag of CodeSourcery tricks for a clue.
A number of executable applications ship with Android, so pull one of them over to the desktop and see what you can learn. Perhaps you can extract information from that file that can assist in solving this puzzle.
The tool you’re going to use to assist in this effort is the object dump command, arm-none-linux-gnueabi-objdump. This utility has a number of options for tearing apart an ELF (executable and linkable format) file for examination. This is the kind of file structure used by applications in the Android/Linux environment. Using the –d option of the objdump command results in a disassembly of the executable file, show- ing the assembly language equivalent of the code in each executable section. Our interest is in the first .text section of the disassembly, as this ought to be the entry point of the application. The following listing shows the .text section from the ping program taken from the Android Emulator (via adbpull).
000096d0 <dlopen-0x60>:
96d0: e1a0000d mov r0, sp 96d4: e3a01000 mov r1, #0; 0x0 96d8: e28f2004 add r2, pc, #4; 0x4 96dc: e28f3004 add r3, pc, #4; 0x43 96e0: eaffff8b b 9514 <dlopen-0x21c>
96e4: ea000e03 b cef8 <dlclose+0x37bc>
96e8: 0000e408 andeq lr, r0, r8, lsl #8 96ec: 0000e410 andeq lr, r0, r0, lsl r4 96f0: 0000e418 andeq lr, r0, r8, lsl r4 96f4: 0000e420 andeq lr, r0, r0, lsr #8 96f8: e1a00000 nop (mov r0,r0)
96fc: e1a00000 nop (mov r0,r0)
The first instruction assigns the value of the stack pointer (sp) to register 0 (r0) B. Next the literal value of 0 is assigned to register r1 C. The address counter plus four memory location spaces is stored in registers r2 and r3 D. The b instruction tells the code to branch to a specific address E. In this case, the address is 0x21c bytes prior to the address of the dlopen function. This value is 9514 in decimal. The next branch is to an address that’s 0x37bc bytes beyond the dlclose label F. The next few instruc- tions G are conditional operations. The code snippet finishes up with a pair of nop instructions H. Note that the address of each instruction is shown to the left of each line. Each instruction occurs at a 4-byte offset from its predecessor. Four bytes times 6 bits per byte equals a 32-bit address bus, which makes sense because the ARM proces- sor family is 32 bit.
Listing 13.6 Disassembly of ping
Stack pointer
B
mov instruction
C
add instruction
D
Branch instruction
E
Branch instruction
F
Conditional expressions
G
nop instructions
H
Okay, so that looks different from the rest of the code in this chapter—and just what does it do? Unfortunately, other than some basic interpretation of the op codes used, there’s little to tell you why those instructions are there. After doing research on the internet, we found a better example of this code, shown in this listing.
.text
.global _start _start:
mov r0, sp mov r1, #0 add r2, pc, add r3, pc, b __libc_init b main
.word __preinit_array_start .word __init_array_start .word __fini_array_start .word __ctors_start .word 0
.word 0
.section .preinit_array __preinit_array_start:
.word 0xffffffff .word 0x00000000 .section .init_array __init_array_start:
.word 0xffffffff .word 0x00000000 .section .fini_array __fini_array_start:
.word 0xffffffff .word 0x00000000 .section .ctors __ctors_start:
.word 0xffffffff .word 0x00000000
The .text directive indicates that this code should be placed in the .text section of the resulting executable B. The globalstart directive C makes the start routine vis- ible to the rest of the application and the linker. The start: label D indicates the first location of the start routine. The mov and add instructions perform some housekeep- ing E with the stack pointer, sp, just as seen in the extracted code from the ping pro- gram. Initialization takes place via a branch instruction to call the __libc_init routine F. This routine is found in the library libc.so. When this routine is complete, execution returns to the next instruction, another branch of the main routine G. This is the main routine implemented by our C application. The next instructions H
set up a jump table to the sections required by a C language executable application. A pair of nop instructions round out the table. The sections preinit_array, init_array, fini_array, and .ctors are defined I. Note that it appears that these
Listing 13.7 crt.S
.text directive
B
global directive
C
start label
D
Set up stack pointer
E
Branch to initialization
F
Branch to main
G
Jump table
H
Required sections
I
sections are required and that the values provided are an allowable address range for these sections. The linker takes care of putting these sections into the resulting exe- cutable file. Attempting to run the application without these sections results in code that crashes.
NOTE All credit for this crt.S file belongs to the author of a blog found at http://honeypod.blogspot.com/2007/12/initialize-libc-for-android.html.
You can find additional reference material for low-level Android program- ming information at http://benno.id.au.
Now that we’ve found an adequate startup routine, we’ll show you how to add this rou- tine to your application. The compiler handles the assembly file just like a C language file:
arm-none-linux-gnueabi-gcc -c -o crt0.o crt.S
The resulting object file, crt0.o, is passed to the linker as an input file, just as any other object file would be. Also, the entry switch to the linker must now specify _start rather than main:
arm-none-linux-gnueabi-ld --entry=_start --dynamic-linker /system/bin/linker -nostdlib -rpath /android/system/lib -rpath-link /android/system/lib -L
\android\system\lib -l c -l android_runtime -l sqlite -o hellodynamic hello.o crt0.o
At this point, you should feel confident that you can build applications for Android/
Linux, so it’s time to build something useful. The next section walks through the con- struction of a DayTime Server.