Làm thế nào để bạn có được đầu ra của trình biên dịch từ nguồn C / C ++ trong gcc?


379

Làm thế nào để làm điều này?

Nếu tôi muốn phân tích làm thế nào một cái gì đó được biên dịch, làm thế nào tôi có được mã lắp ráp được phát ra?


8
Để biết các mẹo về cách làm cho đầu ra asm 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:


421

Sử dụng -Stùy chọn để gcc (hoặc g ++).

gcc -S helloworld.c

Điều này sẽ chạy bộ tiền xử lý (cpp) trên helloworld.c, thực hiện quá trình biên dịch ban đầu và sau đó dừng lại trước khi trình biên dịch chạy.

Theo mặc định, điều này sẽ xuất ra một tập tin helloworld.s. Tệp đầu ra vẫn có thể được đặt bằng cách sử dụng -otùy chọn.

gcc -S -o my_asm_output.s helloworld.c

Tất nhiên điều này chỉ hoạt động nếu bạn có nguồn gốc. Một cách khác nếu bạn chỉ có tệp đối tượng kết quả là sử dụng objdump, bằng cách đặt --disassembletùy chọn (hoặc -dcho dạng viết tắt).

objdump -S --disassemble helloworld > helloworld.dump

Tùy chọn này hoạt động tốt nhất nếu tùy chọn gỡ lỗi được bật cho tệp đối tượng ( -gtại thời gian biên dịch) và tệp chưa bị tước.

Chạy file helloworldsẽ cung cấp cho bạn một số dấu hiệu về mức độ chi tiết mà bạn sẽ nhận được bằng cách sử dụng objdump.


3
Trong khi điều này là chính xác, tôi thấy kết quả từ câu trả lời của Cr McDonough sẽ hữu ích hơn.
Rhys Ulerich

3
một sử dụng bổ sung: objdump -M intel -S --disassemble helloworld> helloworld.dump để có được kết xuất đối tượng trong cú pháp intel tương thích với nasm trên linux.
touchStone 11/03/2015

2
Nếu bạn có một chức năng duy nhất để tối ưu hóa / kiểm tra, thì bạn có thể dùng thử Trình biên dịch C ++ tương tác trực tuyến, ví dụ như godbolt
fiorentinoing

1
@touchStone: GAS .intel_syntaxkhông tương thích với NASM . Nó giống như MASM (ví dụ như mov eax, symboltải, không giống như trong NASM mov r32, imm32là địa chỉ), nhưng cũng không hoàn toàn tương thích với MASM. Tôi thực sự khuyên bạn nên đọc nó như một định dạng đẹp để đọc, đặc biệt nếu bạn muốn viết theo cú pháp NASM. objdump -drwC -Mintel | lesshoặc gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | lesslà hữu ích. (Xem thêm Làm thế nào để loại bỏ tiếng ồn Tiếng Việt khỏi đầu ra lắp ráp GCC / clang? ). -masm=intellàm việc với tiếng kêu, quá.
Peter Cordes

3
Sử dụng tốt hơngcc -O -fverbose-asm -S
Basile Starynkevitch

173

Điều này sẽ tạo mã lắp ráp với mã C + số dòng đan xen, để dễ dàng xem dòng nào tạo mã nào:

# 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

Tìm thấy trong Thuật toán dành cho lập trình viên , trang 3 (là trang thứ 15 chung của PDF).


3
(Đó thực sự là trên trang (được đánh số) 3 (là trang thứ 15 của PDF))
Grumdrig

1
Đáng buồn thay, astrên OS X không biết những cờ này. Tuy nhiên, nếu có, bạn có thể sử dụng một dòng này -Wađể sử dụng các tùy chọn as.
Grumdrig

23
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lstsẽ là phiên bản tay ngắn của điều này.
huyền thoại2k

4
Bạn cũng có thể sử dụng gcc -c -g -Wa,-ahl=test.s test.choặcgcc -c -g -Wa,-a,-ad test.c > test.txt
phuclv

1
Một bài đăng trên blog giải thích điều này chi tiết hơn, bao gồm phiên bản một lệnh như truyền thuyết và Lu'u đã đăng. Nhưng tại sao -O0? Đó là đầy tải / cửa hàng khiến bạn khó theo dõi giá trị và không cho bạn biết bất cứ điều gì về hiệu quả của mã được tối ưu hóa.
Peter Cordes

51

Dòng lệnh sau là từ blog của Christian Garbin

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

