เหตุใดไบนารีสองโปรแกรมที่มีเฉพาะความคิดเห็นจึงไม่เปลี่ยนไปตรงกับใน gcc


110

ฉันสร้างโปรแกรม C สองโปรแกรม

  1. โปรแกรม 1

    int main()
    {
    }
  2. โปรแกรม 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK เมื่อรวบรวมคอมไพเลอร์ (gcc) ควรละเว้นความคิดเห็นและพื้นที่สีขาวที่ซ้ำซ้อนและด้วยเหตุนี้ผลลัพธ์จึงต้องใกล้เคียงกัน

แต่เมื่อฉันตรวจสอบ md5sums ของไบนารีเอาต์พุตมันไม่ตรงกัน ฉันลองรวบรวมด้วยการเพิ่มประสิทธิภาพ-O3แล้ว-Ofastแต่ก็ยังไม่ตรงกัน

เกิดอะไรขึ้นที่นี่?

แก้ไข: คำสั่งที่แน่นอนและมี md5sums คือ (t1.c คือโปรแกรม 1 และ t2.c คือโปรแกรม 2)

gcc ./t1.c -o aaa
gcc ./t2.c -o bbb
98c1a86e593fd0181383662e68bac22f  aaa
c10293cbe6031b13dc6244d01b4d2793  bbb

gcc ./t2.c -Ofast -o bbb
gcc ./t1.c -Ofast -o aaa
2f65a6d5bc9bf1351bdd6919a766fa10  aaa
c0bee139c47183ce62e10c3dbc13c614  bbb


gcc ./t1.c -O3 -o aaa
gcc ./t2.c -O3 -o bbb
564a39d982710b0070bb9349bfc0e2cd  aaa
ad89b15e73b26e32026fd0f1dc152cd2  bbb

และใช่ md5sums จะจับคู่กับการคอมไพล์หลายรายการที่มีแฟล็กเดียวกัน

BTW ระบบของฉันคือgcc (GCC) 5.2.0และLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux


17
โปรดใส่แฟล็กบรรทัดคำสั่งที่แน่นอนของคุณ ตัวอย่างเช่นข้อมูลการดีบักรวมอยู่ในไบนารีหรือไม่ ถ้าเป็นเช่นนั้นการเปลี่ยนหมายเลขบรรทัดจะส่งผลอย่างชัดเจน ...
Jon Skeet

4
ผลรวมของ MD5 สอดคล้องกันในหลาย ๆ ชุดของรหัสเดียวกันหรือไม่
ไม่กระตือรือร้น

3
ฉันไม่สามารถทำซ้ำสิ่งนี้ได้ ฉันเดาได้ว่าเกิดจากการที่ GCC ฝังข้อมูลเมตาทั้งหมดไว้ในไบนารีเมื่อรวบรวมข้อมูลเหล่านี้ (รวมถึงการประทับเวลา) หากคุณสามารถเพิ่มแฟล็กบรรทัดคำสั่งที่แม่นยำที่คุณใช้นั่นจะเป็นประโยชน์
cyphar

2
แทนที่จะตรวจสอบ MD5sums แล้วติดขัดให้ hexdump และ diff เพื่อดูว่าไบต์ใดแตกต่างกัน
MM

12
แม้ว่าคำตอบของคำถาม "อะไรคือความแตกต่างระหว่างเอาต์พุตคอมไพเลอร์ทั้งสอง" เป็นสิ่งที่น่าสนใจฉันทราบว่าคำถามมีข้อสันนิษฐานที่ไม่มีเหตุผล: ผลลัพธ์ทั้งสองควรจะเหมือนกันและเราต้องการคำอธิบายว่าเหตุใดจึงแตกต่างกัน คอมไพเลอร์ทั้งหมดสัญญากับคุณว่าเมื่อคุณให้โปรแกรม C ที่ถูกกฎหมายเอาต์พุตจะเป็นไฟล์ปฏิบัติการทางกฎหมายที่ใช้โปรแกรมนั้น การดำเนินการสองครั้งของคอมไพเลอร์ใด ๆ ที่สร้างไบนารีเดียวกันไม่ได้เป็นการรับประกันมาตรฐาน C
Eric Lippert

คำตอบ:


159

เป็นเพราะชื่อไฟล์ต่างกัน (แม้ว่าเอาต์พุตสตริงจะเหมือนกัน) หากคุณลองแก้ไขไฟล์เอง (แทนที่จะมีสองไฟล์) คุณจะสังเกตเห็นว่าไบนารีของเอาต์พุตไม่แตกต่างกันอีกต่อไป ดังที่ทั้ง Jens และฉันพูดนั่นเป็นเพราะ GCC ทิ้งข้อมูลเมตาทั้งหมดลงในไบนารีที่สร้างขึ้นรวมถึงชื่อไฟล์ต้นฉบับที่แน่นอน (และ AFAICS ก็ส่งเสียงดัง)

ลองสิ่งนี้:

$ cp code.c code2.c subdir/code.c
$ gcc code.c -o a
$ gcc code2.c -o b
$ gcc subdir/code.c -o a2
$ diff a b
Binary files a and b differ
$ diff a2 b
Binary files a2 and b differ
$ diff -s a a2
Files a and a2 are identical

