59 多次 mmap 虚拟地址的关系

2024-05-25 11:04

本文主要是介绍59 多次 mmap 虚拟地址的关系,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

这是来自于网友的一篇帖子 

然后 我们这里来探究一下这个问题

主要是 多次连续的 mmap 获取到的 虚拟地址区域 是否连续

以及 衍生出的一些其他的问题

从 mmap 的实现 我们可以知道, mmap 的空间是 自顶向下 分配的, 因此 两块空间应该是连续的, 第一块在上面, 第二块在下面 

 

 

测试用例

测试用例很简单, 就是多次 mmap 同一个文件, 然后 观察一下 其地址信息

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>#define LEN (10*4096)int main(void) {int fd;char *vadr;if ((fd = open("./1.txt", O_RDWR)) < 0) {return 0;}vadr = mmap(0, LEN, PROT_READ, MAP_PRIVATE | MAP_LOCKED, fd, 0);printf("vaddr = 0x%x\n", vadr);printf(" ch = %c \n", vadr[10]);if ((fd = open("./1.txt", O_RDWR)) < 0) {return 0;}vadr = mmap(0, LEN, PROT_READ, MAP_PRIVATE | MAP_LOCKED, fd, 0);printf("vaddr = 0x%x\n", vadr);printf(" ch = %c \n", vadr[10]);return 0;
}

 

程序执行输出如下, 可以证实我们上面的猜想, 但是还有一些 细节我们需要去了解

root@ubuntu:~/Desktop/linux/HelloWorld# ./startGdbServer.sh
Test07MmapRead.c: In function ‘main’:
Test07MmapRead.c:19:12: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘char *’ [-Wformat=]printf("vaddr = 0x%x\n", vadr);^
Test07MmapRead.c:27:12: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘char *’ [-Wformat=]printf("vaddr = 0x%x\n", vadr);^
Process /root/Desktop/linux/HelloWorld/Test07MmapRead created; pid = 52114
Listening on port 1235
Remote debugging from host ::ffff:192.168.220.132, port 44444
vaddr = 0xf7fea000ch = f
vaddr = 0xf7fe0000ch = f

 

 

基于内核的调试多次 mmap 的调用

我们这里 先放出一部分调试结果信息作为参照

如下调试会分为两个部分, 一个是 内核的调试, 一个是 glibc的调试, 这两部分 完整的解释日志的信息

我们这里核心关注的是 1.txt 对应的两段空间, 以及其到 堆栈底部 的空间

所以 ld-2.23.so, libc-2.23.so 对应的空间应该是 mmap 映射了之后 进行了 munmap 因此才有如下奇怪的 mmap 空间的申请, 然后 我们这里需要 大致梳理清楚 如下日志的具体情况

2260992 - 0x228000 - 0x7ffff7dd7000 - 0x7ffff7fff000 - ld-2.23.so
16384 - 0x4000 - 0x7ffff7ff8000 - vdso_image
4096 - 0x1000 - 0x7ffff7ff7000 - system_dirs
4096 - 0x1000 - 0x7ffff7ff6000 - realname libc.so.6 
3973120 - 0x3ca000 - 0x7ffff7a00000 - libc-2.23.so0x7ffff7dcf000 - dl_main - openaux - dl_map_segments 0x7ffff7dd5000 - dl_main - openaux - dl_map_segments
4096 - 0x1000 - 0x7ffff7ff5000 - dl_main - init_tls - dl_allocate_tls_storage - libc_memalign 
4096 - 0x1000 - 0x7ffff7ff4000 - dl_main - init_tls - dl_allocate_tls_storage - allocate_dtv 
40960 - 0xa000 - 0x7ffff7fea000 - 1.txt
40960 - 0xa000 - 0x7ffff7fe0000 - 1.txt

 

第一个 mmap 调用如下, 主要是 加载 ld-2.23.so, 这个纯粹是在内核的调用, 所以 待会儿 glibc 的调试不会碰到它 

ld-2.23.so 文件大小接近 160kb 左右  

83077a8059544ed5a28adf481c2d58bd.png

 

