2014-04-24 17 views
5

Chỉnh sửa: các nhận xét bên dưới câu trả lời được chấp nhận cho thấy rằng đó có thể là sự cố với trình tải động của Android.Thành phần lớp * * tĩnh trên thư viện động

Tôi có một tiêu đề cho một lớp mẫu với một thành viên tĩnh. Khi chạy, địa chỉ của thành viên tĩnh được sử dụng trong thư viện và trong mã máy khách. Mẫu được khởi tạo ngầm cả trong thư viện và trong mã máy khách. Nó hoạt động tốt trên Linux và OSX, biểu tượng được nhân bản nhưng được đánh dấu là "không độc nhất" như được hiển thị bởi nm (xem bên dưới). Tuy nhiên khi tôi biên dịch cho ARM (Android), biểu tượng được đánh dấu yếu trong cả DSO và tệp thực thi. Trình tải không thống nhất và biểu tượng được sao chép một cách hiệu quả vào thời gian chạy!

tôi đọc những: two instances of a static member, how could that be? Static template data members storage và đặc biệt là câu trả lời này: https://stackoverflow.com/a/2505528/2077394 và: http://gcc.gnu.org/wiki/Visibility

nhưng tôi vẫn còn một chút bối rối. Tôi hiểu rằng các thuộc tính cho khả năng hiển thị giúp tối ưu hóa, nhưng tôi nghĩ nó sẽ hoạt động theo mặc định. Tôi biết tiêu chuẩn C++ không quan tâm đến thư viện được chia sẻ, nhưng điều đó có nghĩa là việc sử dụng các thư viện chia sẻ phá vỡ tiêu chuẩn? (hoặc ít nhất việc thực hiện này không phải là tiêu chuẩn C++ phù hợp?) Tiền thưởng: làm thế nào tôi có thể sửa nó? (Và không sử dụng mẫu không phải là một câu trả lời có thể chấp nhận :))

Tiêu đề:

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

Và xây dựng , xem các định nghĩa ký hiệu:

sản xuất .so:

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

biên dịch và liên kết chính:

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

biểu tượng được đánh dấu là "độc đáo":

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

sản xuất .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 

biên dịch và liên kết chính

~/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 

ký hiệu yếu!

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

Chỉnh sửa, ghi chú cho bối cảnh: Tôi đã chơi với OOLua, thư viện giúp ràng buộc C++ để Lua và unittests của tôi đã thất bại khi tôi bắt đầu để nhắm mục tiêu Android. Tôi không "sở hữu" mã và tôi muốn thay đổi nó một cách sâu sắc.

Chỉnh sửa, để chạy nó trên Android:

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 

Trả lời

4

Android không hỗ trợ các ký hiệu duy nhất. Nó là một phần mở rộng GNU của định dạng ELF chỉ hoạt động với GLIBC 2.11 trở lên. Android không sử dụng GLIBC chút nào, nó sử dụng một thời gian chạy C khác gọi là Bionic.

(cập nhật) Nếu các biểu tượng yếu không làm việc cho bạn (cuối cập nhật) Tôi sợ bạn sẽ phải thay đổi mã như vậy mà nó không dựa vào dữ liệu tĩnh.

+0

Cảm ơn. Tôi có nên kết luận rằng chuỗi công cụ Android không hỗ trợ tiêu chuẩn C++ hoàn toàn miễn là bạn bắt đầu sử dụng thư viện được chia sẻ? – Joky

+0

"Chuỗi công cụ Android không hỗ trợ chuẩn C++ miễn là bạn bắt đầu sử dụng thư viện chia sẻ" --- thử nghiệm của riêng tôi với các ký hiệu yếu (trên Linux) cho thấy chúng thực sự làm việc cho dữ liệu tĩnh trên các thư viện được chia sẻ. Tức là, trình liên kết sẽ chỉ buộc một trong các ký hiệu yếu được sử dụng, các trường hợp khác sẽ bị bỏ qua. Bạn đã thử chạy chương trình của mình chưa? –

+0

Có, địa chỉ được in khác nhau (chỉ trên Android) – Joky

1

Có thể có một số cài đặt trình biên dịch/mối liên kết mà bạn có thể tinh chỉnh để cho phép này (có bạn nhìn vào các -fvisibility cờ?).

Có thể sửa đổi thuộc tính GCC có thể đáng thử (đặt rõ ràng __attribute__ ((visibility ("default"))) trên biến).

Không đó, cách giải quyết duy nhất tôi có thể đề xuất bao gồm: (tất cả đều có phần xấu xí):

  1. Rõ ràng nhanh chóng mọi hình thức của mẫu được tạo ra trong thư viện chia sẻ và cung cấp các initializers trong việc thực hiện của nó (không có trong tiêu đề). Điều này có thể có hoặc không hoạt động.
  2. Giống như (1) nhưng sử dụng hàm shim làm singlet myers cho biến được chia sẻ (ví dụ bên dưới).
  3. Phân bổ một biến trong bản đồ cho lớp dựa trên rtti (cũng có thể thất bại trên một ranh giới thư viện được chia sẻ).

ví dụ:

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

Các thực thi cũng sẽ phải cung cấp triển khai cho bất kỳ loại mà nó sử dụng để nhanh chóng các mẫu.

+0

Vâng tôi biết cách giải quyết khả thi và tôi sẽ làm điều đó nếu tôi thiết kế thư viện của riêng mình. Nhưng ở đây nó là không thể vì tôi không "sở hữu" mã và tôi muốn thay đổi nó sâu sắc. Tôi có vấn đề này với OOLUA. Bây giờ tôi tự hỏi nếu trường hợp thử nghiệm của tôi nên làm việc theo tiêu chuẩn hay không. Nếu nó hoạt động thì tôi có thể coi đó là lỗi của GCC! – Joky

Các vấn đề liên quan