Come smontare una singola funzione usando objdump?


91

Ho un file binario installato sul mio sistema e vorrei esaminare lo smontaggio di una determinata funzione. Preferibilmente da usare objdump, ma sarebbero accettabili anche altre soluzioni.

Da queste domande ho imparato che potrei essere in grado di disassemblare parte del codice se conoscessi solo gli indirizzi di confine. Da questa risposta ho imparato come trasformare i miei simboli di debug divisi in un unico file.

Ma anche operando su quel singolo file, e persino smontando tutto il codice (cioè senza indirizzo di inizio o fine, ma semplice -dparametro per objdump), ancora non vedo quel simbolo da nessuna parte. Il che ha senso nella misura in cui la funzione in questione è statica, quindi non viene esportata. Tuttavia, valgrindriporterà il nome della funzione, quindi deve essere memorizzato da qualche parte.

Guardando i dettagli delle sezioni di debug, trovo quel nome menzionato nella .debug_strsezione, ma non conosco uno strumento che possa trasformarlo in un intervallo di indirizzi.


2
Una nota a staticmargine minore: se una funzione è contrassegnata , potrebbe essere inline dal compilatore nei suoi siti di chiamata. Ciò potrebbe significare che potrebbe non esserci effettivamente alcuna funzione da smontare, di per sé . Se riesci a individuare i simboli per altre funzioni, ma non per la funzione che stai cercando, questo è un forte suggerimento che la funzione è stata integrata. Valgrind può ancora fare riferimento alla funzione pre-inline originale perché le informazioni di debug del file ELF memorizzano da dove ha avuto origine ogni singola istruzione, anche se le istruzioni vengono spostate altrove.
davidg

@davidg: vero, ma poiché la risposta di Tom ha funzionato in questo caso, non sembra essere così. Tuttavia, conosci un modo, ad esempio, per annotare il codice assembly con le informazioni sulla provenienza di ciascuna istruzione?
MvG

1
Buono a sapersi! addr2lineaccetterà PC / IP da stdine stamperà le corrispondenti righe del codice sorgente. Allo stesso modo, objdump -lmescolerà l'objdump con le linee di origine; sebbene per un codice altamente ottimizzato con un forte inlining, i risultati di entrambi i programmi non sono sempre particolarmente utili.
davidg

Risposte:


87

Suggerirei di utilizzare gdb come approccio più semplice. Puoi anche farlo come una frase, come:

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

4
+1 funzione non documentata! -ex 'command'non è dentro man gdb!? Ma in effetti è elencato in gdb docs . Anche per altri, cose come /bin/lspotrebbero essere rimosse, quindi se quel comando esatto non mostra nulla, prova un altro oggetto! Può anche specificare file / oggetto come argomento bareword; ad esempio,gdb -batch -ex 'disassemble main' /bin/ls
hoc_age

3
La pagina man non è definitiva. Per molto tempo non è stato veramente mantenuto, ma ora penso che sia generato dai documenti principali. Anche "gdb --help" è ora più completo.
Tom Tromey

7
gdb /bin/ls -batch -ex 'disassemble main'funziona anche
stefanct

1
Se usi column -ts$'\t'per filtrare l'output GDB, avrai i byte grezzi e le colonne di origine ben allineate. Inoltre, -ex 'set disassembly-flavor intel'prima degli altri -exrisulterà nella sintassi dell'assembly Intel.
Ruslan

Ho chiamato disassemble fnusando il metodo, sopra. Ma sembra che quando ci sono più funzioni con lo stesso nome nel file binario, solo una viene smontata. È possibile smontarli tutti o dovrei smontarli in base all'indirizzo grezzo?
TheAhmad

28

gdb disassemble/rsper mostrare anche i byte sorgente e non elaborati

Con questo formato, si avvicina molto objdump -Sall'output:

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

Compila e disassembla

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

Smontaggio:

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.

Testato su Ubuntu 16.04, GDB 7.11.1.

objdump + soluzioni alternative awk

Stampa il paragrafo come indicato su: /unix/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that-has-the -testo

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

per esempio:

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

dà 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   

Durante l'utilizzo -S, non penso che ci sia un modo a prova di errore, poiché i commenti sul codice potrebbero contenere qualsiasi sequenza possibile ... Ma quanto segue funziona quasi sempre:

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

adattato da: Come selezionare le linee tra due pattern marker che possono verificarsi più volte con awk / sed

Risposte della mailing list

C'è un thread del 2010 sulla mailing list che dice che non è possibile: https://sourceware.org/ml/binutils/2010-04/msg00445.html

