Подтвердить что ты не робот

Равноправие указателя функции в C

Мои вопросы:

  • Является ли равенство указателя функции гарантированным стандартом C?
  • Если ответ (1) - да. Это дело независимо от того, какой указатель получен в разных конечных единицах компиляции (например, основной исполняемый файл и общая библиотека)?
  • Как динамический загрузчик справляется с этим? (Я могу подумать о нескольких причинах, для которых это может быть сложно, все это связано с кодом PIC (например, таблицы GOT в эльфе и любой эквивалент COFF для этого). Независимо от (1) и (2), загрузчик linux гарантирует это.

Ниже приведен пример. Приведенные выше вопросы сводятся к тому, что C гарантирует, что main.c печатает: "Function equality: 1" или "Function equality: 0", и в первом случае, как делает динамический загрузчик что происходит.

common.h:

extern void * getc_main;
extern void * getc_shared;
void assign_getc_shared(); 

main.c:

#include <stdio.h>
#include "common.h"

int main()
{
  getc_main = (void*) getc;
  assign_getc_shared();
  printf("Function equality: %d\n", getc_main == getc_shared);
  return 0;
}

shared.c:

#include <stdio.h>
#include "common.h"

void assign_getc_shared()
{
   getc_shared = (void*) getc;
}

В Unix это будет скомпилировано со следующими командами:

cc -shared -fPIC -o libshared.so shared.c
cc -o main main.c -L. -lshared

И выполняется с помощью:

LD_LIBRARY_PATH=. ./main
4b9b3361

Ответ 1

C 2011 (проект Комитета N1570) 6.5.9 6: "Два указателя сравнивают одинаковые, если и только если... оба являются указателями на одну и ту же функцию... Итак, да, два указателя на одну и ту же функцию сравнивают равные.

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

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

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