接着 munmap 了没有使用的空间, 现在的情况是 ld-2.23.so 占用的空间是 0x7ffff7dd7000 - 0x7ffff7dfd000

释放掉了空间 0x7ffff7dfd000 - 0x7ffff7fff000

276462568b51466c86fcb057b46981c3.png 

第二个 mmap 调用如下,映射的是 vdso 的镜像, 映射的区间是 0x7ffff7ff8000 - 0x7ffff7ffc000

f6e7edd503414a0f94d61b9afac1942e.png

 

第三个 mmap 调用如下, 映射的是 “/root/Desktop/linux/glibc-2.23/install/lib” 的字符串, 映射的区间是 0x7ffff7ff7000 - 0x7ffff7ff8000

9046882641c144f3a3d002a421dec579.png

 

第四个 mmap 调用如下, 映射的是 “libc.so.6” 对应的一个 linkmap 的一个结构体, 映射的区间是 0x7ffff7ff6000 - 0x7ffff7ff7000

0f66c4bbb66c4ac8a05b48e61d3b6ab0.png

 

第五个 mmap 调用如下,映射的是 “libc-2.23.so” 的动态库, 映射的区间是 0x7ffff7a0d000 - 0x7ffff7dd7000

空间之所以 在这里是因为 ld-2.23.so 的 mmap 映射区只有 2m, 这里需要 3m, 因此 继续向前寻找空间, 找到的 ld-2.23.so 前面的一块空间 

cf3c7714d06d4b9c98fbfa7b68458e48.png

 

第六个 mmap 调用如下, dl_main - init_tls - dl_allocate_tls_storage - libc_memalign 映射了 4k 的映射区, 映射的区间是 0x7ffff7ff5000 - 0x7ffff7ff6000

6310225c1fa345859860209bd7371455.png 

第七个 mmap 调用如下, dl_main - init_tls - dl_allocate_tls_storage - allocate_dtv 映射了 4k 的映射区, 映射的区间是 0x7ffff7ff4000 - 0x7ffff7ff5000

737a2b7830594dc4ac983dfe83808672.png 

 

接下来就是我们需要关心的业务 mmap 映射了 

目前 mmap 映射区已经使用的空间如下, 接着需要申请两个 40kb 的空间, 在 0x7ffff7dfd000 - 0x7ffff7ff4000 的空间是可以容纳这需求的 2 * 40kb 的 

0x7ffff7a0d000 - 0x7ffff7dd7000 = libc-2.23.so
0x7ffff7dd7000 - 0x7ffff7dfd000 = ld-2.23.so
0x7ffff7ff4000 - 0x7ffff7ff5000 = dl_main - init_tls - dl_allocate_tls_storage - allocate_dtv
0x7ffff7ff5000 - 0x7ffff7ff6000 = dl_main - init_tls - dl_allocate_tls_storage - libc_memalign
0x7ffff7ff6000 - 0x7ffff7ff7000 = system_dirs
0x7ffff7ff7000 - 0x7ffff7ff8000 = realname libc.so.6
0x7ffff7ff8000 - 0x7ffff7ffc000 = vdso_image

 

第八个 mmap 调用申请了 40kb, 申请的空间为 0x7ffff7fea000 - 0x7ffff7ff4000

16744318c95245e7849ba590c3a8d326.png

 

第九个 mmap 调用申请了 40kb, 申请的空间为 0x7ffff7fe0000 - 0x7ffff7fea000

4b42d959b5894999b726b2b3f5939d96.png 

 

最终整个 mmap 映射区使用空间如下  

0x7ffff7a0d000 - 0x7ffff7dd7000 = libc-2.23.so
0x7ffff7dd7000 - 0x7ffff7dfd000 = ld-2.23.so
0x7ffff7fe0000 - 0x7ffff7fea000 = mmap("1.txt")
0x7ffff7fea000 - 0x7ffff7ff4000 = mmap("1.txt")
0x7ffff7ff4000 - 0x7ffff7ff5000 = dl_main - init_tls - dl_allocate_tls_storage - allocate_dtv 
0x7ffff7ff5000 - 0x7ffff7ff6000 = dl_main - init_tls - dl_allocate_tls_storage - libc_memalign 
0x7ffff7ff6000 - 0x7ffff7ff7000 = system_dirs 
0x7ffff7ff7000 - 0x7ffff7ff8000 = realname libc.so.6 
0x7ffff7ff8000 - 0x7ffff7ffc000 = vdso_image

 

 

