จุดประสงค์ของการลงทะเบียนตัวชี้เฟรม EBP คืออะไร?


96

ฉันเป็นมือใหม่ในภาษาแอสเซมบลีและสังเกตว่าโค้ด x86 ที่คอมไพเลอร์ปล่อยออกมามักจะทำให้ตัวชี้เฟรมอยู่รอบ ๆ แม้ในโหมดรีลีส / ปรับให้เหมาะสมเมื่อสามารถใช้EBPรีจิสเตอร์สำหรับอย่างอื่นได้

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


20
หากคุณคิดว่า x86 มีรีจิสเตอร์น้อยมากคุณควรตรวจสอบ 6502 :)
Sedat Kapanoglu


1
C99 VLA ยังสามารถได้รับประโยชน์จากมัน
Ciro Santilli 郝海东冠状病六四事件法轮功


1
ตัวชี้เฟรมไม่ทำให้ตัวชี้สแต็กซ้ำซ้อน? . TL; DR: 1.การจัดตำแหน่งสแต็กที่ไม่สำคัญ2. การจัดสรรสแต็ก ( alloca) 3.ความสะดวกในการใช้งานรันไทม์: การจัดการข้อยกเว้น, แซนด์บ็อกซ์, GC
Alexander Malakhov

คำตอบ:


102

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

ทำไมคอมไพเลอร์ไม่ทิ้งตัวชี้เฟรม เนื่องจากด้วยตัวชี้เฟรมตัวดีบักเกอร์จึงสามารถทราบได้ว่าตัวแปรและอาร์กิวเมนต์ในพื้นที่ใช้ตารางสัญลักษณ์ที่ใดเนื่องจากมีการรับประกันว่าจะชดเชยกับ EBP อย่างต่อเนื่อง มิฉะนั้นจะไม่มีวิธีง่ายๆในการระบุว่าตัวแปรท้องถิ่นอยู่ที่จุดใดในโค้ด

ดังที่ Greg กล่าวไว้มันยังช่วยคลายสแต็กสำหรับดีบักเกอร์เนื่องจาก EBP มีรายการที่เชื่อมโยงแบบย้อนกลับของสแต็กเฟรมดังนั้นให้ดีบักเกอร์หาขนาดของสแต็กเฟรม (ตัวแปรโลคัล + อาร์กิวเมนต์) ของฟังก์ชัน

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


10
คอมไพเลอร์อาจรู้ว่ามันทำอะไรกับ ESP คะแนนอื่น ๆ ใช้ได้แม้ว่า +1
erikkallen

8
ดีบักเกอร์สมัยใหม่สามารถทำสแต็กแบ็กเทรซได้แม้ในโค้ดที่คอมไพล์ด้วย-fomit-frame-pointer. การตั้งค่านั้นเป็นค่าเริ่มต้นใน gcc ล่าสุด
Peter Cordes

2
@SedatKapanoglu: ส่วนข้อมูลบันทึกข้อมูลที่จำเป็น: yosefk.com/blog/…
Peter Cordes

3
@SedatKapanoglu: .eh_frame_hdrส่วนนี้ใช้สำหรับข้อยกเว้นรันไทม์ด้วย คุณจะพบมัน (with objdump -h) ในไบนารีส่วนใหญ่บนระบบ Linux มันประมาณ 16k สำหรับ/bin/bashเทียบกับ 572B สำหรับ GNU /bin/true, 108k สำหรับffmpeg. มีตัวเลือก gcc ในการปิดใช้งานการสร้างมัน แต่เป็นส่วนข้อมูล "ปกติ" ไม่ใช่ส่วนดีบักที่stripลบโดยค่าเริ่มต้น มิฉะนั้นคุณจะไม่สามารถย้อนกลับผ่านฟังก์ชันไลบรารีที่ไม่มีสัญลักษณ์ดีบักได้ ส่วนนั้นอาจใหญ่กว่าpush/mov/popคำแนะนำที่แทนที่ แต่มีต้นทุนรันไทม์ใกล้ศูนย์ (เช่นแคช uop)
Peter Cordes

3
เกี่ยวกับ "เช่นพารามิเตอร์แรกจะอยู่ที่ EBP-4 เสมอ": พารามิเตอร์แรกบน EBP + 8 (บน x86) ไม่ใช่หรือ
Aydin K.

31

เพียงแค่เพิ่มสองเซ็นต์ของฉันในคำตอบที่ดีอยู่แล้ว

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

ความคิดที่ว่ามันกำลังป้องกันไม่ให้ใช้การลงทะเบียนที่ดีอย่างสมบูรณ์ในการเพิ่มประสิทธิภาพทำให้เกิดคำถาม: เมื่อไหร่และที่ไหนที่การเพิ่มประสิทธิภาพคุ้มค่าจริง ๆ ?

การเพิ่มประสิทธิภาพจะคุ้มค่าในการวนซ้ำที่แน่นเท่านั้นซึ่ง 1) ไม่เรียกใช้ฟังก์ชัน 2) ที่ตัวนับโปรแกรมใช้เวลาส่วนสำคัญและ 3) ในโค้ดที่คอมไพเลอร์จะเห็นจริง ๆ (เช่นฟังก์ชันที่ไม่ใช่ไลบรารี) โดยปกติจะเป็นส่วนเล็ก ๆ ของโค้ดโดยรวมโดยเฉพาะในระบบขนาดใหญ่

รหัสอื่น ๆ สามารถบิดและบีบเพื่อกำจัดรอบและมันก็ไม่สำคัญเพราะตัวนับโปรแกรมไม่เคยอยู่ที่นั่น

