Використання GCC для виготовлення читабельної збірки?


256

Мені було цікаво, як використовувати GCC у своєму вихідному файлі C, щоб скинути мнемонічну версію машинного коду, щоб я міг бачити, до чого збирається мій код. Це можна зробити за допомогою Java, але мені не вдалося знайти спосіб за допомогою GCC.

Я намагаюся переписати метод С на зборах і бачу, як GCC це робить, це буде великою допомогою.


25
зауважте, що "байт-код" зазвичай означає код, спожитий VM, наприклад, JVM або .NET CLR. Вихід GCC краще називати "машинним кодом", "машинною мовою" або "мовою складання"
Хав'єр

2
Я додав відповідь, використовуючи godbolt, оскільки це дуже потужний інструмент для швидкого експерименту з тим, як різні варіанти впливають на формування коду.
Шафік Ягмур



Для отримання додаткових порад щодо того, як зробити вихідний сигнал ASM читабельним, див. Також: Як видалити "шум" від результатів складання GCC / clang?
Пітер Кордес

Відповіді:


335

Якщо ви компілюєте з символами налагодження, ви можете використовувати objdumpдля більш зрозумілого розбирання.

>objdump --help
[...]
-S, --source             Intermix source code with disassembly
-l, --line-numbers       Include line numbers and filenames in output

objdump -drwC -Mintel гарний:

  • -rвідображає назви символів на переїзді (щоб ви бачили putsв callінструкції нижче)
  • -R показує динамічні посилання переїздів / імен символів (корисно у спільних бібліотеках)
  • -C назви символів C ++
  • -w "широкий" режим: він не обертає рядки байтів машинного коду
  • -Mintel: використовуйте GAS / binutils MASM-подібний .intel_syntax noprefixсинтаксис замість AT&T
  • -S: переплетення вихідних ліній з розбиранням.

Ви можете поставити щось подібне alias disas="objdump -drwCS -Mintel"до свого~/.bashrc


Приклад:

> gcc -g -c test.c
> objdump -d -M intel -S test.o

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
#include <stdio.h>

int main(void)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   83 e4 f0                and    esp,0xfffffff0
   6:   83 ec 10                sub    esp,0x10
    puts("test");
   9:   c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
  10:   e8 fc ff ff ff          call   11 <main+0x11>

    return 0;
  15:   b8 00 00 00 00          mov    eax,0x0
}
  1a:   c9                      leave  
  1b:   c3                      ret

3
Чи є перемикач, щоб захопити лише інструкції Intel?
Джеймс

3
Все це інструкції Intel, оскільки вони працюють на процесорах Intel: D.
Тото

12
@toto Я думаю, що він має на увазі синтаксис Intel замість синтаксису AT&T
Amok

7
Можна відмовитися від проміжного файлу об'єкта з допомогою, використовуючи послідовність комутації -Wa,-adhln -g to gcc. Це передбачає, що монтажник - це газ, і це не завжди може бути так.
Марк Батлер

8
@James Так, постачання -Mintel.
fuz

106

Якщо ви подасте прапор GCC-fverbose-asm , він буде

Помістіть додаткову інформацію про коментарі в створений код складання, щоб зробити його більш зрозумілим.

[...] До доданих коментарів належать:

  • інформація про версію компілятора та параметри командного рядка,
  • рядки вихідного коду, пов'язані з інструкцією по збірці, у формі FILENAME: LINENUMBER: CONTENT OF LINE,
  • підказки, які вирази високого рівня відповідають різним операндам інструкцій по збірці.

Але тоді, я б втратив ключ , який використовується для objdump- objdump -drwCS -Mintel, так як я можу використовувати що - щось на зразок verboseз objdump? Так що я можу мати коментарі в ASM-коді, як і -fverbose-asmв gcc?
Пастух

1
@Herdsman: ти не можеш. Додатковий -fverbose-asmдодаток додається у вигляді коментарів до синтаксису виводу ASM, а не директив, які містять у .oфайлі щось зайве . Це все відкидається під час збирання. Подивіться на вихід ASM компілятора замість розбирання, наприклад, на godbolt.org, де ви можете легко зіставити його з вихідною лінією за допомогою миші та кольорового виділення відповідних ліній джерела / asm. Як видалити "шум" з виходу збірки GCC / clang?
Пітер Кордес

75

Використовуйте перемикач -S (примітка: капітал S) до GCC, і він передасть код складання у файл з розширенням .s. Наприклад, наступна команда:

gcc -O2 -S foo.c

залишить згенерований код складання у файлі foo.s.

Зірвано прямо з http://www.delorie.com/djgpp/v2faq/faq8_20.html (але видалення помилкове -c)


