OBJ文件格式分析工具: objdump, nm,ar -- 转帖

来源:互联网 发布:沪江日语软件 编辑:程序博客网 时间:2024/05/09 03:15
AlphaJay的博客
AlphaJay的主页 | TA的博客列表 | RSS | 发送留言 | 关注此人

OBJ文件格式分析工具: objdump, nm,ar

1人收藏此文章, 收藏此文章 发表于7个月前 , 已有951次阅读 共0个评论 1人收藏此文章

首先简要阐述关于gcc、glibc和 binutils模块之间的关系

一、关于gcc、glibc和binutils模块之间的关系

1、gcc(gnu collect compiler)是一组编译工具的总称。它主要完成的工作任务是“预处理”和“编译”,以及提供了与编译器紧密相关的运行库的支持,如 libgcc_s.so、libstdc++.so等。

2、binutils提供了一系列用来创建、管理和维护二进制目标文件的工具程序,如汇编(as)、连接(ld)、静态库归档(ar)、反汇编 (objdump)elf结构分析工具(readelf)无效调试信息和符号的工具(strip)等。通常,binutils与gcc是紧密相集成 的,没有binutils的话,gcc是不能正常工作的

3、glibc是gnu发布的libc库,也即c运行库。glibc是linux系统中最底层的api(应用程序开发接口),几乎其它任何的运行库 都会倚赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现,主要的如下:
(1)string,字符串处理
(2)signal,信号处理
(3)dlfcn,管理共享库的动态加载
(4)direct,文件目录操作
(5)elf,共享库的动态加载器,也即interpreter
(6)iconv,不同字符集的编码转换
(7)inet,socket接口的实现
(8)intl,国际化,也即gettext的实现
(9)io
(10)linuxthreads
(11)locale,本地化
(12)login,虚拟终端设备的管理,及系统的安全访问
(13)malloc,动态内存的分配与管理
(14)nis

(15)stdlib,其它基本功能
 

二、redhat9上升级gcc

1、升级这些库时,最好不要覆盖系统中缺省的;因为这些库,尤其是glibc库,是系统中最核心的共享库和工具,如果盲目覆盖,很可能导致整个系统 瘫痪,因为一般更新glibc库时,其它所有以来libc库的共享库都需要重新被编译一遍。因此,为了调试某个程序进入glibc时,最好把glibc安 装到/usr/local/lib下。

2、首先编译glibc库。注意最好令建立一个glibc-build的目录,configure时加上--enable-add- ons=linuxthreads选项。make install安装到/usr/local下。

3、修改gcc的spec文件(/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs),更改ld- linux.so.2为/usr/local/lib下的新的共享库装载器。

4、编译binutils库,此时被编译出的程序会连接到/usr/local/lib下的新的libc库。注意,在configure前,需要设 置ld缺省连接的路径(LIBRARY_PATH=/usr/local/lib:/lib:/usr/lib),否则binutils会 configure出错,找不到libc中的一些符号。具体步骤如下:
(1)export LIBRARY_PATH=/usr/local/lib:/lib:/usr/lib
(2)mkdir binutils-build && cd binutils-build
(3)../binutils-2.13.90.0.18/configure
(4)make
(5)make -C ld clean
(6)make -C ld LIB_PATH=/usr/lib:/lib:/usr/local/bin(设置编译后的ld的缺省库搜索路径,后面的比前面的优先级高)
(7)make install

 

三、总结

1、运行时,动态库的装载依赖于ld-linux.so.6的实现,它查找共享库的顺序如下:
(1)ld-linux.so.6在可执行的目标文件中被指定,可用readelf命令查看
(2)ld-linux.so.6缺省在/usr/lib和lib中搜索;当glibc安装到/usr/local下时,它查找/usr/local /lib
(3)LD_LIBRARY_PATH环境变量中所设定的路径
(4)/etc/ld.so.conf(或/usr/local/etc/ld.so.conf)中所指定的路径,由ldconfig生成二进制的 ld.so.cache中

2、编译时,搜索库的路径顺序如下:
(1)ld-linux.so.6由gcc的spec文件中所设定
(2)gcc --print-search-dirs所打印出的路径,主要是libgcc_s.so等库。可以通过GCC_EXEC_PREFIX来设定
(3)LIBRARY_PATH环境变量中所设定的路径,或编译的命令行中指定的-L/usr/local/lib
(2)binutils中的ld所设定的缺省搜索路径顺序,编译binutils时指定。

     (可以通过“ld --verbose | grep SEARCH”来查看)

