I am trying to understand how ELF segments are memory mapped. I noticed that various sections are mapped to the same ELF segment. For example, .rodata is mapped to the same segment as .text.
Why is this the case? Why not map .rodata to a separate read-only and not executable segment?
Also, what does it entail to map the .text section to an "execute ONLY" segment (not readable)? Are there any kernel/HW limitations that may hinder this?
EDIT: I may as well add that I am using the GNU linker, if that makes a difference.
(. rodata) => segment for constant data, if the project has the write constant data to . rodata section checkbox enabled in target settings. By default, zero-initialized data is put into the .bss section by the compiler (zeroed by INIT code).
The code segment in memory is typically read-only and has a fixed size, so on embedded systems it can usually be placed in read-only memory (ROM), without the need for loading. If the code segment is not read-only, then the particular architecture allows self-modifying code.
Also, the text segment is often read-only, to prevent a program from accidentally modifying its instructions.
Gathered from comments above
It is not possible on several computer architectures, including x86-64, to mark memory as executable but not readable.
While x86 16- and 32-bit did allow segmentation in legacy modes, and memory segments could in theory be used to mark memory as executable-only, the benefits of a flat address space were so great that x86-64 now mostly ignores its segments registers:
3.2.4 Segmentation in IA-32e Mode
In IA-32e mode of Intel 64 architecture, the effects of segmentation depend on whether the processor is running in compatibility mode or 64-bit mode. In compatibility mode, segmentation functions just as it does using legacy 16-bit or 32-bit protected mode semantics.
In 64-bit mode, segmentation is generally (but not completely) disabled, creating a flat 64-bit linear-address space. The processor treats the segment base of CS, DS, ES, SS as zero, creating a linear address that is equal to the effective address. The FS and GS segments are exceptions. These segment registers (which hold the segment base) can be used as additional base registers in linear address calculations. They facilitate addressing local data and certain operating system data structures.
Note that the processor does not perform segment limit checks at runtime in 64-bit mode.
Kernels thus simply set their segments to cover the entire address space and do not rely on segmentation to achieve memory protection.
What they do use is the page table's attributes. Every page that exists in a process's memory map has a page table entry governing access to it. An overview of their format can be seen here, but most crucially there are two bits that control what type of access is permitted:
It is not possible to indicate an executable-noread-nowrite combination with these flags. If the page is at all present in the memory map, it must be readable.
A solution is fast approaching in Intel's latest microarchitecture, Skylake, which will allow execute-only memory: It is the feature baptized MPK (memory protection keys), support for which landed in Linux kernel 4.6 just recently released. The keys occupy the four bits 62:59 of the page table entry, and areas of memory can be tagged with a key indicating execute-noread-nowrite access.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With