objdump vs / proc / pid / maps的虚拟内存地址?
作者:互联网
我正在尝试了解程序的可执行程序集在程序加载/运行时的确切位置.我发现有两个资源在讨论这个问题,但它们有点难以阅读:
> Understanding ELF using readelf and objdump Linux article(代码格式混乱)
> Michael Guyver, Some Assembly Required*: Relocations, Relocations(很多我不太精通的装配)
所以,这是一个简短的例子;我很感兴趣尾部程序的可执行部分最终在哪里.基本上,objdump告诉我这个:
$objdump -dj .text /usr/bin/tail | head -10
/usr/bin/tail: file format elf32-i386
Disassembly of section .text:
08049100 <.text>:
8049100: 31 ed xor %ebp,%ebp
8049102: 5e pop %esi
8049103: 89 e1 mov %esp,%ecx
...
我假设我看到在这里调用tail的’main()’,没有剥离符号.无论如何,可执行部分的开头是,根据这个,0x08049100;我对它最终到底的地方感兴趣.
然后,我在后台运行尾巴,得到它的pid:
$/usr/bin/tail -f & echo $!
28803
…我检查它的/ proc / pid / maps:
$cat /proc/28803/maps
00547000-006a8000 r-xp 00000000 08:05 3506 /lib/i386-linux-gnu/libc-2.13.so
...
008c6000-008c7000 r-xp 00000000 00:00 0 [vdso]
08048000-08054000 r-xp 00000000 08:05 131469 /usr/bin/tail
08054000-08055000 r--p 0000b000 08:05 131469 /usr/bin/tail
08055000-08056000 rw-p 0000c000 08:05 131469 /usr/bin/tail
08af1000-08b12000 rw-p 00000000 00:00 0 [heap]
b76de000-b78de000 r--p 00000000 08:05 139793 /usr/lib/locale/locale-archive
...
bf845000-bf866000 rw-p 00000000 00:00 0 [stack]
现在我有三次尾 – 但是可执行段r-xp(它是.text?)显然是在0x08048000(一个地址显然是was standardized back with SYSV for x86;另一个看到Anatomy of a Program in Memory : Gustavo Duarte的图像)
使用下面的gnuplot脚本,我得到了这个图像:
第一个(最上面)的图显示了来自objdump(从0x0开始)的部分的“文件偏移”;中间图显示来自objdump和底部图的部分的“VMA”(虚拟内存地址)显示来自/ proc / pid / maps的布局 – 这两个部分都从0x08048000开始;所有三个图都显示相同的范围.
比较最上层和中间的情节,似乎这些部分“从原样”可执行文件到VMA地址(除了结尾)之外更多地翻译;这样整个可执行文件(不仅仅是.text部分)从0x08048000开始.
但是比较中间和底部的情节,似乎当程序在内存中运行时,只有.text被“推回”到0x08048000 – 而且不仅如此,它现在看起来更大了!
我到目前为止唯一的解释是我在某处读到的内容(但丢失了链接):内存中的图像必须分配整数页(大小为4096字节),并从页边界开始.整个页面数量说明了更大的尺寸 – 但是,鉴于所有这些都是虚拟地址,为什么需要将它们“捕捉”到页面边界(也可能不会将虚拟地址映射到物理地址)页面边界?)
那么 – 有人可以提供一个解释,以便为什么/ proc / pid / maps看到.text部分在与objdump不同的虚拟地址区域中?
mem.gp gnuplot脚本:
#!/usr/bin/env gnuplot
set term wxt size 800,500
exec = "/usr/bin/tail" ;
# cannot do - apparently gnuplot waits for children to exit, so locks here:
#runcmd = "bash -c '" . exec . " -f & echo $!'"
#print runcmd
#pid = system(runcmd) ;
#print runcmd, "pid", pid
# run tail -f & echo $! in another shell; then enter pid here:
pid = 28803
# $1 Idx $2 Name $3 Size $4 VMA $5 LMA $6 File off
cmdvma = "<objdump -h ".exec." | awk '$1 ~ \"^[0-9]+$\" && $2 !~ \".gnu_debuglink\" {print $1, $2, \"0X\"$3, \"0X\"$4;}'" ;
cmdfo = "<objdump -h ".exec." | awk '$1 ~ \"^[0-9]+$\" && $2 !~ \".gnu_debuglink\" {print $1, $2, \"0X\"$3, \"0X\"$6;}'" ;
cmdmaps = "<cat /proc/".pid."/maps | awk '{split($1,a,\"-\");b1=strtonum(\"0x\"a[1]);b2=strtonum(\"0x\"a[2]);printf(\"%d \\\"%s\\\" 0x%08X 0x%08X\\n\", NR,$6,b2-b1,b1);}'"
print cmdvma
print cmdfo
print cmdmaps
set format x "0x%08X" # "%016X";
set xtics rotate by -45 font ",7";
unset ytics
unset colorbox
set cbrange [0:25]
set yrange [0.5:1.5]
set macros
set multiplot layout 3,1 columnsfirst
# 0x08056000-0x08048000 = 0xe000
set xrange [0:0xe000]
set tmargin at screen 1
set bmargin at screen 0.667+0.1
plot \
cmdfo using 4:(1+$0*0.01):4:($4+$3):0 with xerrorbars lc palette t "File off", \
cmdfo using 4:(1):2 with labels font ",6" left rotate by -45 t ""
set xrange [0x08048000:0x08056000]
set tmargin at screen 0.667
set bmargin at screen 0.333+0.1
plot \
cmdvma using 4:(1+$0*0.01):4:($4+$3):0 with xerrorbars lc palette t "VMA", \
cmdvma using 4:(1):2 with labels font ",6" left rotate by -45 t ""
set tmargin at screen 0.333
set bmargin at screen 0+0.1
plot \
cmdmaps using 4:(1+$0*0.01):4:($4+$3):0 with xerrorbars lc palette t "/proc/pid/maps" , \
cmdmaps using 4:(1):2 with labels font ",6" left rotate by -45 t ""
unset multiplot
#system("killall -9 " . pid) ;
解决方法:
简短的回答是,可加载的段基于类型为PT_LOAD的ELF程序头被映射到内存中.
PT_LOAD – The array element specifies a loadable segment, described by
p_filesz and p_memsz. The bytes from the file are mapped to the
beginning of the memory segment. If the segment’s memory size
(p_memsz) is larger than the file size (p_filesz), the “extra” bytes
are defined to hold the value 0 and to follow the segment’s
initialized area. The file size may not be larger than the memory
size. Loadable segment entries in the program header table appear in
ascending order, sorted on the p_vaddr member.
例如,在我的CentOS 6.4上:
objdump -x `which tail`
Program Header:
LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
filesz 0x0000e4d4 memsz 0x0000e4d4 flags r-x
LOAD off 0x0000e4d4 vaddr 0x080574d4 paddr 0x080574d4 align 2**12
filesz 0x000003b8 memsz 0x0000054c flags rw-
从/ proc / pid / maps:
cat /proc/2671/maps | grep `which tail`
08048000-08057000 r-xp 00000000 fd:00 133669 /usr/bin/tail
08057000-08058000 rw-p 0000e000 fd:00 133669 /usr/bin/tail
你会注意到map和objdump对后续部分的加载地址所说的有区别,但这与加载器计算部分占用的内存量以及对齐字段有关.第一个可加载段映射到0x08048000,大小为0x0000e4d4,因此您希望它从0x08048000变为0x080564d4,但对齐表示在2 ^ 12字节页上对齐.如果你做数学运算,你最终得到0x8057000,匹配/ proc / pid / maps.所以第二个段映射到0x8057000并且大小为0x0000054c(结束于0x805754c),它与0x8058000对齐,匹配/ proc / pid / maps.
标签:linux,memory,virtual-memory,c-3 来源: https://codeday.me/bug/20190716/1477650.html