Search code examples
cgcccygwinldosdev

C kernel compilation: GCC LD undefined reference to `___main'


So I'm trying to compile a C file to .bin and then add it to an .img file after my first stage bootloader.
I have found these bash commands in this answer by user Michael Petch:

gcc -g -m32 -c -ffreestanding -o kernel.o kernel.c -lgcc
ld -melf_i386 -Tlinker.ld -nostdlib --nmagic -o kernel.elf kernel.o
objcopy -O binary kernel.elf kernel.bin

and used this C code (taken from the same answer, saved as kernel.c):

/* This code will be placed at the beginning of the object by the linker script */
__asm__ ("jmp _main\r\n");

int main(){
    /* Do Stuff Here*/

    return 0; /* return back to bootloader */
}

I executed those commands in cygwin and it produced the following result:

ld: kernel.o: in function `main':
/cygdrive/d/Work/asm/kernel.c:4: undefined reference to `___main'
objcopy: 'kernel.elf': No such file

The linker.ld file is here:

OUTPUT_FORMAT(elf32-i386)
ENTRY(_main)

SECTIONS
{
    . = 0x9000;
    .text : { *(.text.start) *(.text) }
    .data : { *(.data) }
    .bss  : { *(.bss) *(COMMON) }
}

I have dissasembled the kernel.o file using objdump, the result of which is here:

> objdump -d -j .text kernel.o

kernel.o:     file format pe-i386


Disassembly of section .text:

00000000 <.text>:
   0:   eb 00                   jmp    2 <_main>

00000002 <_main>:
   2:   55                      push   %ebp
   3:   89 e5                   mov    %esp,%ebp
   5:   83 e4 f0                and    $0xfffffff0,%esp
   8:   e8 00 00 00 00          call   d <_main+0xb>
   d:   b8 00 00 00 00          mov    $0x0,%eax
  12:   c9                      leave
  13:   c3                      ret

Here is the result of gcc -v if that helps also:

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/10/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: /mnt/share/cygpkgs/gcc/gcc.x86_64/src/gcc-10.2.0/configure --srcdir=/mnt/share/cygpkgs/gcc/gcc.x86_64/src/gcc-10.2.0 --prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --libexecdir=/usr/lib --with-gcc-major-version-only --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --enable-__cxa_atexit --with-dwarf2 --with-tune=generic --enable-languages=c,c++,fortran,lto,objc,obj-c++ --enable-graphite --enable-threads=posix --enable-libatomic --enable-libgomp --enable-libquadmath --enable-libquadmath-support --disable-libssp --enable-libada --disable-symvers --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib --enable-linker-build-id --with-default-libstdcxx-abi=gcc4-compatible --enable-libstdcxx-filesystem-ts
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.0 (GCC)

What am I doing wrong? Is this caused by cygwin? If yes, is there any other option I could use on windows? (I tried MSVC but that is just plainly horrible)

Also, my bootloader is not using any .section pseudo-ops (I have no idea on how to correctly work with them), will this cause any problems in the future and will it work correctly with the compiled C program?


Solution

  • By deeper searching, it can be easily found out that the __main (with an additional underscore internally) is the actual entry point for programs.

    The same problem is mentioned in the following two answers:

    1. https://stackoverflow.com/a/32164910/14320958
    2. https://stackoverflow.com/a/45442576/14320958

    Both of which claim some form of a connection to the -lgcc option and the libgcc library.

    Renaming main to __main works, but is not recommended (the entry point for kernels is apparently by convention kmain as seen in other questions and answers)

    The __main function is what a OS calls when starting a program and it usually contains (for example) a call to exit() (passing the return code from main if it's return type is int) and some other underlying system calls (which are probably system specific, more research would need to be done here)

    GCC expects you to include a __main function even on standalone compilations, since it's by specification (or that's what I seen people claim) the default entry point for all applications