`

从汇编和寄存器层面,简单描述下方法调用的时候都发生了什么

    博客分类:
  • asm
阅读更多

        先看一个简单的汇编程序:

assume cs:code,ss:stack
stack segment
	dw 10 dup('a')
stack ends
code segment
start:
	mov ax,stack
	mov ss,ax
	mov sp,20

	mov ax,1
	mov cx,4
	call s
	mov ax,4c00h
	int 21h
  s:
	add ax,ax
	loop s
	ret 
code ends
end start

         自定义一个stack段,然后产生一次方法调用,用ms的debug工具单步调试,可以看到编译后的代码里边s这个标号变成了offset:

-u
0B55:0000 B8530B        MOV     AX,0B53
0B55:0003 8ED0          MOV     SS,AX
0B55:0005 BC1400        MOV     SP,0014
0B55:0008 B80100        MOV     AX,0001
0B55:000B B90400        MOV     CX,0004
0B55:000E E80500        CALL    0016
0B55:0011 B8004C        MOV     AX,4C00
0B55:0014 CD21          INT     21
0B55:0016 03C0          ADD     AX,AX
0B55:0018 E2FC          LOOP    0016
0B55:001A C3            RET

         call s在编译后对应的指令是:

0B55:000E E80500        CALL    0016

         前边表示内存单元,即调试的时候把程序加载到了0b55:0开始的内存区域,而call s对应的机器码在0b55 * 16 + E的地方,E80500表示call s指令编译后的机器码,E8表示call指令,0500是s标号编译后的地址,由于call实际上等同于是call near ptr s,而near编译后是两个字节的值。而这里的5是offset,而不是绝对地址,call 后边的0016是根据call s下一条指令的地址0b55:0011加上这个offset计算出来的,也就是0b55:0016,由于call near ptr s是段内转移,所以段地址不变,即0b55,只显示0011了。

        另外,从内存单元的机器码和其对应的汇编指令看,mov ax stack被编译成了:

0B55:0000 B8530B        MOV     AX,0B53
         即在0b53:0的位置,应该是我们栈的内存区域(为了更明显起见,我用dw 10 dup('a')初始化的栈内存区):
-d 0b53:0
0B53:0000  61 00 61 00 61 00 61 00-61 00 61 00 61 00 61 00   a.a.a.a.a.a.a.a.
0B53:0010  61 00 61 00 00 00 00 00-00 00 00 00 00 00 00 00   a.a.............
0B53:0020  B8 53 0B 8E D0 BC 14 00-B8 01 00 B9 04 00 E8 0D   .S..............
         从0b53:0开始的16个word,word用两个字节存放,高字节存放在高地址,低字节存放在低地址,用word表示的a,是0061,按地址顺序存放后就成了6100了。

        在开始执行前,先看下寄存器的信息:

-r
AX=0000  BX=0000  CX=0043  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B43  ES=0B43  SS=0B53  CS=0B55  IP=0000   NV UP EI PL NZ NA PO NC
        执行前两条条指令后,ss已经指向了0b53:
-t
AX=0B53  BX=0000  CX=0043  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B43  ES=0B43  SS=0B53  CS=0B55  IP=0003   NV UP EI PL NZ NA PO NC
       在执行call s之前,可以看到sp=0014,stack区域的数据却好像被什么改变了:
-t
AX=0001  BX=0000  CX=0004  DX=0000  SP=0014  BP=0000  SI=0000  DI=0000
DS=0B43  ES=0B43  SS=0B53  CS=0B55  IP=000E   NV UP EI PL NZ NA PO NC
-d 0b53:0
0B53:0000  61 00 61 00 61 00 61 00-61 00 53 0B 00 00 08 00   a.a.a.a.a.S.....
0B53:0010  55 0B 57 05 00 00 00 00-00 00 00 00 00 00 00 00   U.W.............
         不过无妨,看看call之后,sp值减小了2,也就是往栈里压入了一个word:
-t
AX=0001  BX=0000  CX=0004  DX=0000  SP=0012  BP=0000  SI=0000  DI=0000
DS=0B43  ES=0B43  SS=0B53  CS=0B55  IP=0016   NV UP EI PL NZ NA PO NC

         这个word的只是多少呢?从stack的内存区域0b53:0013地址可以看到,是0011,其实呢也就是call s下一条指令的地址!由此我们可以知道方法调用的时候,会把方法调用指令后边那条指令的地址压入栈中:

-d 0b53:0
0B53:0000  61 00 61 00 61 00 61 00-01 00 00 00 16 00 55 0B   a.a.a.a.......U.
0B53:0010  57 05 11 00 00 00 00 00-00 00 00 00 00 00 00 00   W...............

         那什么时候会弹出呢?猜猜也是方法调用返回的时候!即ret指令执行的时候,sp增加了2,即弹出了栈里边存的值,并把弹出来的值赋给了ip寄存器。被调用方法里边执行ret指令后的寄存器信息和栈内存区域:

-t
AX=0010  BX=0000  CX=0000  DX=0000  SP=0014  BP=0000  SI=0000  DI=0000
DS=0B43  ES=0B43  SS=0B53  CS=0B55  IP=0011   NV UP EI PL NZ AC PO NC
-d 0b53:0
0B53:0000  61 00 61 00 61 00 61 00-10 00 10 00 00 00 11 00   a.a.a.a.........
0B53:0010  55 0B 57 05 00 00 00 00-00 00 00 00 00 00 00 00   U.W.............

         ret后,从ip指向的指令地址继续执行,也即方法调用指令的下一条指令继续开始。但是不得不说栈内存区域有点紊乱啊,不知道为啥......

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics