Note that there are two memory regions of 2044KB with null permissions. As mentioned earlier, the ELF's 'execution view' is concerned with how to load an executable binary into memory. When ld.so brings in the dynamic libraries, it looks at the segments labelled as LOAD (look at "Program Headers" and "Section to Segment mapping" from readelf -a xxx.so command.) Usually there are two LOAD segments, and there is a "hole" between the two segments (look at the VirtAddr and MemSiz of these two segments), so ld.so will make this hole inaccessible deliberately: Look for the PROT_NONE symbol in _dl_map_object_from_fd in elf/dl-load.c
Também é fácil ver isso acontecendo como uma chamada mprotect
, usando strace, por exemplo. strace -f grep -- . /proc/self/maps 2>&1 |less
.
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELFopen("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%>%pre%%pre%%pre%%pre%0%pre%%pre%%pre%%pre%%pre%%pre%"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=471728, ...}) = 0
mmap(NULL, 2564360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0e3ad2a000
mprotect(0x7f0e3ad9c000, 2093056, PROT_NONE) = 0
mmap(0x7f0e3af9b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x71000) = 0x7f0e3af9b000
close(3) = 0
%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%>%pre%%pre%%pre%%pre%0%pre%%pre%%pre%%pre%%pre%%pre%"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=471728, ...}) = 0
mmap(NULL, 2564360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0e3ad2a000
mprotect(0x7f0e3ad9c000, 2093056, PROT_NONE) = 0
mmap(0x7f0e3af9b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x71000) = 0x7f0e3af9b000
close(3) = 0
Existem espelhos do repositório glibc no github, por isso não foi difícil procurar por PROT_NONE ...
/* This implementation assumes (as does the corresponding implementation of _dl_unmap_segments, in dl-unmap-segments.h) that shared objects are always laid out with all segments contiguous (or with gaps between them small enough that it's preferable to reserve all whole pages inside the gaps with PROT_NONE mappings rather than permitting other use of those parts of the address space). */
/* _dl_map_segments ensures that any whole pages in gaps between segments are filled in with PROT_NONE mappings. So we can just unmap the whole range in one fell swoop. */
Java
OpenJDK ... uses PROT_NONE mappings to reserve uncommitted address space (which is then committed with mprotect calls, as needed).
A suposição natural é que ele deseja ter memória heap contígua por algum motivo.
Ele usa PROT_NONE para reservar espaço, até que ele realmente precise. O contexto original deste comentário é uma discussão sobre o overcommit da VM Linux: o uso de mapeamentos inacessíveis evita qualquer compromisso do kernel (até que o mapeamento seja necessário e disponibilizado), caso o kernel esteja configurado para strict mode de commit .
Se você está se perguntando por que precisa fazer essa reserva antecipadamente no contexto da JVM, considere que o código nativo está vinculado ao uso de JNI ou equivalente também pode estar usando mmap.