2012-05-03 17 views
6

Tylko po to, aby podać ci kontekst, oto, co próbuję osiągnąć: Umieszczam znak const * w pliku obiektu współdzielonego, aby mieć ciąg wersji w samym pliku .so. Przeprowadzam analizę danych i ten ciąg pozwala mi przekazać dane, która wersja oprogramowania je wygenerowała. Wszystko działa dobrze.Przesunięcie w wartości symbolu nm?

Problem, który mam, to próba bezpośredniego odczytania ciągu znaków z biblioteki .so. Próbowałem użyć

nm libSMPselection.so | grep _version_info 

i dostać

000000000003d968 D __SMPselection_version_info 

to wszystko jest w porządku i zgodnie z oczekiwaniami (char * nazywa _SMPselection_version_info). Jednak oczekiwałbym, że teraz będę mógł otworzyć plik, postarać się 0x3d968 i zacząć czytać mój ciąg znaków, ale wszystko, co dostaję, to śmieci.

Kiedy otwieram plik .so i po prostu szukam zawartości ciągu (wiem jak to się zaczyna), mogę go znaleźć pod adresem 0x2e0b4. Pod tym adresem jest tam, zero zakończone i zgodnie z oczekiwaniami. (Używam tej metody teraz.)

Nie jestem informatykiem. Czy ktoś mógłby mi wyjaśnić, dlaczego wartość symbolu przedstawiona przez nm jest nieprawidłowa, lub inaczej, jaka jest wartość symbolu, jeśli nie jest to adres symbolu?

(Nawiasem mówiąc pracuję na komputerze Mac z OSX 10.7)

Odpowiedz

2

Nikt nie zasugerował najprostszy sposób: Zrób plik binarny, który dynamicznie ładuje twoją bibliotekę (podaj nazwę w wierszu poleceń) i robi dlsym() dla twojego symbolu (lub może to być również z linii poleceń) obsadź go , aby napisać wskaźnik i wypisać go na standardowe wyjście.

+1

To świetny pomysł. Próbuję tego teraz. Jest tylko jeden problem: biblioteki, które testuję, mają raczej długi łańcuch zależności od innych bibliotek. Jeśli spróbuję wczytać dlopena, otrzymam błędy Nie znaleziono symbolu. Łańcuch wersji, który mnie interesuje, oczywiście nie ma zależności. Jak utworzyć zależność dl, aby ignorować zależności? – Simon

+0

Sprawdziłem. Działa to świetnie, jeśli wszystkie obciążenia są załadowane, co jest jednym z moich dwóch przypadków użycia. Dzięki za pomysł. – Simon

1

Na Linuksie masz polecenie „Strings”, który pomaga wyodrębnić ciągów z plików binarnych.

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

W HPUX (i myślę, że w innych smakach Unix też) istnieje podobna komenda zwana 'co'. Wyodrębnia tylko ciągi rozpoczynające się od "@ (#)", ale jeśli kontrolujesz zawartość ciągu, nie stanowi to problemu.

+1

Jak to pomoże mu uzyskać zawartość określonego symbolu? – PlasmaHH

+0

"co" jest miłe, ale naprawdę chcę, aby mój ciąg był wieloma liniami, a co zatrzymywał się na linii nowej linii. Polecenie smings wypisuje cały ciąg bez informowania mnie, gdzie kończy się mój własny ciąg znaków. Wydaje się, że po prostu czytam cały plik, który jest dokładnie tym, co robię. Wydaje się bardziej elegancki, gdybym mógł odczytać wpis symbolu i przejść bezpośrednio do napisu. – Simon

5

Zakładając, że jest to plik ELF lub podobnie skonstruowany plik binarny, należy wziąć pod uwagę adres, pod którym załadowano elementy, na co wpływają rzeczy w nagłówku ELF.

Używając w systemie binarnym wartości , można uzyskać, że dezasembler również pokazuje dokładne przesunięcie pliku symbolu.

Używając objdump -x możesz znaleźć ten adres ładujący, zwykle 0x400000 dla standardowych plików wykonywalnych Linux.

