使用BFD操作ELF

使用BFD操作ELF


创建时间:2001-09-21
文章属性:原创
文章来源: http://www.xfocus.org/
文章提交: alert7  (sztcww_at_sina.com)

使用BFD操作ELF


作者:alert7 <mailto: [email protected] 
                     [email protected]
             >

主页:  http://www.xfocus.org
时间: 2001-9-21


★1. 前言 

BFD是Binary File Descriptor的简称。我们可以使用它来方便的操作应用程序,
可以在你不了解程序文件格式的情况下,读写ELF header,program header 
table,section header table还有各个section等等。当然也可以是其他的BFD
支持的object文件(比如说是COFF,a.out等等)。如果想让BFD支持新的文件格式的话,
只需把一个新的BFD的后端加到它的库里。

对每一个文件格式来说,BFD都分两个部分:一个前端(front end)和一个后端(back ends)。

.BFD的前端给用户提供接口。它管理内存和规范数据结构。前端也决定了哪个后端被使用
  和什么时候后端的例程被调用。
.BFD的前端维持了它的语言形式。后端也可能是它们自己使用的信息。


★2. 如何使用BFD

为了使用BFD,需要包括'bfd.h'并且连接的时候需要和静态库'libbfd.a'或者
动态库'libbfd.so'一起连接。

下面看一个简单的例子:

[alert7@redhat62 elf]$ cat test1.c

/*test1.c only for alert7 test*/
#include <stdio.h>
#include <stdlib.h>
#include <bfd.h>
#include <strings.h>
#include <linux/elf.h>

#define nonfatal(s) {perror(s); return;}
#define fatal(s) {perror(s); exit(-1);}
#define bfd_nonfatal(s) {bfd_perror(s); return;}
#define bfd_fatal(s) {bfd_perror(s); exit(-1);}

main(int argc, char *argv[])  
{
      bfd *ibfd;
    char *filename;
    char **matching;

    if (argc<2) exit(-1);
    filename = argv[1];
    
    bfd_init();

      ibfd = bfd_openr(filename, NULL);
      if (ibfd == NULL)
      {
          bfd_nonfatal("openr");
    }

      if (bfd_check_format_matches(ibfd, bfd_object, &matching))
      {
    int sections_count=bfd_count_sections(ibfd);
    printf("%s section count %d\n",argv[1],sections_count);

    } else {
              bfd_fatal("format_matches");
    }
bfd_close(ibfd);
}

[alert7@redhat62 elf]$ gcc -c test1.c
[alert7@redhat62 elf]$ gcc -o test1 test1.o -lbfd -liberty
[alert7@redhat62 elf]$ ./test1 /lib/ld-linux.so.2
/lib/ld-linux.so.2 section count 19


★3. BFD一些基本的函数

bfd_init(void)
初始化BFD。


bfd *bfd_openr(CONST char *filename, CONST char *target);
以目标target打开filename文件。返回指向bfd的指针。target由
Calls_bfd_find_target函数来解析。
如果错误发生,返回NULL,那些错误可能是bfd_error_no_memory, 
bfd_error_invalid_target or system_call error. 


boolean bfd_set_default_target (const char *name);
设置默认的target向量,用来查找一个匹配的BFD。


boolean bfd_check_format(bfd *abfd, bfd_format format);
确定是否是BFD支持的文件格式(例如是bfd_object, bfd_archive或者bfd_core之一). 
假如在该调用之前已经设置了特别的target,那么仅仅和target相关联的格式被检查。
假如target没有被设置或者被设置为默认的,所有已知的后端将被询问是否跟他们匹配。


函数成功返回true,否则false,以下是一些错误代码:
bfd_error_invalid_operation - 假如格式不是bfd_object, bfd_archive和
        bfd_core之一。
bfd_error_system_call - 假如一个错误发生在读期间 - 甚至一些文件错误促发
        bfd_error_system_calls.
file_not_recognised - 没有后端可以认识该文件格式。
bfd_error_file_ambiguously_recognized - 超过一个的后端能认识该文件格式。


boolean bfd_check_format_matches(bfd *abfd, bfd_format format, 
                    char ***matching);
