代码 (.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
每个部分都有自己的属性,例如 READONLY
、CONTENTS
、ALLOC
、LOAD
、DATA
具有相同属性的部分可以映射在一起。
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.
ELF
有 SHF_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 都是只读的。所以他们可以放在同一个段。
我想了解 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
每个部分都有自己的属性,例如 READONLY
、CONTENTS
、ALLOC
、LOAD
、DATA
具有相同属性的部分可以映射在一起。
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.
ELF
有 SHF_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 都是只读的。所以他们可以放在同一个段。