Как выполнить неизвестные функции из динамически загружаемых библиотек? - PullRequest
0 голосов
/ 12 мая 2010

Легко загружать функции из динамических библиотек, если вы знаете эту функцию во время разработки. просто сделайте что-то вроде этого:

   int (*fn)(int);

   l0 = dlopen("./libfoo.so", RTLD_LAZY);
   if (!l0) 
   {
      fprintf(stderr, "l0 %s\n", dlerror());
      return 1;
   }


   fn = (int (*)(int))dlsym(l0, "foo");


   if ((error = dlerror()) != NULL)  
   {
      fprintf(stderr, "fn:%s\n", error);
      return 1;
   }

   x=(*fn)(y);

...

Как выполнить библиотечную функцию, когда она неизвестна во время разработки? Во время выполнения у вас есть имя функции и массив указателей аргументов и массив размеров аргументов:

char * fn_name = "foo"; int foo_argc; void * foo_argv []; int foo_argv_size [];

В скриптовом языке это простая задача, но как реализовать это в c ++?

Ответы [ 4 ]

1 голос
/ 12 мая 2010

На самом деле есть метод для вызова функции во время выполнения, если вы знаете ее соглашение о вызовах и какие параметры она получает. Это, однако, лежит за пределами стандартной области языка C / C ++.

Для х86 ассемблера:

Предполагая следующее:

  1. Вы знаете, как подготовить все параметры для вашей функции в твердом буфере, точно так, как они будут упакованы в стек.
  2. Ваша функция не принимает / не возвращает объекты C ++ по значению.

Тогда вы можете использовать следующую функцию:

int CallAnyFunc(PVOID pfn, PVOID pParams, size_t nSizeParams)
{
    // Reserve the space on the stack
    // This is equivalent (in some sense) to 'push' all the parameters into the stack.
    // NOTE: Don't just subtract the stack pointer, better to call _alloca, because it also takes
    // care of ensuring all the consumed memory pages are accessible
    _alloca(nSizeParams);

    // Obtain the stack top pointer
    char* pStack;
    _asm {
        mov pStack, esp
    };

    // Copy all the parameters into the stack
    // NOTE: Don't use the memcpy function. Because the call to it
    // will overwrite the stack (which we're currently building)
    for (size_t i = 0; i < nSizeParams; i++)
        pStack[i] = ((char*) pParams)[i];

    // Call your function
    int retVal;
    _asm {
        call pfn
        // Most of the calling conventions return the value of the function (if anything is returned)
        // in EAX register
        mov retVal, eax
    };

    return retVal;
}

Вам может потребоваться настроить эту функцию в зависимости от используемого соглашения о вызове

1 голос
/ 12 мая 2010

Поскольку проблема указана, вы не можете сделать это в стандартном C ++. Невозможно правильно вызвать функцию, если компилятор не знает параметры вызываемого во время компиляции. Даже в случае функции varargs вызывающий код должен быть скомпилирован, зная, какие аргументы переданы - нет никакого способа построить va_list.

Если вы управляете функциями в dll, то, вероятно, лучше всего передать коллекцию boost::any (если оба будут скомпилированы с одним и тем же компилятором и библиотеками) или void* (если вы этого не сделаете иметь такую ​​большую совместимость) или какую-либо сериализованную форму параметров (возможно, буфер протокола Google).

Если dll уже существует и вы не можете изменить ее интерфейс, то вам нужно либо сыграть нестандартные трюки со стеком, чтобы расположить параметры в нужном месте в соответствии с вашим соглашением о вызовах (что и делают динамические языки). когда у них есть механизм вызова C), или же вам нужен огромный оператор switch, охватывающий все сигнатуры функций, которые могут быть. Очевидно, что последнее возможно только в том случае, если у вас уже есть хорошее представление о том, с какими dll (ами) вы имеете дело.

Кроме того, в общем случае вам нужно больше, чем список указателей на аргументы и их размеры. Соглашение о вызовах вполне возможно зависит от типов параметров, а не только от размеров. Например, аргументы с плавающей точкой могут передаваться в регистрах FPU, а не в стеке.

Вам также нужно беспокоиться о типе возвращаемого значения и о том, что ожидает от него вызывающий код.

1 голос
/ 12 мая 2010

Будучи статическим языком, C ++ действительно зависит от знания типа / интерфейса во время компиляции. Я сомневаюсь, что есть хороший низкоуровневый способ выполнить задачу, которую вы хотите решить, но, как всегда, если вы добавите еще один уровень абстракции, вы, вероятно, сможете достичь цели. В частности, в этом случае подход, который я использовал в прошлом, когда не знал точно, какие «аргументы» необходимо передать динамически загружаемому плагину, заключается в передаче одного аргумента std::map< std::string, std::string > > (т.е. словарь ключ = значение). Просто упакуйте это с любой информацией, которая должна быть передана и позволяет плагину понять, что с ним делать. Это несколько неудобно тем, что вы должны кодировать / декодировать большую часть своих данных, но это может быть использовано для изящной обработки большинства случаев, в которых я работал, по крайней мере.

Конечно, это предполагает, что у вас есть возможность контролировать API динамически загружаемой DLL путем стандартизации интерфейса плагина. Если ты не можешь сделать это ... тебе, вероятно, не повезло.

0 голосов
/ 12 мая 2010

Как выполнить библиотечную функцию, когда она неизвестна во время разработки?

Ты не можешь. Там нет отражения в C ++. Вам нужно знать прототип функций, чтобы иметь возможность вызывать его. (Во всяком случае, если вы не реализуете свое собственное отражение).

...