NDK编译基础示例

  • Post author:
  • Post category:其他


本篇博文参考了非虫大大的Android软件安全与逆向分析,这真的是一本好书,里面涵盖的内容比较全也比较基础。下面开始我们的学习。

Android为了提高效率、安全性等,提供了NDK(原生开发套件),现在我们来看看如何编译原生程序。

原生程序的编译有三种方法:

1、使用gcc手动编译

2、使用ndk-build手动编译

3、使用eclipse自动编译

我们分别使用这三种方式来分别编译看看。

准备工作:



1、下载对应版本的ndk,我所使用的平台是windows,解压到指定位置即可。ndk下载链接:

https://developer.android.com/ndk/downloads/index.html

我的路径为F:\android\android-ndk-r11b,那么工具链的完整路径为F:\android\android-ndk-r11b\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin,大家可以看到该目录有很多前缀为arm-linux-androideabi-的exe文件,代表它们使用与arm架构的android程序开发。

现在,就开始动手啦。

首先,新建hello.c程序,代码如下:

#include<stdio.h>
int main(int argc, int** argv[]){
    printf("Hello ARM!\n");
    return 0;
}

1、gcc手动编译

使用这种方式需要首先编写makefile文件。为了之后的方便,我们先将adb 和make工具加入环境变量,环境变量设置方式这里就不细说了。不知道的可以参考这篇文章:

http://jingyan.baidu.com/article/17bd8e52f514d985ab2bb800.html

,make.exe工具路径为F:\android\android-ndk-r11b\prebuilt\windows-x86_64\bin,将该路径添加到环境变量中,注意与adb路径用分号隔开。
现在,我们开始编写我们的makefile文件。新建makefile文件,无后缀名,内容如下:
NDK_ROOT=F:\android\android-ndk-r11b
TOOLCHAINS_ROOT=$(NDK_ROOT)\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64
TOOLCHAINS_PREFIX=$(TOOLCHAINS_ROOT)\bin\arm-linux-androideabi
TOOLCHAINS_INCLUDE=$(TOOLCHAINS_ROOT)\lib\gcc\arm-linux-androideabi\4.9\include-fixed
PLATFORM_ROOT=$(NDK_ROOT)\platforms\android-23\arch-arm
PLATFORM_INCLUDE=$(PLATFORM_ROOT)\usr\include
PLATFORM_LIB=$(PLATFORM_ROOT)\usr\lib

MODULE_NAME=hello
BUILD_TYPE=c
PATH_ANDROID=/data/local/tmp/

RM=del

FLAGS=-I$(TOOLCHAINS_INCLUDE) \
		-I$(PLATFORM_INCLUDE) \
		-L$(PLATFORM_LIB) \
		-nostdlib \
		-lgcc \
		-Bdynamic \
		-lc \
		-pie -fPIE

OBJS=$(MODULE_NAME).o \
		$(PLATFORM_LIB)\crtbegin_dynamic.o \
		$(PLATFORM_LIB)\crtend_android.o

all:
		
	$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c $(MODULE_NAME).$(BUILD_TYPE) -o $(MODULE_NAME).o
	$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -S $(MODULE_NAME).$(BUILD_TYPE) -o $(MODULE_NAME).S
	$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) -o $(MODULE_NAME)		
clean:
		$(RM) *.o
install:
	adb push $(MODULE_NAME) $(PATH_ANDROID)
	adb shell chmod 755 $(PATH_ANDROID)$(MODULE_NAME)
	adb shell $(PATH_ANDROID)$(MODULE_NAME)

makefile文件说明:

1、如果仔细看的话,就会发现这个文件定义了一系列的变量,然后又使用拼接的方式将值赋给另一个变量。
2、前六行定义了路径变量,$(NDK_ROOT)就是取该变量值,在这里可能需要对应版本修改你的对应路径。
3、MODULE_NAME这个变量定义了我们编译完成的名称为hello
4、FLAGS变量增加了一些头文件和库文件的搜索路径和编译选项。在这里我们主要提两个选项,第一个是-nostdlib,android没有采用glibc作为c库,而是采用Google自己开发的Bionic C库,因此需要加入该选项;第二个是-pie -fPIE,在android4.4之后添加了新的保护机制,可执行文件必须是采用PIE编译的,我们加入该行参数,就可以避免报错。
5、all便签指定了编译程序时需要执行的命令。
6、clean标签用于清理生成的目标文件。
7、install标签将我们生成的最终文件安装到手机上,就可以看到c程序输出的结果了。(该标签下的命令就是我们为什么要设置adb环境变量,方便在makefile中编写该命令)
将我们的makefile文件与hello.c放到同一目录下,打开命令行,进入该目录,依次执行以下命令:
make
make install 

