2

When a library is dynamically linked to a program does it have the same address in that program as in any other program?

I my head I imagined each process gets the whole of the address space and then everything in that process (inc. dynamic libraries that are already in memory) gets mapped to semi-random parts of it because of ASLR.

But I did a short experiment that seems to imply that the address of libraries that are in memory are fixed across different processes and thus reusable across programs? Is that correct?

I wrote two short c programs which used the "sleep" function. In one I printed out the address of the sleep function and in the second I assigned a function pointer to that address. I ran them both and the sleep function worked in both.

#include <stdio.h>
#include <unistd.h>

int main()
{
    while(1)
    {
        printf("%s\n", &"hi");
        sleep(2);
        printf("pointer to sleep: %p\n", sleep);
    }
}
#include <stdio.h>
#include <unistd.h>

#define sleepagain ((void (*)(int))0x7fff7652e669) //addr of sleep from first program

int main()
{
    while(1)
    {
        printf("%s\n", &"test");
        sleepagain(2);
    }
}

I wasn't sure what this would show but what it actually showed was a) the address was the same every time I ran the first program and b) that sleep still functioned when I ran the second.

I think I understand how this works but I am curious if it has to work the way it does and what are the reasons behind it?

Just to reference the answer I got already when I took a look with otool -IvV I got:

a.out:
Indirect symbols for (__TEXT,__stubs) 2 entries
address            index name
0x0000000100000f62     2 _printf
0x0000000100000f68     3 _sleep
Indirect symbols for (__DATA,__nl_symbol_ptr) 2 entries
address            index name
0x0000000100001000     4 dyld_stub_binder
0x0000000100001008 ABSOLUTE
Indirect symbols for (__DATA,__got) 1 entries
address            index name
0x0000000100001010     3 _sleep
Indirect symbols for (__DATA,__la_symbol_ptr) 2 entries
address            index name
0x0000000100001018     2 _printf
0x0000000100001020     3 _sleep

Which is also what the indirect address was in lldb. The address was the address of sleep itself:

Process 11209 launched: 'stuff/a.out' (x86_64)
hi
Process 11209 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00007fff7652e669 libsystem_c.dylib`sleep
libsystem_c.dylib`sleep:
->  0x7fff7652e669 <+0>: push   rbp
    0x7fff7652e66a <+1>: mov    rbp, rsp
    0x7fff7652e66d <+4>: push   rbx
    0x7fff7652e66e <+5>: sub    rsp, 0x28
Target 0: (a.out) stopped.

For some additional info:

$ otool -hv a.out
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL LIB64     EXECUTE    15       1296   NOUNDEFS DYLDLINK TWOLEVEL PIE

1 Answers1

3

On macOS, many system libraries are part of the dyld shared cache. There's a machine-wide mapping. So, those libraries end up at the same address in all processes of the same architecture (32- or 64-bit).

The location of the dyld shared cache is randomized at system boot. So, library addresses will be the same from process to process until you reboot.

Not all system libraries are part of the cache, only the ones that Apple deems to be commonly loaded.

Libraries of your own or from third parties will be loaded at random locations each time they're loaded, assuming they are position-independent.

Try looking at the output from vmmap -v <pid>. Look for the line with "machine-wide VM submap" and those that follow.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • I had a go with my own dylib and yeah, each time it was loaded it was at a new location, if I kept it loaded (by running a program using it in an infinite loop) the addresses were the same and I could get it from other programs. Enjoying learning how this stuff works. – AsksStupidQuestions Mar 25 '19 at 15:15
  • 1
    I would not expect keeping one process with it loaded would affect subsequent processes that load it. – Ken Thomases Mar 25 '19 at 19:20
  • Hm, you're right, I guess I got fooled because the lowest two bytes of the address were the same. – AsksStupidQuestions Mar 25 '19 at 19:53
  • 1
    Yeah, ASLR should only move stuff by increments of a page size (4096 a.k.a. 0x1000). So, it will never change the low three hex digits. – Ken Thomases Mar 25 '19 at 20:03