This is not limited to build programs, as the executable format for your OS is laid out, and most OSs have decided to have a fairly extensive format for executable files, dividing the various parts of the program into sections ("segments").
Separating an executable file in different sections has several advantages, for example. the ones you mention:
.bss: stores information about the memory that should be reset to zero when the program starts. Memory that needs to be cleared is common, and the OS usually has special services for transferring cleared memory, and if you manage to allocate a global array of 1 MB, you do not need to insert 1 MB of 0 into the executable file - you can simply encode this information is in the .bss section, and the OS will allocate it 1Mb when the program starts.
.data: This is all your data that was initialized with something other than zero when the program started.
.text: this is the actual code
There may be many other sections, for example. special sections containing boot code that must be run to initialize the program, but may be discarded after its launch, or sections containing debugging information (which should not be loaded into memory if you do not run the program in the debugger). Another common section is a read-only data section:
.rodata: contains data that is not writable, for example. all lines or constant data in your program.
In addition, processors can apply memory protection, such as read / write / executable memory. Separate partitions make this memory protection easy to apply. For instance. the code should be executable, but having an executable file might be a bad idea. Read-only sections can also be more easily shared between other processes, sections of code and readonly memory can be shared across multiple instances of the program. If parts of the text section need to be changed, they can simply be discarded because they are already in the executable file itself, while the / bss data sections cannot be replaced with a special swap area.
source share