Як отримати вихід асемблера з джерела C / C ++ в gcc?


379

Як це зробити?

Якщо я хочу проаналізувати, як щось збирається, як мені отримати код, що випускається?


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

Відповіді:


421

Використовуйте -Sопцію gcc (або g ++).

gcc -S helloworld.c

Це запустить препроцесор (cpp) над helloworld.c, виконає початкову компіляцію, а потім зупинить перед запуском асемблера.

За замовчуванням це виведе файл helloworld.s. Вихідний файл все ще можна встановити за допомогою -oпараметра.

gcc -S -o my_asm_output.s helloworld.c

Звичайно, це працює, лише якщо у вас є першоджерело. Альтернативою, якщо у вас є лише результат об'єктного файлу, є використання objdump, встановивши --disassembleпараметр (або -dдля скороченої форми).

objdump -S --disassemble helloworld > helloworld.dump

Цей параметр найкраще працює, якщо для об’єктного файлу ( -gпід час компіляції) включена опція налагодження, а файл не був знятий.

Біг file helloworldдасть вам певну інформацію про рівень деталізації, який ви отримаєте, використовуючи objdump.


3
Хоча це правильно, я знайшов результати з відповіді Cr McDonough більш корисними.
Rhys Ulerich

3
додаткове використання: objdump -M intel -S - демонтаж helloworld> helloworld.dump, щоб отримати дамп об'єкта в синтаксисі intel, сумісному з Nasm на Linux.
touchStone

2
Якщо у вас є одна функція оптимізації / перевірки, ви можете спробувати онлайн інтерактивні компілятори C ++, тобто godbolt
fiorentinoing

1
@touchStone: GAS .intel_syntaxє НЕ сумісним з NASM . Це більше схоже на MASM (наприклад mov eax, symbol, це навантаження, на відміну від NASM, де це mov r32, imm32адреса), але не повністю сумісний з MASM. Я настійно рекомендую це як гарний формат для читання, особливо якщо ви хочете писати в синтаксисі NASM. objdump -drwC -Mintel | lessабо gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | lessкорисні. (Див. Також Як видалити "шум" з виходу складання GCC / clang? ). -masm=intelпрацює і з клангом.
Пітер Кордес

3
Краще використовуватиgcc -O -fverbose-asm -S
Базиле Старинкевич

173

Це створить код складання з переплетеним кодом C + номери рядків, щоб легше побачити, які рядки генерують який код:

# create assembler code:
g++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst

Знайдено в Алгоритмах для програмістів , сторінка 3 (що становить загальну 15-ту сторінку PDF).


3
(Це насправді на сторінці (пронумеровано) 3 (це 15-а сторінка PDF))
Grumdrig

1
На жаль, asв OS X не знають цих прапорів. Якби це все-таки було, ви, ймовірно, могли б однорядково використовувати це -Waдля передачі опцій as.
Grumdrig

23
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lstце була б коротка версія цього.
legends2k

4
Ви також можете використовувати gcc -c -g -Wa,-ahl=test.s test.cабоgcc -c -g -Wa,-a,-ad test.c > test.txt
phuclv

1
Повідомлення в блозі, де пояснюється це більш докладно, включаючи версію з однією командою, як легенди та Лу'у, розміщені. Але чому -O0? Це багато вантажів / магазинів, які важко відстежувати значення, і нічого не говорить про те, наскільки ефективним буде оптимізований код.
Пітер Кордес

51

Наступний командний рядок - із блогу Крістіана Гарбіна

g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt

Я запустив G ++ з вікна DOS на Win-XP, проти процедури, яка містить неявний склад

c:\gpp_code>g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
horton_ex2_05.cpp: In function `int main()':
horton_ex2_05.cpp:92: warning: assignment to `int' from `double'

