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?
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
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) 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. 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.
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.
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.