The section headers contain a lot of extra information that is needed at build time but isn't needed at runtime, so the existence of program headers is an optimization that allows the shipped binaries on disk to be smaller and the runtime linker to be less complicated. (You'll note that binaries contain something like 30 different sections, with different names and attributes and tiny little 4/8/16 byte alignments, but those got merged into four page-aligned PT_LOAD segments that are only separate because they need to be mmap'd with different permissions. If you're building with -ffunction-sections to let the linker garbage collect dead code, you'll end up with thousands of sections but still only four PT_LOAD segments.)
Section Headers vs. Program Headers
The section headers contain a lot of extra information that is needed at build time but isn't needed at runtime, so the existence of program headers is an optimization that allows the shipped binaries on disk to be smaller and the runtime linker to be less complicated. (You'll note that binaries contain something like 30 different sections, with different names and attributes and tiny little 4/8/16 byte alignments, but those got merged into four page-aligned PT_LOAD segments that are only separate because they need to be mmap'd with different permissions. If you're building with -ffunction-sections to let the linker garbage collect dead code, you'll end up with thousands of sections but still only four PT_LOAD segments.)