При написании кода на C++ часто появляются вопросы о том, как он будет взаимодейтсвовать с внешним миром. Ответить на эти вопросы порой бывает нелегко, и причиной этому служит отсуствие описания ABI в стандарте C++. В докладе рассмотрим, что такое ABI, и как программисты C++ живут в отсутсвии стандарта на него.
4. Идеальный мир
Один бинарник - все аппаратные и
программные платформы
Реальный мир
Одна аппаратная и программная
платформа - несколько
бинарников
Платформа - совокупность аппаратного и программного обеспечения
(Например Intel x86+Windows, Intel x64+Linux)
4
5. Исходный код
Плюсы
• Простота и удобство
• Возможность модификации кода
библиотеки
• Возможность поддержать все
платформы
Минусы
• Разные диалекты C++
• Реализация алгоритмов открыта
• Нельзя использовать из других
языков программирования
5
6. Статическая библиотека
Плюсы
• Реализация алгоритмов скрыта
• Границы модуля четко
определены
Минусы
• Нельзя использовать из других
языков программирования
• Обновление библиотеки требует
перекомпиляции основной
программы
• Большой размер основной
программы
6
7. Динамическая библиотека
Плюсы
• Малый размер основной
программы
• Эффективное использование
ресурсов системы
• Можно использовать из
других языков
программирования
Минусы
• Требует глубоких знаний
механизмов работы ОС и
компилятора
7
8. RPC, COM, протоколы и т. п.
Плюсы
• Можно использовать из
других языков
программирования
• Можно использовать через
сеть
Минусы
• Сложно
• Дорого
• Медленно работает
8
15. Стандарты C++ ABI существуют
• Itanium C++ ABI
• Visual Studio C++ ABI
• Vendor specific C++ ABI (Intel, Embarcadero ...)
15
16. Пример: один компилятор C++
Продукт: Сложная система с большим количеством модулей
Условия: Для каждой платформы вся система компилируется одним
компилятором C++ с одинаковыми параметрами компиляции
Хотим получить: Возможность обновлять отдельные модули независимо от
других без необходимости перекомпиляции всей системы
16
17. Бинарная совместимость
Новую версию библиотеки можно использовать в системе без ее
перекомпиляции
Совместимость по исходному коду
Для использования новой библиотеки, систему нужно перекомпилировать,
но без изменений в исходном коде
17
18. Мы можем...
• Добавить новый класс
• Добавить новый невиртуальный метод или новый конструктор
• Добавить новый enum
• Добавить новый элемент в существующий enum
• Добавить новый статический член
• Удалить приватный невиртуальный метод
• Удалить приватный статический член класа
• ...
18
19. Мы не можем...
• Добавить новые данные в класс
• Удалить ранее экспортированные функции, методы, классы и т. п.
• Удалить виртуальный метод из класса
• Изменить иерархию классов: добавлять, удалять или менять местами
базовые классы
• Изменить шаблонные параметры: добавлять, удалять, менять местами
• Изменять сигнатуру экспортируемой функции: тип возвращаемого
аргументы, входные аргументы, константность, volatile и т. п.
• ...
19
20. Нарушение бинарной совместимости (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
21. Нарушение бинарной совместимости (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
22. Нарушение бинарной совместимости (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
23. Что может помочь
Зарезервированные поля:
struct S {
int a;
int b;
int c;
int reserverd_1;
int reserverd_2;
};
D-pointer (pimpl)
class A { // Exported
public:
A() { d = new APrivate; }
~A() { delete d; }
private:
class APrivate;
APrivate *d;
};
class APrivate { // Not exported
int a, b, c;
};
23
24. Пример: несколько компиляторов C++
Продукт: сложная система с большим количеством модулей
Условия: Разные модули могут компилироваться разными компиляторами
Хотим получить: Возможность обновлять отдельные модули независимо от
других без необходимости перекомпиляции всей системы
24
25. Декорирование имен
QString::replace(int, int, const QChar *, int);
GCC
_ZN7QString7replaceEiiPK5
QChari
Visual Studio
?replace@QString@@QAEA
AV0@HHPBVQChar@@H@
Z
Q126845: "Microsoft does not publish the algorithm its compilers use for name
decoration because it may change in the future."
25
26. Представление данных (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
27. Выравнивание членов структур по умолчанию
(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
28. Соглашение о вызове
• __cdecl
• __stdcall
• __pascal
• __fastcall
Одинаковые соглашения могут реализовываться
компилятора по-разному!
28
29. Модель памяти, исключения
• Каждый модуль должен управлять своей памятью
• Исключения не должны выбираться за пределы модуля
29
30. Макросы определения компилятора
Borland __BORLANDC__
Codeplay VectorC __VECTORC__
Digital Mars __DMC__
Gnu __GNUC__
Intel __INTEL_COMPILER
Microsoft _MSC_VER
Pathscale __PATHSCALE__
Symantec __SYMANTECC__
30
31. Макросы определения аппаратной платформы
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
32. Макросы определения программной платформы
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
33. COM, XPCOM
COM - component object model, спецификация бинарного
интерфейса от Microsoft.
XPCOM - cross-platform COM, спецификация бинарного
интерфейса от Mozilla для реализации браузера Firefox.
33
34. Как работает COM
class IUnknown {
virtual HRESULT QueryInterface (REFIID riid, void **ppvObject) = 0;
virtual ULONG AddRef () = 0;
virtual ULONG Release () = 0;
};
34
36. Использование импортированного класса из 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;
38. Пример: несколько языков программирования
Продукт: Сложная система с большим количеством модулей
Условия: Для реализации модулей используются разные языки
программирования
Хотим получить: Возможность обновлять отдельные модули независимо от
других без необходимости перекомпиляции всей системы. Возможность
использовать модули на C++ из других языков программирования.
38
39. Только Си ABI
• Никакого декорирования имен
• Только POD типы
• Явное указание выравнивания структур
• Явное указание соглашения о вызове
39
40. Проблемы с Си 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
45. Источники
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