2

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).

  • 1
    `movl %esp, %ebp` / `addl $4, %ebp` Aren't you pointing `ebp` to `argc` here? Seems to me like you should add 8 if you wanted `argv`. – Michael Aug 26 '13 at 16:15
  • Well, `4(%esp)` and `4(%ebp)` point to argc, so i am removing argc and therefore `(%ebp)` should point to argv[0] right? –  Aug 26 '13 at 16:17
  • 1
    To me it looks like when you reach the `/* Remove argument count from EBP. */`-point, your `esp` will be pointing at the same place it was when you entered `main`, and so `esp+4` points to `argc`. Therefore to make `ebp` point to `argv` I would've guessed that you should add 8 rather than 4. – Michael Aug 26 '13 at 16:27
  • You're right. I did what you said but, here's the output: http://codepad.org/h2VnzD2J –  Aug 26 '13 at 16:29
  • I may be remembering incorrectly, but it looks like you're missing one level of indirection when accessing `argv`. Your code seems to assume that `esp+8` is `argv[0]`, `esp+12` is `argv[1]`, etc. As far as I can recall it should be something like `mov eax,[esp+8] ; get argv` / `mov ebx,[eax] ; get argv[0]` / `mov ecx,[eax+4] ; get argv[1]`, etc. – Michael Aug 26 '13 at 16:34
  • This looks like the same thing I'm doing (your code) or did I misunderstood it? Since the instruction `pushl (%ebp)` should push `argv[0]` on the stack and _should_ be the same as `movl 8(%ebp), %eax` then `movl (%eax), %ebx`? I'm not sure if [] is the same () in AT&T –  Aug 26 '13 at 16:39
  • `esp+8` is a pointer to `argv`; that is, a pointer to a pointer to char pointers. To get `argv` you'd have to read `[esp+8]` (`8(%esp)` in AT&T syntax). And then you dereference _that_ value to get the pointer to the first argument string. To me it doesn't look like that's what your code is doing. – Michael Aug 26 '13 at 17:16
  • Okay, your solution seems to work (what you remember "incorrectly") but using `addl $4, %eax` to step to next argument doesn't work[?] –  Aug 26 '13 at 17:28
  • related http://stackoverflow.com/questions/3683144/linux-64-command-line-parameters-in-assembly – Ciro Santilli OurBigBook.com Oct 28 '15 at 21:15

1 Answers1

2

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
  • 1
    `addl $12, %esp` to remove two arguments doesn't look right to me! Doesn't matter, since you're exiting with `exit()`, but if you tried to `ret` from `main:` it would matter. – Frank Kotler Aug 26 '13 at 18:19