2010-01-18 37 views
32

Gần đây chúng tôi đã được yêu cầu gửi một phiên bản Linux của một trong các thư viện của chúng tôi, trước đây chúng tôi đã phát triển dưới Linux và được giao cho Windows nơi triển khai thư viện thường dễ dàng hơn nhiều. Vấn đề chúng tôi gặp phải là loại bỏ các biểu tượng được xuất khẩu xuống chỉ những biểu tượng trong giao diện tiếp xúc. Có ba lý do chính đáng để thực hiện điều nàyTách thư viện chia sẻ linux

  • Để bảo vệ các khía cạnh độc quyền của công nghệ khỏi tiếp xúc thông qua các ký hiệu đã xuất.
  • Để ngăn người dùng gặp sự cố với tên biểu tượng xung đột.
  • Để tăng tốc độ tải thư viện (ít nhất là tôi được thông báo).

Lấy một ví dụ đơn giản sau đó:

test.cpp

#include <cmath> 

float private_function(float f) 
{ 
    return std::abs(f); 
} 

extern "C" float public_function(float f) 
{ 
    return private_function(f); 
} 

biên soạn với (g ++ 4.3.2, ld 2.18.93.20081009)

g++ -shared -o libtest.so test.cpp -s 

và kiểm tra những biểu tượng với

nm -DC libtest.so 

cung cấp cho

  w _Jv_RegisterClasses 
0000047c T private_function(float) 
000004ba W std::abs(float) 
0000200c A __bss_start 
     w __cxa_finalize 
     w __gmon_start__ 
0000200c A _edata 
00002014 A _end 
00000508 T _fini 
00000358 T _init 
0000049b T public_function 

rõ ràng là không đủ. Vì vậy, tiếp theo chúng ta redeclare các chức năng công cộng như

extern "C" float __attribute__ ((visibility ("default"))) 
    public_function(float f) 

và biên dịch với

g++ -shared -o libtest.so test.cpp -s -fvisibility=hidden 

mang đến cho

  w _Jv_RegisterClasses 
0000047a W std::abs(float) 
0000200c A __bss_start 
     w __cxa_finalize 
     w __gmon_start__ 
0000200c A _edata 
00002014 A _end 
000004c8 T _fini 
00000320 T _init 
0000045b T public_function 

đó là tốt, trừ std :: rằng abs được tiếp xúc. Vấn đề hơn là khi chúng tôi bắt đầu liên kết trong các thư viện (tĩnh) khác ngoài tầm kiểm soát của chúng tôi, tất cả các ký hiệu chúng tôi sử dụng từ các thư viện đó được xuất. Bên cạnh đó, khi chúng ta bắt đầu sử dụng STL container:

#include <vector> 
struct private_struct 
{ 
    float f; 
}; 

void other_private_function() 
{ 
    std::vector<private_struct> v; 
} 

chúng tôi kết thúc với nhiều xuất khẩu bổ sung từ Thư viện C++

00000b30 W __gnu_cxx::new_allocator<private_struct>::deallocate(private_struct*, unsigned int) 
00000abe W __gnu_cxx::new_allocator<private_struct>::new_allocator() 
00000a90 W __gnu_cxx::new_allocator<private_struct>::~new_allocator() 
00000ac4 W std::allocator<private_struct>::allocator() 
00000a96 W std::allocator<private_struct>::~allocator() 
00000ad8 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::_Vector_impl() 
00000aaa W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::~_Vector_impl() 
00000b44 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_deallocate(private_struct*, unsigned int) 
00000a68 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_get_Tp_allocator() 
00000b08 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_base() 
00000b6e W std::_Vector_base<private_struct, std::allocator<private_struct> >::~_Vector_base() 
00000b1c W std::vector<private_struct, std::allocator<private_struct> >::vector() 
00000bb2 W std::vector<private_struct, std::allocator<private_struct> >::~vector() 

NB: Với optimisations trên bạn sẽ cần phải chắc chắn rằng các vector là thực sự được sử dụng để trình biên dịch không tối ưu hóa các biểu tượng không sử dụng.

Tôi tin rằng đồng nghiệp của tôi đã được quản lý để xây dựng một giải pháp ad-hoc liên quan đến các file phiên bản và sửa đổi các tiêu đề STL xuất hiện để làm việc, nhưng tôi muốn hỏi (!):

Có cách nào sạch thế nào để loại bỏ tất cả các ký hiệu không cần thiết (các biểu tượng IE không phải là một phần của chức năng thư viện được trưng ra) từ một thư viện được chia sẻ Linux? Tôi đã thử khá nhiều tùy chọn cho cả g ++ và ld với rất ít thành công vì vậy tôi muốn các câu trả lời được biết là hoạt động hơn là được tin tưởng.

