Возможно ли в функции получить адрес памяти переменной, инициализированной возвращаемым значением? - PullRequest
2 голосов
/ 03 октября 2019

Я провел некоторое исследование встроенной сборки на C и того, как работает стек вызовов, но я не смог выяснить, возможно ли вообще получить адрес переменной, которая запрашивает возвращаемое значение функции,ВНУТРИ функции.

int hypothetical_func(){

    /*...
    .. some assembly to get the address of 'int a' from the call stack?
    ...*/

    return 5;
}

int main(){
    int a = hypothetical_func();
}

Возможно ли это вообще?

Ответы [ 3 ]

4 голосов
/ 03 октября 2019

NO. int возвращается в регистр , и вызываемый абонент не участвует в том, что делает вызывающий с этим регистром после его возврата. Он никогда не может быть сохранен в памяти.

Если тип возвращаемого значения не был int, а вместо этого достаточно большой, чтобы соглашение о вызовах возвращало его по значению, то hypothetical_func будет иметь адрес длявыход. (Или гипотетическое (ужасное) соглашение о вызовах может вернуть даже int через скрытый указатель вместо регистра. Предполагается, что машина является машиной регистра, как и все реальные процессоры.)

Но это может быть простовозвращаемое значение временное, а не фактическое LHS присваивания. (Или инициализация, которая достаточно близка к тому же в C, если не в C ++). Особенно, если назначение глобальное или что-то. См. Что мешает использовать аргумент функции в качестве скрытого указателя? для случая *out = foo();, где T *out - это аргумент функции. Очень нетривиально доказать, если / когда безопасно передавать эту функцию arg в качестве объекта возвращаемого значения для foo().

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

И, как указывает @prl, возвращаемое значение может даже не быть инициализатором для переменной,Например, printf("%d\n", foo()); просто передает возвращаемое значение в функцию arg. или foo(); отбрасывает возвращаемое значение, нигде не присваивая его. (Но если соглашение о вызовах указывает, что функция возвращает по скрытому указателю, вызывающая сторона должна передать указатель на достаточное времяВызываемый объект все еще собирается записать свое возвращаемое значение, и ему не нужно переставлять ошибки из неверного указателя или перезаписывать что-то еще. Это деталь соглашения об ассемблере / вызове, отдельная от работы абстрактной машины C. Или, я думаю, вы могли бы сказать,объект возвращаемого значения все еще существует, он просто нигде не назначен.)


Плюс с встроенной сборкой, у вас даже нет доступа к этой ,Если вы не рассчитываете написать функцию __attribute__((naked)), в которой вы все равно пишете всю функцию внутри оператора asm, и компилятор не обрабатывает ничего, кроме искажения имени в имени функции. Нет пролога, эпилога или абстрагирования соглашения о вызовах с переменными C для аргументов и тем, который вы return. (/ ворчит, что компиляторы C не могут создавать функции, которые возвращают несколько отдельных значений в нескольких регистрах, как вы можете в рукописном asm.)

Но даже с рукописным asm нет способа сделать это дляобычные соглашения о вызовах на обычных ISA, таких как x86 и ARM. Объект возвращаемого значения для int - это просто регистр.

2 голосов
/ 03 октября 2019

Это невозможно ни в каком практическом смысле. Возвращаемое значение не может быть присвоено переменной, и если это так, переменная может не иметь адреса памяти. Даже если это не так, и возвращаемое значение присваивается переменной в памяти, адрес этой переменной недоступен для функции.

Непрактичный способ выяснить это (при необходимости). например, для отладки) получить адрес возврата из стека и разобрать инструкции, ища тот, который записывает регистр возвращаемого значения в память. Такая инструкция, если она существует, обычно находится в пределах нескольких инструкций после точки возврата.

0 голосов
/ 03 октября 2019

Если int a = hypothetical_func(); находится внутри другой функции, она должна быть в стековом фрейме этой функции, поэтому, используя backtrace(), вы можете найти эти стековые фреймы и найти эту переменную.

Для получения дополнительной информации: https://www.linuxjournal.com/article/6391

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...