3、二进制程序的搜索路径顺序为PATH环境变量中所设定。一般/usr/local/bin高于/usr/bin

4、编译时的头文件的搜索路径顺序,与library的查找顺序类似。一般/usr/local/include高于/usr/include

————————————————————————————————————

前言
如果普通编程不需要了解这些东西,如果想精确控制你的对象文件的格式或者你想查看一下文件对象里的内容以便作出某种判断,刚你可以看一下下面的工 具:objdump, nm, ar。当然,本文不可能非常详细的说明它们的使用方法和功能。如果你觉得本文不够清楚,你可以使用:man. 我的计划只是想让更多的人了解这些工具,以后在今后 的编程过程中能有所帮助。


操作系统: Linux

开始

  • 库文件操作命令:ar ----非常好的东东。。让你能查看函数库里的详细情况和用多个对象文件生成一个库文件。
    • 经 常用法:
      • ar -t libname.a //显示所有对象文件(.o文件)的列表.例: # ar t libtest.a
        libtest1.o
        libtest2.o
      • ar -rv libname.a  objfile1.o objfile2.o ... objfilen.o  //把objfile1.o--objfilen.o打包成一个库文件
    • ar 选项
      d:从库中删除模块。按模块原来的文件名指定要删除的模块。如果使用了任选项v则列出被删除的每个模块。
      m:该操作是在一个库中移动成员。当库中如果有若干模块有相同的符号定义(如函数定义),则成员的位置顺序很重要。如果没有指定任选项,任何指定的成员将 移到库的最后。也可以使用'a','b',或'I'任选项移动到指定的位置。
      p:显示库中指定的成员到标准输出。如果指定任选项v,则在输出成员的内容前,将显示成员的名字。如果没有指定成员的名字,所有库中的文件将显示出来。
      q:快速追加。增加新模块到库的结尾处。并不检查是否需要替换。'a','b',或'I'任选项对此操作没有影响,模块总是追加的库的结尾处。如果使用了 任选项v则列出每个模块。 这时,库的符号表没有更新,可以用'ar s'或ranlib来更新库的符号表索引。
      r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换 其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
      t:显示库的模块表清单。一般只显示模块名。
      x:从库中提取一个成员。如果不指定要提取的模块,则提取库中所有的模块。
      下面在看看可与操作选项结合使用的任选项:

      a:在库的一个已经存在的成员后面增加一个新的文件。如果使用任选项a,则应该为命令行中membername参数指定一个已经存在的成员名。
      b:在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项b,则应该为命令行中membername参数指定一个已经存在的成员名。
      c:创建一个库。不管库是否存在,都将创建。
      f:在库中截短指定的名字。缺省情况下,文件名的长度是不受限制的,可以使用此参数将文件名截短,以保证与其它系统的兼容。
      i:在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项i,则应该为命令行中membername参数指定一个已经存在的成员名(类似任选项 b)。
      l:暂未使用
      N:与count参数一起使用,在库中有多个相同的文件名时指定提取或输出的个数。
      o:当提取成员时,保留成员的原始数据。如果不指定该任选项,则提取出的模块的时间将标为提取出的时间。
      P:进行文件名匹配时使用全路径名。ar在创建库时不能使用全路径名(这样的库文件不符合POSIX标准),但是有些工具可以。
      s:写入一个目标文件索引到库中,或者更新一个存在的目标文件索引。甚至对于没有任何变化的库也作该动作。对一个库做ar s等同于对该库做ranlib。
      S:不创建目标文件索引,这在创建较大的库时能加快时间。
      u:一般说来,命令ar r...插入所有列出的文件到库中,如果你只想插入列出文件中那些比库中同名文件新的文件,就可以使用该任选项。该任选项只用于r操作选项。
      v:该选项用来显示执行操作选项的附加信息。
      V:显示ar的版本.
  • nm --列出目标文件(.o)的符号清单。。NND,太激动了。刚知道此命令时让我三天没睡好觉。我就使劲用了一把。
    • 常用法:
      • nm -s filename.a/filename.o/a.out  里边所有的符号列表一清二楚。例:
        # nm -s a.out
        080495b8 A __bss_start
        08048334 t call_gmon_start
        080495b8 b completed.5751
        080494b8 d __CTOR_END__
        080494b4 d __CTOR_LIST__
        080495ac D __data_start
        080495ac W data_start
        08048450 t __do_global_ctors_aux
        08048360 t __do_global_dtors_aux
        080495b0 D __dso_handle
        080494c0 d __DTOR_END__
        080494bc d __DTOR_LIST__
        080494c8 d _DYNAMIC
        080495b8 A _edata
        080495bc A _end
        0804847c T _fini
        08048498 R _fp_hw
        08048390 t frame_dummy
        080484b0 r __FRAME_END__
        08049594 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
        0804844c T __i686.get_pc_thunk.bx
        080482b8 T _init
        080494b4 a __init_array_end
        080494b4 a __init_array_start
        0804849c R _IO_stdin_used
        080494c4 d __JCR_END__
        080494c4 d __JCR_LIST__
                 w _Jv_RegisterClasses
        080483e0 T __libc_csu_fini
        080483f0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.0
        080483b4 T main
        080495b4 d p.5749
                 U puts@@GLIBC_2.0
        08048310 T _start
    • 选项/属性:
      -a或--debug-syms:显示调试符号。
      -B:等同于--format=bsd,用来兼容MIPS的nm。
      -C或--demangle:将低级符号名解码(demangle)成用户级名字。这样可以使得C++函数名具有可读性。
      -D或--dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
      -f format:使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd。
      -g或--extern-only:仅显示外部符号。
      -n、-v或--numeric-sort:按符号对应地址的顺序排序,而非按符号名的字符顺序。
      -p或--no-sort:按目标文件中遇到的符号顺序显示,不排序。
      -P或--portability:使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。
      -s或--print-armap:当列出库中成员的符号时,包含索引。索引的内容包含:哪些模块包含哪些名字的映射。
      -r或--reverse-sort:反转排序的顺序(例如,升序变为降序)。
      --size-sort:按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。
      -t radix或--radix=radix:使用radix进制显示符号值。radix只能为"d"表示十进制、"o"表示八进制或"x"表示十六进制。
      --target=bfdname:指定一个目标代码的格式,而非使用系统的默认格式。
      -u或--undefined-only:仅显示没有定义的符号(那些外部符号)。
      -l或--line-numbers:对每个符号,使用调试信息来试图找到文件名和行号。对于已定义的符号,查找符号地址的行号。对于未定义符号,查找指 向符号重定位入口的行号。如果可以找到行号信息,显示在符号信息之后。
      -V或--version:显示nm的版本号。
      --help:显示nm的任选项。
  • objdump  文件命令功能强的惊人。能实现上述两个命令(ar,nm)的 很多功能。它主要是查看对象文件的内容信息。
    • 常用法:
      • objdump -h file<.o,.a,.out>//查看对象文件所有的节sections.例如:
        # objdump -h libtest1.o
        libtest1.o:     file format elf32-i386
        Sections:
        Idx Name          Size      VMA       LMA       File off  Algn
          0 .text         00000014  00000000  00000000  00000034  2**2
                          CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
          1 .data         00000000  00000000  00000000  00000048  2**2
                          CONTENTS, ALLOC, LOAD, DATA
          2 .bss          00000000  00000000  00000000  00000048  2**2
                          ALLOC
          3 .rodata       0000000e  00000000  00000000  00000048  2**0
                          CONTENTS, ALLOC, LOAD, READONLY, DATA
          4 .comment      0000001f  00000000  00000000  00000056  2**0
                          CONTENTS, READONLY
          5 .note.GNU-stack 00000000  00000000  00000000  00000075  2**0
                          CONTENTS, READONLY
      • objdump -t 查看对象文件所有的符号列表,相当于 nm -s objfilename,如:
        # objdump -t libtest1.o

        libtest1.o:     file format elf32-i386

        SYMBOL TABLE:
        00000000 l    df *ABS*  00000000 libtest1.c
        00000000 l    d  .text  00000000 .text
        00000000 l    d  .data  00000000 .data
        00000000 l    d  .bss   00000000 .bss
        00000000 l    d  .rodata        00000000 .rodata
        00000000 l    d  .note.GNU-stack        00000000 .note.GNU-stack
        00000000 l    d  .comment       00000000 .comment
        00000000 g     F .text  00000014 print_test1
        00000000         *UND*  00000000 puts
    • 更多信息请查看选项:
      --archive-headers
      -a 显示档案库的成员信息,与 ar tv 类似

          objdump -a libpcap.a
          和 ar -tv libpcap.a 显示结果比较比较
          显然这个选项没有什么意思。

      --adjust-vma=offset
          When  dumping  information, first add offset to all
          the section addresses.  This is useful if the  sec-
          tion  addresses  do  not correspond  to the symbol
          table, which can happen when  putting  sections  at
          particular  addresses when using a format which can
          not represent section addresses, such as a.out.

      -b bfdname
      --target=bfdname
          指定目标码格式。这不是必须的,objdump能自动识别许多格式,
          比如:objdump -b oasys -m vax -h fu.o
          显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys
          编译器生成的目标文件。objdump -i将给出这里可以指定的
          目标码格式列表

      --demangle
      -C 将底层的符号名解码成用户级名字,除了去掉所有开头
         的下划线之外,还使得C++函数名以可理解的方式显示出来。

      --debugging
          显示调试信息。企图解析保存在文件中的调试信息并以C语言
          的语法显示出来。仅仅支持某些类型的调试信息。

      --disassemble
      -d 反汇编那些应该还有指令机器码的section

      --disassemble-all
      -D 与 -d 类似,但反汇编所有section

      --prefix-addresses
          反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。
          显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。

      --disassemble-zeroes
          一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。

      -EB
      -EL
      --endian={big|little}
          这个选项将影响反汇编出来的指令。
          little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址,
          x86都是这种。

      --file-headers
      -f 显示objfile中每个文件的整体头部摘要信息。

      --section-headers
      --headers
      -h 显示目标文件各个section的头部摘要信息。

      --help 简短的帮助信息。

      --info
      -i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。

      --section=name
      -j name 仅仅显示指定section的信息

      --line-numbers
      -l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用
         使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求
         编译时使用了-g之类的调试编译选项。

      --architecture=machine
      -m machine
          指定反汇编目标文件时使用的架构,当待反汇编文件本身没有描述
          架构信息的时候(比如S-records),这个选项很有用。可以用-i选项
          列出这里能够指定的架构

      --reloc
      -r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇
         编后的格式显示出来。

      --dynamic-reloc
      -R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些
         共享库。

      --full-contents
      -s 显示指定section的完整内容。

          objdump --section=.text -s inet.o | more

      --source
      -S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,
         效果比较明显。隐含了-d参数。

      --show-raw-insn
          反汇编的时候,显示每条汇编指令对应的机器码,除非指定了
          --prefix-addresses,这将是缺省选项。

      --no-show-raw-insn
          反汇编时,不显示汇编指令的机器码,这是指定 --prefix-addresses
          选项时的缺省设置。

      --stabs
          Display the contents of the .stab, .stab.index, and
          .stab.excl sections from an ELF file.  This is only
          useful  on  systems  (such as Solaris 2.0) in which
          .stab debugging symbol-table entries are carried in
          an ELF section.  In most other file formats, debug-
          ging  symbol-table  entries  are interleaved  with
          linkage symbols, and are visible in the --syms output.

      --start-address=address
          从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。

      --stop-address=address
          显示数据直到指定地址为止,该选项影响-d、-r和-s选项的输出。

      --syms
      -t 显示文件的符号表入口。类似于nm -s提供的信息

      --dynamic-syms
      -T 显示文件的动态符号表入口,仅仅对动态目标文件有意义,比如某些
         共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。

      --version 版本信息

          objdump --version

      --all-headers
      -x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于
         -a -f -h -r -t 同时指定。

