Как объединить статику для всех экземпляров встроенной функции, определенной в заголовке? - PullRequest
0 голосов
/ 08 апреля 2020

Имея заголовок, который определяет некоторую функцию static inline, которая содержит static переменные в ней, как добиться объединения идентичных локальных переменных stati c во всех TU, которые составляют конечный загружаемый модуль ?. Менее абстрактно:

/*
 * inc.h
 */
#include <stdlib.h>

/* 
 * This function must be provided via header. No extra .c source
 * is allowed for its definition.
 */
static inline void* getPtr() {
    static void* p;
    if (!p) {
        p = malloc(16);
    }
    return p;
}

/*
 * 1.c
 */
#include "inc.h"

void* foo1() {
    return getPtr();
}

void* bar1() {
    return getPtr();
}

/*
 * 2.c
 */
#include "inc.h"

void* foo2() {
    return getPtr();
}

void* bar2() {
    return getPtr();
}

Платформа имеет значение Linux, и этот набор файлов создается с помощью:

$ clang -O2 -fPIC -shared 1.c 2.c

Вполне ожидаемо, что оба TU получат собственные копии getPtr.p. Хотя внутри каждого TU getPtr.p используется во всех getPtr() экземплярах. Это может быть подтверждено проверкой окончательного загружаемого двоичного файла:

$ readelf -s --wide a.out | grep getPtr
    32: 0000000000201030     8 OBJECT  LOCAL  DEFAULT   21 getPtr.p
    34: 0000000000201038     8 OBJECT  LOCAL  DEFAULT   21 getPtr.p

В то же время я ищу способ разделения getPtr.p через отдельную границу TU. Это смутно напоминает то, что происходит с экземплярами шаблонов C ++. И, вероятно, GRP_COMDAT помог бы мне, но я не смог найти никакой информации о том, как пометить мой stati c var для помещения в COMDAT.

Есть ли какой-либо атрибут или другой уровень источника (не вариант компилятора) способ добиться объединения таких объектов?

1 Ответ

1 голос
/ 08 апреля 2020

Если я правильно понимаю, что вы хотите, вы можете получить этот эффект, просто объявив глобальную переменную.

/*
 * inc.h
 */

void* my_p;

static inline void* getPtr() {
    if (!my_p) {
        my_p = malloc(16);
    }
    return my_p;
}

При этом будет использоваться одна и та же переменная my_p для всех случаев getPtr на протяжении всего программа (так как она глобальная). И нет необходимости иметь явное определение my_p в любом модуле. Он будет инициализирован до NULL, что вам нужно. Таким образом, ничего кроме inc.h не нужно менять, и дополнительный файл .c не требуется.

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

На самом деле это расширение к стандарту C (упомянутое в Приложении J.5.11 в N2176), но оно предоставляется g cc и clang на большинстве современных платформ. Это задокументировано в опции компилятора -fcommon (которая включена по умолчанию). Обычно это реализуется путем помещения переменной в секцию common, и компоновщик затем объединяет все экземпляры вместе, как вы предлагаете. Но приведенный выше код показывает, как получить доступ к этой функции без необходимости использовать атрибуты или другие неясные заклинания.

Если вы хотите быть параноиком, вы можете объявить my_p с помощью __attribute__((common)), что приведет к изменению переменной относиться таким образом, даже если действует -fno-common. (Конечно, это может вызвать проблемы, если -fno-common использовался по причине ...)

...