Я хочу напечатать возвращаемое значение в моем трассере, есть два вопроса
Здесь нужен текст, чтобы Stackoverflow форматировал код:
struct Tracer
{
int* _retval;
~Tracer()
{ printf("return value is %d", *_retval); }
};int foo()
{
Tracer __tracter = { __Question_1_how_to_get_return_address_here__ };
if(cond) {
return 0;
} else {
return 99;
}
//Question-2:
// return postion is updated before OR after ~Tracer() called ???
}
Вы не можете переносить или надежно сделать это в C ++. Возвращаемое значение может находиться в памяти или в регистре и может быть или не быть косвенным в разных случаях.
Возможно, вы могли бы использовать встроенную сборку, чтобы заставить что-то работать на определенном оборудовании / компиляторах.
Одним из возможных способов является сделать ваш Tracer
шаблон, который принимает ссылку на переменную возвращаемого значения (при необходимости) и печатает тот до разрушения.
Также обратите внимание, что идентификаторы с __
(двойное подчеркивание) зарезервированы для реализации.
Я нашел несколько подсказок для Вопроса-1, сейчас проверяю код Vc
Для gcc, __builtin_return_address
http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
Для Visual C ++ _ReturnAddress
Ваш вопрос довольно запутанный, вы взаимозаменяемо используете термины «адрес» и «значение», которые не являются взаимозаменяемыми.
Возвращаемое значение — это то, что функция выдает, в x86 (_64), которое приходит в виде значения 4/8 байтов в E / RAX, или EDX: EAX, или XMM0 и т. Д., Вы можете прочитать об этом больше Вот.
Адрес возврата, с другой стороны, это то, на что указывает E / RSP при совершении вызова (то есть на вершине стека), и содержит адрес, на который функция «переходит» назад, когда это сделано (что по определению называется возвращение).
Теперь я даже не знаю, что такое трейсер, но я могу сказать вам, как вы получите, все дело в хуках.
Для значения и при условии, что вы делаете это внутренне, просто подключите функцию к функции с таким же определением, и как только она вернется, вы получите свой результат.
Что касается адреса, это немного сложнее, потому что вам придется пойти немного ниже и, возможно, сделать несколько asm shenanigains, я действительно понятия не имею, что именно вы ищете, но я сделал небольшую «заглушку», если вы будете , чтобы предоставить вызываемому указатель возврата.
Вот:
void __declspec(noinline) __declspec(naked) __stdcall _replaceFirstArgWithRetPtrAndJump_() {
__asm { //let's call the function we jump to "callee", and the function that called us "caller"push ebp //save ebp, ESP IS NOW -4
mov ebp, [esp + 4] //save return address
mov eax, [esp + 8] //get callee's address (which is the first param) - eax is volatile so iz fine
mov[esp + 8], ebp //put the return address where the callee's address was (to the callee, it will be the caller)
pop ebp //restore ebp
jmp eax //jump to callee
} }
#define CallFunc_RetPtr(Function, ...) ((decltype(&Function))_replaceFirstArgWithRetPtrAndJump_)(Function, __VA_ARGS__)
unsigned __declspec(noinline) __stdcall printCaller(void* caller, unsigned param1, unsigned param2) {
printf("I'm printCaller, Called By %p; Param1: %u, Param2: %u\n", caller, param1, param2);
return 20;
}
void __declspec(noinline) doshit() {
printf("us: %p\nFunction we're calling: %p\n", doshit, printCaller);
CallFunc_RetPtr(printCaller, 69, 420);
}
Теперь, конечно, вы могли бы и, возможно, должны использовать _ReturnAddress () или любые другие встроенные функции компилятора, но если это недоступно (что должно быть действительно редким сценарием в зависимости от вашей работы), и вы знаете свой ASM, эта концепция должна работать для любой архитектуры, поскольку набор команд может быть разным, все они следуют одному и тому же дизайну счетчика программ.
Я написал это больше, потому что я искал ответ на этот вопрос довольно давно для определенной цели, и я не мог найти хороший, потому что большинство людей просто говорят: «Блин, это не возможно, не переносимо или что-то в этом роде», и Я чувствую, что это помогло бы.