ใช้ GCC เพื่อสร้างชุดประกอบที่อ่านได้หรือไม่


256

ฉันสงสัยว่าจะใช้GCCในไฟล์ต้นฉบับ C ของฉันเพื่อถ่ายโอนรหัสเครื่องช่วยจำรุ่นใดเพื่อที่ฉันจะได้เห็นว่าโค้ดของฉันรวบรวมอะไร คุณสามารถทำได้ด้วย Java แต่ฉันไม่สามารถหาวิธีที่มี GCC

ฉันกำลังพยายามเขียนวิธี C อีกครั้งในการชุมนุมและดูว่า GCC ทำมันได้อย่างไรจะช่วยได้มาก


25
โปรดทราบว่าโดยทั่วไป 'bytecode' หมายถึงรหัสที่ VM ใช้เช่น JVM หรือ CLR ของ. NET ผลลัพธ์ของ GCC เรียกได้ว่าดีกว่า 'รหัสเครื่อง', 'ภาษาเครื่อง' หรือ 'ภาษาแอสเซมบลี'
Javier

2
ฉันเพิ่มคำตอบโดยใช้ godbolt เนื่องจากเป็นเครื่องมือที่ทรงพลังมากสำหรับการทดสอบอย่างรวดเร็วด้วยตัวเลือกที่ต่างกันที่ส่งผลต่อการสร้างรหัสของคุณ
Shafik Yaghmour



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

คำตอบ:


335

หากคุณรวบรวมสัญลักษณ์ debug คุณสามารถใช้objdumpเพื่อสร้างการถอดแยกชิ้นส่วนที่อ่านได้มากขึ้น

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

objdump -drwC -Mintel เป็นสิ่งที่ดี:

  • -rแสดงชื่อสัญลักษณ์ในการย้าย (ดังนั้นคุณจะได้เห็นputsในcallการเรียนการสอนด้านล่าง)
  • -R แสดงการย้ายลิงก์ / ชื่อสัญลักษณ์แบบไดนามิก (มีประโยชน์ในไลบรารีที่แชร์)
  • -C ปลดชื่อสัญลักษณ์ C ++
  • -w คือโหมด "wide": ไม่ได้เป็นบรรทัดการพันไบต์ของโค้ดเครื่อง
  • -Mintel: ใช้.intel_syntax noprefixไวยากรณ์คล้าย GAS / binutils MASM แทน AT&T
  • -S: สอดแทรกซอร์สซอร์สด้วยการถอดแยก

คุณสามารถใส่สิ่งที่ชอบalias disas="objdump -drwCS -Mintel"ในของคุณ~/.bashrc


ตัวอย่าง:

> 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
มีสวิตช์ให้เลือกเฉพาะคำสั่งของ Intel หรือไม่
James

3
ทั้งหมดนี้เป็นคำสั่งของ Intel เนื่องจากทำงานบนโปรเซสเซอร์ Intel: D
โตโต้

12
@toto ผมคิดว่าเขาหมายถึง Intel ไวยากรณ์แทนของ AT & T ไวยากรณ์
อาละวาด

7
-Wa,-adhln -g to gccมันเป็นไปได้ที่จะละเลยไฟล์วัตถุกลางด้วยโดยใช้ลำดับสวิทช์ สมมติว่าแอสเซมเบลอร์เป็นแก๊สและอาจไม่เป็นเช่นนั้นเสมอไป
Marc Butler

8
@James -Mintelใช่จัดหา
fuz

106

ถ้าคุณให้ธงGCC-fverbose-asmมันจะ

ใส่ข้อมูลความเห็นเพิ่มเติมในรหัสแอสเซมบลีที่สร้างขึ้นเพื่อให้อ่านง่ายขึ้น

[... ] ความคิดเห็นที่เพิ่มเข้ามารวมถึง:

  • ข้อมูลเกี่ยวกับเวอร์ชั่นคอมไพเลอร์และตัวเลือกบรรทัดคำสั่ง
  • บรรทัดซอร์สโค้ดที่เกี่ยวข้องกับคำแนะนำการประกอบในรูปแบบไฟล์: LINENUMBER: เนื้อหาของ LINE
  • คำแนะนำเกี่ยวกับนิพจน์ระดับสูงที่สอดคล้องกับตัวถูกดำเนินการคำสั่งแอสเซมบลีที่หลากหลาย

