เหตุใดบางไลบรารีที่ทำงานร่วมกันจึงสามารถเรียกใช้งานได้ราวกับว่าเป็นไฟล์ที่เรียกใช้งานได้หรือไม่


55

บนระบบ Linux แบบ 32 บิตให้เรียกใช้สิ่งนี้

$ /lib/libc.so.6

และในระบบ 64 บิตนี้

$ /lib/x86_64-linux-gnu/libc.so.6

ในเชลล์จัดเตรียมเอาต์พุตเช่นนี้:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

สาเหตุและวิธีการนี้เกิดขึ้นได้อย่างไรและเป็นไปได้อย่างไรที่จะทำเช่นเดียวกันในห้องสมุดสาธารณะอื่น

ผมมองไปที่/usr/libการหา executables /usr/lib/libvlc.so.5.5.0และฉันพบ เรียกใช้มันนำไปสู่ข้อผิดพลาดในการแบ่งกลุ่ม : - /


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

คำตอบ:


52

ไลบรารีนั้นมีmain()หน้าที่หรือจุดเข้าใช้งานที่เทียบเท่าและถูกรวบรวมในลักษณะที่เป็นประโยชน์ทั้งในรูปแบบที่ปฏิบัติการได้และเป็นวัตถุที่ใช้ร่วมกัน

นี่คือข้อเสนอแนะหนึ่งข้อเกี่ยวกับวิธีการทำเช่นนี้ถึงแม้ว่ามันจะไม่ได้ผลสำหรับฉัน

ต่อไปนี้เป็นอีกคำตอบสำหรับคำถามที่คล้ายกันเกี่ยวกับ SOซึ่งฉันจะลอกเลียนแบบปรับแต่งและเพิ่มคำอธิบายเล็กน้อย

ก่อนแหล่งที่มาสำหรับห้องสมุดตัวอย่างของเราtest.c:

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

รวบรวมว่า:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

ที่นี่เรากำลังรวบรวมไลบรารีที่ใช้ร่วมกัน ( -fPIC) แต่บอกตัวเชื่อมโยงว่าเป็นไฟล์เรียกทำงานปกติ ( -pie) และเพื่อสร้างตารางสัญลักษณ์ที่สามารถส่งออกได้ ( -Wl,-E) เพื่อให้สามารถเชื่อมโยงกับประโยชน์ได้

และถึงแม้ว่าfileจะบอกว่ามันเป็นวัตถุที่ใช้ร่วมกัน แต่มันทำงานเป็นปฏิบัติการได้:

> ./libtest.so 
./libtest.so: Hello!

ตอนนี้เราต้องดูว่ามันสามารถเชื่อมโยงแบบไดนามิกได้หรือไม่ ตัวอย่างโปรแกรมprogram.c:

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

การใช้externช่วยให้เราไม่ต้องสร้างส่วนหัว ตอนนี้รวบรวมว่า:

gcc program.c -L. -ltest

ก่อนที่เราจะสามารถเรียกใช้งานได้เราจำเป็นต้องเพิ่มพา ธ ของlibtest.soสำหรับตัวโหลดแบบไดนามิก:

export LD_LIBRARY_PATH=./

ขณะนี้:

> ./a.out
Test program.
./a.out: Hello!

และจะแสดงให้เห็นการเชื่อมโยงไปยังldd a.outlibtest.so

โปรดทราบว่าฉันสงสัยว่านี่เป็นวิธีรวบรวม glibc จริง ๆ เพราะมันอาจจะไม่พกพาได้เหมือน glibc ตัวเอง (ดูman gccเกี่ยวกับ-fPICและ-pieสวิตช์) แต่มันแสดงให้เห็นถึงกลไกพื้นฐาน สำหรับรายละเอียดที่แท้จริงคุณจะต้องดูที่ไฟล์ต้นฉบับ


