Инкрементные указатели функций - PullRequest
10 голосов
/ 23 февраля 2011

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

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

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

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

Правильно ли я это понял или ядалеко?

Ответы [ 5 ]

7 голосов
/ 23 февраля 2011

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

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

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

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

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

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

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

Однако в разделе 3.9 / 9 говорится, что

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

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

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

5 голосов
/ 23 февраля 2011

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

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

4 голосов
/ 23 февраля 2011

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

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

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

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

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

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

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

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

...