Who is doing read("/proc/self/maps", 1024) at startup?

To my embarrassment I can't find the source of these system calls

openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
read(3, "5641f7469000-5641f746f000 r--p 0"..., 1024) = 1024
read(3, "79cdf5000-7fd79cdf9000 r--p 001f"..., 1024) = 1024
read(3, "0000000 00:00 0 \n7fd79ce59000-7f"..., 1024) = 832
close(3)

Regardless, these reads are inefficient, they should be PAGE_SIZE. Linux /proc implementation allocates internal buffer and "renders" virtual file contents there, and then serves short reads from there so it doesn't make sense to read less than internal buffer size, it is just waste of system calls.

See seq_file.c « fs - kernel/git/torvalds/linux.git - Linux kernel source tree

1 Like

Which target is this - *-linux-gnu?
Also, which program - something simple like hello-world?

Neither Rust library nor compiler read /proc/self/maps.
This looks more like something glibc would do.

Yes this is Linux.

I found __pthread_getattr_np() which does fopen ("/proc/self/maps", "rce"); immediately. And getrlimit and sched_getaffinity.

Thanks!

That is probably a consequence of Rust init calling pthread_attr_getstack -- or rather pthread_getattr_np in preparation for that.

No, it's definitely Rust's runtime doing this, somehow.

I have observed this behavior also. It happens with a program as simple as fn main() {}.

$ printf 'fn main() {}\n' > test-rust.rs
$ rustc --version
rustc 1.59.0
$ rustc -g -O test-rust.rs -o test-rust
$ strace -e trace=openat,read ./test-rust
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
read(3, "561342b1d000-561342b23000 r--p 0"..., 1024) = 1024
read(3, "0000-7f33e4d24000 r--p 00214000 "..., 1024) = 1024
read(3, "linux-x86-64.so.2\n7f33e4d89000-7"..., 1024) = 823
+++ exited with 0 +++

Compare what happens with the equivalent C program:

$ printf 'int main(void) { return 0; }\n' > test-c.c
$ gcc -O2 -g test-c.c -Wl,--no-as-needed -lgcc_s -o test-c
$ strace -e trace=openat,read ./test-c
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
+++ exited with 0 +++

So we can rule out both glibc and libgcc_s (the stack unwinder, iirc).

1 Like

Yes.

This is what glibc does: https://sourceware.org/git/?p=glibc.git;a=blob;f=nptl/pthread_getattr_np.c;h=9c5b73b452ab0faa70f8e734c6c725e98f2fa7e3;hb=c804cd1c00adde061ca51711f63068c103e94eef#l85

It's not really "somehow", rustc runs lang_start before main and that initializes thread stuff.

Several other posts happened while I was failing to persuade GDB to let me set breakpoints in there. It is clear to me now what is going on.

4 Likes

I'm just gonna leave this here: RFC PATCH: Don't use /proc/self/maps to calculate size of initial thread stack

4 Likes