Menggunakan GCC untuk menghasilkan perakitan yang dapat dibaca?

256

Saya bertanya-tanya bagaimana cara menggunakan GCC pada file sumber C saya untuk membuang versi mnemonik dari kode mesin sehingga saya bisa melihat kode apa yang sedang dikompilasi. Anda dapat melakukan ini dengan Java tetapi saya belum dapat menemukan cara dengan GCC.

Saya mencoba untuk menulis ulang metode C dalam perakitan dan melihat bagaimana GCC melakukannya akan sangat membantu.

James
sumber
25
perhatikan bahwa 'bytecode' biasanya berarti kode yang dikonsumsi oleh VM, seperti JVM atau .NET's CLR. Output dari GCC lebih baik disebut 'kode mesin', 'bahasa mesin', atau 'bahasa assembly'
Javier
2
Saya menambahkan jawaban menggunakan godbolt karena ini adalah alat yang sangat kuat untuk bereksperimen dengan cepat dengan bagaimana berbagai opsi memengaruhi pembuatan kode Anda.
Shafik Yaghmour
Kemungkinan duplikat dari Bagaimana Anda mendapatkan output assembler dari sumber C / C ++ di gcc?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Untuk tips lebih lanjut tentang membuat output asm dapat dibaca oleh manusia, lihat juga: Bagaimana menghapus "noise" dari output rakitan GCC / clang?
Peter Cordes

Jawaban:

335

Jika Anda mengompilasi dengan simbol debug, Anda dapat menggunakan objdumpuntuk menghasilkan pembongkaran yang lebih mudah dibaca.

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

objdump -drwC -Mintel itu bagus:

  • -r menunjukkan nama simbol pada relokasi (sehingga Anda akan melihat puts dalam callinstruksi di bawah ini)
  • -R menunjukkan relokasi / nama simbol yang menghubungkan dinamis (berguna untuk perpustakaan bersama)
  • -C demangles nama simbol C ++
  • -w adalah mode "lebar": ini tidak membungkus baris-kode byte mesin
  • -Mintel: gunakan GAS / binutils seperti MASM .intel_syntax noprefix sintaks alih-alih AT&T
  • -S: baris sumber interleave dengan pembongkaran.

Anda bisa memasukkan sesuatu seperti alias disas="objdump -drwCS -Mintel"di~/.bashrc


Contoh:

> 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
Bastien Léonard
sumber
3
Apakah ada saklar untuk hanya mengambil instruksi Intel?
James
3
Semua ini adalah instruksi Intel karena dijalankan pada prosesor Intel: D.
toto
12
@toto Saya pikir dia maksud sintaks Intel bukan AT&T sintaks
Amok
7
Dimungkinkan untuk melupakan file objek antara dengan menggunakan urutan switch -Wa,-adhln -g to gcc. Ini mengasumsikan bahwa assembler adalah gas dan ini mungkin tidak selalu demikian.
Marc Butler
8
@ Ya, pasokan -Mintel.
fuz
106

Jika kamu memberi GCC bendera -fverbose-asm, itu akan

Masukkan informasi komentar tambahan dalam kode perakitan yang dihasilkan agar lebih mudah dibaca.

[...] Komentar tambahan meliputi:

  • informasi tentang versi kompiler dan opsi baris perintah,
  • baris kode sumber yang terkait dengan instruksi perakitan, dalam bentuk FILENAME: LINENUMBER: CONTENT OF LINE,
  • petunjuk di mana ekspresi tingkat tinggi sesuai dengan berbagai operan instruksi perakitan.
Kasper
sumber
Tapi kemudian, saya akan kehilangan semua saklar yang digunakan untuk objdump- objdump -drwCS -Mintel, jadi bagaimana saya bisa menggunakan sesuatu seperti verbosedengan objdump? Sehingga saya dapat memiliki komentar dalam kode asm, seperti halnya -fverbose-asmdalam gcc?
Gembala
1
@ Herdsman: Anda tidak bisa. Hal-hal tambahan yang -fverbose-asmditambahkan adalah dalam bentuk komentar dalam sintaks asm dari output, bukan arahan yang akan memasukkan apa pun tambahan dalam .ofile. Semuanya dibuang pada waktu berkumpul. Lihatlah output asm compiler alih - alih disassembly, misalnya pada godbolt.org di mana Anda dapat dengan mudah mencocokkannya dengan baris sumber melalui mouseover dan penyorotan warna dari garis sumber / asm yang sesuai. Bagaimana cara menghapus "noise" dari output rakitan GCC / clang?
Peter Cordes
75

