ฉันได้อ่านเกี่ยวกับ ตัวเลือกของ GCC สำหรับอนุสัญญาการสร้างรหัสแต่ไม่เข้าใจว่า "สร้างรหัสตำแหน่งที่ไม่ขึ้นกับตำแหน่ง (PIC)" อย่างไร โปรดยกตัวอย่างเพื่ออธิบายฉันว่ามันหมายความว่าอย่างไร
ฉันได้อ่านเกี่ยวกับ ตัวเลือกของ GCC สำหรับอนุสัญญาการสร้างรหัสแต่ไม่เข้าใจว่า "สร้างรหัสตำแหน่งที่ไม่ขึ้นกับตำแหน่ง (PIC)" อย่างไร โปรดยกตัวอย่างเพื่ออธิบายฉันว่ามันหมายความว่าอย่างไร
คำตอบ:
รหัสตำแหน่งอิสระหมายความว่ารหัสเครื่องที่สร้างขึ้นไม่ได้ขึ้นอยู่กับที่อยู่เฉพาะเพื่อให้ทำงานได้
เช่นการกระโดดจะถูกสร้างขึ้นเป็นญาติมากกว่าแน่นอน
หลอกประกอบ:
PIC: วิธีนี้จะใช้ได้ไม่ว่าจะเป็นรหัสที่อยู่ 100 หรือ 1,000
100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP
Non-PIC: วิธีนี้จะใช้ได้ก็ต่อเมื่อรหัสอยู่ที่ 100
100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP
แก้ไข: เพื่อตอบสนองต่อความคิดเห็น
หากรหัสของคุณถูกคอมไพล์ด้วย -fPIC มันเหมาะสำหรับการรวมไว้ในห้องสมุด - ห้องสมุดจะต้องสามารถย้ายจากตำแหน่งที่ต้องการในหน่วยความจำไปยังที่อยู่อื่นอาจมีอีกห้องสมุดที่โหลดอยู่แล้วตามที่อยู่ที่ห้องสมุดของคุณต้องการ
-fPIC
เมื่อคอมไพล์โปรแกรมหรือไลบรารีแบบสแตติกเนื่องจากมีเพียงหนึ่งโปรแกรมหลักเท่านั้นที่จะมีอยู่ในกระบวนการดังนั้นจึงไม่จำเป็นต้องทำการเปลี่ยนตำแหน่งรันไทม์ ในบางระบบโปรแกรมต่างๆยังคงอยู่ในตำแหน่งที่เป็นอิสระเพื่อความปลอดภัยที่เพิ่มขึ้น
ฉันจะพยายามอธิบายสิ่งที่พูดไปแล้วในวิธีที่ง่ายกว่า
เมื่อใดก็ตามที่มีการโหลด lib ที่ใช้ร่วมกันตัวโหลด (โค้ดบนระบบปฏิบัติการที่โหลดโปรแกรมใด ๆ ที่คุณเรียกใช้) จะเปลี่ยนที่อยู่บางอย่างในรหัสขึ้นอยู่กับว่าวัตถุนั้นโหลดไปที่ใด
ในตัวอย่างข้างต้น "111" ในโค้ดที่ไม่ใช่ PIC จะถูกเขียนโดยโหลดเดอร์ในครั้งแรกที่โหลด
สำหรับวัตถุที่ไม่ได้แชร์คุณอาจต้องการให้มันเป็นอย่างนั้นเพราะคอมไพเลอร์สามารถทำการเพิ่มประสิทธิภาพบางอย่างในรหัสนั้น
สำหรับวัตถุที่ใช้ร่วมกันหากกระบวนการอื่นจะต้องการ "เชื่อมโยง" กับรหัสนั้นเขาจะต้องอ่านมันไปยังที่อยู่เสมือนเดียวกันหรือ "111" จะไม่มีเหตุผล แต่พื้นที่เสมือนนั้นอาจถูกใช้งานแล้วในกระบวนการที่สอง
Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.
ฉันคิดว่าสิ่งนี้ไม่ถูกต้องหากรวบรวมด้วย -fpic และเหตุผลที่ -fpic มีอยู่สำหรับเหตุผลด้านประสิทธิภาพหรือเนื่องจากคุณมีตัวโหลดที่ไม่สามารถย้ายหรือเพราะคุณต้องการสำเนาหลายชุดในตำแหน่งที่ตั้งอื่นหรือด้วยเหตุผลอื่น ๆ อีกมากมาย
รหัสที่สร้างไว้ในไลบรารีที่ใช้ร่วมกันควรเป็นรหัสที่ขึ้นอยู่กับตำแหน่งดังนั้นไลบรารีที่ใช้ร่วมกันสามารถโหลดได้อย่างง่ายดายที่ (หรือมากกว่า) ที่อยู่ใด ๆ ในหน่วยความจำ -fPIC
ตัวเลือกเพื่อให้แน่ใจว่า GCC ผลิตรหัสดังกล่าว
-fPIC
ตั้งค่าสถานะไว้ มันไม่ได้เชื่อมโยงกับโปรแกรมหรือไม่ เมื่อโปรแกรมทำงานระบบปฏิบัติการจะอัปโหลดไปยังหน่วยความจำ ฉันพลาดอะไรไปรึเปล่า?
-fPIC
ใช้แฟล็กเพื่อให้แน่ใจว่า lib นี้สามารถโหลดไปยังที่อยู่เสมือนใด ๆในกระบวนการที่เชื่อมโยงหรือไม่ ขออภัยสำหรับความคิดเห็นสองครั้งที่ผ่านไป 5 นาทีไม่สามารถแก้ไขได้ก่อนหน้านี้
libwotnot.so
) และการเชื่อมโยงกับมัน ( -lwotnot
) -fPIC
ในขณะที่การเชื่อมโยงที่คุณไม่จำเป็นต้องเอะอะเกี่ยวกับ มันเคยเป็นกรณีที่เมื่อสร้างห้องสมุดที่ใช้ร่วมกันคุณจำเป็นต้องตรวจสอบให้แน่ใจว่า-fPIC
มีการใช้ไฟล์วัตถุทั้งหมดที่จะสร้างไว้ในห้องสมุดที่ใช้ร่วมกัน กฎอาจมีการเปลี่ยนแปลงเนื่องจากคอมไพเลอร์สร้างด้วยรหัส PIC โดยค่าเริ่มต้นวันนี้ ดังนั้นสิ่งที่สำคัญเมื่อ 20 ปีก่อนและอาจมีความสำคัญเมื่อ 7 ปีที่แล้วมีความสำคัญน้อยกว่าในทุกวันนี้ ที่อยู่นอกเคอร์เนล o / s คือ 'ที่อยู่เสมือน' เสมอ '
-fPIC
ดังนั้นก่อนหน้านี้คุณมีการเพิ่ม หากไม่ผ่านการตั้งค่าสถานะนี้รหัสที่สร้างขึ้นเมื่อสร้าง. so จำเป็นต้องโหลดไปยังที่อยู่เสมือนเฉพาะที่อาจมีการใช้งานหรือไม่
กำลังเพิ่มเพิ่มเติม ...
ทุกขั้นตอนมีพื้นที่ที่อยู่เสมือนที่เหมือนกัน (หากการสุ่มที่อยู่เสมือนถูกหยุดโดยใช้แฟล็กในระบบปฏิบัติการ linux) (สำหรับรายละเอียดเพิ่มเติมปิดการใช้งานและเปิดใช้งานการสุ่มตัวอย่างเค้าโครงพื้นที่ที่อยู่ซ้ำสำหรับตัวเองเท่านั้น )
ดังนั้นหากหนึ่ง exe ที่ไม่มีการเชื่อมโยงที่ใช้ร่วมกัน (สถานการณ์สมมุติ) เราสามารถให้ที่อยู่เสมือนเดียวกันกับคำสั่ง asm เดียวกันโดยไม่มีอันตรายใด ๆ
แต่เมื่อเราต้องการที่จะเชื่อมโยงวัตถุที่ใช้ร่วมกันกับ exe แล้วเราไม่แน่ใจว่าที่อยู่เริ่มต้นที่กำหนดให้กับวัตถุที่ใช้ร่วมกันมันจะขึ้นอยู่กับการสั่งซื้อวัตถุที่ใช้ร่วมกันถูกเชื่อมโยงที่กล่าวว่าการสอน asm ภายใน. ที่อยู่เสมือนที่แตกต่างกันขึ้นอยู่กับกระบวนการเชื่อมโยงไปยัง
ดังนั้นกระบวนการหนึ่งสามารถให้ที่อยู่เริ่มต้นเป็น. so ในฐานะที่เป็น 0x45678910 ในพื้นที่เสมือนของตัวเองและกระบวนการอื่น ๆ ในเวลาเดียวกันสามารถให้ที่อยู่เริ่มต้นของ 0x12131415 และถ้าพวกเขาไม่ใช้การกำหนดที่อยู่ญาติดังนั้นจะไม่ทำงานเลย
ดังนั้นพวกเขาจะต้องใช้โหมดที่อยู่ญาติและตัวเลือก fpic
ลิงก์ไปยังฟังก์ชันในไลบรารีแบบไดนามิกได้รับการแก้ไขเมื่อโหลดไลบรารีหรือเวลาทำงาน ดังนั้นทั้งไฟล์ปฏิบัติการและไลบรารีแบบไดนามิกจะถูกโหลดเข้าสู่หน่วยความจำเมื่อโปรแกรมทำงาน ที่อยู่หน่วยความจำที่โหลดไลบรารีแบบไดนามิกไม่สามารถกำหนดล่วงหน้าได้เนื่องจากที่อยู่คงที่อาจขัดแย้งกับไลบรารีแบบไดนามิกอื่นที่ต้องการที่อยู่เดียวกัน
มีสองวิธีที่ใช้กันทั่วไปในการจัดการกับปัญหานี้:
1.Relocation ตัวชี้และที่อยู่ทั้งหมดในรหัสได้รับการแก้ไขหากจำเป็นเพื่อให้พอดีกับที่อยู่โหลดจริง การย้ายใหม่ทำได้โดยตัวเชื่อมโยงและตัวโหลด
รหัส 2.Position อิสระ ที่อยู่ทั้งหมดในรหัสนั้นสัมพันธ์กับตำแหน่งปัจจุบัน อ็อบเจ็กต์ที่แบ่งใช้ในระบบที่คล้าย Unix จะใช้โค้ดที่ไม่ขึ้นกับตำแหน่งโดยค่าเริ่มต้น สิ่งนี้มีประสิทธิภาพน้อยกว่าการเปลี่ยนตำแหน่งหากโปรแกรมทำงานเป็นเวลานานโดยเฉพาะในโหมด 32 บิต
ชื่อ " รหัสตำแหน่งอิสระ " จริง ๆ แล้วหมายถึงต่อไปนี้:
ส่วนรหัสไม่มีที่อยู่แบบสัมบูรณ์ที่ต้องการการย้ายใหม่ แต่มีเพียงที่อยู่แบบสัมพันธ์เท่านั้น ดังนั้นส่วนรหัสสามารถโหลดได้ที่ที่อยู่หน่วยความจำโดยพลการและใช้ร่วมกันระหว่างกระบวนการหลายกระบวนการ
ส่วนข้อมูลไม่ถูกแชร์ระหว่างหลายกระบวนการเพราะมักจะมีข้อมูลที่เขียนได้ ดังนั้นส่วนข้อมูลอาจมีตัวชี้หรือที่อยู่ที่ต้องการย้ายที่อยู่
ฟังก์ชั่นสาธารณะและข้อมูลสาธารณะทั้งหมดสามารถแทนที่ได้ใน Linux หากฟังก์ชั่นในการปฏิบัติการหลักมีชื่อเดียวกับฟังก์ชั่นในวัตถุที่ใช้ร่วมกันแล้วรุ่นในหลักจะมีความสำคัญไม่เพียง แต่เมื่อถูกเรียกจากหลัก แต่ยังเมื่อถูกเรียกจากวัตถุที่ใช้ร่วมกัน ในทำนองเดียวกันเมื่อตัวแปรส่วนกลางใน main มีชื่อเดียวกันกับตัวแปรกลางในวัตถุที่ใช้ร่วมกันแล้วจะใช้อินสแตนซ์ใน main แม้ว่าจะเข้าถึงได้จากวัตถุที่ใช้ร่วมกัน
การรบกวนสัญลักษณ์ที่เรียกว่านี้มีวัตถุประสงค์เพื่อเลียนแบบพฤติกรรมของห้องสมุดคงที่
วัตถุที่ใช้ร่วมกันมีตารางของตัวชี้ไปยังฟังก์ชั่นของมันเรียกว่าขั้นตอนการเชื่อมโยงตาราง (PLT) และตารางของตัวชี้ไปยังตัวแปรที่เรียกว่าตารางการชดเชยทั่วโลก (GOT) เพื่อใช้คุณสมบัติ "แทนที่" การเข้าถึงฟังก์ชั่นและตัวแปรสาธารณะทั้งหมดผ่านตารางนี้
ps ในกรณีที่ไม่สามารถหลีกเลี่ยงการเชื่อมโยงแบบไดนามิกได้มีหลายวิธีในการหลีกเลี่ยงคุณสมบัติการระบุเวลาของรหัสตำแหน่งที่เป็นอิสระ
คุณสามารถอ่านเพิ่มเติมได้จากบทความนี้: http://www.agner.org/optimize/optimizing_cpp.pdf
ส่วนเพิ่มเติมเล็กน้อยของคำตอบที่โพสต์แล้ว: ไฟล์วัตถุที่ไม่ได้รวบรวมเป็นตำแหน่งที่อิสระสามารถเปลี่ยนตำแหน่งได้ พวกเขามีรายการตารางการย้าย
รายการเหล่านี้อนุญาตให้โหลดเดอร์ (บิตของรหัสที่โหลดโปรแกรมลงในหน่วยความจำ) เพื่อเขียนที่อยู่สัมบูรณ์เพื่อปรับเปลี่ยนสำหรับที่อยู่โหลดจริงในพื้นที่ที่อยู่เสมือน
ระบบปฏิบัติการจะพยายามแชร์ "คลังวัตถุที่ใช้ร่วมกัน" หนึ่งชุดที่โหลดเข้าสู่หน่วยความจำด้วยโปรแกรมทั้งหมดที่เชื่อมโยงไปยังไลบรารีวัตถุที่ใช้ร่วมกันนั้น
เนื่องจากพื้นที่ที่อยู่รหัส (ไม่เหมือนกับส่วนของพื้นที่ข้อมูล) จึงไม่จำเป็นต้องต่อเนื่องกันและเนื่องจากโปรแกรมส่วนใหญ่ที่เชื่อมโยงไปยังไลบรารีเฉพาะมีโครงสร้างการพึ่งพาไลบรารีค่อนข้างคงที่ซึ่งทำให้สำเร็จได้เป็นส่วนใหญ่ ในกรณีที่หายากเหล่านั้นซึ่งมีความคลาดเคลื่อนใช่มันอาจจำเป็นต้องมีสำเนาของไลบรารีวัตถุที่ใช้ร่วมกันสองชุดขึ้นไปในหน่วยความจำ
เห็นได้ชัดว่าความพยายามใด ๆ ที่จะสุ่มโหลดที่อยู่ของไลบรารีระหว่างโปรแกรมและ / หรืออินสแตนซ์ของโปรแกรม (เพื่อลดความเป็นไปได้ในการสร้างรูปแบบที่สามารถใช้ประโยชน์ได้) จะทำให้กรณีเช่นนี้เกิดขึ้นทั่วไป หนึ่งควรพยายามรวบรวมไลบรารีวัตถุที่ใช้ร่วมกันทั้งหมดเพื่อให้มีตำแหน่งที่เป็นอิสระ
เนื่องจากการโทรเข้าสู่ไลบรารีเหล่านี้จากเนื้อความของโปรแกรมหลักจะทำการเปลี่ยนตำแหน่งใหม่ซึ่งทำให้มีโอกาสน้อยกว่าที่จะต้องคัดลอกไลบรารีที่ใช้ร่วมกัน