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

89

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

De estas preguntas aprendí que podría desensamblar parte del código si solo conociera las direcciones de los límites. A partir de esta respuesta , aprendí cómo convertir mis símbolos de depuración divididos nuevamente en un solo archivo.

Pero incluso operando en ese único archivo, e incluso desensamblando todo el código (es decir, sin dirección de inicio o parada, pero -dparámetro simple para 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, valgrindinformará 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 .debug_strsección, pero no conozco una herramienta que pueda convertir esto en un rango de direcciones.

MvG
fuente
2
Una nota al margen menor: si una función está marcada static, el compilador podría incluirla en sus sitios de llamada. Esto puede significar que es posible que no haya ninguna función para desmontar, per se . Si puede detectar símbolos para otras funciones, pero no la función que está buscando, esto es un fuerte indicio de que la función se ha incluido. Valgrind aún puede hacer referencia a la función original preincluida porque la información de depuración del archivo ELF almacena el origen de cada instrucción individual, incluso si las instrucciones se mueven a otro lugar.
davidg
@davidg: cierto, pero dado que la respuesta de Tom funcionó en este caso, este no parece ser el caso. Sin embargo, ¿conoce alguna forma de, por ejemplo, anotar el código ensamblador con esa información de dónde proviene cada instrucción?
MvG
1
¡Qué bueno escuchar! addr2lineaceptará PC / IP de stdine imprimirá sus líneas de código fuente correspondientes. Del mismo modo, objdump -lmezclará el objdump con las líneas fuente; aunque para el código altamente optimizado con mucha inserción, los resultados de ambos programas no siempre son particularmente útiles.
davidg

Respuestas:

86

Sugeriría usar gdb como el enfoque más simple. Incluso puedes hacerlo como una sola línea, como:

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'
Tom Tromey
fuente
4
+1 característica indocumentada! -ex 'command'no está en man gdb!? Pero, de hecho, aparece en la documentación de gdb . También para otros, cosas como /bin/lspodrían ser eliminadas, así que si ese comando exacto no muestra nada, ¡pruebe con otro objeto! También puede especificar archivo / objeto como argumento de palabra simple; por ejemplo,gdb -batch -ex 'disassemble main' /bin/ls
hoc_age
3
La página de manual no es definitiva. Durante mucho tiempo no se mantuvo realmente, pero ahora creo que se genera a partir de los documentos principales. También "gdb --help" ahora también es más completo.
Tom Tromey
7
gdb /bin/ls -batch -ex 'disassemble main'también funciona
stefanct
1
Si usa column -ts$'\t'para filtrar la salida de GDB, tendrá los bytes sin procesar y las columnas de origen bien alineadas. Además, -ex 'set disassembly-flavor intel'antes de que otros -exs resulten en sintaxis de ensamblaje Intel.
Ruslan
Llamé disassemble fnusando el método anterior. Pero parece que cuando hay varias funciones con el mismo nombre en el archivo binario, solo se desmonta una. ¿Es posible desmontarlos todos o debería desmontarlos según la dirección sin procesar?
TheAhmad
26

gdb disassemble/rspara mostrar también los bytes de origen y sin procesar

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

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

C Principal

#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 desmontar

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

Desmontaje:

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.

Probado en Ubuntu 16.04, GDB 7.11.1.

objdump + awk soluciones alternativas

Imprima el párrafo como se menciona en: /unix/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that-has-the -texto

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

p.ej:

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

da solo:

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   

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

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

adaptado de: Cómo seleccionar líneas entre dos patrones de marcador 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 gdbsolución alternativa propuesta por Tom, también comentan sobre otra solución (peor) de compilación con la -ffunction-sectionque se coloca una función por sección y luego se descarga la sección.

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

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
El enfoque de gdb funciona bien en bibliotecas compartidas y archivos de objetos.
Tom Tromey
16

Desmontar una sola función usando Objdump

Tengo dos soluciones:

1. Basado en línea de comandos

Este método funciona perfectamente y además es simple. Yo uso objdump con la -d bandera y el tubo a través de awk . La salida desmontada parece

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

Para empezar, empiezo con la descripción de la salida objdump. Una sección o función está separada por una línea vacía. Por lo tanto, cambiar el FS (Separador de campo) a una nueva línea y el RS (Separador de registros) a dos veces una nueva línea le permite buscar fácilmente la función recomendada, ¡ya que es simplemente buscar 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 con cualquier otra función que le gustaría imprimir.

2. Bash Script

He escrito un pequeño script bash para este problema. Péguelo , cópielo y guárdelo como, por ejemplo, un archivo 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

Cambie el acceso x e invocalo con, por ejemplo:

chmod +x dasm
./dasm test main

Esto es mucho más rápido que invocar gdb con un script. Además, el uso de objdump no cargará las bibliotecas en la memoria y, por lo tanto, es más seguro.


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

El guión se puede encontrar aquí .

abu_bua
fuente
Parece que depende de si objdumpo gdbes más rápido. Para un binario enorme (libxul.so de Firefox) objdumptarda una eternidad, lo cancelé después de una hora, mientras que gdbtarda menos de un minuto.
Simon
5

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

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

Esto funciona igual que la solución gdb (en el sentido de que desplaza las compensaciones hacia cero), excepto que no tiene retrasos (hace el trabajo en aproximadamente 5 ms en mi PC, mientras que la solución gdb tarda aproximadamente 150 ms):

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 }'
PSkocik
fuente
No puedo probar ahora, pero estoy deseando que llegue a esto. ¿Puede desarrollar un poco el aspecto de "desplazamientos de compensación hacia cero"? No vi esto explícito en las respuestas de gdb aquí, y me gustaría escuchar un poco más sobre lo que realmente está sucediendo allí y por qué.
MvG
Básicamente, hace que parezca que la función a la que te diriges (que es lo que hace la primera awk) fuera la única función en el archivo de objeto, es decir, incluso si la función comienza en, digamos 0x2d, el segundo awk la desplazará hacia 0x00(restando 0x2dde la dirección de cada instrucción), lo cual es útil porque el código ensamblador a menudo hace referencias relativas al inicio de la función y si la función comienza en 0, no tiene que hacer las restas en su cabeza. El código awk podría ser mejor, pero al menos hace el trabajo y es bastante eficiente.
PSkocik
En retrospectiva, parece que compilar con -ffunction-sectionses una forma más fácil de asegurarse de que cada función comience en 0.
PSkocik
4

Si tiene un binutils muy reciente (2.32+), esto es muy simple.

Pasar --disassemble=SYMBOLa objdump desensamblará solo la función especificada. No es necesario pasar la dirección de inicio y la dirección final.

LLVM objdump también tiene una opción similar ( --disassemble-symbols).

Léo Lam
fuente
Gracias. Registro de cambios para binutils 2.32, 02 de febrero de 2019: lists.gnu.org/archive/html/info-gnu/2019-02/msg00000.html " La opción --disassemble de Objdump ahora puede tomar un parámetro, especificando el símbolo de inicio para el desmontaje. Desmontaje continuará desde este símbolo hasta el siguiente símbolo o el final de la función " .
osgx
3

Finalización de golpe para ./dasm

Nombres de símbolos completos para esta solución (versión D lang):

  • Al escribir dasm testy luego presionar TabTab, obtendrá una lista de todas las funciones.
  • Al escribir dasm test my luego presionar , se mostrarán TabTab todas las funciones que comienzan 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
Vitaly Fadeev
fuente