Linux 用户空间 api:如何在不解析 /proc/self/maps 的情况下获取作为 PIE 链接的正在运行的可执行文件的地址空间大小?

问题描述 投票:0回答:2

在 dl_iterate_phdr 的帮助下,我可以通过 dlpi_addr 加载主可执行文件和所有二进制文件的

start
地址,它与我可以通过 printf 打印的函数指针地址相关,但是其他字段,例如
p_offset
p_vaddr
p_paddr 
p_memsz
甚至
p_filesz
向我展示了非常小的数字(我的可执行文件本身就有 1.7G,还有大量的依赖项)。

作为参考,这是当我使用 dlpi_addr dlpi_name dlpi_phdr.p_offset dlpi_phdr.p_vaddr dlpi_phdr.p_paddr dlpi_phdr.p_memsz dlpi_phdr.p_filesz 的 64 位 PIE 可执行文件打印以下字符串时得到的输出类型。

“秒 0x%lx %s %ld %ld %ld %ld %ld ”

sec 0x55bf2722f000 64 64 64 784 784 sec 0x7fff8a3ea000 linux-vdso.so.1 0 0 0 4079 4079 sec 0x7f03ce15d000 /lib/x86_64-linux-gnu/libgstplayer-1.0.so.0 0 0 0 21128 21128 sec 0x7f03ce08b000 /lib/x86_64-linux-gnu/libgstvideo-1.0.so.0 0 0 0 108632 108632 sec 0x7f03ce004000 /lib/x86_64-linux-gnu/libgstbase-1.0.so.0 0 0 0 57520 57520 sec 0x7f03cdeae000 /lib/x86_64-linux-gnu/libgstreamer-1.0.so.0 0 0 0 192512 192512 sec 0x7f03cde4d000 /lib/x86_64-linux-gnu/libgobject-2.0.so.0 0 0 0 59216 59216 sec 0x7f03cdd05000 /lib/x86_64-linux-gnu/libglib-2.0.so.0 0 0 0 119968 119968 sec 0x7f03cdcf2000 /lib/x86_64-linux-gnu/libgstwebrtc-1.0.so.0 0 0 0 16776 16776 sec 0x7f03cdcd7000 /lib/x86_64-linux-gnu/libgstsdp-1.0.so.0 0 0 0 20248 20248 sec 0x7f03cdc54000 /lib/x86_64-linux-gnu/libgstaudio-1.0.so.0 0 0 0 64008 64008 sec 0x7f03cdc3e000 /lib/x86_64-linux-gnu/libgstapp-1.0.so.0 0 0 0 15208 15208 sec 0x7f03cdbb6000 /lib/x86_64-linux-gnu/libgstgl-1.0.so.0 0 0 0 92984 92984 sec 0x7f03cd9c7000 /lib/x86_64-linux-gnu/libgio-2.0.so.0 0 0 0 236080 236080 sec 0x7f03cd600000 /lib/x86_64-linux-gnu/libstdc++.so.6 0 0 0 637488 637488 sec 0x7f03cd9a8000 /lib/x86_64-linux-gnu/libz.so.1 0 0 0 8832 8832 ...and more
这是 elf 可执行 objdump 部分列表