Đặc biệt:

  • Biểu tượng từ (nguồn đóng) thư viện tĩnh không được xuất khẩu.
  • Ký hiệu từ thư viện chuẩn không được xuất.
  • Ký hiệu không công khai từ các tệp đối tượng không được xuất.

giao diện xuất khẩu của chúng tôi là C.

Tôi biết trong những câu hỏi tương tự khác trên SO:

nhưng h ave có rất ít thành công với câu trả lời.

+1

Trên liên kết tĩnh của thư viện hệ thống: Việc bạn làm điều đó là bất hợp pháp. Đó là, vì [(e)] (http://www.eglibc.org/) [GLIBC] (http://www.gnu.org/software/libc/) được cấp phép theo [LGPL] (http://opensource.org/licenses/LGPL-3.0) và vì giấy phép đó áp dụng cho tất cả mã sử dụng ngoại trừ nếu được liên kết động, bằng cách liên kết tĩnh bạn tạo mã của mình được bao gồm bởi LGPL và được yêu cầu cung cấp nguồn (cho bất kỳ ai bạn đã cung cấp nhị phân và họ yêu cầu các nguồn). Điều này không áp dụng cho libgcc và libstdC++, đặc biệt không áp dụng cho bất kỳ mã nào sử dụng API công khai, cho dù liên kết như thế nào. –

+0

Tôi nhận thức được điều này và không đề cập đến các biểu tượng từ glibc, tất cả các ký hiệu trên được tạo ra bởi mẫu instantiation từ thư viện chuẩn C++ và, bởi sự cần thiết, được tạo ra trong các tệp đối tượng của tôi. t trong thư viện!). –

Trả lời

6

Vì vậy, các giải pháp chúng tôi có bây giờ là như sau:

test.cpp

#include <cmath> 
#include <vector> 
#include <typeinfo> 

struct private_struct 
{ 
    float f; 
}; 

float private_function(float f) 
{ 
    return std::abs(f); 
} 

void other_private_function() 
{ 
    std::vector<private_struct> f(1); 
} 

extern "C" void __attribute__ ((visibility ("default"))) public_function2() 
{ 
    other_private_function(); 
} 

extern "C" float __attribute__ ((visibility ("default"))) public_function1(float f) 
{ 
    return private_function(f); 
} 

xuất khẩu.phiên bản

LIBTEST 
{ 
global: 
    public*; 
local: 
    *; 
}; 

biên soạn với

g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version 

cho

00000000 A LIBTEST 
     w _Jv_RegisterClasses 
     U _Unwind_Resume 
     U std::__throw_bad_alloc() 
     U operator delete(void*) 
     U operator new(unsigned int) 
     w __cxa_finalize 
     w __gmon_start__ 
     U __gxx_personality_v0 
000005db T public_function1 
00000676 T public_function2 

Đó là khá gần với những gì chúng tôi đang tìm kiếm. Có một vài gotchas mặc dù:

  • Chúng ta phải đảm bảo chúng tôi không sử dụng "xuất khẩu" tiền tố (trong ví dụ này đơn giản "công cộng", nhưng rõ ràng là một cái gì đó hữu ích hơn trong trường hợp của chúng tôi) trong mã nội bộ.
  • Nhiều tên biểu tượng vẫn kết thúc trong bảng chuỗi, dường như nằm xuống RTTI, -fno-rtti khiến chúng biến mất trong các thử nghiệm đơn giản của tôi, nhưng là một giải pháp khá hạt nhân.

Tôi rất sẵn lòng chấp nhận bất kỳ giải pháp nào tốt hơn bất kỳ ai đi kèm!

+0

Tôi chấp nhận giải pháp cuối cùng của chúng tôi, vì nó phù hợp nhất với nhu cầu của chúng tôi, nhưng vì lợi ích của bất kỳ ai khác trong cùng một tình huống muốn thêm rằng các câu trả lời khác là tất cả các giải pháp khả thi nếu tình huống của bạn khác với chúng tôi một chút! –

+0

Phương pháp này có ẩn các ký hiệu khỏi các thư viện tĩnh khác không? Tôi đang chạy vào chính xác cùng một vấn đề với rất nhiều cruft từ phụ thuộc bên ngoài khác nhau được xuất khẩu. –

+0

Dường như, có. Các biểu tượng duy nhất là xuất khẩu của chúng tôi và một số biểu tượng chúng tôi đã liên kết đến trong các thư viện C/C++. –

2

Nói chung, trên nhiều hệ thống Linux và Unix, câu trả lời ở đây là không có câu trả lời ở đây tại thời gian liên kết. nó khá cơ bản với cách thức hoạt động của ld.so.

