Windows 强制执行 READ-ONLY .text 部分,即使因此被 ld 链接器禁用

Windows enforces READ-ONLY .text section, even thus disabled by the ld linker

在下面的玩具程序中,我在 .text 部分声明了一个变量并写入它,这给出了分段错误,因为 .text 部分被标记为只读:

Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>:     movl   [=11=]x2,0x40100a
End of assembler dump.
(gdb) stepi

Program received signal SIGSEGV, Segmentation fault.
0x00401000 in start ()
(gdb)

这是 objdump 输出:

test.exe:     file format pei-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000001f  00401000  00401000  00000200  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .idata        00000014  00402000  00402000  00000400  2**2
                  CONTENTS, ALLOC, LOAD, DATA

但是,link使用 --omagic 开关(禁用只读 .text 部分)会产生以下结果:

ld --omagic -o test.exe test.obj

test.exe:     file format pei-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000001f  00401000  00401000  000001d0  2**4
                  CONTENTS, ALLOC, LOAD, CODE
  1 .idata        00000014  00402000  00402000  000003d0  2**2
                  CONTENTS, ALLOC, LOAD, DATA

但是使用 GDB 进行调试会得到以下(奇怪的)结果:

Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>:     dec    %ebp
   0x00401001 <+1>:     pop    %edx
   0x00401002 <+2>:     nop
   0x00401003 <+3>:     add    %al,(%ebx)
   0x00401005 <+5>:     add    %al,(%eax)
   0x00401007 <+7>:     add    %al,(%eax,%eax,1)
End of assembler dump.
(gdb) stepi
0x00401001 in start ()
(gdb) stepi
0x00401002 in start ()
(gdb) stepi
0x00401003 in start ()
(gdb) stepi
0x00401005 in start ()
(gdb) stepi

Program received signal SIGSEGV, Segmentation fault.
0x00401005 in start ()
(gdb)

首先还是报了segmentation fault,但是汇编代码也改结构了?

如何 link .text 部分在 Windows 10 x64 上可写?

玩具程序:

    BITS 32

    section .text
    global _start
_start:
    mov [var], dword 2
var:    dd 0
    ret

--omagic 标志导致 GNU 链接器生成错误的 PECOFF 可执行文件。节必须在文件中对齐,最小文件对齐为 512 字节,但链接器将 .text 节放在文件偏移量 0x1d0 处。

不要使用 --omagic 标志,而是正常生成可执行文件,然后使用 objcopy 更改 header:

部分中的标志
ld -o test-tmp.exe test.obj
$(OBJCOPY) --set-section-flags .text=code,data,alloc,contents,load test-tmp.exe test.exe

出于某种原因,ld 完全更改了使用 --omagic 选项链接的 PE 可执行文件。

使用 cmp 实用程序的文件快速比较显示:

 137 177 222
 141   0 320
 142   6   5
 213   0 320
 214   2   1
 217 142 205
 218 154 353
 397   0 320
 398   2   1
 437   0 320
 438   4   3
 465   0 307

...

很多差异,尽管 ld 原则上应该只更改部分 header (.text) 的部分标志,即设置标志 IMAGE_SCN_MEM_WRITE.

使用 HxD 手动更改标志,即将偏移量 0x19F 处的字节设置为 0xE0 可解决问题...

运行程序的试验 varret 的顺序互换(否则程序崩溃):

Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>:     movl   [=11=]x2,0x40100b
   0x0040100a <+10>:    ret
End of assembler dump.
(gdb) stepi
0x0040100a in start ()
(gdb) disassemble
Dump of assembler code for function start:
   0x00401000 <+0>:     movl   [=11=]x2,0x40100b
=> 0x0040100a <+10>:    ret
End of assembler dump.
(gdb) x/wx var
0x40100b <var>: 0x00000002
(gdb)

我们看到一切都按预期进行。

我的结论是 ld 以某种方式生成了格式错误的 PE 可执行文件,我看到@RossRidge 对此有答案(ld 不遵守文件的节对齐)。