คุณจะรับเอาต์พุตแอสเซมเบลอร์จากแหล่ง C / C ++ เป็น gcc ได้อย่างไร


379

คนเราจะทำสิ่งนี้ได้อย่างไร

หากฉันต้องการวิเคราะห์ว่ามีบางสิ่งที่รวบรวมได้ฉันจะได้รับรหัสแอสเซมบลีที่ปล่อยออกมาได้อย่างไร


8
สำหรับเคล็ดลับในการทำให้เอาต์พุต asm ที่มนุษย์สามารถอ่านได้ดูเพิ่มเติม: วิธีการลบ "สัญญาณรบกวน" ออกจากเอาต์พุตชุดประกอบ GCC / เสียงดังกราว
Peter Cordes

คำตอบ:


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 ว่ามีประโยชน์มากกว่า
ริส Ulerich

3
การใช้เพิ่มเติม: objdump -M intel -S --disassemble helloworld> helloworld.dump เพื่อรับอ็อบเจ็กต์ดัมพ์ในไวยากรณ์ของ Intel ที่เข้ากันได้กับ nasm บน linux
touchStone

2
หากคุณมีฟังก์ชั่นเดียวเพื่อเพิ่มประสิทธิภาพ / ตรวจสอบจากนั้นคุณสามารถลองใช้คอมไพเลอร์ Interactive 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 / เสียงดังกราวด์? ) -masm=intelทำงานร่วมกับเสียงดังกราวได้เช่นกัน
Peter Cordes

3
ใช้งานได้ดีขึ้นgcc -O -fverbose-asm -S
Basile Starynkevitch

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
บล็อกโพสต์อธิบายในรายละเอียดมากขึ้นรวมทั้งรุ่นหนึ่งคำสั่งเช่นตำนานและ Lu'u โพสต์ แต่ทำไม-O0? เต็มไปด้วยโหลด / ร้านค้าที่ทำให้ยากต่อการติดตามค่าและไม่ได้บอกอะไรคุณเกี่ยวกับประสิทธิภาพของรหัสที่เพิ่มประสิทธิภาพ
Peter Cordes

51

บรรทัดคำสั่งต่อไปนี้มาจากบล็อกของ Christian Garbin

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 - ไม่จำเป็น OP เป็นเรื่องเกี่ยวกับการเอาท์พุทแอสเซมเบลอร์แอสเซมเบลอร์ของซอร์สโค้ด C / C ++ นี่คือรายการที่ฉันเห็นว่ามีประโยชน์มากกว่าสำหรับการทำความเข้าใจว่าคอมไพเลอร์และเครื่องมือเพิ่มประสิทธิภาพกำลังทำอะไร แต่มันจะทำให้แอสเซมเบลอร์ตัวเอง barf เนื่องจากมันไม่ได้คาดหวังว่าหมายเลขบรรทัดและรวบรวมไบต์ออกจากทีโอทีเขาออกจากคำแนะนำการชุมนุม
Jesse Chisholm

ใช้อย่างน้อยที่สุด-O2หรือตัวเลือกการเพิ่มประสิทธิภาพใด ๆ ที่คุณใช้จริงเมื่อสร้างโครงการของคุณหากคุณต้องการดูว่า gcc เพิ่มประสิทธิภาพโค้ดของคุณอย่างไร (หรือถ้าคุณใช้ LTO อย่างที่ควรจะเป็นคุณต้องถอดแยก linker output ออกเพื่อดูว่าคุณได้อะไรจริงๆ)
Peter Cordes

27

ใช้สวิตช์ -S

g++ -S main.cpp

หรือด้วย gcc

gcc -S main.c

ดูสิ่งนี้ด้วย


7
ตรวจสอบคำถามที่พบบ่อย: "การถามและตอบคำถามการเขียนโปรแกรมของคุณเองก็สมบูรณ์แบบ" จุดที่เป็นตอนนี้ StackOverflow มี Q & A เป็นทรัพยากรสำหรับผู้อื่น
Steve Jessop

และบางทีคนอื่นจะเข้ามาและทำให้คุณประหลาดใจด้วยคำตอบที่ดีกว่าแม้ว่าฉันอาจจะ
พูดจา

