Getting command line parameters from an assembly program - linux

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

Related

Trying to assemble an IA32 assembly file and getting operand mismatch error for 'call'

Im trying to assemble an IA32 assembly file which reads the users input. When I try to run it with as -o input.o input.s I get an error saying "operand type mismatch for 'call'
Here is the code;
.code32
.section .rodata
output: .string "You entered %s\n"
inout: .string "%s"
.section .text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
subl $100, %esp
pushl $input
call scanf, %eax
add $8, %esp
pushl $output
call printf
xorl %eax, %eax
movl %ebp, %esp
popl %ebp
ret
This code has many errors. The particular one you asked about is because call only takes a single operand, the function (address) to call. It's unclear what you wanted to do with call scanf, %eax especially since you have not set eax to anything. scanf does take two arguments but even though you allocate a buffer on the stack you don't pass its address. printf as you use it also needs two arguments but you only pass the format string. Also you have a typo inout vs input. Furthermore you can not ret if you use _start as entry point, you need an exit system call. However if you intend to use C functions it's recommended to use main as entry point in which case you can keep the ret. A fixed version may look like:
.section .rodata
output: .string "You entered %s\n"
input: .string "%s"
.section .text
.globl main
main:
pushl %ebp
movl %esp, %ebp
subl $100, %esp
push %esp
pushl $input
call scanf
add $8, %esp
push %esp
pushl $output
call printf
xorl %eax, %eax
movl %ebp, %esp
popl %ebp
ret
Assemble and link using gcc -m32 input.s.

Scanf a char pointer in Assembly

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

Factorial Assembly x86

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

Trying to store two numbers using scanf , but program crashes

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

creating a substring on Linux IA-32 assembler (gas)

I wanna create a substring (ministring) of 3 asciz chars out of my original (thestring). The thing ain't printing when being run so I don't know what the hell I'm I doing. Why it ain't printing? Am I creating the ministring correctly?
.section .data
thestring: .asciz "111010101"
ministring: .asciz ""
formatd: .asciz "%d"
formats: .asciz "%s"
formatc: .asciz "%c"
.section .text
.globl _start
_start:
xorl %ecx, %ecx
ciclo:movb thestring(%ecx,1), %al
movzbl %al, %eax
movl %eax, ministring(%ecx,1)
incl %ecx
cmpl $3, %ecx
jl ciclo
movl thestring, %eax
pushl %eax
pushl $formats
call printf
addl $4, %esp
movl $1, %eax
movl $0, %ebx
int $0x80
You haven't reserved enough memory space to contain the null-terminated ministring which you're creating ... therefore, when you write to this memory, you're overwriting the value of formatd and formats (and so you're eventually passing something other than "%s" to printf).
Instead of your definition of the ministring memory location, try using the following :
ministring: .asciz " "
Also, instead of this:
movl %eax, ministring(%ecx,1)
I don't understand why you aren't using this instead:
movb %al, ministring(%ecx,1)
Also, if you want to print the ministring, then instead of this:
movl thestring, %eax
Do this:
movl ministring, %eax
Also instead of this:
addl $4, %esp
Why not this:
addl $8, %esp
ALso I suggest that you use a debugger to:
Step through the code
Watch the values contained in registers and in memory as you step through
Know the location of any segmentation fault

Resources