1. 程式人生 > >Android編譯環境

Android編譯環境

Android 編譯環境提 供了 ”showcommands” 選項來顯示編譯命令列, 我們可以通過開啟這個選項來檢視一些編譯時的細節。當然,在這之前要把上一篇中的 helloworld 模組 clean:

# make clean-helloworld

上面的 “make clean-$(LOCAL_MODULE)” Android 編譯環境 提供的 make clean 的方式。

接下來使用 showcommands 選項 重新編譯 helloworld:

# make helloworld showcommands

build/core/product_config.mk:229: WARNING: adding test OTA key

target thumb C: helloworld <= development/hello/hello.c

prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -I system/core/include -I hardware/libhardware/include -I hardware/ril/include -I dalvik/libnativehelper/include -I frameworks/base/include -I external/skia/include -I out/target/product/generic/obj/include -I bionic/libc/arch-arm/include -I bionic/libc/include -I bionic/libstdc++/include -I bionic/libc/kernel/common -I bionic/libc/kernel/arch-arm -I bionic/libm/include -I bionic/libm/include/arch/arm -I bionic/libthread_db/include -I development/hello -I out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates -c -fno-exceptions -Wno-multichar -march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -include system/core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -DSK_RELEASE -DNDEBUG -O2 -g -Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -DNDEBUG -UDEBUG -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -MD -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o development/hello/hello.c

target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld)

prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-g++ -nostdlib -Bdynamic -Wl,-T,build/core/armelf.x -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld -Lout/target/product/generic/obj/lib -Wl,-rpath-link=out/target/product/generic/obj/lib -lc -lstdc++ -lm out/target/product/generic/obj/lib/crtbegin_dynamic.o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o -Wl,--no-undefined prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/../lib/gcc/arm-eabi/4.2.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o

target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/helloworld)

out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld out/target/product/generic/symbols/system/bin/helloworld

target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld)

out/host/linux-x86/bin/soslim --strip --shady --quiet out/target/product/generic/symbols/system/bin/helloworld --outfile out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld

Install: out/target/product/generic/system/bin/helloworld

out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld out/target/product/generic/system/bin/helloworld

從上面的命令列可以看到, Android 編譯環境所用的交叉編譯工具鏈是 prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -I -L 引數指定了所用的 C 庫標頭檔案和動態庫檔案路徑分別是 bionic/libc/include out/target/product/generic/obj/lib ,其他還包括很多編譯選項以及 -D 所定義的預編譯巨集。

我們可以利用上面的編譯命令,稍加簡化來手工編譯 helloworld 程式。先手工刪除上次編 譯得到的 helloworld 程式:

# rm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o

# rm out/target/product/generic/system/bin/helloworld

再用 gcc 編譯,生成目標檔案:

# prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -I bionic/libc/arch-arm/include -I bionic/libc/include -I bionic/libc/kernel/common -I bionic/libc/kernel/arch-arm -c -fno-exceptions -Wno-multichar -march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -include system/core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -DSK_RELEASE -DNDEBUG -O2 -g -Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -DNDEBUG -UDEBUG -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -MD -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o development/hello/hello.c

Android.mk 編譯引數比較,上面主要減少了不必要的 -I 引數。

接下來生成可執行檔案:

# prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -nostdlib -Bdynamic -Wl,-T,build/core/armelf.x -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld -Lout/target/product/generic/obj/lib -Wl,-rpath-link=out/target/product/generic/obj/lib -lc -lm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o out/target/product/generic/obj/lib/crtbegin_dynamic.o -Wl,--no-undefined ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/../lib/gcc/arm-eabi/4.2.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o

這裡值得留意的是引數“ -Wl,-dynamic-linker,/system/bin/linker ”,它指定了 Android 專用的動態連結器 /system/bin/linker ,而不是通常所用的 ld.so

生成的可執行程式可用 file readelf 命令來檢視一下:

# file out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld

out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

# readelf -d out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld |grep NEEDED

0x00000001 (NEEDED) Shared library: [libc.so]

0x00000001 (NEEDED) Shared library: [libm.so]

這是 ARM 格式的動態連結可執行檔案,執行時需要 libc.so libm.so 。“ not stripped ” 表示它還沒被 STRIP 。嵌入式系統中為節省空間通常將編譯完成的可執行檔案或動態庫進行 STRIP ,即去掉其中多 餘的符號表資訊。在前面“ make helloworld showcommands ”命令的最後我們也可以看到, Android 編譯環境 中使用了 out/host/linux-x86/bin/soslim 工具進行 STRIP

Android Toolchain與Bionic Libc

Android 所用的 Toolchain (即交叉編譯工具鏈)可 從下面的網址下載:

。如果下載了完整的 Android 專案的原始碼,則可以在“ <your_android>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin ”目錄下找到交叉編譯工具,比如 Android 所用的 arm-eabi-gcc-4.2.1

Android 並沒有採用 glibc 作為 C 庫,而是採用了 Google 自己開發的 Bionic Libc ,它的官方 Toolchain 也是基於 Bionic Libc 而並非 glibc 的。這使得使用或移植其他 Toolchain 來用於 Android 要比較麻煩:在 Google 公佈用於 Android 的官方 Toolchain 之前,多數的 Android 愛好者使用的 Toolchain 是在 下載的一個通用的 Toolchain ,它用來編譯和移植 Android Linux 核心是可行的,因為核心並不需要 C 庫,但是開發 Android 的應用程式時,直接採用或者移植其他的 Toolchain 都比較麻煩,其他 Toolchain 編譯的應用程式只能採用靜態編譯的方式才能運行於 Android 模擬器中,這顯然是實際開發中 所不能接受的方式。目前尚沒有看到說明成功移植其他交叉編譯器來編譯 Android 應用程式的資料。

glibc 相比, Bionic Libc 有如下一些特點:

- 採用 BSD License ,而不是 glibc GPL License

- 大小隻有大約 200k ,比 glibc 差不多小一半,且比 glibc 更快;

- 實現了一個更小、更快的 pthread

- 提供了一些 Android 所需要的重要函式,如 ”getprop”, “LOGI” 等;

- 不完全支援 POSIX 標準,比如 C++ exceptions wide chars 等;

- 不提供 libthread_db libm 的實現

另外, Android 中所用的其他一些二進位制工具也比較特殊:

- 載入動態庫時使用的是 /system/bin/linker 而不是常用的 /lib/ld.so;

- prelink 工具不是常用的 prelink 而是 apriori ,其原始碼位於 ” <your_android>/build/tools/apriori”

- strip 工具也沒有采用常用的 strip ,即“ <your_android>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin ”目錄下的 arm-eabi-strip ,而是位於 <your_android>/ out/host/linux-x86/bin/ soslim 工具。