The right picture is more correct (but in practice, things are more complex), at least for Linux (and probably for other usual OSes, including MacOSX, Windows, other Unixes, Android, ...). The left picture is more relevant for single-address space operating systems (practically uncommon in 2019).
I recommend reading Operating Systems: Three Easy Pieces at first. The notion of virtual address space, and of processes, is tricky (but well explained there). ASLR is making things more complex.
Then, on Linux, read about proc(5) and use the /proc/
pseudo-file systems. Run the cat /proc/$$/maps
and cat /proc/self/maps
commands in a terminal, and understand their output. Do the same (and use also pmap(1)) on a multi-threaded process: every thread has its own call stack. Read some pthread tutorial for more.
In practice, with dynamic linking and shared objects, a virtual address space has "many" segments, since most shared libraries bring both their data and their text segment. For details on Linux, read Drepper's paper How to write shared libraries.
On my Linux/Debian, cat /proc/self/maps
displays the virtual address space of the process running that very cat
command. It shows:
559d55f71000-559d55f73000 r--p 00000000 08:01 5111854 /bin/cat
559d55f73000-559d55f78000 r-xp 00002000 08:01 5111854 /bin/cat
559d55f78000-559d55f7a000 r--p 00007000 08:01 5111854 /bin/cat
559d55f7b000-559d55f7c000 r--p 00009000 08:01 5111854 /bin/cat
559d55f7c000-559d55f7d000 rw-p 0000a000 08:01 5111854 /bin/cat
559d564a5000-559d564c6000 rw-p 00000000 00:00 0 [heap]
7fec9a3df000-7fec9a411000 r--p 00000000 08:01 11295313 /usr/lib/locale/C.UTF-8/LC_CTYPE
7fec9a411000-7fec9a584000 r--p 00000000 08:01 11295312 /usr/lib/locale/C.UTF-8/LC_COLLATE
7fec9a584000-7fec9ae03000 r--p 00000000 08:01 11272726 /usr/lib/locale/locale-archive
7fec9ae03000-7fec9ae25000 r--p 00000000 08:01 1710561 /lib/x86_64-linux-gnu/libc-2.28.so
7fec9ae25000-7fec9af6d000 r-xp 00022000 08:01 1710561 /lib/x86_64-linux-gnu/libc-2.28.so
7fec9af6d000-7fec9afb9000 r--p 0016a000 08:01 1710561 /lib/x86_64-linux-gnu/libc-2.28.so
7fec9afb9000-7fec9afba000 ---p 001b6000 08:01 1710561 /lib/x86_64-linux-gnu/libc-2.28.so
7fec9afba000-7fec9afbe000 r--p 001b6000 08:01 1710561 /lib/x86_64-linux-gnu/libc-2.28.so
7fec9afbe000-7fec9afc0000 rw-p 001ba000 08:01 1710561 /lib/x86_64-linux-gnu/libc-2.28.so
7fec9afc0000-7fec9afc4000 rw-p 00000000 00:00 0
7fec9afc4000-7fec9afc6000 rw-p 00000000 00:00 0
7fec9afd0000-7fec9aff2000 rw-p 00000000 00:00 0
7fec9aff2000-7fec9aff3000 r--p 00000000 08:01 11302810 /usr/lib/locale/C.UTF-8/LC_NUMERIC
7fec9aff3000-7fec9aff4000 r--p 00000000 08:01 11303137 /usr/lib/locale/C.UTF-8/LC_TIME
7fec9aff4000-7fec9aff5000 r--p 00000000 08:01 11302043 /usr/lib/locale/C.UTF-8/LC_MONETARY
7fec9aff5000-7fec9aff6000 r--p 00000000 08:01 11300270 /usr/lib/locale/C.UTF-8/LC_MESSAGES/SYS_LC_MESSAGES
7fec9aff6000-7fec9aff7000 r--p 00000000 08:01 11302816 /usr/lib/locale/C.UTF-8/LC_PAPER
7fec9aff7000-7fec9aff8000 r--p 00000000 08:01 11302045 /usr/lib/locale/C.UTF-8/LC_NAME
7fec9aff8000-7fec9aff9000 r--p 00000000 08:01 11295311 /usr/lib/locale/C.UTF-8/LC_ADDRESS
7fec9aff9000-7fec9affa000 r--p 00000000 08:01 11302824 /usr/lib/locale/C.UTF-8/LC_TELEPHONE
7fec9affa000-7fec9affb000 r--p 00000000 08:01 11295316 /usr/lib/locale/C.UTF-8/LC_MEASUREMENT
7fec9affb000-7fec9b002000 r--s 00000000 08:01 11324488 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
7fec9b002000-7fec9b003000 r--p 00000000 08:01 11295314 /usr/lib/locale/C.UTF-8/LC_IDENTIFICATION
7fec9b003000-7fec9b004000 r--p 00000000 08:01 1703945 /lib/x86_64-linux-gnu/ld-2.28.so
7fec9b004000-7fec9b022000 r-xp 00001000 08:01 1703945 /lib/x86_64-linux-gnu/ld-2.28.so
7fec9b022000-7fec9b02a000 r--p 0001f000 08:01 1703945 /lib/x86_64-linux-gnu/ld-2.28.so
7fec9b02a000-7fec9b02b000 r--p 00026000 08:01 1703945 /lib/x86_64-linux-gnu/ld-2.28.so
7fec9b02b000-7fec9b02c000 rw-p 00027000 08:01 1703945 /lib/x86_64-linux-gnu/ld-2.28.so
7fec9b02c000-7fec9b02d000 rw-p 00000000 00:00 0
7ffc0365b000-7ffc0367c000 rw-p 00000000 00:00 0 [stack]
7ffc03686000-7ffc03689000 r--p 00000000 00:00 0 [vvar]
7ffc03689000-7ffc0368b000 r-xp 00000000 00:00 0 [vdso]
(I removed some spaces)
So even when running a program as simple as /bin/cat
, the virtual address space inside the process is in practice much more complex than what your right picture suggests.
On Linux, shared libraries can be loaded (as plugins) at runtime with dlopen(3), and my manydl.c program shows that you can generate and load many hundred thousands of plugins in the same process.