Как мне получить адрес возврата функции?

Я пишу библиотеку Rust, содержащую реализацию обратных вызовов для LLVM SanitizerCoverage. Эти обратные вызовы могут использоваться для отслеживания выполнения инструментированной программы.

Распространенным способом создания трассировки является печать адреса каждого исполняемого базового блока. Однако для этого необходимо получить адрес инструкции call которая вызвала обратный вызов. C++ примеры, предоставленные LLVM, полагаются на встроенную компилятор __builtin_return_address(0) для получения этой информации.

extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  if (!*guard) return;
  void *PC = __builtin_return_address(0);
  printf("guard: %p %x PC %p\n", guard, *guard, PC);
}

Я пытаюсь воспроизвести ту же функцию в Rust, но, по-видимому, нет эквивалента __builtin_return_address. Единственная ссылка, которую я нашел, - это старая версия Rust, но описанная функция больше не доступна. Функция следующая:

pub unsafe extern "rust-intrinsic" fn return_address() → *const u8

Мое настоящее хакерское решение заключается в том, чтобы в моей корзине был файл C, содержащий следующую функцию:

void* get_return_address() {
  return __builtin_return_address(1);
}

Если я вызываю его из функции Rust, я могу получить адрес возврата самой функции Rust. Это решение, однако, требует компиляции моего кода Rust с помощью -C force-frame-pointers=yes чтобы оно работало, поскольку встроенный компилятор C полагается на наличие указателей фреймов.

В заключение, есть ли более простой способ получить адрес возврата текущей функции в Rust?

edit: удаление встроенного return_address обсуждается в этом выпуске GitHub.

редактирование 2: Дальнейшее тестирование показало, что backtrace способна правильно извлечь адрес возврата текущей функции, таким образом избегая взлома, который я описал ранее. Кредит идет на этот твит.

Проблема с этим решением - это издержки, которые генерируются при создании полной обратной трассировки, когда требуется только адрес возврата текущей функции. Кроме того, ящик использует библиотеки C для извлечения обратной трассировки; это похоже на то, что должно быть сделано в чистом Rust.

edit 3: llvm.returnaddress компилятор __builtin_return_address(0) генерирует вызов встроенного llvm.returnaddress LLVM llvm.returnaddress. Соответствующую документацию можно найти здесь.

Ответ 1

Вероятно, нет, потому что любой подход, который вы выбрали, будет зависеть от указателей кадра, чтобы найти адрес возврата. Я делал подобные вещи в C, и результат также опирался на указатели фреймов, не говоря уже о встроенной сборке. Подсчитайте свои благословения, что вам это не нужно!