35
Не слід змішувати -c та -S, використовувати лише один із них. У цьому випадку одна переважає іншу, ймовірно, залежно від порядку, в якому вони використовуються.
Адам Розенфілд

4
@AdamRosenfield Будь-яка посилання про "не слід змішувати -c та -S"? Якщо це правда, ми можемо нагадати автору та відредагувати його.
Тоні

5
@Tony: gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options "Ви можете використовувати ... один із варіантів -c, -S або -E, щоб сказати, де зупинити gcc. "
Нейт Елдредж

1
Якщо ви хочете всі проміжні виходи, використовуйте gcc -march=native -O3 -save-temps. Ви все ще можете використовувати -cдля зупинки на створенні об'єкт-файлів, не намагаючись зв’язати, чи будь-що інше.
Пітер Кордес

2
-save-tempsЦікаво, оскільки він за один раз скидає точний код, що генерується кодом, тоді як інший варіант викликає компілятор із -Sзасобами компіляції двічі та, можливо, з різними параметрами. Але -save-temps викидає все в поточний каталог, який є безладно. Схоже, це скоріше призначений як варіант налагодження для GCC, ніж інструмент для перевірки вашого коду.
Стефан Гурішон

50

Використання -Sперемикання на GCC в системах на базі x86 створює скидання синтаксису AT&T за замовчуванням, яке можна вказати за допомогою -masm=attперемикача, наприклад:

gcc -S -masm=att code.c

Оскільки, якщо ви хочете створити дамп у синтаксисі Intel, ви можете використовувати -masm=intelкомутатор так:

gcc -S -masm=intel code.c

(Обидва виробляють скидки code.cу свій синтаксис у файл code.sвідповідно)

Для отримання подібних ефектів з objdump, ви хочете скористатися прикладом --disassembler-options= intel/ attswitch (із скидами коду для ілюстрації відмінностей у синтаксисі):

 $ objdump -d --disassembler-options=att code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    0x4(%esp),%ecx
 80483c8:   83 e4 f0                and    $0xfffffff0,%esp
 80483cb:   ff 71 fc                pushl  -0x4(%ecx)
 80483ce:   55                      push   %ebp
 80483cf:   89 e5                   mov    %esp,%ebp
 80483d1:   51                      push   %ecx
 80483d2:   83 ec 04                sub    $0x4,%esp
 80483d5:   c7 04 24 b0 84 04 08    movl   $0x80484b0,(%esp)
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    $0x0,%eax
 80483e6:   83 c4 04                add    $0x4,%esp 
 80483e9:   59                      pop    %ecx
 80483ea:   5d                      pop    %ebp
 80483eb:   8d 61 fc                lea    -0x4(%ecx),%esp
 80483ee:   c3                      ret
 80483ef:   90                      nop

і

$ objdump -d --disassembler-options=intel code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    ecx,[esp+0x4]
 80483c8:   83 e4 f0                and    esp,0xfffffff0
 80483cb:   ff 71 fc                push   DWORD PTR [ecx-0x4]
 80483ce:   55                      push   ebp
 80483cf:   89 e5                   mov    ebp,esp
 80483d1:   51                      push   ecx
 80483d2:   83 ec 04                sub    esp,0x4
 80483d5:   c7 04 24 b0 84 04 08    mov    DWORD PTR [esp],0x80484b0
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    eax,0x0
 80483e6:   83 c4 04                add    esp,0x4
 80483e9:   59                      pop    ecx
 80483ea:   5d                      pop    ebp
 80483eb:   8d 61 fc                lea    esp,[ecx-0x4]
 80483ee:   c3                      ret    
 80483ef:   90                      nop

Те , що ... gcc -S -masm=intel test.cточно не робота для мене, я отримав деяку помісь Intel і AT & T синтаксис , як це: mov %rax, QWORD PTR -24[%rbp]замість цього: movq -24(%rbp), %rax.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

1
Гарна порада. Слід зазначити, що це також працює при виконанні паралельного виводу .oфайлів та ASM, тобто через-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
underscore_d

Можна використати -Mваріант, він такий же, як --disassembler-optionsі набагато коротший, наприкладobjdump -d -M intel a.out | less -N
Eric Wang

34

godbolt є дуже корисним інструментом, у них у списку є лише компілятори C ++, але ви можете використовувати -x cпрапор, щоб змусити його ставитись до коду як C. Потім буде створено список складання для вашого коду поруч, а ви можете використовувати Colouriseопцію для генерації кольорові смуги, щоб візуально вказати, який вихідний код відображається в створеній збірці. Наприклад, наступний код:

#include <stdio.h>

