Использование GCC для создания читаемой сборки?


256

Мне было интересно, как использовать GCC в исходном файле C для вывода мнемонической версии машинного кода, чтобы я мог видеть, во что компилируется мой код. Вы можете сделать это с Java, но я не смог найти способ с GCC.

Я пытаюсь переписать метод C на ассемблере и посмотреть, как это делает GCC, было бы очень полезно.


25
обратите внимание, что «байт-код» обычно означает код, потребляемый виртуальной машиной, такой как JVM или CLR .NET. Выходные данные 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
@ Джеймс Да, поставка -Mintel.
— fuz

106

Если вы дадите GCC флаг -fverbose-asm, он будет

Поместите дополнительную информацию комментариев в сгенерированный код сборки, чтобы сделать его более читабельным.

[...] Добавленные комментарии включают в себя:

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

Но тогда я потерял бы все переключатели, используемые для objdump- objdump -drwCS -Mintelтак как я могу использовать что-то вроде verboseс objdump? Так что я могу иметь комментарии в коде asm, как -fverbose-asmв gcc?
— пастух

1
@ Хердсман: ты не можешь. -fverbose-asmДобавляется дополнительный материал в виде комментариев в синтаксисе asm вывода, а не директив, которые добавят в .oфайл что-то дополнительное . Это все отбрасывается во время сборки. Посмотрите на вывод asm компилятора вместо дизассемблирования, например, на godbolt.org, где вы можете легко сопоставить его с линией исходного текста с помощью наведения мыши и цветовой подсветки соответствующих строк исходного / асм. Как убрать "шум" из вывода сборки 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/Overages-Options.html#Overages-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. Интел это просто, -masm=intelа как на счет остальных?
— Z бозон

Я предполагаю, что это объяснено здесь stackoverflow.com/a/38552509/2542702
— Z бозон

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но будьте осторожны: вы получите сотни файлов дампа.

Кстати, GCC расширяется через плагины или с помощью MELT (высокоуровневый язык, специфичный для домена, для расширения GCC; от которого я отказался в 2017 году)


возможно упомяните, что вывод будет в source.s, так как много людей ожидали бы распечатку на консоли.
— RubenLaguna

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

2
Забыл упомянуть: если вы ищете «похожий на источник, но без шума сохранения / перезагрузки после каждой строки источника», то -Ogэто даже лучше, чем -O1. Это означает «оптимизировать для отладки» и создает asm без слишком многих хитрых / трудных для выполнения оптимизаций, которые делают все, что говорит источник. Он был доступен с gcc4.8, но в clang 3.7 его до сих пор нет. ИДК, если они решили против этого или что.
— Питер Кордес

19

Вы можете использовать GDB для этого, как objdump.

Этот отрывок взят из http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


Вот пример, показывающий смешанный источник + сборка для Intel x86:

  (gdb) disas / m main
Дамп кода ассемблера для функции main:
5 {
0x08048330: push% ebp
0x08048331: mov% esp,% ebp
0x08048333: суб $ 0x8,% esp
0x08048336: и $ 0xfffffff0,% esp
0x08048339: суб $ 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 в командной строке, чтобы увидеть больше помощи
— Хартмут Шорриг

0

используйте -Wa, -adhln в качестве опции на gcc или g ++ для вывода вывода списка на стандартный вывод.

-Wa, ... для параметров командной строки для ассемблера (выполняется в gcc / g ++ после компиляции C / ++). Он вызывается как внутренне (as.exe в Windows). Видеть

> как --help

в командной строке, чтобы увидеть больше помощи для инструмента ассемблера внутри gcc

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.