Code (.text) not execute-only? .rodata is executable?

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:

  • Bit 1 (R/W): 0 indicates read-only, 1 indicates read-write.
  • Bit 63 (XD): 0 indicates executable, 1 indicates non-executable.

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.