Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1685084
  • 博文数量: 782
  • 博客积分: 2455
  • 博客等级: 大尉
  • 技术积分: 4140
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-06 21:37
个人简介

Linux ,c/c++, web,前端,php,js

文章分类

全部博文(782)

文章存档

2015年(8)

2014年(28)

2013年(110)

2012年(307)

2011年(329)

分类:

2011-10-04 12:55:17

 周末无事,闲逛bbs,看到这样一个问题:
 

什么情况下,下面这个foo()  函数会返回一个 "Aello  world" 的指针, 而不会运行报错 
  1. char *foo()
  2. {
  3.     char* p = (char*)"Hello world";

  4.     p[0] = 'A';

  5.     return p;
  6. }

(运行环境: linux ) 
很多公司的笔试题里,都认为这段代码是肯定执行出错的。不过某些时候,还是可以继 
续运行的。

========================================================
基于我对ELF的理解,稍稍研究了一下,结果如下:

这个返回出错是因为"Hello world"会被放到.rodata里面。 
kinwin@ustc-king:/tmp$ cat tt.c 
char * foo(){ 
    char *a = (char *)"Hello  world\n"; 
    a[0] = 'A'; 
    return a; 


kinwin@ustc-king:/tmp$ cc -c tt.c 
kinwin@ustc-king:/tmp$ objdump -d tt.o 

tt.o:     file format elf32-i386 


Disassembly of section .text: 

00000000 
   0:   55                      push   p 
   1:   89 e5                   mov    %esp,p 
   3:   83 ec 10                sub    $0x10,%esp 
   6:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(p) 
   d:   8b 45 fc                mov    -0x4(p),x 
  10:   c6 00 41                movb   $0x41,(x) 
  13:   8b 45 fc                mov    -0x4(p),x 
  16:   c9                      leave   
  17:   c3                      ret    
这里的"Hello World"会生成一个重定位表项 
 kinwin@ustc-king:/tmp$ readelf -r tt.o 

Relocation section '.rel.text' at offset 0x31c contains 1 entries: 
 Offset     Info    Type            Sym.Value  Sym. Name 
00000009  00000501 R_386_32          00000000   .rodata 
链接的时候把这个.rodata中的字符串地址填入那堆0的地方。 

产生segment fault的原因就是指针指的是.rodata中的内容,而.rodata的段属性是 
ALLOC,不带WRITE和EXECUTE,这样在load的时候生成的vm_area_struct也是只读属性的 
,写其中的内容自然会在vm_area_struct那里被捕获,然后被sigsegv了。 

要让它可写也是Ok的,最根本的就是让它写的时候对应地址的vm_area_struct属性是可 
写的,pte层面应该都是可写的。就有几种办法了 
1. loader载入elf文件是根据elf的program header来生成对应的vm_area_struct的,其 
中默认是两个,一个可读可执行,一个可读可写,.rodata落在了第一块。 
因此,法一, program header table的映射关系让.rodata这个section映射到  
program header的可读可写块即可 

或者,法二:干脆在链接的时候让各目标文件的.rodata连接到.data这个section来(任 
一program header中映射的可读可写区域),而.data load的时候是可读可写的,如: 
链接脚本如此写: 
 .data           : 
    *(.rodata) 
    *(.data .data.* .gnu.linkonce.d.*) 
kinwin@ustc-king:/tmp$ gcc test.c -Wl,-T,ld.ver -o test.out 
kinwin@ustc-king:/tmp$ ./test.out 
Aello  world 

法三: 提示gcc在生成目标文件的时候不放入.rodata,而是放入.data,不知道gcc有无 
这样的选项 

法四:以上都为修改elf文件输出,也可以在运行时写只读区域之前,把对应的区域用 
mprotect把权限改了
阅读(377) | 评论(0) | 转发(0) |
0

上一篇:time命令含义

下一篇:用户管理环境文件

给主人留下些什么吧!~~