agcc.bash: Make Native Android C Programs Using the NDK

Sunday, January 8, 2012

I return to school at noon, but before then, I’d like to share my evening project: agcc.bash, a rewrite of agcc and agcc2.pl that works with revisions 6 and 7 of the Android NDK.

The NDK is a GCC-based cross-compiler that lets you embed object code in a traditional Android app. It also works as a standalone cross-compiler, though it is not advertised as such. It is quite rough when used by itself, and agcc wraps the complexity in a little script. While agcc and agcc2.pl are written in Perl, agcc.bash is a Bash script. It also supports the latest revisions of the NDK, though r7 is slightly buggy.

With agcc.bash, building native Android software feels almost like working with GCC. As a quick example, let’s try the classic hello world:

AGCC_NDK=~/android-ndk-r6 ./agcc.bash hello.c -o hello

That wasn’t too exciting… but what happened there? I have the NDK installed at ~/android-ndk-r6 and I told agcc.bash where to look for it, using the AGCC_NDK environment variable. I then invoked agcc.bash as if it were GCC. Let’s see what agcc.bash did behind the scenes:

AGCC_NDK=~/android-ndk-r6 AGCC_ECHO=yes ./agcc.bash hello.c -o hello

=> ./agcc.bash hello.c -o hello
<= /home/jyio/android-ndk-r6/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gcc -o hello -I/home/jyio/android-ndk-r6/platforms/android-8/arch-arm/usr/include -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID -DSK_RELEASE -DNDEBUG -UDEBUG -march=armv5te -mtune=xscale -msoft-float -mthumb-interwork -fpic -fno-exceptions -ffunction-sections -funwind-tables -fmessage-length=0 -march=armv5te -mtune=xscale -msoft-float -mthumb-interwork -fpic -fno-exceptions -ffunction-sections -funwind-tables -fmessage-length=0 hello.c -Bdynamic -Wl,-T,/home/jyio/android-ndk-r6/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/arm-linux-androideabi/lib/ldscripts/armelf_linux_eabi.x -Wl,-dynamic-linker,/system/bin/linker -Wl,–gc-sections -Wl,-z,nocopyreloc -Wl,–no-undefined -Wl,-rpath-link=/home/jyio/android-ndk-r6/platforms/android-8/arch-arm -L/home/jyio/android-ndk-r6/platforms/android-8/arch-arm/usr/lib -nostdlib /home/jyio/android-ndk-r6/platforms/android-8/arch-arm/usr/lib/crtend_android.o /home/jyio/android-ndk-r6/platforms/android-8/arch-arm/usr/lib/crtbegin_dynamic.o -lc /home/jyio/android-ndk-r6/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/lib/gcc/arm-linux-androideabi/4.4.3/libgcc.a -lm -ldl

See how it transformed our innocent little command into a monster! This might be overkill for a hello world program, but it provides pretty good support for more complex software such as OpenSSL, cURL, and Python. It would be a good idea to add the NDK toolchain and agcc.bash to $PATH before trying to build a big program. In my case, the toolchain resides in ~/android-ndk-r6/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin. If you want to build some software that come with configure scripts, you might start with:

CC=agcc.bash ./configure

Do try this at home! Relevant links:

