2012-05-03 12 views
6

Nur dir etwas Kontext zu geben, ist hier, was ich versuche zu erreichen: Ich in einer gemeinsamen Objektdatei ein const char * bin Einbettung, um in dem eine Version Zeichenfolge haben .so-Datei selbst. Ich mache die Datenanalyse und diese Zeichenfolge ermöglicht es mir, die Daten zu informieren, welche Version der Software, die es produziert. Das alles funktioniert gut.Offset in nm Symbolwert?

Das Problem, das ich habe, wenn ich versuche, direkt die Zeichenfolge aus der .so-Bibliothek zu lesen. Ich habe versucht,

nm libSMPselection.so | grep _version_info 

zu verwenden und

000000000003d968 D __SMPselection_version_info 

das ist alles in Ordnung zu bekommen und wie erwartet (die char * ist _SMPselection_version_info genannt). Allerdings hätte ich erwartet, jetzt in der Lage zu sein, die Datei zu öffnen, zu 0x3d968 zu suchen und meine Zeichenfolge zu lesen, aber alles, was ich bekomme, ist Müll.

Wenn ich die .so-Datei öffnen und einfach für den Inhalt der Zeichenfolge suchen (Ich weiß, wie es geht), kann ich es an der Adresse 0x2e0b4 finden. Bei dieser Adresse ist es da, null terminiert und wie erwartet. (Ich verwende diese Methode für jetzt.)

Ich bin kein Informatiker. Könnte jemand mir bitte erklären, warum der durch nm gezeigt Symbolwert nicht korrekt ist, oder anders, was ist der Symbolwert, wenn es nicht die Adresse des Symbols ist?

(Übrigens ich auf einem Mac arbeite mit OSX 10.7)

Antwort

2

Niemand hat den einfachsten Weg vorgeschlagen: Tue eine Binärdatei, die deine lib dynamisch lädt (gib ihr den Namen in der Kommandozeile) und dlsym() für dein Symbol (oder kann es auch in der Kommandozeile bekommen) Zeiger zu string und druckt es auf stdout.

+1

Dies ist eine großartige Idee. Ich versuche es gerade jetzt. Es gibt nur ein Problem: Die Bibliotheken, die ich teste, haben eine ziemlich lange Kette von Abhängigkeiten von anderen Bibliotheken. Wenn ich versuche, mit dlopen zu laden, bekomme ich Symbol-nicht-gefundene Fehler. Der Versions-String, an dem ich interessiert bin, hat natürlich keine Abhängigkeiten. Wie mache ich dl ignorieren Abhängigkeiten? – Simon

+0

Ich habe überprüft. Das funktioniert großartig, wenn ich alle Abhängigkeiten geladen habe, was einer meiner beiden Anwendungsfälle ist. Danke für die Idee. – Simon

1

Unter Linux haben Sie den Befehl ‚Strings‘, die Ihnen helfen Strings aus Binärdateien extrahieren.

http://linux.about.com/library/cmd/blcmdl1_strings.htm

In HP-UX (und ich denke, auch in anderen Unix-Varianten) gibt es einen ähnlichen Befehl namens 'was'. Es extrahiert nur Zeichenfolgen, die mit "@ (#)" beginnen, aber wenn Sie den Inhalt der Zeichenfolge steuern, ist dies kein Problem.

+1

Wie wird ihm das helfen, den Inhalt eines bestimmten Symbols zu bekommen? – PlasmaHH

+0

"Was" ist nett, aber ich möchte wirklich, dass meine Saite mehrere Zeilen hat und was bei Zeilenumbrüchen aufhört. Der Befehl strings gibt den gesamten String aus, ohne mir mitzuteilen, wo meine eigene Zeichenfolge endet. Außerdem scheint es nur die gesamte Datei zu lesen, was genau ich mache. Es erscheint eleganter, wenn ich den Symboleintrag lesen und direkt zum String springen könnte. – Simon

5

Angenommen, es handelt sich um eine ELF oder eine ähnlich strukturierte Binärdatei, müssen Sie die Adresse berücksichtigen, in die die Daten geladen werden, die von den Dingen im ELF-Header beeinflusst wird.

Mit objdump -Fd auf Ihrer Binärdatei können Sie den Disassembler auch den genauen Dateioffset eines Symbols anzeigen lassen.

Mit objdump -x können Sie diese Loader-Adresse finden, normalerweise 0x400000 für Standard-Linux-Executables.

Das nächste, was Sie vorsichtig sein müssen, ist zu sehen, ob es eine indirekte Zeichenfolge ist, können Sie am einfachsten tun, indem Sie objdump -g verwenden. Wenn die Zeichenfolge als indirekte Zeichenfolge gefunden wird, finden Sie an der von ausgegebenen Position nicht die Zeichenfolge, sondern die Adresse. Von diesem müssen Sie die Loader-Adresse erneut subtrahieren.Lassen Sie mich Ihnen ein Beispiel für eine meiner Binärdateien:

