GCC编译工具集和nasm编译器的简要介绍

本文主要是简单的介绍一下GCC编译工具集中各软件的作用,同时简要介绍一下ELF文件和汇编语言的格式以及如何使用nasm编译器编译.asm文件生成可执行文件。

(一)GCC编译工具集

1.GCC的组成

  • GCC(GNU C Compiler)
    gcc是编译工具。它可以将 C/C++语言编写的程序转换成为处理器能够执行的二进制代码。GCC 现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL 语言,以及支持函数式编程和逻辑编程的 Mercury 语言等。而 GCC 也变成了 GNU Compiler Collection 即GNU 编译器家族的意思。
  • Binutils: 一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、 ldd、readelf、 size 等。这 一组工具是开发和调试不可缺少的工具 。
工具作用
addr2line用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置
as主要用于汇编
ld主要用于链接
ar主要用于创建静态库
ldd可以用于查看一个可执行程序依赖的共享库
objcopy将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或者将.elf 转换成.bin 等。
objdump主要的作用是反汇编
readelf显示有关ELF文件的信息
size列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等
  • C运行库
    C 语言标准主要由两部分组成:一部分描述C的语法,另一部分描述C标准库。 C标准库定义了一组标准头文件,每个头文件中包含一些相关的函数、变量、类型声明和宏定义,譬如常见的printf函数便是一个 C标准库函数,其原型定义在stdio头文件中。 C语言标准仅仅定义了C标准库函数原型,并没有提供实现。因此,C语言编译器通常需要一个C运行时库(C Run Time Libray,CRT)的支持。C 运行时库又常简称为 C运行库。与 C语言类似,C++也定义了自己的标准,同时提供相关支持库,称为C++运行时库。

2.GCC编译过程

编译阶段编译命令作用
预处理gcc -E test.c -o test.i编译器将源代码中包含头文件编译进来
编译gcc -S test.i -o test.s检查代码规范性并翻译成汇编语言
汇编gcc -c test.s -o test.o将.s文件转换为目标文件
链接gcc test.o -o test将目标文件转换为可执行文件
  • 预处理
    执行命令gcc -E test.c -o test.i,生成test.i文件
    gcc-E
    test.i部分代码:
    test.i

  • 编译
    执行命令gcc -S test.i -o test.s,生成test.s文件
    gcc-S
    test.s部分代码:
    test.s

  • 汇编
    执行命令gcc -c test.s -o test.o,生成test.o文件
    gcc-o

  • 链接
    执行命令gcc test.o -o test,生成test可执行文件
    gcc-main

(二)EFF文件格式

1.ELF文件

  • ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。实际上,一个文件中不一定包含全部内容,而且它们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。1
  • ELF文件格式如下图,位于ELF Header和Section Header Table 之间的都是段(Section)。一个典型的ELF文件包含下面几个段:
    • .text:已编译程序的指令代码段。
    • .rodata:ro 代表 read only,即只读数据(譬如常数 const)。
    • .data:已初始化的 C 程序全局变量和静态局部变量。
    • .bss:未初始化的 C 程序全局变量和静态局部变量。
    • .debug:调试符号表,调试器用此段的信息帮助调试。
      ELF
  • 使用readelf -S test查看test可执行文件各个section的信息
There are 29 section headers, starting at offset 0x1928:

节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  ··································································
  [26] .symtab           SYMTAB           0000000000000000  00001040
       00000000000005e8  0000000000000018          27    43     8
  [27] .strtab           STRTAB           0000000000000000  00001628
       0000000000000202  0000000000000000           0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  0000182a
       00000000000000fe  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

2.反汇编ELF

  • ELF文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF文件包含的指令和数据,需要使用反汇编的方法,命令为objdump -D test
test:     文件格式 elf64-x86-64


Disassembly of section .interp:

0000000000000238 <.interp>:
 238:	2f                   	(bad)  
 239:	6c                   	insb   (%dx),%es:(%rdi)
 23a:	69 62 36 34 2f 6c 64 	imul   $0x646c2f34,0x36(%rdx),%esp
 241:	2d 6c 69 6e 75       	sub    $0x756e696c,%eax
 246:	78 2d                	js     275 <_init-0x273>
 248:	78 38                	js     282 <_init-0x266>
 24a:	36 2d 36 34 2e 73    	ss sub $0x732e3436,%eax
 250:	6f                   	outsl  %ds:(%rsi),(%dx)
 251:	2e 32 00             	xor    %cs:(%rax),%al
··································································
000000000000063a <main>:
 63a:	55                   	push   %rbp
 63b:	48 89 e5             	mov    %rsp,%rbp
 63e:	48 8d 3d 9f 00 00 00 	lea    0x9f(%rip),%rdi        # 6e4 <_IO_stdin_used+0x4>
 645:	e8 c6 fe ff ff       	callq  510 <puts@plt>
 64a:	b8 00 00 00 00       	mov    $0x0,%eax
 64f:	5d                   	pop    %rbp
 650:	c3                   	retq   
 651:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
 658:	00 00 00 
 65b:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
·····································································
  • 使用objdump -S将其反汇编并且将其C语言源代码混合显示出来:
    执行命令gcc -o hello -g hello.c再执行objdump -S test
    (注意:需要加上参数-g,否则不能显示出c语言的源代码)
······························································
000000000000063a <main>:
#include<stdio.h>