Вихід складається в згенерованому згенерованому коді, який перебуває з початковим кодом C ++ (код C ++ відображається як коментарі в створеному потоці ASM)

  16:horton_ex2_05.cpp **** using std::setw;
  17:horton_ex2_05.cpp ****
  18:horton_ex2_05.cpp **** void disp_Time_Line (void);
  19:horton_ex2_05.cpp ****
  20:horton_ex2_05.cpp **** int main(void)
  21:horton_ex2_05.cpp **** {
 164                    %ebp
 165                            subl $128,%esp
?GAS LISTING C:\DOCUME~1\CRAIGM~1\LOCALS~1\Temp\ccx52rCc.s
166 0128 55                    call ___main
167 0129 89E5          .stabn 68,0,21,LM2-_main
168 012b 81EC8000      LM2:
168      0000
169 0131 E8000000      LBB2:
169      00
170                    .stabn 68,0,25,LM3-_main
171                    LM3:
172                            movl $0,-16(%ebp)

@Paladin - Не обов’язково. ОП збирався отримати еквівалент виводу асемблера вихідного коду C / C ++. Це отримує Лістинг, який, я згоден, є кориснішим для розуміння того, що робить компілятор та оптимізатор. Але це призведе до того, що сам асемблер перетвориться на барф, оскільки він не чекає номерів рядків, а складених байтів від тота, що залишився від інструкцій по збірці.
Джессі Чісгольм

Використовуйте принаймні -O2або будь-які варіанти оптимізації, які ви фактично використовуєте під час створення свого проекту, якщо ви хочете побачити, як gcc оптимізує ваш код. (Або якщо ви використовуєте LTO, як слід, тоді вам доведеться розібрати вихід лінкера, щоб побачити, що ви дійсно отримаєте.)
Пітер Кордес

27

Використовуйте перемикач -S

g++ -S main.cpp

або також з gcc

gcc -S main.c

Переконайтесь також у цьому


7
Перевірте поширені запитання: "Також ідеально задавати і відповідати на власне питання програмування". Справа в тому, що зараз StackOverflow містить запитання і запитання як ресурс для інших.
Стів Джессоп,

І, можливо, хтось інший прийде і здивує вас кращою відповіддю, хоча моє часом може бути трохи багатослівним ...
PhirePhly

Є навіть кнопка відповіді на власну кнопку запитання.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

13

Якщо те, що ви хочете побачити, залежить від зв'язування виводу, тоді також допоміжний вивідний об'єктний файл / виконуваний файл може бути корисним на додаток до вищезгаданого gcc -S. Ось дуже корисний сценарій Лорен Меррітт, який перетворює синтаксис objdump за замовчуванням у більш читаний синтаксис nasm:

#!/usr/bin/perl -w
$ptr='(BYTE|WORD|DWORD|QWORD|XMMWORD) PTR ';
$reg='(?:[er]?(?:[abcd]x|[sd]i|[sb]p)|[abcd][hl]|r1?[0-589][dwb]?|mm[0-7]|xmm1?[0-9])';
open FH, '-|', '/usr/bin/objdump', '-w', '-M', 'intel', @ARGV or die;
$prev = "";
while(<FH>){
    if(/$ptr/o) {
        s/$ptr(\[[^\[\]]+\],$reg)/$2/o or
        s/($reg,)$ptr(\[[^\[\]]+\])/$1$3/o or
        s/$ptr/lc $1/oe;
    }
    if($prev =~ /\t(repz )?ret / and
       $_ =~ /\tnop |\txchg *ax,ax$/) {
       # drop this line
    } else {
       print $prev;
       $prev = $_;
    }
}
print $prev;
close FH;

Я підозрюю, що це також можна використовувати на виході gcc -S.


2
Тим не менш, цей скрипт є брудним злом, який не повністю перетворює синтаксис. Наприклад mov eax,ds:0x804b794, не дуже NASMish. Крім того , іноді це просто смуги корисної інформації: movzx eax,[edx+0x1]залишає читач вгадати операнд чи пам'ять була byteабо word.
Руслан

Щоб розібрати в першу чергу синтаксис NASM, використовуйте Agner Fog'sobjconv . Ви можете змусити його розбирати stdout з вихідним файлом = /dev/stdout, так що ви можете передати на lessперегляд. Є також ndisasm, але він розбирає лише плоскі двійкові файли і не знає про файли об'єктів (ELF / PE).
Пітер Кордес

9

Як усі зазначали, використовуйте -Sваріант для GCC. Я також хотів би додати, що результати можуть відрізнятися (дико!) Залежно від того, додаєте ви чи ні варіанти оптимізації ( -O0для жодної, -O2для агресивної оптимізації).

Зокрема, для архітектур RISC компілятор часто трансформує код майже невпізнанним шляхом оптимізації. Вражаюче та захоплююче дивитись на результати!


9

Ну, як всі сказали, використовуйте варіант -S. Якщо ви використовуєте параметр -save-temps, ви також можете отримати попередньо оброблений файл ( .i), збірний файл ( .s) та об’єктний файл (*. O). (отримати кожен з них за допомогою -E, -S та -c.)


8

Як згадувалося раніше, подивіться на прапор -S.

Варто також переглянути сімейство прапорів "-fdump-tree", зокрема "-fdump-tree-all", що дозволяє побачити деякі проміжні форми gcc. Вони часто можуть бути більш читабельними, ніж асемблер (принаймні для мене), і ви зможете побачити, як виконуються передачі для оптимізації.



8

Я не бачу такої можливості серед відповідей, ймовірно, тому, що це питання з 2008 року, але в 2018 році ви можете використовувати Інтернет-сайт Метта Голдбольта https://godbolt.org

Ви також можете локально git клонувати та запускати його проект https://github.com/mattgodbolt/compiler-explorer


8

-save-temps

Про це згадувалося на https://stackoverflow.com/a/17083009/895245, але дозвольте мені додатково пояснити це.

Великою перевагою цього варіанту -Sє те, що його дуже легко додати до будь-якого сценарію збірки, не втручаючись у саму збірку.

Коли ви робите:

gcc -save-temps -c -o main.o main.c

main.c

#define INC 1

int myfunc(int i) {
    return i + INC;
}

і тепер, крім звичайного виводу main.o, поточний робочий каталог також містить такі файли:

  • main.i є бонусом і містить попередньо оброблений файл:

    # 1 "main.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 31 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 32 "<command-line>" 2
    # 1 "main.c"
    
    
    int myfunc(int i) {
        return i + 1;
    }
  • main.s містить бажану сформовану збірку:

        .file   "main.c"
        .text
        .globl  myfunc
        .type   myfunc, @function
    myfunc:
    .LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $1, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE0:
        .size   myfunc, .-myfunc
        .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
        .section    .note.GNU-stack,"",@progbits

Якщо ви хочете зробити це для великої кількості файлів, спробуйте використовувати замість цього:

 -save-temps=obj

який зберігає проміжні файли у той самий каталог, що і -o вихідний об'єкт, а не поточний робочий каталог, уникаючи потенційних конфліктів базових імен.

Ще одна цікава річ у цьому варіанті, якщо ви додасте -v:

gcc -save-temps -c -o main.o -v main.c

він фактично показує явні файли, що використовуються замість некрасивих часописів під /tmp, тому легко точно знати, що відбувається, що включає етапи попередньої обробки / компіляції / складання:

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s

Тестовано в Ubuntu 19.04 amd64, GCC 8.3.0.




3

Вихід цих товари

Ось етапи перегляду / друку коду складання будь-якої програми C у вашій Windows

консоль / термінал / командний рядок:

  1. Напишіть програму C у редакторі коду С, як блокові коди, і збережіть її із розширенням .c

  2. Складіть і запустіть його.

  3. Після успішного запуску перейдіть до папки, де ви встановили компілятор gcc, і дайте

    наступна команда для отримання файлу ".s" файлу ".c"

    C: \ gcc> gcc -S повний шлях до файлу C ВВЕДЕННЯ

    Приклад команди (як у моєму випадку)

    C: \ gcc> gcc -SD: \ Aa_C_Certified \ alternate_letters.c

    Це виводить '.s' файл вихідного файлу '.c'

4. Після цього введіть таку команду

C; \ gcc> cpp filename.s ВВОД

Приклад команди (як у моєму випадку)

C; \ gcc> cpp alternate_letters.s

Це дозволить надрукувати / вивести весь код мови складання вашої програми C.


2

Використовуйте опцію "-S". Він відображає висновок складання в терміналі.


Для відображення в терміналі використовуйте gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less. -Sсамостійно творить foo.s.
Пітер Кордес

2

нещодавно мені хотілося дізнатись про складання кожної функції в програмі,
ось як я це зробив.

$ gcc main.c                      // main.c source file
$ gdb a.exe                       // gdb a.out in linux
  (gdb) disass main               // note here main is a function
                                  // similary it can be done for other functions

2

Ось рішення для C за допомогою gcc:

gcc -S program.c && gcc program.c -o output
  1. Тут перша частина зберігає вихідний збір програми в тому самому імені файлу, що і Program, але зі зміненим розширенням .s ви можете відкрити його як будь-який звичайний текстовий файл.

  2. Друга частина тут збирає вашу програму для фактичного використання та створює виконуваний файл для вашої програми із вказаним ім'ям файлу.

Program.c використане вище назва вашої програми і виведення є ім'я виконуваного ви хочете створити.

До речі, це моє перше повідомлення в StackOverFlow: -}

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