Bagaimana cara membongkar satu fungsi menggunakan objdump?

91

Saya memasang biner di sistem saya, dan ingin melihat pembongkaran fungsi yang diberikan. Lebih disukai menggunakan objdump, tetapi solusi lain juga dapat diterima.

Dari pertanyaan ini saya telah belajar bahwa saya mungkin dapat membongkar sebagian kode jika saya hanya mengetahui alamat batasnya. Dari jawaban ini saya telah belajar bagaimana mengubah simbol debug saya yang terpisah kembali menjadi satu file.

Tetapi bahkan beroperasi pada file tunggal itu, dan bahkan membongkar semua kode (yaitu tanpa alamat start atau stop, tetapi -dparameter biasa ke objdump), saya masih tidak melihat simbol itu di mana pun. Yang masuk akal sejauh fungsi yang dimaksud adalah statis, jadi tidak diekspor. Namun demikian, valgrindakan melaporkan nama fungsi, sehingga harus disimpan di suatu tempat.

Melihat detail bagian debug, saya menemukan nama itu disebutkan di .debug_strbagian tersebut, tetapi saya tidak tahu alat yang dapat mengubahnya menjadi rentang alamat.

MvG
sumber
2
Catatan kecil: Jika sebuah fungsi ditandai static, itu mungkin dimasukkan oleh kompilator ke dalam situs panggilannya. Ini mungkin berarti sebenarnya tidak ada fungsi untuk dibongkar, per se . Jika Anda dapat melihat simbol untuk fungsi lain, tetapi tidak untuk fungsi yang Anda cari, ini adalah petunjuk kuat bahwa fungsi tersebut telah sebaris. Valgrind mungkin masih mereferensikan fungsi pre-inline asli karena informasi debugging file ELF menyimpan dari mana setiap instruksi berasal, bahkan jika instruksi dipindahkan ke tempat lain.
davidg
@davidg: benar, tetapi karena jawaban oleh Tom berhasil dalam kasus ini, tampaknya tidak demikian. Namun demikian, apakah Anda mengetahui cara untuk, misalnya, membuat anotasi kode assembly dengan informasi dari mana setiap instruksi berasal?
MvG
1
Senang mendengarnya! addr2lineakan menerima PC / IP dari stdindan mencetak baris kode sumber yang sesuai. Demikian pula, objdump -lakan mencampur objdump dengan baris sumber; meskipun untuk kode yang sangat dioptimalkan dengan inlining yang berat, hasil dari salah satu program tidak selalu sangat membantu.
davidg

Jawaban:

87

Saya akan menyarankan menggunakan gdb sebagai pendekatan yang paling sederhana. Anda bahkan dapat melakukannya sebagai satu baris, seperti:

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'
Tom Tromey
sumber
4
+1 fitur tidak berdokumen! -ex 'command'tidak masuk man gdb!? Namun sebenarnya tercantum dalam dokumen gdb . Juga untuk yang lain, hal-hal seperti /bin/lsmungkin dilucuti, jadi jika perintah yang sama persis tidak menampilkan apa-apa, coba objek lain! Bisa juga menentukan file / objek sebagai argumen bareword; misalnya,gdb -batch -ex 'disassemble main' /bin/ls
hoc_age
3
Halaman manual tidak pasti. Untuk waktu yang lama tidak benar-benar dipertahankan, tetapi sekarang saya pikir ini dihasilkan dari dokumen utama. Selain itu, "gdb --help" juga lebih lengkap sekarang.
Tom Tromey
7
gdb /bin/ls -batch -ex 'disassemble main'bekerja juga
stefanct
1
Jika Anda menggunakan column -ts$'\t'untuk memfilter keluaran GDB, Anda akan memiliki byte mentah dan kolom sumber selaras dengan baik. Juga, -ex 'set disassembly-flavor intel'sebelum -exs lain akan menghasilkan sintaks perakitan Intel.
Ruslan
Saya menelepon disassemble fnmenggunakan metode di atas. Tetapi tampaknya ketika ada beberapa fungsi dengan nama yang sama di file biner, hanya satu yang dibongkar. Apakah mungkin untuk membongkar semuanya atau saya harus membongkar berdasarkan alamat mentah?
TheAhmad
28

gdb disassemble/rsuntuk menunjukkan sumber dan byte mentah juga

Dengan format ini, itu menjadi sangat dekat dengan objdump -Skeluaran:

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

Kompilasi dan bongkar

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

Membongkar:

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.

Diuji pada Ubuntu 16.04, GDB 7.11.1.

objdump + solusi awk

Cetak paragraf seperti yang disebutkan di: /unix/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that-has-the -teks

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

misalnya:

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

memberikan hanya:

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   

Saat menggunakan -S, saya rasa tidak ada cara yang terbukti gagal, karena komentar kode dapat berisi urutan yang mungkin ... Tetapi yang berikut ini berfungsi hampir sepanjang waktu:

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

Diadaptasi dari: Bagaimana memilih garis antara dua pola penanda yang mungkin muncul beberapa kali dengan awk / sed

Balasan milis

Ada utas 2010 di milis yang mengatakan itu tidak mungkin: https://sourceware.org/ml/binutils/2010-04/msg00445.html