Gunakan -S (note: capital S) untuk beralih ke GCC, dan itu akan memancarkan kode assembly ke file dengan ekstensi .s. Misalnya, perintah berikut:

gcc -O2 -S foo.c

akan meninggalkan kode perakitan yang dihasilkan pada file foo.s.

Ripped langsung dari http://www.delorie.com/djgpp/v2faq/faq8_20.html (tetapi menghapus yang salah -c)

Andrew Keeton
sumber
35
Anda seharusnya tidak mencampur -c dan -S, hanya gunakan salah satunya saja. Dalam hal ini, satu menimpa yang lain, mungkin tergantung pada urutan penggunaannya.
Adam Rosenfield
4
@AdamRosenfield Ada referensi tentang 'tidak boleh dicampur -c dan -S'? Jika itu benar, kita mungkin harus mengingatkan penulis dan mengeditnya.
Tony
5
@Tony: gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options "Anda dapat menggunakan ... salah satu opsi -c, -S, atau -E untuk mengatakan di mana gcc akan berhenti. "
Nate Eldredge
1
Jika Anda ingin semua output menengah, gunakan gcc -march=native -O3 -save-temps. Anda masih dapat menggunakan -cuntuk berhenti pada pembuatan objek-file tanpa mencoba menautkan, atau apa pun.
Peter Cordes
2
-save-tempsmenarik karena kesedihan dalam satu pergi kode yang dihasilkan kode yang tepat, sedangkan opsi lain memanggil kompiler dengan -Scara kompilasi dua kali, dan mungkin dengan opsi yang berbeda. Tapi -save-temps kesedihan semua dalam direktori saat ini, yang agak berantakan. Sepertinya itu lebih dimaksudkan sebagai opsi debug untuk GCC daripada alat untuk memeriksa kode Anda.
Stéphane Gourichon
50

Menggunakan -Speralihan ke GCC pada sistem berbasis x86 menghasilkan dump sintaks AT&T, secara default, yang dapat ditentukan dengan -masm=attsakelar, seperti:

gcc -S -masm=att code.c

Sedangkan jika Anda ingin menghasilkan dump dalam sintaks Intel, Anda bisa menggunakan -masm=intelsakelar, seperti:

gcc -S -masm=intel code.c

(Keduanya menghasilkan dumps code.cke berbagai sintaks mereka, ke dalam filecode.s masing-masing )

Untuk menghasilkan efek yang serupa dengan objdump, Anda ingin menggunakan --disassembler-options= intel/ attswitch, contoh (dengan dump kode untuk menggambarkan perbedaan dalam sintaks):

 $ 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

dan

$ 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
amaterasu
sumber
Apa yang ... gcc -S -masm=intel test.ctidak persis pekerjaan untuk saya, saya punya beberapa blasteran dari Intel dan AT & T sintaks seperti ini: mov %rax, QWORD PTR -24[%rbp], bukannya ini: movq -24(%rbp), %rax.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
1
Tip yang bagus. Perlu dicatat ini juga berfungsi ketika melakukan output paralel .odan file ASM, yaitu via-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
underscore_d
Bisa menggunakan -Mopsi, itu sama --disassembler-optionstetapi jauh lebih pendek, misalnyaobjdump -d -M intel a.out | less -N
Eric Wang
34

godbolt adalah alat yang sangat berguna, daftar mereka hanya memiliki kompiler C ++ tetapi Anda dapat menggunakan -x cflag untuk membuatnya memperlakukan kode sebagai C. Ini kemudian akan menghasilkan daftar perakitan untuk kode Anda berdampingan dan Anda dapat menggunakan Colouriseopsi untuk menghasilkan batang berwarna untuk secara visual menunjukkan kode sumber mana yang dipetakan ke rakitan yang dihasilkan. Misalnya kode berikut:

#include <stdio.h>

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

menggunakan baris perintah berikut:

-x c -std=c99 -O3

dan Colouriseakan menghasilkan yang berikut:

masukkan deskripsi gambar di sini

Shafik Yaghmour
sumber
Alangkah baiknya mengetahui cara kerja filter godbolt: .LC0, .text, //, dan Intel. Intel itu mudah -masm=inteltetapi bagaimana dengan yang lain?
Z boson
Saya kira itu dijelaskan di sini stackoverflow.com/a/38552509/2542702
Z boson
godbolt mendukung C (bersama dengan satu ton bahasa lain seperti Rust, D, Pascal ...). Hanya saja ada lebih sedikit kompiler C, jadi masih lebih baik menggunakan kompiler C ++ dengan-x c
phuclv
23

