Chinaunix首页 | 论坛 | 博客
  • 博客访问: 187163
  • 博文数量: 38
  • 博客积分: 2010
  • 博客等级: 大尉
  • 技术积分: 424
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-19 12:41
文章分类

全部博文(38)

文章存档

2017年(2)

2016年(1)

2010年(8)

2009年(27)

我的朋友

分类: LINUX

2009-09-18 14:39:11

最近在做下载模块,遇到一个问题,需要把待下载的文件信息(文件名,文件类型等)带在文件中用于下载传输时解析。
具体详细设计不在这里讲了,我只把遇到的问题贴下来。有2种解决方案:
1. 写一个fileConf文件记录所有的信息,用mktail工具把下载文件和fileconf文件合并在一起。下载到目标机上之后把fileconf解析出来。
2. linux可执行文件和*.so文件是linux elf文件,需要编译的时候把appInfo编译到elf的section中。
 
以上2种方法各有好处,基于应用的考虑,我们选择更好的第二种但是这样就需要解析elf文件。elf文件的格式不在此垒述,网上有很多文章,我把我的实验过程记录了下来:
1. 编辑一个test.c文件:
 
#include

int helloworld()
{
    printf("hello world\n");
    return 0;
}
 
int main()
{
    helloworld();
    return 0;
}
 
2. make编译生成elf可执行文件test
 
3. #arm-linux-readelf -a test > a.txt
   a.txt中就可以直观的查看test这个elf文件的格式
 
4. #arm-linux-objdump -d test
   把elf文件用反汇编查看其运行的汇编代码,其中:
   0000837c :
    837c:       e92d4800        push    {fp, lr}
    8380:       e28db004        add     fp, sp, #4      ; 0x4
    8384:       e59f0014        ldr     r0, [pc, #20]   ; 83a0
    8388:       ebffffcb        bl      82bc <_init+0x48>
    838c:       e3a03000        mov     r3, #0  ; 0x0
    8390:       e1a00003        mov     r0, r3
    8394:       e24bd004        sub     sp, fp, #4      ; 0x4
    8398:       e8bd4800        pop     {fp, lr}
    839c:       e12fff1e        bx      lr
    83a0:       0000844c        .word   0x0000844c
 
就是int helloworld()的反汇编出来的指令。
我们看到83a0:这行, 这行提供了word存放的地址,也就是hello world这条打印语句存放的地址是44c
 
5. 用二进制编辑器打开test,查找到地址0x0000044c刚好存放的就是hello world打印语句。
 
因此我们可以通过这样的方式把信息存为一条打印语句,且也能被解析出来。
 
如果在文件中加入全局变量,如:char *pString="abcdefghi";
那么abcdefghi是存放在.rodata这个section里面的,也可以找到其对应的地址,并查看解析。
 
我们也可以自己添加一个section,方法在这篇博客中:
 
6. 如果你使用了strip对elf文件进行优化,那么你也可以从中找出那个打印信息所在的地址,只是反汇编看到的格式不同。
 
遗留问题:
1. 对应与动态连接库,我们就没有办法解析了,如果不加-fpic编译选项还可以,加上这个opt的话,.world的地址是不确定的
编译方式如下,读者可以自行研究:
#arm-linux-gcc -g -c -fPIC -o test.o test.c
#arm-linux-gcc -shared -o testso.so test.o
#arm-linux-readelf -a testso.so > a.txt
#arm-linux-objdump -d testso.so
 
2. 对于全局变量,这种方法也不可靠,在一个大型工程里面,全局变量存放的值可能被打断为几段存放,这个就是不可靠因素。
 
3. 通常在一个比较大的项目中,以上的方法也许对于不同的编译器结果往往不可预期,要多做实验。比如一个较大的elf文件,用反汇编的方法可能会找不到字符串地址,那么我们可以用下面这种方法:
。。。
#define PRINT1  "hello world11111"
#define PRINT2  "hello world22222"
#define PRINT3  "hello world33333"
typedef struct XUX_TAG {
    char szGroup[64];
    char szName[128];
    char szVersion[64];
}T_XUX;
。。。
const T_XUX a = {PRINT1, PRINT2, PRINT3};
。。。
 
这种方法的好处是,并不需要加一个section,只需要定义一个简单的结构体。并把资料预先初始化好。
#arm-linux-strip -s elfFile(优化elf文件)
#arm-linux-readelf -a elfFile > a.txt
#arm-linux-objdump -d elfFile > b.txt
 
从a.txt中我们可以根据section列表中的.rodata 找到可读资料的elf偏移地址和大小:
[14] .rodata           PROGBITS        000420d8 03a0d8 007638 00   A  0   0  4
 
再在elf文件中把0x7638这么大的rodata取出来查找你需要的资料。当然它的风险是增大了一点点搜索的难度而已。
 
 
PS: 以上方法效率低,不可靠,现在用一个新的方法:

把一个变量加入section的方法用例:

 

#define INFO1  "hello world11111"

#define INFO2  "hello world22222"

#define INFO3  "hello world33333"

 

typedef struct INFO_TAG {

    char szGroup[64];

    char szName[128];

    char szVersion[64];

}T_INFO;

 

T_INFO localmemory0 __attribute__ ((section("OURSECTION"))) = {INFO1, INFO2, INFO3};

 

readelf 可以通过offset找到elf文件中的位置。

阅读(2388) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~