2 votes

Compilation du noyau C : référence indéfinie à `___main' par GCC LD

Alors j'essaie de compiler un fichier C en .bin puis de l'ajouter à un fichier .img après mon chargeur d'amorçage de première étape.
J'ai trouvé ces commandes bash dans cette réponse par l'utilisateur 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

et j'ai utilisé ce code C (issu de la même réponse, enregistré sous le nom kernel.c):

/* Ce code sera placé au début de l'objet par le script de liage */
__asm__ ("jmp _main\r\n");

int main(){
    /* Faire des choses ici */

    return 0; /* retourner au chargeur d'amorçage */
}

J'ai exécuté ces commandes dans cygwin et cela a produit le résultat suivant:

ld: kernel.o: dans la fonction `main':
/cygdrive/d/Work/asm/kernel.c:4: référence indéfinie vers `___main'
objcopy: 'kernel.elf': Aucun fichier de ce type

Le fichier linker.ld est ici:

OUTPUT_FORMAT(elf32-i386)
ENTRY(_main)

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

J'ai désassemblé le fichier kernel.o en utilisant objdump, voici le résultat:

> objdump -d -j .text kernel.o

kernel.o:     format du fichier pe-i386

Désassemblage de la 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

Voici le résultat de gcc -v si cela peut aussi aider:

Utilisation des spécifications intégrées.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/10/lto-wrapper.exe
Cible : x86_64-pc-cygwin
Configuré avec: /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
Modèle de thread: posix
Algorithmes de compression LTO pris en charge: zlib zstd
Version gcc 10.2.0 (GCC)

Qu'est-ce que je fais de mal? Est-ce dû à cygwin? Si oui, y a-t-il une autre option que je pourrais utiliser sur Windows? (J'ai essayé MSVC mais c'est simplement horrible)

Aussi, mon chargeur d'amorçage n'utilise pas d'opérations pseudo-.section (je n'ai aucune idée de comment travailler correctement avec eux), est-ce que cela causera des problèmes à l'avenir et est-ce que cela fonctionnera correctement avec le programme C compilé?

1voto

Expolarity Points 112

En effectuant une recherche plus approfondie, il est facile de découvrir que le __main (avec un underscore supplémentaire en interne) est le point d'entrée réel des programmes.

Le même problème est mentionné dans les deux réponses suivantes:

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

Tous deux affirment une forme de connexion avec l'option -lgcc et la bibliothèque libgcc.

Renommer main en __main fonctionne, mais n'est pas recommandé (le point d'entrée pour les noyaux est apparemment par convention kmain comme on peut le voir dans d'autres questions et réponses)

La fonction __main est ce qu'un OS appelle lorsqu'il démarre un programme et elle contient généralement (par exemple) un appel à exit() (en passant le code de retour de main si son type de retour est int) et quelques autres appels système sous-jacents (qui sont probablement spécifiques au système, des recherches supplémentaires seraient nécessaires ici)

GCC s'attend à ce que vous incluiez une fonction __main même lors de compilations autonomes, puisque c'est par spécification (ou du moins c'est ce que j'ai vu des gens affirmer) le point d'entrée par défaut pour toutes les applications

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X