为了学习arm指令集,当然要先写段 hello arm 试验咯。

写段 hello arm

1
2
3
4
5
6
#include<stdio.h>
int main(int argc, char* argv[])
{
printf("hello arm!\n");
return 0;
}

编译方式

这里有两种手动编译方式:gcc和ndk-build.

使用gcc

配置交叉编译环境

因为我们要编译出在android手机上运行的程序,必须要为gcc提供android的平台文件和指令集。
而ndk中自带了gcc等一系列工具,所以现在直接调用即可。没有ndk的话需要先行下载。
~/.bashrc配置如下:

1
2
3
4
5
6
#android-ndk-gcc
export NDKROOT=/media/d/android-sdk-linux/android-sdk-linux/ndk-bundle
export SYSROOT=$NDKROOT/platforms/android-21/arch-arm
export NDKTOOL=$NDKROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin
alias ndk-gcc='$NDKTOOL/arm-linux-androideabi-gcc-4.9 --sysroot=$SYSROOT'
alias ndk-objdump='$NDKTOOL/arm-linux-androideabi-objdump'

其中
NDKROOT是ndk的主目录;
SYSROOT是所选的指令集,这里是arm;
NDKTOOL是ndk的工具链目录;
ndk-gcc是为后面一串命令起的别名,这样使用时就不用一遍一遍的输参数了;
ndk-objdump是可以反汇编arm程序的工具,与ndk-gcc在ndk的同一目录下.

配置完成后可执行source ~./bashrc使立即生效,echo $ndk-gcc查看是否设置成功。

编译

执行ndk-gcc -o hello-arm hello-arm.c直接生成可执行文件。
执行ndk-gcc -S hello-arm.s hello-arm.c生成arm汇编文件。
其他选项执行ndk-gcc --help查看帮助。

使用ndk-build

配置ndk-build环境

如果是单独下的ndk,这时候系统找不到ndk-build命令,需要将其路径加入path中.
ndk-build在ndk根目录下。

新建Android工程

执行android list查看已安装的sdk版本;
执行android create project -t 1 -p ~/Desktop/helloarm -a MainActivity在桌面创建一个新工程.
其中:
-t表示新工程目标sdk;
-p表示工程的路径;
-a表示默认activity的名称.
其他选项可执行android create project -h查看.

配置jni目录

在工程根目录下新建一个jni目录,将hello-arm.c拷贝至此目录.
新建脚本文件名为Android.mk,这是ndk-build需要的工程编译脚本,描述了编译程序的各种选项和依赖.
内容如下:

1
2
3
4
5
6
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ARM_MODE := arm
LOCAL_MODULE := hello-arm
LOCAL_SRC_FILES := hello-arm.c
include $(BUILD_EXECUTABLE)

其中:
LOCAL_PATH表示本工程源码的路径, my-dir表示Android.mk的路径;
CLEAR_VARS让编译器清除已经定义过的宏,避免在编译多个模块时发生错误,因为这些宏是全局的,必须重新设置;
LOCAL_ARM_MODE指定程序使用的ARM指令模式;
LOCAL_MODULE指定生成的模块名,如果生成的是共享库,模块名会变为libhello-arm.so;
LOCAL_SRC_FILES指定源文件列表;
BUILD_EXECUTABLE表示生成的文件是可执行的,其他选项有BUILD_SHARED_LIBRARY(生成动态库),BUILD_STATIC_LIBRARY(生成静态库).

编译

在工程根目录下执行ndk-build,编译完成后的文件在libs/armeabi目录下.

执行

执行adb push hello-arm /data/localpush到手机上(我用的是genymotion虚拟机),

执行/data/local/hello-arm

出现第一个错误

1
sh: ./hello arm: not executable: magic 7F45

这是因为程序指令集和手机指令集不一致造成的。

写一段java代码编译成dex,看看手机是什么架构:

1
2
3
4
5
6
7
8
class GetCPU
{
public static void main(String[] args)
{
String arch = System.getProperty("os.arch");
System.out.println(arch);
}
}

执行javac GetCPU.javadx --dex --output=GetCPU.dex GetCPU.class 编译成dex文件,
push到手机上的 /data/local/目录, 执行dalvikvm -cp /data/local/GetCPU.dex GetCPU:
果然是intel的..

1
2
root@vbox86p:/data/local #dalvikvm -cp /data/local/GetCPU.dex GetCPU
i686

换了一个arm架构的手机(真机):
先执行了GetCPU.dex确认指令集:

1
2
root@aio_otfp:/data/local # dalvikvm -cp /data/local/GetCPU.dex GetCPU
aarch64

现在可以执行 /data/local/hello-arm了.

出现第二个错误!

1
sh: ./hello-arm: can't execute: Permission denied

这是因为没有执行权限,加上执行权限!777!再次执行!

出现第三个错误!

1
error: only position independent executables (PIE) are supported.

这次是因为没有静态编译,重新编译,加上静态选项:

ndk-gcc --static hello-world.c -o hello-arm

颤颤巍巍的再次执行…
终于出现了 ——————— hello arm!

Refernece

如何在命令行下使用Android NDK交叉编译工具

Android NDK 工具链的使用方法(Standalone Toolchain)