13

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.

Community
  • 1
  • 1
mrDudePerson
  • 327
  • 3
  • 12
  • 2
    Short answer: don't. Unfortunately libc comes with not just the dynamic library, but a bunch of static objects needed for initialization and shutdown. If you really want to do it, use `gcc -v` to see what the needed parts are. You might be interested in this [awesome article about program startup](http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html). – Jester Dec 06 '15 at 13:46
  • `gcc -o swap swap.o` links the runtime. `ld -o swap swap.o` does not. Linking includes copying large-ish parts of the runtime over to the executable. What is the problem exactly? – n. m. could be an AI Dec 06 '15 at 13:49
  • @Jester I'll look into that right now! – mrDudePerson Dec 06 '15 at 13:55
  • @n.m. Yes, as I've stated, I understand that. What I want to know is: _What to write instead of `ld -o swap swap.o` in order to link glibc with my program usning *ld*._ – mrDudePerson Dec 06 '15 at 13:58
  • 2
    Use `gcc -v -o swap swap.o` to see how gcc incokes ld. You will need to do more or less the same thing, so you may spare the trouble and just use gcc. – n. m. could be an AI Dec 06 '15 at 14:43
  • @n.m. Oh yeah, I get it now! Thanks :) – mrDudePerson Dec 06 '15 at 14:52
  • @Jester: Good point about libc constructors. I might have overlooked that in my `gcc -nostartfiles` answer if you hadn't pointed it out. – Peter Cordes Dec 06 '15 at 15:30
  • @JetBlue : the space isn't required. – Michael Petch Feb 02 '19 at 22:21

2 Answers2

7

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.

Jet Blue
  • 5,109
  • 7
  • 36
  • 48
Z boson
  • 32,619
  • 11
  • 123
  • 226
2

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
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • @mrDudePerson: Don't forget to "accept" an answer that solved your problem. (click the checkbox under up/down vote arrows). Otherwise the question still shows up as unanswered. – Peter Cordes Dec 07 '15 at 12:53