views:

817

answers:

2

Hello,

I'm in an interesting problem.I forgot I'm using 64bit machine & OS and wrote a 32 bit assembly code. I don't know how to write 64 bit code.

This is the x86 32-bit assembly code for Gnu Assembler (AT&T syntax) on Linux.

//hello.S
#include <asm/unistd.h>
#include <syscall.h>
#define STDOUT 1

.data
hellostr:
    .ascii "hello wolrd\n";
helloend:

.text
.globl _start

_start:
    movl $(SYS_write) , %eax  //ssize_t write(int fd, const void *buf, size_t count);
    movl $(STDOUT) , %ebx
    movl $hellostr , %ecx
    movl $(helloend-hellostr) , %edx
    int $0x80

    movl $(SYS_exit), %eax //void _exit(int status);
    xorl %ebx, %ebx
    int $0x80

    ret

Now, This code should run fine on a 32bit processor & 32 bit OS right? As we know 64 bit processors are backward compatible with 32 bit processors. So, that also wouldn't be a problem. The problem arises because of differences in system calls & call mechanism in 64-bit OS & 32-bit OS. I don't know why but they changed the system call numbers between 32-bit linux & 64-bit linux.

asm/unistd_32.h defines:

#define __NR_write        4
#define __NR_exit         1

asm/unistd_64.h defines:

#define __NR_write              1
#define __NR_exit               60

Anyway using Macros instead of direct numbers is paid off. Its ensuring correct system call numbers.

when I assemble & link & run the program.

$cpp hello.S hello.s //pre-processor
$as hello.s -o hello.o //assemble
$ld hello.o // linker : converting relocatable to executable

Its not printing helloworld.

In gdb its showing:

  • Program exited with code 01.

I don't know how to debug in gdb. using tutorial I tried to debug it and execute instruction by instruction checking registers at each step. its always showing me "program exited with 01". It would be great if some on could show me how to debug this.

(gdb) break _start
Note: breakpoint -10 also set at pc 0x4000b0.
Breakpoint 8 at 0x4000b0
(gdb) start
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Temporary breakpoint 9 (main) pending.
Starting program: /home/claws/helloworld 

Program exited with code 01.
(gdb) info breakpoints 
Num     Type           Disp Enb Address            What
8       breakpoint     keep y   0x00000000004000b0 <_start>
9       breakpoint     del  y   <PENDING>          main

I tried running strace. This is its output:

execve("./helloworld", ["./helloworld"], [/* 39 vars */]) = 0
write(0, NULL, 12 <unfinished ... exit status 1>
  1. Explain the parameters of write(0, NULL, 12) system call in the output of strace?
  2. What exactly is happening? I want to know the reason why exactly its exiting with exitstatus=1?
  3. Can some one please show me how to debug this program using gdb?
  4. Why did they change the system call numbers?
  5. Kindly change this program appropriately so that it can run correctly on this machine.

EDIT:

After reading Paul R's answer. I checked my files

claws@claws-desktop:~$ file ./hello.o 
./hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

claws@claws-desktop:~$ file ./hello
./hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

I agree with him that these should be ELF 32-bit relocatable & executable. But that doesn't answer my my questions. All of my questions still questions. What exactly is happening in this case? Can someone please answer my questions and provide an x86-64 version of this code?

+4  A: 

Remember that everything by default on a 64-bit OS tends to assume 64-bit. You need to make sure that you are (a) using the 32-bit versions of your #includes where appropriate (b) linking with 32-bit libraries and (c) building a 32-bit executable. It would probably help if you showed the contents of your makefile if you have one, or else the commands that you are using to build this example.

FWIW I changed your code slightly (_start -> main):

#include <asm/unistd.h>
#include <syscall.h>
#define STDOUT 1

    .data
hellostr:
    .ascii "hello wolrd\n" ;
helloend:

    .text
    .globl main

main:
    movl $(SYS_write) , %eax  //ssize_t write(int fd, const void *buf, size_t count);
    movl $(STDOUT) , %ebx
    movl $hellostr , %ecx
    movl $(helloend-hellostr) , %edx
    int $0x80

    movl $(SYS_exit), %eax //void _exit(int status);
    xorl %ebx, %ebx
    int $0x80

    ret

and built it like this:

$ gcc -Wall test.S -m32 -o test

verfied that we have a 32-bit executable:

$ file test
test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.4, dynamically linked (uses shared libs), not stripped

and it appears to run OK:

$ ./test
hello wolrd
Paul R
`_start` or `main` what difference would that make?
claws
@claws: I only made that change so that the code would build and link easily with gcc, but I guess it also means that the C runtime library startup code will run before main is called.
Paul R
I've edited my question. Also, when I try to build your code using `gcc -Wall test.S -m32 -o test` It gives this error (underscores are used as separators): /usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.4.1/libgcc.a when searching for -lgcc ______________ /usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-linux-gnu/4.4.1/libgcc.a when searching for -lgcc ______________ /usr/bin/ld: cannot find -lgcc ______________ collect2: ld returned 1 exit status
claws
@claws: looks like you don't have 32-bit gcc or libs installed on your system. You need to install the 32-bit development packages for your system to use -m32
Chris Dodd
@claws: a useful technique for this and other related issues with writing assembly is to first with a simple C example and then compile it with gcc -S to generate assembly source - this then shows you how to handle the ABI, system calls, etc via actual working code. Much easier than working everything out from documentation and first principles etc.
Paul R
+2  A: 

As noted by Paul, if you want to build 32-bit binaries on a 64-bit system, you need to use the -m32 flag, which may not be available by default on your installation (some 64-bit Linux distros don't include 32-bit compiler/linker/lib support by default).

On the other hand, you could instead build your code as 64-bit, in which case you need to use the 64-bit calling conventions. In that case, the system call number goes in %rax, and the arguments go in %rdi, %rsi, and %rdx

Edit

Best place I've found for this is www.x86-64.org, specifically abi.pdf

Chris Dodd
Thank you for mentioning 64-bit convention. I'm desperately looking for that. I want to know more about 64 bit convention. Can you please give me some link. (Official would be better).
claws