4 votes

Quel est le plus petit binaire ELF Hello World x86_64 ?

J'ai essayé d'écrire à la main le plus petit programme x86_64 ELF hello world possible, mais je reçois une erreur de segmentation lorsque j'essaie de l'exécuter.

gdb dit : During startup program terminated with signal SIGSEGV, Segmentation fault.

Voici le fichier hexdump :

00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0200 3e00 0100 0000 7800 0000 0000 0000  ..>.....x.......
00000020: 4000 0000 0000 0000 0000 0000 0000 0000  @...............
00000030: 0000 0000 4000 3800 0100 0000 0000 0000  ....@.8.........
00000040: 0100 0000 0500 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 3100 0000 0000 0000 3100 0000 0000 0000  1.......1.......
00000070: 0200 0000 0000 0000 b801 0000 00bf 0100  ................
00000080: 0000 be9a 0000 00ba 0a00 0000 0f05 b83c  ..."...........<
00000090: 0000 00bf 0000 0000 0f05 4865 6c6c 6f2c  ..........Hello,
000000a0: 2057 6f72 6c64 210a 00                    World!..

Voici le résultat de readelf -a :

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x78
  Start of program headers:          64 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         1
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0

There are no sections in this file.

There are no section groups in this file.

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000031 0x0000000000000031  R E    0x2

There is no dynamic section in this file.

There are no relocations in this file.
No processor specific unwind information to decode

Dynamic symbol information is not available for displaying symbols.

No version information found in this file.

Et voici le code :

0xb8 0x01 0x00 0x00 0x00 /* mov %rax, 1 ; sys_write */
0xbf 0x01 0x00 0x00 0x00 /* mov %rdi, 1 ; STDOUT */
0xbe 0x9a 0x00 0x00 0x00 /* mov %rsi, 0x9a ; address of string */
0xba 0x0a 0x00 0x00 0x00 /* mov %rdi, 15 ; size of string */
0x0f 0x05                /* syscall */

0xb8 0x3c 0x00 0x00 0x00 /* mov %rax, 60 ; sys_exit */
0xbf 0x00 0x00 0x00 0x00 /* mov %rdi, 0 ; exit status */
0x0f 0x05                /* syscall */

Le programme "Hello, World ! \n La chaîne de caractères " suit immédiatement. J'ai utilisé cette instruction MOV . Le fait de jouer avec les champs d'offset, d'alignement et d'adresse virtuelle de l'en-tête du programme n'a rien donné. Les page de manuel est un peu confuse dans cette section. J'ai également essayé de comparer ce binaire à un binaire écrit en assembleur, mais je n'ai rien trouvé d'utile.

J'en viens à ma question : Pouvez-vous me dire quelle est l'erreur et/ou comment je peux déboguer ce binaire ?

3voto

Employed Russian Points 50479

J'ai essayé d'écrire à la main le programme hello world x86_64 ELF le plus petit possible

Vous devez fournir un source pour votre programme, afin que nous puissions le corriger.

gdb dit : During startup program terminated with signal SIGSEGV

C'est GDB qui vous dit qu'il a appelé fork/execve pour créer le programme cible, et s'attend à ce que le noyau informe GDB que le programme est maintenant prêt à être débogué. Au lieu de cela, le noyau a notifié à GDB que le programme était mort avec la mention SIGSEGV , sans jamais atteindre sa première instruction .

GDB ne s'attendait pas à cela. Pourquoi cela se produit-il ?

Cela se produit lorsque le noyau regarde votre exécutable et dit "Je ne peux pas créer un programme en cours d'exécution à partir de cela".

Pourquoi est-ce le cas ici ? Parce que cette LOAD segment :

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000031 0x0000000000000031  R E    0x2

demande au noyau de mapper 0x31 octets à partir de l'offset 0 dans le fichier à l'adresse virtuelle 0 . Mais le noyau refuse (à juste titre) une telle demande insensée, et termine le programme avec SIGSEGV avant de revenir de execve .

Vous pourriez probablement éviter ce problème en créant le fichier ET_DYN au lieu de ET_EXEC -- Cela changerait la signification de l'en-tête de votre programme, qui passerait de "mapper ce segment à 0" à "mapper ce segment n'importe où".

Vous pourriez certainement éviter cela en conservant le ET_EXEC mais en changeant le .p_vaddr y .p_paddr du segment à quelque chose comme 0x10000 .

TL;DR : Votre programme et vos en-têtes de fichiers doivent avoir un sens pour le noyau, sinon vous n'arriverez jamais à décoller.

1voto

La réponse que j'ai acceptée a fait l'affaire. Je veux juste partager le nouveau fichier hexdump du binaire ici :

00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
00000010: 0200 3e00 0100 0000 7800 0100 0000 0000  ..>.....x.......
00000020: 4000 0000 0000 0000 0000 0000 0000 0000  @...............
00000030: 0000 0000 4000 3800 0100 0000 0000 0000  ....@.8.........
00000040: 0100 0000 0500 0000 0000 0000 0000 0000  ................
00000050: 0000 0100 0000 0000 0000 0100 0000 0000  ................
00000060: 3100 0000 0000 0000 3100 0000 0000 0000  1.......1.......
00000070: 0200 0000 0000 0000 b801 0000 00bf 0100  ................
00000080: 0000 be9a 0001 00ba 0f00 0000 0f05 b83c  ...............<
00000090: 0000 00bf 0000 0000 0f05 4865 6c6c 6f2c  ..........Hello,
000000a0: 2057 6f72 6c64 210a 00                    World!..

readelf -a :

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x10078
  Start of program headers:          64 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         1
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0

There are no sections in this file.

There are no section groups in this file.

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000010000 0x0000000000010000
                 0x0000000000000031 0x0000000000000031  R E    0x2

There is no dynamic section in this file.

There are no relocations in this file.
No processor specific unwind information to decode

Dynamic symbol information is not available for displaying symbols.

No version information found in this file.

Le code :

0xb8, 0x01, 0x00, 0x00, 0x00, /* mov $0x1,%rax     ; sys_write */
0xbf, 0x01, 0x00, 0x00, 0x00, /* mov $0x1,%rdi     ; STDOUT */
0xbe, 0x9a, 0x00, 0x01, 0x00, /* mov $0x1009a,%rsi ; address of string */
0xba, 0x0f, 0x00, 0x00, 0x00, /* mov $0xf,%rdx     ; size of string*/
0x0f, 0x05,                   /* syscall */
0xb8, 0x3c, 0x00, 0x00, 0x00, /* mov $0x3c,%rax    ; sys_exit */
0xbf, 0x00, 0x00, 0x00, 0x00, /* mov $0x0,%edi     ; exit status */
0x0f, 0x05                    /* syscall */

Comme indiqué dans les commentaires, il ne s'agit pas du plus petit binaire ELF x86_64 possible. Le code pourrait être amélioré et si vous voulez être fou, vous pouvez mettre des choses dans des parties inutilisées de l'en-tête elf. Mais dans tous les cas, je suis assez satisfait de la taille du fichier de 169 octets.

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