Использование 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.