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