====================================================================

 

 

Dos   exe   file   structure  
   
  offset   size         description  
   
  00   word   "mz"   -   link   file   .exe   signature   (mark   zbikowski?)  
  02   word   length   of   image   mod   512  
  04   word   size   of   file   in   512   byte   pages  
  06   word   number   of   relocation   items   following   header  
  08   word   size   of   header   in   16   byte   paragraphs,   used   to   locate  
          the   beginning   of   the   load   module  
  0a   word   min   #   of   paragraphs   needed   to   run   program  
  0c   word   max   #   of   paragraphs   the   program   would   like  
  0e   word   offset   in   load   module   of   stack   segment   (in   paras)  
  10   word   initial   sp   value   to   be   loaded  
  12   word   negative   checksum   of   pgm   used   while   by   exec   loads   pgm  
  14   word   program   entry   point,   (initial   ip   value)  
  16   word   offset   in   load   module   of   the   code   segment   (in   paras)  
  18   word   offset   in   .exe   file   of   first   relocation   item  
  1a   word   overlay   number   (0   for   root   program)  
   
  -   relocation   table   and   the   program   load   module   follow   the   header  
  -   relocation   entries   are   32   bit   values   representing   the   offset  
  into   the   load   module   needing   patched  
  -   once   the   relocatable   item   is   found,   the   cs   register   is   added   to  
  the   value   found   at   the   calculated   offset  
   
          registers   at   load   time   of   the   exe   file   are   as   follows:  
   
  ax:         contains   number   of   characters   in   command   tail,   or   0  
  bx:cx         32   bit   value   indicating   the   load   module   memory   size  
  dx         zero  
  ss:sp         set   to   stack   segment   if   defined   else,   ss   =   cs   and  
          sp=ffffh   or   top   of   memory.  
  ds         set   to   segment   address   of   exe   header  
  es         set   to   segment   address   of   exe   header  
  cs:ip         far   address   of   program   entry   point,   (label   on   "end"  
          statement   of   program).  
   
          EXE文件包含一个文件头和一个可重定位程序映象.文件头包含MS-DOS  
  用于加载程序的信息,例如程序的大小和寄存器的初始值.文件头还指向一个  
  重定位表,该表包含指向程序映象中可重定位段地址的指针链表.文件头的形  
  式与EXEHEADER结构对应:  
  EXEHEADER   STRUC  
  exSignature   dw   5A4Dh   ;.EXE标志  
  exExraBytes   dw   ?   ;最后(部分)页中的字节数  
  exPages   dw   ?   ;文件中的全部和部分页数  
  exRelocItems   dw   ?   ;重定位表中的指针数  
  exHeaderSize   dw   ?   ;以字节为单位的文件头大小  
  exMinAlloc   dw   ?   ;最小分配大小  
  exMaxAlloc   dw   ?   ;最大分配大小  
  exInitSS   dw   ?   ;初始SS值  
  exInitSP   dw   ?   ;初始SP值  
  exChechSum   dw   ?   ;补码校验值  
  exInitIP   dw   ?   ;初始IP值  
  exInitCS   dw   ?   ;初始CS值  
  exRelocTable   dw   ?   ;重定位表的字节偏移量  
  exOverlay   dw   ?   ;覆盖号  
  EXEHEADER   ENDS  
  程序映象,包含处理器代码和程序的初始数据,紧接在文件头之后.它的  
  大小,以字节为单位,等于.EXE文件的大小减去文件头的大小,也等于exHeaderSize  
  的域的值乘以16.MS-DOS通过把该映象直接从文件拷贝到内存加载.EXE程序  
  然后调整定位表中说明的可重定位段地址.  
   
  定位表是一个重定位指针数组,每个指向程序映象中的可重定位段地址.   文件头中的exRelocItems域说明了数组中指针的个数,exRelocTable域说明了   分配表的起始文件偏移量.每个重定位指针由两个16位值组成:偏移量和段值.   为加载.EXE程序,MS-DOS首先读文件头以确定.EXE标志并计算程序映象的   大小,然后它试图申请内存.首先,它计算程序映象文件的大小加上PSP的大小   再加上EXEHEADER结构中的exMinAlloc域说明的内存大小这三者之和,如果总   和超过最大可用内存块的大小,则MS-DOS停止加载程序并返回一个出错值.否   则,它计算程序映象的大小加上PSP的大小再加上EXEHEADER结构中exMaxAlloc   域说明的内存大小之和,如果第二个总和小于最大可用内存块的大小,则MS-DOS   分配计算得到的内存量.否则,它分配最大可用内存块.   分配完内存后,MS-DOS确定段地址;也称为起始段地址,MS-DOS从此处加载   程序映象.如果exMinAlloc域和exMaxAlloc域中的值都为零,则MS-DOS把映象   尽可能地加载到内存最高端.否则,它把映象加载到紧挨着PSP域之上.   接下来,MS-DOS读取重定位表中的项目调整所有由可重定位指针说明的段   地址.对于重定位表中的每个指针,MS-DOS寻找程序映象中相应的可重定位段   地址,并把起始段地址加到它之上.一旦调整完毕,段地址便指向了内存中被加   载程序的代码和数据段.   MS-DOS在所分配内存的最低部分建造256字节的PSP,把AL和AH设置为加载   .COM程序时所设置的值.MS-DOS使用文件头中的值设置SP与SS,调整SS初始值,   把起始地址加到它之上.MS-DOS还把ES和DS设置为PSP的段地址.   最后,MS-DOS从程序文件头读取CS和IP的初始值,把起始段地址加到CS之   上,把控制转移到位于调整后地址处的程序.

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/todototry/archive/2007/02/01/1500130.aspx

原创粉丝点击