เหตุใดตำแหน่งที่คอมไพล์โค้ดทั้งหมดจึงไม่เป็นอิสระ


87

เมื่อคอมไพล์ไลบรารีที่แบ่งใช้ใน gcc อ็อพชัน -fPIC จะคอมไพล์โค้ดโดยไม่ขึ้นกับตำแหน่ง มีเหตุผลใดบ้าง (ประสิทธิภาพหรืออย่างอื่น) ทำไมคุณถึงไม่รวบรวมตำแหน่งโค้ดทั้งหมดโดยไม่ขึ้นกับใคร?


2
แต่ wowest ไม่ถูกต้องทั้งหมด การเรียกใช้ฟังก์ชันและการกระโดดจำนวนมากใช้การกระโดดแบบสัมพัทธ์ดังนั้นพวกเขาจึงไม่จำเป็นต้องใช้ตารางกระโดดหลังจากที่เคลื่อนที่ไปมา
Unknown

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

@ojblass ที่ฉันหมายถึงก็คือการกระโดดบางอย่างก็เหมือนกับ "กระโดดไปข้างหน้า 50 คำสั่ง" หรือ "กระโดด 5 คำสั่งไปข้างหลัง" แทนที่จะเป็น "ข้ามไปที่ 0x400000" ดังนั้นจะบอกว่าคุณต้องโหลดที่อยู่ทุกครั้งด้วย -fPIC นั้นไม่เป็นความจริงทั้งหมด
Unknown

บทความ Wikipedia ให้คำอธิบายที่ดี โดยทั่วไปในบางสถาปัตยกรรมไม่มีวิธีโดยตรงที่จะข้ามไปยังที่อยู่แบบสัมพัทธ์ ดังนั้น PIC จึงมีราคาแพงกว่าที่จะใช้กับส่วนโค้งเหล่านั้น ดูคำตอบของ @ EvanTeran สำหรับข้อมูลเพิ่มเติม
Alexei Sholik

คำตอบ:


68

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


33

บทความนี้จะอธิบายถึงวิธีการทำงานของ PIC และเปรียบเทียบกับทางเลือก - การย้ายเวลาในการโหลด ฉันคิดว่ามันเกี่ยวข้องกับคำถามของคุณ


16
@ นิค: ไม่เห็นด้วย ถ้ามันช่วยให้ผู้ถามมันเป็นคำตอบ การชี้ไปที่บทความที่เกี่ยวข้องหรือสองบทความสามารถให้ข้อมูลมากมาย
Eli Bendersky

5
ไม่มีข้อสรุปในโพสต์นี้เป็นเพียงลิงก์ไปยังบทความ ไม่มีแม้แต่เบาะแสที่ไม่ได้ใช้ PIC เป็นค่าเริ่มต้นเนื่องจากปัญหาด้านประสิทธิภาพ
นิค

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

4
@Rob: สิ่งที่ได้ผลคือการแนะนำให้แก้ไขและไม่ใช้ความคิดเห็นในการส่งเสียงหอน คำตอบนี้มีอายุ 4 ปี ย้อนกลับไปตอนนั้น SO มีกฎที่เข้มงวดน้อยกว่าว่าคำตอบควรมีลักษณะอย่างไร
Eli Bendersky

6
โพสต์นี้ปรากฏภายใต้ "การตรวจสอบ" ขอให้ฉันทำและฉันก็ทำ มีคนอื่นตั้งค่าสถานะ "ความคิดเห็นหอน" ผลิตโดย SO ไม่ใช่ฉัน
Rob

27

ใช่มีเหตุผลด้านประสิทธิภาพ การเข้าถึงบางอย่างอยู่ภายใต้การกำหนดทิศทางอีกชั้นหนึ่งอย่างมีประสิทธิภาพเพื่อให้ได้ตำแหน่งที่แน่นอนในหน่วยความจำ

นอกจากนี้ยังมี GOT (Global offset table) ซึ่งเก็บค่าชดเชยของตัวแปรส่วนกลาง สำหรับฉันนี่ดูเหมือนตารางฟิกซ์อัพของ IAT ซึ่งจัดเป็นตำแหน่งที่ขึ้นอยู่กับวิกิพีเดียและแหล่งข้อมูลอื่น ๆ