Oltre alla gdbsoluzione alternativa proposta da Tom, commentano anche un'altra (peggiore) soluzione alternativa alla compilazione con la -ffunction-sectionquale inserisce una funzione per sezione e poi scarica la sezione.

Nicolas Clifton gli ha fornito un WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html , probabilmente perché la soluzione GDB copre quel caso d'uso.


L'approccio gdb funziona bene su librerie condivise e file oggetto.
Tom Tromey

16

Disassembla una singola funzione utilizzando Objdump

Ho due soluzioni:

1. Basato sulla riga di comando

Questo metodo funziona perfettamente e in aggiunta uno semplice. Uso objdump con il -d bandiera e tubo attraverso awk . L'output smontato è simile a

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

Per cominciare, comincio con la descrizione dell'output di objdump. Una sezione o una funzione è separata da una riga vuota. Pertanto, cambiando FS (Field Separator) in newline e RS (Record Separator) in due volte newline ti consente di cercare facilmente la funzione consigliata, poiché è semplicemente da trovare all'interno del campo $ 1!

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

Ovviamente puoi sostituire main con qualsiasi altra funzione che desideri stampare.

2. Bash Script

Ho scritto un piccolo script bash per questo problema. Incollalo e copialo e salvalo come file 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

Cambia x-access e invocalo con ad esempio:

chmod +x dasm
./dasm test main

Questo è molto più veloce che invocare gdb con uno script. Inoltre, l'uso di objdump non carica le librerie in memoria ed è quindi più sicuro!


Vitaly Fadeev programmato un completamento automatico per questo script, che è davvero una bella caratteristica e velocizza la digitazione.

Lo script può essere trovato qui .


Sembra dipenda se objdumpo gdbè più veloce. Per un binario enorme (il libxul.so di Firefox) objdumprichiede un'eternità, l'ho cancellato dopo un'ora, mentre gdbrichiede meno di un minuto.
Simon

6

Se hai un binutils molto recente (2.32+), questo è molto semplice.

Il passaggio --disassemble=SYMBOLa objdump disassembla solo la funzione specificata. Non è necessario passare l'indirizzo di partenza e quello di arrivo.

LLVM objdump ha anche un'opzione simile ( --disassemble-symbols).


Grazie. Changelog per binutils 2.32, 2 febbraio 2019: lists.gnu.org/archive/html/info-gnu/2019-02/msg00000.html " . Opzione --disassemble di objdump può ora prendere un parametro, che specifica il simbolo di partenza per lo smontaggio smontaggio continuerà da questo simbolo fino al simbolo successivo o alla fine della funzione. "
osgx

5

Per semplificare l'uso di awk per analizzare l'output di objdump rispetto ad altre risposte:

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

4

Funziona proprio come la soluzione gdb (in quanto sposta gli offset verso zero) tranne per il fatto che non è in ritardo (esegue il lavoro in circa 5 ms sul mio PC mentre la soluzione gdb impiega circa 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 }'

Non posso fare il test adesso, ma non vedo l'ora che arrivi questo. Puoi approfondire un po 'l'aspetto dello “spostamento verso lo zero”? Non ho visto questo esplicito nelle risposte di gdb qui, e mi piacerebbe sentire un po 'di più su cosa sta effettivamente succedendo lì e perché.
MvG

Fondamentalmente fa sembrare che la funzione target (che è ciò che fa la prima awk) fosse l'unica funzione nel file oggetto, cioè, anche se la funzione inizia, diciamo 0x2d, il secondo awk la sposterà verso 0x00(sottraendo 0x2ddall'indirizzo di ogni istruzione), che è utile perché il codice assembly fa spesso riferimenti relativi all'inizio della funzione e se la funzione inizia da 0, non devi fare le sottrazioni nella tua testa. Il codice awk potrebbe essere migliore ma almeno fa il lavoro ed è abbastanza efficiente.
PSkocik

In retrospettiva sembra che la compilazione -ffunction-sectionssia un modo più semplice per assicurarsi che ogni funzione inizi da 0.
PSkocik

3

Completamento di Bash per ./dasm

Completare i nomi dei simboli per questa soluzione (versione D lang):

  • Digitando dasm teste quindi premendo TabTab, otterrai un elenco di tutte le funzioni.
  • Digitando dasm test me quindi premendo verranno mostrate TabTab tutte le funzioni che iniziano con m , o nel caso in cui esista una sola funzione, verrà completata automaticamente.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.