Upload
corehardby
View
127
Download
2
Embed Size (px)
Citation preview
О некоторых вопросах бинарной совместимости в C++Гомон Сергей, [email protected]
Обо мне
Более 5 лет опыта разработки
на C++.
Основные сферы интересов:
● Защита информации
● Сетевая разработка
● Обработка изображений2
Способы распространения библиотек
• Исходный код
• Статическая библиотека
• Динамическая библиотека
• RPC, COM, протоколы и т. п.
3
Идеальный мир
Один бинарник - все аппаратные и программные платформы
Реальный мир
Одна аппаратная и программная платформа - несколько бинарников
Платформа - совокупность аппаратного и программного обеспечения (Например Intel x86+Windows, Intel x64+Linux)
4
Исходный код
Плюсы
• Простота и удобство
• Возможность модификации кода
библиотеки
• Возможность поддержать все
платформы
Минусы
• Разные диалекты C++
• Реализация алгоритмов открыта
• Нельзя использовать из других
языков программирования
5
Статическая библиотека
Плюсы
• Реализация алгоритмов скрыта
• Границы модуля четко
определены
Минусы
• Нельзя использовать из других
языков программирования
• Обновление библиотеки требует
перекомпиляции основной
программы
• Большой размер основной
программы 6
Динамическая библиотека
Плюсы
• Малый размер основной
программы
• Эффективное использование
ресурсов системы
• Можно использовать из
других языков
программирования
Минусы
• Требует глубоких знаний
механизмов работы ОС и
компилятора
7
RPC, COM, протоколы и т. п.
Плюсы
• Можно использовать из
других языков
программирования
• Можно использовать через
сеть
Минусы
• Сложно
• Дорого
• Медленно работает
8
Staticlib (C++ ABI)
Staticlib (C++ ABI)
Header-only lib
Dynlib (C ABI)
Web-service (C# pinvoke, Java jni, ...)
Internet
Client
Unit-tested
External API tested
App (C# pinvoke, Java
jni,...)
9
C# assembly COM object
CLI assembly wrapper (C++
ABI)
C++ App/Lib
Pascal dll (C ABI)
Client 10
API и ABI
API - application programming interface, интерфейс прикладного программирования (набор заголовочных файлов, предоставляемых библиотекой)
11
ABI - application binary interface, бинарный интерфейс приложения (скомпилированный API, ABI=API+compiler ABI)
Что описывает C++ ABI• Objects layout• Calling convention• Name mangling• Virtual function mechanism• Static object instantiation• C++ support function: set_uexpected(), operator new[]...• Exception handling• RTTI• Template instantiation• Memory debug facilities
12
ABI языка Си
• Objects alignment
• Name mangling
• Calling convention
• C support functions: atexit()
13
Стандарты Си и C++ не содержит описание ABI
14
Стандарты C++ ABI существуют
• Itanium C++ ABI
• Visual Studio C++ ABI
• Vendor specific C++ ABI (Intel, Embarcadero ...)
15
Пример: один компилятор C++
Продукт: Сложная система с большим количеством модулей
Условия: Для каждой платформы вся система компилируется одним компилятором C++ с одинаковыми параметрами компиляции
Хотим получить: Возможность обновлять отдельные модули независимо от других без необходимости перекомпиляции всей системы
16
Бинарная совместимость
Новую версию библиотеки можно использовать в системе без ее перекомпиляции
Совместимость по исходному коду
Для использования новой библиотеки, систему нужно перекомпилировать, но без изменений в исходном коде
17
Мы можем...• Добавить новый класс• Добавить новый невиртуальный метод или новый конструктор• Добавить новый enum• Добавить новый элемент в существующий enum• Добавить новый статический член• Удалить приватный невиртуальный метод• Удалить приватный статический член класа• ...
18
Мы не можем...• Добавить новые данные в класс• Удалить ранее экспортированные функции, методы, классы и т. п.• Удалить виртуальный метод из класса• Изменить иерархию классов: добавлять, удалять или менять местами
базовые классы• Изменить шаблонные параметры: добавлять, удалять, менять местами• Изменять сигнатуру экспортируемой функции: тип возвращаемого
аргументы, входные аргументы, константность, volatile и т. п.• ...
19
Нарушение бинарной совместимости (1)
enum Enum { one, two, max = 0xFFFFFFFF};
sizeof(Enum) is 4
enum Enum { one, two, three, max = 0xFFFFFFFF};
sizeof(Enum) is 4
enum Enum { one, two, max = 0xFFFFFFFF, three};
sizeof(Enum) is 8
20
Нарушение бинарной совместимости (2)
class A {public: A() {} ~A() {}};
sizeof(A) is 1
class A {public: A() {} ~A() {} void f() {}};
sizeof(A) is 1
class A {public: A() {} ~A() {} virtual void f() {}};
sizeof(A) is 4
21
Нарушение бинарной совместимости (3)
class A {public: A() {} ~A() {}};
sizeof(A) is 1
class A {public: A() {} ~A() {}private: static int _a;};
sizeof(A) is 1
class A {public: A() {} ~A() {}private: int _a;};
sizeof(A) is 4
22
Что может помочьЗарезервированные поля:
struct S { int a; int b; int c; int reserverd_1; int reserverd_2;};
D-pointer (pimpl)
class A { // Exportedpublic: A() { d = new APrivate; } ~A() { delete d; }private: class APrivate; APrivate *d;};
class APrivate { // Not exported int a, b, c;};
23
Пример: несколько компиляторов C++
Продукт: сложная система с большим количеством модулей
Условия: Разные модули могут компилироваться разными компиляторами
Хотим получить: Возможность обновлять отдельные модули независимо от других без необходимости перекомпиляции всей системы
24
Декорирование имен
QString::replace(int, int, const QChar *, int);
GCC_ZN7QString7replaceEiiPK5
QChari
Visual Studio?
replace@QString@@QAEAAV0@HHPBVQChar@@H@Z
Q126845: "Microsoft does not publish the algorithm its compilers use for name decoration because it may change in the future."
25
Представление данных (Windows x64)
Microsoft Intel GNU
wchar_t 2 2 4
long int 4 4 8
long double 8 16 16
Разные способы представления знаковых
x = 21 = 0001 0101-21 = 1110 1011 - two's complement (дополнительный код): 2^N - x-21 = 1110 1010 - one's complement (обратный код): all bits of x is flipped
26
Выравнивание членов структур по умолчанию (Windows x86)
Microsoft Intel Borland
2 byte int 2 2 1
4 byte int 4 4 1
8 byte int 8 8 1
float 4 4 1
double 8 8 1
27
Соглашение о вызове
• __cdecl
• __stdcall
• __pascal
• __fastcall
Одинаковые соглашения могут реализовываться
компилятора по-разному!28
Модель памяти, исключения
• Каждый модуль должен управлять своей памятью
• Исключения не должны выбираться за пределы модуля
29
Макросы определения компилятораBorland __BORLANDC__
Codeplay VectorC __VECTORC__
Digital Mars __DMC__
Gnu __GNUC__
Intel __INTEL_COMPILER
Microsoft _MSC_VER
Pathscale __PATHSCALE__
Symantec __SYMANTECC__
30
Макросы определения аппаратной платформыx86 _M_IX86, __INTEL__, __i386__
x86-64 _M_X64, __x86_64__, __amd64
IA64 __IA64__
DEC Alpha __ALPHA__
Motorola Power PC __POWERPC__
Any little endian __LITTLE_ENDIAN__
Any big endian __BIG_ENDIAN__
31
Макросы определения программной платформы
DOS 16 bit __MSDOS__, _MSDOS
Windows 16 bit _WIN16
Windows 32 bit _WIN32, __WINDOWS__
Windows 64 bit _WIN64, _WIN32
Linux 32 bit __unix__, __linux__
Linux 64 bit __unix__, __linux__, __LP64__, __amd64
BSD __unix__, __BSD__, __FREEBSD__
Mac OS __APPLE__, (__DARWIN__, __MACH__)
OS/2 __OS2__
32
COM, XPCOM
COM - component object model, спецификация бинарного
интерфейса от Microsoft.
XPCOM - cross-platform COM, спецификация бинарного
интерфейса от Mozilla для реализации браузера Firefox.
33
Как работает COM
class IUnknown { virtual HRESULT QueryInterface (REFIID riid, void **ppvObject) = 0; virtual ULONG AddRef () = 0; virtual ULONG Release () = 0;};
34
Экспортирование классов
class Interface {public: virtual void destroy() = 0; virtual void hello() = 0; virtual void world() = 0;protected: virtual ~Interface() {}};
class Class : public Interface {public: static Interface* create() { return new Class; } void destroy() override { delete this; } void hello() override { std::cout << "Hello" << std::endl; } virtual void world() override { std::cout << "World" << std::endl; }private: Class() {} ~Class() {}};
35
CH_DLL_FUNC(Interface*) create() { return Class::create(); }
Использование импортированного класса из C++
36
auto hDynLib = LoadLibraryW(L"DynLib.dll");
auto createFunc =
reinterpret_cast<CreateFunc>(GetProcAddress(hDynLib, "create"));
Interface *obj = createFunc();
obj->hello();
obj->world();
obj->destroy();
obj = nullptr;
Использование импортированного класса из C
37
struct VPtr;struct CInterface { VPtr *vptr;};
typedef void DestroyFunc(CInterface*);typedef void HelloFunc(CInterface*);typedef void WorldFunc(CInterface*);
struct VPtr { DestroyFunc *destroy; HelloFunc *hello; WorldFunc *world;};
HMODULE hDynLib = LoadLibraryW(L"DynLib.dll");CreateFunc createFunc =(CreateFunc)( GetProcAddress(hDynLib, "create"));CInterface *obj = (CInterface*)(createFunc());obj->vptr->hello(obj);obj->vptr->world(obj);obj->vptr->destroy(obj);obj = nullptr;
Пример: несколько языков программирования
Продукт: Сложная система с большим количеством модулей
Условия: Для реализации модулей используются разные языки программирования
Хотим получить: Возможность обновлять отдельные модули независимо от других без необходимости перекомпиляции всей системы. Возможность использовать модули на C++ из других языков программирования.
38
Только Си ABI
• Никакого декорирования имен
• Только POD типы
• Явное указание выравнивания структур
• Явное указание соглашения о вызове
39
Проблемы с Си ABIСколько занимает памяти?
struct S { long l; int i; short s; char ch;};
15, 16, 24, 32?
Какой символ экспортируется?
int func(int a, int b, int c);
func, _func, _func@12 ?
40
Макросы иногда помогают
41
#ifdef __cplusplus# define CH_EXTERNC extern "C"#else // __cplusplus# define CH_EXTERNC#endif // __cplusplus
#ifdef _WIN32# define CH_CDECL __cdecl# define CH_DLLEXPORT __declspec(dllexport)# define CH_DLLIMPORT __declspec(dllimport)# ifdef CH_BUILD_DLL# define CH_EXPORT CH_EXTERNC CH_DLLEXPORT# else // CH_BUILD_DLL# define CH_EXPORT CH_EXTERNC CH_DLLIMPORT# endif#else // _WIN32// ...#endif // _WIN32#define CH_DLL_FUNC(type) CH_EXPORT type CH_CDECL
Упаковка стурктур// DynLibPackPush.h
#ifdef _MSC_VER
#pragma pack(push, 1)
#else // _WIN32
// ...
#endif
// DynLibPackPop.h
#ifdef _MSC_VER
#pragma pack(pop)
#else // _WIN32
// ...
#endif
42
Экспорт функции
43
// DynLib.h
#pragma once
#include <cstdint>#include "DynLibMacro.h"#include "DynLibPackPush.h"
typedef uint32_t (*CreateFunc)(uint32_t,uint32_t);CH_DLL_FUNC(uint32_t) create(uint32_t,uint32_t);
#include "DynLibPackPop.h"
Спасибо за внимание!
44
Источники
1. COM in plain C - http://www.codeproject.com/Articles/13601/COM-in-plain-C
2. Export C++ classes from a DLL -
http://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL
3. Policies/Binary Compatibility Issues With C++ -
https://community.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B
4. Matthew Wilson - Imperfect C++
5. Agner Fog - Calling conventions for different C++ compilers and operating systems
45