แต่แล้วฉันจะหายไปทั้งสวิทช์ที่ใช้สำหรับobjdump- objdump -drwCS -Mintelดังนั้นวิธีที่ฉันสามารถใช้สิ่งที่ชอบverboseด้วยobjdump? เพื่อให้ฉันสามารถแสดงความคิดเห็นในรหัส asm เช่นเดียวกับ-fverbose-asmgcc?
Herdsman

1
@ Herdsman: คุณทำไม่ได้ สิ่งที่-fverbose-asmเพิ่มพิเศษอยู่ในรูปแบบของความคิดเห็นในไวยากรณ์ asm ของผลลัพธ์ไม่ใช่คำสั่งที่จะใส่อะไรพิเศษลงใน.oไฟล์ มันถูกละทิ้งในเวลารวมตัว ดูคอมไพเลอร์เอาต์พุต asm แทนการถอดแยกชิ้นส่วนเช่นgodbolt.orgที่คุณสามารถจับคู่กับบรรทัดซอร์สผ่าน mouseover และการไฮไลต์สีของบรรทัดซอร์ส / asm ที่สอดคล้องกันได้อย่างง่ายดาย วิธีการลบ "สัญญาณรบกวน" ออกจากชุดประกอบ GCC / เสียงดังกราวด์?
Peter Cordes

75

ใช้สวิตช์ -S (note: capital S) เป็น GCC และจะปล่อยรหัสแอสเซมบลีไปยังไฟล์ที่มีนามสกุล. s ตัวอย่างเช่นคำสั่งต่อไปนี้:

gcc -O2 -S foo.c

จะปล่อยให้รหัสการประกอบที่สร้างขึ้นบนไฟล์ foo.s

ฉีกตรงจากhttp://www.delorie.com/djgpp/v2faq/faq8_20.html (แต่ลบผิดพลาด-c)


35
คุณไม่ควรผสม -c และ -S ให้ใช้หนึ่งในนั้นเท่านั้น ในกรณีนี้มีคนหนึ่งที่เอาชนะคนอื่น ๆ อาจขึ้นอยู่กับลำดับที่พวกเขากำลังใช้
Adam Rosenfield

4
@ AdamRosenfield การอ้างอิงใด ๆ เกี่ยวกับ 'ไม่ควรผสม -c และ -S' หากเป็นจริงเราอาจเตือนผู้เขียนและแก้ไข
โทนี่

5
@Tony: gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options "คุณสามารถใช้ ... หนึ่งในตัวเลือก -c, -S หรือ -E เพื่อบอกว่า gcc หยุดอยู่ที่ใด "
Nate Eldredge

1
gcc -march=native -O3 -save-tempsหากคุณต้องการทั้งหมดผลกลางการใช้งาน คุณยังสามารถใช้-cเพื่อหยุดที่การสร้างวัตถุไฟล์โดยไม่ต้องพยายามเชื่อมโยงหรืออะไรก็ตาม
Peter Cordes

2
-save-tempsเป็นที่น่าสนใจในขณะที่ทิ้งในหนึ่งไปรหัสที่สร้างรหัสที่แน่นอนในขณะที่ตัวเลือกอื่น ๆ ของการเรียกรวบรวมด้วย-Sวิธีการรวบรวมสองครั้งและอาจมีตัวเลือกที่แตกต่างกัน แต่ -save-tempsทิ้งทุกอย่างไว้ในไดเรกทอรีปัจจุบันซึ่งเป็นสิ่งที่ยุ่งเหยิง ดูเหมือนว่าตั้งใจจะเป็นตัวเลือกการแก้ไขข้อบกพร่องสำหรับ GCC มากกว่าเครื่องมือในการตรวจสอบรหัสของคุณ
Stéphane Gourichon

50

การใช้-Sสวิตช์ไปที่ GCC บนระบบที่ใช้ x86 จะสร้างดัมพ์ของไวยากรณ์ AT&T โดยค่าเริ่มต้นซึ่งสามารถระบุได้ด้วย-masm=attสวิตช์เช่น:

gcc -S -masm=att code.c

ในกรณีที่คุณต้องการสร้างดัมพ์ในไวยากรณ์ของ Intel คุณสามารถใช้-masm=intelสวิตช์ได้ดังนี้:

gcc -S -masm=intel code.c

(ทั้งคู่สร้างดัมพ์code.cลงในไวยากรณ์ต่าง ๆ ลงในไฟล์code.sตามลำดับ)

