Показ дописів із міткою AIX. Показати всі дописи
Показ дописів із міткою AIX. Показати всі дописи

четвер, 13 травня 2010 р.

Універсальний тип даних для вказівника (C/C++ pointer)

Вирішував нещодавно цікаву задачу: як правильно здійснити приведення типу для вказівника типу (void *)? Якщо на 32-бітних системах розмір вказівника рівний розміру типу даних int, то в 64-бітних системах усе залежить від реалізації. Наприклад, тип int може бути як 32-бітним (Microsoft Win64, більшість UNIX-подібних систем), так і 64-бітним (якщо система використовує модель даних ILP64 або SILP64). Аналогічна ситуація і з типом long: він 64-бітний на більшості систем, проте є системи, де його розмірність рівна 32 біт. Детальніше про це можна прочитати тут:
http://en.wikipedia.org/wiki/64-bit#Specific_C-language_data_models.

Отже, задача звелася до того, що слід мати універсальний тип даних, розмірність якого завжди відповідає розмірності вказівника. Як виявилося, в UNIX-подібних системах існують спеціальні типи даних, спроектовані для операцій з вказівниками, такі як ptrdiff_t, intptr_t, uintptr_t.
Тип ptrdiff_t призначений зберігання результатів віднімання вказівників і є знаковим.
Тип intptr_t є його синонімом.
Тип uintptr_t є беззнаковим цілочисельним типом і здатен безпечно зберігати у собі вказівник незалежно від розрядності платформи.

Отже, для моєї задачі потрібен був тип uintptr_t. Слід було лише виявити, для якої платформи який заголовний файл (header) слід включити, щоб використати цей тип.
Ось список заголовних файлів відповідно для системи:
GNU/Linux - stdint.h;
Solaris - inttypes.h;
AIX - inttypes.h;
IRIX - додадковий заголовний файл не потрібен;
HP-UX - sys/wsio.h.

Відповідно, універсальний код для включення типу uintptr_t:

#if defined(__hpux)
#include <sys/wsio.h>
#else
# if defined(__sun) || defined(_AIX)
#include <inttypes.h>
# else
# ifndef __sgi
#include <stdint.h>
# endif
# endif
#endif

неділя, 20 грудня 2009 р.

Деякі проблеми лінкування динамічних бібліотек на AIX

На жаль, не зміг опублікувати це по свіжих слідах, тому спробую зробити це по пам'яті і старих записках.

Якось я майже два тижні витратив на вирішення проблеми, пов'язаної з лінкуванням динамічних бібліотек на IBM AIX 5.1.

Проблема полягала в тому, що створена динамічна бібліотека викликала падіння аплікації при спробі використати будь-яку функцію з бібліотеки, що означало, що бібліотека зібрана не так, як слід. Дослідження показало, що проблема була в тому, що на AIX слід явно експортувати всі необхідні символи, причому як від бібліотеки до програми, так і в зворотньому напрямку.

Граючись із різними опціями та читаючи документацію, я виявив, що зараз (починаючи з IBM AIX 5.1) замість цілого ряду опцій для включення створення динамічної бібліотеки, дозволу run-time лінкування, та ін., можна використати всього лише опцію -G, яка вкаже лінкеру на необхідність створення shared object з можливістю run-time лінкування. Також слід вказати опцію -bE:exportlist де "exportlist" -- назва файлу зі списком символів (класів та функцій) з бібліотеки, які мають бути викликані з головної аплікації.
Якщо ми використовуємо run-time лінкування, головна аплікація має бути зібрана з опцією -brtl. Для самої аплікації також слід вказати -bE:exportlist_app, де "exportlist_app" -- назва файлу з символами, які експортуються з аплікації, щоб бути видимими у бібліотеці.

У моєму випадку "exportlist" бібліотеки містив її функції та клас "theApplication" з іншої бібліотеки. Файл "exportlist_app" для аплікації містив лише клас "theApplication", інакше у бібліотеці символ "theApplication" з аплікації був недоступний. В результаті це символ у програмі був оголошений двічі -- один раз явно проініціалізований у головній аплікації, а інший -- із конструктором по замовчуванню в моїй бібліотеці, що приводило до падіння аплікації (хоча явного виклику конструктора чи створення об'єкта класу і не було, через недоступність об'єкта класу з аплікації, лінкер створював новий об'єкт у бібліотеці).

Узагальнюючи, ось процес створення динамічної бібліотеки на IBM AIX:
  1. Скомпілювати кожен вихідний файл в об'єктний файл, без лінкування. Наприклад:
    xlc -c foo.c -o foo.o
  2. Створити експортний файл, у якому перераховуються глобальні символи, які мають бути експортовані. Автоматичний спосіб генерує багато зайвого, тому я створював вручну, щось типу такого:
    function_one
    function_two
    ....
    class_one
    ....
  3. Використати опцію -G для створення динамічної бібліотеки зі згенерованих об'єктних файлів, щоб мати символи, які лінкуються до аплікації під час завантаження, і опцію лінкера -bE для вказання імені експортного файлу. (Також можна використати опцію -qmkshrobj для вказання пріоритету для C++ shared object; див. Initializing static objects in libraries (C++).) Наприклад:
    xlc -G -o libfoo.so foo1.o foo2.o -bE:exportlist
  4. Злінкувати бібліотеку з головною аплікацією, якщо бібліотека використовує run-time лінкування, додати опцію -brtl (а також -bE за потребою):
    xlc  -bE:main.exp -brtl -o myprogram main.c -Ldirectory -lfoo