Con trỏ void *
trả về bởi dlopen(0, RTLD_LAZY)
mang đến cho bạn một struct link_map *
, tương ứng với thực thi chính.
Gọi dl_iterate_phdr
cũng trả về mục nhập cho thực thi chính trên thực hiện gọi lại rất đầu tiên.
Bạn có thể bị nhầm lẫn bởi thực tế là .l_addr == 0
trong bản đồ liên kết và rằng dlpi_addr == 0
khi sử dụng dl_iterate_phdr
.
Điều này đang xảy ra, vì l_addr
(và dlpi_addr
) không thực sự ghi lại địa chỉ tải của hình ảnh ELF. Thay vào đó, họ ghi lại di chuyển đã được áp dụng cho hình ảnh đó.
Thông thường, tệp thi hành chính được xây dựng để tải tại 0x400000
(đối với x86_64 Linux) hoặc tại 0x08048000
(đối với ix86 Linux) và được tải tại cùng địa chỉ đó (nghĩa là chúng không được di chuyển).
Nhưng nếu bạn liên kết tệp thực thi của mình với cờ -pie
, thì nó sẽ được liên kết tại số 0x0
, và nó sẽ được di chuyển sang một số địa chỉ khác.
Vậy làm thế nào để bạn đến được tiêu đề ELF? Dễ dàng:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
static int
callback(struct dl_phdr_info *info, size_t size, void *data)
{
int j;
static int once = 0;
if (once) return 0;
once = 1;
printf("relocation: 0x%lx\n", (long)info->dlpi_addr);
for (j = 0; j < info->dlpi_phnum; j++) {
if (info->dlpi_phdr[j].p_type == PT_LOAD) {
printf("a.out loaded at %p\n",
(void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr));
break;
}
}
return 0;
}
int
main(int argc, char *argv[])
{
dl_iterate_phdr(callback, NULL);
exit(EXIT_SUCCESS);
}
$ gcc -m32 t.c && ./a.out
relocation: 0x0
a.out loaded at 0x8048000
$ gcc -m64 t.c && ./a.out
relocation: 0x0
a.out loaded at 0x400000
$ gcc -m32 -pie -fPIC t.c && ./a.out
relocation: 0xf7789000
a.out loaded at 0xf7789000
$ gcc -m64 -pie -fPIC t.c && ./a.out
relocation: 0x7f3824964000
a.out loaded at 0x7f3824964000
Cập nhật:
Tại sao trang người đàn ông nói "địa chỉ cơ sở" và không di dời?
Đó là một lỗi ;-)
Tôi đoán rằng những người đàn ông trang được viết rất lâu trước khi prelink
và pie
, và ASLR
tồn tại. Nếu không có prelink, các thư viện được chia sẻ luôn được liên kết để tải tại địa chỉ 0x0
, và sau đó relocation
và base address
trở thành một và giống nhau.
cách dlpi_name trỏ đến chuỗi trống khi thông tin đề cập đến tệp thực thi chính?
Đó là một sự cố khi triển khai.
Cách này hoạt động, là hạt nhân open(2)
s thực thi và chuyển mô tả tệp mở cho bộ nạp (trong vector auxv[]
, dưới dạng AT_EXECFD
). Mọi thứ trình tải đều biết về tệp thực thi mà nó nhận được bằng cách đọc bộ mô tả tệp đó.
Không có cách nào dễ dàng trên UNIX để ánh xạ một bộ mô tả tệp quay lại tên mà nó đã được mở dưới dạng. Đối với một điều, UNIX hỗ trợ liên kết cứng và có thể có nhiều tên tệp tham chiếu đến cùng một tệp.
Hạt nhân Linux mới hơn cũng chuyển vào tên đã được sử dụng để execve(2)
thực thi (cũng trong auxv[]
, dưới dạng AT_EXECFN
). Nhưng đó là tùy chọn, và ngay cả khi nó được thông qua, glibc không đặt nó vào .l_name
/dlpi_name
để không phá vỡ các chương trình hiện tại mà đã trở thành phụ thuộc vào tên đang trống.
Thay vào đó, glibc lưu tên đó trong __progname
và __progname_full
.
Bộ nạp coudreadlink(2)
tên từ /proc/self/exe
trên các hệ thống không sử dụng AT_EXECFN
, nhưng hệ thống /proc
tập tin không được bảo đảm đến được gắn một trong hai, do đó vẫn sẽ để lại nó với một cái tên trống đôi khi.
'read()' từ '/ proc/self/exe' – Dave
Ah, tôi quên đề cập đến, tôi cũng cần địa chỉ cơ sở cùng với tiêu đề ELF và tiêu đề ELF phải là địa chỉ cơ sở là –