สิ่งนี้อธิบายว่าเหตุใด md5sums ของคุณจึงไม่เปลี่ยนแปลงระหว่างบิลด์ แต่แตกต่างกันระหว่างไฟล์ต่างๆ หากคุณต้องการคุณสามารถทำตามที่ Jens แนะนำและเปรียบเทียบผลลัพธ์ของstringsแต่ละไบนารีคุณจะสังเกตเห็นว่าชื่อไฟล์ฝังอยู่ในไบนารี หากคุณต้องการ "แก้ไข" สิ่งนี้คุณสามารถstripใช้ไบนารีได้และข้อมูลเมตาจะถูกลบออก:

$ strip a a2 b
$ diff -s a b
Files a and b are identical
$ diff -s a2 b
Files a2 and b are identical
$ diff -s a a2
Files a and a2 are identical

แก้ไข: อัปเดตเพื่อบอกว่าคุณสามารถตัดไบนารีเพื่อ "แก้ไข" ปัญหาได้
cyphar

30
และนี่คือเหตุผลที่คุณควรเปรียบเทียบเอาต์พุตการประกอบไม่ใช่การตรวจสอบ MD5
Lightness Races ใน Orbit

1
ฉันได้ถามคำถามที่ติดตามที่นี่
Federico Poloni

4
ขึ้นอยู่กับรูปแบบไฟล์อ็อบเจ็กต์เวลาคอมไพล์จะถูกเก็บไว้ในอ็อบเจ็กต์ไฟล์ด้วย ดังนั้นการใช้ไฟล์ COFF สำหรับไฟล์ตัวอย่าง a และ a2 จะไม่เหมือนกัน
Martin Rosenau

28

สาเหตุส่วนใหญ่คือชื่อไฟล์และการประทับเวลาที่คอมไพเลอร์เพิ่ม (โดยปกติจะอยู่ในส่วนข้อมูลการดีบักของส่วน ELF)

ลองวิ่ง

 $ strings -a program > x
 ...recompile program...
 $ strings -a program > y
 $ diff x y

และคุณอาจเห็นเหตุผล ฉันเคยใช้สิ่งนี้เพื่อค้นหาว่าเหตุใดแหล่งที่มาเดียวกันจึงทำให้เกิดรหัสที่แตกต่างกันเมื่อคอมไพล์ในไดเรกทอรีต่างกัน การค้นพบคือ__FILE__มาโครขยายเป็นชื่อไฟล์ที่สมบูรณ์ซึ่งแตกต่างกันในต้นไม้ทั้งสอง


1
อ้างอิงจากgcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (ฉันรู้ว่าล้าสมัย) พวกเขาไม่บันทึกการประทับเวลาและอาจเป็นปัญหาตัวเชื่อมโยง แม้ว่าฉันจำได้ว่าได้อ่านเรื่องราวเมื่อเร็ว ๆ นี้เกี่ยวกับวิธีที่ บริษัท รักษาความปลอดภัยระบุลักษณะนิสัยการทำงานของทีมแฮ็กโดยใช้ข้อมูลการประทับเวลา GCC ในไบนารีของพวกเขา
cyphar

3
และไม่ต้องพูดถึงว่า OP ระบุว่า "md5sums จับคู่กับการคอมไพล์หลายรายการที่มีแฟล็กเดียวกัน" ซึ่งบ่งชี้ว่าอาจไม่ใช่การประทับเวลาที่เป็นสาเหตุของปัญหา อาจเกิดจากการที่ชื่อไฟล์ต่างกัน
cyphar

1
@cyphar ชื่อไฟล์ที่แตกต่างกันควรถูกจับโดยวิธีสตริง / diff เช่นกัน
Jens

15

หมายเหตุ : โปรดจำไว้ว่าชื่อไฟล์ต้นฉบับจะอยู่ในไบนารีที่ไม่ถูกตัดดังนั้นสองโปรแกรมที่มาจากไฟล์ต้นฉบับที่มีชื่อต่างกันจะมีแฮชที่แตกต่างกัน

ในสถานการณ์ที่คล้ายคลึงกันหากไม่นำไปใช้ข้างต้นคุณสามารถลอง:

  • ทำงานstripกับไบนารีเพื่อขจัดไขมันบางส่วน หากไบนารีที่แยกออกมาเหมือนกันแสดงว่าเป็นข้อมูลเมตาบางส่วนที่ไม่จำเป็นต่อการทำงานของโปรแกรม
  • สร้างออกกลางที่ชุมนุมเพื่อตรวจสอบว่าความแตกต่างไม่ได้อยู่ในคำแนะนำที่แท้จริงของ CPU (หรืออย่างไรจะดีกว่าที่ระบุความแตกต่างจริงเป็น )
  • ใช้stringsหรือถ่ายโอนข้อมูลทั้งสองโปรแกรมไปยังฐานสิบหกและเรียกใช้ความแตกต่างบนฐานข้อมูลฐานสิบหกสองชุด เมื่อพบความแตกต่างแล้วคุณอาจลองดูว่ามีคำคล้องจองหรือเหตุผลสำหรับพวกเขาหรือไม่ (PID, การประทับเวลา, การประทับเวลาของไฟล์ต้นฉบับ ... ) ตัวอย่างเช่นคุณอาจมีกิจวัตรในการจัดเก็บการประทับเวลาในเวลาคอมไพล์เพื่อวัตถุประสงค์ในการวินิจฉัย

ระบบของฉันคือgcc (GCC) 5.2.0และLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
ผู้ใช้ที่ลงทะเบียน

2
คุณควรพยายามจริงทำให้สองไฟล์แยกต่างหาก ฉันไม่สามารถสร้างซ้ำได้ด้วยการแก้ไขไฟล์เดียว
cyphar

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