ฉันรู้ว่าคุณไม่ได้ถามสิ่งนี้ แต่จากประสบการณ์ของฉันปัญหาด้านประสิทธิภาพ 99% ไม่มีอะไรเกี่ยวข้องกับการเพิ่มประสิทธิภาพคอมไพเลอร์เลย พวกเขามีทุกสิ่งที่เกี่ยวข้องกับการออกแบบมากเกินไป


ขอบคุณ @ ไมค์ฉันพบว่าคำตอบของคุณมีประโยชน์มาก
sixtyfootersdude

2
การใช้ตัวชี้เฟรมยังช่วยให้คุณประหยัดคำแนะนำสองสามคำทุกการเรียกใช้ฟังก์ชันซึ่งเป็นการเพิ่มประสิทธิภาพเพียงเล็กน้อยด้วยตัวมันเอง BTW การใช้ "ถามคำถาม" ของคุณไม่ถูกต้อง คุณหมายถึง "ตั้งคำถาม"
augurar

@augurar: คงที่. ขอบคุณ. ฉันรู้สึกไม่พอใจกับไวยากรณ์เล็กน้อย :)
Mike Dunlavey

3
ภาษา @augurar วิวัฒนาการ: "ถามคำถาม" ตอนนี้หมายถึง "ทำให้เกิดคำถาม" การเป็น nitpicker ผู้กำหนดล่วงหน้าสำหรับการใช้งานที่ล้าสมัยไม่ได้เพิ่มอะไรเลย
user3364825

9

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

คอมไพเลอร์อาจเลือกที่จะรักษารีจิสเตอร์ EBP เพื่อช่วยในการคลายสแต็กระหว่างการจัดการข้อยกเว้น แต่อีกครั้งขึ้นอยู่กับการใช้งานคอมไพเลอร์ที่แม่นยำ


คอมไพเลอร์ส่วนใหญ่เริ่มต้น-fomit-frame-pointerเมื่อเปิดใช้งานการเพิ่มประสิทธิภาพ (เมื่อ ABI อนุญาต) GCC, clang, ICC และ MSVC ทำได้ทั้งหมด IIRC แม้ว่าจะกำหนดเป้าหมายเป็น Windows 32 บิตก็ตาม ใช่คำตอบของฉันเกี่ยวกับเหตุใดการใช้ ebp จึงดีกว่าการลงทะเบียน esp เพื่อค้นหาพารามิเตอร์บนสแต็ก แสดงให้เห็นว่าแม้แต่ Windows 32 บิตก็สามารถละเว้นตัวชี้เฟรมได้ 32-bit x86 Linux ทำได้และทำได้แน่นอน และแน่นอนว่า ABI 64 บิตอนุญาตให้ละเว้นตัวชี้เฟรมตั้งแต่เริ่มต้น
Peter Cordes

4

อย่างไรก็ตาม x86 มีรีจิสเตอร์น้อยมาก

นี่เป็นความจริงในแง่ที่ว่า opcodes สามารถระบุรีจิสเตอร์ได้ 8 รายการ ตัวโปรเซสเซอร์เองจะมีรีจิสเตอร์มากกว่านั้นจริงๆและใช้การเปลี่ยนชื่อรีจิสเตอร์การวางท่อการดำเนินการเก็งกำไรและ Buzzwords ตัวประมวลผลอื่น ๆ เพื่อหลีกเลี่ยงขีด จำกัด นั้น วิกิพีเดียมีวรรคเบื้องต้นที่ดีเป็นสิ่งที่หน่วยประมวลผล x86 สามารถทำได้เพื่อเอาชนะขีด จำกัด ลงทะเบียน: http://en.wikipedia.org/wiki/X86#Current_implementations


1
คำถามเดิมเกี่ยวกับรหัสที่สร้างขึ้นซึ่ง จำกัด เฉพาะการลงทะเบียนที่อ้างอิงได้โดย opcodes
Darron

1
ใช่ แต่นี่คือสาเหตุที่การกำหนดตัวชี้เฟรมในงานสร้างที่ปรับให้เหมาะสมไม่สำคัญเท่าในปัจจุบัน
Michael

1
การเปลี่ยนชื่อการลงทะเบียนไม่เหมือนกับการมีการลงทะเบียนจำนวนมาก ยังคงมีสถานการณ์มากมายที่การเปลี่ยนชื่อทะเบียนจะไม่ช่วยได้ แต่การลงทะเบียน "ปกติ" จะมีมากขึ้น
jalf

1

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

คุณประหยัดไปเท่าไหร่ในการลงทะเบียนบริสุทธิ์? คุ้มมั้ย?


การลงทะเบียนเพิ่มเติมถูก จำกัด โดยการเข้ารหัสคำสั่ง x86-64 ใช้บิตในไบต์คำนำหน้า REX เพื่อขยายส่วนที่ระบุการลงทะเบียนของคำแนะนำจาก 3 เป็น 4 บิตสำหรับการลงทะเบียน src และ dest หากมีที่ว่าง x86-64 อาจไปที่การลงทะเบียนสถาปัตยกรรม 32 รายการแม้ว่าการบันทึก / เรียกคืนจำนวนมากบนสวิตช์บริบทจะเริ่มเพิ่มขึ้น 15 เป็นขั้นตอนที่ใหญ่ขึ้นจาก 7 แต่ 31 เป็นการปรับปรุงที่น้อยกว่ามากในกรณีส่วนใหญ่ (ไม่นับสแต็กพอยน์เตอร์เป็นแบบใช้งานทั่วไป) การพุช / ป๊อปอย่างรวดเร็วนั้นยอดเยี่ยมสำหรับมากกว่าสแต็กเฟรม แม้ว่าจะไม่เป็นการแลกเปลี่ยนกับ # ของ regs
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.