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
Related
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
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 wrote the code that reads from stdin and writes to the stdout:
#include <stdio.h>
#include <unistd.h>
int main() /* copy input to output */
{
char buf[BUFSIZ];
int n;
while ((n = read(0, buf, BUFSIZ)) > 0)
write(1, buf, n);
return 0;
}
After I converted into the assembly code (a .s file) in 32-bit AT&T syntax:
.text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp #16 bit alignment
subl $8224, %esp #space for local variables
jmp _READ
_WRITE:
movl 8220(%esp), %eax
movl %eax, 8(%esp)
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $1, (%esp)
call write
int $0x80
_READ:
movl $8192, 8(%esp) #buffer length
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $0, (%esp)
call read
movl %eax, 8220(%esp)
cmpl $0, 8220(%esp)
jg _WRITE
movl $0, %eax
leave
ret
It works fine, but I'm not sure how to making the "read" and "write" system calls using plain assembly(i.e. moving numbers into certain registers and use "int 0x80" to execute the system calls).
My goal is to make it work even if it is compiled with the "-nostdlib" option.
Hint: 32-bit x86 is old, slow, weird and deprecated. You should use amd64 instead.
The list of system calls for Linux i386 is available in Linux source code:
https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_32.tbl
Or in glibc headers in asm/unistd_32.h. You can and should #include <asm/unistd.h> so you can use $__NR_write instead of $4 to make your asm source code self-documenting.
The system call number goes in eax. Parameter sequence is always ebx, ecx, edx, esi, edi, ebp. So code becomes:
.text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp #16 bit alignment
subl $8224, %esp #space for local variables
jmp _READ
_WRITE:
movl 8220(%esp), %edx
leal 28(%esp), %ecx
movl $1, %ebx
movl $4, %eax
int $0x80
_READ:
movl $8192, %edx #buffer length
leal 28(%esp), %ecx
movl $0, %ebx
movl $3, %eax
int $0x80
movl %eax, 8220(%esp)
cmpl $0, 8220(%esp)
jg _WRITE
movl $1, %eax
movl $0, %ebx
int $0x80
Assemble and link with:
$ as --32 hel.s -o hel.o
$ ld -melf_i386 hel.o -o hel
http://www.linuxjournal.com/article/4048
See also
What are the calling conventions for UNIX & Linux system calls on i386 and x86-64
https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/
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
Given the following code :
.section .rodata
input_format1: .string "%d%d"
output_format1: .string "Yes. %d is a power of %d\n"
output_format2: .string "No. %d is not a power of %d\n"
.section .text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
addl $-8 ,%esp # moving down the stack
pushl %esp
pushl 4(%esp)
pushl $input_format1
call scanf # call scanf to get a number from the user
addl $12,%esp
movl (%esp),%ebx # store the actual number
movl 4(%esp),%ecx
.loop:
#return from printf:
movl %ebp,%esp
popl %ebp
ret
After the program reaches scanf and I press the 1st number ,it crashes.What am I doing wrong ?
Thanks
Ron
The problem is in:
pushl 4(%esp)
That pushes the value in the stack, not the address of such variable.
Compare that with the previous, correct, instruction pushl %esp. This one does push an address.
What you need is pushl %esp+4, but that cannot be done in just one instruction, AFAIK. Instead do something like:
lea 4(%esp), %eax
push %eax
UPDATE:
Your other problem is because each time you do a push %esp gets decremented, so calculating the right address for your local variables is messy. That's one reason to have a stack frame, and you have one! So use %ebp to refer to your local variables, but with a negative offset:
pushl %ebp
movl %esp, %ebp
addl $-8 ,%esp # moving down the stack
lea -4(%ebp), %eax
pushl %eax
lea -8(%ebp), %eax
pushl %eax
pushl $input_format1
call scanf # call scanf to get a number from the user
addl $12,%esp
movl -8(%ebp),%ebx # store the actual number
movl -4(%ebp),%ecx