Widoczność symboli i przestrzeń nazw

Eksperymentuję z widocznością symboli C++ na Linuksie i gcc. Wydaje się, że preferowanym sposobem jest użycie -fvisibility=hidden i eksportowanie używanych symboli jeden po drugim zgodnie z Visibility gcc strona wiki ( http://gcc.gnu.org/wiki/Visibility ). Mój problem polega na tym, że Wiele bibliotek nie radzi sobie z tym dobrze, zapominają o jawnym eksportowaniu symboli, co jest poważnym problemem. Po kilku naprawionych błędach nawet niektóre części dopalacza mogą być nadal dotknięte. Oczywiście te błędy powinny być stałe, ale do tego chciałbym użyć "bezpieczny" sposób, aby ukryć jak najwięcej symboli.

Wymyśliłem rozwiązanie: umieszczam wszystkie symbole w przestrzeni nazw i używam atrybutu ukrycia symboli i eksportuję publiczny interfejs, w ten sposób tylko moje symbole mogą być dotknięte.

Problem polega na tym, że otrzymuję komunikat ostrzegawczy, gdy kompiluję coś z tą biblioteką dla każdej klasy, której nie wyeksportowałem i używam w aplikacji jako pola klasy.

namespace MyDSO __attribute__ ((visibility ("hidden"))) {
  struct Foo {
    void bar() __attribute__ ((visibility ("default"))) {}
  };
}

struct Bar {
  MyDSO::Foo foo;
};

int main() {}

The komunikat ostrzegawczy może być odtworzony w tym małym przykładzie, ale oczywiście przestrzeń nazw powinna znajdować się w bibliotece innej klasy w aplikacji.

$ gcc-4.7.1 namespace.cpp -o namespace
namespace.cpp:7:8: warning: ‘Bar’ declared with greater visibility than the type of its field ‘Bar::foo’ [-Wattributes]

Ponieważ rozumiem widoczność symboli, ukrywanie przestrzeni nazw powinno mieć podobny efekt jak użycie -fvisibility=hidden, ale nigdy nie otrzymałem podobnych ostrzeżeń używając tego ostatniego. Widzę, że kiedy przekażę-fvisibility = hidden do aplikacji, klasa w aplikacji również będzie ukryta, więc nie otrzymam Ostrzeżenia. Ale kiedy nie przejdę opcji żaden z symboli w nagłówkach nie będzie wydawał się Ukryty dla kompilatora, więc nie otrzymam ponownie Ostrzeżenia.

Co proponuje ten Komunikat ostrzegawczy? Czy to poważny problem? W jakich sytuacjach może to spowodować jakiś problem? Czym różni się ukryta przestrzeń nazw od fvisibility = hidden?

Author: VargaD, 2013-03-22

2 answers

Zanim odpowiem na twoje konkretne pytanie, powinienem wspomnieć dla innych czytających, że stosowanie atrybutów widoczności symboli w przestrzeni nazw jest cechą specyficzną dla GCC. MSVC obsługuje tylko dllexport na klasach, funkcjach i zmiennych, a jeśli chcesz, aby Twój kod był przenośny, musisz tam dopasować MSVC. Jak wskazuje mój oryginalny przewodnik widoczności symboli GCC (Ten, do którego podlinkowałeś na stronie GCC), makro oparte na maszynach dllexport MSVC można łatwo ponownie wykorzystać, aby osiągnąć coś podobnego na GCC, więc przeniesienie do MSVC zapewni obsługę widoczności symboli "za darmo".

Jeśli chodzi o twój konkretny problem, GCC ma rację, aby cię ostrzec. Jeśli użytkownik zewnętrzny próbował użyć publicznego paska typu, prawie na pewno musi użyć wszystkiego wewnątrz paska, w tym Bar:: foo. Z tego samego powodu wszystkie prywatne funkcje członka, mimo że są prywatne, muszą być widoczne. Wiele osób jest tym zaskoczonych, argumentując, że prywatne symbole funkcji Członkowskich są z definicji niedostępne dla nikogo, ale zapominają o tym tylko dlatego, że programista nie ma dostępu, nie oznacza to, że kompilator nie potrzebuje } dostępu. Innymi słowy, prywatne funkcje Członkowskie są prywatne dla ciebie, ale nie dla kompilatora. Jeśli pojawiają się one w pliku nagłówkowym, oznacza to, że kompilator potrzebuje dostępu, nawet w anonimowej przestrzeni nazw (która jest anonimowa tylko dla programistów, a nie dla kompilatorów, którzy używają skrótu zawartości jako "prawdziwej" nazwy przestrzeni nazw).

Ukrywanie przestrzeni nazw ma bardzo różne efekty to-fvisibility=hidden. Dzieje się tak, ponieważ GCC wypluwa wiele symboli powyżej tych dla określonego typu, np. dla vtables, dla type_info itp. -fvisibility=hidden ukrywa rzeczy, których nie można ukryć w żaden sposób zalecony przez kompilator, i jest to rzecz absolutnie niezbędna do załadowania dwóch binariów do tego samego procesu ze zderzającymi się symbolami np. dwa współdzielone obiekty zbudowane przy użyciu różnych wersji Boost.

Doceniam twoje próby naprawienia problemów spowodowanych przez zepsute widoczność symboli w ELF i konsekwencje dla zepsutych binariów C++ i znacznie utraconej produktywności programisty. Nie da się ich jednak naprawić - są to błędy w samym ELFIE, który został zaprojektowany dla C, a nie c++. Jeśli to jakieś pocieszenie, napisałem wewnętrzny BlackBerry white paper kilka miesięcy temu na ten temat, ponieważ problemy z widocznością symboli Elf są tak samo problemem dla nas w BB10, jak dla każdej dużej korporacji ze znaczącym kodem C++. Więc może zobaczysz jakieś rozwiązania proponowane dla C++17, zwłaszcza jeśli implementacja modułów C++ robi duże postępy.

 18
Author: Niall Douglas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-08 14:02:04

Twoje użycie atrybutów widoczności wydaje mi się odwrotne; myślę, że lepiej byłoby użyć -fvisibility=hidden i dodać visibility" default " do przestrzeni nazw deklaracji biblioteki, ponieważ interfejs biblioteki prawdopodobnie ma domyślną widoczność lub nie możesz jej użyć z aplikacji. Jeśli nie chcesz modyfikować nagłówków biblioteki, możesz użyć # pragma GCC visibility push/pop wokół twoich # includes.

Również, jak mówi Niall, oznaczanie poszczególnych członków funkcje ponieważ domyślne nie działa, cały Typ Foo musi mieć domyślną widoczność, jeśli jest częścią interfejsu biblioteki.

 0
Author: Jason Merrill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-17 21:21:38