Apakah Anda mencoba gcc -S -fverbose-asm -O source.ckemudian melihat ke dalam source.sfile assembler yang dihasilkan ?

Kode assembler yang dihasilkan masuk ke source.s(Anda bisa menimpanya dengan -o assembler-nama file ); yang -fverbose-asmpilihan meminta compiler untuk memancarkan beberapa komentar assembler "menjelaskan" kode assembler yang dihasilkan. The -Opilihan meminta compiler untuk mengoptimalkan sedikit (bisa mengoptimalkan lebih dengan -O2atau -O3).

Jika Anda ingin memahami apa yang gccsedang dilakukan coba lewati-fdump-tree-all tapi hati-hati: Anda akan mendapatkan ratusan file dump.

BTW, GCC dapat dikembangkan melalui plugin atau dengan MELT (bahasa khusus domain tingkat tinggi untuk memperpanjang GCC; yang saya tinggalkan pada tahun 2017)

Basile Starynkevitch
sumber
mungkin menyebutkan bahwa hasilnya akan masuk source.s, karena banyak orang akan mengharapkan hasil cetakan di konsol.
RubenLaguna
1
@ecerulm: -S -o-dumps ke stdout. -masm=intelsangat membantu jika Anda ingin menggunakan sintaks NASM / YASM. (Tapi itu menggunakan qword ptr [mem], bukan hanya qword, jadi lebih seperti Intel / MASM daripada NASM / YASM). gcc.godbolt.org melakukan pekerjaan yang bagus untuk merapikan tempat sampah: secara opsional menghapus garis komentar saja, label yang tidak digunakan, dan arahan assembler.
Peter Cordes
2
Lupa menyebutkan: Jika Anda mencari "mirip dengan sumber tetapi tanpa suara store / reload setelah setiap baris sumber", maka -Ogitu bahkan lebih baik daripada -O1. Ini berarti "optimalkan untuk debugging" dan buat asm tanpa terlalu banyak optimasi rumit / sulit diikuti yang melakukan semua yang dikatakan sumber. Sudah tersedia sejak gcc4.8, tapi dentang 3.7 masih belum memilikinya. IDK jika mereka memutuskan menentangnya atau apa.
Peter Cordes
19

Anda dapat menggunakan gdb untuk objdump seperti ini.

Kutipan ini diambil dari http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


Berikut ini adalah contoh yang menunjukkan sumber campuran + perakitan untuk Intel x86:

  (gdb) disas / m main
Dump kode assembler untuk fungsi utama:
5 {
0x08048330: push% ebp
0x08048331: mov% esp,% ebp
0x08048333: sub $ 0x8,% esp
0x08048336: dan $ 0xfffffff0,% esp
0x08048339: sub $ 0x10,% esp

6 printf ("Halo. \ N");
0x0804833c: movl $ 0x8048440, (% esp)
0x08048343: hubungi 0x8048284 

7 mengembalikan 0;
8}
0x08048348: mov $ 0x0,% eax
0x0804834d: pergi
0x0804834e: ret

Akhir dump assembler.
Vishal Sagar
sumber
1
tautan yang diarsipkan
vlad4378
Dan untuk mengalihkan disassembler GDB ke sintaks Intel, gunakan set disassembly-flavor intelperintah.
Ruslan
13

Gunakan -S (note: capital S) untuk beralih ke GCC, dan itu akan memancarkan kode assembly ke file dengan ekstensi .s. Misalnya, perintah berikut:

gcc -O2 -S -c foo.c

codymanix
sumber
4

Saya belum memberikan suntikan ke gcc, tetapi dalam kasus g ++. Perintah di bawah ini berfungsi untuk saya. -g untuk build debug dan -Wa, -adhln diteruskan ke assembler untuk dicantumkan dengan kode sumber

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

DAG
sumber
Ini bekerja untuk gcc juga! -Wa, ... adalah untuk opsi baris perintah untuk bagian assembler (jalankan di gcc / g ++ setelah kompilasi C / ++). Itu dipanggil sebagai internal (as.exe di Windows). Lihat> sebagai --help sebagai baris perintah untuk melihat lebih banyak bantuan
Hartmut Schorrig
0

gunakan -Wa, -adhln sebagai opsi pada gcc atau g ++ untuk menghasilkan output listing ke stdout.

-Wa, ... adalah untuk opsi baris perintah untuk bagian assembler (jalankan di gcc / g ++ setelah kompilasi C / ++). Itu dipanggil sebagai internal (as.exe di Windows). Lihat

> as --help

sebagai baris perintah untuk melihat lebih banyak bantuan untuk alat assembler di dalam gcc

Hartmut Schorrig
sumber