栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

GCC编译工具集和nasm编译器

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

GCC编译工具集和nasm编译器

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

一、gcc编译工具集

1.1gcc工具有哪些

1.2gcc编译过程

1.2.1.这里我们是使用一个hello.c文件进行演示:

 1.2.2检错

1.2.3库文件连接

二、ELF文件格式

2.1ELF文件

 2.2反汇编ELF

 三、nasm汇编编译器编译生成可执行程序

 3.1Ubuntu安装nasm汇编编译器

3.2编译代码hello.asm

总结

参考文献:






前言

GCC是Linux下的编译工具集,是GNU Compiler oleton的缩写,包含g. g++等编译器。这个工具集不仅包含编译器,还包含其他工具集,例如ar、 nm等。
GCC工具集不仅能编译C++语言,其他例如Objective-C. Paseal. Fortan、Java、Ada等语言均能进行编译SCC在可以根据不同的硬件平台进行编译,即能进行交叉编译,在A平台上编译B平台的程序,支持常见的X86、ARM、PowerPC、mips等,以及Linux、Windows等软件平台。在本书中仅介绍对C语言进行编译,其他语言的编译请读者查阅相关资料。
GCC在各种平台下都被广泛地采用,特别是嵌入式平台下,这得益于它的目标机定义规则。当对目标机的硬件进行了合适的定义后,可以生成目标机能够正确解析的文件格式。
 


提示:以下是本篇文章正文内容,下面案例可供参考




一、gcc编译工具集

1.1gcc工具有哪些
工具
addr2line用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置
as主要用于汇编
ld主要用于链接
ar主要用于创建静态库
ldd可以用于查看一个可执行程序依赖的共享库
objcopy将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或者将.elf 转换成.bin 等
objdump主要的作用是反汇编
readelf显示有关ELF文件的信息
size列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等

1.2gcc编译过程

源文件、目标文件和可执行文件是编译过程中经常用到的名词。源文件通常指存放可编辑代码的文件,如存放C、C++和汇编语言的文件。目标文件是指经过编译器的编译生成的CPU可识别的二进制代码,但是目标文件- -般不能执行,因为其中的一-些函数过程没有相关的指示和说明。可执行文件就是目标文件与相关的库链接后的文件,它是可以执行的。
预编译过程将程序中引用的头文件包含进源代码中,并对- - 些宏进行替换。
编译过程将用户可识别的语言翻译成- -组处理器可识别的操作码,生成目标文件,通常翻译成汇编语言,而汇编语言通常和机器操作码之间是一-种一对一-的关系。GNU中有C/C++编译器GCC和汇编器as。
所有的目标文件必须用某种方式组合起来才能运行,这就是链接的作用。目标文件中.通常仅解析了文件内部的变量和函数,对于引用的函数和变量还没有解析,这需要将其他已经编写好的目标文件引用进来,将没有解析的变量和函数进行解析,通常引用的目标是库。链接完成后会生成可执行文件。

编译阶段编译命令作用
预处理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将目标文件转换为可执行文件

1.2.1.这里我们是使用一个hello.c文件进行演示:

(1)先创建一个文件目录,再使用vim文本编辑器编写一个hello world程序,代码如下:

#include
int main(void)
{
	printf("Hello World!n");
	return 0;
}

简单编译:

gcc test.c -o test

实际编译:

预处理:gcc -E test.c -o test.i,生成test.i文件

在文件中打开test.i文件,部分内容如下:

编译为汇编代码:gcc -S test.i -o test.s

test部分内容如下: 

 

汇编:gcc -c test.s -o test.o

连接:gcc test.o -o test

 图为连接之后生成的文件,接着运行,观察运行结果;

 1.2.2检错

gcc -pedantic test.c -o test1

-pedantic:帮助发现一些不符合ANSI/ISO C标准的代码,当出现不符合的代码,会发出警告信息

gcc -Wall test.c -o test2

 -Wall:帮助发现一些不符合ANSI/ISO C标准的代码,当出现不符合的代码,gcc会发出尽可能多的警告信息

 gcc -Werror test.c -o test3

 -Werror:gcc会在所有产生警告的地方停止编译,迫使进行代码的修改

import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarnings('ignore') import ssl ssl._create_default_https_context = ssl._create_unverified_context 



1.2.3库文件连接

库文件:动态链接库(.so),静态链接库(.a)
函数库:头文件(.h),库文件(.so)

