为什么栈段在树莓派上是可执行的?

4

我有一台运行Raspbian GNU/Linux 8 (Jessie)操作系统的树莓派3。

我写了这个简单的程序。我用gcc -o hello hello.c编译它。

#include <stdio.h>

void main(){
  printf("hello!\n");
}

从readelf输出看,一切似乎都没问题:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x0004cc 0x000104cc 0x000104cc 0x00008 0x00008 R   0x4
  PHDR           0x000034 0x00010034 0x00010034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x00010154 0x00010154 0x00019 0x00019 R   0x1
      [Requesting program interpreter: /lib/ld-linux-armhf.so.3]
  LOAD           0x000000 0x00010000 0x00010000 0x004d8 0x004d8 R E 0x10000
  LOAD           0x000f0c 0x00020f0c 0x00020f0c 0x0011c 0x00120 RW  0x10000
  DYNAMIC        0x000f18 0x00020f18 0x00020f18 0x000e8 0x000e8 RW  0x4
  NOTE           0x000170 0x00010170 0x00010170 0x00044 0x00044 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x000f0c 0x00020f0c 0x00020f0c 0x000f4 0x000f4 R   0x1

但是当我运行程序时,栈是可执行的:

0x7efdf000 0x7f000000 0x00000000 rwx [stack]

我尝试使用选项-z noexecstack进行编译,但是没有任何变化。

我还尝试下载带有以下代码的libarmmem.so版本:

#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits
#endif

但是没有任何变化。

为什么在树莓派上堆栈段可执行?

编辑:我添加了LD_DEBUG=files ./hello命令的输出。

     23110: 
     23110: file=/usr/lib/arm-linux-gnueabihf/libarmmem.so [0];  needed by ./hello [0]
     23110: file=/usr/lib/arm-linux-gnueabihf/libarmmem.so [0];  generating link map
     23110:   dynamic: 0x76f273fc  base: 0x76f13000   size: 0x00014524
     23110:     entry: 0x76f13568  phdr: 0x76f13034  phnum:          6
     23110: 
     23110: 
     23110: file=libc.so.6 [0];  needed by ./hello [0]
     23110: file=libc.so.6 [0];  generating link map
     23110:   dynamic: 0x76f0ef20  base: 0x76dd4000   size: 0x0013e550
     23110:     entry: 0x76dea840  phdr: 0x76dd4034  phnum:         10
     23110: 
     23110: 
     23110: calling init: /lib/arm-linux-gnueabihf/libc.so.6
     23110: 
     23110: 
     23110: calling init: /usr/lib/arm-linux-gnueabihf/libarmmem.so
     23110: 
     23110: 
     23110: initialize program: ./hello
     23110: 
     23110: 
     23110: transferring control: ./hello
     23110: 
hello!
     23110: 
     23110: calling fini: ./hello [0]
     23110: 
     23110: 
     23110: calling fini: /usr/lib/arm-linux-gnueabihf/libarmmem.so [0]
     23110:

添加更多信息: 我编辑了architecture.S文件,然后进行了编译,结果收到了以下反馈:

gcc -std=gnu99 -O2 -c -o trampoline.o trampoline.c
gcc -shared -o libarmmem.so architecture.o memcmp.o memcpymove.o memcpymove-a7.o memset.o trampoline.o
`architecture' referenced in section `.text' of trampoline.o: defined in discarded section `.note.GNU-stack' of architecture.o
`architecture' referenced in section `.text' of trampoline.o: defined in discarded section `.note.GNU-stack' of architecture.o
`architecture' referenced in section `.text' of trampoline.o: defined in discarded section `.note.GNU-stack' of architecture.o
`architecture' referenced in section `.text' of trampoline.o: defined in discarded section `.note.GNU-stack' of architecture.o
collect2: error: ld returned 1 exit status
Makefile:13: recipe for target 'libarmmem.so' failed
make: *** [libarmmem.so] Error 1

请原谅我的无知...你是如何得到这个结果的:0x7efdf000 0x7f000000 0x00000000 rwx [stack]。那是trace的一部分吗? - jww
@jww 不是只有 /proc/pidof hello/maps。 - Livio
1
请以 LD_DEBUG=files ./hello 的方式运行程序,并将输出添加到您的帖子中。有一些报告称 LD_PRELOAD 库会导致此问题。 - Florian Weimer
@FlorianWeimer 还要看一下这个 $cat /etc/ld.so.preload /usr/lib/arm-linux-gnueabihf/libarmmem.so - Livio
1个回答

3
很可能是 /usr/lib/arm-linux-gnueabihf/libarmmem.so 导致了这个问题。我找到了这个源文件: 它缺乏不可执行栈注释,因此当DSO预加载时,glibc保守地使堆栈可执行。其他源文件都有这个注释。
/* Prevent the stack from becoming executable */
#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits
#endif

所以你只需要将这段代码复制到architecture.S(文件末尾)并重新构建即可。

你可以使用以下命令验证是否是这个DSO导致的问题:eu-readelf -l /usr/lib/arm-linux-gnueabihf/libarmmem.so。它应该没有任何GNU_STACK程序头,或者最后一列被标记为RWEGNU_STACK程序头。


@Livio,抱歉看起来有些混乱。评论框不是发布编译器输出消息的好地方。请修改帖子以说明您所做的事情和结果如何。 - Florian Weimer
1
哦,抱歉,我没有注意到architecture.S在开头附近没有节指令。你需要将我发布的片段放在文件末尾,或者如果你将其留在开头,则在其后添加一个.data指令。 - Florian Weimer
应该只需添加 ASFLAGS += -Wa,--noexecstack 就足够了。编译器驱动程序会自动执行正确的操作,并使用适当的标志调用汇编程序。 - jww
哦,等等……trampoline.c在GCC编译器下会导致NX堆栈的丢失(如果它是一个真正的“trampoline”的话)。 libarmmem.so可能无法保留它们。 - jww
@jww,我不这么认为。这些不是在堆栈上的跳板。 - Florian Weimer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接