此时,可以看到控制台输出”Hello ARM!”。

同时,我们打开刚才的目录,可以看到里面生成了一些.o和.S文件。假设我们想删除其中后缀为.o的文件,执行make clean命令,再打开目录就会看到.o文件已经被删除。。如果想修改删除规则,修改makefile中clean标签内容即可。

2、ndk-build编译

首先,我们先将ndk-build命令加入环境变量,该命令一般位于ndk安装目录下,我安在F:\android下,因此我的ndk-build命令路径为F:\android\android-ndk-r11b,参考gcc编译方式中的adb环境变量设置,将此路径加入到android变量中即可,注意用分号隔开。
使用ndk-build工具,必须先有一个Android工程。我们可以使用sdk开发包中的tools目录下的android脚本来生成。首先我们列出Android SDK中所有已经按照的SDK平台版本。在sdk\toos目录下执行以下命令:
android list

执行这条命令后,会列出很多个SDK版本,在这里选择其中一个版本作为我们建立项目的版本号,记住该版本的id

接下来创建Android工程,执行以下命令:
android create project -n hello2 -p hello2 -t 9 -k com.droider.hello2 -a MyActivity

命令行说明:-n 指定工程名称,-t 指定平台版本号,这里可以输入刚才选择版本号的id,-k 指定工程包名,-a 指定默认Actitivy名称。

执行完以上命令后,会看到tools目录下生成了hello2的工程。
下一步,进入工程根目录,新建jni文件夹,并将我们最开始编写的hello.c文件复制进去。接着编写ndk-build所需要的脚本文件,主要是Android.mk与Application.mk两个脚本,Application.mk可选,我们暂时不使用,这里我们只使用Android.mk文件,该文件是工程的编译脚本,描述了编译原生程序所需的编译选项,头文件,源文件及依赖库等。
新建Android.mk,文件内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_MODULE := hello
LOCAL_SRC_FILES := hello.c
include $(BUILD_EXECUTABLE)

Android.mk文件说明:

1、LOCAL_PATH:定义本地源码路径
2、CLEAR_VARS:让编译系统清除掉一些已经定义过的宏
3、LOCAL_ARM_MODE:指定生成的原生程序使勇的ARM指定模式
4、LOCAL_MODULE:指定模块名称,即原生程序生成后的文件名
5、LOCAL_SRC_FILES:指定C或C++源列表。示例中使用的只有一个hello.c文件。
6、BUILD_EXECUTABLE:指定生成可执行文件。

Android.mk文件编写完后,将它与hello.c一同放在jni目录下,然后进入命令行切换到hello2根目录,执行ndk-build命令,执行效果如下:

此时,我们看执行结果的最后一行即我们生成文件的位置。因此,我们进入hello2目录下的libs/armeabi下,将hello文件复制到模拟器或手机中,执行过程参考gcc手动编译。其实主要也就是以下三条命令即可:
adb push hello /data/local/tmp
adb shell chmod 755 /data/local/tmp/hello
adb shell /data/local/tmp/hello

说明:1、adb需要配置环境变量。



2、传入的hello必须修改权限为可执行文件
此时,可以看到,控制台命令行输出”Hello ARM!”。

3、Eclipse创建工程并自动编译

使用eclipse自动编译原生程序的原理依然是使用ndk-build工具。
1、打开eclilpse,新建Android工程,取名为hello3。
2、新建Build,当代码修改保存后,eclipse会自动编译生成原生程序,新建Build流程如下:


在hello3工程上右键选择Properties,点击Builders选项,再点击右侧New按钮,然后双击Program打开Edit Configuration,在对话框的Name一栏中设置Builder名称,Location输入ndk-build命令路径,Working Drrectory右侧的Browser Workspace选择hello3工程,点击Apply按钮。点击Refresh标签,勾选“Refresh resource upon completion”复选框。点击Build Options标签,勾选“During auto builds”和”Specify working set of relevant resources”复选框。点击“Specify Resources”按钮,勾选hello3工程的jni目录,点击finish按钮,点击Ok关闭Edit Configuration对话框。点击OK关闭Properties对话框。
3、此时hello3自动编译,并在libs/armeabi目录下省hello可执行文件。
结果如下:

以后在Eclipse中对jni目录下任何文件进行修改保存操作,都会触发JNI_Builder执行来重新编译工程。
至此,我们已经将三种编译方式完全讲述完毕。



版权声明:本文为u012195899原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。