Android交叉编译入门

通过这篇文章了解c/c++编译器的基本使用,能够在后续移植第三方框架进行交叉编译时(编译android可用的库),清楚的了解应该传递什么参数,怎么传递参数给编译器,各个参数的意义是什么,从而为后面音视频的深入学习编译ffmpeg做好准备工作。

交叉编译

交叉编译就是程序的编译环境和实际运行环境不一致,即在一个平台上生成另一个平台上的可执行代码。
比如NDK,你在Mac、Win或者Linux上生成的C/C++的代码要在Android平台上运行,就需要使用到交叉编译了。
通俗点说就是你的电脑和手机使用的CPU不同,所以CPU的指令集就不同,比如arm的指令集在X86上就不能运行。

常用的编译工具链

gcc
GNU C编译器。原本只能处理C语言,很快扩展,变得可处理C++。(GNU计划,又称革奴计划。目标是创建一套完全自由的操作系统)

Android在NDK r18之后彻底移除了gcc,默认使用clang编译,所以使用不同版本的ndk对ffmpeg进行交叉编译时会出现同样的脚本在旧版的ndk能编译通过,但是旧版的就不编译不通过的问题。

笔者会在后面的学习过程中使用最新的ndk对最新版的ffmpeg进行交叉编译,并且会通过文章记录学习过程,感兴趣的同学可以持续关注。

g++
GNU c++编译器

gcc和g++都能够编译c/c++,但是编译时候行为不同。

对于gcc与g++会有以下区别:

  • 后缀为.c的源文件,gcc把它当作是C程序,而g++当作是C++程序;后缀为.cpp的,两者都会认为是c++程序

  • g++会自动链接c++标准库stl,gcc不会

  • gcc不会定义__cplusplus宏,而g++会

clang
clang 是一个C、C++、Object-C的轻量级编译器。基于LLVM (LLVM是以C++编写而成的构架编译器的框架系统,可以说是一个用于开发编译器相关的库)

对比gcc,clang具有编译速度更快、编译产出更小等优点,但是某些软件在使用clang编译时候因为源码中内容的问题会出现错误。

另外clang++也是一个编译器,clang++与clang就相当于gcc与g++的区别。

静态库和动态库

  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。Linux中后缀名为”.a”。

  • 动态库则与静态库相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库。Linux中后缀名为”.so”。gcc在编译时默认使用动态库。

总结起来就是静态库节省运行时间,动态库节省运行空间,典型的时间换空间,在开发过程中可根据情况自行选择。

Java中在不经过封装的情况下只能直接使用动态库。

编译器过程

一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)、和连接(linking)才能变成可执行文件。

我们以最简单的一个c语言程序来做一个例子:

#include <stdio.h>

int main(){
    printf("hello c world\r\n");
    return 0;
}
  1. 预处理

gcc -E main.c -o main.i

-E的作用是让gcc在预处理结束后停止编译。

预处理阶段主要处理include和define等。它把#include包含进来的.h 文件插入到#include所在的位置,把源程序中使用到的用#define定义的宏用实际的字符串代替。

  1. 编译阶段

gcc -S main.i -o main.s

-S的作用是编译后结束,编译生成了汇编文件。

在这个阶段中,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。

  1. 汇编阶段

gcc -c main.s -o main.o

汇编阶段把 .s文件翻译成二进制机器指令文件.o,这个阶段接收.c, .i, .s的文件都没有问题。

  1. 链接阶段

gcc -o main main.s

链接阶段,链接的是函数库。在main.c中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明。系统把这些函数实现都被做到名为libc.so的动态库。

一步到位:

gcc main.c -o main

到这里我们成功的在 mac 平台生成了可执行文件,运行./main即可看到输出。试想一下我们可以将这个可执行文件拷贝到安卓手机上执行吗?

肯定是不行的,主要原因是两个平台的 CPU 指令集不一样,根本就无法识别指令,这时候交叉编译就排上用场了。

如果你不信可以把 main 可执行文件 push 到手机 /data/local/tmp 里面执行验证一下能否正确输出。

