Итак, есть проект на Си/Vala, который собирается под 64bit Windows с помощью GCC (MinGW). Есть желание использовать в проекте библиотеку Intel Math Kernel Library (Intel MKL), но нет желания собирать все это дело с помощью Visual Studio.
Если попробуем просто слинковать lib и dll-файлы из MKL со своим проектом (передав их линковщику через gcc -l), то увидим ошибку вида:
Как видно, линковщик не может найти символы __GSHandlerCheck и __security_check_cookie.
На sourceforge.net есть обсуждение, где приведены вышеобозначенные ошибки и даже есть идеи, в какую сторону копать.
Я же расскажу, что делать, по шагам:
Если попробуем просто слинковать lib и dll-файлы из MKL со своим проектом (передав их линковщику через gcc -l), то увидим ошибку вида:
... C:\Views\dockd_headers\lip__mgls_nt\mgls_nt\src\release\mgls64.lib(./x64/release/lpclsdll.obj):(.text[?messageBox@@YAXPEAD@...]+0xcb): undefined reference to `__security_check_cookie' C:\Views\dockd_headers\lip__mgls_nt\mgls_nt\src\release\mgls64.lib(./x64/release/lpclsdll.obj):(.xdata[$unwind$?messageBox@@YAXPEAD@...]+0xc): undefined reference to `__GSHandlerCheck' ...
Как видно, линковщик не может найти символы __GSHandlerCheck и __security_check_cookie.
На sourceforge.net есть обсуждение, где приведены вышеобозначенные ошибки и даже есть идеи, в какую сторону копать.
Я же расскажу, что делать, по шагам:
- Искомые символы можно найти в библиотеках libcmt.lib и BufferOverflowU.lib соответственно. В свою очередь библиотеки найти можно в составе различных девелоперских продуктов от Microsoft, в том числе бесплатных. Я взял WinDDK 7600.16385.1.
- Просто так слинковать их со своим проектом опять же не получится, т.к. libcmt.lib библиотека жирная и будет конфликтовать с внутренностями mingw:
... C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-mingw_helpers.o):mingw_helpers.c:(.text+0x0): multiple definition of `_decode_pointer' C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/startup/mt/objfre/amd64/tidtable.obj):(.text[_decode_pointer]+0x0): first defined here C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-mingw_helpers.o): In function `_encode_pointer': C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/mingw_helpers.c:26: multiple definition of `_encode_pointer' C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/startup/mt/objfre/amd64/tidtable.obj):(.text[_encode_pointer]+0x0): first defined here C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-pesect.o): In function `_ValidateImageBase': C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/pesect.c:31: multiple definition of `_ValidateImageBase' C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/misc/mt/objfre/amd64/pesect.obj):(.text[_ValidateImageBase]+0x0): first defined here C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-pesect.o): In function `_FindPESection': C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/pesect.c:51: multiple definition of `_FindPESection' C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/misc/mt/objfre/amd64/pesect.obj):(.text[_FindPESection]+0x0): first defined here C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-pesect.o): In function `_IsNonwritableInCurrentImage': C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/pesect.c:173: multiple definition of `_IsNonwritableInCurrentImage' C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/misc/mt/objfre/amd64/pesect.obj):(.text[_IsNonwritableInCurrentImage]+0x0): first defined here C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmsvcrt.a(dyyns00201.o):(.text+0x0): multiple definition of `_errno' C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/dos/mt/objfre/amd64/dosmap.obj):(.text[_errno]+0x0): first defined here ...
- Решим вопрос, взяв из libcmt.lib только нужный нам obj-файл. Называется он gshandler.obj, что логично. Для извлечения его из lib-файла нам все же понадобится Visual Studio (я использовал старенькую 2005-ю).
- Открываем Visual Studio Command Prompt.
- Получаем список obj-файлов в libcmt.lib с помощью утилиты lib.exe:
lib /list libcmt.lib > libcmt.txt
- В получившемся libcmt.txt ищем полный путь к gshandler.obj, у меня он выглядел так:
d:\5359.obj.amd64fre\minkernel\crts\crtw32\misc\mt\objfre\amd64\gshandler.obj
- Теперь можем извлечь его из lib-файла:
lib /extract:d:\5359.obj.amd64fre\minkernel\crts\crtw32\misc\mt\objfre\amd64\gshandler.obj libcmt.lib
- Мне показалось удобным и стройным завернуть извлеченный obj-файл в персональный lib-файл:
lib /out:gshandler.lib gshandler.obj
- Готово! Линковка проекта someproj с базовым набором dll и lib-файлов MKL в CMake-файле выглядит примерно так:
set(MKL_LIBRARIES mkl_core mkl_avx mkl_sequential mkl_intel_lp64_dll BufferOverflowU gshandler) target_link_libraries(someproj ${MKL_LIBRARIES})
В предыдущем посте «Клонирование GObject» я рассказывал про идею некоего обобщенного кода, не зависимого от конкретной задачи,
который бы сохранял объекты Gobject в файлы (типы объектов и значения
свойств) и потом воссоздавал последовательность объектов по этим файлам. Предполагалось сохранять в текстовый файл имена классов и порядок объектов, а также имена свойств объектов и их значения.
Идея красивая, но здесь мы сталкиваемся с одним ограничением системы типов GObject (ограничением вполне естественным, т.к. написан GObjecct на Си и вся поддержка ООП реализована на уровне библиотеки): чтобы создать экземпляр класса по имени класса нужно, чтобы этот класс уже был зарегистрирован в системе типов GObject. Т.е. для типа FooBar должен быть заранее вызван метод foo_bar_get_type непосредственно или опосредованно (например, через метод создания вроде foo_bar_new).
Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type (см. тему на stack overflow) и вызова этого метода.
UPDATE: Найден довольно изящный и портабельный способ поиска метода foo_bar_get_type, см. Создание объектов GObject по имени типа. Часть 2.
Пример на Vala ниже иллюстрирует описанную проблему. Мы можем создать объект типа Dog по имени, т.к. выше уже создан объект типа Dog и класс зарегистрирован в системе типов. Создание объекта типа Cow же завершается неудачей (см. вывод программы после исходного кода).
Идея красивая, но здесь мы сталкиваемся с одним ограничением системы типов GObject (ограничением вполне естественным, т.к. написан GObjecct на Си и вся поддержка ООП реализована на уровне библиотеки): чтобы создать экземпляр класса по имени класса нужно, чтобы этот класс уже был зарегистрирован в системе типов GObject. Т.е. для типа FooBar должен быть заранее вызван метод foo_bar_get_type непосредственно или опосредованно (например, через метод создания вроде foo_bar_new).
Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type (см. тему на stack overflow) и вызова этого метода.
UPDATE: Найден довольно изящный и портабельный способ поиска метода foo_bar_get_type, см. Создание объектов GObject по имени типа. Часть 2.
Пример на Vala ниже иллюстрирует описанную проблему. Мы можем создать объект типа Dog по имени, т.к. выше уже создан объект типа Dog и класс зарегистрирован в системе типов. Создание объекта типа Cow же завершается неудачей (см. вывод программы после исходного кода).
public abstract class Animal : Object { public abstract string to_string(); } public class Cow : Animal { public override string to_string() { return "I'm a cow!"; } } public class Dog : Animal { public override string to_string() { return "I'm a dog!"; } } int main() { Animal a; Type type; a = new Dog(); //< Явно создаем Dog, здесь класс и регистрируется в системе типов. print("1) %s\n", a.to_string()); if((type = Type.from_name("Dog")) != 0) { a = Object.new(type) as Animal; print("2) %s\n", a.to_string()); } else print("2) Failed to get type!\n"); if((type = Type.from_name("Cow")) != 0) { a = Object.new(type) as Animal; print("3) %s\n", a.to_string()); } else print("3) Failed to get type!\n"); return 0; }Вывод программы:
1) I'm a dog!
2) I'm a dog!
3) Failed to get type!
В одной из программ на работе используется ряд самописных Gtk-виджетов для настройки фильтров изображения. Виджеты лежат в Box'е, добавляются, удаляются и настраиваются пользователем. У каждого типа виджетов свой набор настроек. Все настройки виджеты хранят в своих свойствах.
Чтобы сохранять после перезапуска программы список фильтров и их параметры, была идея сделать некий обобщенны код, не зависимый от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам.
В качестве "proof of concept" был написан код, клонирующий объект Gobject. Функция g_object_clone возвращает копию объекта, переданного ей в качестве параметра src. Код основан на примере со StackOverflow. Обращаю внимание, что полноценно копируются только простые свойства, для boxed-типов копируется указатель с инкрементированием счетчика ссылок.
Код gobject_clone.c:
Чтобы сохранять после перезапуска программы список фильтров и их параметры, была идея сделать некий обобщенны код, не зависимый от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам.
В качестве "proof of concept" был написан код, клонирующий объект Gobject. Функция g_object_clone возвращает копию объекта, переданного ей в качестве параметра src. Код основан на примере со StackOverflow. Обращаю внимание, что полноценно копируются только простые свойства, для boxed-типов копируется указатель с инкрементированием счетчика ссылок.
Код gobject_clone.c:
// gcc `pkg-config --libs --cflags glib-2.0 gobject-2.0` gobject_clone.c point.c -o gobject_clone #include <glib-object.h> #include "point.h" #include <stdio.h> #include <string.h> //< For strcmp.В качестве объектов в примере взят простенький класс Point с гитхаба за авторством Jacob Gelbman. Код point.h:// See http://stackoverflow.com/questions/3003655/is-there-a-good-way-to-copy-a-gtk-widget?answertab=votes#tab-top static GObject *g_object_clone(GObject *src) { GObject *dst; GParameter *params; GParamSpec **specs; guint n, n_specs, n_params; specs = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), &n_specs); params = g_new0(GParameter, n_specs); n_params = 0; for (n = 0; n < n_specs; ++n) if((specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) { params[n_params].name = g_intern_string(specs[n]->name); g_value_init(¶ms[n_params].value, specs[n]->value_type); g_object_get_property(src, specs[n]->name, ¶ms[n_params].value); ++n_params; } dst = g_object_newv(G_TYPE_FROM_INSTANCE(src), n_params, params); g_free(specs); g_free(params); return dst; } // Print, clear, print again. static void psp (Point *p) { g_print("OBJECT %p\n", p); g_print(" Before clearing:\n"); point_print(p); g_signal_emit_by_name(p, "clear"); g_print(" After clearing:\n"); point_print(p); g_print(" ...\n"); } int main(int argc, char **argv) { g_type_init(); GArray *array = g_array_new(FALSE, FALSE, sizeof(gint)); gint val1 = 33, val2 = 44; g_array_append_val(array, val1); g_array_append_val(array, val2); g_array_append_val(array, val1); GObject *p1 = g_object_new(TYPE_POINT, "x", 5, "y", 10, "a", array, NULL); GObject *p2 = g_object_clone(p1); //< Note: garray uses ref/unref for copy/free. psp(POINT(p1)); g_print("Appending value '%d' to array.\n", val2); g_print(" ...\n"); g_array_append_val(array, val2); psp(POINT(p2)); g_clear_object(&p1); g_clear_object(&p2); return 0; }
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance #ifndef __POINT_H__ #define __POINT_H__ #include <glib.h> #include <glib-object.h>Код point.c:#define TYPE_POINT (point_get_type ()) #define POINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_POINT, Point)) #define POINT_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), TYPE_POINT, PointClass)) #define IS_POINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_POINT)) #define IS_POINT_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), TYPE_POINT)) #define POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_POINT, PointClass)) typedef struct _PointClass PointClass; typedef struct _Point Point; struct _PointClass { GObjectClass parent; void (*clear) (Point *self); }; struct _Point { GObject parent; gint x; gint y; GArray *array; }; GType point_get_type(void); void point_print(Point *self); #endif /*__POINT_H__*/
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance #include <glib-object.h> #include "point.h"G_DEFINE_TYPE(Point, point, G_TYPE_OBJECT); enum { PROP_0, PROP_ARRAY, PROP_X, PROP_Y }; enum { CLEAR, LAST_SIGNAL }; static guint point_signals[LAST_SIGNAL] = {0}; void point_print (Point *self) { g_print(" x: %d, y: %d\n", self->x, self->y); g_print(" ar:"); if(self->array) { guint i; for(i = 0; i < self->array->len; i++) g_print(" %d", g_array_index(self->array, gint, i)); } else g_print("null"); g_print("\n"); } static void point_clear (Point *self) { g_object_set(self, "x", 0, "y", 0, "a", NULL, NULL); } static void point_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { Point *point = POINT(obj); switch (prop_id) { case PROP_ARRAY: g_value_set_boxed(value, point->array); break; case PROP_X: g_value_set_int(value, point->x); break; case PROP_Y: g_value_set_int(value, point->y); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); break; } } static void point_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { Point *point = POINT(obj); switch (prop_id) { case PROP_ARRAY: if(point->array) g_array_unref(point->array); point->array = g_value_dup_boxed(value); break; case PROP_X: point->x = g_value_get_int(value); break; case PROP_Y: point->y = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); break; } } static void point_finalize(GObject *point) { if(POINT(point)->array) g_array_unref(POINT(point)->array); G_OBJECT_CLASS(point_parent_class)->finalize(point); } static void point_class_init (PointClass *cls) { GObjectClass *g_object_class = G_OBJECT_CLASS(cls); GParamSpec *array_param; GParamSpec *x_param; GParamSpec *y_param; g_object_class->get_property = point_get_property; g_object_class->set_property = point_set_property; g_object_class->finalize = point_finalize; cls->clear = point_clear; array_param = g_param_spec_boxed( "a", "a", "some int array", G_TYPE_ARRAY, G_PARAM_READWRITE); x_param = g_param_spec_int( "x", "x", "x loc of point", INT_MIN, /* => */ INT_MAX, 0, G_PARAM_READWRITE); y_param = g_param_spec_int( "y", "y", "y loc of point", INT_MIN, /* => */ INT_MAX, 0, G_PARAM_READWRITE); g_object_class_install_property( g_object_class, PROP_ARRAY, array_param); g_object_class_install_property( g_object_class, PROP_X, x_param); g_object_class_install_property( g_object_class, PROP_Y, y_param); point_signals[CLEAR] = g_signal_new( "clear", /* signal_name */ TYPE_POINT, /* itype */ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, /* signal_flags */ G_STRUCT_OFFSET(PointClass, clear), /* class_offset */ NULL, /* accumulator */ NULL, /* accu_data */ g_cclosure_marshal_VOID__VOID, /* c_marshaller */ G_TYPE_NONE, /* return_type */ 0); /* n_params */ } static void point_init(Point *point) { point->array = NULL; }
Подписаться на:
Сообщения (Atom)