SylixOS-工具链使用
三个概念
首先我们区分以下三个概念:
- 代码
- 可执行文件
- 进程
我们在开发环境或编辑器编写的叫代码,如:
#include <stdio.h> |
将其编译后得到可执行文件,如:
我们将可执行文件下载到目标机的文件系统里,然后执行一条shell命令将其执行,那么它将”变为“一个操作系统的进程,如:
[root@sylixos_station:/apps]# ./sample & |
流程如下图:
编译的流程
将C代码编译成可执行文件,其细节流程如下:
预处理器CPP、编译器CC1、汇编器AS、链接器LD等等构成了一条链条(上一条命令的输出作为下一条命令的输入),所以常被称为“工具链toolchain”。
在计算机(如x86+windows)上编译另一个目标系统(如arm+sylixos,处理器和操作系统与主机的不同)程序的方式,被称为“交叉编译cross compile”。
此时“工具链”称为“交叉工具链cross toolchain”或“交叉编译器cross compiler”。
预处理步骤实验命令:
arm-sylixos-eabi-cpp -DSYLIXOS -I"D:\workspace\SylixOS_Base/libsylixos/SylixOS" -I"D:\workspace\SylixOS_Base/libsylixos/SylixOS/include" -I"D:\workspace\SylixOS_Base/libsylixos/SylixOS/include/inet" -mcpu=arm920t -O0 -g3 -gdwarf-2 -Wall -fmessage-length=0 -fsigned-char -fno-short-enums -fPIC -E sample.c -o sample_cpp.c |
纯C编译步骤实验命令:
D:\ACOINFO\arm-sylixos-toolchain\lib\gcc\arm-sylixos-eabi\4.9.3\cc1.exe -mcpu=arm920t -O0 -g3 -gdwarf-2 -Wall -fmessage-length=0 -fsigned-char -fno-short-enums -fPIC sample_cpp.c -o sample.s |
汇编步骤实验命令:
arm-sylixos-eabi-as -mcpu=arm920t -c sample.s -o sample.o |
链接步骤实验命令:
arm-sylixos-eabi-ld -nostdlib -fPIC -shared -o sample sample.o -L"D:\workspace\SylixOS_Base/libsylixos/Debug" -L"D:\ACOINFO\arm-sylixos-toolchain\arm-sylixos-eabi\lib" -L"D:\ACOINFO\arm-sylixos-toolchain\lib\gcc\arm-sylixos-eabi\4.9.3" -lvpmpdm -lm -lgcc |
可执行文件的段
一个可执行文件包含相当多的段,不同的段存放不同的内容。
查看可执行文件的段信息可以使用工具链里的readelf工具。
实验命令:
arm-sylixos-eabi-readelf -S sample |
输出如下:
There are 24 section headers, starting at offset 0x1d63c: |
操作系统动态装载器loader将可执行文件装载运行,上面的段实际上不全出现在对应进程的内存视图上,进程的内存视图往往只有如下几个段:
.text段:
代码段(codesegment/textsegment)通常是指用来存放可执行代码的一块内存区域。通常这部分内存区域的属性是只读。.rodata段:
只读数据段(readonly datasegment),用来存放程序中的字符串和#define定义的常量。.data段:
数据段(datasegment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。.bss段:
bss段(bsssegment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文BlockStarted by Symbol的简称。
bss段并不会记录在可执行文件里,因为其存放未初始化的全局变量,可执行文件只需要记录该段的大小,loader动态装载可执行文件时,根据其大小在内核堆里分配出来并清零就可以了。
可执行文件的符号
一个可执行文件包含相当多的符号。
查看可执行文件的符号信息可以使用工具链里的nm工具。
实验命令:
arm-sylixos-eabi-nm -D sample |
输出如下:
000083a0 D __bss_end__ |
查看可执行文件的未定义符号,使用如下实验命令:
arm-sylixos-eabi-nm -u sample |
输出如下:
U puts |
所谓未定义符号,是指不在该可执行文件里定义的符号,符号可以是函数名、变量名。如可执行文件sample的未定义符号是puts和sleep,这两个都是操作系统的API。
因为GCC的优化,所以可执行文件sample并不调用复杂的printf,而是更快速的puts函数。
可执行文件依赖的动态库
链接可执行文件的流程:
链接步骤实验命令:
arm-sylixos-eabi-ld -nostdlib -fPIC -shared -o sample sample.o -L"D:\workspace\SylixOS_Base/libsylixos/Debug" -L"D:\ACOINFO\arm-sylixos-toolchain\arm-sylixos-eabi\lib" -L"D:\ACOINFO\arm-sylixos-toolchain\lib\gcc\arm-sylixos-eabi\4.9.3" -lvpmpdm -lm -lgcc |
链接可执行文件sample时链接了三个库:libvpmpdm、libm、libgcc。
其中libvpmpdm是一个动态库,文件为libvpmpdm.so;而libm和libgcc是静态库,文件分别是libm.a和libgcc.a。
GCC对库的顺序有要求,比如libvpmpdm.so用到了libgcc和libm的符号,libm也用到了libgcc的符号,链接命令应该使用:
-lvpmpdm -lm -lgcc |
而不能够是:
-lgcc -lm -lvpmpdm |
否则,可执行文件sample装载失败。
查看可执行文件依赖的动态库信息可以使用工具链里的readelf工具和GNU的grep工具。
实验命令:
arm-sylixos-eabi-readelf -a sample | grep NEEDED |
输出如下:
0x00000001 (NEEDED) Shared library: [libvpmpdm.so] |
因为可执行文件sample依赖于动态库libvpmpdm.so,所以libvpmpdm.so应该存放在目标机的文件系统的/lib目录里:
[root@sylixos_station:/lib]# ls |
实际上并非一定要将动态库存放到目标机的文件系统的/lib目录里,loader动态装载可执行文件时,会在目标机的环境变量LD_LIBRARY_PATH指定的路径里查找该可执行文件依赖的动态库。
[root@sylixos_station:/lib]# echo $LD_LIBRARY_PATH |
可执行文件sample运行时,我们也可以输入modules命令查看进程依赖的动态库:
[root@sylixos_station:/apps]# modules |
反汇编可执行文件
反汇编可执行文件可以使用工具链里的objdump工具。
实验命令:
arm-sylixos-eabi-objdump -d sample |
输出如下:
|
查看可执行文件的大小
查看可执行文件的大小可以使用工具链里的size工具。
实验命令:
arm-sylixos-eabi-size sample |
输出如下:
text data bss dec hex filename |
裁剪可执行文件
一个简单的hello world程序,其大小就达到了120KB:
2015/06/26 14:46 122,250 sample |
显然就有点占目标机的磁盘空间了。
裁剪可执行文件可以使用工具链里的strip工具。
实验命令:
arm-sylixos-eabi-strip sample |
裁剪后,可执行文件sample的大小降低到1.7KB左右:
2015/06/26 16:01 1,756 sample |
注意:裁剪后的可执行文件不能同于调试,因为去掉了调试信息。
制作静态库
制作静态库可以使用工具链里的ar工具。
实验命令:
arm-sylixos-eabi-ar -r libsample.a sample.o |
注意:GCC对库的名字有要求,库的名字前缀必须是lib,扩展名为.a代表这是个静态库(.o的集合),扩展名为.so代表这是个动态库。
这个静态库并没有什么实际意义,这里只是为了演示。
调试程序
当我们觉得程序有问题时,我们可以使用工具链里的gdb工具进行调试。
注意:strip过的可执行文件不能调试,因为不带调试信息,此外,编译时用参数 -O0 -g3 -gdwarf-2,即不优化代码并带调试信息。
先在主机启动gdb
实验命令:arm-sylixos-eabi-gdb sample
然后在目标机启动debug server:
[root@sylixos_station:/apps]# debug :1234 /apps/sample &
[root@sylixos_station:/apps]# [GDB]Waiting for connect...主机的gdb连接目标机的debug server
(gdb) target remote 192.168.7.30:1234
Remote debugging using 192.168.7.30:1234
如果能连接上,目标机应该会输出:
[root@sylixos_station:/apps]# [GDB]Connected. host : 192.168.7.20 |
列出代码
(gdb) l
1 /*
2 * sample.c
3 *
4 * Created on: 2015-06-26
5 * Author: Administrator
6 */
7
8 #include <stdio.h>
9
10 int main (int argc, char *argv[])在main函数入口处下个断点
(gdb) b main
Breakpoint 1 at 0xc00202bc: file sample.c, line 12.恢复运行
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0x30c78d14) at sample.c:12
12 printf("Hello World!\n");
程序遇到第一个断点,将停在main函数的入口处。