类似bfd_check_format, 除了当false返回时,bfd_errno被设置为
bfd_error_file_ambiguously_recognized.在这种情况下,假如matching不等于NULL,
它被填充为以NULL为结尾的一个已经注册了的所有的格式名称的列表。然后用户可以从中
选择一个格式再试一次。
当不需要时候,用户应该free指向列表指针的内存。


bfd *bfd_openw(CONST char *filename, CONST char *target);
使用格式的target,为文件filename创建一个BFD。返回指向BFD的指针。
错误可能是bfd_error_system_call, bfd_error_no_memory, bfd_error_invalid_target. 


boolean bfd_close(bfd *abfd);
Close BFD.假如以写打开的BFD,未完成的操作会被完成,然后文件才会被关闭。假如创建文件是
可执行的,将会调用chmod使它一致。所有BFD相关的内存被释放。
所有OK的话返回true,否则false.


bfd_get_symtab_upper_bound
返回所有标号需要的字节大小(也包括一个NULL指针)。假如没有标号,将返回为0。
错误返回-1。


bfd_canonicalize_symtab
从BFD中读标号。返回标号指针的个数(不包括NULL)。


boolean bfd_get_section_contents
   (bfd *abfd, asection *section, PTR location,
    file_ptr offset, bfd_size_type count);
把BFD abfd section中的数据读入内存location开始地址。数据从section的偏移量offset
开始读,count为要读数据的字节数。


boolean bfd_set_section_contents
   (bfd *abfd,
    asection *section,
    PTR data,
    file_ptr offset,
    bfd_size_type count);
把内存中data地址开始的数据设置为abfd BFD的节section的内容,数据从section的偏移量
offset开始写,count为要写的数据字节数。


void bfd_map_over_sections(bfd *abfd,
    void (*func)(bfd *abfd,
    asection *sect,
    PTR obj),
    PTR obj);
为BFD abfd相关的每个section调用私有的函数func,该函数将会被如下调用:
       func(abfd, the_section, obj);
该函数看起来就象执行了如下代码:
          section *p;
          for (p = abfd->sections; p != NULL; p = p->next)
             func(abfd, p, ...)


更多的bfd的库函数说明可以在以下找到:
http://www.gnu.org/manual/bfd-2.9.1/html_node/bfd_toc.html


★4. 通过BFD读符号表中符号

/*test2.c only for alert7 test*/
#include <stdio.h>
#include <stdlib.h>
#include <bfd.h>
#include <strings.h>
#include <linux/elf.h>

#define nonfatal(s) {perror(s); return;}
#define fatal(s) {perror(s); exit(-1);}
#define bfd_nonfatal(s) {bfd_perror(s); return;}
#define bfd_fatal(s) {bfd_perror(s); exit(-1);}

main(int argc, char *argv[])
{
        bfd *ibfd;
        char *filename;
        char **matching;

        if (argc<2) exit(-1);
        filename = argv[1];

        bfd_init();

        ibfd = bfd_openr(filename, NULL);
        if (ibfd == NULL)
        {
                bfd_nonfatal("openr");
        }

        if (bfd_check_format_matches(ibfd, bfd_object, &matching))
        {

        long storage_needed;
         asymbol **symbol_table;
         long number_of_symbols;
         long i;
         symbol_info symbolinfo ;

         storage_needed = bfd_get_symtab_upper_bound (ibfd);

         if (storage_needed < 0)
           bfd_nonfatal("bfd_get_symtab_upper_bound");

         if (storage_needed == 0) {
            return ;
         }
         symbol_table = (asymbol **) xmalloc (storage_needed);
        number_of_symbols =
            bfd_canonicalize_symtab (ibfd, symbol_table);

         if (number_of_symbols < 0)
           bfd_nonfatal("bfd_canonicalize_symtab");

        printf("Scanning %i symbols\n", number_of_symbols);
        for(i=0;i<number_of_symbols;i++)
                {
                if (symbol_table[i]->section==NULL) continue;
                printf("Section %s  ",symbol_table[i]->section->name);
                bfd_symbol_info(symbol_table[i],&symbolinfo);
                printf("Symbol \"%s\"  value 0x%x\n",
                         symbolinfo.name, symbolinfo.value);
                }


        } else {
                bfd_fatal("format_matches");
        }
bfd_close(ibfd);
}
/* EOF */


