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.
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
Related
So I have a task to do, which requires from me to scanf a char* in assembly. I tried this code:
.data
INPUT_STRING: .string "Give me a string: "
SCANF_STRING: .string "%s"
PRINTF_STRING: .string "String: %s\n"
.text
.globl main
.type main, #function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $32, %esp
pushl $INPUT_STRING
call printf #printf("Give me a string: ")
addl $4, %esp
pushl -12(%ebp) # char*
pushl $SCANF_STRING # "%s"
call scanf scanf("%s", char*)
addl $8, %esp
pushl -12(%ebp)
pushl PRINTF_STRING
call printf #printf("String: %s\n")
addl $16, %esp
movl -4(%ebp), %ecx
xorl %eax, %eax
leave
leal -4(%ecx), %esp
ret
It writes down first printf correctly, then it waits for input (so scanf works), but then when I enter anything -> Segmentation fault.
I know, that the char* should be somehow initialized, but how can I do it from the assembly level?
I am compiling it on Manjaro 64 bit, with gcc -m32
GCC's stack-alignment code on entry to main is over-complicated:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $32, %esp
...
leave
leal -4(%ecx), %esp
ret
Do it so:
pushl %ebp
movl %esp, %ebp
subl $32, %esp # Space for 32 local bytes
andl $-16, %esp # Alignment by 16
...
leave
ret
The version of the i386 System V ABI used on modern Linux does guarantee/require 16-byte stack alignment before a call, so you could have re-aligned with 3 pushes (including the push %ebp) instead of an and. Unlike x86-64, most i386 library functions don't get compiled to use movaps or movdqa 16-byte aligned load/store on locals in their stack space, so you can often get away with unaligning the stack like you're doing with PUSHes before scanf. (ESP % 16 == 0 when you call printf the first time, though; that's correct.)
You want to use 12 bytes of the local stack frame for the string. scanf needs the start address of those 12 bytes. The address for that area isn't known at compile time. A -12(%ebp) gives you the value at this address, not the address itself. LEA is the instruction to calculate an address. So you have to insert this instruction to get the address at run time and to pass it to the C function:
leal -12(%ebp), %eax
pushl %eax # char*
And this is the working example (minor mistakes also corrected):
.data
INPUT_STRING: .string "Give me a string: "
SCANF_STRING: .string "%11s" ##### Accept only 11 characters (-1 because terminating null)
PRINTF_STRING: .string "String: %s\n"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
subl $32, %esp
mov $32, %ecx
mov %esp, %edi
mov $88, %al
rep stosb
pushl $INPUT_STRING
call printf # printf("Give me a string: ")
addl $4, %esp
leal -12(%ebp), %eax
pushl %eax # char*
pushl $SCANF_STRING # "%s"
call scanf # scanf("%s", char*)
addl $8, %esp
leal -12(%ebp), %eax
pushl %eax # char*
pushl $PRINTF_STRING ##### '$' was missing
call printf # printf("String: %s\n")
addl $8, %esp ##### 16 was wrong. Only 2 DWORD à 4 bytes were pushed
leave
ret
I have assembly language progamme,but it show error
“Program received signal SIGSEGV,Segentation fault”
Why?the program is below:
.code32
.section .data
.section .text
.globl _start
_start:
pushl $3
pushl $2
call power
addl $8, %esp
pushl %eax
pushl $2
pushl $5
call power
addl $8, %esp
popl %ebx
addl %eax, %ebx
movl $1, %eax
int $0x80
.type power,#function
power:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %ebx
movl 12(%ebp), %ecx
movl %ebx, -4(%ebp)
power_loop_start:
cmpl $1, %ecx
je power_loop_end
movl -4(%ebp), %eax
imull %ebx, %eax
movl %eax, -4(%ebp)
decl %ecx
jmp power_loop_start
power_loop_end:
movl -4(%ebp), %eax
movl %ebp, %esp
popl %ebp
ret
After compilation to run the program,it show segmentation fault (core dump) and gdb says:
Program received signal SIGSEGV,Segregation fault.
Why and how I fix it?
My problem is that I use 64 to compile 32 bit program.And I add some compiler
options to solve it.Such as bellow:
as -32 power.s -o power.o
ld -m elf_i386 power.o -o power
./power
I get the error 'Segmentation fault (core dumped)' when trying to run some compiled assembly. The code that makes the program crash is the following:
.global tiny
.type tiny, #function
tiny:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
# Compiling A1MINUS
movl $3, %eax
negl %eax
movl %eax, -4(%ebp)
# Compiling A0
movl -4(%ebp), %eax
movl %eax, -8(%ebp)
# Compiling A0
movl $7, %eax
movl %eax, -12(%ebp)
movl -8(%ebp), %eax
cmp %eax, -12(%ebp) // Error
movl $1, %eax // Error
movl %eax, %ebx //Error
movl $0, %eax //Error
cmovl %eax, %ebx // Error
movl %eax, -16(%ebp) // Error
# IF
movl -16(%ebp), %eax
cmp %eax , 0
jz lbl_3
movl -8(%ebp), %eax
pushl %eax
call print_int
addl $4, %esp
jmp lbl_4
lbl_3:
movl -12(%ebp), %eax
pushl %eax
call print_int
addl $4, %esp
lbl_4:
movl %ebp, %esp
popl %ebp
ret
I have no clue what my mistake is, any help is appreciated.
The wrong code (cmovl) is supposed to place a 0 or 1 in -16(%ebp), depending on the result of the comparison done by cmp. (the broken code is a translation of a 'lower then' statement).
As said in the comments, cmovl appearently can't make the code crash. Now I am even more clueless.
I am pretty sure the line cmp %eax, 0 causes the crash, because in at&t syntax the 0 there is a memory reference, a NULL pointer. Presumably you wanted to compare eax to 0, in which case you need cmp $0, %eax.
Learn to use a debugger.
The goal of my code below to mimmic the cat program. It takes an input, and then spits it back out.
Such as:
$ Hi how are you
$ Hi how are you
$ good
$ good
What is happening though is:
$ Hi how are you
$ Hi how are you
$ good
$ good
$ow are you
My string is not being removed from my buffer and so if the input is shorter the second time, it will spit out the extra characters that have not been written over. I was wondering if anyone knew how to clear the buffer so this would not occur. Thanks
.file "rand.c"
.section .data
.buffer:
.space 10
.len:
.space 1
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
loop:
movl $3, %eax
movl $1, %ebx
movl $.buffer, %ecx
movl $100, %edx
int $0x80
movl $4, %eax
movl $1, %ebx
movl $.buffer, %ecx
movl $100, %edx
int $0x80
jmp loop
movl $1, %eax
movl $0, %ebx
int $0x80
Reading through the "Professional Assembly Language Book"; it seems that it provides an erroneous code for reading command-line arguments. I corrected it a bit and now it went from segfaulting to reading argument count then segfaulting.
Here's the full code:
.data
output1:
.asciz "There are %d params:\n"
output2:
.asciz "%s\n"
.text
.globl main
main:
movl 4(%esp), %ecx /* Get argument count. */
pushl %ecx
pushl $output1
call printf
addl $4, %esp /* remove output1 */
/* ECX was corrupted by the printf call,
pop it off the stack so that we get it's original
value. */
popl %ecx
/* We don't want to corrupt the stack pointer
as we move ebp to point to the next command-line
argument. */
movl %esp, %ebp
/* Remove argument count from EBP. */
addl $4, %ebp
pr_arg:
pushl (%ebp)
pushl $output2
call printf
addl $8, %esp /* remove output2 and current argument. */
addl $4, %ebp /* Jump to next argument. */
loop pr_arg
/* Done. */
pushl $0
call exit
Code from the book:
.section .data
output1:
.asciz “There are %d parameters:\n”
output2:
.asciz “%s\n”
.section .text
.globl _start
_start:
movl (%esp), %ecx
pushl %ecx
pushl $output1
call printf
addl $4, %esp
popl %ecx
movl %esp, %ebp
addl $4, %ebp
loop1:
pushl %ecx
pushl (%ebp)
pushl $output2
call printf
addl $8, %esp
popl %ecx
addl $4, %ebp
loop loop1
pushl $0
call exit
Compiled it with GCC (gcc cmd.S), maybe that's the problem? __libc_start_main modifies the stack in some way? Not quite sure...
Even worse, trying to debug it to look at the stack but GDB seems to throw a lot of printf-related stuff (one of them was printf.c: File not found or something similar).
With the help from #Michael, I was able to track down the problem.
Using %ebp as argv as #Michael suggested (he used %eax though). Another problem was that I needed to compare the value of (%ebp) with 0 (the null terminator) and end the program at that point.
Code:
movl 8(%esp), %ebp /* Get argv. */
pr_arg:
cmpl $0, (%ebp)
je endit
pushl %ecx
pushl (%ebp)
pushl $output2
call printf
addl $8, %esp /* remove output2 and current argument. */
addl $4, %ebp
popl %ecx
loop pr_arg
ret