设为首页 加入收藏

TOP

x86的ABI分析(函数实现原理)--part2(一)
2015-07-20 18:02:33 来源: 作者: 【 】 浏览:11
Tags:x86 ABI分析 函数 实现 原理 --part2


As we all know, function is a important concept in programming design. At this moment, I even
don't know what kind of programming language can working without function. ( maybe i am new).
maybe some special language can ? But this concept is very necessary indeed. Now, the question
is : How can we realize this concept in assemble language/machine code ?


1. Overview


Three issues will be explain by this example..
a). How can we call a function?
b). How can we build a stack frame for local variable ?
c). How can we pass parameters between caller and callee ?

#include 
  
   
/*
*    This is a empty function, it does nothing. we build it for show how can we call a function.
*/
void call()
{ }
/*
*    Explain how can we build a stack frame.
*/
void frame()
{
        int b;
}
/*
*    Explain something about pass parameters and return result.
*/
int parameters( int a, int b, int c)
{

   
        int sum;
        sum = a + b + c;
        return sum;
}
	
int main()
{
        int ret;
        call( );
        frame( );
        ret = parameters(1,2,3);
        return 0;
}


   

For discuss the issues, we need to translate it to a low level language, assemble language. In this example, I use a linux compiler--gcc. It will help us to get a assemble code. (Actually, there have a problem in here--different compiler may be use different convention, even use different Application Binary Interface, but they still have some common features.) The corresponding new code is :

        ......
        call:
                pushl  %ebp
                movl  %esp, %ebp
                popl %ebp
                ret
        ......
        frame:
                pushl  %ebp
                movl  %esp, %ebp
                subl   $16, %esp
                leave
                ret
                ......
        parameters:
                pushl  %ebp
                movl  %esp, %ebp
                subl  $16, %esp
                movl  12(%ebp), %eax
                addl  8(%ebp), %eax
                addl  16(%ebp), %eax
                movl  %eax, -4(%ebp)
                movl  -4(%ebp), %eax
                leave
                ret
        ......
        main:
                leal 4(%esp), %ecx
                andl $ -16, %esp
                pushl  -4(%ecx)
                pushl  %ebp
                movl  %esp, %ebp
                pushl  %ecx
                subl  $28, %esp
                call  call
                call  frame
                movl  $3, 8(%esp)
                movl  $2, 4(%esp)
                movl  $1, (%esp)
                call  parameters
                movl  %eax, -8(%ebp)
                movl  $0, %eax
                addl  $28, %esp
                popl  %ecx
                popl  %ebp
                leal  -4(%ecx), %esp
                ret
        ......

(Be careful, Here is AT&T syntax.)

2. How can we call a function?

From the view of machine, call a function is equal to change the instruction stream. It
seems like simple. Actually, there are another problem, How can we return to the instruction
stream of the caller ? A valid way is save the instruction pointer before jump to the callee.
Now, Let us see this example:

        int main()
        {
                ...
                call( );
                ...
        }
        void call()
        { }

This is simple function call, how can we realize it by assemble language ? examine the
corresponding code.

        main:
                ......
                call  call
                ......

In @main function, it call a function @call by a assemble instruction--call. This instruction
does two things needed to be done. one, save the current value of register @IP in stack.
Two, revise the value of @IP to the address of caller.

pushl %IP;
movl call, %IP;

        call:
                ....
                ret

when we complete this subroutine, the next step is to return to the previous instruction
stream. The current status is

.... <-- %EBP for caller
....
0xeeee0000 <-- return address
<-- %ESP for caller
So, we just need to pop the data from stack.
pop %IP;

3. How can we build a stack frame for local variable ?

For local variable, there is a important feature that we need--reentrant. we want to local variable
can be independent in every function call, even call a recursive function. So we dynamically create
independent memory space for every function call, this is called --stack frame. Now , examine the code.

        int main()
        {
            ...
            frame( );
            ...
        }
        void frame()
        {
            int b;
        }

before we call @frame, all of thing is same with the example above. The curren

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇栈(一)――栈的基本操作 下一篇HDU 2639 Bone Collector II

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: