Chaîne de compilation d’un programme C ou Assembleur#

Retour à « Hello, world! »#

#include <stdio.h>
int main(int argc, char *argv[])
{
        if (argc < 2)
                return 1;

        printf("Hello, %s!\n", argv[1]);

        return 0;
}

Préprocesseur: C (texte) vers C « préprocessé » (texte)#

gcc -E hello.c > hello.cpp.c

Extrait de hello.cpp.c:

# 0 "hello.c"
# 209 "/usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h" 3 4
typedef long unsigned int size_t;
# 34 "/usr/include/stdio.h" 2 3 4

# 143 "/usr/include/stdio.h" 3 4
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

extern int fflush (FILE *__stream);

extern int fprintf (FILE *__restrict __stream,
      const char *__restrict __format, ...);

extern int printf (const char *__restrict __format, ...);

# 3 "hello.c"
int main(int argc, char *argv[])
{
        if (argc < 2)
                return 1;

        printf("Hello, %s!\n", argv[1]);

        return 0;
}

Compilation du C « préprocessé » (texte) vers l’assembleur (texte)#

gcc -S hello.cpp.c

Génération d’un fichier hello.cpp.s:

	.file	"hello.cpp.c"
	.text
	.section	.rodata
.LC0:
	.string	"Hello, %s!\n"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	%edi, -4(%rbp)
	movq	%rsi, -16(%rbp)
	cmpl	$1, -4(%rbp)
	jg	.L2
	movl	$1, %eax
	jmp	.L3
.L2:
	movq	-16(%rbp), %rax
	addq	$8, %rax
	movq	(%rax), %rax
	movq	%rax, %rsi
	leaq	.LC0(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, %eax
.L3:
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	1f - 0f
	.long	4f - 1f
	.long	5
0:
	.string	"GNU"
1:
	.align 8
	.long	0xc0000002
	.long	3f - 2f
2:
	.long	0x3
3:
	.align 8
4:

Compilation de l’assembleur vers le langage machine#

as -v --64 -o hello.cpp.o hello.cpp.s

Désassemblage#

objdump -d hello.cpp.o
hello.cpp.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   f3 0f 1e fa             endbr64
   4:   55                      push   %rbp
   5:   48 89 e5                mov    %rsp,%rbp
   8:   48 83 ec 10             sub    $0x10,%rsp
   c:   89 7d fc                mov    %edi,-0x4(%rbp)
   f:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
  13:   83 7d fc 01             cmpl   $0x1,-0x4(%rbp)
  17:   7f 07                   jg     20 <main+0x20>
  19:   b8 01 00 00 00          mov    $0x1,%eax
  1e:   eb 27                   jmp    47 <main+0x47>
  20:   48 8b 45 f0             mov    -0x10(%rbp),%rax
  24:   48 83 c0 08             add    $0x8,%rax
  28:   48 8b 00                mov    (%rax),%rax
  2b:   48 89 c6                mov    %rax,%rsi
  2e:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # 35 <main+0x35>
  35:   48 89 c7                mov    %rax,%rdi
  38:   b8 00 00 00 00          mov    $0x0,%eax
  3d:   e8 00 00 00 00          call   42 <main+0x42>
  42:   b8 00 00 00 00          mov    $0x0,%eax
  47:   c9                      leave
  48:   c3                      ret

Edition de liens#

En théorie:

ld -lc -o hello hello.cpp.o

En pratique:

/usr/lib/gcc/x86_64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccXZXHwp.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 -o hello /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../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/11/../../.. hello.cpp.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/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o

Compilation directe d’un code en assembleur x86 sans dépendances#

# section declaration
.data

msg:
        .string "Hello, world!\n"
        len = . - msg

# section declaration
.text

# we must export the entry point to the ELF linker or
# loader. They conventionally recognize _start as their
# entry point. Use ld -e foo to override the default.
.global _start

_start:

        movl    $len,%edx       # third argument: message length
        movl    $msg,%ecx       # second argument: pointer to message to write
        movl    $1,%ebx         # first argument: file handle (stdout)
        movl    $4,%eax         # system call number (sys_write)
        int     $0x80           # call kernel

        movl    $0,%ebx         # first argument: exit code
        movl    $1,%eax         # system call number (sys_exit)
        int     $0x80           # call kernel
as -o hello_syscall.o hello_syscall.s
ld -s -o hello_syscall hello_syscall.o