文章目录
🌹1.GUN工具的使用
GUN是一个开源的组织,给我们带来了很多开源软件和编译工具,如我们常用的
- C编译器 :gcc
- 预处理器 :cpp
- C++编译器 g++
- 汇编器 :as
- 链接器 :ld
- 二进制工具集 :objcopy
二进制工具集的含义:他们专门用于控制和将二进制目标代码从一种格式转换为另一种格式,这些工具被称作为objcopy和objdump,许多linux机器使用的底层软件在编译阶段都非常依赖这些工具。调试的时候也需要他们。objcopy用于从一个文件拷贝目标代码到另一个文件,并在这个过程中进行转换。这样外面可以在不同的目标代码格式之间进行自动的转换并操纵这个过程中的内容。他们可以任何方式来操作二进制文件。
🚀1.1符号显示器nm
可通过arm-linux-gcc -v 查看是否有arm-linux-gcc这个编译器
显示符号 $nm -n main_elf
显示内容:
Ø第一列为符号地址
Ø第二列为符号所在段
Ø第三列为符号名称
比如我执行arm-linux -nm -n demo,demo是我的一个可执行程序,会出现如下信息
这些符号的含义如下图所示:
这个符号显示器很有用,可以看到绝大部分的信息,但是局部变量是看不到的,因为他分配在栈里面
💒1.2objdump:信息查看器
objdump 有点像那个快速查看之类的工具,就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。对于一般只想让自己程序跑起来的程序员,这个命令没有更多意义,对于想进一步了解系统的程序员,应该掌握这种工具,至少你可以自己写写shellcode了,或者看看人家给的 exploit 中的 shellcode 是什么东西。
Ø 查看所有段信息 $objdump -h main_elf
Ø 查看文件头信息 $objdump -f main_elf
Ø 查看反汇编 $objdump -d main_elf
Ø 查看内嵌反汇编 $objdump -S -d main_elf
我一一演示一遍这些命令的使用
🎪1.3objcopy:段剪辑器
objcopy被用来复制一个目标文件的内容到另一个文件中,可以使用不同于源文件的格式来输出目的文件,即可以进行格式转换
去除elf格式信息
$objcopy -O binary -S main_elf main.bin
⚡️2.程序编译过程
程序编译4步骤:
(1)预处理
C/C++源文件中,以“#”开头的命令被称为预处理命令,如包含命令“#include”、宏定义命令“#define”、条件编译命令“#if”、“#ifdef”等。预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些东西输出到一个“.i”文件中等待进一步处理。
(2)编译
编译就是把C/C++代码(比如上述的“.i”文件)“翻译”成汇编代码。
(3)汇编
汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现为ELF目标文件(OBJ文件)。“反汇编”是指将机器代码转换为汇编代码,这在调试程序时常常用到。
(4)链接
链接就是将上步生成的OBJ文件和系统库的OBJ文件、库文件链接起来,最终生成了可以在特定平台运行的可执行文件。
- 一个标准应用程序通常都由链接器采用默认链接脚本(arm-linux-ld --verbose)将“用户程序” 和“库” 共同链接生成可执行程序
♐2.1程序链接过程
链接过程中需要一个标号(_start)作为程序入口
标号(_start)的作用是: 将用户程序从汇编带到了C语言程序入口, 即main()函数, 从此开始我们的应用程序之旅
2.2指定头文件
头文件在哪里?
-
系统目录
-
系统目录在哪?工具链里的某个include目录
-
怎么确定?
echo 'main(){}'| gcc -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)
-
可以不使用系统include目录吗?可以,编译时指定参数
-nostdinc
-
-
可以自己指定头文件目录
-I <头文件目录>
🎪2.3 指定库文件
库文件在哪里?
-
系统目录
-
系统目录在哪?工具链里的某个lib目录
-
怎么确定?
echo 'main(){}'| gcc -E -v - // 它会列出头文件目录、库目录(LIBRARY_PATH)
-
可以不使用系统lib目录吗?可以,编译时指定参数
-nostdlib
-
-
可以自己指定库文件目录
-L <库文件目录>
- 指定库文件
-l <abc> // 链接 libabc.so 或 lib.a
⌛3.裸机开发流程
*假设你写了一个程序名字为uart_test.c
-
使用命令编译:arm-linux-gcc uart_test.c -c -o uart.o
-
链接并强行指定main入口为0x48000000:arm-none-linux-gnueabi-ld uart.o -o uart-test -e main -Ttext=0x48000000
-
把elf文件变为bin结尾的二进制文件arm-none-linux-gnueabi-objcopy -O binary -S uart
-
把生成的.bin结尾的文件放到windows
-
linux板子终端进入uboot修改环境变量执行setenv loadaddr 0x48000000修改并通过saveenv进行保存,注意环境变量的地址为你强行指定main函数的地址
-
在uboot敲loadb命令等待文件传输
-
打开超级终端serucecrt把.bin文件传输到板子里
-
通过go 0x48000000 执行main函数,这时一个完整的程序就可以执行了,可以在里面执行最简单的点灯操作,如下图所示