从汇编角度分析C语言指针

从汇编角度分析C语言指针

分析在ubuntu18.04中进行。

C语言源代码 pointer.c

#include <stdio.h>

int main(void)
{
	int a=1;
	int *pa=&a;
	int *pb=NULL;
	pb=pa;

	return 0;
}

使用指令:

gcc -m32 -S -o pointer.s pointer.c

生成32位汇编源程序 pointer.s如下:

	.file	"pointer.c"
	.text									#代码段
	.globl	main							#全局标签
	.type	main, @function					
main:										#代码入口
.LFB0:
	.cfi_startproc
	leal	4(%esp), %ecx 					#leal:加载有效地址,这句话的意思是将R[esp]+4赋值给R[ecx]   l代表操作32位
	.cfi_def_cfa 1, 0
	andl	$-16, %esp 						#32位下-16的16进制码为0xFFFFFFF0,将堆栈与下一个最低的16字节边界对齐  
											#为了SIMD(Single Instruction Multiple Data)指令,我们的例程不包括,所以可能没必要
	pushl	-4(%ecx)						
	pushl	%ebp							
	.cfi_escape 0x10,0x5,0x2,0x75,0
	movl	%esp, %ebp						
	pushl	%ecx							
	.cfi_escape 0xf,0x3,0x75,0x7c,0x6
	subl	$20, %esp						
	call	__x86.get_pc_thunk.ax						
	addl	$_GLOBAL_OFFSET_TABLE_, %eax	#关于_GLOBAL_OFFSET_TABLE_
											#https://stackoverflow.com/questions/9685699/what-is-global-offset-table
	movl	%gs:20, %eax					#参考http://www.cache.one/read/15313050
	movl	%eax, -12(%ebp)
	xorl	%eax, %eax 						#eax寄存器归零
	movl	$1, -24(%ebp)					#应该对应语句 a=1  即变量a的存储地址为ebp-24,保存的值为1
	leal	-24(%ebp), %eax					#把变量a的地址写入寄存器eax
	movl	%eax, -20(%ebp)					#把变量a的地址保存在内存ebp-20中,ebp-20应该是指针变量pa的存储地址,保存的值为变量a的存储地址值								
	movl	$0, -16(%ebp)					#ebp-16应该是指针变量pb的存储地址,其值初始化为NULL,即0.
	movl	-20(%ebp), %eax					#把指针变量pa中保存的变量a的地址值写到寄存器eax,执行完毕后eax中是变量a的地址值
	movl	%eax, -16(%ebp)					#把变量a的地址值保存在内存ebp-16中,即指针变量pb的存储地址,存储的值为变量a的地址
	movl	$0, %eax						#返回值
	movl	-12(%ebp), %edx
	xorl	%gs:20, %edx					#Stack canaries, 堆栈金丝雀,校验作用
											#参见 https://stackoverflow.com/questions/12234817/what-does-this-instruction-do-mov-gs0x14-eax
	je	.L3
	call	__stack_chk_fail_local
.L3:
	addl	$20, %esp                     	#恢复
	popl	%ecx							#恢复
	.cfi_restore 1
	.cfi_def_cfa 1, 0
	popl	%ebp							#恢复
	.cfi_restore 5
	leal	-4(%ecx), %esp					#恢复
	.cfi_def_cfa 4, 4
	ret										#返回
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.section	.text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
	.globl	__x86.get_pc_thunk.ax
	.hidden	__x86.get_pc_thunk.ax
	.type	__x86.get_pc_thunk.ax, @function
__x86.get_pc_thunk.ax:
.LFB1:
	.cfi_startproc
	movl	(%esp), %eax
	ret
	.cfi_endproc
.LFE1:
	.hidden	__stack_chk_fail_local
	.ident	"GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
	.section	.note.GNU-stack,"",@progbits

在as汇编中,以.开头的语句为汇编命令(或称为伪指令、指示符) --《Linux内核完全注释》3.2.2.2节

使用到的各个寄存器介绍(参考Linux系统分析入门–简单汇编代码分析):
eax:累加器(Accumulator)
ebx:基地址寄存器(Base Register)
ecx:计数寄存器(Count)
edx:数据寄存器(Data Register)
ebp:堆栈基指针(Base Pointer)
esi和edi:变址寄存器(Index Register)
esp:堆栈顶指针(Stack Pointer)
intel堆栈从高地址增长到低地址。
80386的寄存器,共16个,8个通用(如前所述)、段寄存器(6个)、状态寄存器和指令寄存器。如下表所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
前面的代码由于目前汇编水平有限并不能完全看懂。但是可以分析一下明显与C语言语句有对应关系的几个。从movl $1, -24(%ebp)到movl %eax, -16(%ebp) 的栈内存分布。
这是在执行movl $1, -24(%ebp)时的内存分布,设ebp=0x20,则esp=0x08,寄存器ecx中的值保存在地址0x1c中
在这里插入图片描述
在执行完语句movl $1, -24(%ebp)后,内存ebp-24=0x8处的值变为1,此处保存变量a的值。

在这里插入图片描述
在执行完语句leal -24(%ebp), %eax之后,eax中的值为ebp-24,也即变量a的存储地址,在我们的假设中即eax=0x08。
在执行完语句movl %eax, -20(%ebp),内存分布如下
在这里插入图片描述
在执行完movl $0, -16(%ebp)之后,内存分布如下图

在这里插入图片描述在执行完movl -20(%ebp), %eax后,eax中的值变为内存地址ebp-20=0x0c中保存的值,也即指针变量pa的值,即eax=0x08。
执行完movl %eax, -16(%ebp)后,内存地址ebp-16=0x10中保存的值变为0x08,即指针变量pb的值变为0x08,也即指针变量pb指向了变量a,此时内存分布为
在这里插入图片描述
如果在源代码最后加入一句:

*pa=2;

最后汇编文件会加入语句:

	movl	-20(%ebp), %eax				#从地址ebp-20中取出保存的值,即指针变量pa的值,并赋给eax寄存器
	movl	$2, (%eax)					#将2赋值给eax寄存器的值表示的内存

按我们的假设,执行完第一句之后,eax的值为指针变量pa的保存的值,也即内存地址ebp-20=0x0c处内存的值,为0x08。
执行完第二句之后,内存地址0x08处保存的值变为2,也即变量a的值变为2。

如果最后直接加入

a=2;

则汇编文件会加入语句:

movl	$2, -24(%ebp)

即直接将值赋给变量a。就是将2写入保存变量a的地址ebp-24=0x08。

从上面可以发现先定义的变量在低地址,也就是栈顶,后定义的变量在高地址,也就是栈底。

参考:
1.https://en.wikibooks.org/wiki/X86_Assembly/GNU_assembly_syntax
2.INTEL 80386 PROGRAMMER’S REFERENCE MANUAL 1986

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值