У меня есть код на С ++, который вызывает адрес экспортированной функции из другой dll и возвращает структуру. Код на C++ выглядит следующим образом (я изменил имена переменных на A, B, C ...):
// .h
struct _A { unsigned char _B[32]; } A;
// .cpp
typedef struct _A* (__cdecl *_C)();
_C C = NULL;
...
C = (_C)GetProcAddress(..., ...);
A = *C();
Приведенный выше код (только часть A = *C();) при компиляции в VS 2015 превращается в следующий листинг кода:
A DB 020H DUP (?)
...
call DWORD PTR C
mov esi, eax
mov esx, 8
mov edi, OFFSET A
Как (и где) указатель передается из подпрограммы, хранящейся всего в трех строках mov? Я не могу понять, как данные, переданные из подпрограммы, сохраняются для последующих обращений.
Просто из любопытства я попытался изменить struct _A на _B [11] вместо _B [32], и код изменился на следующий:
call DWORD PTR C
mov ecx, DWORD PTR [eax]
mov DWORD PTR A, ecx
mov edx, DWORD PTR [eax+4]
mov DWORD PTR A+4, edx
...
Так что для меня это имеет смысл. Это чтение из eax и копирование в A, как и ожидалось. Но как первый ассемблерный код получает и сохраняет структуру, возвращенную подпрограммой?
@JesperJuhl Я знаю, что он передается через eax, но как это сохраняется для последующих ссылок? Например, позже в коде, когда на самом деле упоминается этот A, все, что он, кажется, делает, это нажимает OFFSET A, и затем он может волшебным образом ссылаться на структуру, которую я получил обратно от C (), но как (и когда) было это скопировано? Я не могу понять эту часть, так как все, что я вижу, это три мова, которые заполняют esi, esx и edi и больше ничего не делают ...





But how is the first assembly code retrieving and storing the struct returned from the subroutine?
Прежде всего, он не возвращает структуру, он возвращает структуру указатель на в EAX. Тип возвращаемого значения функции - struct _A*. Вы не показываете какие, на который он указывает; возможно, какой-то статический буфер в функции, не обеспечивающей потокобезопасность?
Похоже, вы пропустили rep movsd в первом примере после настройки esi, edi и ecx (ваш esx, очевидно, является опечаткой). Это приведет к запоминанию 4*8 = 32 байта из указателя, возвращенного в EAX, в статическое хранилище для A. (Обратите внимание на mov edi, offset A, чтобы получить фактический адрес A в EDI.)
С меньшей структурой он копирует ее с помощью нескольких инструкций mov вместо настройки для rep movsd (что имеет значительные накладные расходы на запуск и является плохим выбором для 32-байтовой копии, если SSE был доступен). т.е. он полностью разворачивает цикл копирования.
(В первой версии я недостаточно внимательно изучил код и, судя по формулировке, подумал, что вы на самом деле возвращаете структуру по значению, когда говорили о возврате структуры. Кажется, стыдно удалять то, о чем я писал этот связанный случай. Вместо указателя скрытый у вас есть явный указатель на объект, который существует в C++, а не только в реализации asm того, что делает абстрактная машина C++.)
Возвращаемые большие структуры по значению возвращаются скрытым указателем (вызывающий передает указатель в качестве первого аргумента, а функция возвращает его в EAX для удобства вызывающего). Это типично для большинства соглашений о вызовах; см. ссылки на документы соглашения о вызовах в вики-теги x86.
значение самого A составляет 32 байта и не помещается в регистр. Часто в asm вам нужен указатель на объект. push OFFSET A, вероятно, является частью вызова функции, которая принимает A по ссылке (вероятно, явно в исходном коде C++; я не думаю, что какие-либо стандартные соглашения о вызовах x86 реализуют передачу по значению как передачу по константной ссылке, только по неконстантной ссылке, например. для Windows x64 и, возможно, других) .
Ваш компилятор, вероятно, не смог оптимизировать A = foo(); (возвращая большую структуру по значению), передав адрес A непосредственно в качестве выходного указателя.
A является глобальным, и вызываемому разрешается предположить, что его буфер возвращаемого значения не является псевдонимом глобального A. Вызывающий не может предположить, что функция не обращается к A напрямую, но согласно абстрактной машине C++ значение A не изменяется до тех пор, пока функция не вернется.
Спасибо! Я полностью пропустил этот rep movsd, но да, я нашел его двумя строчками ниже. Думаю, теперь это имеет смысл. Спасибо за ответ!
Питер, его функция возвращает указатель, а не структуру, поэтому она не может использовать скрытый параметр, который используется для возврата структуры.
@prl: о, лол, да, это не скрыто, это источник C++ разыменовывает указатель для копирования в вызывающей стороне. / facepalm.
Возвращаемое значение передается в ax.