2014-04-24 15 views
5

Edytuj: komentarze poniżej zaakceptowanej odpowiedzi wskazują, że może to być problem z dynamicznym programem ładującym systemu Android.Statyczny * szablon * element członkowski w bibliotece dynamicznej

Mam nagłówek dla klasy szablonu ze statycznym członkiem. W środowisku wykonawczym adres statycznego elementu jest używany w bibliotece iw kodzie klienta. Szablon jest niejawnie utworzony zarówno w bibliotece, jak iw kodzie klienta. Działa dobrze na systemach Linux i OSX, symbol jest duplikowany, ale oznaczony jako "unikalny", jak pokazano za pomocą nm (patrz poniżej). Jednak gdy kompiluję dla ARM (Android), symbol jest oznaczony jako słaby zarówno w DSO, jak iw pliku wykonywalnym. Program ładujący nie unifikuje się, a symbol jest skutecznie duplikowany w czasie wykonywania!

czytam te: two instances of a static member, how could that be? Static template data members storage a zwłaszcza tę odpowiedź: https://stackoverflow.com/a/2505528/2077394 oraz: http://gcc.gnu.org/wiki/Visibility

ale nadal jestem trochę zdziwiony. Rozumiem, że atrybuty widoczności pomagają zoptymalizować, ale pomyślałem, że powinien działać domyślnie. Wiem, że standard C++ nie dba o bibliotekę współdzieloną, ale czy oznacza to, że używanie bibliotek współdzielonych łamie standard? (lub przynajmniej ta implementacja nie jest zgodna ze standardem C++?) Bonus: jak mogę to naprawić? (A nie przy użyciu szablonu nie jest dopuszczalne odpowiedź :))

Header:

template<class T> 
struct TemplatedClassWithStatic { 
    static int value; 
}; 
template<class T> 
int TemplatedClassWithStatic<T>::value = 0; 

shared.cpp:

#include "TemplateWithStatic.hpp" 
int *addressFromShared() { 
    return &TemplatedClassWithStatic<int>::value; 
} 

main.cpp:

#include "TemplateWithStatic.hpp" 
#include <cstdio> 

int *addressFromShared(); 
int main() { 
    printf("%p %p\n", addressFromShared(), &TemplatedClassWithStatic<int>::value); 
} 

i budynku , patrząc na definicje symboli:

wytwarzania .so:

g++-4.8 -shared src/shared.cpp -o libshared.so -I include/ -fPIC 

kompilowania i łączący główne:

g++-4.8 src/main.cpp -I include/ -lshared -L. 

Symbole są oznaczone jako "Unikalny":

nm -C -A *.so a.out | grep 'TemplatedClassWithStatic<int>::value' 
libshared.so:0000000000200a70 u TemplatedClassWithStatic<int>::value 
a.out:00000000006012b0 u TemplatedClassWithStatic<int>::value 

wytwarzania .so

~/project/android-ndk-r9/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++ -o libshared.so src/shared.cpp -I include/ --sysroot=/Users/amini/project/android-ndk-r9/platforms/android-14/arch-arm/ -shared 

kompilowanie i łączenie główne

~/project/android-ndk-r9/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++ src/main.cpp libshared.so -I include/ --sysroot=${HOME}/project/android-ndk-r9/platforms/android-14/arch-arm/ -I ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/include -I ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -I ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/include/backward -I ~/project/android-ndk-r9/platforms/android-14/arch-arm/usr/include ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/libgnustl_static.a -lgcc 

symbole są słabe!

nm -C -A *.so a.out | grep 'TemplatedClassWithStatic<int>::value' 
libshared.so:00002004 V TemplatedClassWithStatic<int>::value 
a.out:00068000 V TemplatedClassWithStatic<int>::value 

Edit, uwaga dla kontekstu: Grałem z OOLua, biblioteka pomaga wiązania C++ do Lua i moich unittests dopuściły kiedy zacząłem kierować Android. Nie "posiadam" kodu i wolę go raczej głęboko modyfikować.

