GCC、objdump、机器表示基础知识

GCC、objdump、机器表示基础知识

前言

​ 最近在看CSAPP(深入理解计算机系统)第三章,打算将自己所学的知识及学习的过程记录下来,以免学了跟没学一样。自从上大学后就一直没有怎么好好学习了,希望自己能够从今天起,每天学习一点点,逐渐改变自己颓废的生活方式。不求一天学多少内容,但求能够细水长流,每天都能在短暂的学习的时间里保持充沛的精力。

​ 如书中所言,人们在学习时常常会这样想

我理解了一般规则,不愿意劳神去学习细节

​ 我们总是觉得理解了一个知识点,可以证明自己的智力,而学习细节则是枯燥而又无聊的。这也是我的思维方式,我总觉得不应该在简单的事情上浪费时间,于是现在薄弱的基础严重制约了我的能力。在每个领域的学习之初,必然有大量重复而又无聊的基础知识,和一些琐屑般的细节,我们往往会忽视它们。当遇到能力的瓶颈的时候,对造成这一切的原因一无所知,也许恰恰就是那些被自己忽视掉的细节。

​ 步入正题,开始对csapp第三章——程序的机器级表示的学习。

下面是学习机器代码的原因、能力要求及学习方式。

本章使用x86-64 Linux平台,GCC编译器,objdump反汇编器

基础知识

GCC的调用方式
linux> gcc -Og -o p p1.c p2.c

-Og 使用能够生成符合原始C代码整体结构的机器代码的优化等级。(便于学习,如果无法使用,则使用-O1)

-o p 用于将生成的可执行文件命名为p

机器级编程的抽象方式:
  1. 由指令集体系结构来定义机器级程序的格式和行为,将程序行为描述成每条指令按顺序执行的
  2. 机器级程序所使用的内存地址是虚拟地址,操作系统负责将虚拟地址翻译成实际处理器内存中的物理地址
汇编程序员可见寄存器
  • 程序计数器(“PC”,在x86-64中用%rip表示)给出将要执行下一条指令在内存中的地址。
  • 整数寄存器 包含16个命名的为之,存储64位的值,可以存储地址或整数数据,及其他特定功能。
  • 条件码寄存器 保存最近执行的算术或者逻辑指令的状态信息。实现例如if或while语句。
  • 向量寄存器 存放一个或多个整数或浮点数值。
GCC参数功能(资料来源
#-c 预处理、编译、汇编
linux> gcc -c a.c  #将源文件编译为目标文件a.o(进行编译、汇编,但是不进行链接)
#-o 制定目标名称,并且可以将.c文件或.o文件作为输入
linux> gcc -o app a.c  #将a.c生成的可执行文件写入app.out内,若不用-o,则生成a.out

linux> gcc -c func.c main.c          #只编译汇编不链接,生成func.o和main.o
linux> gcc func.o main.o -o app.out  #将目标文件链接为可执行文件app.out
#-E 生成预处理文件
linux> gcc -E a.c -o a.i #把预处理结果导出a.i文件
linux> gcc -E a.c > a.txt#只激活预处理,不生成文件,重定向到a.txt中
linux> gcc -E a.c |more  #在more中显示 
#-S 生成汇编文件
linux> gcc -S a.c        #编译a.c,将其翻译成汇编语言,存储在circle.s文件中
#-l 手动添加链接库
linux> gcc a.c -o main.out -lm #链接数学库math.h


#通常,GCC会自动在标准库目录中搜索文件,例如/usr/lib
#想链接其他目录中的库,得特别指明
linux> gcc a.c -o main.out /usr/lib/libm.a #将libm.a链接到main.o
linux> gcc a.c -o main.out -L/usr/lib -lm  #使用-L选项,为GCC增加另一个搜索链接库目录
objdump参数功能
linux> objdump -d a.o    #显示文件
linux> objdump -f a.o    #显示文件头信息
linux> objdump -D a.o    #反汇编所有section
linux> objdump -h a.o    #显示目标文件各个section的头部摘要信息
linux> objdump -x a.o    #显示所有可用的头信息,包括符号表、重定位入口
linux> objdump -i a.o    #显示可用架构和目标格式列表
linux> objdump -r a.o    #显示文件的重定位入口
linux> objdump -R a.o    #显示文件的动态重定位入口,仅仅对于动态目标文件有意义
linux> objdump -S a.o    #尽可能反汇编出源代码,与-d类似
linux> objdump -t a.o    #显示文件的符号表入口
ATT与intel汇编代码格式的区别
//mstore.c
long mult2(long ,long );

void multstore(long x,long y,long *dest){
	long t = mult2(x,y);
	*dest = t;
}

使用gcc生成intel格式的汇编代码

linux> gcc -Og -S -masm=intel mstore.c
linux> vim mstore.s

结果如下:

	.file	"mstore.c"  
	.intel_syntax noprefix
	.text
	.globl	multstore
	.type	multstore, @function
multstore:
.LFB0:
	.cfi_startproc
	push	rbx
	.cfi_def_cfa_offset 16
	.cfi_offset 3, -16
	mov	rbx, rdx
	call	mult2@PLT
	mov	QWORD PTR [rbx], rax
	pop	rbx
	.cfi_def_cfa_offset 8
	ret
	.cfi_endproc
.LFE0:
	.size	multstore, .-multstore
	.ident	"GCC: (Debian 8.3.0-6) 8.3.0"
	.section	.note.GNU-stack,"",@progbits

而GCC默认生成的汇编代码是ATT格式的

linux> gcc -Og -S mstore.c
linux> vim mstore.s

结果如下:

	.file	"mstore.c"
	.text
	.globl	multstore
	.type	multstore, @function
multstore:
.LFB0:
	.cfi_startproc
	pushq	%rbx            #	push	rbx  (in intel)指令后缀、寄存器名字
	.cfi_def_cfa_offset 16
	.cfi_offset 3, -16
	movq	%rdx, %rbx      #   mov	rbx, rdx (in intel) 操作数相反
	call	mult2@PLT
	movq	%rax, (%rbx)    #	mov	QWORD PTR [rbx], rax (in intel)不同表示方式
	popq	%rbx
	.cfi_def_cfa_offset 8
	ret
	.cfi_endproc
.LFE0:
	.size	multstore, .-multstore
	.ident	"GCC: (Debian 8.3.0-6) 8.3.0"
	.section	.note.GNU-stack,"",@progbits

从中我们能发现如下区别:

  • Intel代码省略了指令的大小的后缀。是push和mov,而不是pushq和movq。
  • Intel代码省略了寄存器名字前面的'%'符号
  • Intel代码用不同的方式来描述内存中的位置。'QWORD PTR [rbx]',而不是'(%rbx)'。
  • 在带有多个操作数时,列出操作数的顺序相反。
posted @ 2019-07-18 21:12  shadowgully  阅读(567)  评论(0编辑  收藏  举报