This question already has answers here:
Assembling 32-bit binaries on a 64-bit system (GNU toolchain)
(2 answers)
Closed 6 years ago.
I have program written in 32 bit assembly language... Now I just can't compile it on 64 bit OS. On our school they are specific and program has to be written in 32 bit version. Here is my program:
bits 32
extern _printf
global _start
section .data
message db "Hello world!!", 10, 0
section .text
_start:
pushad
push dword message
call _printf
add esp, 4
popad
ret
Any idea? I have tried so many ways to compile that.
Error output after compiling:
nasm -f elf64 vaja4.asm
ld vaja4.o -o vaja4
./vaja4
output:
vaja4.o: In function `_start':
vaja4.asm:(.text+0x7): undefined reference to `_printf'
First change _printf to printf and the _start symbol to main, then use gcc to link the object file, which will automatically link it to libc, you need to do that because AFAIK you can't link to libc without a main. Also you should use elf32 not elf64 when assembling because the code has 32 bits instructions :
bits 32
extern printf
global main
section .data
message db "Hello world!!", 10, 0
section .text
main:
pushad
push dword message
call printf
add esp, 4
popad
ret
And build with:
nasm -f elf32 vaja4.asm
gcc -m32 vaja4.o -o vaja4
$./test
$Hello world!!
Edit:
Since you're now compiling 32-bit code on a 64-bit system, you will need to install the 32-bit version of the libraries
apt-get install ia32-libs
On Ubuntu 12.10, you need to install development packages first
sudo apt-get update
sudo apt-get install libc6-dev-i386
for
gcc -m32 vaja4.o -o vaja4
to work.
I doubt that the error you see is because of 32/64 bit issue. The error that you see i.e
vaja4.asm:(.text+0x7): undefined reference to `_printf'
is clearly telling you the symbol _printf is undefined which means that the library for printf function is not being linked.
your linking step i.e
ld vaja4.o -o vaja4
does not include any libraries. You need to link your program with a library that can provide definition of the printf function. I believe ld should pick the library it self without bothering you with these messages but because it is not able to find a suitable C library for this function, I guess you dont have the required libraries i.e either 32 bit or 64 library is missing.
Anyway, plz try the following sequence of commands to assemble and link your program:
nasm -f elf vaja4.asm
ld -m elf_i386 vaja4.o vaja4
./vaja4
It looks to me like you forgot to link against the C library, which is the part that provides the printf function (and others):
ld vaja4.o -o vaja4 -lc
Related
I am trying to follow the exercise in the book PC Assembly by Paul Carter. http://pacman128.github.io/pcasm/
I'm trying to run the program from 1.4 page 23 on Ubuntu 18. The files are all available on the github site above.
Since original code is for 32bit I compile using
nasm -f elf32
for first.asm and asm_io.asm to get the object files. I also compile driver.c
I use the linker from gcc and run
gcc -m32 -o first first.o asm_io.o driver.o
but it keeps giving me a bun of errors like
undefined reference to '_scanf'
undefined reference to '_printf'
(note _printf appears instead of printf because some conversion is done in the file asm_io.asm to maintain compatibility between windows and linux OS's)
I don't know why these errors are appearing. I also try running using linker directly
ld -m elf_i386 -e main -o first -first.o driver.o asm_io.o -I /lib/i386-linux-gnu/ld-linux.so.2
and many variations since it seems that its not linking with the C libraries.
Any help? Stuck on this for a while and couldn't find a solution on similar questions
Linux doesn't prepend _ to names when mapping from C to asm symbol names in ELF object files1.
So call printf, not _printf, because there is no _printf in libc.
Whatever "compatibility" code did that is doing it wrong. Only Windows and OS X use _printf, Linux uses printf.
So either you've misconfigured something or defined the wrong setting, or it requires updating / porting to Linux.
Footnote 1: In ancient history (like over 20 years ago), Linux with the a.out file format did use leading underscores on symbol names.
Update: the library uses the NASM preprocessor to %define _scanf scanf and so on, but it requires you to manually define ELF_TYPE by assembling with nasm -d ELF_TYPE.
They could have detected ELF32 or ELF64 output formats on their own, because NASM pre-defines __OUTPUT_FORMAT__. Someone should submit a pull-request to make this detection automatic with code something like this:
%ifidn __OUTPUT_FORMAT__, elf32
%define ELF_TYPE 32
%elifidn __OUTPUT_FORMAT__, elf64
%define ELF_TYPE 64
%endif
%ifdef ELF_TYPE
...
%endif
I try to assemble and link my teacher's NASM code, but it does not work on my linux (Ubuntu 16.03) while it's working on her pc (Windows)
segment .data
a dw 10
segment .bss
segment .text
global _main:
extern _printf
_main:
_b100: mov eax, 10
_b150: mov eax, a
_b200: mov ebx, eax
fin:
ret
Those are the instructions i follow to assemble the code
nasm -g -f elf32 test.asm;ld -m elf_i386 -s -o demo *.o
ld returns an error
ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080
Regardless the effect of errors the executable is generated every time I run the commands but when I want to execute breakpoints on the program with gdb I can't.
First the code needs some patching for linux:
-global _main:
+global main
-_main:
+main:
Remove the underscore from main symbol. Also in the global directive don't add the colon, that's needed when you specify new label.
The removal of underscore will apply also to other external symbols, like printf or when you will publish function from your asm to the C with global.
Compiling:
nasm -g -felf32 -Fdwarf test.asm; gcc -m32 -o demo test.o
And you need to have nasm, gcc and 32 bit libraries installed, not sure what is the minimal set of packages, but going by sudo apt-get install nasm gcc gcc-multilib may be enough even on clean install of *buntu.
This question already has answers here:
Assembling 32-bit binaries on a 64-bit system (GNU toolchain)
(2 answers)
Closed 6 years ago.
I tried to write simple program without main
segment .data
fmt db "test", 0xa, 0
segment .text
global _start
extern printf
_start:
lea rdi, [fmt] ; print simple string
xor eax, eax
call printf
mov eax, 60 ; exit successfully
xor edi, edi
syscall
Compile:
yasm -f elf64 main.s; ld -o main main.o
Got
main.o: In function `_start':
main.s:(.text+0xb): undefined reference to `printf'
How should one fix this?
Cause of Your Error
The reason you get undefined reference to printf while linking is because you didn't link against the C library. As well, when using your own _start label as an entry point there are other issues that must be overcome as discussed below.
Using LIBC without C Runtime Startup Code
To make sure you are using the appropriate dynamic linker at runtime and link against the C library you can use GCC as a frontend for LD. To supply your own _start label and avoid the C runtime startup code use the -nostartfiles option. Although I don't recommend this method, you can link with:
gcc -m64 -nostartfiles main.o -o main
If you wish to use LD you can do it by using a specific dynamic linker and link against the C library with -lc. To link x86_64 code you can use:
ld -melf_x86_64 --dynamic-linker=/lib64/ld-linux-x86-64.so.2 main.o -lc -o main
If you had been compiling and linking for 32-bit, then the linking could have been done with:
ld -melf_i386 --dynamic-linker=/lib/ld-linux.so.2 main.o -lc -o main
Preferred Method Using C Runtime Startup Code
Since you are using the C library you should consider linking against the C runtime. This method also works if you were to create a static executable. The best way to use the C library is to rename your _start label to main and then use GCC to link:
gcc -m64 main.o -o main
Doing it this way allows you to exit your program by returning (using RET) from main the same way a C program ends. This also has the advantage of flushing standard output when the program exits.
Flushing output buffers / exit
Using syscall with EAX=60 to exit won't flush the output buffers. Because of this you may find yourself having to add a newline character on your output to see output before exiting. This is still not a guarantee you will see what you output with printf. Rather than the sys_exit SYSCALL you might consider calling the C library function exit. the MAN PAGE for exit says:
All open stdio(3) streams are flushed and closed. Files created by tmpfile(3) are removed.
I'd recommend replacing the sys_exit SYSCALL with:
extern exit
xor edi, edi ; In 64-bit code RDI is 1st argument = return value
call exit
The printf function is provided by the C standard library, libc. To use it, you need to pass -lc to the linker:
ld -o main main.o -lc
I recommend you to use the C compiler as a frontend for the linker to get possible other flags right, too:
cc -o main -nostartfiles main.o
Note that since your program doesn't initialize the C standard library, e parts of it may not work as expected. If you want to use the C standard library in your assembly program, I recommend you to make your assembly program start from main and to link it by invoking the C compiler:
yasm -f elf64 main.s
cc -o main main.o
I'm learning assembly with NASM for a class I have in college. I would like to link the C Runtime Library with ld, but I just can't seem to wrap my head around it. I have a 64 bit machine with Linux Mint installed.
The reason I'm confused is that -- to my knowledge -- instead of linking the C runtime, gcc copies the things that you need into your program. I might be wrong though, so don't hesitate to correct me on this, please.
What I did up to this point is, to link it using gcc. That produces a mess of a machine code that I'm unable to follow though, even for a small program like swapping rax with rbx, which isn't that great for learning purposes. (Please note that the program works.)
I'm not sure if it's relevant, but these are the commands that I'm using to compile and link:
# compilation
nasm -f elf64 swap.asm
# gcc
gcc -o swap swap.o
# ld, no c runtime
ld -s -o swap swap.o
Thank you in advance!
Conclusion:
Now that I have a proper answer to the question, here are a few things that I would like to mention. Linking glibc dynamically can be done like in Z boson's answer (for 64 bit systems). If you would like to do it statically, do follow this link (that I'm re-posting from Z boson's answer).
Here's an article that Jester posted, about how programs start in linux.
To see what gcc does to link your .o-s, try this command out: gcc -v -o swap swap.o. Note that 'v' stands for 'verbose'.
Also, you should read this if you are interested in 64 bit assembly.
Thank you for your answers and helpful insight! End of speech.
Here is an example which uses libc without using GCC.
extern printf
extern _exit
section .data
hello: db 'Hello world!',10
section .text
global _start
_start:
xor eax, eax
mov edi, hello
call printf
mov rax, 0
jmp _exit
Compile and link like this:
nasm -f elf64 hello.asm
ld hello.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -m elf_x86_64
This has worked fine so far for me but for static linkage it's complicated.
If you want to call simple library functions like atoi, but still avoid using the C runtime, you can do that. (i.e. you write _start, rather than just writing a main that gets called after a bunch of boiler-plate code runs.)
gcc -o swap -nostartfiles swap.o
As people say in comments, some parts of glibc depend on constructors/destructors run from the standard startup files. Probably this is the case for stdio (puts/printf/scanf/getchar), and maybe malloc. A lot of functions are "pure" functions that just process the input they're given, though. sprintf/sscanf might be ok to use.
For example:
$ cat >exit64.asm <<EOF
section .text
extern exit
global _start
_start:
xor edi, edi
jmp exit ; doesn't return, so optimize like a tail-call
;; or make the syscall directly, if the jmp is commented
mov eax, 231 ; exit(0)
syscall
; movl eax, 1 ; 32bit call
; int 0x80
EOF
$ yasm -felf64 exit64.asm && gcc -nostartfiles exit64.o -o exit64-dynamic
$ nm exit64-dynamic
0000000000601020 D __bss_start
0000000000600ec0 d _DYNAMIC
0000000000601020 D _edata
0000000000601020 D _end
U exit##GLIBC_2.2.5
0000000000601000 d _GLOBAL_OFFSET_TABLE_
00000000004002d0 T _start
$ ltrace ./exit64-dynamic
enable_breakpoint pid=11334, addr=0x1, symbol=(null): Input/output error
exit(0 <no return ...>
+++ exited (status 0) +++
$ strace ... # shows the usual system calls by the runtime dynamic linker
I've been following this tutorial for an intro to assembly on Linux.
section .text
global _start ;must be declared for linker (ld)
_start:
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptior
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x080 ;call kernel
section .data
msg db 'Hello, world!', 0xa ;the string
len equ $ - msg ;length of the string
I've then had problems compiling it. I've looked around and found (on SO) that I should compile it like this:
nasm -f elf64 hello.asm
gcc -o hello hello.o
But I keep getting this error from GCC:
hello.o: In function `_start':
hello.asm:(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o:(.text+0x0): first defined here
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
(NB: I'm running Debian Linux on a 64 bit Intel i7)
If you are going to learn assembly, then you are much better served learning to use the assembler nasm and the linker ld without relying on gcc. There is nothing wrong with using gcc, but it masks part of the linking process that you need to understand going forward.
Learning assembly in the current environment (generally building on x86_64 but using examples that are written in x86 32-bit assembler), you must learn to build for the proper target and the language (syscall) differences between the two. Your code example is 32-bit assembler. As such your nasm compile string is incorrect:
nasm -f elf64 hello.asm
The -f elf64 attempts to compile a 64-bit object file, but the instructions in your code are 32-bit instructions. (It won't work)
Understanding and using ld provides a better understanding of the differences. Rather than using gcc, you can use nasm and ld to accomplish the same thing. For example (with slight modification to the code):
msg db 0xa, 'Hello, StackOverflow!', 0xa, 0xa ;the string
You compile and build with:
nasm -f elf -o hello-stack_32.o hello-stack_32.asm
ld -m elf_i386 -o hello-stack_32 hello-stack_32.o
Note the use of -f elf for 32-bit code in the nasm call and the -m elf_i386 linker option to create a compatible executable.
output:
Hello, StackOverflow!
If you are serious about learning assembler, there are a number of good references on the web. One of the best is The Art of Assembly. (it is written primarily for 8086 and x86, but the foundation it provides is invaluable). In addition, looking at the executables you create in binary can be helpful. Take a look at Binary Vi (BVI). It is a good tool.
bvi screenshot
You should add -nostdlib when linking your binary.
gcc -o hello hello.o -nostdlib