7.6: Практичний приклад
- Page ID
- 34520
Ми можемо пройти кроки, зроблені для створення простого додатка крок за кроком.
Зауважте, що коли ви вводите gcc, фактично запускає програму драйвера, яка приховує більшість кроків від вас. За звичайних обставин це саме те, що ви хочете, тому що точні команди та параметри, щоб отримати реальний робочий виконуваний файл у реальній системі, можуть бути досить складними та специфічними для архітектури.
Ми покажемо процес компіляції з двома наступними прикладами. Обидва є вихідними файлами C, один визначив функцію main () для початкової точки входу в програму, а інший оголошує функцію допоміжного типу. Існує одна глобальна змінна теж, тільки для ілюстрації.
1 #include <stdio.h>
/* We need a prototype so the compiler knows what types function() takes */
int function(char *input);
5
/* Since this is static, we can define it in both hello.c and function.c */
static int i = 100;
/* This is a global variable */
10 int global = 10;
int main(void)
{
/* function() should return the value of global */
15 int ret = function("Hello, World!");
exit(ret);
}
1 #include <stdio.h>
static int i = 100;
5 /* Declard as extern since defined in hello.c */
extern int global;
int function(char *input)
{
10 printf("%s\n", input);
return global;
}
Всі компілятори мають можливість виконати лише перший крок компіляції. Зазвичай це щось на кшталт -S і вивід, як правило, буде поміщено у файл з тією ж назвою, що і вхідний файл, але з розширенням .s.
Таким чином, ми можемо показати перший крок з gcc -S, як показано в прикладі нижче.
1 ianw@lime:~/programs/csbu/wk7/code$ gcc -S hello.c
ianw@lime:~/programs/csbu/wk7/code$ gcc -S function.c
ianw@lime:~/programs/csbu/wk7/code$ cat function.s
.file "function.c"
5 .pred.safe_across_calls p1-p5,p16-p63
.section .sdata,"aw",@progbits
.align 4
.type i#, @object
.size i#, 4
10 i:
data4 100
.section .rodata
.align 8
.LC0:
15 stringz "%s\n"
.text
.align 16
.global function#
.proc function#
20 function:
.prologue 14, 33
.save ar.pfs, r34
alloc r34 = ar.pfs, 1, 4, 2, 0
.vframe r35
25 mov r35 = r12
adds r12 = -16, r12
mov r36 = r1
.save rp, r33
mov r33 = b0
30 .body
;;
st8 [r35] = r32
addl r14 = @ltoffx(.LC0), r1
;;
35 ld8.mov r37 = [r14], .LC0
ld8 r38 = [r35]
br.call.sptk.many b0 = printf#
mov r1 = r36
;;
40 addl r15 = @ltoffx(global#), r1
;;
ld8.mov r14 = [r15], global#
;;
ld4 r14 = [r14]
45 ;;
mov r8 = r14
mov ar.pfs = r34
mov b0 = r33
.restore sp
50 mov r12 = r35
br.ret.sptk.many b0
;;
.endp function#
.ident "GCC: (GNU) 3.3.5 (Debian 1:3.3.5-11)"
55
Збірка трохи складна для повного опису, але ви повинні бути в змозі побачити, де i визначається як data4 (тобто 4 байта або 32 біта, розмір int), де визначена функція (функція:) та виклик printf ().
Тепер у нас є два файли збірки, готові бути зібрані в машинний код!
Збірка - досить прямий процес. Асемблер зазвичай називається як і приймає аргументи аналогічно gcc.
1 ianw@lime:~/programs/csbu/wk7/code$ as -o function.o function.s
ianw@lime:~/programs/csbu/wk7/code$ as -o hello.o hello.s
ianw@lime:~/programs/csbu/wk7/code$ ls
function.c function.o function.s hello.c hello.o hello.s
5
Після складання ми маємо об'єктний код, який готовий до з'єднання в кінцевий виконуваний файл. Зазвичай ви можете пропустити використання асемблера вручну, викликавши компілятор з -c, який безпосередньо перетворить вхідний файл у об'єктний код, помістивши його у файл з тим самим префіксом, але .o як розширення.
Ми не можемо перевірити об'єктний код безпосередньо, оскільки він знаходиться в двійковому форматі (в наступних тижнях ми дізнаємося про цей двійковий формат). Однак ми можемо використовувати деякі інструменти для перевірки об'єктних файлів, наприклад readelf —symbols покаже нам символи в об'єктному файлі.
1 ianw@lime:~/programs/csbu/wk7/code$ readelf --symbols ./hello.o
Symbol table '.symtab' contains 15 entries:
Num: Value Size Type Bind Vis Ndx Name
5 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
10 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 4 OBJECT LOCAL DEFAULT 5 i
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
15 10: 0000000000000000 0 SECTION LOCAL DEFAULT 10
11: 0000000000000004 4 OBJECT GLOBAL DEFAULT 5 global
12: 0000000000000000 96 FUNC GLOBAL DEFAULT 1 main
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND function
14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND exit
20
ianw@lime:~/programs/csbu/wk7/code$ readelf --symbols ./function.o
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
25 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS function.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
30 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 4 OBJECT LOCAL DEFAULT 5 i
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
35 10: 0000000000000000 0 SECTION LOCAL DEFAULT 10
11: 0000000000000000 128 FUNC GLOBAL DEFAULT 1 function
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND global
Хоча вихід досить складний (знову ж таки!) Ви повинні бути в змозі зрозуміти багато чого з цього. Наприклад
- У виході
hello.oподивіться на символ з ім'ямi. Зверніть увагу, як він говорить, що цеЛОКАЛЬНИЙ? Це тому, що ми оголосили йогостатичнимі як такий він був позначений як локальний для цього об'єктного файлу. - У тому ж виході зверніть увагу, що
глобальназмінна визначається якGLOBAL, тобто вона видима поза цим файлом. Аналогічно зовні видно функціяmain (). - Зверніть увагу, що символ
функції(для викликуфункції ()зліва маєUNDабо undefined. Це означає, що компонувальнику залишилося знайти адресу функції. - Погляньте на символи у файлі
function.cі те, як вони вписуються в висновок.
Насправді виклик компонувальника, який називається ld, є дуже складним процесом у реальній системі (вам набридло це ще?). Ось чому ми залишаємо процес зв'язування до gcc.
Але, звичайно, ми можемо шпигувати за тим, що робить gcc під капотом з прапором -v (verbose).
1 /usr/lib/gcc-lib/ia64-linux/3.3.5/collect2 -static
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crt1.o
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crti.o
/usr/lib/gcc-lib/ia64-linux/3.3.5/crtbegin.o
5 -L/usr/lib/gcc-lib/ia64-linux/3.3.5
-L/usr/lib/gcc-lib/ia64-linux/3.3.5/../../..
hello.o
function.o
--start-group
10 -lgcc
-lgcc_eh
-lunwind
-lc
--end-group
15 /usr/lib/gcc-lib/ia64-linux/3.3.5/crtend.o
/usr/lib/gcc-lib/ia64-linux/3.3.5/../../../crtn.o
Перше, що ви помічаєте, це те, що викликається програма під назвою collect2. Це проста обгортка навколо ld, яка використовується всередині gcc.
Наступне, що ви помітите, це об'єктні файли, що починаються з crt, який вказується на компонувальник. Ці функції надаються gcc і системними бібліотеками і містять код, необхідний для запуску програми. Насправді функція main () не перша, яка викликається під час запуску програми, а функція під назвою _start, яка знаходиться в об'єктних файлах crt. Ця функція виконує деякі загальні налаштування, про які програмістам додатків не потрібно турбуватися.
Ієрархія шляху досить складна, але по суті ми можемо бачити, що останній крок полягає в зв'язуванні деяких додаткових об'єктних файлів, а саме
crt1.o: надано системними бібліотеками (libc) цей об'єктний файл містить функцію_start, яка насправді є першим, що викликається в програмі.crti.o: надається системними бібліотекамиcrtbegino.crtavereso.crtendo.crtno.
Про те, як вони використовуються для запуску програми, ми обговоримо трохи пізніше.
Далі ви можете побачити, що ми пов'язуємо в наших двох об'єктних файлах, hello.o та function.o. Після цього ми вказуємо деякі додаткові бібліотеки з прапорами -l. Ці бібліотеки є специфічними для системи і необхідні для кожної програми. Основним з них є -lc, який приносить в бібліотеці C, яка має всі загальні функції, такі як printf ().
Після цього ми знову пов'язуємо ще деякі системні об'єктні файли, які роблять деяку очистку після виходу програм.
Хоча деталі складні, концепція прямо вперед. Всі об'єктні файли будуть пов'язані між собою в один виконуваний файл, готовий до запуску!
Ми розглянемо більш детальну інформацію про виконуваний файл у короткому майбутньому, але ми можемо зробити деяку перевірку аналогічно об'єктним файлам, щоб побачити, що сталося.
1 ianw@lime:~/programs/csbu/wk7/code$ gcc -o program hello.c function.c
ianw@lime:~/programs/csbu/wk7/code$ readelf --symbols ./program
Symbol table '.dynsym' contains 11 entries:
5 Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 6000000000000de0 0 OBJECT GLOBAL DEFAULT ABS _DYNAMIC
2: 0000000000000000 176 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2 (2)
3: 600000000000109c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
10 4: 0000000000000000 704 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.2 (2)
5: 600000000000109c 0 NOTYPE GLOBAL DEFAULT ABS _edata
6: 6000000000000fe8 0 OBJECT GLOBAL DEFAULT ABS _GLOBAL_OFFSET_TABLE_ 7: 60000000000010b0 0 NOTYPE GLOBAL DEFAULT ABS _end
8: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
9: 0000000000000000 544 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2 (2)
15 10: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
Symbol table '.symtab' contains 127 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
20 1: 40000000000001c8 0 SECTION LOCAL DEFAULT 1
2: 40000000000001e0 0 SECTION LOCAL DEFAULT 2
3: 4000000000000200 0 SECTION LOCAL DEFAULT 3
4: 4000000000000240 0 SECTION LOCAL DEFAULT 4
5: 4000000000000348 0 SECTION LOCAL DEFAULT 5
25 6: 40000000000003d8 0 SECTION LOCAL DEFAULT 6
7: 40000000000003f0 0 SECTION LOCAL DEFAULT 7
8: 4000000000000410 0 SECTION LOCAL DEFAULT 8
9: 4000000000000440 0 SECTION LOCAL DEFAULT 9
10: 40000000000004a0 0 SECTION LOCAL DEFAULT 10
30 11: 40000000000004e0 0 SECTION LOCAL DEFAULT 11
12: 40000000000005e0 0 SECTION LOCAL DEFAULT 12
13: 4000000000000b00 0 SECTION LOCAL DEFAULT 13
14: 4000000000000b40 0 SECTION LOCAL DEFAULT 14
15: 4000000000000b60 0 SECTION LOCAL DEFAULT 15
35 16: 4000000000000bd0 0 SECTION LOCAL DEFAULT 16
17: 4000000000000ce0 0 SECTION LOCAL DEFAULT 17
18: 6000000000000db8 0 SECTION LOCAL DEFAULT 18
19: 6000000000000dd0 0 SECTION LOCAL DEFAULT 19
20: 6000000000000dd8 0 SECTION LOCAL DEFAULT 20
40 21: 6000000000000de0 0 SECTION LOCAL DEFAULT 21
22: 6000000000000fc0 0 SECTION LOCAL DEFAULT 22
23: 6000000000000fd0 0 SECTION LOCAL DEFAULT 23
24: 6000000000000fe0 0 SECTION LOCAL DEFAULT 24
25: 6000000000000fe8 0 SECTION LOCAL DEFAULT 25
45 26: 6000000000001040 0 SECTION LOCAL DEFAULT 26
27: 6000000000001080 0 SECTION LOCAL DEFAULT 27
28: 60000000000010a0 0 SECTION LOCAL DEFAULT 28
29: 60000000000010a8 0 SECTION LOCAL DEFAULT 29
30: 0000000000000000 0 SECTION LOCAL DEFAULT 30
50 31: 0000000000000000 0 SECTION LOCAL DEFAULT 31
32: 0000000000000000 0 SECTION LOCAL DEFAULT 32
33: 0000000000000000 0 SECTION LOCAL DEFAULT 33
34: 0000000000000000 0 SECTION LOCAL DEFAULT 34
35: 0000000000000000 0 SECTION LOCAL DEFAULT 35
55 36: 0000000000000000 0 SECTION LOCAL DEFAULT 36
37: 0000000000000000 0 SECTION LOCAL DEFAULT 37
38: 0000000000000000 0 SECTION LOCAL DEFAULT 38
39: 0000000000000000 0 SECTION LOCAL DEFAULT 39
40: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
60 41: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
42: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
43: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
44: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
45: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
65 46: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
47: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
48: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
49: 0000000000000000 0 FILE LOCAL DEFAULT ABS <built-in>
50: 0000000000000000 0 FILE LOCAL DEFAULT ABS abi-note.S
70 51: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
52: 0000000000000000 0 FILE LOCAL DEFAULT ABS abi-note.S
53: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
54: 0000000000000000 0 FILE LOCAL DEFAULT ABS abi-note.S
55: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
75 56: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
57: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
58: 0000000000000000 0 FILE LOCAL DEFAULT ABS <built-in>
59: 0000000000000000 0 FILE LOCAL DEFAULT ABS abi-note.S
60: 0000000000000000 0 FILE LOCAL DEFAULT ABS init.c
80 61: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
62: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
63: 0000000000000000 0 FILE LOCAL DEFAULT ABS initfini.c
64: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
65: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
85 66: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
67: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
68: 0000000000000000 0 FILE LOCAL DEFAULT ABS <built-in>
69: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
70: 4000000000000670 128 FUNC LOCAL DEFAULT 12 gmon_initializer
90 71: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
72: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
73: 0000000000000000 0 FILE LOCAL DEFAULT ABS initfini.c
74: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
75: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
95 76: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
77: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
78: 0000000000000000 0 FILE LOCAL DEFAULT ABS <built-in>
79: 0000000000000000 0 FILE LOCAL DEFAULT ABS /build/buildd/glibc-2.3.2
80: 0000000000000000 0 FILE LOCAL DEFAULT ABS auto-host.h
100 81: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
82: 0000000000000000 0 FILE LOCAL DEFAULT ABS <built-in>
83: 6000000000000fc0 0 NOTYPE LOCAL DEFAULT 22 __CTOR_LIST__
84: 6000000000000fd0 0 NOTYPE LOCAL DEFAULT 23 __DTOR_LIST__
85: 6000000000000fe0 0 NOTYPE LOCAL DEFAULT 24 __JCR_LIST__
105 86: 6000000000001088 8 OBJECT LOCAL DEFAULT 27 dtor_ptr
87: 40000000000006f0 128 FUNC LOCAL DEFAULT 12 __do_global_dtors_aux
88: 4000000000000770 128 FUNC LOCAL DEFAULT 12 __do_jv_register_classes
89: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
90: 6000000000001090 4 OBJECT LOCAL DEFAULT 27 i
110 91: 0000000000000000 0 FILE LOCAL DEFAULT ABS function.c
92: 6000000000001098 4 OBJECT LOCAL DEFAULT 27 i
93: 0000000000000000 0 FILE LOCAL DEFAULT ABS auto-host.h
94: 0000000000000000 0 FILE LOCAL DEFAULT ABS <command line>
95: 0000000000000000 0 FILE LOCAL DEFAULT ABS <built-in>
115 96: 6000000000000fc8 0 NOTYPE LOCAL DEFAULT 22 __CTOR_END__
97: 6000000000000fd8 0 NOTYPE LOCAL DEFAULT 23 __DTOR_END__
98: 6000000000000fe0 0 NOTYPE LOCAL DEFAULT 24 __JCR_END__
99: 6000000000000de0 0 OBJECT GLOBAL DEFAULT ABS _DYNAMIC
100: 4000000000000a70 144 FUNC GLOBAL HIDDEN 12 __do_global_ctors_aux
120 101: 6000000000000dd8 0 NOTYPE GLOBAL DEFAULT ABS __fini_array_end
102: 60000000000010a8 8 OBJECT GLOBAL HIDDEN 29 __dso_handle
103: 40000000000009a0 208 FUNC GLOBAL DEFAULT 12 __libc_csu_fini
104: 0000000000000000 176 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2
105: 40000000000004a0 32 FUNC GLOBAL DEFAULT 10 _init
125 106: 4000000000000850 128 FUNC GLOBAL DEFAULT 12 function
107: 40000000000005e0 144 FUNC GLOBAL DEFAULT 12 _start
108: 6000000000001094 4 OBJECT GLOBAL DEFAULT 27 global
109: 6000000000000dd0 0 NOTYPE GLOBAL DEFAULT ABS __fini_array_start
110: 40000000000008d0 208 FUNC GLOBAL DEFAULT 12 __libc_csu_init
130 111: 600000000000109c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
112: 40000000000007f0 96 FUNC GLOBAL DEFAULT 12 main
113: 6000000000000dd0 0 NOTYPE GLOBAL DEFAULT ABS __init_array_end
114: 6000000000000dd8 0 NOTYPE WEAK DEFAULT 20 data_start
115: 4000000000000b00 32 FUNC GLOBAL DEFAULT 13 _fini
135 116: 0000000000000000 704 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.2
117: 600000000000109c 0 NOTYPE GLOBAL DEFAULT ABS _edata
118: 6000000000000fe8 0 OBJECT GLOBAL DEFAULT ABS _GLOBAL_OFFSET_TABLE_
119: 60000000000010b0 0 NOTYPE GLOBAL DEFAULT ABS _end
120: 6000000000000db8 0 NOTYPE GLOBAL DEFAULT ABS __init_array_start
140 121: 6000000000001080 4 OBJECT GLOBAL DEFAULT 27 _IO_stdin_used
122: 60000000000010a0 8 OBJECT GLOBAL DEFAULT 28 __libc_ia64_register_back
123: 6000000000000dd8 0 NOTYPE GLOBAL DEFAULT 20 __data_start
124: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
125: 0000000000000000 544 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
145 126: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
Деякі речі, які слід зазначити
- Примітка. Я побудував виконуваний файл «легкий» спосіб!
- Див. Є дві таблиці символів:
dynsymіsymtab. Ми пояснюємо, як працюють символиdynsymнайближчим часом, але помічаємо, що деякі з них версії з символом@. - Зверніть увагу на багато символів, які були включені з додаткових об'єктних файлів. Багато з них починаються з
__, щоб уникнути зіткнення з будь-якими іменами, які може вибрати програміст. Прочитайте і виберіть символи, про які ми згадували раніше, з об'єктних файлів і подивіться, чи змінилися вони якимось чином.