Можно ли под windows используя VC подставить библиотечной функции стек(параметры) собранный "ручками"
int f(const char*,....); // такой вот прототип нашей функции, вызывающей scanf
//
int f("несущественно", a,b,c)
{
// делаем что-то умное
// вызываем scanf так, что он имеет параметры как f() - "несущественно", a,b,c
}
Re: Вызвать библиотечную функцию (fscanf) со "своим" стеком?
Здравствуйте, Strudel, Вы писали:
S>Можно ли под windows используя VC подставить библиотечной функции стек(параметры) собранный "ручками"
S>
S>int f(const char*,....); // такой вот прототип нашей функции, вызывающей scanf
S>//
S>int f("несущественно", a,b,c)
S>{
S>// делаем что-то умное
S>// вызываем scanf так, что он имеет параметры как f() - "несущественно", a,b,c
S>}
S>
А ты представь, как это сделать на асме. Я вот представил, и имхо на C++ такого лучше не делать..
Делай что должно, и будь что будет
Re[2]: Вызвать библиотечную функцию (fscanf) со "своим" стек
Hello, Strudel!
You wrote on Tue, 09 Dec 2003 13:35:16 GMT:
S> Можно ли под windows используя VC подставить библиотечной функции S> стек(параметры) собранный "ручками"
S>
S> int f(const char*,....); // такой вот прототип нашей функции, вызывающей
S> scanf
S> //
S> int f("несущественно", a,b,c)
S> {
S> // делаем что-то умное
S> // вызываем scanf так, что он имеет параметры как f() -
S> "несущественно", a,b,c }
S>
Можно, только windows тут совершенно не при чем. Смотреть va_arg, va_start, va_end.
Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Вызвать библиотечную функцию (fscanf) со "своим" стек
Здравствуйте, Sergey, Вы писали:
S>Можно, только windows тут совершенно не при чем. Смотреть va_arg, va_start, va_end.
S>Best regards, S> Sergey.
По поводу windows и va_arg, va_start, va_end — в MSDN как раз unix особенности упомянуты. А еще есть разные MSVC- специфические(?) модификаторы вызова (так что-ли?).
Но главное не это? Для чего нам нужны va_arg, va_start, va_end и так понятно — можно извлечь параметры f().
Главное — что дальше? И нужно ли их извлекать? Или можно просто подставить слегка модифицированный стек, а дальше вызов
scanf — ручками.
Re[3]: Вызвать библиотечную функцию (fscanf) со "своим" стек
Hello, SergH!
You wrote on Tue, 09 Dec 2003 15:03:43 GMT:
S>> Можно, только windows тут совершенно не при чем. Смотреть va_arg, S>> va_start, va_end.
S> А не мог бы ты алгоритм подсказать? А то я чего-то пока не соображу..
Выделить с помощью alloca блок нужного размера (форматную строку, само собой, придется разбирать самостоятельно, т.к. способа определить размер аргументов нет), скопировать туда аргументы по одному (тут упомянутые макросы пригодятся), вызвать scanf, скопировать аргументы назад.
S> Кстати, для printf есть vprintf, но тут-то scanf..
Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: Вызвать библиотечную функцию (fscanf) со "своим" стек
Hello, Strudel!
You wrote on Tue, 09 Dec 2003 15:20:39 GMT:
S> По поводу windows и va_arg, va_start, va_end — в MSDN как раз unix S> особенности упомянуты.
Один черт, винда тут не при чем.
S> А еще есть разные MSVC- специфические(?) модификаторы вызова (так S> что-ли?).
AFAIK, нет таких для функций с переменным числом параметров.
S> Но главное не это? Для чего нам нужны va_arg, va_start, va_end S> и так понятно — можно извлечь параметры f(). Главное — что дальше?
S> И S> нужно ли их извлекать?
S> Или можно просто подставить слегка S> модифицированный стек, а дальше вызов scanf — ручками.
Hello, SergH!
You wrote on Tue, 09 Dec 2003 15:38:29 GMT:
S> Э, с разбором то можно, наверное. Но разбирать-то нехочется. Я что-то S> такое придумал.
S> очистить стек от временных переменных
Зачем? Но если уж надо, у MSVC есть __declspec( naked ).
S> вытащить оттуда адрес возврата и сохранить в регистрах
Адрес первого аргумента известны, адрес возврата под ним лежит. Влияние алайнмента правда проверить придется.
S> вызвать fscanf S> вернуть в стек адрес возврата S> закончиться
S>> void f(const char*fmtstr, ...) S>> { S>> long *tmpsp; S>> const char** firstarg = &fmtstr; S>> __asm S>> { S>> mov tmpsp, esp S>> mov esp, firstarg S>> call scanf S>> mov esp, tmpesp S>> }
S> Не, так низя. она же тебе адрес возврата изгадит нафиг.
Да, еще пару строк дописать придется — адрес возврата сохранять.
Здравствуйте, Sergey, Вы писали:
S>> очистить стек от временных переменных
S>Зачем? Но если уж надо, у MSVC есть __declspec( naked ).
S>> вытащить оттуда адрес возврата и сохранить в регистрах
S>Адрес первого аргумента известны, адрес возврата под ним лежит. Влияние алайнмента правда проверить придется.
Это я знаю.
S>Да, еще пару строк дописать придется — адрес возврата сохранять.
S>
S>Возможно, придется пошаманить и запихнуть firstarg и retadr в структуру, чтоб выравнивание учлось — точно не знаю.
Хм, очевидно у нас разное представление о стеке. Имхо там так (растет сверху вниз, как положено):
В самом начале функции:
....
параметры функции
адрес возврата
-------
свободно
Чуть погодя:
....
параметры функции
адрес возврата
-------
сохранённые регистры
локальные переменные
--------
свободно
Так вот, хочется мне выкинуть из стека всё, кроме "параметры функции" и вызвать fscanf. К сожалению, при этом приходится где-то в глобальной памяти (другом стеке?) хранить свои сохранённые регистры и свой адрес возврата. Именно для того чтобы его сохранить я и хочу его вытащить.
А то, что ты предлагаешь это просто установить SP, нигде ничего не сохранив. Точнее, сохранив в локальной переменной, которая находится в этом же стекеи которая, конечно, испортится при вызове fscanf. Заодно испортятся и сохраннные там регистры.
Делай что должно, и будь что будет
Re[7]: Вызвать библиотечную функцию (fscanf) со "своим" стек
Hello, SergH!
You wrote on Tue, 09 Dec 2003 16:07:33 GMT:
S> Хм, очевидно у нас разное представление о стеке. Имхо там так (растет S> сверху вниз, как положено):
S> В самом начале функции: S>
S> ....
S> параметры функции
S> адрес возврата
S> -------
S> свободно
S>
S> Чуть погодя: S>
S> ....
S> параметры функции
S> адрес возврата
S> -------
S> сохранённые регистры
S> локальные переменные
S> --------
S> свободно
S>
Ну да, так и есть.
S> Так вот, хочется мне выкинуть из стека всё, кроме "параметры функции" и S> вызвать fscanf.
Выкидывать-то зачем? Пусть валяется, никому не помешает.
S> К сожалению, при этом приходится где-то в глобальной памяти (другом S> стеке?) хранить свои сохранённые регистры и свой адрес возврата. Именно S> для того чтобы его сохранить я и хочу его вытащить.
Чтоб его сохранить, достаточно знать его адрес. Локальные переменные для этого вытаскивать не надо.
S> А то, что ты предлагаешь это просто установить SP, нигде ничего не S> сохранив. Точнее, сохранив в локальной переменной, которая находится в S> этом же стекеи которая, конечно, испортится при вызове fscanf.
Да, запортится. Я просто прозевал.
Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[8]: Вызвать библиотечную функцию (fscanf) со "своим" стек
Здравствуйте, Sergey, Вы писали:
S>Выкидывать-то зачем? Пусть валяется, никому не помешает.
Скорее всего "выкинуть" — изменить ESP
Вобщем, так работает (запускала в Wnidows 2000 SP4, хотя имхо должно работать везде на x86, вплодь до DOS-а, только регистры поменять на 16-ти разрядные..):
#include <stdio.h>
#include <malloc.h>
int __cdecl f1(char* str, ...)
{
// до начала выполнения стек выглядит так (сверху вниз):
//
// параметры
// адрес возврата (слово)
// -----------------
// свободно
//
// esp содержит указатель на начало последнего
// слова в стеке, т.е. на начало адреса возврата
//
// пролог функции:
// push ebp
// mov ebp, esp
//
// теперь стек такой:
//
// параметры
// адрес возврата (слово)
// регистр ebp (слово)
// -----------------
// свободно
//
// а ebp указывает на начало сохранённого регистра ebp
//
// далее граница стека (видимо, из дебажных соображений?) сдвигается
// на 0x48 байт, в эту область пихаются локальные переменные, остаток
// заполняется 0xCCCC, а после этой области сохраняются регистры
//
// итого, стек выглядит так:
//
// параметры
// адрес возврата (слово)
// регистр ebp (слово)
// локальные перемнные
// пустота с 0xCC
// сохранённые регистры
// -----------------
// свободно
//
// В нормальном оторажении (адреса снизу вверх):
//
// свободно
// -----------------------
// esp -> сохранённые регистры
// пустота с 0xCC
// локальные пременные
// ebp -> регистр ebp (слово)
// адрес возврата (слово)
// параметры
//
// значит, интересная (копируемая) область начинается с esp и идет до ebp + 8void* stack_copy = NULL;
int stack_len = 0;
//
// Вычисление длинны копируемой области
//__asm
{
mov stack_len, ebp
sub stack_len, esp
}
stack_len += 2 * sizeof(int); // + ebp + адрес возврата
// Выделяем память
stack_copy = (char*)malloc(stack_len);
__asm
{
// параметры movsb:
// es::esi - строка источник
// es::edi - строка приёмник
// ecx - длина
//
// Копируем стек
mov ecx, stack_len
mov esi, esp
mov edi, stack_copy
push ss
pop es
rep movsb
// длину области памяти и её адрес нужно хранить в регистрах,
// иначе f2 их испортит. Только не в eax, он испльзуется для
// возвращения результата. ecx и edx тоже портятся.. Вообще,
// во всех (пяти) просмотренных прологах функций почему-то
// сохраняются только ebp, ebx, esi и edi. Это что ли
// соглашение такое? Никому нельзя верить..
mov ebx, stack_len
mov esi, stack_copy
// Очищаем стек
add esp, ebx
// вызываем scanf. Поскольку она тоже __cdecl, парамтры
// для неё передаются гарантированно в том же порядке
// и она не очищает стек.
call scanf
// Возвращаем стек на место
sub esp, ebx
// Копируем всё обратно
// mov ecx, stack_len - сделано заранее
// mov esi, stack_copy - сделано заранее
mov ecx, ebx
mov edi, esp
rep movsb
}
free(stack_copy);
return 0;
}
void main()
{
int a, b;
printf("enter a and b\n");
f1("%d%d", &a, &b);
printf("a = %d b = %d\n", a, b);
}
Re[9]: Вызвать библиотечную функцию (fscanf) со "своим" стек
Здравствуйте, TaniaDen, Вы писали:
TD>Вобщем, так работает..
TD> // длину области памяти и её адрес нужно хранить в регистрах, TD> // иначе f2 их испортит. Только не в eax, он испльзуется для TD> // возвращения результата. ecx и edx тоже портятся.. Вообще, TD> // во всех (пяти) просмотренных прологах функций почему-то TD> // сохраняются только ebp, ebx, esi и edi. Это что ли TD> // соглашение такое? Никому нельзя верить..
Судя по этому вот
When using __asm to write assembly language in C/C++ functions, you don't need to preserve the EAX, EBX, ECX, EDX, ESI, or EDI registers. For example, in the POWER2.C example in Writing Functions with Inline Assembly, the power2 function doesn't preserve the value in the EAX register. However, using these registers will affect code quality because the register allocator cannot use them to store values across __asm blocks. In addition, by using EBX, ESI or EDI in inline assembly code, you force the compiler to save and restore those registers in the function prologue and epilogue.