Objdump kullanarak tek bir işlevi nasıl sökebilirim?


91

Sistemime bir ikili yükledim ve belirli bir işlevin sökülmesine bakmak istiyorum. Tercihen kullanılması objdump, ancak başka çözümler de kabul edilebilir.

Gönderen bu sorulara ben sadece sınır adreslerini biliyorsanız ben kod sökmeye kısmına mümkün olabilir öğrendim. Gönderen Bu yanıt benim bölünmüş ayıklama sembolleri tek bir dosya haline geri çevirmek için öğrendik.

Ama o tek dosya üzerinde çalışırken ve hatta tüm kodu parçalara ayırırken bile (yani başlangıç ​​veya bitiş adresi olmadan, ancak düz -dparametresi olmadan objdump), hala o sembolü hiçbir yerde görmüyorum. Bu, söz konusu işlev statik olduğu sürece mantıklıdır, dolayısıyla dışa aktarılmaz. Yine de, valgrindişlev adını bildirecek, bu nedenle bir yerde saklanmalıdır.

Hata ayıklama bölümlerinin detaylarına baktığımda, .debug_strbölümde bahsedilen adı buluyorum , ancak bunu bir adres aralığına dönüştürebilecek bir araç bilmiyorum.


2
Küçük bir yan not: Bir işlev işaretlenmişse static, derleyici tarafından çağrı sitelerine satır içi olabilir. Bu, aslında sökülmesi gereken herhangi bir işlevin kendiliğinden olmayabileceği anlamına gelebilir . Diğer işlevler için sembolleri tespit edebiliyor, ancak aradığınız işlevi göremiyorsanız, bu, işlevin satır içi olduğuna dair güçlü bir ipucudur. ELF dosyası hata ayıklama bilgisi, talimatlar başka bir yere taşınsa bile, her bir talimatın nereden kaynaklandığını saklar.
davidg

@davidg: doğru, ancak Tom'un cevabı bu durumda işe yaradığından, durum böyle görünmüyor. Yine de, her bir talimatın nereden geldiğine dair bu bilgilerle montaj koduna açıklama eklemenin bir yolunu biliyor musunuz?
MvG

1
Duymak güzel! addr2linePC'leri / IP'leri kabul edecek stdinve ilgili kaynak kod satırlarını yazdıracaktır. Benzer şekilde, objdump -lobjdump ile kaynak satırlarını karıştırır; ancak yoğun satır içi ile yüksek düzeyde optimize edilmiş kod için, her iki programın da sonuçları her zaman özellikle yararlı değildir.
davidg

Yanıtlar:


87

Gdb'yi en basit yaklaşım olarak kullanmanızı öneririm. Bunu tek satırlık olarak bile yapabilirsiniz, örneğin:

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

4
+1 belgelenmemiş özellik! -ex 'command'içinde değil man gdb!? Ancak aslında gdb belgelerinde listelenmiştir . Ayrıca diğerleri için, bu tür şeyler /bin/lsçıkarılabilir, bu nedenle bu tam komut hiçbir şey göstermezse, başka bir nesne deneyin! Dosya / nesneyi bareword argümanı olarak da belirtebilir; örneğin,gdb -batch -ex 'disassemble main' /bin/ls
hoc_age

3
Man sayfası kesin değil. Uzun bir süredir gerçekten korunmadı, ancak şimdi ana belgelerden oluşturulduğunu düşünüyorum. Ayrıca "gdb --help" de artık daha tamamlandı.
Tom Tromey

7
gdb /bin/ls -batch -ex 'disassemble main'aynı zamanda çalışıyor
stefanct

1
column -ts$'\t'GDB çıktısını filtrelemek için kullanırsanız , ham baytları ve kaynak sütunlarını güzelce hizalanmış olursunuz. Ayrıca, -ex 'set disassembly-flavor intel'diğer -exe-postalardan önce Intel derleme sözdizimi ile sonuçlanır.
Ruslan

disassemble fnYukarıdaki yöntemi kullanarak aradım . Ancak, ikili dosyada aynı ada sahip birden fazla işlev olduğunda, yalnızca birinin demonte edildiği görülmektedir. Hepsini sökmek mümkün mü yoksa ham adrese göre demonte etmeliyim?
TheAhmad

28

gdb disassemble/rs, kaynak ve ham baytları da göstermek için

Bu formatla objdump -Sçıktıya gerçekten çok yaklaşıyor :

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

Derleyin ve sökün

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

Demontaj:

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.

Ubuntu 16.04, GDB 7.11.1'de test edilmiştir.

objdump + awk geçici çözümleri

Paragrafı şu adreste belirtildiği gibi yazdırın: /unix/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that-has-the -Metin

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

Örneğin:

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

sadece şunu verir:

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   

Kullanırken -Sçok kodun yorumlar olası diziyi içerebilir gibi tedbirli bir yol yoktur sanmıyorum ... Ama şu neredeyse her zaman çalışır:

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

şundan uyarlanmıştır: awk / sed ile birden çok kez ortaya çıkabilen iki işaret modeli arasındaki çizgiler nasıl seçilir

Posta listesi yanıtları

Posta listesinde bunun mümkün olmadığını söyleyen bir 2010 dizisi var: https://sourceware.org/ml/binutils/2010-04/msg00445.html

