NASM - The Netwide Assembler

NASM Forum => Using NASM => Topic started by: alzamabar on February 21, 2010, 08:19:32 PM

Title: How to produce a x86-64 compatible object file with NASM
Post by: alzamabar on February 21, 2010, 08:19:32 PM
Hi, I just started studying Assembly on Jeff Duntemann's book. I run Ubuntu 9.10 64-bit. Following the book I'm executing this command:

Code: [Select]
nasm -f elf -g -F stabs eatsyscall.asm -l eatsyscall.lst

The above goes ok and it produces a eatsyscall.o file.

Now I run the linker:

Code: [Select]
ld -o eatsyscall eatsyscall.o

And I get the following exception:

Code: [Select]
ld: i386 architecture of input file `eatsyscall.o' is incompatible with i386:x86-64 output

What can I do to produce x86-64 compatible files?


Title: Re: How to produce a x86-64 compatible object file with NASM
Post by: lukus001 on February 21, 2010, 10:54:44 PM
I'm not too sure myself of the details, as I have only started ASM a couple of days ago but I get that same error since I am also on a 64bit linux OS.

Add -melf_i386 to ld to get it to link.  If you type -m on it's own it'll complain and tell you what other options you have
Title: Re: How to produce a x86-64 compatible object file with NASM
Post by: Keith Kanios on February 21, 2010, 11:20:25 PM
The particular file you are assembling is (seemingly) 32-bit in nature, as can be seen by using the -f elf (32-bit ELF) object format command. If it was meant to be a 64-bit example on Linux, Jeff would/should have used -f elf64.

ld will attempt to link to the default architecture of the target machine, in your case being 64-bit Ubuntu Linux. As lukus001 alluded to, you'll want to pass the -m32 argument to ld in order to produce a 32-bit binary, as demonstrated:

Code: [Select]
ld -m32 -o eatsyscall eatsyscall.o

That should get you on your way.

Let us know how Jeff Duntemann's book works for you.
Title: Re: How to produce a x86-64 compatible object file with NASM
Post by: Frank Kotler on February 21, 2010, 11:33:03 PM
Exactly! If you're using gcc, it's just "-m32", I believe.

They've improved ld. I'm told it used to just complain about illegal instructons ("push reg32", for example... or was that Gas). Now it appears to recognize what the problem is. Also, my ld just says "missing argument to -m", Luke says it tells you your options now. Next step will be to tell you what output formats your input *is* compatible with, and next, just do it...

Becoming quite a common problem... maybe should be documented somewhere. (where would you have seen it, Alzamabar? Nasm manual? Not really a "Nasm question", but...)

Actually, now I re-read the question, it asks for 64-bit compatible files. From 32-bit example code, you don't. The 64-bit world is quite different! The system call numbers are different, even "just call printf" is completely different. Learn, at least a little, 32-bit code first. 64-bit examples are fairly rare (still!), but here's something the late Chuck Crayne left for us to learn from:

Code: [Select]
global _start

section .data
    string1 db  "Hello World!",10,0

section .text

%ifidn __OUTPUT_FORMAT__, elf64

        ; calculate the length of string
        mov     rdi, string1
        mov     rcx, -1
        xor     al,al
        repnz scasb

        ; place the length of the string in RDX
        mov     rdx,  -2
        sub     rdx, rcx

        ; print the string using write() system call
        mov     rsi, string1
        push    0x1
        pop     rax
        mov     rdi,rax

        ; exit from the application here
        xor     rdi,rdi
        push    0x3c
        pop     rax

%elifidn __OUTPUT_FORMAT__, elf32

mov ecx, string1

        ; calculate the length of string
or edx, byte -1
cmp byte [ecx + edx + 1], 1
inc edx
jnc .getlen

        ; place the length of the string in EDX

        ; print the string using write() system call
push byte 1
pop ebx
push byte 4
pop eax
int 80h

        ; exit from the application here
xor ebx, ebx
push byte 1
pop eax
int 80h

%elifidn __OUTPUT_FORMAT__, macho

        ; calculate the length of string
or eax, byte -1
cmp byte [string1 + eax + 1], 1
inc eax
jnc getlen2

        ; print the string using write() system call
push eax
push string1
push 1
mov eax, 4 ; __NR_write
call sys_call

        ; exit from the application here
push 0
mov eax, 1
call sys_call

int 80h
%error "Output Format (-f) must be elf32, elf64, or macho!"

I've only been able to test the elf32 version of this. If you have trouble on some other platform, let us know and we'll try to improve it.


Title: Re: How to produce a x86-64 compatible object file with NASM
Post by: lukus001 on February 22, 2010, 12:32:54 AM
When I had the same problem as Alzamabar and started searching for an answer, I read a thread that said to pass the -m32 command but that never worked for me. 
lukus001@r31773:~/development/projects/test$ ld -m32 -o test test.o
ld: unrecognised emulation mode: 32
Supported emulations: elf_x86_64 elf_i386 i386linux
Title: Re: How to produce a x86-64 compatible object file with NASM
Post by: ishipaco on August 09, 2010, 06:17:40 PM
This is a topic that has occurred on quite a few sites. I had the same problem using Jeff's book. For several days (9 Aug 2010) I've been communicating with him about this and another problem. Here are a bit of background info and some explanations/solutions:

Jeff wrote the book using Ubuntu 8.10 on a 32-bit machine, back before 64-bit CPUs and operating systems were widespread. People with that setup shouldn't run into any problems. The problem arises when you try to assemble and link on a 64-bit machine. As others have noted, when using the commands Jeff wrote, the -f elf nasm command line option will produce an object module with 32-bit code. By default, the linker wants to see a system default 64-bit object module. It will fail to link and will produce the error message you mention. nasm and ld are very versatile utilities. If you would like to see all the different output formats that Nasm can produce, run the following command at your terminal prompt:

Code: [Select]
     nasm -hf
Meanwhile, there are two fixes I've found that work for people using 64-bit machines. First, to produce a native 64-bit object module and executable, simply change elf to elf64 in Jeff's command, like so:

Code: [Select]
     nasm -f elf64 -g -F stabs eatsyscall.asm -l eatsyscall.lst
      ld -o eatsyscall eatsyscall.o

Note, you don't want to change the ld command from Jeff's in the book. These two commands will assemble your source code into a 64-bit object module (eatsyscall.o) and then link that module to create an executable file (eatsyscall) meant to be run on a 64-bit machine. Admittedly, this is probably the simplest way to develop executable programs on a 64-bit machine and OS. A disadvantage is that your hard-crafted program (the executable file), being a 64-bit program, won't run on 32-bit machines like many of your friends have. If that's of concern to you, you should probably consider:

A second option, assembling and linking to produce a 32-bit executable, I prefer and recommend for several reasons. Trivially, 32-bit object and executable files are significantly smaller than their 64-bit equivalents. That's not a big issue now that terabyte hard drives are so cheap and common. More important, the executable files generated will run just fine on both 32- and 64-bit machines. To go this route (hint, hint), don't change Jeff's nasm command; -f elf is equivalent to -f elf32; i.e., his command already produces a 32-bit object module (which we've already learned). What needs changing in this case is the linker command. You need to tell ld you want a 32-bit executable instead of the default 64-bit format. You do that with the following two commands from your terminal:

Code: [Select]
nasm -f elf -g -F stabs eatsyscall.asm -l
ld -o eatsyscall eatsyscall.o -melf_i386

 :o Hope this helps in understanding this problem and providing a couple of "solutions."

BTW, the other, more serious, problem with which Jeff is currently dealing, is the fact that he was blindsided by the recent release of Ubuntu 10.4. For reasons not completely known, the Insight debugger, which Jeff uses extensively throughout his book, has been dropped from the Ubuntu 10.4 package. Further, an Insight package is not to be found on any of the standard repositories that Synaptic knows about. Insight packages from other sources don't seem to work, possibly a result of version conflicts in the Ubuntu 10.4 kernel, Insight, GDB, Kdbg, and/or poor installation packaging . . . at this point we don't yet know!

Bottom line: if you want the best experience working with Duntemann's book, try using an earlier version of Ubuntu (8.10 would be best, perhaps on a separate, bootable partition?), which came packaged with Insight. If you can do that, and understand and heed the above info about 32/64-bit machines and OSs, Jeff's instructions should work fine. I suspect this has been a valuable learning experience for many of us. :D
Title: Re: How to produce a x86-64 compatible object file with NASM
Post by: Keith Kanios on August 09, 2010, 06:55:37 PM
The code is 32-bit, so nasm -f elf... is correct. The trick is in supplying ld the right parameters, in which is dependent on the build/system involved.

In lukus001's case, ld -m32 simply needed to change to a suggested "emulation" option, most favorably ld -melf_i386.

You also may run into issues with implicit linking against 64-bit libraries, so you can (should be able to) explicitly supply paths to the 32-bit libraries in such cases... assuming they are installed.
Title: Re: How to produce a x86-64 compatible object file with NASM
Post by: Keith Kanios on August 09, 2010, 09:53:03 PM
You can also try supplying the NASM generated 32-bit ELF object along with the -m32 switch to gcc, and see if it correctly invokes ld in proxy.