ในการสร้างเอฟเฟกต์ที่คล้ายกันกับ objdump คุณต้องการใช้--disassembler-options= intel/ attสวิตช์ตัวอย่าง (โดยมีโค้ดดั๊มเพื่อแสดงความแตกต่างในไวยากรณ์):

 $ 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

อะไร ... gcc -S -masm=intel test.cไม่ว่าการทำงานสำหรับฉันฉันมีบางผสมข้ามของ Intel และ AT & T ไวยากรณ์เช่นนี้แทนที่จะนี้:mov %rax, QWORD PTR -24[%rbp] movq -24(%rbp), %rax
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

1
เคล็ดลับที่ดี ควรสังเกตว่าสิ่งนี้ยังใช้งานได้เมื่อดำเนินการเอาต์พุตแบบขนานของ.oและไฟล์ ASM เช่นผ่าน-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
underscore_d

สามารถใช้-Mตัวเลือกได้เช่นเดียวกัน--disassembler-optionsแต่สั้นกว่ามากเช่นobjdump -d -M intel a.out | less -N
Eric Wang

34

godboltเป็นเครื่องมือที่มีประโยชน์มากพวกมันมีเพียงตัวคอมไพล์เลอร์ C ++ เท่านั้น แต่คุณสามารถใช้-x cflag เพื่อให้มันปฏิบัติกับโค๊ดเหมือน C มันจะสร้างรายชื่อแอสเซมบลีสำหรับโค้ดของคุณเคียงข้างกันและคุณสามารถใช้Colouriseตัวเลือกเพื่อสร้าง แถบสีเพื่อระบุด้วยสายตาว่าซอร์สโค้ดแมปกับแอสเซมบลีที่สร้างขึ้น ตัวอย่างเช่นรหัสต่อไปนี้:

#include <stdio.h>

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

ใช้บรรทัดคำสั่งต่อไปนี้:

-x c -std=c99 -O3

และColouriseจะสร้างสิ่งต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่


มันจะเป็นการดีถ้ารู้ว่าตัวกรอง godbolt ทำงานอย่างไร:. LC0, .text, // และ Intel Intel นั้นง่าย-masm=intelแต่ส่วนที่เหลือล่ะ
Z boson

ฉันเดาว่ามันอธิบายไว้ที่นี่stackoverflow.com/a/38552509/2542702
Z boson

godbolt รองรับ C (พร้อมกับภาษาอื่น ๆ มากมายเช่น Rust, D, Pascal ... ) เป็นเพียงว่ามีคอมไพเลอร์ C น้อยกว่ามากดังนั้นจึงยังดีกว่าที่จะใช้คอมไพเลอร์ C ++ ด้วย-x c
phuclv

23

คุณลองgcc -S -fverbose-asm -O source.cดูในsource.sไฟล์แอสเซมเบลอร์ที่สร้างขึ้นหรือไม่?

รหัสแอสเซมเบลอร์ที่สร้างขึ้นจะเข้าสู่source.s(คุณสามารถแทนที่ด้วยแอส-o เซมเบลอร์ชื่อไฟล์ ); -fverbose-asmตัวเลือกถามคอมไพเลอร์ที่จะปล่อยความคิดเห็นบางส่วนประกอบ "อธิบาย" รหัสประกอบที่สร้างขึ้น -Oตัวเลือกถามคอมไพเลอร์ที่จะเพิ่มประสิทธิภาพบิต (มันสามารถเพิ่มประสิทธิภาพมากขึ้นด้วย-O2หรือ-O3)

หากคุณต้องการเข้าใจสิ่งที่gccกำลังทำอยู่ลองผ่านไป-fdump-tree-allแต่ระวัง: คุณจะได้รับไฟล์ดัมพ์นับร้อย

BTW, GCC เป็นส่วนขยายผ่านปลั๊กอินหรือด้วยMELT (ภาษาเฉพาะโดเมนระดับสูงเพื่อขยาย GCC ซึ่งฉันละทิ้งในปี 2560)


อาจพูดถึงว่าผลลัพธ์จะอยู่ในsource.sเนื่องจากคนจำนวนมากคาดว่าจะพิมพ์ออกมาบนคอนโซล
RubenLaguna

