First I want to clarify that I know this question might have been answered hundreds of times. However after hours of Google search I simply couldn't find anything that's exactly what I want. Also even though I've been writing c programs for quite a while, I'm kind of new to nasm and ld. So I would really appreciate it if I can get a simple answer without having to read a whole nasm/ld tutorial or the complete manual.
What I want to do is:
say I have a function written in c that calls some function in the c standard library:
/* foo.c */
#include <stdio.h>
void foo(int i)
{
printf("%d\n", i);
}
I want to call this function in nasm so I tried this:
; main.asm
global _start
extern foo
section .text
_start:
push 1234567
call foo
add esp, 4
mov eax, 1
xor ebx, ebx
int 80h
Then I tried to compile them and run:
[user ~/Documents/asm/callc]#make all
nasm main.asm -felf
gcc -c foo.c -o foo.o -m32
ld -o main main.o foo.o -melf_i386 -lc
[user ~/Documents/asm/callc]#ls
foo.c foo.o main main.asm main.o Makefile
[user ~/Documents/asm/callc]#./main
bash: ./main: No such file or directory
[user ~/Documents/asm/callc]#bash main
main: main: cannot execute binary file
I didn't get any errors but apparently I couldn't run the executable output file.
If the c function doesn't call any library functions then the code above can be compiled and it will run without any problems. I also figured out a way to call library functions directly in nasm and use gcc to produce the final executable file. But none of them is exactly what I want.
EDIT:
1. I'm running 64-bit Ubuntu but I'm trying to write 32-bit programs so I used flags like -m32 and -melf_i386.
2. Output of file *:
[user ~/Documents/asm/sof]#file *
foo.c: C source, ASCII text
foo.c~: empty
foo.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
main: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
main.asm: C source, ASCII text
main.asm~: empty
main.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
Makefile: makefile script, ASCII text
Makefile~: makefile script, ASCII text
3. I really have no idea of how to tell ld to include the c standard library. I found something like -lglibc or -lc in some other posts. -lgibc doesn't work and -lc seems to be able to get rid of all errors and I probably thought it worked at first but maybe that's the problem since it probably doesn't link the correct library.
UPDATE
Adding -I/lib32/ld-linux.so.2 to the ld command solved my problem.
Below are commands to compile/assemble/link and run the program:
nasm main.asm -felf
gcc -c foo.c -o foo.o -m32
ld -o main main.o foo.o -melf_i386 -lc -I/lib32/ld-linux.so.2
./main
The C library provides code using the _start interface that starts the C runtime, calls main(), and shuts the runtime down. Hence if you intend to use the C library in your program you must not use the _start interface but provide a main() function.
This is the correct way to do it:
; main.asm
global main
extern foo
section .text
main:
push 1234567
call foo
add esp, 4
xor eax, eax
ret
Build with:
nasm -f elf32 -o main.o main.asm
gcc -m32 -o foo.o -c foo.c
gcc -m32 -o main main.o foo.o
Two remarks:
main() returns, instead of doing an exit system call, to allow the C runtime shutdown code to run.
gcc is used for linking. Internally gcc invokes ld with the appropriate parameters to link with the C library. These are platform specific and subject to change. Hence, don't use ld for this.
Related
I want to compile this C code with the GNU C Compiler on Ubuntu without linking any standard libraries, having only the following code execute.
static void exit(long long code)
{asm inline
("movq $60,%%rax\n"
"movq %[code],%%rdi\n"
"syscall"
:
:[code]"rm"(code)
:"rax"
,"rdi");}
static void write(long long fd,char *msg,long long len)
{asm inline
("movq $0x1,%%rax\n"
"movq %[fd],%%rdi\n"
"movq %[msg],%%rsi\n"
"movq %[len],%%rdx\n"
"syscall"
:
:[fd]"rm"(fd)
,[msg]"rm"(msg)
,[len]"rm"(len)
:"rax"
,"rdi"
,"rsi"
,"rdx");}
#define PRINT(msg) write(1,msg,sizeof(msg))
void _start()
{PRINT("Hello World.\n");
exit(0);}
I compiled with cc example.c -ffreestanding -nostartfiles -O3 -o example.
When I called the output file I saw a lot of extra system calls with strace that should not have been there:
brk
arch_prctl
access
mmap
arch_prctl
mprotect
I then compiled like this: cc example.c -c -O3 -o example.o; ld example.o -o example and it did not do the extra syscalls. It even made the filesize somewhat smaller.
The objdump -d of it was exactly the same. In the objdump -D I found some extra symbols (_DYNAMIC,__GNU_EH_FRAME_HDR,.interp) in the first case compared to the second, but still no sign of any extra syscalls in the code.
Do you know why I get the extra system calls with cc example.c -ffreestanding -nostartfiles -O3 -o example and not with cc example.c -c -O3 -o example.o; ld example.o -o example?
I found out what is happening.
If I compile the code with cc example.c -ffreestanding -nostartfiles -O3 -o example the compiler makes a dynamically linked executable. Dynamically linked executables have an .interp section. That is what I was seeing in my objdump -D.
Dynamically linked executables are executing via the program interpreter and the dynamic linker. The additional system calls I saw, came from the dynamic linker. I still do not know why the executable wants to dynamically link anything in a program that does not link any libraries and wants to be freestanding.
If you do not want the extra system calls from the dynamic linker - you should give gcc the extra -static option. The compiler does not automatically do this if there is no dynamic linking happening.
I have ASM code:
extern my_func
extern printf
extern exit
global _start
section .data
...
section .text
_start:
...
call printf
...
call my_func
...
call exit
and C code:
int my_func(int a, int b)
{
return a+b;
}
I'm using fedora on 64-bit machine. I want the executable be 32-bit.
For dynamic linking I do:
nasm -f elf32 asm.asm ; this gives me asm.o
gcc -m32 -Wall -c c_code.c ; this gives me c_code.o
ld c_code.o asm.o -melf_i386 -L /usr/lib/ -lc -I /lib/ld-linux.so.2 ; this gives me a.out which runs fine and weights 5601 bytes.
What I want to do is link libc statically. I do the following:
gcc -o a2.out -m32 -static -m32 asm.o c_code.o
And I get error:
asm.o: In function `_start':
asm.asm:(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-redhat-linux/4.8.3/../../../../lib64/crt1.o:(.text+0x0):
first defined here
collect2: error: ld returned 1 exit status
Then I change _start to main in ASM code and the whole thing links fine! ldd shows "not a dynamic executable". But the file created weights 721067 bytes! I think that it compiles statically a lot of unnecessary code.
So, my 1st question is:
1) How can I link statically only libc for the required printf and exit functions?
When I try
gcc -m32 -o a3.out -lc asm.o c_code.o ; ASM file has main instead of _start
I get a file that weights 7406 bytes. ldd shows the same dynamic libraries as for the a.out which weights 5601 bytes.
2) Why is that difference? Looks like some additional code that "connects" _start with main in my code...
3) What is the difference between linking with gcc and ld?
Thanks a lot for your attention!
1) How can I link statically only libc for the required printf
and exit functions?
Try compiling with -nostartfiles -static -nostdlib -lc which will avoid adding crt1.o and crtend.o. But keep in mind that this will disable all Glibc initialization code so some Glibc functions will fail to work.
2) Why is that difference? Looks like some additional code
that "connects" _start with main in my code...
GCC adds start files (crt*.o) which perform initialization. See the many online articles for details (e.g. this one).
3) What is the difference between linking with gcc and ld?
Already answered above but in general you can run gcc -v and inspect ld's (or collect2's) arguments.
I am trying to move on from my assembly file kernel stage to my C file kernel stage (finally...). But, I am having some trouble in the process of linking my compiled C kernel to my compiled assembly kernel entry program.
Here is the code for my kernel_entry.asm file.
[BITS 32] ; Starting in 32 bit protected mode
[EXTERN main] ; Extern to C file main function
call main ; Invoke main in our C kernel
jmp $ ; Jump here - Infinite loop
Here is the code for my kernel.c file.
void main() {
char* video_memory = (char*) 0xB8000;
*video_memory = 'X';
}
Here are the command lines I am using to compile them.
nasm -f elf -o kernel_entry.o kernel_entry.asm
gcc -ffreestanding -c kernel.c -o kernel.o
ld -o kernel.bin -Ttext 0x0500 kernel_entry.o kernel.o --oformat binary
The last command line gives me this error.
ld: i386 architecture of input file `kernel_entry.o' is incompatible with i386:x86-64 output
ld: warning: cannot find entry symbol _start; defaulting to 0000000000000500
Note: I am loading my kernel to the address and offset 0x0000:0x0500, which is why I use -Ttext 0x0500, I am unsure why the second ld warning appears but for now it seems unimportant (although if you offer any help in that regard it would be appreciated as well).
Can anyone tell me why I cannot link these files together? I am also running on Ubuntu dekstop 64 bit. Thank you in advance for any help you may give.
It looks as though you're compiling this code on a 64-bit system. As such, kernel.o is a 64-bit binary, and cannot be linked with the 32-bit kernel_entry.o.
Since you don't have any code in place to get the system into long mode, you probably want to compile the "kernel" as 32-bit code. Use -m32 to trigger this:
gcc -m32 -ffreestanding -c kernel.c -o kernel.o
^^^^
I have started to learn assembly language and currently making a simple asm program to call printf function in C in stdio.h.
I am unable to link the object file properly after assembling. The problem I am facing is
undefined reference to printf
After browsing other questions with similar problems on SO, I tried
nasm -f elf -l call.lst call.asm
gcc -o call call.o
After doing this, I get the error:
i386 architecture of input file call.o is compatible with x86_64 output.
How should I properly link the file? Currently, I am doing the following :-
nasm -f elf call.asm
ld -m elf_i386 -s -o call call.o
What changes should I make to the above line ?
If you're writing 32-bit assembly code, you need to tell GCC this:
$ gcc -m32 -o call call.o
Alternatively, if you're writing 64-bit assembly (with [BITS 64]), then you would:
$ nasm -f elf64 -l call.lst call.asm
$ gcc -m64 -o call call.o
This just worked for me:
test.s
[BITS 32]
extern printf
global main
main:
push message
call printf
add esp, 4
xor eax, eax
ret
message:
db "hello",0xA,0
Build
nasm -f elf test.s
gcc -m32 -o calltest test.o
./calltest
There are only two files, main.c and kernel.asm, and I tried to make a program with them using NASM and GCC. contents are as follows:
main.c
#include <stdio.h>
void Print_String() {
printf("Hello World!\n");
}
kernle.asm
extern Print_String
[section .text]
global _start
_start:
call Print_String
Compile and Link:
nasm -f elf -o kernel.o kernel.asm
gcc -c -o main.o main.c
ld -s -lc -o final kernel.o main.o
Then I run the final file with the command: ./final, but the result is depressing:
bash: ./final: No such file or directory
However, the current directory does have the file final, for the command ls, it displays:
final kernel.asm kernel.o main.c main.o
So why it cannot find the file final? Is there anything wrong? Any help appreciated!
It is not that it cannot find it, per se. The error message is somewhat misleading. The dynamic linker cannot resolve its dependencies, and thus your program image is not loadable (and not executable)
The problem is that you are dynamically linking against libc without any other of the paraphernalia to make dynamic linking actually work. Thus you are left with a binary image that cannot be loaded.
You might find that it's easier to statically link against libc. This can be done as follows:
ld -Bstatic -o final kernel.o main.o -lc
Notice you have to move the '-lc' bit after the code module main.o which uses it.
If you try this, you'll get a whole bunch of unresolved symbols. That's because you will also need to link against libgcc and libgcc_eh.
The following got me fairly close (apologies, working on a 64-bit system here):
ld -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3/32/ -melf_i386 -Bstatic -lc -o final kernel.o main.o -lc -lgcc -lgcc_eh
This failed for me with
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/32//libgcc_eh.a(unwind-dw2-fde-glibc.o): In function `_Unwind_Find_FDE':
(.text+0x193b): undefined reference to `dl_iterate_phdr'
Which doesn't make much sense. You may have more luck linking 32-bit on a 32-bit system.
Update
Apologies for the ramble above. I had a think about this again, and, of course, it is possible to make dynamic linking work. The missing piece is to specify the dynamic linker:
In my case, this was:
ld -dynamic-linker /lib32/ld-linux.so.2 -melf_i386 -o final kernel.o main.o -lc
So for you the following should work:
ld -dynamic-linker /lib/ld-linux.so.2 -o final kernel.o main.o -lc
Update again
In response to markzar's comment - you have to make a syscall to cleanly exit. This has the effect of doing something similar to exit(0) in C:
mov eax,1 ; Syscall #1
mov ebx,0 ; Return code 0 = success
int 80H
Try this. First change kernel.asm as follows:
extern Print_String
[section .text]
global main
main:
call Print_String
Then use the following commands to create the executable (instead of the linker).
nasm -f elf -o kernel.o kernel.asm
gcc -c -o main.o main.c
gcc -o final kernel.o main.o
Very simple: there is no main() to call in the program... thus whatever you do, the C-program-startup machinery doesn't get a hold.