Sections: Idx Name Size VMA LMA File off Algn 0 .interp 0000001c 0000000000000350 0000000000000350 00000350 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .note.gnu.property 00000020 0000000000000370 0000000000000370 00000370 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .note.gnu.build-id 00000024 0000000000000390 0000000000000390 00000390 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .note.ABI-tag 00000020 00000000000003b4 00000000000003b4 000003b4 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .gnu.hash 00000624 00000000000003d8 00000000000003d8 000003d8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .dynsym 000059a0 0000000000000a00 0000000000000a00 00000a00 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .dynstr 00004e8a 00000000000063a0 00000000000063a0 000063a0 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 7 .gnu.version 00000778 000000000000b22a 000000000000b22a 0000b22a 2**1 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .gnu.version_r 000002c0 000000000000b9a8 000000000000b9a8 0000b9a8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 9 .rela.dyn 00552480 000000000000bc68 000000000000bc68 0000bc68 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .rela.plt 00001290 000000000055e0e8 000000000055e0e8 0055e0e8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 11 .init 00000017 0000000000560000 0000000000560000 00560000 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 12 .plt 00000c70 0000000000560020 0000000000560020 00560020 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 13 .plt.got 00000288 0000000000560c90 0000000000560c90 00560c90 2**3 CONTENTS, ALLOC, LOAD, READONLY, CODE 14 .text 03693fac 0000000000560f40 0000000000560f40 00560f40 2**6 CONTENTS, ALLOC, LOAD, READONLY, CODE 15 .fini 00000009 0000000003bf4eec 0000000003bf4eec 03bf4eec 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 16 .rodata 01f5cd20 0000000003bf5000 0000000003bf5000 03bf5000 2**12 CONTENTS, ALLOC, LOAD, READONLY, DATA 17 .debug_gdb_scripts 00000022 0000000005b51d20 0000000005b51d20 05b51d20 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 18 .eh_frame_hdr 0012f7a4 0000000005b51d44 0000000005b51d44 05b51d44 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 19 .eh_frame 00755710 0000000005c814e8 0000000005c814e8 05c814e8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 20 .gcc_except_table 002634a8 00000000063d6bf8 00000000063d6bf8 063d6bf8 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 21 .tdata 00005398 000000000663b740 000000000663b740 0663a740 2**3 CONTENTS, ALLOC, LOAD, DATA, THREAD_LOCAL 22 .tbss 00000a00 0000000006640ad8 0000000006640ad8 0663fad8 2**3 ALLOC, THREAD_LOCAL 23 .init_array 00000090 0000000006640ad8 0000000006640ad8 0663fad8 2**3 CONTENTS, ALLOC, LOAD, DATA 24 .fini_array 00000008 0000000006640b68 0000000006640b68 0663fb68 2**3 CONTENTS, ALLOC, LOAD, DATA 25 .data.rel.ro 00367708 0000000006640b70 0000000006640b70 0663fb70 2**4 CONTENTS, ALLOC, LOAD, DATA 26 .dynamic 00000360 00000000069a8278 00000000069a8278 069a7278 2**3 CONTENTS, ALLOC, LOAD, DATA 27 .got 0001ba20 00000000069a85d8 00000000069a85d8 069a75d8 2**3 CONTENTS, ALLOC, LOAD, DATA 28 .data 00037c58 00000000069c4000 00000000069c4000 069c3000 2**4 CONTENTS, ALLOC, LOAD, DATA 29 .bss 026a5348 00000000069fbc80 00000000069fbc80 069fac58 2**7 ALLOC 30 .comment 0000006b 0000000000000000 0000000000000000 069fac58 2**0 CONTENTS, READONLY 31 .debug_aranges 00420190 0000000000000000 0000000000000000 069facc3 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 32 .debug_pubnames 08936c54 0000000000000000 0000000000000000 06e1ae53 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 33 .debug_info 1df05433 0000000000000000 0000000000000000 0f751aa7 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 34 .debug_abbrev 00442a52 0000000000000000 0000000000000000 2d656eda 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 35 .debug_line 0423eb3e 0000000000000000 0000000000000000 2da9992c 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 36 .debug_str 0b447666 0000000000000000 0000000000000000 31cd846a 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 37 .debug_loc 123b4882 0000000000000000 0000000000000000 3d11fad0 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 38 .debug_pubtypes 0cce2c03 0000000000000000 0000000000000000 4f4d4352 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 39 .debug_ranges 07b1f340 0000000000000000 0000000000000000 5c1b6f55 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 40 .debug_addr 0000d840 0000000000000000 0000000000000000 63cd6295 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 41 .debug_line_str 00001f90 0000000000000000 0000000000000000 63ce3ad5 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 42 .debug_loclists 00039d5e 0000000000000000 0000000000000000 63ce5a65 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 43 .debug_rnglists 00009afd 0000000000000000 0000000000000000 63d1f7c3 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS 44 .debug_str_offsets 0001c6c0 0000000000000000 0000000000000000 63d292c0 2**0 CONTENTS, READONLY, DEBUGGING, OCTETS
那么,这些小数字是怎么回事?我该如何正确使用 Linux api?

参考现有答案

