.init/ .finikhông được phản đối. Nó vẫn là một phần của tiêu chuẩn ELF và tôi dám nói nó sẽ là mãi mãi. Mã trong .init/ .finiđược chạy bởi trình tải / thời gian chạy liên kết khi mã được tải / không tải. Tức là trên mỗi tải ELF (ví dụ mã thư viện dùng chung) .initsẽ được chạy. Vẫn có thể sử dụng cơ chế đó để đạt được điều tương tự như với __attribute__((constructor))/((destructor)). Đó là trường học cũ nhưng nó có một số lợi ích.
.ctors/ .dtorscơ chế ví dụ yêu cầu hỗ trợ bởi system-rtl / loader / linker-script. Điều này là không chắc chắn có sẵn trên tất cả các hệ thống, ví dụ như các hệ thống nhúng sâu, nơi mã thực thi trên kim loại trần. Tức là ngay cả khi __attribute__((constructor))/((destructor))được GCC hỗ trợ, không chắc chắn nó sẽ chạy vì nó liên kết với trình liên kết để tổ chức nó và cho trình tải (hoặc trong một số trường hợp, mã khởi động) để chạy nó. Để sử dụng .init/ .finithay vào đó, cách dễ nhất là sử dụng cờ liên kết: -init & -fini (nghĩa là từ dòng lệnh GCC, cú pháp sẽ là -Wl -init my_init -fini my_fini).
Trên hệ thống hỗ trợ cả hai phương thức, một lợi ích có thể là mã trong .initđược chạy trước .ctorsvà mã .finisau .dtors. Nếu thứ tự có liên quan đó là ít nhất một cách thô sơ nhưng dễ dàng để phân biệt giữa các hàm init / exit.
Một nhược điểm lớn là bạn không thể dễ dàng có nhiều hơn một _initvà một _finichức năng cho mỗi mô-đun có thể tải và có thể sẽ phải phân đoạn mã nhiều .sohơn động lực. Một điều nữa là khi sử dụng phương thức liên kết được mô tả ở trên, người ta sẽ thay thế các hàm _init và _finimặc định ban đầu (được cung cấp bởi crti.o). Đây là nơi tất cả các loại khởi tạo thường xảy ra (trên Linux, đây là nơi khởi tạo biến toàn cục được khởi tạo). Một cách xung quanh được mô tả ở đây
Lưu ý trong liên kết ở trên rằng _init()không cần phải xếp tầng cho bản gốc vì nó vẫn được đặt đúng chỗ. Các calltrong inline lắp ráp tuy nhiên là x86 ghi nhớ và gọi một hàm từ lắp ráp sẽ trông hoàn toàn khác nhau cho nhiều kiến trúc khác (như ARM ví dụ). Mã tức là không minh bạch.
.init/ .finivà .ctors/ .detorscơ chế là tương tự, nhưng không hoàn toàn. Mã trong .init/ .finichạy "như là". Tức là bạn có thể có một số chức năng trong .init/ .fini, nhưng về mặt cú pháp, AFAIK rất khó để đặt chúng ở đó hoàn toàn trong C mà không phá vỡ mã trong nhiều .sotệp nhỏ .
.ctors/ .dtorsđược tổ chức khác với .init/ .fini. .ctors/ .dtorsphần chỉ là các bảng có con trỏ tới các hàm và "người gọi" là một vòng lặp do hệ thống cung cấp, gọi từng hàm một cách gián tiếp. Tức là người gọi vòng lặp có thể là kiến trúc cụ thể, nhưng vì nó là một phần của hệ thống (nếu nó tồn tại hoàn toàn), điều đó không thành vấn đề.
Đoạn mã sau đây thêm các con trỏ hàm mới vào .ctorsmảng hàm, chủ yếu giống như cách làm __attribute__((constructor))(phương thức có thể cùng tồn tại với __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;
Người ta cũng có thể thêm các con trỏ hàm vào một phần tự phát minh hoàn toàn khác. Một kịch bản liên kết đã sửa đổi và một chức năng bổ sung bắt chước trình tải .ctors/ .dtorsvòng lặp là cần thiết trong trường hợp đó. Nhưng với nó, người ta có thể đạt được sự kiểm soát tốt hơn đối với thứ tự thực hiện, thêm đối số và trả về mã xử lý eta (Ví dụ, trong một dự án C ++, sẽ hữu ích nếu cần một cái gì đó chạy trước hoặc sau các nhà xây dựng toàn cầu).
Tôi thích __attribute__((constructor))/((destructor))ở nơi có thể, đó là một giải pháp đơn giản và thanh lịch ngay cả khi nó cảm thấy như gian lận. Đối với các lập trình viên kim loại trần như tôi, điều này không phải lúc nào cũng là một lựa chọn.
Một số tài liệu tham khảo tốt trong cuốn sách Trình liên kết & bộ tải .
#define __attribute__(x)). Nếu bạn có nhiều thuộc tính, ví dụ,__attribute__((noreturn, weak))thật khó để "macro out" nếu chỉ có một bộ dấu ngoặc.