2

I have written a Hello World program in C. I used the command gcc -S -o hello.s hello.c (code was in hello.c) to generate assembly, then used as to assemble it. Finally I linked the object code using ld, but it gave me the following error:

ld: a.out:hello.c:(.text+0xa): undefined reference to `__main'
ld: a.out:hello.c:(.text+0x16): undefined reference to `puts'

The C code was as follows:

#include <stdio.h>
int main(void) {
    printf("Hello world!\n");
} 

I use an Intel Core i3 processor, and used MinGW to compile. My operating system is Windows 7.

This was the generated assembly:

    .file   "hello.c"
    .text
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "Hello world!\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB13:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $16, %esp
    call    ___main
    movl    $LC0, (%esp)
    call    _puts
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE13:
    .ident  "GCC: (MinGW.org GCC Build-2) 9.2.0"
    .def    _puts;  .scl    2;  .type   32; .endef

R Z
  • 422
  • 4
  • 13
  • 4
    If you want `main` and `printf` to work you need your system's C library as well, not only `libgcc`. If you use `gcc -v` to link, you'll see the full gory `ld` command that is run. You may not need all of it for any given program, but you'll at least need to understand what they do and which pieces are necessary or not. – Nate Eldredge Oct 12 '20 at 21:39
  • 1
    `___main` (C name `__main`) is how MinGW gets its libc initialized, instead of using dynamic linker hooks to get code to run *before* `main` (or even before `_start`), like glibc does on GNU/Linux. – Peter Cordes Oct 12 '20 at 21:48

2 Answers2

2

These are the steps I generally follow for the manual Build process with either gcc (C) or g++ (C and C):

  1. gcc -E file.c -o file.i
  2. gcc -S file.i -o file.s
  3. gcc -c file.s -o file.o
  4. gcc file.o -o file.out

The the 1st step (Preprocessing) can also be performed with cpp file.c > file.i.

In the 2nd step (Compiling) I sometimes also name the assembly file as .asm instead of .s. .s is traditional for Unix assembler source files, while .asm is traditional for assemblers like NASM. .S source files will get preprocessed and assembled.

In the 3rd step (Assembling) you can also use the assembler directly: as file.s -o file.o. If you used gcc -v -c file.s (verbose), you'd see it was just running as on it.

In the 4th step (Linking) you could also use the ld tool directly, but the right options for paths and libraries depends on the system so it's normally best to let GCC invoke ld for you. With gcc -v, it will show you what options it passed.

Finally, if we have a very simple application where we don't need a tool like Cmake or Bazel, then we can resume the above steps in a single command: gcc file.c -o file.out.

Again, gcc -v will show you the commands it ran to carry out the internal steps, from whatever starting point to wherever you told it to stop. (With the default being to make an executable.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Maf
  • 696
  • 1
  • 8
  • 23
  • 1
    `-v` is "verbose". You can use it with any of the 4 commands. The default is to build all the way to an executable, whatever type of input you give it, e.g. `gcc foo.c` makes an executable. The `-E` / `-S` or `-c` options just stop early. (And since you happened to give it an output from the previous step, it stops after only one step. e.g. it's common to `gcc -c foo.c` in a large project, instead of `gcc *.c -o program`). Also, preprocessing is only logically separate from compiling a C file with GCC for at least a decade, unlike assembling, if you don't use `-E` it's not a separate step – Peter Cordes Oct 22 '22 at 13:20
  • I need to read more how to use ld, since it does not link code generated by `as`. I'm starting from here: https://linux.die.net/man/1/ld – Maf Oct 22 '22 at 13:46
  • 1
    It does, if the asm you assemble `as` doesn't require libraries. Or if you pass the right options to it. As in [Linking a program using printf with ld?](https://stackoverflow.com/q/55314762); my answer there uses NASM instead of `as`, but the key difference is the content of the source file; porting the `.asm` to a `.S` would be simple. [Minimal executable size now 10x larger after linking than 2 years ago, for tiny programs?](https://stackoverflow.com/q/65037919) is an example of using `as` + `ld`. – Peter Cordes Oct 22 '22 at 13:55
1

Thanks to Nate Eldredge for helping me get the answer.

To link the file produced by as, use gcc -v (file).

R Z
  • 422
  • 4
  • 13