http://en.wikipedia.org/wiki/Position_independent_code


23

นอกเหนือจากคำตอบที่ได้รับการยอมรับ สิ่งหนึ่งที่ส่งผลเสียต่อประสิทธิภาพของรหัส PIC เป็นอย่างมากคือการไม่มี "ที่อยู่ IP สัมพัทธ์" บน x86 ด้วย "ที่อยู่แบบสัมพัทธ์ IP" คุณสามารถขอข้อมูลที่มีขนาด X ไบต์จากตัวชี้คำสั่งปัจจุบัน สิ่งนี้จะทำให้รหัส PIC ง่ายขึ้นมาก

การกระโดดและการโทรมักจะสัมพันธ์กับ EIP ดังนั้นสิ่งเหล่านี้จึงไม่ก่อให้เกิดปัญหา อย่างไรก็ตามการเข้าถึงข้อมูลจะต้องใช้กลอุบายเพิ่มเติมเล็กน้อย บางครั้งรีจิสเตอร์จะถูกสงวนไว้ชั่วคราวเป็น "ตัวชี้ฐาน" สำหรับข้อมูลที่โค้ดต้องการ ตัวอย่างเช่นเทคนิคทั่วไปคือการใช้วิธีการโทรบน x86 ในทางที่ผิด:

เทคนิคนี้และเทคนิคอื่น ๆ จะเพิ่มชั้นของทิศทางในการเข้าถึงข้อมูล ตัวอย่างเช่น GOT (Global offset table) ที่ใช้โดยคอมไพเลอร์ gcc

x86-64 เพิ่มโหมด "RIP ญาติ" ซึ่งจะทำให้สิ่งที่มากง่าย


1
IIRC MIPS ไม่มีการกำหนดแอดเดรสแบบสัมพันธ์กับพีซียกเว้นการกระโดดแบบสัมพัทธ์
phuclv

1
นี่เป็นเทคนิคทั่วไปที่ใช้ในเชลล์โค้ดเพื่อรับแอดเดรสที่เรียกใช้งาน ฉันใช้สิ่งนี้ในโซลูชัน CTF สองสามรายการ
sherrellbc

2

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

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

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


1

นอกจากนี้ฮาร์ดแวร์หน่วยความจำเสมือนในโปรเซสเซอร์สมัยใหม่ส่วนใหญ่ (ใช้โดยระบบปฏิบัติการที่ทันสมัยที่สุด) หมายความว่าโค้ดจำนวนมาก (แอปพื้นที่ผู้ใช้ทั้งหมดยกเว้นการใช้ mmap หรือสิ่งที่คล้ายกัน) ไม่จำเป็นต้องเป็นตำแหน่งที่ไม่ขึ้นกับตำแหน่ง ทุกโปรแกรมมีพื้นที่แอดเดรสของตัวเองซึ่งคิดว่าเริ่มต้นที่ศูนย์


4
แต่ถึงแม้จะมีรหัส VM-MMU PIC ก็จำเป็นเพื่อให้แน่ใจว่าไลบรารี. เดียวกันจะถูกโหลดลงในหน่วยความจำเพียงครั้งเดียวเมื่อใช้โดยไฟล์ปฏิบัติการอื่น
mmmmmmmm

1

position-independent code มีค่าใช้จ่ายด้านประสิทธิภาพในสถาปัตยกรรมส่วนใหญ่เนื่องจากต้องมีการลงทะเบียนเพิ่มเติม

ดังนั้นนี่คือจุดประสงค์ด้านประสิทธิภาพ


0

ปัจจุบันระบบปฏิบัติการและคอมไพเลอร์โดยค่าเริ่มต้นทำให้รหัสทั้งหมดเป็นรหัสอิสระของตำแหน่ง ลองคอมไพล์โดยไม่มีแฟล็ก -fPIC โค้ดจะคอมไพล์ได้ดี แต่คุณจะได้รับคำเตือนเท่านั้น OS เหมือน windows ใช้เทคนิคที่เรียกว่า memory mapping เพื่อให้ได้สิ่งนี้


-5

