如何使用objdump反汇编一个函数?


89

我的系统上安装了二进制文件,并希望查看给定功能的反汇编。最好使用objdump,但其他解决方案也是可以接受的。

这些问题中,我了解到,如果我只知道边界地址,则可以分解部分代码。从这个答案中,我学习了如何将拆分的调试符号转回单个文件。

但是,即使对单个文件进行操作,甚至分解所有代码(即,没有开始或停止地址,但使用的普通-d参数objdump),我仍然看不到该符号。只要所讨论的函数是静态的,就有意义,因此不会导出。不过,valgrind将报告函数名称,因此必须将其存储在某处。

查看调试部分的详细信息,我发现该.debug_str部分中提到的名称,但是我不知道有什么工具可以将其转换为地址范围。


2
一个小小的注意事项:如果一个函数被标记static,编译器可能会将其内联到其调用位置。这可能意味着有可能实际上不是拆卸,任何功能本身。如果您可以发现其他功能的符号,但找不到所需的功能,则有力暗示该功能已内联。Valgrind仍可以引用原始的预内联函数,因为ELF文件调试信息存储了每个单独指令的来源,即使这些指令已移至其他位置。
davidg

@davidg:是的,但是由于Tom的回答在这种情况下有效,因此似乎并非如此。但是,您是否知道一种方法,例如使用每个指令的来源信息来注释汇编代码?
MvG 2014年

1
很高兴听到!addr2line将接受来自的PC / IPstdin并打印出其相应的源代码行。同样,objdump -l将objdump与源代码行混合;尽管对于带有大量内联的高度优化的代码,这两个程序的结果并不总是特别有用。
davidg

Answers:


86

我建议使用gdb作为最简单的方法。您甚至可以单线完成它,例如:

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'

4
+1未记录的功能!-ex 'command'不在man gdb!?但是实际上在gdb docs中列出了。同样对于其他人,类似的东西/bin/ls可能会被剥离,因此,如果该确切命令什么都不显示,请尝试另一个对象!也可以将文件/对象指定为裸字参数;例如,gdb -batch -ex 'disassemble main' /bin/ls
hoc_age 2014年

3
手册页不是最终的。长期以来,它并没有真正维护过,但是现在我认为它是由主要文档生成的。另外,“ gdb --help”现在也更加完整。
Tom Tromey 2014年

7
gdb /bin/ls -batch -ex 'disassemble main'效果也很好
stefanct16年

1
如果column -ts$'\t'用于过滤GDB输出,则原始字节和源列将很好地对齐。同样,-ex 'set disassembly-flavor intel'在其他之前-ex将导致Intel汇编语法。
Ruslan

disassemble fn使用上面的方法进行了调用。但是,似乎在二进制文件中有多个具有相同名称的函数时,只有一个函数被反汇编。是否可以全部分解它们,或者我应该根据原始地址分解它们?
TheAhmad

26

gdbdisassemble/rs也显示源字节和原始字节

使用这种格式,它实际上非常接近objdump -S输出:

gdb -batch -ex "disassemble/rs $FUNCTION" "$EXECUTABLE"

main.c

#include <assert.h>

int myfunc(int i) {
    i = i + 2;
    i = i * 2;
    return i;
}

int main(void) {
    assert(myfunc(1) == 6);
    assert(myfunc(2) == 8);
    return 0;
}

编译和反汇编

gcc -O0 -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
gdb -batch -ex "disassemble/rs myfunc" main.out

拆卸:

Dump of assembler code for function myfunc:
main.c:
3       int myfunc(int i) {
   0x0000000000001135 <+0>:     55      push   %rbp
   0x0000000000001136 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x0000000000001139 <+4>:     89 7d fc        mov    %edi,-0x4(%rbp)

4           i = i + 2;
   0x000000000000113c <+7>:     83 45 fc 02     addl   $0x2,-0x4(%rbp)

5           i = i * 2;
   0x0000000000001140 <+11>:    d1 65 fc        shll   -0x4(%rbp)

6           return i;
   0x0000000000001143 <+14>:    8b 45 fc        mov    -0x4(%rbp),%eax

7       }
   0x0000000000001146 <+17>:    5d      pop    %rbp
   0x0000000000001147 <+18>:    c3      retq   