objdump -Fd BIN | grep VersionString 
    45152f:  48 8b 1d 9a df 87 00 mov 0x87df9a(%rip),%rbx  # ccf4d0 <acVersionString> (File Offset: 0x8cf4d0) 

objdump -x BIN 
... 
LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**12 
... 

So betrachten wir 0x8cf4d0 in der Datei und finden in der Hexeditor:

008C:F4D0 D8 C1 89 00 00 00 00 00 01 00 00 00 FF FF FF FF 

So nehmen wir den 0x89C1D8 dort, subtrahieren 0x400000 und haben 0x49c1d8 und wenn wir uns in der Hexeditor suchen dort zu finden:

0049:C1D0 FF FF 7F 7F FF FF 7F FF 74 72 75 6E 6B 5F 38 30 
0049:C1E0 34 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

Welche "trunk_8043" bedeutet.

YMMV, vor allem, wenn es ein anderes Dateiformat ist, aber das ist der allgemeine Weg, wie diese Dinge strukturiert sind, mit vielen Warzen und Details, die für Sonderfälle abweichen.

+0

Ok, danke, ich fühle mich wie du es beantwortet hast. Was ich mir erhofft hatte, war, dass ich die Saite bekommen konnte, ohne die gesamte Datei zu scannen (oder sie zu zerlegen). Übrigens, meine Version von objdump hat nicht die Option -F (ich benutze GNU objdump 2.17.50.0.6-20.el5 20061020). – Simon

+0

@Simon: Das ist eine ziemlich alte Version von objdump (ich kann mich nicht einmal mehr erinnern, wie 2006 war). Sie können diesen Dateioffset selbst erstellen, indem Sie den gleichen '0x400000' Offset von dem '0xccf4d0' subtrahieren. Vielleicht gibt es auch ein Tool, das all diese Dinge für Sie erledigt, oder Sie könnten sich ein kleines Skript schreiben. – PlasmaHH

1

Warum sollten Sie erwarten, dass der von nm angezeigte Offset der .so Datei ist? .so Dateien sind nicht einfach Speicherbilder; Sie enthalten viele andere Informationen sowie eine mehr oder weniger komplizierte Format. Unter Unix (mindestens unter den meisten Unices) verwenden freigegebene Objekte das Elf-Format . Um die Informationen zu finden, müssen Sie die verschiedenen Felder in der Datei interpretieren, um zu finden, wo das gewünschte Symbol ist, in welchem ​​Segment und wo dieses Segment in der Datei beginnt. (Sie können sich wahrscheinlich eine Bibliothek, die sie zu lesen vereinfachen.)

Auch, wenn Sie richtig sind zu sagen, dass Sie haben eine char const* eingebettet, dh, dass Ihr Code so etwas wie enthalten:

char const* version = "..."; 

dann ist die Adresse oder der Versatz von version die Adresse oder der Versatz des Zeigers, nicht die Zeichenfolgendaten, auf die es gezeigt wird. Definieren Sie es als:

char const version[] = "..."; 

wird dies lösen.

Schließlich könnte die einfachste Lösung nur sicherstellen, dass die Zeichenfolge ein hoch identifizierbares Muster hat, und scannen Sie die gesamte Datei linear nach diesem Muster suchen.

+0

Das Scannen der gesamten Datei ist genau das, was ich mache. Es scheint einfach weniger elegant und ich möchte etwas lernen, also habe ich diese Frage gestellt. Die Deklaration des Arrays anstelle des Zeigers wird aus der Liste der Symbole entfernt, die nm anzeigt. – Simon

+1

@Simon Nun, es ist eleganter, die Datei korrekt zu analysieren, aber es ist auch viel mehr Arbeit. Was das Deklarieren des Arrays anstelle eines Zeigers betrifft, liegt der Grund für das Verschwinden von C++ darin, dass ein konstantes Objekt standardmäßig eine interne Verknüpfung aufweist. Wenn Sie es als 'extern char const version [] =" ... "deklarieren, wird dies nicht passieren. das 'extern' erzwingt eine externe Verknüpfung und die Initialisierung macht es zu einer Definition und nicht zu einer Deklaration. –

+0

Danke, natürlich habe ich die Verknüpfung vergessen! Mit dem 'extern'-Schlüsselwort erscheint die Zeichenfolge nun in der Symboltabelle und die Adresse, die ich von 'nm' erhalten habe, stimmt tatsächlich mit der Position des Stachels überein. Es funktioniert jetzt. Ich bin in der Lage, die Saite zu bekommen, indem ich nach der Adresse suche, die ich von 'nm' bekomme! – Simon