Cuộc gọi hệ thống nào được sử dụng để tải các thư viện trong Linux?


23

Trong các straceđầu ra, các đường dẫn đến các thư viện thực thi cuộc gọi nằm trong các cuộc gọi đến open(). Đây có phải là cuộc gọi hệ thống được sử dụng bởi các tệp thực thi được liên kết động? Thế còn dlopen()? open()không phải là một cuộc gọi mà tôi đã đoán sẽ đóng một vai trò trong việc thực hiện các chương trình.

Câu trả lời:


33

dlopenkhông phải là một cuộc gọi hệ thống, đó là một chức năng thư viện trong thư viện libdl . Chỉ có các cuộc gọi hệ thống hiển thị trong strace.

Trên Linux và trên nhiều nền tảng khác (đặc biệt là các nền tảng sử dụng định dạng ELF cho các tệp thực thi), dlopenđược triển khai bằng cách mở thư viện đích open()và ánh xạ nó vào bộ nhớ mmap(). mmap()thực sự là phần quan trọng ở đây, đó là thứ kết hợp thư viện vào không gian địa chỉ của tiến trình, để CPU có thể thực thi mã của nó. Nhưng bạn phải open()tập tin trước khi bạn có thể mmap()!


2
"Mmap () thực sự là phần quan trọng": Và sau đó, trình liên kết động phải thực hiện các thao tác di chuyển, khởi tạo và vì vậy một (nhưng điều này không được nhìn thấy ở cấp độ gọi hệ thống).
ysdx

1
Vì việc tải các thư viện được thực hiện bởi một chức năng thư viện, tôi nghĩ rằng nó có liên quan để thêm rằng chính nó có thể thực thi ld-linuxđược và được ánh xạ bởi kernel như một phần của lệnh execvegọi hệ thống.
kasperd

mmap theo câu trả lời này. Cũng lưu ý rằng sau khi "mở" -ing mỗi thư viện, một số (832) byte được đọc trước lệnh gọi mmap, tôi cho rằng để kiểm tra xem thư viện có hợp lệ không.
Johan

@kasperd Vậy nhân Linux có biết về trình tải động không? Nó gọi nó khi ứng dụng được chạy? Hay chính ứng dụng làm điều đó? Nếu sau này, làm thế nào để một thực thi khác có quyền truy cập vào bộ nhớ của ứng dụng?
Melab

@Melab Vâng, kernel biết về trình liên kết động. Nhân sẽ đọc đường dẫn đến trình liên kết động từ tiêu đề của tệp thực thi. Và kernel sẽ ánh xạ cả vào bộ nhớ. Tôi không biết liệu điểm đầu vào mà điều khiển chuyển kernel ban đầu nằm trong linker hay có thể thực thi được không. Nếu tôi đang thực hiện nó, có lẽ tôi sẽ có điều khiển chuyển kernel đến một điểm vào trong trình liên kết với một địa chỉ trả về trên ngăn xếp trỏ đến điểm vào của tệp thực thi.
kasperd

5

dlopen không có gì để làm với các thư viện chia sẻ như bạn nghĩ về chúng. Có hai phương pháp tải một đối tượng chia sẻ:

  1. Bạn nói với trình liên kết thời gian biên dịch (ld, mặc dù thông thường nó được gọi thông qua trình biên dịch) rằng bạn muốn sử dụng các hàm từ một thư viện chia sẻ cụ thể. Với cách tiếp cận này, bạn phải biết tên của thư viện sẽ là gì khi trình liên kết thời gian biên dịch được chạy, nhưng bạn có thể gọi các hàm của thư viện như thể chúng được liên kết tĩnh vào chương trình của bạn. Khi ứng dụng được chạy, trình liên kết thời gian chạy động (ld.so) sẽ được gọi ngay trước khi mainhàm được gọi và thiết lập không gian xử lý của ứng dụng để ứng dụng sẽ tìm thấy các chức năng của thư viện. Điều này liên quan đến open()ing ingrary, và sau đó mmap()ing nó, tiếp theo là thiết lập một số bảng tra cứu.
  2. Bạn nói với trình liên kết thời gian biên dịch mà bạn muốn liên kết libdl, từ đó bạn (bằng phương thức đầu tiên) có thể gọi dlopen()dlsym()chức năng. Với dlopen, bạn có một tay cầm đến thư viện, sau đó bạn có thể sử dụng với dlsym để nhận một con trỏ hàm đến một hàm cụ thể. Phương pháp này phức tạp hơn nhiều đối với lập trình viên so với phương pháp đầu tiên (vì bạn phải thực hiện thiết lập thủ công, thay vì trình liên kết tự động thực hiện cho bạn) và nó cũng dễ hỏng hơn (vì bạn không nhận được trình biên dịch -Thời gian kiểm tra xem bạn đang gọi các hàm với các loại đối số chính xác như bạn nhận được trong phương thức đầu tiên), nhưng ưu điểm là bạn có thể quyết định đối tượng chia sẻ nào sẽ tải trong thời gian chạy (hoặc thậm chí có tải nó không), thực hiện đây là một giao diện dành cho chức năng loại plugin. Cuối cùng, giao diện dlopen cũng ít di động hơn so với cách khác, vì cơ chế của nó phụ thuộc vào việc triển khai chính xác trình liên kết động (do đó libtoollibltdl, cố gắng trừu tượng hóa những khác biệt này).

