6

I am learning AT&T x86 assembly language. I am trying to write an assembly program which takes an integer n, and then return the result (n/2+n/3+n/4). Here is what I have done:

.text .global _start _start: pushl $24 call profit movl %eax, %ebx movl $1, %eax int $0x80 profit: popl %ebx popl %eax mov $0, %esi movl $4, %ebp div %ebp addl %eax, %esi movl %ecx, %eax movl $3, %ebp div %ebp addl %eax, %esi movl %ecx, %eax movl $2, %ebp div %ebp addl %eax, %esi movl %esi, %eax cmpl %ecx, %esi jg end pushl %ebx ret end: mov %ecx, %eax ret 

The problem is I am getting segmentation fault. Where is the problem?

4
  • 8
    Did you try stepping through the code with a debugger to see what is going on ? Commented Sep 26, 2012 at 15:07
  • 1
    AT&T branched into processors? (-: Commented Sep 26, 2012 at 15:22
  • tripleee: "AT&T syntax" refers to the assembler syntax above, originally used in SysV Unix. It puts the operand size onto the instruction as a suffix, uses a "%" to tag register names, and puts the destination register at the end of the list. It's the syntax supported by the GNU assembler on linux. It's quite different from "Intel" syntax used more commonly in the PC industry. Commented Sep 26, 2012 at 15:30
  • 6
    If you're going to learn Intel assembly language, use Intel syntax. In a decent world, that abomination you're calling AT&T syntax would never have existed. Commented Sep 26, 2012 at 15:52

5 Answers 5

7

I think the code fails here:

_start: pushl $24 call profit movl %eax, %ebx movl $1, %eax int $0x80 profit: popl %ebx popl %eax 

So, you push $24 (4 bytes) and then call profit, which pushes eip and jumps to profit. Then you pop the value of eip into ebx and the value $24 into eax.

Then, in the end, if jg end branches to end:, then the stack won't hold a valid return address and ret will fail. You probably need pushl %ebx there too.

 cmpl %ecx, %esi jg end pushl %ebx ret end: mov %ecx, %eax ; `pushl %ebx` is needed here! ret 
Sign up to request clarification or add additional context in comments.

2 Comments

Good level of detail in explanation. nice.
The normal and more efficient solution is to leave the return address on the stack and access the first arg with mov 4(%esp), %eax. (Or whatever offset it's at if you push something else first). If you make a traditional stack frame with EBP, you'd be able to access the first arg as 8(%ebp) regardless of other pushes.
2
  1. you use ecx without ever explicitly initializing it (I'm not sure if Linux will guarantee the state of ecx when the process starts - looks like it's 0 in practice if not by rule)
  2. when the program takes the jg end jump near the end of the procedure, the return address is no longer on the stack, so ret will transfer control to some garbage address.

Comments

2

Your problem is that you pop the return address off of the stack and when you branch to end you don't restore it. A quick fix is to add push %ebx there as well.

What you should do is modify your procedure so it uses the calling convention correctly. In Linux, the caller function is expected to clean the arguments from the stack, so your procedure should leave them where they are.

Instead of doing this to get the argument and then restoring the return address later

popl %ebx popl %eax 

You should do this and leave the return address and arguments where they are

movl 4(%esp), %eax 

and get rid of the code that pushes the return address back onto the stack. You then should add

subl $4, %esp 

after the call to the procedure to remove the argument from the stack. It's important to follow this convention correctly if you want to be able to call your assembly procedures from other languages.

Comments

1

It looks to me like you have a single pushl before you call profit and then the first thing that profit does is to do two popl instructions. I would expect that this would pop the value you pushed onto the stack as well as the return code so that your ret would not work.

push and pop should be the same number of times.

call pushes the return address onto the stack.

Comments

1

You do not appear to be doing function calls correctly. You need to read and understand the x86 ABI (32-bit, 64-bit) particularly the "calling convention" sections.

Also, this is not your immediate problem, but: Don't write _start, write main as if this were a C program. When you start doing something more complicated, you will want the C library to be available, and that means you have to let it initialize itself. Relatedly, do not make your own system calls; call the wrappers in the C library. That insulates you from low-level changes in the kernel interface, ensures that errno is available, and so on.

1 Comment

The question is about segmentation fault in assembly language, not about using inline assebly in C nor how to use wrappers for system calls. _start is perfectly valid here this being a pure assembly program.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.