代码 (.text) 不是只执行? .rodata 是可执行的吗?

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

我想了解 ELF 段是如何进行内存映射的。我注意到各个部分都映射到同一个 ELF 段。例如,.rodata 映射到与 .text 相同的段。

为什么会这样?为什么不将 .rodata 映射到一个单独的只读且不可执行的段?

此外,将 .text 部分映射到 "execute ONLY" 段(不可读)需要什么?是否有任何 kernel/HW 限制可能会阻碍这一点?

编辑: 我还可以补充一点,我正在使用 GNU 链接器,如果这会有所不同的话。

% objdump -h /bin/ls

/bin/ls:     file format elf64-x86-64

Sections:     
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     000000c0  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       00000cd8  0000000000400358  0000000000400358  00000358  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       000005dc  0000000000401030  0000000000401030  00001030  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  00000112  000000000040160c  000000000040160c  0000160c  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000070  0000000000401720  0000000000401720  00001720  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     000000a8  0000000000401790  0000000000401790  00001790  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000a98  0000000000401838  0000000000401838  00001838  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         0000001a  00000000004022d0  00000000004022d0  000022d0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000720  00000000004022f0  00000000004022f0  000022f0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .text         0001112a  0000000000402a10  0000000000402a10  00002a10  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .fini         00000009  0000000000413b3c  0000000000413b3c  00013b3c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .rodata       00006754  0000000000413b80  0000000000413b80  00013b80  2**6
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .eh_frame_hdr 0000081c  000000000041a2d4  000000000041a2d4  0001a2d4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame     00002c7c  000000000041aaf0  000000000041aaf0  0001aaf0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .init_array   00000008  000000000061de00  000000000061de00  0001de00  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 18 .fini_array   00000008  000000000061de08  000000000061de08  0001de08  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .jcr          00000008  000000000061de10  000000000061de10  0001de10  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .dynamic      000001e0  000000000061de18  000000000061de18  0001de18  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got          00000008  000000000061dff8  000000000061dff8  0001dff8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .got.plt      000003a0  000000000061e000  000000000061e000  0001e000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .data         000002a0  000000000061e3c0  000000000061e3c0  0001e3c0  2**6
                  CONTENTS, ALLOC, LOAD, DATA
 24 .bss          00000e08  000000000061e680  000000000061e680  0001e660  2**6
                  ALLOC

每个部分都有自己的属性,例如 READONLYCONTENTSALLOCLOADDATA

具有相同属性的部分可以映射在一起。

根据elf format

   sh_flags  Sections support one-bit flags that describe miscellaneous
             attributes.  If a flag bit is set in sh_flags, the
             attribute is "on" for the section.  Otherwise, the
             attribute is "off" or does not apply.  Undefined attributes
             are set to zero.

             SHF_WRITE      This section contains data that should be
                            writable during process execution.

             SHF_ALLOC      This section occupies memory during process
                            execution.  Some control sections do not
                            reside in the memory image of an object
                            file.  This attribute is off for those
                            sections.

             SHF_EXECINSTR  This section contains executable machine
                            instructions.

             SHF_MASKPROC   All bits included in this mask are reserved
                            for processor-specific semantics.

ELFSHF_EXECINSTR 节属性,所以是编译器或者 link 没有设置属性。

根据以上评论收集

在包括 x86-64 在内的多种计算机体系结构上,无法将内存标记为可执行table但不可读。

虽然 x86 16 位和 32 位确实允许在传统模式下进行分段,并且理论上可以使用内存段将内存标记为 executable-only,但平面地址的好处 space 太棒了,x86-64 现在 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.

内核因此简单地将它们的段设置为覆盖整个地址space,而不依赖于段来实现内存保护。

他们使用的是页面 table 的属性。存在于进程内存映射中的每个页面都有一个页面 table 条目来管理对它的访问。可以看到它们格式的概述 here,但最关键的是有两个位控制允许的访问类型:

  • Bit 1 (R/W): 0表示只读,1表示读写。
  • Bit 63 (XD): 0表示executable, 1表示非executable.

无法使用这些标志指示 executable-noread-nowrite 组合。如果页面完全存在于内存映射中,则它必须是可读的。

Intel 最新的微架构 Skylake 中正在快速接近一个解决方案,允许只执行内存:它是洗礼 MPK (memory protection keys) 的功能,支持为此登陆了最近发布的 Linux 内核 4.6。这些键占用页面 table 条目的四位 62:59,内存区域可以用表示执行-不读-现在访问的键标记。

section和segment是两个不同的概念,程序加载使用segment,甚至可以strip section table。一个段可以包含多个部分。 .rodata 和 .text 都是只读的。所以他们可以放在同一个段。