คำตอบ:
มี (ในกรณีส่วนใหญ่รหัสส่วนลดที่แปลความหมาย) มีสองขั้นตอนในการรับจากซอร์สโค้ด (สิ่งที่คุณเขียน) ไปยังโค้ดที่เรียกใช้งานได้ (สิ่งที่คุณเรียกใช้)
สิ่งแรกคือการคอมไพล์ซึ่งเปลี่ยนซอร์สโค้ดเป็นโมดูลวัตถุ
การเชื่อมโยงที่สองคือสิ่งที่รวมโมดูลวัตถุเข้าด้วยกันเพื่อสร้างปฏิบัติการ
ความแตกต่างนั้นมีไว้เพื่อให้ห้องสมุดของบุคคลที่สามถูกรวมอยู่ในปฏิบัติการของคุณโดยที่คุณไม่ต้องเห็นซอร์สโค้ดของพวกเขา (เช่นไลบรารีสำหรับการเข้าถึงฐานข้อมูลการสื่อสารเครือข่ายและอินเทอร์เฟซผู้ใช้แบบกราฟิก) หรือสำหรับการคอมไพล์ ตัวอย่าง C และรหัสแอสเซมบลี) จากนั้นเชื่อมโยงทั้งหมดเข้าด้วยกัน
เมื่อคุณลิงก์ไฟล์ไปยังไฟล์ที่สามารถเรียกทำงานได้แบบสแตติกเนื้อหาของไฟล์นั้นจะถูกรวมในเวลาลิงก์ กล่าวอีกนัยหนึ่งเนื้อหาของไฟล์จะถูกแทรกลงในไฟล์สั่งการที่คุณจะเรียกใช้
เมื่อคุณลิงก์แบบไดนามิกตัวชี้ไปยังไฟล์ที่กำลังลิงก์อยู่ (เช่นชื่อไฟล์ของไฟล์) จะรวมอยู่ในแฟ้มที่ปฏิบัติการได้และเนื้อหาของไฟล์ดังกล่าวจะไม่รวมอยู่ในเวลาลิงก์ เฉพาะเมื่อคุณเรียกใช้ไฟล์ปฏิบัติการในภายหลังว่ามีการซื้อไฟล์ที่ลิงก์แบบไดนามิกเหล่านี้และพวกเขาจะซื้อเฉพาะไฟล์สำเนาที่อยู่ในหน่วยความจำของไฟล์ปฏิบัติการเท่านั้นไม่ใช่ไฟล์ในดิสก์
มันเป็นวิธีการเชื่อมโยงที่เลื่อนออกไป มีวิธีการที่เลื่อนออกไปมากขึ้น (เรียกว่าการรวมล่าช้าในบางระบบ) ซึ่งจะไม่นำไฟล์ที่เชื่อมโยงแบบไดนามิกจนกว่าคุณจะพยายามเรียกใช้ฟังก์ชันภายใน
ไฟล์ที่ลิงก์แบบสแตติกจะถูก 'ล็อค' เพื่อเรียกใช้งาน ณ เวลาลิงก์ดังนั้นจึงไม่มีการเปลี่ยนแปลง ไฟล์ที่ลิงก์แบบไดนามิกที่อ้างอิงโดยโปรแกรมเรียกทำงานสามารถเปลี่ยนแปลงได้เพียงแค่แทนที่ไฟล์บนดิสก์
สิ่งนี้อนุญาตให้อัปเดตการทำงานโดยไม่ต้องเชื่อมโยงรหัสอีกครั้ง ตัวโหลดจะทำการลิงก์ซ้ำทุกครั้งที่คุณเรียกใช้
นี่คือทั้งดีและไม่ดี - ในมือข้างหนึ่งก็ช่วยให้การปรับปรุงง่ายขึ้นและการแก้ไขข้อผิดพลาดในขณะที่มันสามารถนำไปสู่โปรแกรมที่หยุดทำงานหากการปรับปรุงจะเข้ากันไม่ได้ - บางครั้งก็รับผิดชอบใน "นรกนรก" พูดถึงในแอปพลิเคชั่นที่สามารถใช้งานไม่ได้หากคุณเปลี่ยนไลบรารี่ที่เชื่อมโยงแบบไดนามิกด้วยแอพพลิเคชั่นที่ไม่รองรับ (นักพัฒนาที่ทำสิ่งนี้
ในฐานะที่เป็น ตัวอย่างให้ดูขอให้กรณีของผู้ใช้ของพวกเขารวบรวมmain.c
ไฟล์แบบคงที่และเชื่อมโยงแบบไดนามิก
Phase Static Dynamic
-------- ---------------------- ------------------------
+---------+ +---------+
| main.c | | main.c |
+---------+ +---------+
Compile........|.........................|...................
+---------+ +---------+ +---------+ +--------+
| main.o | | crtlib | | main.o | | crtimp |
+---------+ +---------+ +---------+ +--------+
Link...........|..........|..............|...........|.......
| | +-----------+
| | |
+---------+ | +---------+ +--------+
| main |-----+ | main | | crtdll |
+---------+ +---------+ +--------+
Load/Run.......|.........................|..........|........
+---------+ +---------+ |
| main in | | main in |-----+
| memory | | memory |
+---------+ +---------+
คุณสามารถเห็นได้ในกรณีสแตติกที่โปรแกรมหลักและไลบรารีรันไทม์ C ถูกลิงก์พร้อมกัน ณ เวลาลิงก์ (โดยผู้พัฒนา) เนื่องจากโดยทั่วไปผู้ใช้จะไม่สามารถเชื่อมโยงไฟล์ที่เรียกใช้ซ้ำได้พวกเขาจะติดอยู่กับพฤติกรรมของไลบรารี
ในกรณีแบบไดนามิกโปรแกรมหลักจะเชื่อมโยงกับไลบรารีการนำเข้า C runtime (สิ่งที่ประกาศสิ่งที่อยู่ในไลบรารีแบบไดนามิก แต่ไม่ได้กำหนดจริง) วิธีนี้ช่วยให้ตัวเชื่อมโยงเชื่อมโยงแม้ว่ารหัสจริงจะหายไป
จากนั้นเมื่อรันไทม์ตัวโหลดระบบปฏิบัติการจะทำการเชื่อมโยงล่าช้าของโปรแกรมหลักด้วย C runtime DLL (ไดนามิกลิงก์ไลบรารีหรือไลบรารีที่แบ่งใช้หรือชื่ออื่น ๆ )
เจ้าของ C runtime สามารถปล่อย DLL ใหม่ได้ตลอดเวลาเพื่อให้การปรับปรุงหรือแก้ไขข้อบกพร่อง ตามที่ระบุไว้ก่อนหน้านี้มีทั้งข้อดีและข้อเสีย
.dll
หรือเป็น.so
ส่วนขยาย) - คิดว่าคำตอบเป็นการอธิบายแนวคิดแทนที่จะเป็นคำอธิบายที่ถูกต้อง และตามข้อความนี้เป็นตัวอย่างที่แสดงการเชื่อมโยงแบบสแตติกและแบบไดนามิกสำหรับไฟล์ C runtime ดังนั้นใช่นั่นคือสิ่งที่ `crt บ่งชี้ในพวกเขาทั้งหมด
ฉันคิดว่าคำตอบที่ดีสำหรับคำถามนี้ควรอธิบายว่าการเชื่อมโยงคืออะไร
เมื่อคุณรวบรวมรหัส C (ตัวอย่าง) มันจะถูกแปลเป็นภาษาเครื่อง เพียงลำดับของไบต์ที่เมื่อรันจะทำให้โปรเซสเซอร์เพิ่มลบเปรียบเทียบ "goto" อ่านหน่วยความจำเขียนหน่วยความจำเรียงลำดับของสิ่งนั้น สิ่งนี้ถูกเก็บไว้ในไฟล์ object (.o)
เมื่อนานมาแล้วนักวิทยาศาสตร์คอมพิวเตอร์ได้ประดิษฐ์สิ่งนี้ "รูทีนย่อย" ดำเนินการนี้-ก้อนของรหัสและผลตอบแทนที่นี่ ไม่นานก่อนที่พวกเขาจะรู้ว่ารูทีนย่อยที่มีประโยชน์ที่สุดสามารถเก็บไว้ในสถานที่พิเศษและใช้งานโดยโปรแกรมใด ๆ ที่ต้องการ
ในตอนแรกโปรแกรมเมอร์จะต้องเจาะที่อยู่หน่วยความจำที่รูทีนย่อยเหล่านี้ตั้งอยู่ที่ CALL 0x5A62
สิ่งที่ชอบ นี่เป็นที่น่าเบื่อและมีปัญหาหากที่อยู่หน่วยความจำเหล่านั้นจำเป็นต้องเปลี่ยน
ดังนั้นกระบวนการเป็นไปโดยอัตโนมัติ คุณเขียนโปรแกรมที่โทรและคอมไพเลอร์ไม่ทราบที่อยู่ของหน่วยความจำprintf()
printf
คอมไพเลอร์เพิ่งเขียนCALL 0x0000
และเพิ่มหมายเหตุลงในไฟล์อ็อบเจ็กต์ที่บอกว่า "ต้องแทนที่ 0x0000 นี้ด้วยตำแหน่งหน่วยความจำของprintf "
คงที่เชื่อมโยงหมายความว่าโปรแกรมลิงเกอร์ (แอฟริกาหนึ่งเรียกว่าLD ) เพิ่มprintf
's รหัสเครื่องโดยตรงไปยังแฟ้มที่ปฏิบัติการของคุณและเปลี่ยนแปลง 0x0000 printf
ไปยังที่อยู่ของ สิ่งนี้จะเกิดขึ้นเมื่อสร้างไฟล์ปฏิบัติการได้
การเชื่อมโยงแบบไดนามิกหมายความว่าขั้นตอนข้างต้นไม่เกิดขึ้น ไฟล์เรียกทำงานยังคงมีข้อความระบุว่า "ต้องแทนที่ 0x000 ด้วยตำแหน่งหน่วยความจำของ printf" โหลดเดอร์ของระบบปฏิบัติการต้องการค้นหารหัส printf โหลดลงในหน่วยความจำและแก้ไขที่อยู่ CALL ทุกครั้งที่มีการเรียกใช้โปรแกรมแต่ละครั้งที่มีการรันโปรแกรม
เป็นเรื่องปกติสำหรับโปรแกรมที่จะเรียกใช้ฟังก์ชั่นบางอย่างซึ่งจะถูกเชื่อมโยงแบบคงที่ (ฟังก์ชั่นไลบรารีมาตรฐานเช่น printf
มักจะเชื่อมโยงแบบคงที่) และฟังก์ชั่นอื่น ๆ ที่เชื่อมโยงแบบไดนามิก ส่วนที่คงที่ "กลายเป็นส่วนหนึ่ง" ของความสามารถในการปฏิบัติการและตัวแบบไดนามิก "เข้าร่วม" เมื่อเรียกใช้งาน
มีข้อดีและข้อเสียของทั้งสองวิธีและมีความแตกต่างระหว่างระบบปฏิบัติการ แต่เมื่อคุณไม่ได้ถามฉันจะจบที่นี่
ld
เอกสารGNU
ไลบรารีที่ลิงก์แบบสแตติกถูกลิงก์ในเวลาคอมไพล์ ไลบรารีที่ลิงก์แบบไดนามิกถูกโหลด ณ รันไทม์ การเชื่อมโยงแบบคงที่จะทำให้บิตไลบรารีลงในไฟล์เรียกทำงานของคุณ การลิงก์แบบไดนามิกจะทำเฉพาะการอ้างอิงกับไลบรารีเท่านั้น บิตสำหรับไลบรารีแบบไดนามิกมีอยู่ที่อื่นและสามารถสลับออกได้ในภายหลัง
เนื่องจากไม่มีการโพสต์ข้างต้นแสดงวิธีเชื่อมโยงบางสิ่งบางอย่างแบบคงที่และเห็นว่าคุณทำถูกต้องแล้วดังนั้นฉันจะแก้ไขปัญหานี้:
โปรแกรม C อย่างง่าย
#include <stdio.h>
int main(void)
{
printf("This is a string\n");
return 0;
}
ลิงก์โปรแกรม C แบบไดนามิก
gcc simpleprog.c -o simpleprog
และทำงานfile
บนไบนารี:
file simpleprog
และนั่นจะแสดงให้เห็นว่ามีการเชื่อมโยงบางสิ่งบางอย่างตามแนวของ:
"simpleprog: ELF 64- บิต LSB ที่ปฏิบัติการได้, x86-64, รุ่น 1 (SYSV), ลิงก์แบบไดนามิก (ใช้ libs ที่ใช้ร่วมกัน), สำหรับ GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0
ให้เราเชื่อมโยงโปรแกรมในเวลานี้:
gcc simpleprog.c -static -o simpleprog
การเรียกใช้ไฟล์ในไบนารีที่เชื่อมโยงแบบคงที่นี้จะแสดง:
file simpleprog
"simpleprog: ELF 64- บิต LSB ที่ปฏิบัติการได้, x86-64, รุ่น 1 (GNU / Linux), การเชื่อมโยงแบบคงที่, สำหรับ GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, ไม่ได้ถูกสแตติก"
และคุณสามารถเห็นมันเชื่อมโยงอย่างมีความสุขแบบคงที่ น่าเสียดายที่ไม่ใช่ว่าทุกไลบรารีจะง่ายต่อการเชื่อมโยงแบบคงที่และอาจต้องใช้ความพยายามเพิ่มเติมlibtool
หรือใช้การเชื่อมโยงรหัสวัตถุและไลบรารี C ด้วยมือ
โชคดีที่ไลบรารี C แบบฝังจำนวนมากเช่นmusl
เสนอตัวเลือกการเชื่อมโยงแบบคงที่สำหรับเกือบทั้งหมดหากไม่ใช่ไลบรารีทั้งหมด
ตอนนี้strace
ไบนารีที่คุณสร้างขึ้นและคุณสามารถเห็นว่าไม่มีไลบรารีใด ๆ ที่เข้าถึงได้ก่อนที่โปรแกรมจะเริ่มต้น:
strace ./simpleprog
ตอนนี้เปรียบเทียบกับผลลัพธ์ของstrace
ในโปรแกรมที่เชื่อมโยงแบบไดนามิกและคุณจะเห็นว่า strace ของเวอร์ชันที่เชื่อมโยงแบบคงที่นั้นสั้นกว่ามาก!
(ฉันไม่ทราบ C # แต่เป็นที่น่าสนใจที่จะมีแนวคิดการเชื่อมโยงแบบคงที่สำหรับภาษา VM)
การเชื่อมโยงแบบไดนามิกเกี่ยวข้องกับการรู้วิธีการค้นหาฟังก์ชั่นที่จำเป็นซึ่งคุณมีเพียงการอ้างอิงจากโปรแกรมของคุณ ภาษาของคุณรันไทม์หรือ OS ค้นหารหัสบนระบบแฟ้มเครือข่ายหรือแคชรหัสที่คอมไพล์จับคู่ข้อมูลอ้างอิงจากนั้นใช้มาตรการหลายอย่างเพื่อรวมเข้ากับภาพโปรแกรมของคุณในหน่วยความจำเช่นการย้าย พวกเขาทั้งหมดจะทำที่รันไทม์ มันสามารถทำได้ด้วยตนเองหรือโดยคอมไพเลอร์ มีความสามารถในการอัปเดตโดยมีความเสี่ยงที่จะเกิดความสับสน (เช่น DLL hell)
การเชื่อมโยงสแตติกทำในเวลาคอมไพล์ซึ่งคุณบอกคอมไพเลอร์ว่าส่วนการทำงานทั้งหมดอยู่ที่ไหนและสั่งให้รวมเข้าด้วยกัน ไม่มีการค้นหาไม่มีความกำกวมไม่มีความสามารถในการอัปเดตโดยไม่ต้องคอมไพล์ใหม่ การอ้างอิงทั้งหมดของคุณนั้นเป็นรูปแบบเดียวกับอิมเมจโปรแกรม