공유 라이브러리 아키텍처에 의해 로드된 메모리 주소 영역이 종속됩니까?

공유 라이브러리 아키텍처에 의해 로드된 메모리 주소 영역이 종속됩니까?

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-linuxglibc의 일부입니다. 객체 매핑을 시도하는 방법은 소스 코드의 함수에서 볼 수 있습니다 _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.  */
}

관련 정보