มีแม้กระทั่งคำตอบปุ่มคำถามของคุณเอง
Ciro Santilli 郝海东冠状病六四事件法轮功

13

หากสิ่งที่คุณต้องการดูขึ้นอยู่กับการเชื่อมโยงของเอาต์พุตแล้ว objdump ในไฟล์อ็อบเจกต์เอาต์พุต / ไฟล์เรียกทำงานอาจมีประโยชน์นอกเหนือจาก gcc -S ที่กล่าวมาข้างต้น นี่เป็นสคริปต์ที่มีประโยชน์มากโดย Loren Merritt ที่แปลงไวยากรณ์เริ่มต้น 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
Ruslan

การถอดแยกชิ้นส่วนใน NASM ไวยากรณ์ในครั้งแรกที่ใช้Agner objconvหมอก คุณสามารถเอามันไปถอดแยกเพื่อ stdout ด้วยไฟล์เอาต์พุต = /dev/stdoutเพื่อให้คุณสามารถไพพ์เข้าไปlessดูได้ นอกจากนี้ยังมีndisasmแต่มันแยกส่วนไบนารีแบบแบนเท่านั้นและไม่ทราบเกี่ยวกับไฟล์วัตถุ (ELF / PE)
Peter Cordes

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 แต่ในปี 2561 คุณสามารถใช้เว็บไซต์ออนไลน์ของ Matt Goldbolt 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ดังนั้นจึงเป็นเรื่องง่ายที่จะรู้ว่าสิ่งที่เกิดขึ้นซึ่งรวมถึงขั้นตอนการประมวลผล / การรวบรวม / การชุมนุม / preprocessing

/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

การส่งออกของ commnads เหล่านี้

นี่คือขั้นตอนในการดู / พิมพ์รหัสการประกอบของโปรแกรม C ใด ๆ บน Windows ของคุณ

พร้อมท์คอนโซล / เทอร์มินัล / คำสั่ง:

  1. เขียนโปรแกรม C ในโปรแกรมแก้ไขรหัส C เช่น codeblocks และบันทึกด้วย extention .c

  2. รวบรวมและเรียกใช้

  3. เมื่อทำงานสำเร็จให้ไปที่โฟลเดอร์ที่คุณได้ติดตั้งคอมไพเลอร์ gcc และให้

    ทำตามคำสั่งเพื่อรับไฟล์ '.s' ของไฟล์ '.c'

    C: \ gcc> gcc -S พา ธ ที่สมบูรณ์ของไฟล์ C ENTER

    คำสั่งตัวอย่าง (ในกรณีของฉัน)

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

    เอาต์พุตนี้เป็นไฟล์ '.s' ของไฟล์ '.c' ดั้งเดิม

4. หลังจากนี้ให้พิมพ์คำสั่งต่อไปนี้

C; \ gcc> cpp filename.s ENTER

คำสั่งตัวอย่าง (ในกรณีของฉัน)

C; \ gcc> cpp Alternate_letters.s

สิ่งนี้จะพิมพ์ / ส่งออกรหัสภาษาแอสเซมบลีทั้งหมดของโปรแกรม C ของคุณ


2

ใช้ "-S" เป็นตัวเลือก มันจะแสดงผลการชุมนุมใน terminal


gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |lessที่จะแสดงในขั้วใช้ ในตัวเองสร้าง-S foo.s
Peter Cordes

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. ที่นี่ส่วนแรกเก็บเอาท์พุทแอสเซมบลีของโปรแกรมในชื่อไฟล์เดียวกับโปรแกรม แต่ด้วยนามสกุล. s ที่เปลี่ยนแปลงคุณสามารถเปิดเป็นไฟล์ข้อความทั่วไปได้

  2. ส่วนที่สองที่นี่รวบรวมโปรแกรมของคุณสำหรับการใช้งานจริงและสร้างไฟล์ปฏิบัติการสำหรับโปรแกรมของคุณด้วยชื่อไฟล์ที่ระบุ

program.cใช้ข้างต้นเป็นชื่อของโปรแกรมของคุณและเอาท์พุทเป็นชื่อของปฏิบัติการที่คุณต้องการในการสร้าง

BTW มันเป็นโพสต์แรกของฉันใน StackOverFlow: -}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.