Итак, есть проект на Си/Vala, который собирается под 64bit Windows с помощью GCC (MinGW). Есть желание использовать в проекте библиотеку Intel Math Kernel Library (Intel MKL), но нет желания собирать все это дело с помощью Visual Studio.

Если попробуем просто слинковать 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 есть обсуждение, где приведены вышеобозначенные ошибки и даже есть идеи, в какую сторону копать.

Я же расскажу, что делать, по шагам:
  1. Искомые символы можно найти в библиотеках libcmt.lib и BufferOverflowU.lib соответственно. В свою очередь библиотеки найти можно в составе различных девелоперских продуктов от Microsoft, в том числе бесплатных. Я взял WinDDK 7600.16385.1.
  2. Просто так слинковать их со своим проектом опять же не получится, т.к. 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
    
        ... 
  3. Решим вопрос, взяв из libcmt.lib только нужный нам obj-файл. Называется он gshandler.obj, что логично. Для извлечения его из lib-файла нам все же понадобится Visual Studio (я использовал старенькую 2005-ю).
  4. Открываем Visual Studio Command Prompt.
  5. Получаем список obj-файлов в libcmt.lib с помощью утилиты lib.exe:
      lib /list libcmt.lib > libcmt.txt
  6. В получившемся libcmt.txt ищем полный путь к gshandler.obj, у меня он выглядел так:
    d:\5359.obj.amd64fre\minkernel\crts\crtw32\misc\mt\objfre\amd64\gshandler.obj
  7. Теперь можем извлечь его из lib-файла:
    lib /extract:d:\5359.obj.amd64fre\minkernel\crts\crtw32\misc\mt\objfre\amd64\gshandler.obj libcmt.lib
  8. Мне показалось удобным и стройным завернуть извлеченный obj-файл в персональный lib-файл:
    lib /out:gshandler.lib gshandler.obj
  9.  Готово! Линковка проекта 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) и вызова этого метода.

Пример на 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:
// 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.

// 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(&params[n_params].value, specs[n]->value_type);
      g_object_get_property(src, specs[n]->name, &params[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;
}
В качестве объектов в примере взят простенький класс Point с гитхаба за авторством Jacob Gelbman. Код point.h:
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance
#ifndef __POINT_H__
#define __POINT_H__
#include <glib.h>
#include <glib-object.h>

#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__*/

Код point.c:
// 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;
}


 

Copyright © 2007 DamnSmallBlog. Content is licensed under Creative Commons Attribution-Noncommercial.

Design: GeckoandFly and Blogcrowds.