TL;DR: Могу ли я использовать ld
компоновщик GNU --version-script
или какой-либо другой метод, чтобы повысить видимость выбранных символов с hidden
(из-за -fvisibility=hidden
или явного __attribute__
) до default
видимости, чтобы они были доступны в глобальная таблица символов общей библиотеки?
Могу ли я каким-либо образом сказать gnu ld version-script повысить видимость символов HIDDEN
в разделе global:
version-script до DEFAULT
видимости?
Для довольно странной системы мне нужно связать общую библиотеку, чтобы все нестатические функциональные символы имели видимость по умолчанию, но все остальные символы получали «скрытую» видимость, если явно не аннотированы соответствующими атрибутами видимости.
Если бы был способ сказать gcc
что-то вроде (недопустимый воображаемый синтаксис) -fvisibility=hidden -fvisibility-functions=default
, это было бы идеально.
Компиляция с помощью gcc -fvisibility=hidden
сделает видимыми все неаннотированные символы.
Поэтому я подумал, что буду продвигать функции, которые имеют глобальную область видимости и скрытую видимость в объектах ELF, до видимости по умолчанию (глобальной), используя скрипт версии компоновщика.
Однако похоже, что GNU LD не меняет атрибут видимости, когда символ указан в разделе global
версии сценария компоновщика. Символы в разделе local
скрываются, но уже скрытые символы в разделе global
не повышаются до default
.
SCCCE
Учитывая упрощенный объект (расширенные макросы и т. д.):
/* Public global variables are annotated */
extern int exportvar __attribute__ ((visibility ("default")));
int exportvar;
/* Internal ones are not annotated */
extern int nonexportvar;
int nonexportvar;
/* Nor are static vars obviously */
static int nonexportvar;
/* For various weird reasons we can't annotate functions with visibility information */
extern void exportfunc(void);
void exportfunc(void) {
};
static void staticfunc(void) {
};
со скриптом компоновщика:
Фрагмент скрипта компоновщика linker-script
:
{
global:
exportfunc;
exportvar;
local:
*;
};
построено с использованием Makefile
(без табуляции для удобства копирования/вставки):
.RECIPEPREFIX=~
USE_LLVM?=0
VERBOSE_SYMS?=
EXTRA_CFLAGS?=
LINK_FLAGS?=
# Don't optimise so we retain the unused statics etc in this
# demo code.
EXTRA_CFLAGS+=-O0
ifneq (,$(LINKER_SCRIPT))
LINK_FLAGS+=--version-script=$(LINKER_SCRIPT)
endif
ifeq (1,$(USE_LLVM))
CC=clang -c $(EXTRA_CFLAGS)
LINK_SHARED=ld.lld -shared $(LINK_SHARED)
else
CC=gcc -c $(EXTRA_CFLAGS)
COMMA:=,
LINK_SHARED=gcc -shared $(addprefix -Wl$(COMMA),$(LINK_FLAGS))
endif
all: clean demo.so dumpsyms
clean:
~ @rm -f demo.o demo.so
demo.o: demo.c
~ $(CC) -o $@ $<
demo.so: demo.o
~ $(LINK_SHARED) -o $@ $<
# Show object file and full symbol table if VERBOSE_SYMS=1
ifneq (,$(VERBOSE_SYMS))
dumpsyms: demo.o demo.so
~ @echo
~ @echo "demo.o:"
~ @readelf --syms demo.o | egrep '(Symbol table|exportvar|nonexportvar|staticvar|exportfunc|nonexportfunc)'
~ @echo
~ @echo "demo.so:"
~ @readelf --syms demo.so | egrep '(Symbol table|exportvar|nonexportvar|staticvar|exportfunc|staticfunc)'
~ @echo
else
# Only show dynamic symbols by default
dumpsyms: demo.o demo.so
~ @echo
~ @echo "demo.so:"
~ @readelf --dyn-syms demo.so | egrep '(Symbol table|exportvar|nonexportvar|staticvar|exportfunc|nonexportfunc)'
~ @echo
endif
С флагами по умолчанию (нет видимости, нет скрипта компоновщика)
$ make
gcc -c -O0 -o demo.o demo.c
gcc -Wall -O0 -shared -o demo.so demo.o
demo.so:
Symbol table '.dynsym' contains 8 entries:
5: 00000000000010f9 7 FUNC GLOBAL DEFAULT 11 exportfunc
6: 0000000000004028 4 OBJECT GLOBAL DEFAULT 21 nonexportvar
7: 0000000000004024 4 OBJECT GLOBAL DEFAULT 21 exportvar
Со скриптом компоновщика и видимостью по умолчанию
make LINKER_SCRIPT=linker-script
gcc -c -O0 -o demo.o demo.c
gcc -Wall -O0 -Wl,--version-script=linker-script -shared -o demo.so demo.o
demo.so:
Symbol table '.dynsym' contains 7 entries:
5: 0000000000004024 4 OBJECT GLOBAL DEFAULT 21 exportvar
6: 00000000000010f9 7 FUNC GLOBAL DEFAULT 11 exportfunc
nonexportvar
исчез из таблицы динамических символов, как и ожидалось.
С -fvisibility=hidden
и без скрипта компоновщика
$ make EXTRA_CFLAGS="-fvisibility=hidden"
gcc -c -fvisibility=hidden -o demo.o demo.c
gcc -Wall -fvisibility=hidden -shared -o demo.so demo.o
demo.so:
Symbol table '.dynsym' contains 6 entries:
5: 0000000000004024 4 OBJECT GLOBAL DEFAULT 21 exportvar
exportfunc
не отображается в таблице символов экспорта. Это ожидаемо, так как ни один скрипт компоновщика не перекрывает видимость.
С -fvisibility=hidden
и скриптом компоновщика
Можем ли мы использовать скрипт компоновщика, чтобы сделать скрытые функциональные символы видимыми?
$ make EXTRA_CFLAGS="-fvisibility=hidden" LINKER_SCRIPT=linker-script
gcc -c -fvisibility=hidden -o demo.o demo.c
gcc -Wall -fvisibility=hidden -Wl,--version-script=linker-script -shared -o demo.so demo.o
demo.so:
Symbol table '.dynsym' contains 6 entries:
5: 0000000000004024 4 OBJECT GLOBAL DEFAULT 21 exportvar
... вроде нет.
Почему?
$ make EXTRA_CFLAGS="-fvisibility=hidden" LINKER_SCRIPT=linker-script VERBOSE_SYMS=1
gcc -c -fvisibility=hidden -o demo.o demo.c
gcc -Wall -fvisibility=hidden -Wl,--version-script=linker-script -shared -o demo.so demo.o
demo.o:
Symbol table '.symtab' contains 13 entries:
5: 0000000000000008 4 OBJECT LOCAL DEFAULT 3 staticvar
10: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 exportvar
11: 0000000000000004 4 OBJECT GLOBAL HIDDEN 3 nonexportvar
12: 0000000000000000 7 FUNC GLOBAL HIDDEN 1 exportfunc
demo.so:
Symbol table '.dynsym' contains 6 entries:
5: 0000000000004024 4 OBJECT GLOBAL DEFAULT 21 exportvar
Symbol table '.symtab' contains 52 entries:
33: 000000000000402c 4 OBJECT LOCAL DEFAULT 21 staticvar
39: 00000000000010f9 7 FUNC LOCAL DEFAULT 11 exportfunc
42: 0000000000004028 4 OBJECT LOCAL DEFAULT 21 nonexportvar
50: 0000000000004024 4 OBJECT GLOBAL DEFAULT 21 exportvar
Похоже, что GNU ld
превратил GLOBAL HIDDEN
символов в .o
в LOCAL DEFAULT
символов в .so
.
Сценарий компоновщика здесь не работает; результат один и тот же с ним или без него.
Могу ли я каким-либо образом указать скрипту версии компоновщика повысить видимость символов HIDDEN
в разделе global
до DEFAULT
видимости?
Вещи, которые я не могу сделать
К сожалению, я не могу отказаться от требования, чтобы функции были видны по умолчанию, в то время как все остальные символы должны быть видны только тогда, когда они явно аннотированы как таковые. Мне нужно соответствовать поведению сборки Windows, которая использует сгенерированный файл .def
для экспорта всех функций, а использует __declspec__("dllexport")
для экспорта только выбранных других символов.
Из-за размера кодовой базы, сложности, совместного управления и т. д. я не могу просмотреть и аннотировать каждую функцию подходящим макросом атрибутов.
Я не могу перечислить все символы, которые нужно экспортировать вручную, и поддерживать рукописный скрипт компоновщика; кодовая база имеет слишком много разных конфигураций, версий и вариантов сборки.
Я мог бы использовать другой широкодоступный компилятор и цепочку инструментов, если это поможет, и версии цепочки инструментов не являются проблемой. Я пробовал использовать LLVM clang
и ld.lld
с идентичными результатами.
Помощь? Идеи?
__attribute__(section(".globals")))
для перемещения явно экспортированных переменных в отдельный раздел ELF, но это не работает, потому что тегируются как инициализированные, так и неинициализированные, константные и неконстантные переменные. Итак, gcc задыхается отvar1 causes a section type conflict with var2
. В любом случае, было бы больно сопоставлять. - person Craig Ringer   schedule 05.06.2020