如何检测当前进程中动态库(共享对象)的大小?建议解析 /proc/self/maps,这有点愚蠢,这个获取 ELF 标头主要可执行文件仅显示我已经拥有的起始地址

我尝试了 dladdr 以及 dladdr1 来加载一些信息,我以各种方式加载了 /proc/self/exe 的文件部分,但我不知道 dl_iterate_phdr 的输出如何让我将其信息与第三方库相关联

更新: 这是 readelf 实用程序的输出

Elf file type is DYN (Position-Independent Executable file) Entry point 0x6cfe20 There are 14 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000310 0x000310 R 0x8 INTERP 0x000350 0x0000000000000350 0x0000000000000350 0x00001c 0x00001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x55f378 0x55f378 R 0x1000 LOAD 0x560000 0x0000000000560000 0x0000000000560000 0x3694ef5 0x3694ef5 R E 0x1000 LOAD 0x3bf5000 0x0000000003bf5000 0x0000000003bf5000 0x2a450a0 0x2a450a0 R 0x1000 LOAD 0x663a740 0x000000000663b740 0x000000000663b740 0x3c0518 0x2a65888 RW 0x1000 DYNAMIC 0x69a7278 0x00000000069a8278 0x00000000069a8278 0x000360 0x000360 RW 0x8 NOTE 0x000370 0x0000000000000370 0x0000000000000370 0x000020 0x000020 R 0x8 NOTE 0x000390 0x0000000000000390 0x0000000000000390 0x000044 0x000044 R 0x4 TLS 0x663a740 0x000000000663b740 0x000000000663b740 0x005398 0x005d98 R 0x8 GNU_PROPERTY 0x000370 0x0000000000000370 0x0000000000000370 0x000020 0x000020 R 0x8 GNU_EH_FRAME 0x5b51d44 0x0000000005b51d44 0x0000000005b51d44 0x12f7a4 0x12f7a4 R 0x4 GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 GNU_RELRO 0x663a740 0x000000000663b740 0x000000000663b740 0x3888c0 0x3888c0 R 0x1 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 03 .init .plt .plt.got .text .fini 04 .rodata .debug_gdb_scripts .eh_frame_hdr .eh_frame .gcc_except_table 05 .tdata .init_array .fini_array .data.rel.ro .dynamic .got .data .bss 06 .dynamic 07 .note.gnu.property 08 .note.gnu.build-id .note.ABI-tag 09 .tdata .tbss 10 .note.gnu.property 11 .eh_frame_hdr 12 13 .tdata .init_array .fini_array .data.rel.ro .dynamic .got
我正在使用 rust 绑定,但欢迎使用 c/c++/whatever-calling-libc 回答

libc::dl_iterate_phdr(Some(collect_objs), std::ptr::null_mut()); unsafe extern "C" fn collect_objs( info: *mut dl_phdr_info, _sz: usize, data: *mut c_void, ) -> c_int { libc::printf("sec 0x%lx %s %ld %ld %ld %ld %ld \n\0".as_bytes().as_ptr() as *const i8, (*info).dlpi_addr, (*info).dlpi_name, (*(*info).dlpi_phdr).p_offset, (*(*info).dlpi_phdr).p_vaddr, (*(*info).dlpi_phdr).p_paddr, (*(*info).dlpi_phdr).p_memsz, (*(*info).dlpi_phdr).p_filesz); 0 }
    
linux elf libc
2个回答
1
投票
此程序文件中的大部分数据是调试信息,位于 ELF

.debug_*

 部分。

如果将 MemSiz 字段加起来,我会得到 156 618 913 字节(约 156 MB)。程序运行时,仅加载具有非零 MemSiz 的条目(并保存在内存中)。这些都不是调试信息。

如果我将 FileSiz 字段加起来,我会得到 116 093 745 字节(大约 116 MB,比 MemSiz 小大约 40 MB)。造成这种差异的原因是某些内存区域(例如

.bss

.tbss
 部分)是用 0 (NUL) 字节初始化的,而这些 0 (NUL) 字节在程序文件中被省略。