End of assembler dump.

已在Ubuntu 16.04,GDB 7.11.1上测试。

objdump + AWK解决方法

如以下所述打印段落:https : //unix.stackexchange.com/questions/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that-has-the -文本

objdump -d main.out | awk -v RS= '/^[[:xdigit:]]+ <FUNCTION>/'

例如:

objdump -d main.out | awk -v RS= '/^[[:xdigit:]]+ <myfunc>/'

给出:

0000000000001135 <myfunc>:
    1135:   55                      push   %rbp
    1136:   48 89 e5                mov    %rsp,%rbp
    1139:   89 7d fc                mov    %edi,-0x4(%rbp)
    113c:   83 45 fc 02             addl   $0x2,-0x4(%rbp)
    1140:   d1 65 fc                shll   -0x4(%rbp)
    1143:   8b 45 fc                mov    -0x4(%rbp),%eax
    1146:   5d                      pop    %rbp
    1147:   c3                      retq   

使用时-S,我不认为有一种防故障的方法,因为代码注释可以包含任何可能的序列...但是以下代码几乎一直有效:

objdump -S main.out | awk '/^[[:xdigit:]]+ <FUNCTION>:$/{flag=1;next}/^[[:xdigit:]]+ <.*>:$/{flag=0}flag'

改编自:如何在两个标记模式之间选择可能会用awk / sed多次出现的线条

邮件列表回复

邮件列表上有一个2010线程,它表明不可能:https : //sourceware.org/ml/binutils/2010-04/msg00445.html

除了gdbTom提出的解决方法外,他们还评论了另一种(更糟糕的)编译方法,-ffunction-section即每部分放入一个函数然后转储该部分。

Nicolas Clifton给了它WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html,可能是因为GDB解决方法涵盖了该用例。


gdb方法在共享库和目标文件上运行良好。
汤姆·特罗米

16

使用Objdump反汇编一个单一函数

我有两种解决方案:

1.基于命令行

这种方法可以完美地工作,并且简单易行。我用objdump的-d标志和通过它的awk。反汇编的输出看起来像

000000000000068a <main>:
68a:    55                      push   %rbp
68b:    48 89 e5                mov    %rsp,%rbp
68e:    48 83 ec 20             sub    $0x20,%rsp

首先,我从objdump输出的描述开始。甲部分功能由一个空行分离。因此,将FS(字段分隔符)更改为换行符,将RS(记录分隔符)更改为两次换行符,可以轻松搜索建议的功能,因为只需在$ 1字段中查找!

objdump -d name_of_your_obj_file | awk -F"\n" -v RS="\n\n" '$1 ~ /main/'

当然,您可以用要打印的任何其他功能替换main

2. Bash脚本

我为此问题编写了一个小的bash脚本。粘贴并复制并将其另存为例如dasm文件。

#!/bin/bash
# Author: abu
# filename: dasm
# Description: puts disassembled objectfile to std-out