基于glibc的调试多次 mmap 的调用

这里主要是基于 glibc 进行调试, 这里的断点是上面的 内核调试断点的一部分

然后 上面各个 mmap 映射区的 大部分映射空间的逻辑意义 也可以在这里找到, 上面给出了各个 mmap 映射区的逻辑意义信息, 但是没有给为什么 

这里 基于 glibc 的调试就是 给出为什么 

 

第一个 mmap 调用如下, 映射的是 “/root/Desktop/linux/glibc-2.23/install/lib” 的字符串, 映射的区间是 0x7ffff7ff7000 - 0x7ffff7ff8000

4a825416b1914a9bb1191472087958d4.png

 

分配空间的大小参照, 以及具体使用如下, 注意这里的 malloc 不同于我们不同的应用程序的 malloc 调用

fae71955ff464bdd9291f3e6321215dc.png

 

第二个 mmap 调用如下,映射的是 “libc.so.6” 对应的一个 linkmap 的一个结构体, 映射的区间是 0x7ffff7ff6000 - 0x7ffff7ff7000

972885a97ec641dca07e4271fdcc8611.png

 

分配空间的大小参照, 以及具体使用如下, 主要是新建了一个 linkmap 结构体, 然后存储它的各个属性 

08e405dcb5684d3f844941b8f17d7f14.png  

第三个 mmap 调用如下,映射的是 “libc-2.23.so” 的动态库, 映射的区间是 0x7ffff7a38000 - 0x7ffff7dd7000

因为在宿主机 和 在 qemu 虚拟机中 libc-2.23.so 大小不太一样, 因此这里 libc-2.23.so 对应的 vma 区间不太一致 

50067e952d4440e7a18a0fde02314179.png

 

第四个 mmap 调用如下, 这里我们不太关心, 直接跳过 

内核中没有该断点, 因为这里是传入的地址, 内核的断点是在 申请空间的地方

a5c4eaad61444992b9ff5b96ea8e7e34.png

 

第五个 mmap 调用如下, 这里我们不太关心, 直接跳过 

内核中没有该断点, 因为这里是传入的地址, 内核的断点是在 申请空间的地方

7277b67bda764e74a7961e2ab7884a44.png

 

第六个 mmap 调用如下, dl_main - init_tls - dl_allocate_tls_storage - libc_memalign 映射了 4k 的映射区, 映射的区间是 0x7ffff7ff5000 - 0x7ffff7ff6000

ce72e0d0996147adbf844a368a229610.png

 

第七个 mmap 调用如下, dl_main - init_tls - dl_allocate_tls_storage - allocate_dtv 映射了 4k 的映射区, 映射的区间是 0x7ffff7ff4000 - 0x7ffff7ff5000

2786ddaca9e048e4b83aa79b25d53612.png

 

第八个 mmap 调用申请了 40kb, 申请的空间为 0x7ffff7fea000 - 0x7ffff7ff4000

从 rax 寄存器中可以看到 mmap 申请的空间的首地址, 从上层堆栈信息中可以看到当前执行的代码位置 

a1192f7d97de413e8676211ddbd98cc6.png

 

第九个 mmap 调用申请了 40kb, 申请的空间为 0x7ffff7fea000 - 0x7ffff7fe0000

从 rax 寄存器中可以看到 mmap 申请的空间的首地址, 从上层堆栈信息中可以看到当前执行的代码位置 

4fd5801c44fe4638836d44dcb2d29ba5.png

 

 

 main 中 第一个 mmap 和 第二个 mmap 调用如下, 你也可以对一下 上面 glibc 上层调用 mmap 的代码的地址信息 

809febbf94624237bcdf9f5131da7821.png

