1

Allocating memory needs system calls like mmap or brk. Since the BSS section is a continous memory just like heap, how is the BSS section allocated during program execution?

ASLR is disabled:

$ cat /proc/sys/kernel/randomize_va_space
0

Here is my program for testing:

#include <stdio.h>
#include <stdlib.h>

char buf[0x10] = {};

int main() {
    char *b = malloc(0); /* heap */
    char *a = buf; /* bss: Block Started by Symbol */
    printf("bss: 0x%lx\n", ((size_t)a>>12)<<12);
    printf("heap: 0x%lx\n", ((size_t)b>>12)<<12);
}

And using strace can not find any system call related to BSS address (0x555555558000):

$ strace ./test
execve("./test", ["./test"], 0x7fffffffe1e0 /* 45 vars */) = 0
brk(NULL)                               = 0x555555559000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffe020) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7fbb000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=75771, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 75771, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7fa8000
close(3)                                = 0
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
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0i8\235HZ\227\223\333\350s\360\352,\223\340."..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2216304, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2260560, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7c00000
mmap(0x7ffff7c28000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7ffff7c28000
mmap(0x7ffff7dbd000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7ffff7dbd000
mmap(0x7ffff7e15000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7ffff7e15000
mmap(0x7ffff7e1b000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7e1b000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7fa5000
arch_prctl(ARCH_SET_FS, 0x7ffff7fa5740) = 0
set_tid_address(0x7ffff7fa5a10)         = 157518
set_robust_list(0x7ffff7fa5a20, 24)     = 0
rseq(0x7ffff7fa60e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7ffff7e15000, 16384, PROT_READ) = 0
mprotect(0x555555557000, 4096, PROT_READ) = 0
mprotect(0x7ffff7ffb000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7ffff7fa8000, 75771)           = 0
getrandom("\xbd\x2c\xf0\xbd\x13\x19\x32\x9b", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x555555559000
brk(0x55555557a000)                     = 0x55555557a000
newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}, AT_EMPTY_PATH) = 0
write(1, "bss: 0x555555558000\n", 20bss: 0x555555558000
)   = 20
write(1, "heap: 0x555555559000\n", 21heap: 0x555555559000
)  = 21
exit_group(0)                           = ?
+++ exited with 0 +++
user13267
  • 6,871
  • 28
  • 80
  • 138
b3a1e
  • 21
  • 4
  • Two things: Empty initializer for arrays is a GCC extension, it's not allowed in standard C. Secondly, because you initialize the array it will not be put into the BSS segment but in the DATA segment. BSS is for *uninitialized* data. – Some programmer dude Mar 22 '23 at 11:34
  • 1
    This is done at user-space by the bootstrap code added by the compiler which runs before `main()` - see e.g. [STM32 startup.s and LinkerScript.ld] for bare metal example. Not sure where to find the ones used by Linux from top of my head. – Quimby Mar 22 '23 at 11:40
  • 2
    @Someprogrammerdude Zero-initialized static storage variables should go to .bss no matter if initialized explicitly or implicitly. As for what happens in case of `= {};` nobody knows... – Lundin Mar 22 '23 at 11:52
  • 1
    Dump the executable header, sections can be pre-allocated by the OS loader. – Richard Critten Mar 22 '23 at 11:55
  • @Lundin Well, as for explicitly zero-initialized, static, non-const variables, it depends on the compiler whether they go in `.bss` or `.data`. I think GCC started putting them into `.bss` by default in the GCC 3.3 series when the `-fno-zero-initialized-in-bss` option was added to put them back into `.data` like in the old days. – Ian Abbott Mar 22 '23 at 12:31
  • @IanAbbott The whole point of having a .bss in the first place is to speed up zero-initialization. Every system I've ever worked with does that, including various obscure embedded systems compilers. – Lundin Mar 22 '23 at 12:57
  • 1
    @Haris: Yes, environment variables. – Nate Eldredge Mar 22 '23 at 13:55
  • Closely related, near-duplicate: https://stackoverflow.com/questions/75342668/where-is-it-documented-that-global-array-in-c-compiled-by-gcc-is-initialized-l – Nate Eldredge Mar 22 '23 at 13:56
  • @Lundin I've worked on older systems that didn't put any explicitly initialized variables in `.bss`. Even these days, const variables seem to go in `.data` if zero-initialized (either expicitly or by resolving tentative definitions). Perhaps there ought to be a (write-protected) `.robss` section for such things! (I.e. write-protected (on systems with an memory protection unit) after the run-time loader has filled it with zeros.) – Ian Abbott Mar 22 '23 at 15:45

0 Answers0