Могу ли я сделать некоторые символы видимыми только для других членов библиотеки? - PullRequest
0 голосов
/ 14 октября 2018

Рассмотрим 3 исходных файла C:

/* widgets.c */
void widgetTwiddle ( struct widget * w ) {
    utilityTwiddle(&w->bits, 1);
}

и

/* wombats.c */
void wombatTwiddle ( struct wombat * w ) {
    utilityTwiddle(&w->bits, 1);
}

и

/* utility.c */
void utilityTwiddle ( int * bitsPtr, int bits ) {
    *bitsPtr ^= bits;
}

, которые компилируются и помещаются в библиотеку (скажем, либоlibww.a или libww.so).Есть ли способ сделать utilityTwiddle() видимым и доступным для других двух членов библиотеки, но не быть видимым для тех, кто ссылается на библиотеку?То есть, учитывая это:

/* appl.c */
extern void utilityTwiddle ( int * bitsPtr, int bits );
int main ( void ) {
    int bits;
    utilityTwiddle(&bits, 1);
    return 0;
}

и

cc -o appl appl.c -lww

он не сможет соединиться, потому что utilityTwiddle() не виден для appl.c.И, следовательно, appl.c может свободно определять свою собственную utilityTwiddle функцию или переменную.

[EDIT] И, надеюсь, очевидно, что мы хотели бы, чтобы это работало:

/* workingappl.c */
extern void wombatTwiddle ( struct wombat * wPtr );
int main ( void ) {
    struct wombat w = { .bits = 0 };
    wombatTwiddle(&w);
    return 0;
}

Это Ограничение видимости символов при связывании общих библиотек кажется связанным, но, похоже, не рассматривается, доступны ли подавленные символы другим членам библиотеки.

[EDIT2] Я как-то нашел способ сделать это без изменения источника Си.Добавьте файл карты:

/* utility.map */
{ local: *; };

и затем выполните:

$ gcc -shared -o utility.so utility.c -fPIC -Wl,--version-script=utility.map

, чтобы получить динамическую таблицу символов без utilityTwiddle:

$ nm -D utility.so
             w _Jv_RegisterClasses
             w __cxa_finalize
             w __gmon_start__

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

/* libww.map */
{ global: list; of; all; symbols; to; export; local: *; };

и

$ gcc -shared -o libww.so *.c -fPIC -Wl,--version-script=libww.map

[EDIT3] Мальчик, похоже, это тоже должнобыть возможным без использования общих библиотек.Если я сделаю:

ld -r -o wboth.o widgets.o wombats.o utility.o

, я вижу, что компоновщик разрешен в местоположение utilityTwiddle(), где widgetTwiddle() и wombatTwiddle() называют его:

$ objdump -d wboth.o
0000000000000000 <widgetTwiddle>:
   0:   be 01 00 00 00          mov    $0x1,%esi
   5:   e9 00 00 00 00          jmpq   a <widgetTwiddle+0xa>
0000000000000010 <wombatTwiddle>:
  10:   be 01 00 00 00          mov    $0x1,%esi
  15:   e9 00 00 00 00          jmpq   1a <wombatTwiddle+0xa>
0000000000000020 <utilityTwiddle>:
  20:   31 37                   xor    %esi,(%rdi)
  22:   c3                      retq

, но utilityTwiddle остается символом:

$ nm wboth.o
                 U _GLOBAL_OFFSET_TABLE_
0000000000000020 T utilityTwiddle
0000000000000000 T widgetTwiddle
0000000000000010 T wombatTwiddle

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

$ nm wboth.o
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T widgetTwiddle
0000000000000010 T wombatTwiddle
0000000000000020 T xtilityTwiddle

1 Ответ

0 голосов
/ 14 октября 2018

Вы не можете достичь того, чего хотите, создав статическую библиотеку libww.a.Если вы прочитаете статические библиотеки , вы поймете, почему.Статическая библиотека может быть использована для предложения связке N объектных файлов для компоновщика, из которого он будет извлекать k (возможно = 0), в котором он нуждается, и связывать их .Таким образом, вы ничего не можете достичь, связавшись со статической библиотекой, чего вы не можете достичь, связав эти объектные файлы k напрямую.Для целей связывания статические библиотеки на самом деле не существуют.

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

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

