-1

I have the following assembly program which prints 'hello world' to the screen by making an appropriate syscall:

.global _start

     .text
            _start:
            # write(1, message, 13)
            mov     $1, %rax                # system call 1 is write
            mov     $1, %rdi                # file handle 1 is stdout
            mov     $message, %rsi          # address of string to output
            mov     $13, %rdx               # number of bytes
            syscall                         # invoke operating system to do the write
    
            # exit(0)
            mov     $60, %rax               # system call 60 is exit
            xor     %rdi, %rdi              # we want return code 0
            syscall                         # invoke operating system to exit
    message:
            .ascii  "Hello, world\n"

If I issue the command gcc hello.s in the ubuntu terminal, will the assembly code be linked with a C library as someone in a webpage contends? And if yes, why is that? The assembly code doesn't seem to refer to C code anywhere.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Nick_h
  • 21
  • 4
  • 1
    `gcc` is the Gnu C compiler and wrapper for `as` and `ld`. `as` is the assembler. `ld` is the linker. Calling gcc will do preprocessing, compilation assembly and linkage, depending on options. – stark Jan 29 '22 at 16:20
  • I know all this. My question is if the command 'gcc hello.s' as is will link to a C library? Dissasembling the output executable would by of any help? – Nick_h Jan 29 '22 at 16:23
  • Do `gcc -v hello.s` and it will show all of the steps that it runs: as and ld. It fails trying to link with crt1.o which is part of the C library. – stark Jan 29 '22 at 16:34
  • If you build that way, it will be, but you don't need to and *shouldn't*. Use `gcc -nostdlib -static foo.s` since you're defining your own `_start` and using raw system calls. See also [Assembling 32-bit binaries on a 64-bit system (GNU toolchain)](https://stackoverflow.com/a/36901649) (but leave out the -m32) – Peter Cordes Jan 29 '22 at 22:26

1 Answers1

3

If I issue the command gcc hello.s in the ubuntu terminal, will the assembly code be linked with a C library as someone in a webpage contends?

Yes.

And if yes, why is that?

That's the documented default behavior of the gcc command when linking, because it's intended primarily for C programs.

The assembly code doesn't seem to refer to C code anywhere.

It doesn't matter, because the assembler and linker don't check whether it does or not.

The default behavior is to set up a link involving the C library, its standard startup code, and your code. You can see the details if you run gcc -v hello.s:

/usr/lib/gcc/x86_64-linux-gnu/9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper -plugin-opt=-fresolution=/tmp/ccVrfSjS.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/9/../../.. /tmp/cc17H49O.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o

The crt*.o files are startup code, and -lc links the C library.

In a C program, the standard startup code is supposed to be the first thing that runs, in order to initialize the library and set up various things in the way that compiled C code expects. So it defines the _start label which is used as the program's entry point, and it contains a call to a symbol called main. Therefore the link can only succeed if the code you supply does define a symbol called main, and does not define a symbol called _start (because there are not allowed to be two, or the linker would not know which to use). That is not the case for your code, on either count.

If you want some other behavior, it's determined by command line switches, not by the contents of your code. -nostdlib tells the linker not to include the C library and its startup code; in that case, your code must supply _start and main is not required. So if you compile with gcc -nostdlib -no-pie foo.s, your code compiles and runs succesfully.

(The -no-pie option is needed because your code uses an absolute static address as an immediate, in mov $message, %rsi. These are limited to 32 bits and so cannot work if the code is to be relocated to an arbitrary spot in the 64-bit address space. Better practice is to use RIP-relative addressing: lea message(%rip), %rsi, and then you won't need -no-pie. You can also use a form of mov with a 64-bit immediate, movabs $message, %rsi, but it uses a few more bytes of code.)

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • Ok, thank you. I have had a vague idea about start up code, _start and main and now your answer cleared things up for me. – Nick_h Jan 29 '22 at 17:38
  • 1
    BTW, `-static` implies `-no-pie`, so `gcc -nostdlib -static` is a good choice for code where you definitely want to link just it, no dynamic anything. (Or use all 3 options). But `-no-pie` will also let gcc/ld make a static executable if no libraries are getting linked, so your way is fine, too. – Peter Cordes Jan 29 '22 at 22:30