Tôi đã chạy G ++ từ một cửa sổ DOS trên Win-XP, chống lại một thói quen có chứa một diễn viên ngầm

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'

Đầu ra được mã hóa được tạo ra được gắn với mã C ++ gốc (mã C ++ được hiển thị dưới dạng các nhận xét trong luồng asm được tạo)

  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 - Không nhất thiết. OP nói về việc lấy đầu ra của trình biên dịch mã tương đương với mã nguồn C / C ++, điều này nhận được Liệt kê, mà tôi đồng ý là hữu ích hơn để hiểu trình biên dịch và trình tối ưu hóa đang làm gì. Nhưng nó sẽ khiến trình biên dịch mã tự khóa, vì nó không mong đợi các số dòng và biên dịch các byte ra khỏi tổng số còn lại của các hướng dẫn lắp ráp.
Jesse Chisholm

Sử dụng ít nhất -O2hoặc bất kỳ tùy chọn tối ưu hóa nào bạn thực sự sử dụng khi xây dựng dự án của mình, nếu bạn muốn xem gcc tối ưu hóa mã của bạn như thế nào. (Hoặc nếu bạn sử dụng LTO, như bạn nên, thì bạn phải tháo rời đầu ra của trình liên kết để xem những gì bạn thực sự nhận được.)
Peter Cordes

27

Sử dụng công tắc -S

g++ -S main.cpp

hoặc cũng với gcc

gcc -S main.c

Cũng thấy điều này


7
Kiểm tra Câu hỏi thường gặp: "Hoàn toàn ổn khi hỏi và trả lời câu hỏi lập trình của riêng bạn". Vấn đề là bây giờ StackOverflow chứa Q & A làm tài nguyên cho người khác.
Steve Jessop

Và có thể một người khác sẽ đến và làm bạn ngạc nhiên với một câu trả lời tốt hơn, mặc dù đôi khi tôi có thể hơi dài dòng ...
PhirePhly

Thậm chí có câu trả lời nút câu hỏi của riêng bạn.
Ciro Santilli 郝海东 冠状 病 事件

13

Nếu những gì bạn muốn thấy phụ thuộc vào liên kết của đầu ra, thì objdump trên tệp đối tượng đầu ra / tệp thực thi cũng có thể hữu ích ngoài gcc -S đã nói ở trên. Đây là một kịch bản rất hữu ích của Loren Merritt, chuyển đổi cú pháp objdump mặc định thành cú pháp nasm dễ đọc hơn:

#!/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;

Tôi nghi ngờ điều này cũng có thể được sử dụng trên đầu ra của gcc -S.


2
Tuy nhiên, tập lệnh này là một bản hack bẩn không chuyển đổi hoàn toàn cú pháp. Ví dụ, mov eax,ds:0x804b794không phải là rất NASMish. Ngoài ra, đôi khi nó chỉ loại bỏ thông tin hữu ích: movzx eax,[edx+0x1]để người đọc đoán xem toán hạng bộ nhớ là bytehay word.
Ruslan

Để tháo rời cú pháp NASM ở vị trí đầu tiên, hãy sử dụng Agner Fogobjconv . Bạn có thể làm cho nó tháo rời ra thiết bị xuất chuẩn với tệp đầu ra = /dev/stdout, vì vậy bạn có thể chuyển sang lessxem. Cũng có ndisasm, nhưng nó chỉ phân tách các nhị phân phẳng và không biết về các tệp đối tượng (ELF / PE).
Peter Cordes

9

Như mọi người đã chỉ ra, hãy sử dụng -Stùy chọn cho GCC. Tôi cũng muốn thêm rằng các kết quả có thể thay đổi (một cách dữ dội!) Tùy thuộc vào việc bạn có thêm các tùy chọn tối ưu hóa hay không ( -O0không -O2áp dụng cho tối ưu hóa áp lực).

Trên các kiến ​​trúc RISC nói riêng, trình biên dịch sẽ thường biến đổi mã gần như vượt quá sự công nhận khi thực hiện tối ưu hóa. Thật ấn tượng và hấp dẫn khi nhìn vào kết quả!


9

Vâng, như mọi người đã nói, sử dụng tùy chọn -S. Nếu bạn sử dụng tùy chọn -save-temps, bạn cũng có thể nhận tệp được xử lý trước ( .i), tệp lắp ráp ( .s) và tệp đối tượng (*. O). (lấy từng cái bằng cách sử dụng -E, -S và -c.)


8