__attribute__((visibility("default|hidden|protected|internal")

и / или переключателя компилятора 2 :

-fvisibility=default|hidden|protected|internal

Вот демонстрация того, как сборка libww.so, так что utilityTwiddle скрыта от клиентов библиотеки, в то время как wombatTwiddle и widgetTwiddle видны.

Ваш исходный код должен быть немного уточнен одним илидругой для компиляции.Вот первое сокращение:

ww.h (1)

#ifndef WW_H
#define WW_H

struct widget {
    int bits;
};

struct wombat {
    int bits;
};

extern void widgetTwiddle ( struct widget * w );
extern void wombatTwiddle ( struct wombat * w );

#endif

utility.h (1)

#ifndef UTILITY_H
#define UTILITY_H

extern void utilityTwiddle ( int * bitsPtr, int bits );

#endif

utility.c

#include "utility.h"

void utilityTwiddle ( int * bitsPtr, int bits ) {
    *bitsPtr ^= bits;
}

wombats.c

#include "utility.h"
#include "ww.h"

void wombatTwiddle ( struct wombat * w ) {
    utilityTwiddle(&w->bits, 1);
}

widgets.c

#include "utility.h"
#include "ww.h"

void widgetTwiddle ( struct widget * w ) {
    utilityTwiddle(&w->bits, 1);
}

Скомпилируйте все файлы *.c в файлы *.o по умолчанию:

$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c

и свяжите их с libww.so по умолчанию:

$ gcc -shared -o libww.so widgets.o wombats.o utility.o

Вот *Twiddle символов в глобальной таблице символов libww.so

$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle

Это всего лишь сумма глобальных (extern) *Twiddle символов, которые вошли в связьlibww.so из объектных файлов.Все они определены (T), как и должно было бы быть, если бы сама библиотека была связана без внешних *Twiddle зависимостей.

Любой файл ELF (объектфайл, разделяемая библиотека, программа) имеет глобальную таблицу символов, но совместно используемая библиотека также имеет таблицу символов dynamic .Вот символы *Twiddle в таблице динамических символов libww.so:

$ nm -D libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle

Они точно такие же.Это то, что мы хотим изменить, чтобы исчезло utilityTwiddle.

Вот второй разрез.Мы должны немного изменить исходный код.

utility.h (2)

#ifndef UTILITY_H
#define UTILITY_H

extern void utilityTwiddle ( int * bitsPtr, int bits ) __attribute__((visibility("hidden")));

#endif

Затем перекомпилировать и перекомпоновать, как раньше:

$ gcc -Wall -Wextra -c widgets.c wombats.c utility.c
$ gcc -shared -o libww.so widgets.o wombats.o utility.o

Вот символы *Twiddle, которые теперь находятся в глобальной таблице символов:

$ nm libww.so | egrep '*Twiddle'
000000000000063a T utilityTwiddle
00000000000005fa T widgetTwiddle
000000000000061a T wombatTwiddle

Никаких изменений там нет.И вот теперь символы *Twiddle в таблице символов dynamic :

$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle

utilityTwiddle пропали.

Вот третий разрез, который достигает того жерезультат по-другому.Это более многословно, но иллюстрирует, как играет опция компилятора -fvisibility.На этот раз utility.h снова соответствует (1) , но ww.h равно:

ww.h (2)

#ifndef WW_H
#define WW_H

struct widget {
    int bits;
};

struct wombat {
    int bits;
};

extern void widgetTwiddle ( struct widget * w )  __attribute__((visibility("default")));
extern void wombatTwiddle ( struct wombat * w ) __attribute__((visibility("default")));

#endif

Теперь мы перекомпилируем так:

$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c

Мы говорим компилятору аннотировать каждый глобальный символ, который он генерирует с __attribute__((visibility("hidden"))), если нет явной компенсации __attribute__((visibility("...")))в исходном коде.

Затем повторно подключите общую библиотеку, как и ранее.Снова мы видим в глобальной таблице символов:

$ nm libww.so | egrep '*Twiddle'
00000000000005ea t utilityTwiddle
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle

и в таблице динамических символов:

$ nm -D libww.so | egrep '*Twiddle'
00000000000005aa T widgetTwiddle
00000000000005ca T wombatTwiddle

Наконец, чтобы показать, что удаление utilityTwiddle из таблицы динамических символов libww.so одним из этих способов действительно скрывает это от клиентов, связывающихся сlibww.so.Вот программа, которая хочет вызвать все *Twiddle s:

prog.c

#include <ww.h>

extern void utilityTwiddle ( int * bitsPtr, int bits );

int main()
{
    struct widget wi = {1};
    struct wombat wo = {2};
    widgetTwiddle(&wi);
    wombatTwiddle(&wo);
    utilityTwiddle(&wi.bits,wi.bits);
    return 0;
}

У нас нет проблем при ее создании, например:

$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o utility.o widgets.o wombats.o

Но никто не может построить его так:

$ gcc -Wall -Wextra -I. -c prog.c
$ gcc -o prog prog.o -L. -lww
prog.o: In function `main':
prog.c:(.text+0x4a): undefined reference to `utilityTwiddle'
collect2: error: ld returned 1 exit status

Понятно, что -fvisibility это опция компиляции , а не опция связывания.Вы передаете его своим командам компиляции, а не командам связывания, потому что это действует так же, как разбрасывание квалификаторов __attribute__((visibility("..."))) по объявлениям в вашем исходном коде, что компилятор должен выполнять, вводя информацию о связяхв объектные файлы, которые он генерирует.Если вы хотите увидеть доказательства этого, вы можете просто повторить последнюю компиляцию и запросить сохранение файлов сборки:

$ gcc -Wall -Wextra -fvisibility=hidden -c widgets.c wombats.c utility.c -save-temps

Затем сравните, скажем:

widgets.s

    .file   "widgets.c"
    .text
    .globl  widgetTwiddle
    .type   widgetTwiddle, @function
widgetTwiddle:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movq    %rdi, -8(%rbp)
    movq    -8(%rbp), %rax
    movl    $1, %esi
    movq    %rax, %rdi
    call    utilityTwiddle@PLT
    nop
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   widgetTwiddle, .-widgetTwiddle
    .ident  "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
    .section    .note.GNU-stack,"",@progbits

с:

утилита.s

    .file   "utility.c"
    .text
    .globl  utilityTwiddle
    .hidden utilityTwiddle
    ^^^^^^^^^^^^^^^^^^^^^^
    .type   utilityTwiddle, @function
utilityTwiddle:
    ...
    ...


[1] См. GCCmanual :

[2] См. Руководство GCC, 3.16 Варианты для соглашений по генерации кода .

...