首页 > 代码库 > x86的ABI(C函数实现原理)分析

x86的ABI(C函数实现原理)分析

    This article is aim to explain how to use assemble language realize those common function
in C. But I fail to get a simple method to introduce it because of some reasons . I will try to
extract some key point at this article. Then analysize a example in detail in next article. 
    ( Of course, For those experienced guys, maybe a example is enough.)

    1. How can we create local variable in a function?

/**
*    How can we create local variable in a function?
*
*    This is a interesting problem. First at all, what is the essence of a variable ?
*        The answer is space,memory space. Recall the reason that we introduced the concept of
*    the variable .we need those space to save some information. So,in fact, create a variable is
*    equal to allocate some space.
*
*        Now, the question is we should reside those variable in where ?
*    Actually, we has been know something about global variable and static variable. They are
*    arranged when compiled. But what about local variable ? where is it reside in ?
*        Obviously,we can‘t arrange a special memory space for it when compile. Think about
*    recursive function. If we specify a special memory space for a local variable, how can we
*    ensure a function is reentrant. That will be awful, totally awful. So the conclusion is we
*    can‘t reside the local variable in a absolute address, no matter where. It should be reside
*    in a relative address. If we build a base address for every function call, then every function
*    call will become independent. That is what we want theoretically. Actually, How can we
*    build that frame ?
*
*        Now, it is time to introduce a important design -- stack frame. It is helpful to realize
*    our plan above. two special register, esp and ebp, was introduced to build a frame for a
*    function. In addition, it provide a nice solution for function call, too.
*
*        the emphasis of this frame is stack. It is building down. %ebp was used to point to
*    the bottom of the frame .%esp was used to point to the top of frame. That means if we
*    build a function like this:
*            int func()
*            {
*                ....
*            }
*
*    we will get a frame like this
*            0xbfffe6a0    <-- %ebp
*            ....
*            0xbfffe690    <-- %esp
*
*    if there are some local variable in the function, just like this:
*            int func( )
*            {
*                int a, b, c;
*            }
*
*    the frame maybe:
*            0xbfffe6a0    <-- %ebp
*            0xbfffe69c    <-- c, 4 bytes
*            0xbfffe698    <-- b, 4 bytes
*            0xbfffe694    <-- a, 4 bytes
*                               <-- %esp
*
*    we have been know how to create some local variable in assemble language.
*/

    2. How can we call a function ?

/*
*    How can we call a function ?
*
*        This seems like simple. From the view of machine, call a function is equal to change the
*    flow of instructions, is equal to revise the value of register %IP.
*            mov func,ip;
*
*        but how can we return the caller ? maybe save the value of IP before revise is necessary
*    . It seems we have two choice : save in a register, or save in the memory. Think about the
*    current situation, register is still finite, and function call is infinite logically. So the memory
*    will be a good choice.
*
*        But where should be the place for those IP informations ? I thought we could allocate
*    some memory space to build a special structure. It is used to save those information. Actually,
*    a better way was introduced. see the following example, if we call a subroutine in main
*    function:
*            int main()
*            {
*                fun();
*            }
*            void fun()
*            {
*                ...
*            }
*
*
*        The value of pre-IP will be push into the stack and register %IP will be revise to the
*    entry of fun function. the information will be saved like this.
*            0xbfffe6a0 <-- %ebp for main()
*            0xbfffe69c <-- var 2
*            0xbfffe698 <-- var 1
*            0xbfffe694 <-- var 0
*            ....
*            xxxxxxxx   <-- return address
*                           <-- %esp for main()
*
*    when we finish this subroutine, we could return the caller by pop the value into register %IP.
*/

    3. How can we pass parameters between caller and callee ?

/**
*    How can we pass parameters between caller and callee ?
*
*        In C language, it is common to transmit data between caller and callee. How can we
*    realize it in assemble language ? Need to say, every function call should have a independent
*    parameters list, because we need our function is reentrant. see this example:
*            int main()
*            {
*                fun(1,2,3);
*            }
*            int fun(int a, int b, int c)
*            {
*   
*            }
*
*        before we call fun(). The stack frame of main is :
*            0xbbbb00ff <-- %ebp for main
*            ....            <-- variables
*            ....            <-- %esp for main
*
*
*        when we call this function, the following steps will be done.
*        First at all, Push the parameters into the stack,
*            0xbbbb00ff  <-- %ebp for main
*            ....             <-- variables
*            0xbbbb00ec <-- 3, the last parameter
*            0xbbbb00e8 <-- 2, the second parameter
*            0xbbbb00e4 <-- 1, the first parameter
*                           <-- %esp for main
*
*        then push the return address of caller,
*            0xbbbb00ff  <-- %ebp for main
*            ....             <-- variables
*            0xbbbb00ec <-- 3, the last parameter
*            0xbbbb00e8 <-- 2, the second parameter
*            0xbbbb00e4 <-- 1, the first parameter
*            0xbbbb00e0 <-- return address of caller
*                              <-- %esp for main
*
*        then jump to the function. It will build a new stack frame for this call. But before that,
*    we need to save the information of caller frame.Because we will recover the frame of caller
*    when we finish this subroutine. The frame is :
*            0xbbbb00ff  <-- the base address of main frame
*            ....             <-- variables
*            0xbbbb00ec <-- 3, the last parameter
*            0xbbbb00e8 <-- 2, the second parameter
*            0xbbbb00e4 <-- 1, the first parameter
*            0xbbbb00e0 <-- return address of caller
*                              <-- the top address of main frame
*                            <-- %ebp for fun
*            0xbbbb00dc <-- the value of %ebp of main.
*            ....             <-- variables
*                            <-- %esp for fun
*
*    Based on those steps, we build a new frame for callee.
*/