1

I wanted to see how the Linux kernel function mmap() is implemented, so I downloaded the GNU C Library (glibc) source from the GNU page. (I downloaded glibc-2.27 because ldd --version told me I was using GLIBC 2.27)
Now to find the definition of mmap() I did grep -r "mmap(void" * which returned nothing, so I tried grep -r "mmap (void" * which returned the following:

conform/data/sys/mman.h-data:function {void*} mmap (void*, size_t, int, int, int, off_t)
include/sys/mman.h:extern void *__mmap (void *__addr, size_t __len, int __prot,
malloc/memusage.c:mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
manual/llio.texi:@deftypefun {void *} mmap (void *@var{address}, size_t @var{length}, int @var{protect}, int @var{flags}, int @var{filedes}, off_t @var{offset})
misc/sys/mman.h:extern void *mmap (void *__addr, size_t __len, int __prot,
misc/mmap.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)
support/xunistd.h:void *xmmap (void *addr, size_t length, int prot, int flags, int fd);
support/xmmap.c:xmmap (void *addr, size_t length, int prot, int flags, int fd)
sysdeps/unix/sysv/linux/mmap.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)
sysdeps/mach/hurd/dl-sysdep.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)
sysdeps/mach/hurd/mmap.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)

Of all the results that were about mmap() and not about __mmap() I found that the definition of mmap() is in malloc/memusage.c which defined mmap() as the following:

/* `mmap' replacement.  We do not have to keep track of the size since
   `munmap' will get it as a parameter.  */
void *
mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
{
  void *result = NULL;

  /* Determine real implementation if not already happened.  */
  if (__glibc_unlikely (initialized <= 0))
    {
      if (initialized == -1)
        return NULL;

      me ();
    }

  /* Always get a block.  We don't need extra memory.  */
  result = (*mmapp)(start, len, prot, flags, fd, offset);

  ...

  /* Return the pointer to the user buffer.  */
  return result;
}

I figured result = (*mmapp)(start, len, prot, flags, fd, offset); is what matters, and in this file there are two other parts that deal with this mmapp function pointer, which are:

  1. declaration: static void *(*mmapp) (void *, size_t, int, int, int, off_t);
  2. initialization in some function called me(): mmapp = (void *(*)(void *, size_t, int, int, int, off_t))dlsym (RTLD_NEXT, "mmap");
    The dlsym() function, according to the manual, takes a "handle" of a dynamic library returned by dlopen() and the null-terminated symbol name, returning the address where that symbol is loaded into memory.

Thus, the whole process can be summarized as follows:

  1. mmap() calls a function pointed to by the pointer mmapp
  2. mmapp is set to point to the symbol "mmap" in a dynamic library loaded into memory

But I can't find any information on dynamic library with the symbol "mmap".
Am I doing something wrong in the process of code analysis? I haven't much experience in code analysis, let alone looking into system call functions or kernel codes, so any advice or push in the right direction would be greatly appreciated.
Thanks in advance!

WannabeArchitect
  • 1,058
  • 2
  • 11
  • 22
  • 1
    None of this is going to be very interesting to you, since at the bottom, all you're going to see in the library is a system call. The real action here is all in the kernel. See for instance https://github.com/torvalds/linux/blob/master/mm/mmap.c. But you might rather look at some older or "toy" OS in which the code may be much simpler and more readable. – Nate Eldredge Nov 05 '20 at 05:18
  • @NateEldredge Hey I think this might actually be very helpful. But I think you can tell from my question that I'm not very familiar with Linux kernel stuff - I want to ask, in the manual for mmap it says we need to include but there are no directory named sys in the git (there is linux/include/linux/mman.h but it doesn't have mmap definition nor declaration)... am I misunderstanding something here? – WannabeArchitect Nov 05 '20 at 05:29
  • It's too long a story for a comment. Ask a separate question. – Nate Eldredge Nov 05 '20 at 06:22
  • 1
    https://stackoverflow.com/questions/20082433/what-is-the-difference-between-linux-if-h-and-net-if-h/20083006#20083006 is sort of relevant, though – Nate Eldredge Nov 05 '20 at 06:23

1 Answers1

5

The Linux kernel has multiple different mmap syscalls, and they're not the same across different versions and architectures. The libc mmap function abstracts that and presents the POSIX interface to its users. However, Glibc code is complicated by the fact that it also targets non-Linux platforms, and has codepaths for all parts of the off_t/off64_t transition (see Glibc manual § Feature Test Macros for some more information about that). You'd have an easier time looking at an alternative minimal libc like musl which only targets modern Linux and doesn't aim for historical nor full POSIX compatibility.

In musl's case: prototype and constants are in include/sys/mman.h. Implementation is in src/mman/mmap.c which does some argument processing before handing off to syscall(SYS_mmap2) (in most cases).

In Glibc's case: prototype is in misc/sys/mman.h. Constants are scattered as not all platforms are the same; see bits/mman.h, sysdeps/unix/sysv/linux/bits/mman-shared.h, sysdeps/unix/sysv/linux/bits/mman-map-flags-generic.h, sysdeps/unix/sysv/linux/x86/bits/mman.h for example. The actual definition is in sysdeps/unix/sysv/linux/mmap.c, and like musl's, it does some argument processing before handing off to syscall(SYS_mmap2) (in most cases).

Either way, mmap in the libc can only work with kernel support. Hence @NateEldredge's comment that it's fairly uninteresting.

By the way, what you saw in malloc/memusage.c is not the definition of mmap that you are using. That gets built into a separate libmemusage library which intercepts mmap calls (among others) and adds some tracking around them. Your distribution may or may not ship this; if it does, you would have to opt-in to using it by running your program with LD_PRELOAD=libmemusage.so (which is effectively what the memusage wrapper script does). That is why it is using dlsym: it is defining a replacement mmap symbol, but needs to look up the original mmap to be able to wrap calls to it.

Within the Linux kernel, SYS_mmap2 is mapped to sys_mmap_pgoff on most architectures, defined in mm/mmap.c as

SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
        unsigned long, prot, unsigned long, flags,
        unsigned long, fd, unsigned long, pgoff)

If given a file descriptor, it results in calling the

struct file {
    const struct file_operations {
        int (*mmap) (struct file *, struct vm_area_struct *);
    } *f_op;
}

callback (from include/linux/fs.h) when defined by the filesystem providing that file. These have various different implementations, but in general they add a new vma to the process's pagetable which either makes other kernel machinery map specific pages into the process, or makes pagefaults within that area calling back into the filesystem to provide a page to be mapped at that time.

ephemient
  • 198,619
  • 38
  • 280
  • 391