คำถามเกิดขึ้นในปี 2009 สิบปีผ่านไปและตอนนี้รหัสทั้งหมดเป็นตำแหน่งที่เป็นอิสระ ซึ่งตอนนี้บังคับใช้โดยระบบปฏิบัติการและคอมไพเลอร์ ไม่มีวิธีใดที่จะเลือกไม่ใช้ โค้ดทั้งหมดถูกบังคับด้วย PIE และแฟล็ก -no-pic / -no-pie จะถูกละเว้นซึ่งเป็นส่วนหนึ่งของข้ออ้าง ASLR นี้ เหตุผลก็คือการชะลอตัวแอพที่เร็วก่อนหน้านี้และขายฮาร์ดแวร์รุ่นใหม่ภายใต้หน้ากากของความปลอดภัยที่เพิ่มขึ้น นั่นเป็นเรื่องที่ไร้เหตุผลอย่างสิ้นเชิงเพราะตอนนี้หน่วยความจำขนาดใหญ่ทำให้เราสามารถกำจัดนรกของการเชื่อมโยงแบบไดนามิกได้เลยโดยรวบรวมแอปทั้งหมดแบบคงที่

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

คุณไม่บ่นเพราะคุณไม่รู้ด้วยซ้ำว่ารหัสของคุณถูกทำให้พิการโดยวงล้อฝึกเหล่านี้ ฉันจะว่าอย่างไรได้? เพลิดเพลินกับซอฟต์แวร์ที่ช้าลง 2 เท่าด้วย PIC ของพวกเขาตอนนี้! ยิ่งไปกว่านั้นด้วยการถือกำเนิดของ LLVM ในไม่ช้าจะมีการบังคับใช้ JIT (รหัสที่มีการจัดการ) โดยไม่มีการเข้าถึง x86 inline assembly ซึ่งจะทำให้โค้ด C / C ++ ช้าลง "ผู้ที่สละเสรีภาพเพื่อความมั่นคงก็ไม่สมควรได้รับเช่นกัน"


นั่นเป็นเพียงคำชี้แจงข้อเท็จจริง: 10 ปีที่แล้ว PIC เป็นทางเลือก แต่วันนี้เป็นค่าเริ่มต้นและบังคับ ฉันสงสัยว่าโค้ดที่ไม่ใช่ PIE จะได้รับการสนับสนุนใน OS รุ่นอื่น ๆ เช่นเดียวกับการรองรับโหมดจริงถูกยกเลิกหลังจาก Windows 9x ดังนั้นคำถามที่จะใช้หรือไม่ใช้ PIC จึงกลายเป็นหัวข้อวิทยาศาสตร์คอมพิวเตอร์เชิงทฤษฎีมากกว่าเว้นแต่คุณจะปลดล็อกระบบปฏิบัติการของคุณและเปิดใช้งานการสนับสนุนอีกครั้ง สิ่งที่สำคัญที่สุดที่ผู้คนต้องรู้เกี่ยวกับ PIC ก็คือมันช้าพอที่คอมไพเลอร์จนถึงตอนนี้รองรับการคอมไพล์แบบคงที่และ DLL ส่วนใหญ่มีเวอร์ชันคงที่
SmugLispWeenie

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

แค่พูดคุยกับผู้คนถามความคิดเห็นของพวกเขา ผมเองพบว่า PIC vs non-PIC กลายเป็นคำถามเกี่ยวกับอุดมการณ์ PIC คือการเขียนโปรแกรมที่เทียบเท่ากับลัทธิคอมมิวนิสต์ซึ่งมีการผลิตโค้ดจำนวนมากและทุกคนจะได้รับสำเนาเดียวกัน Non-PIC เป็นการเขียนโปรแกรมที่เทียบเท่ากับ Capitalism ซึ่งมีโค้ดเดียวกันหลายรุ่นที่แข่งขันกัน ดังนั้นผู้คนที่มีความคิดฝ่ายซ้ายมากขึ้นจึงสนับสนุน PIC โดยไม่รู้ตัวเพื่อพิสูจน์จุดที่อุดมการณ์ที่พวกเขาชื่นชอบสามารถทำงานได้อย่างน้อยก็ในคอมพิวเตอร์ คนเหล่านี้จะแนะนำให้คุณไม่พูดโดยใช้ libpng ที่ปรับเปลี่ยนเป็นการส่วนตัว
SmugLispWeenie

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