gdbTom tarafından önerilen geçici çözümün yanı sıra, -ffunction-sectionbölüm başına bir işlev koyan ve ardından bölümü atan başka bir (daha kötü) geçici çözüm hakkında da yorum yapıyorlar .

Nicolas Clifton buna bir WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html verdi , çünkü GDB geçici çözümü bu kullanım durumunu kapsıyor.


Gdb yaklaşımı, paylaşılan kitaplıklar ve nesne dosyalarında iyi çalışır.
Tom Tromey

16

Objdump kullanarak Tek Bir Fonksiyonu Sökme

İki çözümüm var:

1. Komut Satırı Tabanlı

Bu yöntem mükemmel çalışıyor ve ek olarak basit bir yöntem. Kullandığım objdump ile -d bayrak ve boru aracılığıyla awk . Demonte çıktı gibi görünüyor

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

Başlangıç ​​olarak, objdump çıktısının açıklamasıyla başlıyorum. Bir bölüm veya işlev , boş bir satırla ayrılır. Bu nedenle, FS'yi (Alan Ayırıcı) yeni satıra ve RS'yi (Kayıt Ayırıcı) iki kez satırsonu olarak değiştirmek, önerilen işlevi kolayca aramanızı sağlar, çünkü bu yalnızca $ 1 alanında bulmaktır!

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

Tabii ki yerini alabilir ana yazdırmak istediğiniz başka fonksiyonu ile.

2. Bash Komut Dosyası

Bu sayı için küçük bir bash betiği yazdım. Yapıştırın ve kopyalayın ve örneğin dasm dosyası olarak kaydedin .

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

X erişimini değiştirin ve örneğin şunu kullanarak çağırın:

chmod +x dasm
./dasm test main

Bu, gdb'yi bir komut dosyasıyla çağırmaktan çok daha hızlıdır. Bu arada yanında olacak objdump kullanarak değil belleğe kütüphane yükleme ve dolayısıyla güvenlidir!


Vitaly Fadeev , bu betiğe bir otomatik tamamlama programladı, bu gerçekten güzel bir özellik ve yazmayı hızlandırıyor.

Komut dosyası burada bulunabilir .


O takdirde bağlıdır görünüyor objdumpveya gdbdaha hızlıdır. Büyük bir ikili dosya için (Firefox 'libxul.so) objdumpsonsuza kadar sürer, bir saat sonra iptal ettim, gdbbir dakikadan az sürüyor.
Simon

6

Çok yeni bir binutils'iniz (2.32+) varsa, bu çok basittir.

--disassemble=SYMBOLObjdump'a geçmek yalnızca belirtilen işlevi parçalarına ayıracaktır. Başlangıç ​​adresini ve bitiş adresini iletmeye gerek yoktur.

LLVM objdump da benzer bir seçeneğe ( --disassemble-symbols) sahiptir.


Teşekkür ederim. Binutils 2.32, 02 Şubat 2019 için değişiklik günlüğü : lists.gnu.org/archive/html/info-gnu/2019-02/msg00000.html " Objdump'ın - demonte seçeneği artık sökme için başlangıç ​​sembolünü belirten bir parametre alabilir. bu sembolden bir sonraki sembole veya fonksiyonun sonuna kadar devam edecek. "
osgx

5

Objdump çıktısını diğer yanıtlara göre ayrıştırmak için awk kullanımını basitleştirmek için:

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

4

Bu, aynı gdb çözümü gibi çalışır (ofsetleri sıfıra kaydırır), ancak laggy değildir (işi bilgisayarımda yaklaşık 5 ms'de bitirirken, gdb çözümü yaklaşık 150 ms sürer):

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

Şu anda test edemiyorum, ama buna ne zaman ulaşacağımı dört gözle bekliyorum. “Ofseti sıfıra kaydırır” yönü hakkında biraz ayrıntı verebilir misiniz? Buradaki gdb cevaplarında bunu açıkça görmedim ve orada gerçekte neler olduğu ve neden olduğu hakkında biraz daha fazla şey duymak istiyorum.
MvG

Temelde, hedeflediğiniz işlevin (ilk awkyaptığı şey budur ) nesne dosyasındaki tek işlevmiş gibi görünmesini sağlar , yani işlev, örneğin 0x2d, ikinci awk, onu doğru kaydırır 0x00(çıkararak 0x2dher komutun adresinden), bu yararlıdır çünkü derleme kodu genellikle fonksiyonun başlangıcına göre referanslar yapar ve eğer fonksiyon 0'dan başlıyorsa, çıkarma işlemlerini kafanızda yapmanız gerekmez. Awk kodu daha iyi olabilirdi ama en azından işi yapıyor ve oldukça verimli.
PSkocik

Geriye dönüp bakıldığında -ffunction-sections, her bir işlevin 0'da başlamasını sağlamanın daha kolay bir yolu olduğu görülüyor.
PSkocik

3

İçin Bash tamamlama ./dasm

Bu çözüm için sembol adlarını tamamlayın (D dil sürümü):

  • Yazıp dasm testtuşuna bastığınızda TabTab, tüm işlevlerin bir listesini alacaksınız.
  • Yazarak dasm test mve sonra basıldığında TabTab , m ile başlayan tüm işlevler gösterilir veya yalnızca bir işlev varsa, otomatik olarak tamamlanır.

Dosya /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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.