Następną rzeczą, którą należy zachować ostrożność, jest sprawdzenie, czy jest to pośredni ciąg znaków, który można najłatwiej wykonać, korzystając z objdump -g. Kiedy łańcuch zostanie znaleziony jako łańcuch pośredni, na wyjściu pozycji przez objdump -Fd nie znajdziesz łańcucha, ale adres. Z tego należy ponownie odjąć adres programu ładującego.Pokażę wam przykład dla jednego z moich plików binarnych:

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 
... 

Więc patrzymy na 0x8cf4d0 w pliku i odnaleźć w edytor heksadecymalny:

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

Więc bierzemy tam 0x89C1D8, odejmowanie 0x400000 i mają 0x49c1d8 i kiedy patrzymy tam w edytor heksadecymalny znaleźć:

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 

co oznacza „trunk_8043”.

YMMV, zwłaszcza gdy ma jakiś inny format pliku, ale jest to ogólny sposób, w jaki są one uporządkowane, z dużą ilością brodawek i szczegółów, które różnią się w szczególnych przypadkach.

+0

OK, dzięki, czuję, że na to odpowiedziałeś. Miałem nadzieję, że uda mi się uzyskać ciąg bez zeskanowania całego pliku (lub jego złożenia). Tak przy okazji, moja wersja programu objdump nie ma opcji -F (używam GNU objdump 2.17.50.0.6-20.el5 20061020). – Simon

+0

@Simon: To dość stara wersja objdump (nie pamiętam już, jak wyglądał rok 2006). Możesz uzyskać ten plikoffset samodzielnie przez odjęcie tego samego przesunięcia '0x400000' od' 0xccf4d0'. Może jest też narzędzie, które robi wszystkie te rzeczy dla ciebie, lub możesz napisać sobie mały skrypt ... – PlasmaHH

1

Dlaczego można oczekiwać przesunięcia wyświetlanego przez nm jako przesunięcia w pliku w pliku .so? Pliki .so nie są po prostu obrazami pamięci; zawierają też wiele innych informacji i mają mniej lub bardziej skomplikowany format. Pod Unixem (przynajmniej w większości UNICE) obiekty współdzielone korzystają z formatu elfa . Aby znaleźć te informacje, należy zinterpretować różne pola w pliku, aby znaleźć miejsce, w którym znajduje się poszukiwany symbol: , w którym segmencie i gdzie ten segment zaczyna się w pliku. (prawdopodobnie można znaleźć bibliotekę, która uprości je czytać.)

Ponadto, jeśli masz rację, mówiąc, że już osadzony char const*, znaczy, że kod zawarty coś takiego:

char const* version = "..."; 

to adres lub przesunięcie version jest adresem lub przesunięciem wskaźnika , a nie danymi ciągów, na które wskazuje. Zdefiniowanie go jako:

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

rozwiąże to.

Wreszcie, najprostszym rozwiązaniem może być po prostu upewnienie się, że ciąg znaków ma jakiś wysoce rozpoznawalny wzór i przeskanowanie całego pliku w sposób liniowy w poszukiwaniu tego wzoru.

+0

Skanowanie całego pliku jest dokładnie tym, co robię. Po prostu wydaje się mniej elegancki i chcę się czegoś nauczyć, więc zadałem to pytanie. Deklarowanie tablicy zamiast wskaźnika powoduje zniknięcie z listy symboli wyświetlanych przez nm. – Simon

+1

@Simon Cóż, to jest bardziej eleganckie, aby poprawnie przeanalizować plik, ale to także dużo więcej pracy. Jeśli chodzi o deklarowanie tablicy zamiast wskaźnika, powodem jej zniknięcia jest powód subtelności C++: obiekt const ma domyślnie wewnętrzne powiązanie. Jeśli zadeklarujesz 'extern char const version [] = "..." ', to się nie stanie; "extern" wymusza połączenie zewnętrzne, a inicjalizacja czyni je definicją, a nie deklaracją. –

+0

Dzięki, oczywiście, zapomniałem o sprzężeniu! Używając słowa kluczowego 'extern', łańcuch pojawi się teraz w tabeli symboli, a adres, który otrzymałem z' nm' faktycznie odpowiada lokalizacji żądła, działa teraz. Jestem w stanie uzyskać ciąg znaków, szukając adresu, który otrzymałem od 'nm'! – Simon