.debug_*

 部分的总大小(在 objdump(1) 的输出中)为 1 563 733 215 字节 (1563 MB)。它们仅由 gdb(1) 等调试器在调试程序时使用。这些部分包含行号信息(哪个汇编指令位于哪个 (C) 源文件的哪一行)、函数信息(哪个汇编指令位于哪个函数中)、类型信息(哪个结构体具有哪些字段名称和类型)、堆栈布局信息(在堆栈上存储局部变量和函数参数的任何汇编指令中)等。

当我们将其加起来时:FileSiz 为 116 MB +

.debug_*

 部分为 1563 MB,我们得到 1679 MB,我们可以将其四舍五入为 1.7 GB,与问题中报告的文件大小相匹配。

要缩小程序文件,请运行

strip filename

。这将删除 
.debug_*
 部分,并使文件大小约为 116 MB。仍然可以在调试器中调试程序,但调试器显示的信息将不那么详细,而且用处也更少。即使没有 
.debug_*
 部分,常规执行(包括 C++ 中的异常处理)也不会发生变化。


0
投票
事实证明,函数名称描述了函数内部执行的操作,而

not描述了它返回的内容,实际上,而不是dl_iterate_phdr

应该被称为
dl_iterate_objects
,因为在回调中,如文档所述,它返回给你(惊喜)共享对象,它们是可执行文件/lib,而不是节头本身。它返回的变量名称
p_memsz
实际上并不是指向某个内存对象的指针/指针偏移量,正如您可能从其名称中想到的那样,而是一个节的大小,而是一个实际大小,无论结构的其他字段是否是实际指针如手册中所述,
dlpi_phnum
不是当前节的编号,而是节数组的长度。

文档的名称和实际情况之间存在矛盾,这导致我加载对象的第一部分(这是标头部分),并惊讶为什么它相对于可执行文件大小如此之小,我应该查看示例在手册的底部了解它实际返回的内容,而不是拼凑出这个 api 实际是什么的谜题

这是手册中示例程序的代码输出

#define _GNU_SOURCE #include <link.h> #include <stdlib.h> #include <stdio.h> static int callback(struct dl_phdr_info *info, size_t size, void *data) { char *type; int p_type, j; printf("Name: \"%s\" (%d segments)\n", info->dlpi_name, info->dlpi_phnum); for (j = 0; j < info->dlpi_phnum; j++) { p_type = info->dlpi_phdr[j].p_type; type = (p_type == PT_LOAD) ? "PT_LOAD" : (p_type == PT_DYNAMIC) ? "PT_DYNAMIC" : (p_type == PT_INTERP) ? "PT_INTERP" : (p_type == PT_NOTE) ? "PT_NOTE" : (p_type == PT_INTERP) ? "PT_INTERP" : (p_type == PT_PHDR) ? "PT_PHDR" : (p_type == PT_TLS) ? "PT_TLS" : (p_type == PT_GNU_EH_FRAME) ? "PT_GNU_EH_FRAME" : (p_type == PT_GNU_STACK) ? "PT_GNU_STACK" : (p_type == PT_GNU_RELRO) ? "PT_GNU_RELRO" : NULL; printf(" %2d: [%14p; memsz:%7lx] flags: 0x%x; ", j, (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr), info->dlpi_phdr[j].p_memsz, info->dlpi_phdr[j].p_flags); if (type != NULL) printf("%s\n", type); else printf("[other (0x%x)]\n", p_type); } return 0; } int main(int argc, char *argv[]) { dl_iterate_phdr(callback, NULL); exit(EXIT_SUCCESS); }
并为其输出巨大的可执行文件正在运行