Điều này dẫn đến một số giải pháp thay thế nhiều lao động. Ví dụ: chúng tôi đổi tên STL thành _STL thay vì std để tránh xung đột trên STL và chúng tôi sử dụng các không gian tên cao, thấp và ở giữa để tránh các ký hiệu của chúng tôi khỏi các xung đột có thể xảy ra với biểu tượng của người khác.

Dưới đây là một giải pháp bạn sẽ không yêu:

  1. Tạo một .so nhỏ với chỉ API tiếp xúc với bạn nó.
  2. Mở ứng dụng thực với dlopen và liên kết với dlsym.

Vì vậy, miễn là bạn không sử dụng RTLD_GLOBAL, bây giờ bạn đã hoàn thành cách điện nếu không bí mật cụ thể .. -Bymymbolic cũng có thể được mong muốn.

+0

Thật không may là ẩn các chi tiết của thuật toán của chúng tôi là một nửa vấn đề đối với chúng tôi. –

+0

Vâng, bạn có thể tham gia đổi tên biểu tượng bán buôn nếu bạn sử dụng phương pháp tiếp cận hai đối tượng được chia sẻ của tôi. @ joshperry của công cụ có thể làm công việc. – bmargulies

5

Việc bạn sử dụng thuộc tính hiển thị mặc định và -fvisibility = hidden sẽ được tăng cường với tính năng ẩn nội dung ẩn.

Bạn cũng nên quên cách cố gắng ẩn xuất stdlib, xem this GCC bug vì lý do.

Ngoài ra, nếu bạn có tất cả biểu tượng công khai của mình trong một tiêu đề cụ thể, bạn có thể bọc chúng trong #pragma GCC visibility push(default)#pragma GCC visibility pop thay vì sử dụng thuộc tính. Mặc dù nếu bạn đang tạo một thư viện nền tảng chéo, hãy xem Controlling Exported Symbols of Shared Libraries cho một kỹ thuật để thống nhất các cửa sổ của bạn DLL và chiến lược xuất khẩu DSO Linux.

+0

Cảm ơn bạn đã dành thời gian trả lời, các liên kết được thực hiện để đọc thú vị. 1. Chúng tôi phơi bày giao diện C thuần túy (để tương thích chủ yếu), tại sao chúng ta nên trình bày chi tiết về triển khai của chúng tôi? Chỉ vì chúng tôi * có thể * chia sẻ RTTI, vv trên các ranh giới thư viện không có nghĩa là chúng tôi * sẽ *. 2. Trong các ví dụ đơn giản của tôi -fvisibility-inlines-hidden không có sự khác biệt, tôi không tin rằng nó sẽ ảnh hưởng đến giao diện của chúng tôi (hoặc giải quyết vấn đề của chúng tôi) nhưng nó có thể hữu ích trong tương lai. 3.Rất tiếc, bài viết được tham chiếu không xuất hiện để cung cấp giải pháp cho bất kỳ vấn đề nào của chúng tôi (ngoài những gì chúng tôi có). –

+0

Tôi thấy ý bạn là gì bởi private_struct đang ở trong quá trình tạo véc tơ được xuất. Đồng nghiệp của bạn đang thay đổi những gì để làm cho các tiêu đề khiến chúng biến mất? – joshperry

4

Nếu bạn quấn lên phần tin của bạn trong một không gian tên vô danh thì không std::abs cũng không private_function có thể được nhìn thấy trong bảng biểu tượng:

namespace{ 
#include<cmath> 
    float private_function(float f) 
    { 
    return std::abs(f); 
    } 
} 
extern "C" float public_function(float f) 
{ 
     return private_function(f); 
} 

biên dịch (g ++ 4.3.3):

g++ -shared -o libtest.so test.cpp -s

kiểm tra:

# nm -DC libtest.so 
     w _Jv_RegisterClasses 
0000200c A __bss_start 
     w __cxa_finalize 
     w __gmon_start__ 
0000200c A _edata 
00002014 A _end 
000004a8 T _fini 
000002f4 T _init 
00000445 T public_function 
+1

Trong khi nó hoạt động cho một ví dụ đơn giản và là một cách tốt để ẩn các cấu trúc cục bộ, các không gian tên riêng sẽ không mở rộng quy mô cho toàn bộ dự án. –

5

Chỉ cần lưu ý rằng Ulrich Drepper đã viết một bài luận về (tất cả?) Các khía cạnh của writing shared libraries cho Linux/Unix, bao gồm kiểm soát các biểu tượng xuất khẩu trong số nhiều chủ đề khác.

Điều này rất tiện lợi trong việc làm rõ cách chỉ xuất các chức năng trên danh sách trắng từ một lib được chia sẻ.

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