84256796d7934c279d0ccb3ba2419550.png 

 

同一个文件, 两个虚拟地址空间怎么关联的?

虚拟地址空间映射物理页的时候是从 pagecache 中获取的 给定的文件对应的物理页

两个虚拟地址空间 对应的文件一样, 因此是映射到了相同的物理页 

b6deb44adfea42388c8667b6812d9a11.png

 

第一个 mmap 映射区的第一个物理页如下 

0ac3a169640e4e40898f0fc7ce32c367.png

 

第二个 mmap 映射区的第一个物理页如下, 可以发现 两个虚拟地址空间映射的物理页相同

f69c98d2ce064193acc9dd2a0dbfa5bc.png

 

 

 

 

 

这篇关于59 多次 mmap 虚拟地址的关系的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1001321

相关文章

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

读软件设计的要素04概念的关系

1. 概念的关系 1.1. 概念是独立的,彼此间无须相互依赖 1.1.1. 一个概念是应该独立地被理解、设计和实现的 1.1.2. 独立性是概念的简单性和可重用性的关键 1.2. 软件存在依赖性 1.2.1. 不是说一个概念需要依赖另一个概念才能正确运行 1.2.2. 只有当一个概念存在时,包含另一个概念才有意义 1.3. 概念依赖关系图简要概括了软件的概念和概念存在的理

数据依赖基础入门:函数依赖与数据库设计的关系

在数据库设计中,数据依赖 是一个重要的概念,它直接影响到数据库的结构和性能。函数依赖 作为数据依赖的一种,是规范化理论的基础,对数据库设计起着至关重要的作用。如果你是一名数据库设计的初学者,这篇文章将帮助你理解函数依赖及其在数据库设计中的应用。 什么是数据依赖? 数据依赖 是指同一关系中属性间的相互依赖和制约关系,它是数据库设计中语义的体现。在现实世界中,数据之间往往存在某种依赖关系,而这

c++ 和C语言的兼容性关系

C++ 和 C 语言有很高的兼容性,但也存在一些差异和限制。下面是它们的兼容性关系的详细介绍: 兼容性 C++ 是 C 的超集: C++ 语言设计为兼容 C 语言的语法和功能,大部分 C 代码可以在 C++ 编译器中编译运行。 标准库兼容性: C++ 标准库包含了 C 标准库的内容,如 stdio.h、stdlib.h、string.h 等头文件,但 C++ 的标准库也提供了额外的功能,如

七、Maven继承和聚合关系、及Maven的仓库及查找顺序

1.继承   2.聚合   3.Maven的仓库及查找顺序

file-max与ulimit的关系与差别

http://zhangxugg-163-com.iteye.com/blog/1108402 http://ilikedo.iteye.com/blog/1554822

【编程底层原理】方法区、永久代和元空间之间的关系

Java虚拟机(JVM)中的内存布局经历了几个版本的变更,其中方法区、永久代和元空间是这些变更中的关键概念。以下是它们之间的关系: 一、方法区: 1、方法区是JVM规范中定义的一个概念,它用于存储类信息、常量、静态变量、即时编译器编译后的代码等数据。 3、它是JVM运行时数据区的一部分,与堆内存一样,是所有线程共享的内存区域。 二、永久代(PermGen): 1、在Java SE 7之前,

在项目中,控制权限保存时,如果多次修改权限,该如何写?

在项目中,控制权限保存时,如果多次修改权限,该如何写? 错误代码: package cn.itcast.crm.service.impl;import java.util.List;import javax.annotation.Resource;import org.apache.commons.lang.xwork.StringUtils;import org.springfr

笔记整理—内核!启动!—kernel部分(1)驱动与内核的关系

首先,恭喜完成了uboot部分的内容整理,其次补充一点,uboot第一部分和第二部分的工作不是一定的,在不同的版本中,可能这个初始化早一点,那个的又放在了第二部分,版本不同,造成的工作顺序不同,但终归是要完成基本内容初始化并传参给kernel的。         那么至于驱动与内核的关系,用一张图来说明最适合不过:         驱动位于OS层的中下层与硬件相接。驱动是内