void func()
{
  printf( "hello world\n" ) ;
}

використовуючи наступний командний рядок:

-x c -std=c99 -O3

і Colouriseстворив би наступне:

введіть тут опис зображення


Було б добре знати, як працюють фільтри godbolt: .LC0, .text, // та Intel. Intel легко, -masm=intelале як бути з рештою?
Z бозон

Я думаю, це пояснено тут stackoverflow.com/a/38552509/2542702
Z boson

godbolt дійсно підтримує C (разом з тоною інших мов, таких як Rust, D, Pascal ...). Це просто те, що є набагато менше компіляторів C, тому все ж краще використовувати компілятори C ++ за допомогою-x c
phuclv

23

Ви спробували gcc -S -fverbose-asm -O source.cпотім заглянути в створений source.sфайл асемблера?

Згенерований код асемблера переходить у source.s(ви можете перекрити його за допомогою -o асемблера-імені файлу ); -fverbose-asmопція задає компілятор випускати деякі асемблерні коментарі «пояснюють» згенерований код на асемблері. -OОпція задає компілятор для оптимізації трохи (це може оптимізувати більше з -O2або -O3).

Якщо ви хочете зрозуміти, що gccробиться, спробуйте пройти, -fdump-tree-allале будьте обережні: ви отримаєте сотні файлів дамп.

BTW, GCC - це розширюваний через плагіни або з MELT (мова для домену на високому рівні для розширення GCC; яку я відмовився у 2017 році)


можливо, зауважте, що вихід буде мати місце source.s, оскільки багато людей очікують роздруківки на консолі.
RubenLaguna

1
@ecerulm: -S -o-скидає на stdout. -masm=intelкорисно, якщо ви хочете використовувати синтаксис NASM / YASM. (але він використовує qword ptr [mem], а не просто qword, тому він більше схожий на Intel / MASM, ніж на NASM / YASM). gcc.godbolt.org виконує гарну роботу з пристосування дампа: необов'язково знімаючи рядки, що стосуються лише коментарів, невикористані мітки та директиви асемблера.
Пітер Кордес

2
Забув згадати: Якщо ви шукаєте "схожий на джерело, але без шуму зберігання / перезавантаження після кожного вихідного рядка", то -Ogце навіть краще, ніж -O1. Це означає "оптимізувати для налагодження" та робить asm без занадто багато складних / важкодослідних оптимізацій, які роблять усе, що каже джерело. Він доступний з gcc4.8, але в "clang 3.7" його ще немає. IDK, якщо вони вирішили проти цього чи що.
Пітер Кордес

19

Ви можете використовувати gdb для цього, як objdump.

Цей уривок взято з http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


Ось приклад, що показує змішане джерело + збірка для Intel x86:

  (gdb) катастрофа / м головна
Дамп коду асемблера для основної функції:
5 {
0x08048330: push% ebp
0x08048331: mov% esp,% ebp
0x08048333: sub $ 0x8,% esp
0x08048336: і $ 0xfffffff0,% esp
0x08048339: sub $ 0x10,% esp

6 printf ("Привіт. \ N");
0x0804833c: movl $ 0x8048440, (% esp)
0x08048343: дзвоніть 0x8048284 

7 повернення 0;
8}
0x08048348: mov $ 0x0,% eax
0x0804834d: відпустка
0x0804834e: ret

Кінець скиду асемблера.


А щоб переключити розбиральник GDB на синтаксис Intel, використовуйте set disassembly-flavor intelкоманду.
Руслан

13

Використовуйте перемикач -S (примітка: капітал S) до GCC, і він передасть код складання у файл з розширенням .s. Наприклад, наступна команда:

gcc -O2 -S -c foo.c


4

Я не давав знімати gcc, але у випадку g ++. Команда нижче працює для мене. -g для збирання налагодження і -Wa, -adhln передається ассемблеру для переліку з вихідним кодом

g ++ -g -Wa, -adhln src.cpp


Він працює і для gcc! -Wa, ... - це параметри командного рядка для частини асемблера (виконати в gcc / g ++ після компіляції C / ++). Він викликає внутрішньо (as.exe в Windows). Дивіться> як --help як командний рядок, щоб отримати додаткову допомогу
Hartmut Schorrig

0

використовувати -Wa, -adhln як варіант на gcc або g ++ для створення виводу з лістингу для stdout.

-Wa, ... - це параметри командного рядка для частини асемблера (виконати в gcc / g ++ після компіляції C / ++). Він викликає , як внутрішньо (as.exe в Windows). Побачити

> як - допомогти

як командний рядок, щоб отримати додаткову допомогу для інструмента асемблера всередині gcc

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.