-->

How to disassemble one single function using objdu

2019-01-10 09:14发布

问题:

I've got a binary installed on my system, and would like to look at the disassembly of a given function. Preferrably using objdump, but other solutions would be acceptable as well.

From this questions I've learned that I might be able to disassemble part of the code if I only know the boundary addresses. From this answer I've learned how to turn my split debug symbols back into a single file.

But even operating on that single file, and even disassembling all the code (i.e. without start or stop address, but plain -d parameter to objdump), I still don't see that symbol anywhere. Which makes sense insofar as the function in question is static, so it isn't exported. Nevertheless, valgrind will report the function name, so it has to be stored somewhere.

Looking at the details of the debug sections, I find that name mentioned in the .debug_str section, but I don't know a tool which can turn this into an address range.

回答1:

I would suggest using gdb as the simplest approach. You can even do it as a one-liner, like:

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


回答2:

gdb disassemble/rs to show source and raw bytes as well

With this format, it gets really close to objdump -S output:

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

a.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;
}

Compile and disassemble

gcc -std=c99 -O0 -g a.c
gdb -batch -ex 'file a.out' -ex "disassemble/rs myfunc"

Disassembly:

Dump of assembler code for function main:
a.c:
1       int main(void) {
   0x00000000004004d6 <+0>:     55      push   %rbp
   0x00000000004004d7 <+1>:     48 89 e5        mov    %rsp,%rbp

2           int i;
3           i = 0;
   0x00000000004004da <+4>:     c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)

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

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

6           return 0;
   0x00000000004004e8 <+18>:    b8 00 00 00 00  mov    $0x0,%eax

7       }
   0x00000000004004ed <+23>:    5d      pop    %rbp
   0x00000000004004ee <+24>:    c3      retq   
End of assembler dump.

Tested on Ubuntu 16.04, GDB 7.11.1.

objdump + awk workarounds

Print the paragraph as mentioned at: https://unix.stackexchange.com/questions/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that-has-the-text

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

e.g.:

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

gives just:

000000000000064a <myfunc>:
 64a:   55                      push   %rbp
 64b:   48 89 e5                mov    %rsp,%rbp
 64e:   89 7d fc                mov    %edi,-0x4(%rbp)
 651:   83 45 fc 02             addl   $0x2,-0x4(%rbp)
 655:   d1 65 fc                shll   -0x4(%rbp)
 658:   8b 45 fc                mov    -0x4(%rbp),%eax
 65b:   5d                      pop    %rbp
 65c:   c3                      retq 

When using -S, I don't think there is a fail-proof way, as the code comments could contain any possible sequence... But the following works almost all the time:

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

adapted from: How to select lines between two marker patterns which may occur multiple times with awk/sed

Mailing list replies

There is a 2010 thread on the mailing list which says it is not possible: https://sourceware.org/ml/binutils/2010-04/msg00445.html

Besides the gdb workaround proposed by Tom, they also comment on another (worse) workaround of compiling with -ffunction-section which puts one function per section and then dumping the section.

Nicolas Clifton gave it a WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html , likely because the GDB workaround covers that use case.



回答3:

Disassemble One Single Function using Objdump

I have two solutions:

1. Commandline Based

This method works perfectly and is also very short. I use objdump with -d option and pipe it to awk. The disassembled output looks like

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

A section or function is seperated by an empty line. Hence changing the FS (Field Seperator) to newline and the RS (Record Seperator) to twice newline let you easily search for your recommended function, since it is simply to find within the $1 field!

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

Of course you can replace main to any function you want to be output.

2. Bash Script

I have written a small bash script for this issue. Simply copy it and save it as e.g. dasm file.

#!/bin/bash
# Author: abu
# 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

Change the x-access and invoke it with e.g.:

chmod +x dasm
./dasm test main

This is much faster than invoking gdb with a script. Beside the way using objdump will not load the libraries into memory and is therefore safer!


Vitaly Fadeev programmed the autocompletion to this script, which is really a nice feature and speeds up typing.

The script can be found here.



回答4:

This works just like the gdb solution (in that that it shifts the offsets towards zero) except that it's not laggy (gets the job done in about 5ms on my PC whereas the gdb solution takes about 150ms):

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 }'


回答5:

To simplify the usage of awk for parsing objdump's output relative to other answers:

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


回答6:

Bash completion for ./dasm

Complete symbol names to this solution (D lang version):

  • By typing dasm test and then pressing TabTab, you will get a list of all functions.
  • By typing dasm test m and then pressing TabTab all functions starting with m will be shown, or in case only one function exists, it will be autocompleted.

File /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