以上小程序打印符号的name和value还有相关的section.

[alert7@redhat62 elf]$ gcc -c test2.c
[alert7@redhat62 elf]$ gcc -o test2 test2.o -lbfd -liberty
[alert7@redhat62 elf]$ ./test2 helo
Scanning 75 symbols
Section .interp  Symbol ""  value 0x80480f4
Section .note.ABI-tag  Symbol ""  value 0x8048108
Section .hash  Symbol ""  value 0x8048128
......
Section *ABS*  Symbol "initfini.c"  value 0x0
Section .text  Symbol "gcc2_compiled."  value 0x804841c
Section *ABS*  Symbol "helo.c"  value 0x0
......
Section *ABS*  Symbol "_end"  value 0x8049540
Section .rodata  Symbol "_IO_stdin_used"  value 0x804843c
Section .data  Symbol "__data_start"  value 0x8049448
Section *UND*  Symbol "__gmon_start__"  value 0x0


再看看objdump输出什么
[alert7@redhat62 elf]$ objdump -t helo

helo:     file format elf32-i386

SYMBOL TABLE:
080480f4 l    d  .interp        00000000
08048108 l    d  .note.ABI-tag  00000000
08048128 l    d  .hash  00000000
......
00000000 l    df *ABS*  00000000              initfini.c
08048344 l       .text  00000000              gcc2_compiled.
00000000 l    df *ABS*  00000000              init.c
......
08049540 g     O *ABS*  00000000              _end
0804843c g     O .rodata        00000004              _IO_stdin_used
08049448 g       .data  00000000              __data_start
00000000  w      *UND*  00000000              __gmon_start__

(输出太多了,占太多大家宝贵的硬盘空间不好,所以只保留了以上输出)

其实objdump也是使用BFD的
[alert7@redhat62 elf]$ ldd /usr/bin/objdump
        libopcodes-2.9.5.0.22.so => /usr/lib/libopcodes-2.9.5.0.22.so (0x4001c000)
        libbfd-2.9.5.0.22.so => /usr/lib/libbfd-2.9.5.0.22.so (0x40032000)
        libdl.so.2 => /lib/libdl.so.2 (0x40077000)
        libc.so.6 => /lib/libc.so.6 (0x4007b000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)


★5. 写标号

演示如何进行写操作

/*test3.c for just show wirte operation*/
#include <stdio.h>
#include <bfd.h>
       main()
       {
         bfd *abfd;
         asymbol *ptrs[2];
         asymbol *new;

         abfd = bfd_openw("foo",NULL);
     if (abfd==NULL) exit(0);
         bfd_set_format(abfd, bfd_object);
         new = bfd_make_empty_symbol(abfd);
         new->name = "dummy_symbol";
         new->section = bfd_make_section_old_way(abfd, ".text");
         new->flags = BSF_GLOBAL;
         new->value = 0x12345;

         ptrs[0] = new;
         ptrs[1] = (asymbol *)0;

         bfd_set_symtab(abfd, ptrs, 1);
         bfd_close(abfd);
       }

[alert7@redhat62 elf]$ gcc -c test3.c
[alert7@redhat62 elf]$ gcc -o test3 test3.o -lbfd -liberty
[alert7@redhat62 elf]$ ./test3
[alert7@redhat62 elf]$ ls -ls foo
   4 -rw-rw-r--    1 alert7   alert7        350 Sep 21 09:56 foo
[alert7@redhat62 elf]$ file foo
foo: ELF 32-bit LSB relocatable, no machine, version 1, not stripped
[alert7@redhat62 elf]$ nm  foo
00012345 T dummy_symbol


★ 后记

本文只用来抛砖引玉,如果想用BFD写个实际有用的程序(比如实现绿盟月刊第14期上有篇
wangdb写的《如何修改动态库符号表》上的功能),需要自己好好研究研究那些BFD的库函数。
No pain,No gain...
不过虽然BFD功能很强大,但毕竟是别人封装过的,所以或许在自己琢磨那些库函数的时候,
就已经头晕了,而且不灵活,很不爽。我就是这样啦:(
封装了的库函数会屏蔽底层操作的细节,追求技术的你还是不要用它吧,但这就需要一些
ELF的知识。

你可能感兴趣的:(使用BFD操作ELF)