int main(void)
{
 63a:	55                   	push   %rbp
 63b:	48 89 e5             	mov    %rsp,%rbp
	printf("Hello World!\n");
 63e:	48 8d 3d 9f 00 00 00 	lea    0x9f(%rip),%rdi        # 6e4 <_IO_stdin_used+0x4>
 645:	e8 c6 fe ff ff       	callq  510 <puts@plt>
	return 0;
 64a:	b8 00 00 00 00       	mov    $0x0,%eax
}
 64f:	5d                   	pop    %rbp
 650:	c3                   	retq   
 651:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
 658:	00 00 00 
 65b:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
·······························································

(三)汇编语言格式

1.汇编语言定义

汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。2

2.汇编语言组成

  • 传送指令
    包括通用数据传送指令MOV、条件传送指令CMOVcc、堆栈操作指令PUSH/PUSHA/PUSHAD/POP/POPA/POPAD、交换指令XCHG/XLAT/BSWAP、地址或段描述符选择子传送指令LEA/LDS/LES/LFS/LGS/LSS等。
  • 逻辑运算
    这部分指令用于执行算术和逻辑运算,包括加法指令ADD/ADC、减法指令SUB/SBB、加一指令INC、减一指令DEC、比较操作指令CMP、乘法指令MUL/IMUL、除法指令DIV/IDIV、符号扩展指令CBW/CWDE/CDQE、十进制调整指令DAA/DAS/AAA/AAS、逻辑运算指令NOT/AND/OR/XOR/TEST等。
  • 移位指令
    这部分指令用于将寄存器或内存操作数移动指定的次数。包括逻辑左移指令SHL、逻辑右移指令SHR、算术左移指令SAL、算术右移指令SAR、循环左移指令ROL、循环右移指令ROR等。
  • 位操作
    这部分指令包括位测试指令BT、位测试并置位指令BTS、位测试并复位指令BTR、位测试并取反指令BTC、位向前扫描指令BSF、位向后扫描指令BSR等。
  • 控制转移
    这部分包括无条件转移指令JMP、条件转移指令JCC/JCXZ、循环指令LOOP/LOOPE/LOOPNE、过程调用指令CALL、子过程返回指令RET、中断指令INTn、INT3、INTO、IRET等。
  • 串操作
    这部分指令用于对数据串进行操作,包括串传送指令MOVS、串比较指令CMPS、串扫描指令SCANS、串加载指令LODS、串保存指令STOS,这些指令可以有选择地使用REP/REPE/REPZ/REPNE和REPNZ的前缀以连续操作。
  • 输入输出
    这部分指令用于同外围设备交换数据,包括端口输入指令IN/INS、端口输出指令OUT/OUTS。

(四)nasm汇编编译器编译生成可执行程序

1.Ubuntu安装nasm汇编编译器

  • 先判断是否安装nams
    使用命令whereis nasm,如果显示nasm: /usr/bin/nasm ,则已经安装;如果只显示nasm: ,则未安装。
  • 安装nasm
    使用命令sudo apt install nasm安装nasm编译器
    如无法安装可以参考linux下的汇编环境搭建(nasm)

2.编译代码hello.asm

  • hello.asm源代码
; hello.asm 
section .data            ; 数据段声明
        msg db "Hello, world!", 0xA     ; 要输出的字符串
        len equ $ - msg                 ; 字串长度
section .text            ; 代码段声明
global _start            ; 指定入口函数
_start:                  ; 在屏幕上显示一个字符串
        mov edx, len     ; 参数三:字符串长度
        mov ecx, msg     ; 参数二:要显示的字符串
        mov ebx, 1       ; 参数一:文件描述符(stdout) 
        mov eax, 4       ; 系统调用号(sys_write) 
        int 0x80         ; 调用内核功能
                         ; 退出程序
        mov ebx, 0       ; 参数一:退出代码
        mov eax, 1       ; 系统调用号(sys_exit) 
        int 0x80         ; 调用内核功能

  • 编译生成hello可执行文件
    使用命令nasm -f elf64 hello.asm将hello.asm文件生成hello.o文件
    使用命令ld -s -o hello hello.o将hello.o文件生成hello 可执行文件
    执行结果如下:
    nasm
    • 遇到的问题
      使用命令nasm -f elf64 hello.asm生成hello.o文件后再使用命令gcc hello.o -o hello报错如下:
      hello.o:在函数‘_start’中:
      hello.asm:(.text+0x0): `_start'被多次定义
      /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o:(.text+0x0):第一次在此定义
      /usr/bin/ld: hello.o: relocation R_X86_64_32 against `.data' can not be used when making a PIE object; recompile with -fPIC
      /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o:在函数‘_start’中:(.text+0x20):对‘main’未定义的引用
      /usr/bin/ld: 最后的链结失败: 无效的操作
      collect2: error: ld returned 1 exit status
      
      • 原因:
        gcc编译需要链接c语言标准库,而用nasm生成的.o文件没有用到c语言的标准库,因而使用gcc编译时会报错
      • 解决方法
        将_start改为main即可
  • 与gcc编译生成的可执行文件对比(功能都是输出Hello,world!
    • nasm汇编语言生成的可执行代码大小为512字节
      nasm
    • gcc编译生成的可执行代码大小为8296字节
      gcc
      可见使用nasm汇编生成的main可执行文件要比gcc编译生成的可执行文件要更小。

(五)总结

通过对gcc工具集的使用,我了解了在一个程序编译过程中是如何一步一步的编译成可执行文件的。除此之外,还了解了ELF文件和汇编文件的格式以及如何用nasm汇编编辑器编译hello.asm文件生成可执行文件hello。以上这些实验,使我对于Linux操作系统上的程序编译的理解更加深入。


  1. ELF文件格式 ↩︎

  2. 汇编语言 ↩︎

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值