I see what you did there, cool.

Speaking of calling programs in terminal, I tried to get arguments to a program. I read online that the first element in stack is the number of arguments (argc) and after it there are argc elements.

I tried the following to print out the first argument, but it failed:


Code:
section .text
  global _start

_start:

  pop rax               ; Pop rax (argc)
  pop rax               ; Pop rax (first argument)
  mov ecx, eax          ; Move first argument to ecx, to be printed
  mov eax, 4            ; sys_write
  mov edx, 1            ; File descriptor: 1
  int 80h               ; Display first argument

  mov eax, 1            ; The system call for exit (sys_exit)
  mov ebx, 0            ; Exit with return code of 0 (no error)
  int 80h               ; Exit program


The comments should help you understand it. I would like to know if any of you knows why it doesn't work, I called it "./arguments hello".
Fundamentally wrong assumption there. You're not using the C runtime, so you don't get argc and argv (unless I'm mistaken)- you'll need to ask the system for them, and I don't know offhand how you'd do that for Linux.
According to this, the arguments are located on the stack with the element that gets popped first containing the number of arguments. Then the rest of the arguments are located on the stack as well, just pop them off accordingly.
Looks to me like your arguments to write() are weird. Should be write(int fd, const void *buf, size_t count), but the signature I'm inferring from your code is more like write(GARBAGE, const void *buf, int fd).

Popping rax (a 64-bit value) when you're trying to get 32-bit values back out is probably also not going to work at all. Since it seems everything you're getting in is 32 bits wide, you're getting garbage by popping 64-bit-wide values. If my quick mental work-through is correct, you're getting argc and argv[0] in rax after the first pop, then argv[1] and argv[2] in rax following the second pop. I think that given the endianness (x86 is little-endian), you actually have argv[1] in the high 32 bits of rax.

I think I noted this earlier- you seem to be mixing 32 and 64-bit ABIs, and I'm amazed it works. You're assembling 64-bit (hence the assembler not letting you use 32-bit registers with push/pop) but using the system's 32-bit ABI, which might break horribly when you start throwing around pointers, depending on where they actually are in memory. I wrote a quick program in C that's similar to yours and compiled it on a 64-bit system as a 64-bit binary, and the results are rather different (especially for syscalls):

Code:
#include <unistd.h>

int main(int argc, char **argv) {
    write(1, argv[1], 5);
}

Disassemblies, my own comments within:

Code:
(gdb) disassemble _start
Dump of assembler code for function _start:
# Housekeeping
   0x0000000000400400 <+0>:     xor    %ebp,%ebp
   0x0000000000400402 <+2>:     mov    %rdx,%r9
   0x0000000000400405 <+5>:     pop    %rsi
   0x0000000000400406 <+6>:     mov    %rsp,%rdx
   0x0000000000400409 <+9>:     and    $0xfffffffffffffff0,%rsp
# argc
   0x000000000040040d <+13>:    push   %rax
# argv
   0x000000000040040e <+14>:    push   %rsp
# More housekeeping?
   0x000000000040040f <+15>:    mov    $0x4005b0,%r8
   0x0000000000400416 <+22>:    mov    $0x400520,%rcx
   0x000000000040041d <+29>:    mov    $0x4004e4,%rdi
# More setup and eventually call main()
   0x0000000000400424 <+36>:    callq  0x4003e0 <__libc_start_main@plt>
# Terminate
   0x0000000000400429 <+41>:    hlt
   0x000000000040042a <+42>:    nop
   0x000000000040042b <+43>:    nop
End of assembler dump.
(gdb) disassemble main
Dump of assembler code for function main:
# Function prelude
   0x00000000004004e4 <+0>:     push   %rbp
   0x00000000004004e5 <+1>:     mov    %rsp,%rbp
   0x00000000004004e8 <+4>:     sub    $0x10,%rsp
# Smidge more setup
   0x00000000004004ec <+8>:     mov    %edi,-0x4(%rbp)
   0x00000000004004ef <+11>:    mov    %rsi,-0x10(%rbp)
# argv
   0x00000000004004f3 <+15>:    mov    -0x10(%rbp),%rax
# rax = argv[1]
   0x00000000004004f7 <+19>:    add    $0x8,%rax
   0x00000000004004fb <+23>:    mov    (%rax),%rax
# Arguments to write()
   0x00000000004004fe <+26>:    mov    $0x5,%edx
   0x0000000000400503 <+31>:    mov    %rax,%rsi
   0x0000000000400506 <+34>:    mov    $0x1,%edi
# C library call
   0x000000000040050b <+39>:    callq  0x4003f0 <write@plt>
# Leave function
   0x0000000000400510 <+44>:    leaveq
   0x0000000000400511 <+45>:    retq
(gdb) disassemble write
# Magic address determined at link-time, looks like
   0x00007ffff7b44b30 <+0>:     cmpl   $0x0,0x298d71(%rip)        # 0x7ffff7ddd8a8
   0x00007ffff7b44b37 <+7>:     jne    0x7ffff7b44b49 <write+25>
# Syscall 1 (not int 80h)
   0x00007ffff7b44b39 <+9>:     mov    $0x1,%eax
   0x00007ffff7b44b3e <+14>:    syscall
   0x00007ffff7b44b40 <+16>:    cmp    $0xfffffffffffff001,%rax
   0x00007ffff7b44b46 <+22>:    jae    0x7ffff7b44b79 <write+73>
   0x00007ffff7b44b48 <+24>:    retq
# snipped the rest since it's mostly error-handling

You'll note that it's throwing around 64-bit pointers and using 32-bit register specifiers where possible to save on code space. In most cases, constants get sign-extended out to 64 bits when you use the 32-bit register parts, but you can't make any assumptions about what your pointers will be doing.
Hmmmmmm Thanks a lot Tari and Souvik. I know fixed the way I write the arguments. However, I have a bigger problem.

I need to get the length of a string located in a register.


Code:
section .bss
  inputText:  resb 128
  inputTextSize: resb 4

section .text
  global _start

_start:
  ;Handle argc and argv
  pop   rax      ;Get the number of arguments
  pop   rbx      ;Get the program name
  pop   rcx      ;Get the first actual argument

  ;Display first actual argument located in rcx
  mov rax, 4
  mov rbx, 1
  ;mov edx, size of rcx
  int 80h

  ;Close program
  mov   rax, 1
  mov   rbx, 0
  int   80h


I save the first actual argument to rcx, which is the 64-bit register of "ecx", where I save the string to be display by write().

However, I still need to save the length of the string in edx.

Also, I'm using 64-bit registers in the above code.
ephan wrote:
However, I still need to save the length of the string in edx.

Since nobody's replied for over a week now, I'll bite. Getting the length of a string is pretty easy. As someone probably already mentioned in this thread, C strings are nul-terminated. This means you just have to count characters until you hit a zero character (ASCII NUL, not "0").

I don't have actual x86/x86-64 code to do this, but here's a C implementation:

Code:
size_t strlen(const char *s)
{
    const char *start = s;
    while (*s != '\0')
        ++s;
    return s - start;
}

I'll leave translating that to assembly language as an exercise for you.
And indeed it's a trivial four- or five-line implementation in x86, very clean indeed. I wouldn't even be surprised if deep within the x86 there's some kind of loop-and-compare-until instruction.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 5 of 5
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement