Sử dụng GCC để sản xuất lắp ráp có thể đọc được?


256

Tôi đã tự hỏi làm thế nào để sử dụng GCC trên tệp nguồn C của mình để kết xuất một phiên bản ghi nhớ của mã máy để tôi có thể thấy mã của mình đang được biên dịch thành gì. Bạn có thể làm điều này với Java nhưng tôi chưa thể tìm ra cách nào với GCC.

Tôi đang cố gắng viết lại một phương thức C trong lắp ráp và xem GCC thực hiện nó như thế nào sẽ là một trợ giúp lớn.


25
lưu ý rằng 'mã byte' thường có nghĩa là mã được sử dụng bởi VM, như CLR của JVM hoặc .NET. Đầu ra của GCC tốt hơn được gọi là 'mã máy', 'ngôn ngữ máy' hoặc 'ngôn ngữ lắp ráp'
Javier

2
Tôi đã thêm một câu trả lời bằng cách sử dụng godbolt vì đây là một công cụ rất mạnh để thử nghiệm nhanh chóng cách các tùy chọn khác nhau ảnh hưởng đến việc tạo mã của bạn.
Shafik Yaghmour



Để biết thêm mẹo về cách làm cho đầu ra asm của con người có thể đọc được, hãy xem thêm: Làm cách nào để loại bỏ tiếng ồn Tiếng Đức khỏi đầu ra lắp ráp GCC / clang?
Peter Cordes

Câu trả lời:


335

Nếu bạn biên dịch với các biểu tượng gỡ lỗi, bạn có thể sử dụng objdumpđể tạo ra sự tháo gỡ dễ đọc hơn.

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

objdump -drwC -Mintel là tốt đẹp

  • -rhiển thị tên biểu tượng trên các di dời (vì vậy bạn sẽ thấy putstrong callhướng dẫn bên dưới)
  • -R hiển thị tên di chuyển / tên biểu tượng liên kết động (hữu ích trên các thư viện dùng chung)
  • -C tên biểu tượng C ++
  • -w là chế độ "rộng": nó không bao bọc các byte mã máy
  • -Mintel: sử dụng .intel_syntax noprefixcú pháp GAS / binutils giống như MASM thay vì AT & T
  • -S: xen kẽ các dòng nguồn với sự tháo gỡ.

Bạn có thể đặt một cái gì đó như alias disas="objdump -drwCS -Mintel"trong của bạn~/.bashrc


Thí dụ:

> 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
Có một công tắc để chỉ lấy các hướng dẫn của Intel không?
James

3
Tất cả những điều này là hướng dẫn của Intel vì chúng chạy trên bộ xử lý Intel: D.
toto

12
@toto Tôi nghĩ rằng anh ấy có nghĩa là cú pháp Intel thay vì cú pháp AT & T
Amok

7
Có thể từ bỏ tệp đối tượng trung gian bằng cách sử dụng trình tự chuyển đổi -Wa,-adhln -g to gcc. Điều này giả định rằng trình biên dịch là khí và điều này có thể không phải luôn luôn như vậy.
Marc Butler

8
@James Có, cung cấp -Mintel.
fuz

106

Nếu bạn cho GCC cờ -fverbose-asm, nó sẽ

Đặt thêm thông tin bình luận trong mã lắp ráp được tạo để làm cho nó dễ đọc hơn.

[...] Các ý kiến ​​được thêm vào bao gồm:

  • thông tin về phiên bản trình biên dịch và các tùy chọn dòng lệnh,
  • các dòng mã nguồn được liên kết với các hướng dẫn lắp ráp, ở dạng FILENAME: LINENUMBER: NỘI DUNG LINE,
  • gợi ý về các biểu thức mức cao tương ứng với các toán hạng lệnh lắp ráp khác nhau.

Nhưng sau đó, tôi sẽ mất tất cả các công tắc được sử dụng cho objdump- objdump -drwCS -Mintelvậy làm thế nào tôi có thể sử dụng một cái gì đó như verbosevới objdump? Để tôi có thể có ý kiến ​​trong mã asm, như -fverbose-asmtrong gcc?
chăn gia súc

1
@Herdsman: bạn không thể. Các công cụ -fverbose-asmbổ sung được thêm vào dưới dạng các bình luận theo cú pháp asm của đầu ra, không phải là các lệnh sẽ đưa thêm bất cứ điều gì vào .otệp. Tất cả đều bị loại bỏ tại thời điểm lắp ráp. Nhìn vào đầu ra asm của trình biên dịch thay vì tháo gỡ, ví dụ như trên godbolt.org nơi bạn có thể dễ dàng kết hợp nó với dòng nguồn thông qua di chuột và tô sáng màu của các dòng nguồn / asm tương ứng. Làm cách nào để loại bỏ "nhiễu" khỏi đầu ra lắp ráp GCC / clang?
Peter Cordes

75

Sử dụng công tắc -S (lưu ý: viết hoa S) sang GCC và nó sẽ phát mã lắp ráp thành một tệp có phần mở rộng .s. Ví dụ: lệnh sau:

gcc -O2 -S foo.c

sẽ để lại mã lắp ráp được tạo trên tệp foo.s.

Ripped thẳng từ http: //www.d Bachelorie.com/djgpp/v2faq/faq8_20.html (nhưng loại bỏ lỗi -c)


35
Bạn không nên trộn -c và -S, chỉ sử dụng một trong số chúng. Trong trường hợp này, cái này sẽ ghi đè lên cái kia, có thể tùy thuộc vào thứ tự chúng được sử dụng.
Adam Rosenfield

4
@AdamRosenfield Bất kỳ tài liệu tham khảo nào về 'không nên trộn -c và -S'? Nếu đó là sự thật, chúng ta có thể nên nhắc nhở tác giả và chỉnh sửa nó.
Tony

5
@Tony: gcc.gnu.org/onlinesocs/gcc/Overall-Options.html#Overall-Options "Bạn có thể sử dụng ... một trong các tùy chọn -c, -S hoặc -E để nói nơi gcc sẽ dừng. "
Nate Eldredge

1
Nếu bạn muốn tất cả các đầu ra trung gian, sử dụng gcc -march=native -O3 -save-temps. Bạn vẫn có thể sử dụng -cđể dừng việc tạo tệp đối tượng mà không cần cố gắng liên kết hoặc bất cứ điều gì.
Peter Cordes

2
-save-tempsThật thú vị khi nó bỏ đi một mã chính xác được tạo mã, trong khi tùy chọn khác gọi trình biên dịch với -Snghĩa là biên dịch hai lần và có thể với các tùy chọn khác nhau. Nhưng -save-temps bỏ tất cả trong thư mục hiện tại, đó là loại lộn xộn. Có vẻ như nó được dùng như một tùy chọn gỡ lỗi cho GCC hơn là một công cụ để kiểm tra mã của bạn.
Stéphane Gourichon

50

-STheo mặc định, việc sử dụng chuyển đổi sang GCC trên các hệ thống dựa trên x86 sẽ tạo ra kết xuất cú pháp AT & T, có thể được chỉ định bằng công -masm=atttắc, như vậy:

gcc -S -masm=att code.c

Trong khi đó, nếu bạn muốn tạo kết xuất theo cú pháp Intel, bạn có thể sử dụng công -masm=inteltắc, như vậy:

gcc -S -masm=intel code.c

(Cả hai đều tạo ra các kết xuất code.ctheo cú pháp khác nhau của chúng, vào tệp code.stương ứng)

Để tạo hiệu ứng tương tự với objdump, bạn muốn sử dụng --disassembler-options= intel/ attswitch, một ví dụ (với các đoạn mã để minh họa sự khác biệt trong cú pháp):

 $ 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

Điều mà ... gcc -S -masm=intel test.ckhông chính xác làm việc cho tôi, tôi đã nhận được một số giao thức của cú pháp Intel và AT & T như thế này : mov %rax, QWORD PTR -24[%rbp], thay vì này : movq -24(%rbp), %rax.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

1
Mẹo hay. Cần lưu ý rằng điều này cũng hoạt động khi thực hiện đầu ra song song của.o các tệp và ASM, tức là thông qua-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
underscore_d

Có thể sử dụng -Mtùy chọn, nó giống như --disassembler-optionsnhưng ngắn hơn nhiều, ví dụ nhưobjdump -d -M intel a.out | less -N
Eric Wang

34

godbolt là một công cụ rất hữu ích, họ chỉ liệt kê các trình biên dịch C ++ nhưng bạn có thể sử dụng -x ccờ để xử lý mã như C. Sau đó, nó sẽ tạo một danh sách lắp ráp cho mã của bạn cạnh nhau và bạn có thể sử dụng Colourisetùy chọn để tạo thanh màu để trực quan chỉ ra mã nguồn nào ánh xạ tới tổ hợp được tạo. Ví dụ mã sau:

#include <stdio.h>

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

sử dụng dòng lệnh sau:

-x c -std=c99 -O3

Colourisesẽ tạo ra như sau:

nhập mô tả hình ảnh ở đây


Sẽ thật tuyệt khi biết các bộ lọc godbolt hoạt động như thế nào: .LC0, .text, // và Intel. Intel thì dễ -masm=intelnhưng phần còn lại thì sao?
Z boson

Tôi đoán nó được giải thích ở đây stackoverflow.com/a/38552509/2542702
Z boson

godbolt hỗ trợ C (cùng với rất nhiều ngôn ngữ khác như Rust, D, Pascal ...). Chỉ là có ít trình biên dịch C hơn, nên sử dụng trình biên dịch C ++ với-x c
phuclv

23

Bạn đã thử gcc -S -fverbose-asm -O source.csau đó nhìn vào tạo rasource.s tập tin chưa?

Mã trình biên dịch được tạo đi vào source.s(bạn có thể ghi đè mã đó với -o tên trình biên dịch chương trình biên dịch mã ); các -fverbose-asmtùy chọn yêu cầu trình biên dịch phát ra một số ý kiến lắp ráp "giải thích" mã lắp ráp tạo ra. Các -Otùy chọn yêu cầu trình biên dịch để tối ưu hóa một chút (nó có thể tối ưu hóa hơn với -O2hoặc-O3 ).

Nếu bạn muốn hiểu những gì gccđang làm hãy thử đi qua-fdump-tree-all nhưng hãy thận trọng: bạn sẽ nhận được hàng trăm tệp kết xuất.

BTW, GCC là các plugin thông qua có thể mở rộng hoặc với MELT (ngôn ngữ dành riêng cho tên miền cấp cao để mở rộng GCC; tôi đã từ bỏ vào năm 2017)


có thể đề cập rằng đầu ra sẽ được đưa vào source.s, vì rất nhiều người sẽ mong đợi một bản in trên bảng điều khiển.
RubenLaguna

1
@ecerulm: -S -o-đổ vào stdout. -masm=intellà hữu ích nếu bạn muốn sử dụng cú pháp NASM / YASM. (nhưng nó sử dụng qword ptr [mem], thay vì chỉ qword, vì vậy nó giống Intel / MASM hơn là NASM / YASM). gcc.godbolt.org thực hiện tốt công việc dọn dẹp bãi rác: tùy ý tước các dòng chỉ nhận xét, nhãn không sử dụng và chỉ thị trình biên dịch.
Peter Cordes

2
Quên đề cập: Nếu bạn đang tìm kiếm "tương tự như nguồn nhưng không có tiếng ồn của cửa hàng / tải lại sau mỗi dòng nguồn", thì -Ogthậm chí còn tốt hơn -O1. Nó có nghĩa là "tối ưu hóa để gỡ lỗi" và tạo ra asm mà không có quá nhiều tối ưu hóa khó / khó theo dõi mà thực hiện mọi thứ mà nguồn tin nói. Nó đã có sẵn từ gcc4.8, nhưng clang 3.7 vẫn không có. IDK nếu họ quyết định chống lại nó hoặc những gì.
Peter Cordes

19

Bạn có thể sử dụng gdb cho điều này như objdump.

Đoạn trích này được lấy từ http://source.redhat.com/gdb/cản/onlinesocs/gdb_9.html#SEC64


Dưới đây là một ví dụ hiển thị nguồn hỗn hợp + lắp ráp cho Intel x86:

  (gdb) disas / m chính
Kết xuất mã trình biên dịch mã cho hàm main:
5 {
0x08048330: đẩy% ebp
0x08048331: Mov% đặc biệt,% ebp
0x08048333: phụ $ 0x8,% đặc biệt
0x08048336: và $ 0xfffffff0,% đặc biệt
0x08048339: phụ $ 0x10,% đặc biệt

6 printf ("Xin chào. \ N");
0x0804833c: di chuyển $ 0x8048440, (% đặc biệt)
0x08048343: gọi 0x8048284 

7 trả về 0;
số 8 }
0x08048348: Mov $ 0x0,% eax
0x0804834d: để lại
0x0804834e: nghỉ

Kết thúc lắp ráp đổ.


Và để chuyển trình dịch ngược của GDB sang cú pháp Intel, hãy sử dụng set disassembly-flavor intellệnh.
Ruslan

13

Sử dụng công tắc -S (lưu ý: viết hoa S) sang GCC và nó sẽ phát mã lắp ráp thành một tệp có phần mở rộng .s. Ví dụ: lệnh sau:

gcc -O2 -S -c foo.c


4

Tôi đã không tiêm cho gcc, nhưng trong trường hợp của g ++. Lệnh dưới đây làm việc cho tôi. -g cho bản dựng gỡ lỗi và -Wa, -adhln được chuyển cho trình biên dịch để liệt kê với mã nguồn

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


Nó hoạt động cho gcc quá! -Wa, ... dành cho các tùy chọn dòng lệnh cho phần trình biên dịch (thực thi trong gcc / g ++ sau khi biên dịch C / ++). Nó gọi dưới dạng nội bộ (as.exe trong Windows). Xem> dưới dạng
Hartmut Schorrig

0

sử dụng -Wa, -adhln làm tùy chọn trên gcc hoặc g ++ để tạo đầu ra danh sách cho thiết bị xuất chuẩn.

-Wa, ... dành cho các tùy chọn dòng lệnh cho phần trình biên dịch (thực thi trong gcc / g ++ sau khi biên dịch C / ++). Nó gọi dưới dạng nội bộ (as.exe trong Windows). Xem

> như - trợ giúp

dưới dạng dòng lệnh để xem thêm trợ giúp cho công cụ biên dịch bên trong gcc

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.