¿Cómo desmontar una sola función usando objdump?


Tengo un binario instalado en mi sistema, y me gustaría ver el desmontaje de una función dada. Preferiblemente usando objdump, pero otras soluciones también serían aceptables.

De estas preguntas He aprendido que podría ser capaz de desensamblar parte del código si solo conozco las direcciones de límite. De esta respuesta he aprendido cómo convertir mis símbolos de depuración dividida en un solo archivo.

Pero incluso operando en ese solo archivo, e incluso desensamblando todo el código (es decir, sin dirección de inicio o parada, pero sin parámetro -d a objdump), todavía no veo ese símbolo en ninguna parte. Lo que tiene sentido en la medida en que la función en cuestión es estática, por lo que no se exporta. Sin embargo, valgrind reportará el nombre de la función, por lo que debe almacenarse en algún lugar.

Mirando los detalles de las secciones de depuración, encuentro ese nombre mencionado en la sección .debug_str, pero no conozco una herramienta que pueda convertir esto en un rango de direcciones.

6 answers

Sugeriría usar gdb como el enfoque más simple. Incluso puedes hacerlo como un solo trazador de líneas, como:

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'
 54
Author: Tom Tromey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-01 01:47:57

Gdb disassemble/rs para mostrar también los bytes fuente y raw

Con este formato, se acerca mucho a la salida objdump -S:

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

A. c: {[16]]}

#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;
}

Compilar y desensamblar

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

Desmontaje:

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.

Probado en Ubuntu 16.04, GDB 7.11.1.

Soluciones de Objdump + awk

Imprimir el párrafo mencionado en: 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>/'

Por ejemplo:

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

Da solo:

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 

Cuando se usa -S, no creo que haya una forma a prueba de fallos, ya que los comentarios de código podrían contener cualquier secuencia posible... Pero lo siguiente funciona casi todo el tiempo:

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

Adaptado de: Cómo seleccionar líneas entre dos patrones de marcadores que pueden ocurrir varias veces con awk / sed

Respuestas de la lista de correo

Hay un hilo de 2010 en la lista de correo que dice que no es posible: https://sourceware.org/ml/binutils/2010-04/msg00445.html

Además de la solución gdb propuesta por Tom, también comentan otra (peor) solución de compilar con -ffunction-section que pone una función por sección y luego descarga la sección.

Nicolas Clifton le dio un WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html , probablemente porque la solución del GDB cubre ese caso de uso.

 10
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-04 16:09:05

Desmontar Una Sola Función usando Objdump

Tengo dos soluciones:

1. Basado en Línea de comandos

Este método funciona perfectamente y también es muy corto. Yo uso objdump con -d opción y tubería que a awk. La salida desmontada se ve como

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 separated by an empty line. Por lo tanto, cambiando el FS (Separador de campo) a nueva línea y el RS (Seperator Registro) a dos veces nueva línea le permiten buscar fácilmente para su función recomendada, ya que es simplemente para encontrar dentro del campo $1!

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

Por supuesto, puede reemplazar main a cualquier función que desee que sea de salida.

2. Bash Script

He escrito un pequeño script bash para este número. Simplemente cópielo y guárdelo como, por ejemplo, dasm archivo.

#!/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

Cambiar el x-access e invocarlo con por ejemplo:

chmod +x dasm
./dasm test main

Esto es mucho más rápido que invocar gdb con un script. Al lado de la forma de usar objdump no cargar las bibliotecas en la memoria y por lo tanto es más seguro!


Vitaly Fadeev programó el autocompletado a este script, que es realmente una característica agradable y acelera la escritura.

El script se puede encontrar aquí.

 5
Author: abu_bua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-29 20:21:52

Esto funciona igual que la solución gdb (en el sentido de que desplaza las compensaciones hacia cero), excepto que no está rezagado (hace el trabajo en aproximadamente 5ms en mi PC, mientras que la solución gdb tarda alrededor de 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 }'
 4
Author: PSkocik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-07 16:23:42

Para simplificar el uso de awk para analizar la salida de objdump en relación con otras respuestas:

objdump -d filename | sed '/<functionName>:/,/^$/!d'
 2
Author: fcr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-01 04:23:27

Finalización de Bash para ./dasm

Nombre completo de los símbolos a esta solución (versión D lang):

  • Escribiendo dasm test y luego presionando la pestaña Tab , obtendrá una lista de todas las funciones.
  • Escribiendo dasm test m y luego presionando la pestaña Tab se mostrarán todas las funciones que comiencen con m, o en caso de que solo exista una función, se completará automáticamente.

Archivo /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
 1
Author: Vitaly Fadeev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-28 13:42:05