Selain gdbsolusi yang diusulkan oleh Tom, mereka juga mengomentari solusi lain (lebih buruk) dari kompilasi -ffunction-sectionyang menempatkan satu fungsi per bagian dan kemudian membuang bagian tersebut.

Nicolas Clifton memberikan WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html , kemungkinan karena solusi GDB mencakup kasus penggunaan tersebut.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
Pendekatan gdb berfungsi dengan baik pada pustaka bersama dan file objek.
Tom Tromey
16

Bongkar Satu Fungsi Tunggal menggunakan Objdump

Saya punya dua solusi:

1. Berbasis Commandline

Metode ini bekerja dengan sempurna dan tambahan yang sederhana. Saya menggunakan objdump dengan -d flag dan menyalurkannya melalui awk . Output yang dibongkar terlihat seperti

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

Untuk memulainya, saya mulai dengan deskripsi output objdump. Sebuah bagian atau fungsi dipisahkan oleh baris kosong. Oleh karena itu, mengubah FS (Pemisah Bidang) menjadi baris baru dan RS (Pemisah Rekaman) menjadi dua kali baris baru memungkinkan Anda dengan mudah mencari fungsi yang direkomendasikan, karena ini hanya untuk menemukan dalam bidang $ 1!

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

Tentu saja Anda dapat mengganti main dengan fungsi lain yang ingin Anda cetak.

2. Skrip Bash

Saya telah menulis skrip bash kecil untuk masalah ini. Tempel dan salin dan simpan sebagai misalnya 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

Ubah x-access dan panggil dengan misalnya:

chmod +x dasm
./dasm test main

Ini jauh lebih cepat daripada menjalankan gdb dengan sebuah skrip. Selain cara menggunakan objdump tidak akan memuat perpustakaan ke dalam memori dan karena itu lebih aman!


Vitaly Fadeev memprogram pelengkapan otomatis untuk skrip ini, yang merupakan fitur bagus dan mempercepat pengetikan.

Scriptnya dapat ditemukan di sini .

abu_bua
sumber
Sepertinya itu tergantung apakah objdumpatau gdblebih cepat. Untuk biner yang sangat besar (Firefox 'libxul.so) objdumpmembutuhkan waktu lama, saya membatalkannya setelah satu jam, sementara gdbmembutuhkan waktu kurang dari satu menit.
Simon
6

Jika Anda memiliki binutils terbaru (2.32+), ini sangat sederhana.

Meneruskan --disassemble=SYMBOLke objdump hanya akan membongkar fungsi yang ditentukan. Tidak perlu memasukkan alamat awal dan alamat akhir.

Objdump LLVM juga memiliki opsi serupa ( --disassemble-symbols).

Léo Lam
sumber
Terima kasih. Changelog untuk binutils 2.32, 02 Feb 2019: lists.gnu.org/archive/html/info-gnu/2019-02/msg00000.html " Opsi --disassemble Objdump sekarang dapat mengambil parameter, menentukan simbol awal untuk pembongkaran. Pembongkaran akan melanjutkan dari simbol ini ke simbol berikutnya atau akhir fungsi. "
osgx
5

Untuk menyederhanakan penggunaan awk untuk mengurai keluaran objdump relatif terhadap jawaban lain:

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

Ini berfungsi seperti solusi gdb (karena itu menggeser offset ke nol) kecuali itu tidak lamban (menyelesaikan pekerjaan dalam waktu sekitar 5ms di PC saya sedangkan solusi gdb membutuhkan waktu sekitar 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 }'
PSkocik
sumber
Saya tidak bisa menguji sekarang, tapi saya menantikan saat saya menyelesaikan ini. Bisakah Anda menjelaskan sedikit tentang aspek "pergeseran offset menuju nol"? Saya tidak melihat ini secara eksplisit dalam jawaban gdb di sini, dan saya ingin mendengar lebih banyak tentang apa yang sebenarnya terjadi di sana dan mengapa.
MvG
Ini pada dasarnya membuatnya tampak seolah-olah fungsi yang Anda targetkan (yang dilakukan oleh yang pertama awk) adalah satu-satunya fungsi dalam file objek, yaitu, bahkan jika fungsi dimulai pada, katakanlah 0x2d, awk kedua akan menggesernya ke arah 0x00(dengan mengurangi 0x2ddari alamat setiap instruksi), yang berguna karena kode assembly sering membuat referensi relatif terhadap awal fungsi dan jika fungsi dimulai dari 0, Anda tidak perlu melakukan pengurangan di kepala Anda. Kode awk bisa lebih baik tetapi setidaknya berfungsi dan cukup efisien.
PSkocik
Dalam retrospeksi, tampaknya kompilasi dengan -ffunction-sectionsadalah cara yang lebih mudah untuk memastikan setiap fungsi dimulai pada 0.
PSkocik
3

Penyelesaian pesta untuk ./dasm

Lengkapi nama simbol untuk solusi ini (versi D lang):

  • Dengan mengetik dasm testdan kemudian menekan TabTab, Anda akan mendapatkan daftar semua fungsi.
  • Dengan mengetik dasm test mdan kemudian menekan TabTab semua fungsi yang dimulai dengan m akan ditampilkan, atau jika hanya ada satu fungsi, maka akan diselesaikan secara otomatis.

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
Vitaly Fadeev
sumber