Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [VC++]импорт методов, и работа с их адресами 
:(
    Опции темы
GoldFinch
Дата 16.5.2009, 20:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Есть длл, написанная на VС++, экспортирующая методы классов (возможно сами классы). 
Заголовочных файлов к этой длл нет.

При помощи dumpbin.exe были получены экспорты длл, при помощи lib.exe был получен .lib файл, при помощи undname.exe были получены имена и прототипы методов, при помощи IDA был восстановлен порядок методов в таблицах виртуальных методов (ВМТ)

Тестовый код "UObject o; o.AddRef;" показал что метод UObject::AddRef() импортируется корректно и генерирует корректный код вызова
Код
 call    ?AddRef@UObject@@UAGKXZ
?AddRef@UObject@@UAGKXZ:
  jmp [__imp_?AddRef@UObject@@UAGKXZ]


Теперь надо получить адрес этого метода. 
Допустим надо получить адрес простой импортированной функции. Пишем 
  int foo(){return int(&ExitProcess);}
получаем 
  mov     eax, [__imp__ExitProcess@4]
Для метода, пробуем написать
  int foo(){return int(&UObject::AddRef);}
получаем error C2440: приведение типов: невозможно преобразовать 'unsigned long (__stdcall UObject::* )(void)' в 'int'  Не существует контекста, в котором такое преобразование возможно

Пишем шаблон для преобразования
Код

template<typename T>
inline int int_cast(T x){
    union {
        T x;
        int y;
    } t;
    t.x=x;    
    return t.y;
};

код
  int foo(){return int_cast(&UObject::AddRef);}
компилится успешно, однако получаем адрес функции которая вызывает метод через таблицу методов
Код
?foo@@YAHXZ proc near
mov     eax, offset ??_9UObject@@$B3AG ; [thunk]: UObject::`vcall'{4,{flat}}
retn
?foo@@YAHXZ endp
??_9UObject@@$B3AG proc near

arg_0= dword ptr  4

mov     eax, [esp+arg_0]
mov     eax, [eax]
jmp     dword ptr [eax+4] ;4 - смещение метода в ВМТ
??_9UObject@@$B3AG endp

а хочется совсем не этого, хочется получить реальный адрес этого метода, т.е. [__imp_?AddRef@UObject@@UAGKXZ]

как это сделать?

Это сообщение отредактировал(а) GoldFinch - 16.5.2009, 20:38
PM MAIL ICQ   Вверх
Earnest
Дата 16.5.2009, 21:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



А метод случаем не виртуальный? Когда ты просишь адрес виртуального метода, то тебе компилятор подсовывает адрес именно функции-переходника, т.к. предполоагается полиморфный вызов в зависимости от this. 
И еще один вопрос: неужели банальный GetProcAddress не поможет (с истинным декорированным именем, конечно)? Если функция импортируется, то GetProcAddress должна возвратить ее, а метод с точки зрения линкера - та же функция... С преобразованием типа тут тоже возможны проблемы, конечно... 


--------------------
...
PM   Вверх
GoldFinch
Дата 16.5.2009, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Earnest, метод виртуальный. сделать его невиртуальным невозможно, т.к. не будет соответствовать манглинг.

GetProcAddress я както использовал, но потом от этого отказался вроде изза большего объема кода
Код

//глобальные переменные
  typ1 (class1::*met1)(agrlist1);
  typ2 (class1::*met1)(agrlist2);
  ...
//импорт
  (FARPROC&)met1=GetProcAddress(hMod,"?met1?class1?...");
  (FARPROC&)met2=GetProcAddress(hMod,"?met2?class2?...");
  ...
//вызов
  r=(obj->*met)(arg1,arg2,...);

проблем с преобразованием типа нет, а вот вызов выглядит не очень красиво

Это сообщение отредактировал(а) GoldFinch - 16.5.2009, 22:00
PM MAIL ICQ   Вверх
Earnest
Дата 16.5.2009, 22:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Я и не предлагала сделать его невиртуальным, просто скзала, как компилятор интерпретирует такие вызовы (и заодно указатели). GetProcAddress - это стандартный метод получения адресов функций, чего тут мудрить. А если 
не нравится код, напиши обертку к импортируемым классам (чтобы для клиента выглядело все как у "нормальных").
И не совсем понимаю, каким образом твой первый метод решал эту проблему (некрасивого кода вызова).

Кстати, ты ведь вроде говорил, что смог построить lib-файл. Так что тогда мешает подключить билиотеку стандартным оразом? Ну, заголовочный файл напиши руками, ты ведь вроде выяснил сигнатуры... Насколько я понимаю, цель именно такая: использовать библиотеку как если бы у нее были открыт интерфейсы (т.е. lib + h)?


--------------------
...
PM   Вверх
GoldFinch
Дата 17.5.2009, 00:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Earnest, 
я уже писал в 1м посте, что вызовы методов работают нормально, obj->met(),
мне же надо получить адрес метода, не для того чтобы его вызвать, а для последующей работы с этим адресом.
PM MAIL ICQ   Вверх
Earnest
Дата 17.5.2009, 00:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Наверное, пропустила.
В общем, адрес метода - это совсем не адрес с точки зрения компилятора и языка, так что просто &CMyObj::Method будет возвращать адрес стаба, как у тебя и получается. А вот GetProcAddress про C++ и прочий полиморфизм не в курсе, должна выдавать ровно то, что просят. Насчет длинного и некрасивого кода - как писал кто-то из гуру, любую проблему в программировании можно решить с помощью правильно спроектированных прокладок. А на C++ они к тому же почти бесплатны в смысле издержек.


--------------------
...
PM   Вверх
GoldFinch
Дата 17.5.2009, 00:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



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


Если же задуматься об издержках на импорт, то получается что при статическом импорте компилятор удаляет все неиспользуемые импорты и они импортируются поиском в таблице экспорта длл при загрузке программы. При вызове метода через указатель на объект (obj->met) адрес метода берется из ВМТ.
При динамическом импорте через GetProcAddress импортирование происходит поиском в таблице экспорта длл в момент вызова GetProcAddres. Будут ли удалены лишние вызовы GetProcAddress - неизвестно. При вызове метода адрес метода берется из переменной с адресом.
Однако у меня конкретная длл, которая не будет меняться за все время использования моей программы. Поэтому для импорта можно забить готовые адреса методов сразу на этапе компиляции, они не изменятся. При этом получится наибольшая скорость вызова функций, хотя не очень понятно как это будет выглядеть в коде, и как генерировать этот код из длл.


PM MAIL ICQ   Вверх
mes
Дата 17.5.2009, 10:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  16.5.2009,  19:21 Найти цитируемый пост)

Тестовый код "UObject o; o.AddRef;" показал что метод UObject::AddRef() импортируется корректно и генерирует корректный код вызова
Код


 call    ?AddRef@UObject@@UAGKXZ
?AddRef@UObject@@UAGKXZ:
  jmp [__imp_?AddRef@UObject@@UAGKXZ]




Код

void test (UObject& o) {    o.AddRef();  } 

а такой тестовый код, что дает ?


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 17.5.2009, 13:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



mes, вызов через ВМТ
PM MAIL ICQ   Вверх
mes
Дата 17.5.2009, 13:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  17.5.2009,  12:11 Найти цитируемый пост)
вызов через ВМТ 

тогда то, что Вы получили в первом примере, относится к оптимизации и получено вследствие того, что компилятору известен реальный тип объекта в компил-тайм.
И задача (без знания реального имени функции-реализации), имхо, не выполнима  


--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 19.6.2009, 21:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



ап!

Цитата(Earnest @  17.5.2009,  01:12 Найти цитируемый пост)
как писал кто-то из гуру, любую проблему в программировании можно решить с помощью правильно спроектированных прокладок.


попробовал написать обертку
Код

//=============== .h =====================

class Foo;

class Foo_Imp_
{
protected:
    static void (Foo::*Met1_ptr)(int);
    static int (Foo::*Met2_ptr)();
    static int (Foo::*Met3_ptr)(int,char);
    // ... еще методы ...
protected:
    Foo_Imp_()
    {
        if (!imported_)
        {
            imported_=true;
            HMODULE hDll=TheDll::Handle();  //загружает и возвращает хендл длл "the.dll"
            (FARPROC&)Met1_ptr=GetProcAddress(hDll,"??Met1@@Foo");
            (FARPROC&)Met2_ptr=GetProcAddress(hDll,"??Met2@@Foo");
            (FARPROC&)Met3_ptr=GetProcAddress(hDll,"??Met3@@Foo");
            // ... еще методы ...
        }
    }
private:
    static bool imported_;
};


class Foo :Foo_Imp_
{
public:
    char** VMT; //ВМТ класса
    int thedata1; //какието известные члены класса
    char stuff[0x34]; //какието неизвестные члены класса
public:
    void Met1(int arg1) {return (this->*Foo_Imp_::Met1_ptr)(arg1);}
    int Met2() {return (this->*Foo_Imp_::Met2_ptr)();}
    int Met3(int arg1,char arg2) {return (this->*Foo_Imp_::Met3_ptr)(arg1,arg2);}
    // ... еще методы ...
};


//================ .cpp =================

bool Foo_Imp_::imported_=false;

void (Foo::*Foo_Imp_::Met1_ptr)(int);
int (Foo::*Foo_Imp_::Met2_ptr)();
int (Foo::*Foo_Imp_::Met3_ptr)(int,char);
// ... еще методы ...



есть какие-нибудь идеи как сделать это короче?

Это сообщение отредактировал(а) GoldFinch - 19.6.2009, 21:18
PM MAIL ICQ   Вверх
Earnest
Дата 22.6.2009, 09:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



GoldFinch, а зачем? Ты код буста видел? Цель в том, чтобы был короче-яснее клиентский код, а обслуживающий... ну, по возможности.
Насчет сократить, я бы попробовала так (имея в виду, что методов сильно не 3).
Во-первых, таблица вместо перечисления методов, причем не только имя, но и закодированная сигнатура.
Во-вторых, набор типизированных шаблонов для вызова методов по сигнатуре. Шаблоны нужно делать по числу аргументов: для 0, 1, 2, 3 и сколько там по максимуму.
Методы имлементировать придется явно, как у и у тебя, но через шаблоны.
И еще не поняла, зачем тебе в обертке имитировать тело настоящего класса, все эти VMT, члены-данные... Т.е. если ты можешь правильно установить VMT, но нафига вообще огород городить - напиши объявление класса с правильно расположенными виртуальными функциями.


--------------------
...
PM   Вверх
GoldFinch
Дата 22.6.2009, 13:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



Earnest, обертка чтобы спрятать указатели на методы, и она превращает виртуальные методы в длл в невиртуальные, это позволяет вызывать их не зная их порядок в ВМТ (хотя они в оригинале виртуальные, полиморфизм там вроде не используется)

количество методов в 1 классе - до сотен (200-300), классы и длл писал не я
код буста не видел, но мне надо будет скорее всего писать все это руками, по мере необходимости добавляя новые методы 

PM MAIL ICQ   Вверх
mes
Дата 22.6.2009, 13:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 1
Всего: 250



Цитата(GoldFinch @  19.6.2009,  20:18 Найти цитируемый пост)
попробовал написать обертку

с певого взгляда бросилось в глаза

Цитата(GoldFinch @  19.6.2009,  20:18 Найти цитируемый пост)
Код

class Foo_Imp_
{
protected:
    static void (Foo::*Met1_ptr)(int);
    static int (Foo::*Met2ptr)();  

Зачем сразу ограничивать себя, принуждая дата-члены класса делать статическими.
Этот момент интуитивно напрашивается в таком исполнении :
Код

class Foo;
struct PtrTable
{
    void (Foo::*Met1)(int);
    int  (Foo::*Met2)();

    PtrTable () { /* import */ }

};

class Foo
{
    public:
      void  Met1 (int i) {  return (this->*table().Met1) (i); }
      int   Met2 ()      {  return (this->*table().Met2) ();  }

    private:
        static PtrTable& table() { static PtrTable table; return table; } // 1.
};


1. Выполнил как функцию, так как таблица принадлежит только этому классу, но если не подходит можно вернуть к "классическому" виду
 инициализации таблица произойдет при первом вызове функции PtrTable. Если нужно ручное управление инициализацией, то можно добавить функцию:
static void Init () { PtrTable(); } или поместить пустой вызов PtrTable() в конструктор в зависимости от требований.
ну а красиво  скрыть это : (this->*table().Met1)  я уверен Вы справитесь сами smile






--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 17.7.2009, 12:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

Репутация: нет
Всего: 26



ап!

почитал про static, действительно оно обладает замечательным свойством инициализировать переменные только 1 раз

получается что 
отдельный класс PtrTable и "static PtrTable& table() { static PtrTable table; return table; }"
можно заменить на статическую переменную с адесом метода прямо внутри обертки метода
при этом эту переменную можно сделать классом, в конструкторе которого и импортировать метод

Код

// обертка winapi
class Dll
{
public:
    template<typename T>
    static void import(T& f, const char* name)
    {
        if( !handle_ )
            handle_ = LoadLibraryA("test_dll.dll");
        (FARPROC&)f=GetProcAddress(handle_,name);
    }
private:
    static HMODULE handle_;
};
HMODULE Dll::handle_ = 0;

Код

// импортируемый класс
class Foo
{
...

    void init(char* str) 
    { 
        static struct Imp_
        {
            void (Foo::*met)(char* str);
            Imp_() { Dll::import(met,"?init@Foo@@UAEXPAD@Z"); }            
        } imp_;
        return (this->*imp_.met)(str); 
    }

...
};


получается что вся информация о методе сосредоточена в одном месте, и ее можно завернуть в компактный макрос
PM MAIL ICQ   Вверх
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




[ Время генерации скрипта: 0.0607 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.