if [ $# = 2 ]; then
        sstrg="^[[:xdigit:]]{2,}+.*<$2>:$"
        objdump -d $1 | awk -F"\n" -v RS="\n\n" '$1 ~ /'"$sstrg"'/'
elif [ $# = 1 ]; then
        objdump -d $1 | awk -F"\n" -v RS="\n\n" '{ print $1 }'
else
    echo "You have to add argument(s)"
    echo "Usage:   "$0 " arg1 arg2"  
    echo "Description: print disassembled label to std-out"
    echo "             arg1: name of object file"
    echo "             arg2: name of function to be disassembled"
    echo "         "$0 " arg1    ... print labels and their rel. addresses" 
fi

更改x-access并使用以下命令调用它:

chmod +x dasm
./dasm test main

这是很多比用脚本调用GDB更快。另外,使用objdump不会将库加载到内存中,因此更安全!


Vitaly Fadeev为此脚本编写了自动补全功能,这确实是一个不错的功能,可以加快键入速度。

脚本可以在这里找到。


现在看来,这取决于是否objdumpgdb更快。对于一个巨大的二进制文件(Firefox的libxul.so)来说objdump,它要花很长时间,所以我在一小时后取消了它,而gdb花了不到一分钟。
西蒙

5

为了简化awk相对于其他答案来解析objdump输出的用法:

objdump -d filename | sed '/<functionName>:/,/^$/!d'

4

就像gdb解决方案一样工作(因为它将偏移量移向零),除了它没有延迟(在我的PC上大约5毫秒完成工作,而gdb解决方案大约需要150毫秒):

objdump_func:

#!/bin/sh
# $1 -- function name; rest -- object files
fn=$1; shift 1
exec objdump -d "$@" | 
awk " /^[[:xdigit:]].*<$fn>/,/^\$/ { print \$0 }" |
awk -F: -F' '  'NR==1 {  offset=strtonum("0x"$1); print $0; } 
                NR!=1 {  split($0,a,":"); rhs=a[2]; n=strtonum("0x"$1); $1=sprintf("%x", n-offset); printf "%4s:%s\n", $1,rhs }'

我暂时无法测试,但是我很期待能解决这个问题。您能否详细说明“偏移量向零偏移”方面?我在这里的gdb答案中没有看到这个明确的内容,我想听到更多关于那里实际发生的情况以及原因的更多信息。
MvG

基本上,它看起来像您所针对的函数(第一个awk函数所执行的)是目标文件中唯一的函数,也就是说,即使该函数以0x2d第二个awk开始(例如,将其0x00减去)0x2d从每个指令的地址开始),这很有用,因为汇编代码通常会相对于函数的开头进行引用,并且如果函数从0开始,则不必在头上做减法。awk代码可能会更好,但至少可以完成工作并且相当有效。
PSkocik

现在回想起来似乎与编制-ffunction-sections是确保每个功能从0开始一个更简单的方法
PSkocik

4

如果您有最新的binutils(2.32+),这非常简单。

传递--disassemble=SYMBOL给objdump只会反汇编指定的函数。无需传递起始地址和结束地址。

LLVM objdump也具有类似的选项(--disassemble-symbols)。


谢谢。Binutils 2.32的变更日志,2019年2月2日:lists.gnu.org/archive/html/info-gnu/2019-02/msg00000.htmlObjdump的--disassemble选项现在可以使用参数,指定反汇编的起始符号。将从该符号继续到下一个符号或函数结束。
osgx

3

Bash完成 ./dasm

完整的符号名该解决方案(d语言版本):

  • 通过键入dasm test然后按TabTab,您将获得所有功能的列表。
  • 键入dasm test m然后按TabTab ,将显示以m开头的所有功能,或者如果仅存在一个功能,则该功能将自动完成。

档案/etc/bash_completion.d/dasm

# bash completion for dasm
_dasm()
{
    local cur=${COMP_WORDS[COMP_CWORD]}

    if [[ $COMP_CWORD -eq 1 ]] ; then
    # files
    COMPREPLY=( $( command ls *.o -F 2>/dev/null | grep "^$cur" ) )

    elif [[ $COMP_CWORD -eq 2 ]] ; then
    # functions
    OBJFILE=${COMP_WORDS[COMP_CWORD-1]}

    COMPREPLY=( $( command nm --demangle=dlang $OBJFILE | grep " W " | cut -d " " -f 3 | tr "()" "  " | grep "$cur" ) )

    else
    COMPREPLY=($(compgen -W "" -- "$cur"));
    fi
}

complete -F _dasm dasm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.