hấp dẫn; vì vậy thư viện được tải động được gọi là thư viện được liên kết động tốt hơn, vì tải tệp nhị phân vào bộ nhớ không phải là phần khó, làm cho các địa chỉ được sử dụng trong đó có ý nghĩa. Khi tôi yêu cầu tải một thư viện động, tôi thực sự yêu cầu liên kết (hoặc hủy liên kết) thư viện vào (hoặc ra) không gian địa chỉ của tôi.
Dmitry

4

Ngày nay, hầu hết các hệ điều hành sử dụng phương pháp cho các thư viện dùng chung được giới thiệu vào cuối năm 1987 bởi SunOS-4.0. Phương pháp này dựa trên bộ nhớ ánh xạ qua mmap ().

Với thực tế là vào đầu những năm 1990, Sun thậm chí đã tặng mã dựa trên a.out cũ (Solaris lúc đó đã dựa trên ELF) cho người FreeBSD và mã này sau đó đã được chuyển giao cho nhiều hệ thống khác (bao gồm cả Linux) , bạn có thể hiểu tại sao không có sự khác biệt lớn giữa các nền tảng.


3

ltrace -Sphân tích một ví dụ tối thiểu cho thấy mmapđược sử dụng trong glibc 2.23

Trong glibc 2.23, Ubuntu 16.04, chạy latrace -Strên một chương trình tối thiểu sử dụng dlopenvới:

ltrace -S ./dlopen.out

trình diễn:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "\177ELF\002\001\001", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0

vì vậy chúng tôi thấy ngay rằng dlopencuộc gọi open+ mmap.

Công ltracecụ tuyệt vời theo dõi cả các cuộc gọi thư viện và các cuộc gọi hệ thống, và do đó hoàn hảo để kiểm tra những gì đang xảy ra trong trường hợp này.

Một phân tích gần hơn cho thấy opentrả về bộ mô tả tệp 3(cái miễn phí tiếp theo sau stdin, out và err).

readsau đó sử dụng bộ mô tả tệp đó, nhưng TODO tại sao mmapcác đối số bị giới hạn ở bốn và chúng ta không thể thấy fd nào được sử dụng ở đó vì đó là đối số thứ 5 . stracexác nhận như mong đợi đó 3là một, và trật tự của vũ trụ được phục hồi.

Những linh hồn dũng cảm cũng có thể mạo hiểm với mã glibc, nhưng tôi không thể tìm thấy mmapsau một grep nhanh và tôi lười biếng.

Đã thử nghiệm với ví dụ tối thiểu này với bản dựng sẵn trên GitHub .


2

stracebáo cáo về các cuộc gọi hệ thống (tức là các chức năng được thực hiện trực tiếp bởi kernel). Thư viện động không phải là hàm kernel; dlopenlà một phần của thư viện C, không phải kernel. Việc thực hiện dlopensẽ gọi open(là một cuộc gọi hệ thống) để mở tệp thư viện để có thể đọc được.


5
Các cuộc gọi thư viện có thể được nhìn thấy bằng cách sử dụng ltrace.
kasperd

@kasperd ltrace -Slà hoàn hảo để phân tích điều này vì nó cũng hiển thị các tòa nhà chọc trời
Ciro Santilli 改造
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.