Инкрементные указатели функций

Я только что узнал о указателях функций (указатели, указывающие на адрес, где хранится машинный код функции). Это заставило меня задуматься о машинный код и о том, как он хранится в памяти.

Является ли машинный код сохраненным последовательно в памяти, так что можно "вручную" увеличить указатель до тех пор, пока он не укажет на следующую/предыдущую функцию?

Это то, что делает отладчик? Он позволяет мне "видеть", где счетчик программ указывает на машинный код?

Заключение: можно запрограммировать с указателями функций примитивный отладчик?

Я понял это право, или я ухожу?

Ответ 1

Вид. Вы предполагаете, что функции будут заложены в памяти так же, как и в исходном коде. Скорее всего, их не будет - компилятор обычно перемещает их вокруг всех волей-неволей.

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

Фактически, именно так работает компьютер - у него есть специальный register, называемый счетчик программ, который всегда указывает на текущую инструкцию и увеличивает его на определенную сумму после каждой команды (команда GOTO эквивалентна записи значения в счетчик программ).

В реальном мире, однако, это не как работают отладчики - на самом деле, я даже не уверен, возможно ли указать указатель на сегмент кода в памяти в C, кроме указателя функции. Скорее всего, вам нужно будет использовать эту технику, если вам нужно будет имитировать счетчик программ, например, написать эмулятор для другого типа процессора.

Ответ 2

Используя проект стандарта C, который мне удалось отследить (N1124), мы имеем аналогичные правила. В разделе о сложениях (раздел 6.5.6/2) говорится, что

Для добавления оба операнда должны иметь арифметический тип, или один операнд должен быть указатель на тип объекта

И тип объекта определен в разделе & sect; 6.2.5/1 как

Значение значения, хранящегося в объекте или возвращаемого функцией, определяется типом выражения, используемого для доступа к нему. (Идентификатор, объявленный как объект, является самым простым таким выражением, тип указан в объявлении идентификатора.) Типы разделяются на типы объектов (типы, которые полностью описывают объекты), типы функций ( типы, описывающие функции) и неполные типы (типы, которые описывают объекты, но не содержат информации, необходимой для определения их размеров).

Поскольку типы функций отличаются от типов объектов, это говорит о том, что арифметика указателя на указателях функций запрещена.

В С++ эта операция является незаконной. Определение добавления указателя, приведенное в разделе 5.7/1, гласит следующее:

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

Однако в разделе 3.9/9 указано, что

Тип объекта - это (возможно, cv-квалифицированный) тип не тип функции, а не ссылочный тип, а не тип void.

Взятый вместе, это означает, что вы не можете увеличивать указатель на функции в С++.

Надеюсь, это поможет!

Ответ 3

Вы можете (или, по крайней мере, могли) сделать что-то подобное, но это явно нетривиально. Прежде всего, вы не можете фактически увеличивать или уменьшать указатель на функцию - он указывает на адрес, но математика указателя обычно выполняется с шагом sizeof(pointed to type) - но с функцией, которая не имеет смысла, поэтому вы не может выполнять математику.

Большинство отладчиков работают (в первую очередь), используя отладочную информацию, которая связывает адреса с номерами строк, именами функций, именами переменных и т.д.

Ответ 4

  • Машинный код может храниться не последовательно. Компилятор чувствует себя свободно, чтобы разделить или объединить некоторые функции (в оптимизации).
  • Если вы вручную увеличиваете указатель на функцию, вы, вероятно, попадете в середину функции, что неверно.
  • Процедуры отладки уже доступны: вы можете получить трассировки стека текущей точки выполнения и разрешить имена функций, в которых содержатся указатели на выполнение в стеке (man backtrace, man backtrace_symbols). С помощью addr2line вы можете преобразовать их в номера строк.

Ответ 5

Нет никакой гарантии относительно того, где будут находиться отдельные функции в памяти.

Сама функция будет непрерывным блоком памяти (поскольку ЦП выполняет инструкции последовательно), но если вы включите оптимизацию кода, она может не похожа на саму функцию (инструкции могут быть сильно переупорядочены). Он может даже взять код очистки из другой функции.

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