Name: "" (14 segments) 0: [0x563b1048e040; memsz: 310] flags: 0x4; PT_PHDR 1: [0x563b1048e350; memsz: 1c] flags: 0x4; PT_INTERP 2: [0x563b1048e000; memsz: 55f180] flags: 0x4; PT_LOAD 3: [0x563b109ee000; memsz:36936b5] flags: 0x5; PT_LOAD 4: [0x563b14082000; memsz:2a44490] flags: 0x4; PT_LOAD 5: [0x563b16ac78e0; memsz:2a656e8] flags: 0x6; PT_LOAD 6: [0x563b16e342a8; memsz: 360] flags: 0x6; PT_DYNAMIC 7: [0x563b1048e370; memsz: 20] flags: 0x4; PT_NOTE 8: [0x563b1048e390; memsz: 44] flags: 0x4; PT_NOTE 9: [0x563b16ac78e0; memsz: 5d98] flags: 0x4; PT_TLS 10: [0x563b1048e370; memsz: 20] flags: 0x4; [other (0x6474e553)] 11: [0x563b15fded44; memsz: 12f6c4] flags: 0x4; PT_GNU_EH_FRAME 12: [0x563b1048e000; memsz: 0] flags: 0x6; PT_GNU_STACK 13: [0x563b16ac78e0; memsz: 388720] flags: 0x4; PT_GNU_RELRO Name: "linux-vdso.so.1" (4 segments) 0: [0x7ffe23dbc000; memsz: fef] flags: 0x5; PT_LOAD 1: [0x7ffe23dbc3e0; memsz: 120] flags: 0x4; PT_DYNAMIC 2: [0x7ffe23dbc500; memsz: 60] flags: 0x4; PT_NOTE 3: [0x7ffe23dbc560; memsz: 4c] flags: 0x4; PT_GNU_EH_FRAME Name: "/lib/x86_64-linux-gnu/libgstplayer-1.0.so.0" (9 segments) 0: [0x7ff5f2572000; memsz: 5288] flags: 0x4; PT_LOAD 1: [0x7ff5f2578000; memsz: 6261] flags: 0x5; PT_LOAD 2: [0x7ff5f257f000; memsz: 443c] flags: 0x4; PT_LOAD 3: [0x7ff5f2584750; memsz: b60] flags: 0x6; PT_LOAD 4: [0x7ff5f25848e0; memsz: 250] flags: 0x6; PT_DYNAMIC 5: [0x7ff5f2572238; memsz: 24] flags: 0x4; PT_NOTE 6: [0x7ff5f2580fc0; memsz: 64c] flags: 0x4; PT_GNU_EH_FRAME 7: [0x7ff5f2572000; memsz: 0] flags: 0x6; PT_GNU_STACK 8: [0x7ff5f2584750; memsz: 8b0] flags: 0x4; PT_GNU_RELRO Name: "/lib/x86_64-linux-gnu/libgstvideo-1.0.so.0" (9 segments) 0: [0x7ff5f24a0000; memsz: 1a858] flags: 0x4; PT_LOAD 1: [0x7ff5f24bb000; memsz: 73271] flags: 0x5; PT_LOAD 2: [0x7ff5f252f000; memsz: 2f4f4] flags: 0x4; PT_LOAD 3: [0x7ff5f255f870; memsz: 118e8] flags: 0x6; PT_LOAD 4: [0x7ff5f256ee20; memsz: 250] flags: 0x6; PT_DYNAMIC 5: [0x7ff5f24a0238; memsz: 24] flags: 0x4; PT_NOTE 6: [0x7ff5f2545d08; memsz: 2e14] flags: 0x4; PT_GNU_EH_FRAME 7: [0x7ff5f24a0000; memsz: 0] flags: 0x6; PT_GNU_STACK 8: [0x7ff5f255f870; memsz: 10790] flags: 0x4; PT_GNU_RELRO Name: "/lib/x86_64-linux-gnu/libgstbase-1.0.so.0" (9 segments) 0: [0x7ff5f2419000; memsz: e0b0] flags: 0x4; PT_LOAD 1: [0x7ff5f2428000; memsz: 54c5d] flags: 0x5; PT_LOAD 2: [0x7ff5f247d000; memsz: 20650] flags: 0x4; PT_LOAD 3: [0x7ff5f249e9f0; memsz: 1450] flags: 0x6; PT_LOAD 4: [0x7ff5f249ece8; memsz: 220] flags: 0x6; PT_DYNAMIC 5: [0x7ff5f2419238; memsz: 24] flags: 0x4; PT_NOTE 6: [0x7ff5f248d9c4; memsz: 153c] flags: 0x4; PT_GNU_EH_FRAME 7: [0x7ff5f2419000; memsz: 0] flags: 0x6; PT_GNU_STACK 8: [0x7ff5f249e9f0; memsz: 610] flags: 0x4; PT_GNU_RELRO ...and more
    
© www.soinside.com 2019 - 2024. All rights reserved.