1
คำตอบที่ดีขอบคุณ! :-) ฉันพยายามที่จะใช้nmในห้องสมุดสาธารณะ แต่มันไม่ใช่รุ่นแก้ปัญหา ดังนั้นทำไมlibvlcและคนอื่น ๆ ผิดพลาด?
Ho1

1
เนื่องจากไลบรารีที่ใช้ร่วมกันส่วนใหญ่ไม่ได้มีวัตถุประสงค์เพื่อให้ปฏิบัติการได้ GNU libcจึงเป็นข้อยกเว้น
goldilocks

ผมพบว่าสองคน: และld libpthread
Ho1

@ Ho1 ld.soมีความพิเศษในด้านอื่น ๆ ในระดับหนึ่งความสามารถในการปฏิบัติการที่แท้จริงนั้นมากกว่าความสามารถในการเชื่อมโยงแบบไดนามิกปกติ
Random832

1
ตัวเลือกด้านบนแม้ว่าจะสร้าง executable-shared-library แต่มันไม่สมบูรณ์ในแง่ที่ว่ามีการตั้งค่าสถานะข้อผิดพลาดเมื่อบาง executable พยายามที่จะเชื่อมโยงกับสิ่งนี้ การอ้างอิงตัวอย่างโดยละเอียดเพิ่มที่นี่: unix.stackexchange.com/a/479334/152034
parasrish

21

มาดำดิ่งกันเพื่อหาคำตอบในการสุ่ม glibc repo ใน github รุ่นนี้ยังมี“แบนเนอร์” version.cที่ไฟล์

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

ดังนั้นตัวเชื่อมโยง / คอมไพเลอร์รู้ได้อย่างไรว่านี่คือจุดเข้าใช้งานได้อย่างไร

ลองดำน้ำในMakefile ในธงลิงเกอร์มีธงที่น่าสนใจ:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

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

ภาษาคำสั่ง linker มีคำสั่งเฉพาะสำหรับการกำหนดคำสั่งปฏิบัติการแรกในไฟล์เอาต์พุต (จุดเข้าใช้งาน) อาร์กิวเมนต์เป็นชื่อสัญลักษณ์:

ENTRY (สัญลักษณ์)

เช่นเดียวกับการกำหนดสัญลักษณ์คำสั่ง ENTRY อาจถูกวางเป็นคำสั่งอิสระในไฟล์คำสั่งหรือในคำจำกัดความของส่วนในคำสั่ง SECTIONS ไม่ว่าอะไรก็ตามที่เหมาะสมกับโครงร่างของคุณมากที่สุด

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

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

ตัวอย่างเช่นคุณสามารถใช้กฎเหล่านี้เพื่อสร้างจุดเริ่มต้นด้วยคำสั่งการกำหนด: หากไม่มีการกำหนดสัญลักษณ์เริ่มต้นภายในไฟล์อินพุตของคุณคุณสามารถกำหนดได้โดยกำหนดค่าที่เหมาะสม ---

start = 0x2020;

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

start = other_symbol;

(สามารถดูเอกสารปัจจุบันได้ที่นี่ )

จริง ๆldlinker ไม่สร้างปฏิบัติการด้วยฟังก์ชั่นจุดเริ่มต้นถ้าคุณให้มันมีตัวเลือกบรรทัดคำสั่ง-e(การแก้ปัญหาในทางปฏิบัติส่วนใหญ่) ให้สัญลักษณ์ฟังก์ชั่นstartหรือที่อยู่สัญลักษณ์แทรกในแอสเซมเบลอร์

อย่างไรก็ตามโปรดทราบว่ามันไม่ชัดเจนว่ารับประกันว่าจะทำงานร่วมกับ linkers อื่น ๆ (ฉันไม่ทราบว่า lld ของ lvm มีธงเดียวกัน) เหตุใดจึงเป็นประโยชน์สำหรับวัตถุประสงค์อื่นนอกเหนือจากการให้ข้อมูลเกี่ยวกับไฟล์ดังนั้นฉันไม่ทราบ


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