Find the answer to your Linux question:
Results 1 to 8 of 8
Good day. Can you please help me to solve next problem. When I check assembler code of C function (generated by -S option) I can not determine wheter arquments passed ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Just Joined!
    Join Date
    Apr 2012
    Posts
    3

    functions arguments


    Good day. Can you please help me to solve next problem.

    When I check assembler code of C function (generated by -S option) I can not determine wheter arquments passed from 1 function to another. Normally, according to C calling convention arquments had to be pushed to stack and retrieved from it in destination function. But there is not stack instructions in assembler code. Instead destination function directly takes arquments from %edi, or %esi. But caller function doesn't put to %edi any value. Here are extract of C and corresponding Assembler code

    int ahm_data;

    int noinline do_test_call_1(int x, int y){

    return x+y;

    }

    int noinline do_test_call(int x, int y){

    int z;
    /* I switch places of variables */
    z = do_test_call_1(y,x);

    return z;

    }

    int var;

    void test_call(int x,int y){

    var=do_test_call(x,y);
    }

    Assembler code generated for this code is

    .globl do_test_call_1

    do_test_call_1:
    .LFB1943:
    .loc 1 1197 0
    .cfi_startproc
    .LVL526:
    pushq %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register 6
    call mcount
    .loc 1 1197 0
    .loc 1 1201 0
    popq %rbp
    .cfi_def_cfa 7, 8
    .cfi_restore 6
    .loc 1 1199 0
    leal (%rdi,%rsi), %eax
    .loc 1 1201 0
    ret
    .cfi_endproc
    .LFE1943:
    .size do_test_call_1, .-do_test_call_1
    .p2align 4,,15
    .globl do_test_call

    do_test_call:
    .LFB1944:
    .loc 1 1203 0
    .cfi_startproc
    .LVL527:
    pushq %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register 6
    call mcount
    .loc 1 1203 0
    movl %edi, %eax
    movl %esi, %edi
    .LVL528:
    .loc 1 1207 0
    movl %eax, %esi
    .LVL529:
    call do_test_call_1
    .LVL530:
    .loc 1 1211 0
    popq %rbp
    .cfi_def_cfa 7, 8
    .cfi_restore 6
    ret
    .cfi_endproc
    .LFE1944:
    .size do_test_call, .-do_test_call
    .p2align 4,,15
    .globl test_call

    test_call:
    .LFB1945:
    .loc 1 1215 0
    .cfi_startproc
    .LVL531:
    pushq %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register 6
    call mcount
    .loc 1 1215 0 #how does arquments passed to
    .loc 1 1217 0 #do_test_call
    call do_test_call
    .LVL532:
    .loc 1 1218 0
    popq %rbp
    .cfi_def_cfa 7, 8
    .cfi_restore 6
    .loc 1 1217 0
    movl %eax, var(%rip) //result in %eax
    .loc 1 1218 0
    ret
    .cfi_endproc
    .LFE1945:
    .size test_call, .-test_call
    .globl var
    .bss
    .align 4

    .size var, 4
    var:
    .zero 4

  2. #2
    Just Joined!
    Join Date
    Dec 2008
    Location
    Lund, Sweden
    Posts
    31
    Quote Originally Posted by heliks85 View Post
    Good day. Can you please help me to solve next problem.

    When I check assembler code of C function (generated by -S option) I can not determine wheter arquments passed from 1 function to another. Normally, according to C calling convention arquments had to be pushed to stack and retrieved from it in destination function. But there is not stack instructions in assembler code.
    It is quite common for the first arguments to be passed in registers rather than on the stack. It makes for faster code execution. It is all defined in the ABI (Application Binary Interface) for the compiler-processor combination in question.

    Instead destination function directly takes arquments from %edi, or %esi. But caller function doesn't put to %edi any value.
    Yes it does:
    .
    .
    .
    .loc 1 1203 0
    movl %edi, %eax
    movl %esi, %edi
    .LVL528:
    .loc 1 1207 0
    movl %eax, %esi
    .LVL529:
    call do_test_call_1
    .
    .
    .
    Here edi is loaded with the contents of eax (movl %edi, %eax).

  3. #3
    Just Joined!
    Join Date
    Jun 2008
    Posts
    7
    The function that calls test_call will place the x, y parameters in esi and edi (I think in that order).

    test_call will "take" its first parameter (which is in esi) and pass it as the first parameter to do_test_call. It's second param (which is in edi) gets passed as the second parameter to do_test_call. Ah! The parameters are already in the right place (test_call (x,y) .. do_test_call(x,y)...). That is, test_call "needs to move" esi to esi and edi to edi. Thus, test_call doesn't have to do anything as regards these parameters.

    Finally, we can then also verify that do_test_call does in fact switch the order of parameters, as that is necessary to do within do_test_call (esi to edi and edi to esi) so that do_test_call_1 can be called correctly.

  4. $spacer_open
    $spacer_close
  5. #4
    Just Joined!
    Join Date
    Jun 2008
    Posts
    7
    More specifically, those 3 move instructions are a parameter switch between esi and edi using eax as the temp storage.

  6. #5
    Just Joined!
    Join Date
    Apr 2012
    Posts
    3
    Thank you all for your responses. Yes you are right, but we can see how %edi, and %esi are loaded at do_test_call function, not on test_call. And it is because I replaced positions of arguments when calling do_test_call_1 from do_test_call, in order to force sources of arguments come into play. And we see that they are %edi and %esi. But test_call function does not load any values to this registers when it calls do_test_call. Yes because there is no need, they are already at the right place, %edi and %esi. test_call is system_call. And from user space I put value to %ebx which supposed to be the first argument of the test_call. But as we see function takes first argument from %edi instead %ebx. I found something on arch/x86/include/asm/calling.h file like this

    x86 function call convention, 64-bit:
    -------------------------------------
    arguments | callee-saved | extra caller-saved | return
    [callee-clobbered] | | [callee-clobbered] |
    ---------------------------------------------------------------------------
    rdi rsi rdx rcx r8-9 | rbx rbp[*] r12-15 | r10-11 | rax, rdx [**]

    x86 function calling convention, 32-bit:
    ----------------------------------------
    arguments | callee-saved | extra caller-saved | return
    [callee-clobbered] | | [callee-clobbered] |
    -------------------------------------------------------------------------
    eax edx ecx | ebx edi esi ebp[*] | <none> | eax, edx [**]

    Here for 86_64 arch the first arg is %rdi, but my user space code is like this

    movl $347, %eax #test_call number
    movl $string, %ebx # first arg of test_call
    movl say, %ecx
    int $0x80
    So I don't put anything to %edi, but test_call gets its firts arg from %edi. How %edi gets data which I put to %ebx after int $0x80?

  7. #6
    Just Joined!
    Join Date
    Jun 2008
    Posts
    7
    I am not sure what is the new question. Are you doing an assembly language call to test_call and effectively trying to define a new calling convention using bp? If so, then I would instead pass the arguments as the compiler has coded up and shown you in assembly language, unless you find the flags or configuration files that would allow you to specify precise details of a new convention. Since I haven't looked at assembler for a while, I had to google to address your initial question. I have not yet read the details in the ABI spec, but the example you showed and others I saw online indicate to me that rsi and rdi (64bit, right?) are where the parameters are passed and not rbp.

  8. #7
    Just Joined!
    Join Date
    Apr 2012
    Posts
    3
    Quote Originally Posted by Jose_X View Post
    I have not yet read the details in the ABI spec, but the example you showed and others I saw online indicate to me that rsi and rdi (64bit, right?) are where the parameters are passed and not rbp.
    Seems So. İ am not about to create a new calling convention. Just in 32 bit case things were simple, on user space you load args to registers %ebx, %ecx, %edx (up to 6 arguments supported) and in kernel side for the system_call(x,y,z) args would have values x==%ebx, y==%ecx, z==%edx ...
    But 64 bit I again load args to registers %ebx, %ecx, %edx and make system_call (int $0x80), then in kernel side args for system_call(x,y,z) is like x==%edi, y==%esi ... So if convention required to load %edi, %esi on user side then there would no question as the register values doesn't change when you make a system call. On kernel side you get from registers that what you pass on user side. But I load %ebx on user side and get that value from %edi on kernel side. Either data transfer from %ebx to %edi occurs while execution gets to system call itself as there is some small routine executed right after int $0x80, or cpu itself passes data from %ebx to %edi. This seems not so real.

  9. #8
    Just Joined!
    Join Date
    Jun 2008
    Posts
    7
    Let me ask, what does "call mcount" do? Could this play a role in flipping registers or something else like that? I have been curious to know.

    OK, the compilation you did came from the compiler (gcc I assume). My guess is that it uses a convention for user space functions, and that is what you listed above. If you want to (call the kernel) use the kernel conventions (since apparently those are different), you either have to use manual assembly just as you were doing or else invoke some gcc flag or other to produce for kernel system calls.

    My suggestion if you are going to call a kernel interface through assembly is to ignore the convention suggested by that compiler assembly and instead just use whatever the kernel docs state. Specifically, keep using bp+friends and don't worry about the si/di that gcc produces for ordinary user space calls.

    My guess would be that if we dig down into the implementation of glibc we would find the use of the kernel registers (like bp) right before the int x80. It should be [STRIKE]easy[/STRIKE] possible to verify this.
    Last edited by Jose_X; 04-18-2012 at 05:41 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •