2016-06-06 7 views
-5

В настоящее время я занимаюсь языком C.Как работает эта функция get_sp()?

Все, кажется разумным, но, когда я столкнулся с этой инициализацией и функциями

unsigned long get_sp(void) { 
    __asm__("movl %esp,%eax\n\t" 
      "and $0xff000000, %eax" 
      ); 
} 

int (*fp)(char *)=(int(*)(char *))&puts; 

Я действительно не знаю, что делает эти линии означают.

Какова действительная переменная? какой тип? ...

Может кто-нибудь объяснить меня в глубине?

+1

Возможный дубликат [Как работают указатели функций в C?] (http://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work) –

+1

@TalhaIrfan: Этот вопрос отвечает только на половину вопроса. Функция 'get_sp()' не объясняется и IMHO не является дубликатом этого. –

+0

@MichaelPetch Я думаю, что ты прав Майкл. Я редактирую вопрос, чтобы связать его с get_sp() –

ответ

3

1st, вы определяете функцию get_sp(), которая возвращает unsigned long. Содержимое этой функции представляет собой встроенную сборку, которая получает адрес указателя стека, помещает его в регистр eax, а затем и с 0xff000000. IE: получает значение в eax, которое установило любой из 8 лучших адресов адреса указателя стека. Регистр eax используется для возвращаемого значения, поэтому возвращается указатель на скрытый указатель стека.

Вторая строка назначает fp адрес функции puts. puts - это функция, которая возвращает int, и ожидает ввода char *. Следовательно, тип/имя int (*fp)(char *).

После этой линии, можно было бы назвать функцию Кладет как fp("hello");

2

Вторая часть просто указатель на функцию с инициализаторе (литье &puts).

Первая часть более интересна:

Он получает %esp в переменную C, и маски от младших 24 бит. то есть округляется до границы 16MiB. ИДК, что это для, но 4B смещение от вызова не-встраиваемой функции может или не вопрос (если %esp был очень близко к границе 16MiB, или это явно то, что вы пытаетесь обнаружить.)

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

/********* Safe version of the function *************/ 
// Actually unsigned long was fine, since this asm only works on 32bit anyway 
static inline uintptr_t get_sp(void) { 
    uintptr_t result; 
    __asm__("movl %%esp, %0\n\t" 
      : "=g" (result) 
      ); 
    result &= 0xff000000; // do this outside the inline asm so the compiler knows that the low 24b are always zero. 
    return result; 
} 

Это compiles to the same asm when not inlined, но может быть безопасно встраиваемыми. (например, поместите его в заголовок с static inline). Он также, конечно, избегает предупреждений компилятора о функциях с отсутствующими возвращаемыми значениями.

Как отмечает Майкл Пётч в комментариях, включение этой функции всегда или внутри, или даже макроса, вероятно, является хорошей идеей для согласованности. (Хотя оптимизирован по сравнению с не-оптимизированные сборки будет потреблять разное количество стека в любом случае.)


Смотрите тег вики больше о том, как писать GNU C встроенный ассемблер, который не сосет.

+0

Только прокомментировать, что это такое _ESP_, которого он ищет? например, что произойдет, если этот код будет скомпилирован без оптимизации. Сам вызов функции теперь произведет обратный адрес и возможно предыдущее значение _EBP_ в стеке (смещение ESP), если присутствует стек стека. Я бы, вероятно, объявлял это как «статический inline» или, в худшем случае, _C_ MACRO, который всегда будет встроен. Также вы уверены в ограничении вывода '= g'? Это означало бы, что 'i' действителен, но вы не сможете использовать значение' i' (imm.) В качестве цели. Может быть, '= rm'? –

+1

@MichaelPetch: Я рассмотрел вопрос о том, что «esp» вы получите. Поскольку 'get_sp' маскирует все, кроме старшего байта, я решил, что, вероятно, не имеет значения, было ли оно вложенным или нет. Для ограничения вывода, я думаю, gcc всегда будет выбирать 'r', потому что ему нужно замаскировать результат. Вероятно, я должен был это сделать. Я думал, что в некоторых случаях он мог просто хранить '% esp' где-то и не нужно маскировать, например, если позже он загрузился только с высоким байтом. Я не думаю, что существует риск, что 'i' может когда-либо соответствовать ограничению только для вывода, поэтому мне не нужно было вручную исключать его. –

+1

Технически возможно находиться на границе с выравниванием 16 МБ с _ESP_, в этом случае дополнительные толкания будут иметь другое значение. Конечно, вероятность того, что _ESP_ находится вблизи этой границы, является тонкой, но не обязательно невозможной. Я просто не видел никого в ответ или комментарий, что «И» эффективно округляет указатель стека до ближайшей границы 16 МБ (выравнивание?). –

Смежные вопросы