Search code examples
cpointersfile-descriptorstrace

Linux read system call logged by strace - how to understand pointer to buffer value?


I ran strace and in its output I got lines like:

read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\273\0\0\0\0\0\0"..., 832) = 832

I've read man on read, so string "" is pointer to buf (ssize_t read(int fd, void *buf, size_t count);), but what does that particular string mean? In particular:

  • ELF most probably executable-linked - why here for pointer?
  • \ lets escape special characters - why escape digits here?
  • > what is it for?

Solution

  • What you're seeing here is the dynamic loader opening and reading the header of the needed libraries. Almost any strace of an ELF program (which is the standard executable format in Linux) starts with a bunch of open/read/mmap/close for this reason: the dynamic loader is loading the needed libraries.

    What you see here:

    read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\273\0\0\0\0\0\0"..., 832) = 832
    

    Is just what the loader is reading from the file:

    • 3: this is the fd that was assigned to the open file by open().
    • "\177ELF\2\1\1\0...": this is is the content of the file that is being read. Numbers are there because those are octal escape sequences, for example \1 means the byte 1. They are printed like this because otherwise you would not be able to see them, and would create a mess on your terminal, since most of those are special non printable characters.
    • 832: this is the number of bytes the loader wants to read from the file.
    • = 832: this is the result of the read(), which means that all the requested bytes were read.

    In other words, those escape sequences, are just a way to make non-printable bytes human readable. You can test this running od -bc on the file that the loader is trying to open, you can see its content in octal form plus with printable characters and backslash escapes:

    $ od -bc /lib/x86_64-linux-gnu/libc.so.6 | head -n4
    0000000 177 105 114 106 002 001 001 003 000 000 000 000 000 000 000 000
            177   E   L   F 002 001 001 003  \0  \0  \0  \0  \0  \0  \0  \0
    0000020 003 000 076 000 001 000 000 000 260 034 002 000 000 000 000 000
            003  \0   >  \0 001  \0  \0  \0 260 034 002  \0  \0  \0  \0  \0
    

    A more complete example would be the following, from strace /bin/true:

    open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 4
    read(4, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\4\2\0\0\0\0\0"..., 832) = 832
    fstat(4, {st_mode=S_IFREG|0755, st_size=1689360, ...}) = 0
    mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0d3d877000
    mmap(NULL, 3795296, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 4, 0) = 0x7f0d3d2dd000
    mprotect(0x7f0d3d472000, 2097152, PROT_NONE) = 0
    mmap(0x7f0d3d672000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 4, 0x195000) = 0x7f0d3d672000
    mmap(0x7f0d3d678000, 14688, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0d3d678000
    close(4)  
    

    You can see that the loader is opening "libc", which is the ELF file for the standard C library. It reads its header to determine which sections are to be loaded, and then mmaps all the needed sections in memory, assigning the right permissions.