1
@ecululm: ดัมพ์-S -o-ไปที่ stdout -masm=intelมีประโยชน์ถ้าคุณต้องการใช้ไวยากรณ์ NASM / YASM (แต่มันใช้qword ptr [mem]มากกว่าเพียงแค่qwordดังนั้นจึงเป็นเหมือน Intel / MASM มากกว่า NASM / YASM) gcc.godbolt.orgทำงานได้ดีในการจัดระเบียบการถ่ายโอนข้อมูล: เลือกที่จะคัดลอกบรรทัดแสดงความคิดเห็นเท่านั้น, ป้ายกำกับที่ไม่ได้ใช้และคำสั่งแอสเซมเบลอร์
Peter Cordes

2
ลืมบอกไป: หากคุณกำลังมองหา "คล้ายกับแหล่งที่มา แต่ไม่มีเสียงของการจัดเก็บ / โหลดหลังจากบรรทัดทุกแหล่งที่มา" จากนั้นจะดียิ่งขึ้นกว่า-Og -O1มันหมายถึง "ปรับให้ดีที่สุดสำหรับการแก้ไขข้อบกพร่อง" และสร้าง asm โดยไม่มีการปรับให้เหมาะสมที่ยุ่งยาก / ยากที่จะติดตามมากเกินไปที่ทำทุกอย่างที่แหล่งข้อมูลกล่าว มีให้ตั้งแต่ gcc4.8 แต่เสียงดังกราว 3.7 ยังไม่มีอยู่ IDK ถ้าพวกเขาตัดสินใจหรืออะไร
Peter Cordes

19

คุณสามารถใช้ gdb สำหรับสิ่งนี้เช่น objdump

ข้อความที่ตัดตอนมานี้นำมาจากhttp://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


นี่คือตัวอย่างที่แสดงการผสมซอร์ส + แอสเซมบลีสำหรับ Intel x86:

  (gdb) disas / m หลัก
การถ่ายโอนข้อมูลรหัสแอสเซมเบลอร์สำหรับฟังก์ชั่น main:
5 {
0x08048330: ผลักดัน% ebp
0x08048331: mov% esp,% ebp
0x08048333: ย่อย $ 0x8,% esp
0x08048336: และ $ 0xfffffff0,% esp
0x08048339: ย่อย $ 0x10,% esp

6 printf ("Hello. \ n");
0x0804833c: mov $ 0x8048440 (% esp)
0x08048343: โทร 0x8048284 

7 ส่งคืน 0;
8}
0x08048348: mov $ 0x0,% eax
0x0804834d: ออก
0x0804834e: เลิก

สิ้นสุดการดัมพ์ของแอสเซมเบลอร์

1
ลิงก์ที่
เก็บถาวร

และในการเปลี่ยน GDBT ถอดแยกชิ้นส่วนเป็นไวยากรณ์ของ Intel ให้ใช้set disassembly-flavor intelคำสั่ง
Ruslan

13

ใช้สวิตช์ -S (note: capital S) เป็น GCC และจะปล่อยรหัสแอสเซมบลีไปยังไฟล์ที่มีนามสกุล. s ตัวอย่างเช่นคำสั่งต่อไปนี้:

gcc -O2 -S -c foo.c


4

ฉันไม่ได้ให้ช็อตเด็ดกับ gcc แต่ในกรณีของ g ++ คำสั่งด้านล่างใช้ได้สำหรับฉัน -g สำหรับการสร้าง debug และ -Wa, -adhln จะถูกส่งไปยังแอสเซมเบลอร์เพื่อแสดงรายการพร้อมซอร์สโค้ด

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


มันใช้งานได้กับ gcc ด้วย! -Wa, ... สำหรับตัวเลือกบรรทัดคำสั่งสำหรับส่วนแอสเซมเบลอร์ (ดำเนินการใน gcc / g ++ หลังการคอมไพล์ C / ++) มันเรียกใช้ภายใน (as.exe ใน Windows) ดู> as - ช่วยเป็นบรรทัดคำสั่งเพื่อดูวิธีใช้เพิ่มเติม
Hartmut Schorrig

0

ใช้-Wa, -adhlnเป็นตัวเลือกบน gcc หรือ g ++ เพื่อสร้างเอาต์พุตรายการใน stdout

-Wa, ... สำหรับตัวเลือกบรรทัดคำสั่งสำหรับส่วนแอสเซมเบลอร์ (ดำเนินการใน gcc / g ++ หลังการคอมไพล์ C / ++) มันจะเรียกว่าเป็นภายใน (as.exe ใน Windows) ดู

> เป็น - ช่วย

เป็นบรรทัดคำสั่งเพื่อดูความช่วยเหลือเพิ่มเติมสำหรับเครื่องมือแอสเซมเบลอร์ภายใน gcc

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