也不一定必须要是/data/local/tmp这个路径,push到任意一个有可读可写可执行的权限的目录下测试均可。

交叉编译实验

下面我们使用ndk来对main.c进行交叉编译,看看编译后的可执行文件是不是真的能在Android上运行。

笔者这里以armeabi为例,在mac平台上进行交叉编译。

既然是gcc被移除了,那我们使用clang来进行交叉编译。

  1. 首先找到clang工具链
NDK路径/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi19-clang
  1. 执行命令
NDK路径/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi19-clang -o main main.c

在mac平台能能正常生成可执行文件main,我们将可执行文件用push到/data/local/tmp这个目录下,然后使用adb执行./main即可看到输出hello c world。说明我们的交叉编译成功了。

如果不使用clang,如何gcc进行交叉编译呢?

首先也是先找到gcc的工具链

NDK路径/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc

然后执行gcc编译命令

NDK路径/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc -o main main.c

我们发现报错了

找不到stdio.h头文件

这种错误是说在我们编的时候编译器找不到我们引入的 stdio.h 头文件,那怎么告诉编译器 stdio.h 头文件在哪里呢? 下面知识点说明怎么指定这些报错的头文件

我们通过参数告诉gcc工具链到那个目录下去寻找头文件,传递参数进去再次试一下

NDK路径/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc --sysroot=NDK路径/platforms/android-21/arch-arm -isystem NDK路径/sysroot/usr/include -pie -o main main.c

还是报错


找不到types.h头文件

因为找不到<asm/types.h>头文件,我们进去-isystem配置的头文件查找目录中发现aarch64-linux-android和arm-linux-androideabi都存在asm的子目录,所以编译器就不知道用那个了,我们再指定一下即可。

NDK路径/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc --sysroot=NDK路径/platforms/android-21/arch-arm -isystem NDK路径/sysroot/usr/include -isystem NDK路径/sysroot/usr/include/arm-linux-androideabi -pie -o main main.c

终于成功了,我们将可执行文件push到手机的/data/local/tmp这个目录下,然后使用adb执行./main即可看到输出hello c world

在这里我们使用了clang和gcc进行了交叉编译发现clang更加的简单,直接找到工具链的路径即可进行编译了,但是gcc就比较复杂了,需要指定多个参数。

这里需要需要我们明白每个参数的意思是什么:

--sysroot=XX  
    使用xx作为这一次编译的头文件与库文件的查找目录,查找下面的 usr/include usr/lib目录  
-isysroot XX  
    头文件查找目录,覆盖--sysroot ,查找 XX/usr/include  
-isystem XX  
    指定头文件查找路径(直接查找根目录)  
-IXX  
    头文件查找目录  
优先级:  
    -I -> -isystem -> sysroot  (前面的优先级更高)    

例如 gcc --sysroot=目录1 -isysroot 目录2 -isystem 目录3 -I目录4 main.c
的意思就是 查找 目录1/usr/lib 的库文件、
查找目录2 /usr/include 的头文件、
查找 目录3 下的头文件、
查找 目录4 下的头文件。

-L:XX    
    指定库文件查找目录  
-lxx.so  
    指定需要链接的库名   

例如:
gcc -L目录1 -l库名
链接ndk的日志库:
gcc -LC:NDK路径\platforms\android-21\arch-arm\usr\lib
-llog -lGLESv2
或者是
gcc --sysroot=NDK路径\platforms\android-21\arch-arm
-llog -lGLESv2

生成动态库

gcc -fPIC -shared main.c -o libTest.so
或者
clang -fPIC -shared main.c -o libTest.so

即使不加-fPIC也可以生成.so文件,但是对于源文件有要求,
因为不加fPIC编译的so必须要在加载到用户程序的地址空间时重定向所有目标地址,所以在它里面不能引用其它地方的代码。
要想验证编译出来的so动态库能不能正常使用,通过JNI调用测试即可。

最后如果你对音视频开发感兴趣可关注我,后续我们共同探讨,共同进步。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容