x86では、プロセスのVMでは、共有ライブラリがオンラインのほとんどの記事で言及されているmmap領域であるヒープとスタックの間にロードされることがわかりました。ただし、PowerPC Linuxシステムでは、プログラム自体がロードされる場所の下にロードされたすべてのライブラリが表示されます。 「strace」は、ライブラリロードアドレスがライブラリをロード/マッピングする前に事前に決定されることを示します(ldによって決定されると仮定)。
これが建築に関連しているのだろうか。これに関するオンライン文書はありますか?
メモリマップ(ppc):
> cat /proc/self/maps
00100000-00102000 r-xp 00000000 00:00 0 [vdso]
0fe40000-0ffab000 r-xp 00000000 08:02 147120 /lib/libc-2.11.1.so
0ffab000-0ffbb000 ---p 0016b000 08:02 147120 /lib/libc-2.11.1.so
0ffbb000-0ffbf000 r--p 0016b000 08:02 147120 /lib/libc-2.11.1.so
0ffbf000-0ffc0000 rw-p 0016f000 08:02 147120 /lib/libc-2.11.1.so
0ffc0000-0ffc3000 rw-p 00000000 00:00 0
0ffd0000-0fff0000 r-xp 00000000 08:02 147113 /lib/ld-2.11.1.so
0fff0000-0fff1000 r--p 00020000 08:02 147113 /lib/ld-2.11.1.so
0fff1000-0fff2000 rw-p 00021000 08:02 147113 /lib/ld-2.11.1.so
10000000-10005000 r-xp 00000000 08:02 195850 /bin/cat
10014000-10015000 rw-p 00004000 08:02 195850 /bin/cat
10015000-10036000 rwxp 00000000 00:00 0 [heap]
24000000-24001000 rw-p 00000000 00:00 0
24013000-24014000 rw-p 00000000 00:00 0
bfade000-bfaff000 rw-p 00000000 00:00 0 [stack]
strace (ppcから):
> strace cat
execve("/bin/cat", ["cat"], [/* 26 vars */]) = 0
brk(0) = 0x10015000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x24000000
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(0x3, 0xbfb84558) = 0
mmap(NULL, 70203, PROT_READ, MAP_PRIVATE, 3, 0) = 0x24001000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\2\1\0\0\0\0\0\0\0\0\0\0\3\0\24\0\0\0\1\17\345\376\260\0\0\0004"..., 512) = 512
fstat64(0x3, 0xbfb84540) = 0
mmap(0xfe40000, 1582324, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xfe40000
mprotect(0xffab000, 65536, PROT_NONE) = 0
mmap(0xffbb000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16b000) = 0xffbb000
mmap(0xffc0000, 9460, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffc0000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x24013000
mprotect(0xffbb000, 16384, PROT_READ) = 0
mprotect(0xfff0000, 4096, PROT_READ) = 0
munmap(0x24001000, 70203) = 0
brk(0) = 0x10015000
brk(0x10036000) = 0x10036000
fstat64(0x1, 0xbfb84d00) = 0
fstat64(0, 0xbfb84d00) = 0
答え1
ランタイムスタックの(好ましい)構成は、ハードウェアアーキテクチャによって異なります。ただし、プログラム自体と共有ライブラリ、動的にリンクされた実行可能ファイルの場合、マップされた領域のメモリ位置はリンカによって決定されます。一般的に言えば、ユーザープログラムのコンポーネントがどこにあるべきかを判断するのはカーネルの仕事ではありません。 CPU アーキテクチャもそのような順序を意味しません。同じハードウェア上または実行中のオペレーティングシステム(カーネルなど)内でも、さまざまなリンカがそれを別の方法で配置することを想像できます(exec Linux呼び出しはELFファイルからリンカ名を抽出します。の変数を参照elf_interpreter
)。load_elf_binary()
fs/binfmt_elf.c
Linuxでは、デフォルトの動的リンカーはld-linux
glibcの一部です。オブジェクトマッピングを試みる方法は、ソースコードの関数で見ることができます_dl_map_object_from_fd()
。elf/dl-load.c
実行可能ファイルのデフォルト設定が考慮される場合があり(おそらく実行可能ファイルを作成したコンパイラとリンカによって異なります)、メモリマップ構成がカーネルによって決定されることがあります。
動的リンカーとそのアーキテクチャの依存関係に関するGoogle情報があります。たとえば、次のようになります。
答え2
少し掘り下げた後、私は自分の質問に答えます。簡単に言えば、これはアーチに頼ることです。 PowerPC32は優先ロードアドレス機能を定義しますが、他のほとんどのアーキテクチャ(x86を含む)はそうではありません。
/* The idea here is that to conform to the ABI, we are supposed to try
to load dynamic objects between 0x10000 (we actually use 0x40000 as
the lower bound, to increase the chance of a memory reference from
a null pointer giving a segfault) and the program's load address;
this may allow us to use a branch instruction in the PLT rather
than a computed jump. The address is only used as a preference for
mmap, so if we get it wrong the worst that happens is that it gets
mapped somewhere else. */
ElfW(Addr)
__elf_preferred_address (struct link_map *loader, size_t maplength,
ElfW(Addr) mapstartpref)
{
ElfW(Addr) low, high;
struct link_map *l;
Lmid_t nsid;
/* If the object has a preference, load it there! */
if (mapstartpref != 0)
return mapstartpref;
/* Otherwise, quickly look for a suitable gap between 0x3FFFF and
0x70000000. 0x3FFFF is so that references off NULL pointers will
cause a segfault, 0x70000000 is just paranoia (it should always
be superseded by the program's load address). */
low = 0x0003FFFF;
high = 0x70000000;
for (nsid = 0; nsid < DL_NNS; ++nsid)
for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
{
ElfW(Addr) mapstart, mapend;
mapstart = l->l_map_start & ~(GLRO(dl_pagesize) - 1);
mapend = l->l_map_end | (GLRO(dl_pagesize) - 1);
assert (mapend > mapstart);
/* Prefer gaps below the main executable, note that l ==
_dl_loaded does not work for static binaries loading
e.g. libnss_*.so. */
if ((mapend >= high || l->l_type == lt_executable)
&& high >= mapstart)
high = mapstart;
else if (mapend >= low && low >= mapstart)
low = mapend;
else if (high >= mapend && mapstart >= low)
{
if (high - mapend >= mapstart - low)
low = mapend;
else
high = mapstart;
}
}
high -= 0x10000; /* Allow some room between objects. */
maplength = (maplength | (GLRO(dl_pagesize) - 1)) + 1;
if (high <= low || high - low < maplength )
return 0;
return high - maplength; /* Both high and maplength are page-aligned. */
}