Edit, aby go uruchomić na Androidzie:

adb push libshared.so data/local/tmp/ 
adb push a.out data/local/tmp/ 
adb shell "cd data/local/tmp/ ; LD_LIBRARY_PATH=./ ./a.out" 
0xb6fd7004 0xb004 

Odpowiedz

4

Android nie obsługuje unikalnych symboli. Jest to rozszerzenie GNU formatu ELF, które działa tylko z GLIBC 2.11 i nowszymi. Android wcale nie korzysta z GLIBC, wykorzystuje inne środowisko wykonawcze C o nazwie Bionic.

(aktualizacja) Jeśli słabe symbole nie pracują dla ciebie (aktualizacja koniec) Obawiam trzeba by zmodyfikować kod tak, że nie opierają się na danych statycznych.

+0

Dzięki. Czy powinienem stwierdzić, że toolchain Androida nie obsługuje w pełni standardu C++, o ile zaczniesz używać bibliotek współdzielonych? – Joky

+0

"Toolchain Android nie obsługuje w pełni standardu C++, o ile zaczniesz korzystać z bibliotek współdzielonych" - moje własne eksperymenty ze słabymi symbolami (w systemie Linux) sugerują, że faktycznie działają w przypadku danych statycznych w bibliotekach współdzielonych. Oznacza to, że linker wymusi użycie tylko jednego ze słabych symboli, inne wystąpienia zostaną zignorowane. Czy próbowałeś uruchomić swój program? –

+0

Tak, adres wydrukowany jest inny (tylko na Androidzie) – Joky

1

Być może niektóre ustawienia kompilator/linker, które można modyfikować oddzielnie, aby umożliwić to (pan spojrzał na flagi -fvisibility?).

Prawdopodobnie warto spróbować modyfikatora atrybutu GCC (jawnie ustawić __attribute__ ((visibility ("default"))) na zmiennej).

przeciwnym razie, jedyne obejścia mogę sugerować to: (wszystkie są nieco brzydki):

  1. Jawnie wystąpienia wszelkich form szablonu, które są tworzone w udostępnionej biblioteki i zapewnić inicjatorów w jego realizacji (nie w nagłówku). To może, ale nie musi działać.
  2. Podobnie jak (1), ale użyj funkcji shim jako singlet myers dla wspólnej zmiennej (przykład poniżej).
  3. Przyporządkuj zmienną w mapie dla klasy na podstawie rtti (która może również zawieść na granicy biblioteki współużytkowanej).

np.

template<class T> 
struct TemplatedClassWithStatic { 
    static int& getValue() { return TemplatedClassWithStatic_getValue((T const*)0); } 
}; 
// types used by the shared library.. can be forward declarations here but you run the risk of violating ODR. 
int& TemplatedClassWithStatic_getValue(TypeA*); 
int& TemplatedClassWithStatic_getValue(TypeB*); 
int& TemplatedClassWithStatic_getValue(TypeC*); 

shared.cpp

int& TemplatedClassWithStatic_getValue(TypeA*) { 
    static int v = 0; 
    return v; 
} 
int& TemplatedClassWithStatic_getValue(TypeB*) { 
    static int v = 0; 
    return v; 
} 
int& TemplatedClassWithStatic_getValue(TypeC*) { 
    static int v = 0; 
    return v; 
} 

wykonywalny będzie również musiał dostarczyć implementacje dla wszelkich rodzajów, że używa do instancji szablonu.

+0

Tak, znam możliwe obejście tego problemu i zrobiłbym to, gdybym zaprojektował własną bibliotekę. Ale tutaj nie jest to możliwe, ponieważ nie "posiadam" kodu i wolę go raczej głęboko modyfikować. Mam ten problem z OOLUA. Teraz zastanawiam się, czy mój przypadek testowy powinien działać zgodnie ze standardem czy nie. Jeśli to powinno działać, mogę uznać to za błąd GCC! – Joky

Powiązane problemy