.init
/ .fini
ไม่เลิกใช้ มันยังคงเป็นส่วนหนึ่งของมาตรฐานเอลฟ์และฉันกล้าพูดว่ามันจะเป็นตลอดไป โค้ดใน.init
/ .fini
ถูกรันโดยโหลดเดอร์ / รันไทม์ลิงเกอร์เมื่อโค้ดถูกโหลด / ไม่โหลด เช่นในแต่ละโหลดเอลฟ์ (ตัวอย่างเช่นไลบรารีที่แชร์) .init
จะถูกเรียกใช้ ยังคงเป็นไปได้ที่จะใช้กลไกนั้นเพื่อให้บรรลุในสิ่งเดียวกันกับ __attribute__((constructor))/((destructor))
ก็ยังคงเป็นไปได้ที่จะใช้กลไกที่ว่าเพื่อให้บรรลุเกี่ยวกับสิ่งเดียวกันเช่นเดียวกับมันเป็นโรงเรียนเก่า แต่ก็มีประโยชน์บางอย่าง
.ctors
.dtors
กลไก/ ตัวอย่างเช่นต้องการการสนับสนุนโดย system-rtl / loader / linker-script สิ่งนี้ยังห่างไกลจากความแน่นอนที่จะมีอยู่ในทุกระบบเช่นระบบฝังตัวที่ลึกซึ่งโค้ดทำงานบนโลหะเปลือย เช่นแม้ว่า__attribute__((constructor))/((destructor))
GCC จะได้รับการสนับสนุน แต่ก็ไม่แน่ใจว่ามันจะทำงานได้หรือไม่ขึ้นอยู่กับตัวเชื่อมโยงเพื่อจัดระเบียบและโหลดเดอร์ (หรือในบางกรณีรหัสบูต) เพื่อเรียกใช้ หากต้องการใช้.init
/ .fini
แทนวิธีที่ง่ายที่สุดคือการใช้แฟล็กลิงเกอร์: -init & -fini (เช่นจากบรรทัดคำสั่ง GCC ไวยากรณ์จะเป็น-Wl -init my_init -fini my_fini
)
ในระบบที่รองรับทั้งสองวิธีประโยชน์ที่เป็นไปได้ประการหนึ่งคือรหัส.init
นั้นทำงานก่อนหน้า.ctors
และรหัส.fini
หลังจากนั้น.dtors
นั้น ถ้าคำสั่งซื้อนั้นเกี่ยวข้องอย่างน้อยหนึ่งน้ำมันดิบ แต่เป็นวิธีที่ง่ายในการแยกความแตกต่างระหว่างฟังก์ชั่นเริ่มต้น / ออก
ข้อเสียเปรียบที่สำคัญคือคุณไม่สามารถมีได้มากกว่า_init
หนึ่ง_fini
ฟังก์ชันในแต่ละโมดูลที่สามารถโหลดได้ง่ายและอาจจะต้องมีการแยกส่วนของรหัส.so
มากกว่าแรงจูงใจ อีกวิธีหนึ่งคือเมื่อใช้วิธีการเชื่อมโยงที่อธิบายไว้ข้างต้นหนึ่งแทนที่ _init เดิมและ_fini
ฟังก์ชั่นเริ่มต้น (จัดทำโดยcrti.o
) นี่คือที่ทุกประเภทของการเริ่มต้นมักจะเกิดขึ้น (บน Linux นี่คือที่การกำหนดตัวแปรทั่วโลกจะเริ่มต้นได้) วิธีที่อธิบายไว้ที่นี่
สังเกตุในลิงค์ด้านบนว่า_init()
ไม่จำเป็นต้องมีการเรียงซ้อนกันไปตามต้นฉบับเนื่องจากยังคงใช้งานได้ call
ในแบบอินไลน์การชุมนุม แต่เป็น 86-ช่วยในการจำและเรียกฟังก์ชั่นจากการชุมนุมจะมีลักษณะที่แตกต่างอย่างสิ้นเชิงสำหรับสถาปัตยกรรมอื่น ๆ อีกมากมาย (เช่น ARM ตัวอย่าง) รหัส Ie ไม่โปร่งใส
.init
/ .fini
และ.ctors
/ .detors
กลไกมีความคล้ายคลึงกัน แต่ไม่มาก รหัสใน.init
/ .fini
รัน "ตามสภาพ" นั่นคือคุณสามารถมีฟังก์ชั่นได้หลายอย่างใน.init
/ .fini
แต่มันเป็นเรื่องยาก AFAIK ที่จะทำให้พวกเขาอยู่ที่นั่นอย่างโปร่งใส syntactically บริสุทธิ์ C โดยไม่ทำลายรหัสใน.so
ไฟล์เล็ก ๆมากมาย
.ctors
/.dtors
มีการจัดที่แตกต่างกว่า/.init
/.fini
.ctors
.dtors
section เป็นเพียงตารางที่มีตัวชี้ไปยังฟังก์ชั่นและ "ตัวเรียก" เป็นลูปที่ระบบจัดเตรียมไว้ให้ซึ่งจะเรียกแต่ละฟังก์ชั่นทางอ้อม เช่น loop-caller นั้นเป็นสถาปัตยกรรมที่เฉพาะเจาะจง แต่เนื่องจากเป็นส่วนหนึ่งของระบบ (หากมีอยู่นั่นคือทั้งหมด) มันไม่สำคัญ
ตัวอย่างต่อไปนี้เพิ่มพอยน์เตอร์ฟังก์ชั่นใหม่ให้กับ.ctors
ฟังก์ชั่นอาเรย์__attribute__((constructor))
ทำ (เมธอดสามารถอยู่ร่วมกัน__attribute__((constructor)))
ได้
#define SECTION( S ) __attribute__ ((section ( S )))
void test(void) {
printf("Hello\n");
}
void (*funcptr)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;
หนึ่งยังสามารถเพิ่มตัวชี้ฟังก์ชั่นไปยังส่วนที่คิดค้นขึ้นเองอย่างสมบูรณ์ สคริปต์ลิงเกอร์ที่ถูกแก้ไขและฟังก์ชั่นเพิ่มเติมที่เลียนแบบตัวโหลด.ctors
/ .dtors
วนซ้ำนั้นจำเป็นในกรณีดังกล่าว แต่ด้วยสิ่งนี้เราสามารถควบคุมลำดับการดำเนินการได้ดีขึ้นเพิ่มการโต้แย้งและการจัดการโค้ดส่งคืน (ในโครงการ C ++ ตัวอย่างเช่นมันจะมีประโยชน์หากต้องการบางสิ่งที่ทำงานก่อนหรือหลังตัวสร้างส่วนกลาง)
ฉันต้องการ __attribute__((constructor))/((destructor))
ที่เป็นไปได้มันเป็นทางออกที่ง่ายและสง่างามแม้ว่ามันจะรู้สึกเหมือนโกง สำหรับตัวแปลงสัญญาณโลหะเปลือยอย่างตัวฉันนี่ไม่ใช่ตัวเลือกเสมอไป
บางอ้างอิงที่ดีในหนังสือเล่มLinkers และรถตัก
#define __attribute__(x)
) หากคุณมีคุณสมบัติหลายอย่างเช่น__attribute__((noreturn, weak))
มันยากที่จะ "แมโครออก" หากมีวงเล็บชุดเดียว