2
votes

I am new to assembly language programming. I am trying to follow the steps outlined here to get a better understanding of assembly and optimization. My operating system is Linux Mint, and I am trying to use the NASM assembler, albeit unsuccessfully.

As in the walkthrough, the code is:

BITS 32
GLOBAL main
SECTION .text
main:
    mov eax, 42
    ret

It compiles successfully with nasm using the command: nasm -f elf tiny.asm

But if I try to use gcc to link with the command: gcc -Wall -s tiny.o

I get the following error: /usr/bin/ld: i386 architecture of input file `tiny.o' is incompatible with i386:x86-64 output

A quick search told me that I should link using this ld command: ld -m elf_i386 -s -o tiny tiny.o

However, doing this gives me the following warning: ld: warning: cannot find entry symbol _start; defaulting to 0000000008048060

And if I ./tiny I get a Segmentation Fault. And ./tiny ; echo $? also returns the number '139' which is... unexpected.

Browsing around, I see that the problem is solved for some by passing 1 to the eax register and 0 to ebx, and using an int command I'm unfamiliar with to end the program... But considering my objective is to make the program as small as possible, I would rather not add additional lines of code.

I should add that compiling and linking this similar code (GAS):

.global main
.text
main:
    mov $32, %eax

Using the gcc compiler seems to run flawlessly. I'm at a loss here. Any point in the right direction would be greatly appreciated.

2
If you are on 64-bit Linux Mint and trying to create 32-bit application (linking using GCC) try doing sudo apt-get install gcc-multilib g++-multilib Then try nasm -f elf32 tiny.asm and gcc -m32 -Wall -s tiny.o -o tiny - Michael Petch
Thank you so much! this solves everything for me, do you mind explaining a little more what the additional options do? Are they simply enabling the 32bit code to function in a 64bit environment? - Damazae
By default files needed for GCC to create 32-bit program are not installed on 64-bit Linux. sudo apt get... line simply downloads and installs the GCC files needed for 32-bit GCC development on 64-bit Linux Mint. Since you had BITS 32 at the top of assembler you want to compile that to a 32-bit ELF object which is what nasm -f elf32 tiny.asm does )(it output by default a file called tiny.o. Object files need to be linked to an executable gcc -m32 -Wall -s tiny.o -o tiny does that. The -m32 option that I added says we want to generate a 32-bit executable from 32-bit objects. - Michael Petch
By using GCC to link the executable, the generated program actually does some C runtime initialization and then calls the label called main as if it was a C function. When you do the ret in your main it returns back to the C runtime code and exits the program cleanly for you. - Michael Petch

2 Answers

0
votes

This applies to Linux Mint, Ubuntu, Xubuntu and other modern Ubuntu based distributions. For other Debian systems the information is the same, but you can remove the sudo command from the installation command and run the command as the root user.


By default files needed for GCC to create 32-bit program are not installed on 64-bit Linux Mint. This command will install the required files:

sudo apt-get install gcc-multilib g++-multilib 

To assemble asa 32-bit object, and then link to a 32-bit executable you can do this:

nasm -f elf32 tiny.asm -o tiny.o
gcc -m32 -Wall -s tiny.o -o tiny

Since you had BITS 32 at the top of the assembler file you want to compile that to a 32-bit ELF object which is what the -f elf32 option on the NASM command line does.

GCC is used to link objects to a final executable. The addition of -m32 tells GCC to link the objects to a 32-bit executable. The -m32 option overrides the normal default behavior of generating 64-bit executables on a 64-bit Linux distribution.


By using GCC to link the executable, the generated program actually supplies a _start that does C runtime initialization and then calls the label called main as if it was a C function.

When you do the ret in your main function it returns back to the C runtime code and exits the program cleanly for you.

Using GCC is the easier and safer method for new assembly language developers. It also allows you to add code that calls the C runtime functions like printf, scanf, atoi and most of the other standard C library functions.

0
votes

The default entry point for a program is the _start symbol. When you compile with gcc, the compiler creates a function called _start doing some setup before calling your main. It's actually part of the C library I believe. Because you're using a more low-level tool, you're not linking to the C library and hence you have to do this yourself.

So just change your main to _start and ld won't complain anymore. Now, you'd probably be better off using libc's _start, because your current implementation would still segfault: you need to stop your program with the exit syscall, not with ret.

Michael's comment tells you how to use gcc to compile from assembly code (so you have libc's _start). If you still want to do it yourself your program needs to look like this:

BITS 32
GLOBAL _start
SECTION .text
_start:
    mov ebx, 42
    mov eax, 1
    int 0x80

You put exit's syscall number in eax, the return code in ebx, and trigger the 0x80 interrupt.