29 Comments

  1. Jave says:

    Hi Jiang Yio, thank you for your script and I have successfully compiled the hello world. I see your script works with C, but is it possible to make it also work with C++? thx

    1. Jiang Yio says:

      I did some more work on agcc.bash and it’s on GitHub. For now, if you symlink/link/rename it to anything ending in -g++ or -c++ and call it that way, it invokes the C++ compiler with the same arguments. It does not automatically add the C++ headers and libraries, so you need to specify those using -I and -L. While CyanogenMod comes with libstdc++.so and libstlport.so, you cannot assume that these exist on other ROM’s, so link statically or bundle the shared object. You could find some C++ libraries in android-ndk/sources/.

      Oh, and there’re also quite a few bug fixes, and I’m now using it with r7.

      1. Stuart Ambler says:

        Thanks! I used today’s GitHub version of agcc.bash to compile hello.c for Samsung Galaxy S2 Epic 4G Touch, NDK 7b. Ok. Great to find your work!

  2. James says:

    Thank you for putting this script together. I’m able to use it to compile simple programs but how do I use it with a ./configure script?

    1. Jiang Yio says:

      You might do something like

      AGCC_NDK=~/android-ndk-r7 CC=agcc.bash ./configure
      AGCC_NDK=~/android-ndk-r7 make

      Of course, you’re going to need all of the dependencies for that program.

      Feel free to have a look at the GitHub repo for more examples of what could be done. It contains an updated agcc.bash, as well as a build environment. A lot of common libraries have already been ported, which might help you along.

      1. James says:

        I guess I need to know how your environment is setup because when I was compiling I was using –host with the ./configure script. I’ll take a look at your build environment in the GitHub repo.

        1. Jiang Yio says:

          If you use –host, use either arm-linux-androideabi or arm-android-eabi.

          1. James says:

            I figured out my issue and it was that your script is just too good :-) Seriously, do you have a paypal account for donations? My script to setup my environment did not need to exist and was causing ./configure scripts to not find arm-linux-androideabi-gcc when I pointed it to agcc.bash. I had no problem using it to compile a simple piece of code like hello.c and now I’m able to compile Nmap. I have a lot to learn going through your botbrew (great name!) and hopefully I can learn enough to be able to contribute but at the very least compile the tools I want to run on my Android phone.

          2. Jiang Yio says:

            Good to see that you got it working! Sometimes you need the other arm-linux-androideabi-* tools, which you could specify while configuring like STRIP=”arm-linux-androideabi-strip –strip-unneeded” and so on. I set up the BotBrew build system because I got tired of manually satisfying dependencies. It gives me great satisfaction to build programs that are useful to me and others, so I don’t request donations, but it’s nice to see the occasional “it works!” :)

  3. jfmherokiller says:

    i was able to compile and run from the bsd-games package (pom, ppt, fish, worm, tetris but it did not clear the screen correctly) manually using a native toolchain

    1. Jiang Yio says:

      Thanks for the heads-up. I’m looking into just using the NDK’s standalone toolchain option, since it’d simplify some things. It might also help to have a customized toolchain, but that’s a pretty daunting project for me at the moment. Are you running these programs using adb? Terminal app? I find that it sometimes helps to set the TERM environment variable to something sensible.

      1. jfmherokiller says:

        Im running them using Cyanogenmods built in terminal. The toolchain is not completely built by me it is the c4droid apps gcc plugin.The binutils port I use is compiled by me.


        I was hoping to use your m4 btw but it gives a segmentation fault.


        I set the TERM variable to linux i forgot to add.

        1. Jiang Yio says:

          We also don’t have working autotools because we don’t have Perl yet. Do you remember how you generated the segfault?

  4. jfmherokiller says:

    I just ran m4 in the terminal

    1. Jiang Yio says:

      m4 processed a trivial input file for me just fine. Do you have a specific example?

  5. jfmherokiller says:

    I basicly ran m4 with no arguments and it gave me a segmentation fault. I bet that m4 can’t use the libraries I have compiled.


    I took a look at perl and it failed on a try.c file which dropped me to the sh shell.

    The error is in sys/prctl.h of the system headers specificly
    "extern int prctl(int option, unsigned long arg2, unsigned long arg3,
    unsigned long arg4, unsigned long arg5);"


    the error "expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘extern’"
    removing extern does not help since it then complains about int

    1. Jiang Yio says:

      Perl’s a monstrosity :)
      I haven’t gotten it cross-compiled yet, but it needs to be modified before it’d build, and there are hardcoded values that might need to be modified before it’d be Android-friendly. Just like many other packages, of course.

  6. jfmherokiler says:

    is there an xserver dev pkg with headers? because I would like to be able to port my own window manager, like twm

    btw I just reinstalled m4 and it works

  7. Jiang Yio says:

    I actually have a working cross-build of Perl without any modules. So, it’s next to useless :)

    1. jfmherokiller says:

      well its progress.

  8. Stuart Ambler says:

    Thanks, Jiang Yio. You may have saved me from returning my new cell phone instead of keeping it, because prior to finding this page, I hadn’t found enough open source tools to get into it, and then to run on it. But I’m having problems building the entire cookbook. Got around a couple but did’t have time to figure out why dbus wouldn’t build (configure said source directory already configured, but it had been empty prior to that). If you contact me I can provide logs, and also details on the couple of minor things I fixed/changed to get on with the build. Thanks again.

    1. Stuart Ambler says:

      … tried building everything, one by one. The majority worked, but a number didn’t. Haven’t tried running them on the device yet. I took notes, if you contact me.

      1. jfmherokiller says:

        did you get bison working? because I keep getting a bitsign error apon linking attempts.

      2. Jiang Yio says:

        Yeah, some of the stuff I used to get a basic X server working is somewhat iffy. But now there’s this, which is awesome.

        I also made the mistake of removing *.la *.lo pkgconfig, thinking that they would not work in a cross-compiler environment. They actually work, and so I’m fixing it.

        And… I’m starting to migrate to the standalone-toolchain generated by the NDK. It’s not as easy to use as agcc.bash because I cannot do arbitrary things without a wrapper, but it’s actually much cleaner.

        I haven’t looked into getting Bison working, yet.

        1. jfmherokiller says:

          id ssuggest trying lynx for a quick compiile I only had to patch one thing to make it work.

          I just found a way to get byacc and flex working
          First get btyacc id suggest 2.1 since that’s what I used. Compile it then place the binary where it needs to go.Then rename it from btyacc to byacc.After That procede to compile flex.I have not tried bison yet but I believe it will now be easyer to compile.

          The steps are for native compiling I do not know if they work cross compiling.

          1. Jiang Yio says:

            Incidentally, I just got w3m cross-compiled. It’s somewhat more involved than lynx, it seems, but it works better for me so I think it’s worth the trouble.

  9. Maciek says:

    Hi!

    Nice tool! Some glitch though when building busybox:

    https://github.com/jyio/botbrew/issues/1

    Can you help?

  10. Marco77 says:

    Thanks Jiang for your hardwork.
    I managed to cross-compile on Cygwin using the official NDK (r8) distribution.

    Here are the commands I passed:

    pastie.org/4394514

    It results in an “ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped” binary and runs correctly on the device.

    However, using the newer script on Github, it won’t link:

    $ AGCC_ECHO=yes agcc -o hello2 hello.c
    => /home/Beta/agccwin.bash -o hello2 hello.c
    <= c:/Tools/android-ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/bin/arm-linux-androideabi-gcc -o hello2 -march=armv5te -mtune=xscale -msoft-float -mthumb-interwork -fpic -fno-exceptions -ffunction-sections -funwind-tables -fmessage-length=0 hello.c -Ic:/Tools/android-ndk/platforms/android-8/arch-arm/usr/include -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID -DSK_RELEASE -DNDEBUG -UDEBUG -Wl,-rpath-link=c:/Tools/android-ndk/platforms/android-8/arch-arm/usr/lib -Lc:/Tools/android-ndk/platforms/android-8/arch-arm/usr/lib -Lc:/Tools/android-ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/lib/gcc/arm-linux-androideabi/4.4.3 -Bdynamic -Wl,-T,c:/Tools/android-ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/arm-linux-androideabi/lib/ldscripts/armelf_linux_eabi.x -Wl,-dynamic-linker,/system/bin/linker -Wl,–gc-sections -Wl,-z,nocopyreloc -Wl,–no-undefined -nostdlib c:/Tools/android-ndk/platforms/android-8/arch-arm/usr/lib/crtend_android.o c:/Tools/android-ndk/platforms/android-8/arch-arm/usr/lib/crtbegin_dynamic.o c:/Tools/android-ndk/sources/cxx-stl/gnu-libstdc++/libs/armeabi/libgnustl_static.a -lc -ldl -lgcc -lm -std=gnu99
    arm-linux-androideabi-gcc.exe: c:/Tools/android-ndk/sources/cxx-stl/gnu-libstdc++/libs/armeabi/libgnustl_static.a: No such file or directory

    The "libs" directory mentioned above is missing
    Does it mean I have to rebuild-all-prebuilt.sh?

    1. Jiang Yio says:

      I believe you could extract it from a prebuilt NDK by Google. The C++ standard library isn’t included by default.