Như đã đề cập trước đó, hãy nhìn vào cờ -S.

Cũng đáng để xem xét họ cờ '-fdump-tree', đặc biệt là '-fdump-tree-all', cho phép bạn xem một số dạng trung gian của gcc. Chúng thường có thể dễ đọc hơn trình biên dịch chương trình (ít nhất là với tôi) và cho bạn thấy cách tối ưu hóa thực hiện.


8

Nếu bạn đang tìm kiếm lắp ráp LLVM:

llvm-gcc -emit-llvm -S hello.c


8

-save-temps

Điều này đã được đề cập tại https://stackoverflow.com/a/17083009/895245 nhưng hãy để tôi làm rõ hơn nữa.

Ưu điểm lớn của tùy chọn -Snày là rất dễ dàng để thêm nó vào bất kỳ tập lệnh xây dựng nào, mà không can thiệp nhiều vào bản thân bản dựng.

Khi bạn làm:

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

C chính

#define INC 1

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

và bây giờ, ngoài đầu ra bình thường main.o, thư mục làm việc hiện tại cũng chứa các tệp sau:

  • main.i là một phần thưởng và chứa các tập tin dự phòng:

    # 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 chứa lắp ráp được tạo mong muốn:

        .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

Nếu bạn muốn làm điều đó cho một số lượng lớn tệp, hãy xem xét sử dụng thay thế:

 -save-temps=obj

lưu các tệp trung gian vào cùng thư mục với -ođầu ra đối tượng thay vì thư mục làm việc hiện tại, do đó tránh xung đột tên cơ sở tiềm năng.

Một điều thú vị khác về tùy chọn này là nếu bạn thêm -v:

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

nó thực sự hiển thị các tệp rõ ràng đang được sử dụng thay vì tạm thời xấu xí /tmp, vì vậy thật dễ dàng để biết chính xác những gì đang xảy ra, bao gồm các bước tiền xử lý / biên dịch / lắp ráp:

/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

Đã thử nghiệm trong Ubuntu 19.04 amd64, GCC 8.3.0.




3

Đầu ra của những dấu phẩy

Dưới đây là các bước để xem / in mã lắp ráp của bất kỳ chương trình C nào trên Windows của bạn

giao diện điều khiển / thiết bị đầu cuối / lệnh:

  1. Viết chương trình C trong trình soạn thảo mã C như codeblocks và lưu nó với phần mở rộng .c

  2. Biên dịch và chạy nó.

  3. Sau khi chạy thành công, hãy chuyển đến thư mục mà bạn đã cài đặt trình biên dịch gcc của mình và đưa ra

    lệnh sau để lấy tệp '.s' của tệp '.c'

    C: \ gcc> gcc -S đường dẫn đầy đủ của tệp C ENTER

    Một lệnh ví dụ (như trong trường hợp của tôi)

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

    Điều này xuất ra tệp '.s' của tệp '.c' ban đầu

4 . Sau đó, gõ lệnh sau

C; \ gcc> tên tệp cpp. ENTER

Lệnh ví dụ (như trong trường hợp của tôi)

C; \ gcc> cpp Alternate_letters.s

Điều này sẽ in / xuất toàn bộ mã ngôn ngữ hội của chương trình C của bạn.


2

Sử dụng "-S" làm tùy chọn. Nó hiển thị đầu ra lắp ráp trong thiết bị đầu cuối.


Để hiển thị trong thiết bị đầu cuối, sử dụng gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less. -Strên chính nó tạo ra foo.s.
Peter Cordes

2

Gần đây tôi muốn biết việc lắp ráp từng chức năng trong một chương trình,
đây là cách tôi đã làm.

$ 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

Đây là một giải pháp cho C bằng cách sử dụng gcc:

gcc -S program.c && gcc program.c -o output
  1. Ở đây phần đầu tiên lưu trữ đầu ra lắp ráp của chương trình trong cùng tên tệp với Chương trình nhưng với một .s đã thay đổi phần mở rộng , bạn có thể mở nó như bất kỳ tệp văn bản thông thường nào.

  2. Phần thứ hai ở đây biên dịch chương trình của bạn để sử dụng thực tế và tạo một tệp thực thi cho Chương trình của bạn với một tên tệp được chỉ định.

Chương trình.c được sử dụng ở trên là tên chương trình của bạn và đầu ra là tên của tệp thực thi bạn muốn tạo.

BTW Đây là bài viết đầu tiên của tôi trên StackOverFlow: -}

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.