web-dev-qa-db-fra.com

Comment désassembler une seule fonction en utilisant objdump?

J'ai un binaire installé sur mon système et j'aimerais regarder le désassemblage d'une fonction donnée. Utiliser de préférence objdump, mais d’autres solutions seraient également acceptables.

De this questions J'ai appris que je pourrais peut-être désassembler une partie du code si je ne connaissais que les adresses limites. De cette réponse j'ai appris à reconvertir mes symboles de débogage divisés en un seul fichier.

Mais même en opérant sur ce fichier unique, et même en désassemblant tout le code (c'est-à-dire sans adresse de démarrage ou d'arrêt, mais en clair -d paramètre sur objdump), je ne vois toujours pas ce symbole nulle part. Ce qui est logique dans la mesure où la fonction en question est statique, elle n'est donc pas exportée. Néanmoins, valgrind rapportera le nom de la fonction, il doit donc être stocké quelque part.

En regardant les détails des sections de débogage, je trouve ce nom mentionné dans le .debug_str _ section, mais je ne connais pas d’outil qui puisse en faire une plage d’adresses.

68
MvG

Je suggérerais d'utiliser gdb comme approche la plus simple. Vous pouvez même le faire en une ligne, comme:

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'
67
Tom Tromey

gdb disassemble/rs pour afficher également les octets source et bruts

Avec ce format, il se rapproche vraiment de objdump -S sortie:

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

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

Compiler et démonter

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

Démontage:

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.

Testé sur Ubuntu 16.04, GDB 7.11.1.

solutions de contournement objdump + awk

Imprimez le paragraphe comme indiqué à: https://unix.stackexchange.com/questions/82944/how-to-grep-for-text-in-a-file-and-display-the- paragraphe-that -has-the-text

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

par exemple.:

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

donne juste:

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   

Lorsque vous utilisez -S, Je ne pense pas qu'il existe un moyen infaillible, car les commentaires de code pourraient contenir une séquence possible ... Mais ce qui suit fonctionne presque tout le temps:

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

adapté de: Comment sélectionner des lignes entre deux motifs de marqueur pouvant se produire plusieurs fois avec awk/sed

Réponses à la liste de diffusion

Il y a un fil 2010 sur la liste de diffusion qui dit que c'est impossible: https://sourceware.org/ml/binutils/2010-04/msg00445.html

Outre la solution de contournement gdb proposée par Tom, ils commentent également une solution de contournement (pire) de la compilation avec -ffunction-section qui met une fonction par section puis décharge la section.

Nicolas Clifton lui a donné un WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html , probablement parce que la solution de contournement de la GDB couvre ce cas d'utilisation.

Démonter une seule fonction avec Objdump

J'ai deux solutions:

1. Basé sur la ligne de commande

Cette méthode fonctionne parfaitement et est également très courte. J'utilise objdump avec l'option - d et le conduit vers awk . La sortie désassemblée ressemble à

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

Une fonction ou est séparée par une ligne vide. Par conséquent, changer le [~ # ~] fs [~ # ~] (séparateur de champ) en nouvelle ligne et le [ ~ # ~] rs [~ # ~] (Séparateur d’enregistrement) permet de rechercher facilement deux fois votre fonction recommandée, puisqu’il s’agit simplement de la trouver dans le champ $ 1!

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

Bien sûr, vous pouvez remplacer main par n'importe quelle fonction que vous voulez afficher.

2. Script Bash

J'ai écrit un petit script bash pour ce numéro. Copiez-le simplement et enregistrez-le, par exemple. dasm fichier.

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

Changez le x-access et appelez-le avec, par exemple:

chmod +x dasm
./dasm test main

C'est beaucoup plus rapide que d'appeler gdb avec un script. En plus d'utiliser objdump , pas ne chargera les librairies en mémoire et sera donc plus sûr!


Vitaly Fadeev a programmé l'auto-complétion pour ce script, ce qui est vraiment une fonctionnalité intéressante et accélère la frappe.

Le script peut être trouvé ici .

11
abu_bua

Cela fonctionne comme la solution gdb (en ce sens que les décalages sont décalés vers zéro), sauf que ce n'est pas lent (le travail est fait en 5 ms environ sur mon PC, alors que la solution gdb prend environ 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 }'
4
PSkocik

Pour simplifier l'utilisation de awk pour analyser la sortie d'objdump par rapport à d'autres réponses:

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

Achèvement Bash pour ./dasm

Noms de symbole complets à cette solution (version D lang):

  • En tappant dasm test puis en appuyant sur TabTab, vous obtiendrez une liste de toutes les fonctions.
  • En tappant dasm test m puis en appuyant sur TabTab toutes les fonctions commençant par m seront affichées ou, si une seule fonction existe, elle sera complétée automatiquement.

Fichier /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
2
Vitaly Fadeev