一尘不染

共享库是否使用与应用程序相同的堆?

linux

假设我在Linux中有一个使用共享库(.so文件)的应用程序。我的问题是这些库中的代码是否将在与主应用程序相同的堆中分配内存,还是使用自己的堆?

因此,例如,.so文件中的某些函数调用malloc,它将使用与应用程序相同的堆管理器,还是使用另一个应用程序?同样,那些共享内存中的全局数据又如何呢?它在哪里?对于应用程序,我知道它位于bss和数据段中,但是不知道这些共享对象文件在哪里。


阅读 266

收藏
2020-06-03

共1个答案

一尘不染

我的问题是这些库中的代码是否将在与主应用程序相同的堆中分配内存,还是使用自己的堆?

如果库使用与malloc/free应用程序相同的库(例如from glibc),则可以,程序和所有库都将使用单个堆。

如果库mmap直接使用,它可以分配的内存不是程序本身使用的内存。

因此,例如,.so文件中的某些函数调用malloc,它将使用与应用程序相同的堆管理器,还是使用另一个应用程序?

如果来自.so的函数调用malloc,则此malloc与从程序调用的malloc相同。您可以在Linux / glibc(> 2.1)中看到符号绑定日志

 LD_DEBUG=bindings ./your_program

是的,堆管理器的多个实例(具有默认配置)在不相互了解的情况下无法共存(问题在于使brk分配的堆大小在实例之间保持同步)。但是,当多个实例可以共存时,可能存在一种配置。

大多数经典的malloc实现(ptmalloc
*,dlmalloc等)可以使用两种从系统获取内存的方法:brkmmap。Brk是经典堆,它是线性的,可以增长或收缩。Mmap允许在任何地方获得大量内存;您可以按任何顺序将此内存返回给系统(释放它)。

构建malloc时,可以禁用brk方法。然后,malloc将仅使用mmaps
来模拟线性堆,甚至将禁用经典线性堆,并且所有分配将由不连续的mmaped碎片进行。

因此,某些库可以具有自己的内存管理器,例如malloc,已brk禁用或使用非malloc内存管理器进行编译。该管理器应具有malloc和以外的函数名称free,例如malloc1free1或不应将其显示/导出到动态链接器。

同样,那些共享内存中的全局数据又如何呢?它在哪里?对于应用程序,我知道它位于bss和数据段中,但是不知道这些共享对象文件在哪里。

您应该像ELF文件一样考虑程序和.so。每个ELF文件都有“程序头”(readelf -l elf_file)。如何将数据从ELF加载到内存的方式取决于程序标头的类型。如果类型为“
LOAD”,则文件的相应部分将被私有mmap编辑(Sic!)到内存中。通常,有2个LOAD段。第一个用于带有R +
X(读+执行)标志的代码,第二个用于带有R +
W(读+写)标志的数据。两者.bss.data(全局数据)区段被放置在型LOAD的段与写入启用旗标。

可执行库和共享库都具有LOAD段。某些段具有memory_size>
file_size。这意味着该段将在内存中扩展;它的第一部分将使用ELF文件中的数据填充,第二部分的大小(memory_size-
file_size)将使用零填充(对于*bss节),使用mmap(/dev/zero)memset(0)

当内核或动态链接器将ELF文件加载到内存中时,他们将不会考虑共享。例如,您想两次启动同一程序。第一个进程将使用mmap加载ELF文件的只读部分;第二个进程将执行相同的mmap(如果aslr是活动的-
第二个mmap将进入不同的虚拟地址)。页面缓存(VFS子系统)的任务是将数据的单个副本保存在物理内存中(使用COPY-
WRITE或COW)。mmap只会将每个进程中的虚拟地址映射到单个物理位置。如果有任何进程将更改内存页面;它将在写入时复制到唯一的专用物理内存。

加载代码在glibc/elf/dl-load.c_dl_map_object_from_fd)中,用于ld.so和linux- kernel/fs/binfmt_elf.c内核的ELF加载器(elf_mapload_elf_binary)。搜索PT_LOAD

因此,全局数据和bss数据始终在每个进程中私密地映射,并且它们受到COW的保护。

堆和堆栈在运行时使用brk +
mmap(堆)进行分配,并由OS内核自动在类似brk的进程中分配(用于主线程的堆栈)。其他线程的堆栈分配mmap在中pthread_create

2020-06-03