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?
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?
Câu trả lời:
Sử dụng -S
tù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 -o
tù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 --disassemble
tùy chọn (hoặc -d
cho 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 ( -g
tại thời gian biên dịch) và tệp chưa bị tước.
Chạy file helloworld
sẽ 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.
.intel_syntax
là không tương thích với NASM . Nó giống như MASM (ví dụ như mov eax, symbol
tải, không giống như trong NASM mov r32, imm32
là đị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 | less
hoặc gcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | less
là 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=intel
làm việc với tiếng kêu, quá.
gcc -O -fverbose-asm -S
Đ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).
as
trê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
.
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lst
sẽ là phiên bản tay ngắn của điều này.
gcc -c -g -Wa,-ahl=test.s test.c
hoặcgcc -c -g -Wa,-a,-ad test.c > test.txt
-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.
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)
-O2
hoặ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.)
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.
mov eax,ds:0x804b794
khô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à byte
hay word
.
objconv
. 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 less
xem. 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).
Như mọi người đã chỉ ra, hãy sử dụng -S
tù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 ( -O0
khô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ả!
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.
Tôi không thấy khả năng này trong số các câu trả lời, có thể vì câu hỏi là từ năm 2008, nhưng năm 2018 bạn có thể sử dụng trang web trực tuyến của Matt Goldbolt https://godbolt.org
Bạn cũng có thể git clone cục bộ và chạy dự án của mình https://github.com/mattgodbolt/compiler-explorer
-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 -S
nà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.
Sử dụng tùy chọn -S:
gcc -S program.c
Từ: http : //www.d Bachelorie.com/djgpp/v2faq/faq8_20.html
gcc -c -g -Wa, -a, -ad [các tùy chọn GCC khác] foo.c> foo.lst
thay thế cho câu trả lời của PhirePhly Hoặc chỉ sử dụng -S như mọi người đã nói.
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:
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
Biên dịch và chạy nó.
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.
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.
gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less
. -S
trên chính nó tạo ra foo.s
.
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
Đâ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
Ở đâ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.
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: -}