1

I have this assembly code(Linux 32Bit compiled with gcc -m32) and I do not really understand why my program doesn't work.

.data

    .bla:
            .ascii "%d\n\0"

    .globl main

    .text

    main:           pushl $4
                    call factorial
                    movl $1, %eax
                    ret
    factorial:
                    push %ebp
                    movl %esp, %ebp
                    movl 8(%ebp), %ecx
                    cmpl $1, %ecx
                    jg .rek
                    movl $1, %eax
                    movl %ebp, %esp
                    pop %ebp
                    ret

    .rek:
                    decl %ecx
                    pushl %ecx
                    call factorial
                    addl $4, %esp
                    movl 8(%ebp), %ecx
                    imull %ecx, %eax
                    pushl %eax
                    pushl $.bla
                    call printf
                    addl $8, %esp
                    movl %ebp, %esp
                    pop %ebp
                    ret

Unfortunately every time a Segmentation Fault does occur + parameters bigger than 4 do not work.

This should be my stack when I run the program with "3":

3
ret add
ebp
2
ret add
ebp
1
ret add
ebp

When I reach the bottom of the recursion I take the return value saved in eax and multiply it with 8(%ebp) which should be the next value.

I really appreciate any help you can provide.

Noahnder
  • 415
  • 1
  • 4
  • 12

1 Answers1

2

Three issues I see.

1) Your call maine (I'm assuming that was a typo, and you meant, main) should be call factorial

2) In your main program, you don't restore your stack pointer.

3) Your printf call modifies %eax and overwrites the result of your factorial before it returns.

Repaired program:

.data

.bla:
        .ascii "%d\n\0"

.globl main

.text

main:   pushl $5
        call factorial
        addl $4, %esp         # ADDED THIS
        movl $1, %eax
        ret
factorial:
        push %ebp
        movl %esp, %ebp
        movl 8(%ebp), %ecx
        cmpl $1, %ecx
        jg .rek
        movl $1, %eax
        movl %ebp, %esp
        pop %ebp
        ret
.rek:
        decl %ecx
        pushl %ecx
        call factorial   # FIXED THIS
        addl $4, %esp
        movl 8(%ebp), %ecx
        imull %ecx, %eax
        pushl %eax            # ADDED THIS - SAVE RETURN VALUE
        pushl %eax
        pushl $.bla
        call printf
        addl $8, %esp         # MODIFIED THIS
        pop %eax              # ADDED THIS (restore eax result)
        movl %ebp, %esp
        pop %ebp
        ret
lurker
  • 56,987
  • 9
  • 69
  • 103
  • I'm pretty sure `printf()` is allowed to modify the argument on the stack. `pop %eax` doesn't necessarily restore the value of `%eax` from before the call to `printf()`. – EOF Aug 27 '15 at 13:11
  • 1
    @EOF no, `printf` should not be modifying the stack arguments. Why would it do that? Can you give an example where that happens? Did you down vote my answer on that basis? It would seem to violate the principle of the C calling sequence. Also, I pointed out, correctly, all of the issues with the OP's code. So on that basis alone a downvote, I think, is improper. – lurker Aug 27 '15 at 13:12
  • A function is absolutely allowed to modify its arguments. You wouldn't expect a function's arguments to be left unmodified if they were passed in registers, why would arguments passed on the stack be any different? – EOF Aug 27 '15 at 13:17
  • @EOF, please give an example where this happens in a standard C calling sequence. Of course I could write an assembly program that modifies the stack, anyplace. But a C lib function will not do this. Again, example please to substantiate your claim. Passed as registers is a *totally different* scenario. Yes, I would expect it to be different. – lurker Aug 27 '15 at 13:19
  • how about http://stackoverflow.com/q/30284914/3185968 ? Or better http://stackoverflow.com/q/1684682/3185968 ? – EOF Aug 27 '15 at 13:20
  • @EOF that question and answer actually support my point if you read it carefully. *The C programming language mandates that arguments are passed by value. So any modification of an argument (like an x++; as the first statement of your foo) is local to the function and does not propagate to the caller.* In this case, parameters are *passed by value* to `printf`. If someone wants to write a custom `printf` in assembly language that alters the values, that's a different story. It would then be violating the C standard. – lurker Aug 27 '15 at 13:25
  • @EOF so as not to belabor the point, I modified my answer to save `%eax` on the stack separately, although I do not believe it is necessary in this case. – lurker Aug 27 '15 at 13:39
  • Try compiling this: `extern void bar(int); int foo(volatile int x){++x; bar(x); return x;}` On my gcc 4.8.4, -m32, this modifies the argument on the stack. – EOF Aug 27 '15 at 13:57
  • @EOF that's a special case using a `volatile` keyword. It effectively modifies the "call by value" nature of the argument. `printf` isn't declared with `volatile`. I was looking for an example where a standard C lib function modified the argument. Wouldn't you be a bit surprised, for example, if you called, `printf("%d\n", x)` and find that your `x` was different after the call? But I do appreciate you highlighting cases where a modification could occur and the discussion is of benefit to other readers as well. – lurker Aug 27 '15 at 14:01
  • My point is that it is *permissible* for a function to modify its arguments on the stack. If a `volatile` argument may be modified, *any* argument may be modified. – EOF Aug 27 '15 at 14:02
  • Thank you that fixed it! – Noahnder Aug 27 '15 at 14:26
  • @EOF @Lurker IMHO - I think whether the passed arguments (using CDECL) are read only or not are more of an implementation (compiler/linker) detail that really isn't identified in that calling convention spec. I would always err on the side of caution by assuming that although the caller may have pushed call by value data on, the callee could have modified it (I don't see anything in the CDECL convention that says a callee can't modify arguments). So I would save (using push/pop, or xchg instructions) `EAX` with the assumption the copy on the stack could have (but maybe not) been modified. – Michael Petch Aug 27 '15 at 19:36
  • @MichaelPetch that's good input from the general CDECL perspective. I guess my point is that it would be quite surprising indeed if you called a standard lib function like `printf("%d", x)` and discover that it changed the value of `x`. – lurker Aug 27 '15 at 21:24
  • @lurker, technically speaking whether smashing the arguments is allowed or not by a compiler (in code generation), the compiler would still generate code that made that `printf` work properly. The rule would be simple for the _compiler_ developers - never assume that what you push on the stack before a call is retained after. The generated assembly could always avoid using values pushed on the stack (after a function call). The only way you'd end up in a mess is if you start modifying the assembly code directly. – Michael Petch Aug 27 '15 at 21:43
  • @MichaelPetch yes, I agree. Thanks again for your input, I appreciate it. – lurker Aug 27 '15 at 21:51