①编译
gcc -c -I /usr/include 源文件 -o 生成.o文件
②链接
gcc -L /usr/lib 动态链接库文件名 生成.o文件 -o 生成可执行文件
③强制性使用静态链接库
gcc链接时会优先使用动态链接库,想强制使用静态链接库执行在命令中加-static
gcc -L /usr/lib -static 静态链接库文件名 生成.o文件 -o 生成可执行文件
 

 静态链接时搜索路径顺序:
a、ld会去找gcc命令中的参数-L
b、gcc的环境变量LBRARY_PATH(指定程序静态链接库文件的搜索路径)
c、内定目录/lib /usr/lib /usr/local/lib
动态链接时搜索路径顺序:
a、编译目标代码时指定的动态库搜索路径
b、环境变量LD_LIBRARY_PATH(指定程序动态链接库文件的搜索路径)
c、配置文件/etc/ld.so.conf中指定的动态库搜索路径
d、默认的动态库搜索路径/lib
e、默认的动态库搜索路径/usr/li

二、ELF文件格式

2.1ELF文件

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

使用readelf -S test查看test可执行文件各个section的信息

zj123@zj123-virtual-machine:~/test2$ readelf -S hello
There are 29 section headers, starting at offset 0x1930:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002b8  000002b8
       00000000000000a8  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000000360  00000360
       0000000000000082  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           00000000000003e2  000003e2
       000000000000000e  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          00000000000003f0  000003f0
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000000410  00000410
       00000000000000c0  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000000004d0  000004d0
       0000000000000018  0000000000000018  AI       5    22     8
  [11] .init             PROGBITS         00000000000004e8  000004e8
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000000500  00000500
       0000000000000020  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000000520  00000520
       0000000000000008  0000000000000008  AX       0     0     8
  [14] .text             PROGBITS         0000000000000530  00000530
       00000000000001a2  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         00000000000006d4  000006d4
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         00000000000006e0  000006e0
       0000000000000011  0000000000000000   A       0     0     4
  [17] .eh_frame_hdr     PROGBITS         00000000000006f4  000006f4
       000000000000003c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000000730  00000730
       0000000000000108  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000200db8  00000db8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000200dc0  00000dc0
       0000000000000008  0000000000000008  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000200dc8  00000dc8
       00000000000001f0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000200fb8  00000fb8
       0000000000000048  0000000000000008  WA       0     0     8
  [23] .data             PROGBITS         0000000000201000  00001000
       0000000000000010  0000000000000000  WA       0     0     8
  [24] .bss              NOBITS           0000000000201010  00001010
       0000000000000008  0000000000000000  WA       0     0     1
  [25] .comment          PROGBITS         0000000000000000  00001010
       0000000000000029  0000000000000001  MS       0     0     1
  [26] .symtab           SYMTAB           0000000000000000  00001040
       00000000000005e8  0000000000000018          27    43     8
  [27] .strtab           STRTAB           0000000000000000  00001628
       0000000000000203  0000000000000000           0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  0000182b
       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),

 2.2反汇编ELF

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

zj123@zj123-virtual-machine:~/test2$ objdump -D hello

hello:     文件格式 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

Disassembly of section .note.ABI-tag:

0000000000000254 <.note.ABI-tag>:
 254:	04 00                	add    $0x0,%al
 256:	00 00                	add    %al,(%rax)
 258:	10 00                	adc    %al,(%rax)
 25a:	00 00                	add    %al,(%rax)
 25c:	01 00                	add    %eax,(%rax)
 25e:	00 00                	add    %al,(%rax)
 260:	47                   	rex.RXB
 261:	4e 55                	rex.WRX push %rbp
 263:	00 00                	add    %al,(%rax)
 265:	00 00                	add    %al,(%rax)
 267:	00 03                	add    %al,(%rbx)
 269:	00 00                	add    %al,(%rax)
 26b:	00 02                	add    %al,(%rdx)
 26d:	00 00                	add    %al,(%rax)
 26f:	00 00                	add    %al,(%rax)
 271:	00 00                	add    %al,(%rax)
	...

使用objdump -S将其反汇编并且将其C语言源代码混合显示出来:
执行命令gcc -o hello -g hello.c再执行objdump -S test

 

hello:     文件格式 elf64-x86-64


Disassembly of section .init:

00000000000004e8 <_init>:
 4e8:	48 83 ec 08          	sub    $0x8,%rsp
 4ec:	48 8b 05 f5 0a 20 00 	mov    0x200af5(%rip),%rax        # 200fe8 <__gmon_start__>
 4f3:	48 85 c0             	test   %rax,%rax
 4f6:	74 02                	je     4fa <_init+0x12>
 4f8:	ff d0                	callq  *%rax
 4fa:	48 83 c4 08          	add    $0x8,%rsp
 4fe:	c3                   	retq   

Disassembly of section .plt:

0000000000000500 <.plt>:
 500:	ff 35 ba 0a 20 00    	pushq  0x200aba(%rip)        # 200fc0 <_GLOBAL_OFFSET_TABLE_+0x8>
 506:	ff 25 bc 0a 20 00    	jmpq   *0x200abc(%rip)        # 200fc8 <_GLOBAL_OFFSET_TABLE_+0x10>
 50c:	0f 1f 40 00          	nopl   0x0(%rax)

0000000000000510 :
 510:	ff 25 ba 0a 20 00    	jmpq   *0x200aba(%rip)        # 200fd0 
 516:	68 00 00 00 00       	pushq  $0x0
 51b:	e9 e0 ff ff ff       	jmpq   500 <.plt>

Disassembly of section .plt.got:

0000000000000520 <__cxa_finalize@plt>:
 520:	ff 25 d2 0a 20 00    	jmpq   *0x200ad2(%rip)        # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
 526:	66 90                	xchg   %ax,%ax

Disassembly of section .text:

0000000000000530 <_start>:
 530:	31 ed                	xor    %ebp,%ebp
 532:	49 89 d1             	mov    %rdx,%r9
 535:	5e                   	pop    %rsi
 536:	48 89 e2             	mov    %rsp,%rdx
 539:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
 53d:	50                   	push   %rax
 53e:	54                   	push   %rsp
 53f:	4c 8d 05 8a 01 00 00 	lea    0x18a(%rip),%r8        # 6d0 <__libc_csu_fini>
 546:	48 8d 0d 13 01 00 00 	lea    0x113(%rip),%rcx        # 660 <__libc_csu_init>
 54d:	48 8d 3d e6 00 00 00 	lea    0xe6(%rip),%rdi        # 63a 
554: ff 15 86 0a 20 00 callq *0x200a86(%rip) # 200fe0 <__libc_start_main@GLIBC_2.2.5> 55a: f4 hlt 55b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 0000000000000560 : 560: 48 8d 3d a9 0a 20 00 lea 0x200aa9(%rip),%rdi # 201010 <__TMC_END__> 567: 55 push %rbp 568: 48 8d 05 a1 0a 20 00 lea 0x200aa1(%rip),%rax # 201010 <__TMC_END__> 56f: 48 39 f8 cmp %rdi,%rax 572: 48 89 e5 mov %rsp,%rbp 575: 74 19 je 590 577: 48 8b 05 5a 0a 20 00 mov 0x200a5a(%rip),%rax # 200fd8 <_ITM_deregisterTMCloneTable> 57e: 48 85 c0 test %rax,%rax 581: 74 0d je 590 583: 5d pop %rbp 584: ff e0 jmpq *%rax 586: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 58d: 00 00 00 590: 5d pop %rbp 591: c3 retq 592: 0f 1f 40 00 nopl 0x0(%rax) 596: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 59d: 00 00 00 ......

 三、nasm汇编编译器编译生成可执行程序

 3.1Ubuntu安装nasm汇编编译器

(1)先判断是否安装nams
         使用命令whereis nasm,如果显示nasm: /usr/bin/nasm ,则已经安装;如果只显示nasm:          ,则未安装。
(2)安装nasm
         使用命令sudo apt install nasm安装nasm编译器

3.2编译代码hello.asm

(1)使用vim文本编辑器编译一个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 可执行文件

执行结果如下:


   与gcc编译生成的可执行文件对比(功能都是输出Hello,world!):

汇编语言(hello程序)生成的可执行文件:  

 

 gcc生成(hello程序)生成的可执行文件:

 由文件大小可见使用nasm汇编生成的main可执行文件要比gcc编译生成的可执行文件要更小

 


 




总结

通过对gcc工具集的使用,我了解了在一个程序编译过程中是如何一步一步的编译成可执行文件的。除此之外,还了解了ELF文件和汇编文件的格式以及如何用nasm汇编编辑器编译hello.asm文件生成可执行文件hello。以上这些实验,使我对于Linux操作系统上的程序编译的理解更加深入。在这次实验中也出现了许多小错误,如命令中少打了.o或者-o等,导致程序一直运行失败,这告诉我在使用gcc工具的指令时要做到一丝不苟。
 

参考文献:

GCC编译工具集和nasm编译器的简要介绍_不#曾&轻听的博客-CSDN博客_nasm编译器

 使用gcc和gcc的伙伴_Harriet的博客-CSDN博客



 


 





 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/311846.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号