Указатель на функцию с GCC, присвоение адреса

Я столкнулся с чем-то, чего совершенно не понимаю. Есть прототип функции:

typedef void ( * TMain ) ( void );

и функциональная переменная:

TMain myFunc = MyFunc;
...
myFunc ();

Это, конечно, отлично работает. Почему бы и нет.

Из MAP-файла я знаю, что MyFunc находится по адресу 0x20100. А теперь самое смешное. После присвоения «myFunc = MyFunc;» переменная "myFunc" не содержит значение 0x20100, а скорее 0x20101!

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

myFunc = ( TMain ) myTable [ 5 ];    // that would be 0x20100
myFunc ();                           // which produces a proper crash

Однако если я это сделаю

myFunc = ( TMain ) ( ( Int8 * ) myTable [ 5 ] + 1 );  
myFunc ();

тогда это работает.

Что здесь происходит? Всегда ли мне нужно добавлять смещение 1 или это более или менее случайно? Или есть лучший (и рабочий) способ выполнить задачу?

Большое спасибо за любой намек. Уолтер


person Walter    schedule 01.02.2012    source источник
comment
Не уверен, но это может зависеть от архитектуры. Так что, возможно, захочется сказать, какая архитектура является целевой.   -  person phimuemue    schedule 01.02.2012
comment
Является ли массив myTable типа TMain[]? Я предполагаю, что это не так, поскольку вы его разыгрываете.   -  person Blagovest Buyukliev    schedule 01.02.2012
comment
Несколько вопросов к вам: Какую платформу вы используете (ARM, x86 ...)? Вы когда-нибудь проверяли реальный адрес функции с помощью отладчика? Вы видите, как компилятор генерирует косвенный вызов myFunc ()?   -  person Giuseppe Guerrini    schedule 01.02.2012
comment
почему вы храните адреса, а не функции? вместо объявления int myTable[N] вы можете сделать TMain myTable[N], а позже вам не понадобится приведение: myfunc = myTable[5], или вы вызываете напрямую mytable[5](), даже если вы выполняете приведение функций для заполнения массива, это будет работать правильно.   -  person Bort    schedule 01.02.2012
comment
Для настройки режима большого пальца здесь более подходящим вариантом может быть цифра 1.   -  person Michael Dorgan    schedule 11.04.2013


Ответы (2)


Я предполагаю, что вы находитесь на цели ARM и создали свою программу в режиме Thumb? (Thumb по умолчанию установлен в ARM Ubuntu или Linaro.)

Нижний бит адреса функции сообщает процессору, в каком наборе команд он должен интерпретировать функцию. 0 - это режим охраны. 1 - это режим большого пальца. Таким образом, все указатели на функции в режиме Thumb будут нечетными.

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

person ams    schedule 01.02.2012

Некоторые архитектуры ЦП резервируют первые байты функции для определенных целей. VAXen имеет там маску регистра сохранения. CDC Cyber ​​помещает туда обратный адрес. Некоторые используют некоторые биты «адреса» для обозначения косвенного адреса (навскидку, я не могу вспомнить, какие именно, но они из 1970-х).

Если вы действительно не знаете, что делаете, вам не следует писать код, который выполняет такую ​​арифметику с указателями. Присвойте переменной имя функции и покончите с этим: это гарантированно будет работать в каждой C реализации.

person wallyk    schedule 01.02.2012