Результаты, которые вы наблюдаете, когда некоторые глобальные определения символов в некоторых объектных файлах, заархивированных в libxxx.a
, были скомпилированы с атрибутом функции или атрибутом переменной visibility("hidden")
Этот атрибут приводит к тому, что когда объектный файл, содержащий глобальное определение символа, связывается с общей библиотекой:
- Связывание символа изменяетсяот глобального к локальному в таблице статических символов (
.symtab
) выходной общей библиотеки, поэтому, когда эта общая библиотека связана с чем-либо еще, компоновщик не может видеть определение символа. - Символопределение не добавлено в таблицу символов dynamic (
.dynsym
) выходной разделяемой библиотеки (которая по умолчанию будет), чтобы при загрузке разделяемой библиотеки в процессзагрузчик также не может найти определение символа.
Короче говоря, глобальное определение символа в объектном файлескрыт для целей динамического связывания.
Проверьте это с помощью:
$ readelf -s libxxx.a | grep HIDDEN
, и я ожидаю, что вы получите хиты за неисследованные глобальные символы.Если вы этого не сделаете, вам не нужно читать дальше, потому что у меня нет другого объяснения того, что вы видите, и я не буду рассчитывать на какое-либо обходное решение, которое я предложил не стрелять вам в ногу.
Вот иллюстрация:
ac
#include <stdio.h>
void aa(void)
{
puts(__func__);
}
вс
#include <stdio.h>
void __attribute__((visibility("hidden"))) bb(void)
{
puts(__func__);
}
de.c
#include <stdio.h>
void __attribute__((visibility("default"))) dd(void)
{
puts(__func__);
}
void ee(void)
{
puts(__func__);
}
Мы скомпилируем a.c
и b.c
следующим образом:
$ gcc -Wall -c a.c b.c
И мы можем видеть, что символы aa
и ab
определены и глобальны в своем соответствующем объектефайлы:
$ nm --defined-only a.o b.o
a.o:
0000000000000000 T aa
0000000000000000 r __func__.2361
b.o:
0000000000000000 T bb
0000000000000000 r __func__.2361
Но мы также можем наблюдать эту разницу:
$ readelf -s a.o
Symbol table '.symtab' contains 13 entries:
Num: Value Size Type Bind Vis Ndx Name
...
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 aa
...
по сравнению с:
$ readelf -s b.o
Symbol table '.symtab' contains 13 entries:
Num: Value Size Type Bind Vis Ndx Name
...
10: 0000000000000000 19 FUNC GLOBAL HIDDEN 1 bb
...
aa
является символом GLOBAL
с DEFAULT
видимостью и bb
является символом GLOBAL
с HIDDEN
видимостью.
Мы скомпилируем de.c
иначе:
$ gcc -Wall -fvisibility=hidden -c de.c
Здесь мы находимсяуказание компилятору, что любому символу должна быть предоставлена скрытая видимость, если в исходном коде для него не указан уравновешивающий атрибут visibility
.И соответственно мы видим:
$ readelf -s de.o
Symbol table '.symtab' contains 15 entries:
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
...
11: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 dd
...
14: 0000000000000013 19 FUNC GLOBAL HIDDEN 1 ee
Архивирование этих объектных файлов в статической библиотеке их никак не меняет:
$ ar rcs libabde.a a.o b.o de.o
И затем, если мы свяжем их все в общую библиотеку:
$ gcc -o libabde.so -shared -Wl,--whole-archive libabde.a -Wl,--no-whole-archive
мы находим, что:
$ readelf -s libabde.so | egrep '(aa|bb|dd|ee|Symbol table)'
Symbol table '.dynsym' contains 8 entries:
6: 0000000000001105 19 FUNC GLOBAL DEFAULT 12 aa
7: 000000000000112b 19 FUNC GLOBAL DEFAULT 12 dd
Symbol table '.symtab' contains 59 entries:
45: 0000000000001118 19 FUNC LOCAL DEFAULT 12 bb
51: 000000000000113e 19 FUNC LOCAL DEFAULT 12 ee
54: 0000000000001105 19 FUNC GLOBAL DEFAULT 12 aa
56: 000000000000112b 19 FUNC GLOBAL DEFAULT 12 dd
bb
и ee
, которые были GLOBAL
с HIDDEN
видимостью в объектных файлах, LOCAL
встатический символ libabde.so
и отсутствуют в его динамической таблице символов.
В этом свете вы можете пересмотреть свою миссию :
Символы, которым была предоставлена скрытая видимость в объектных файлах в libxxx.a
, были скрыты, потому что у человека, который их скомпилировал, была причина желать скрыть их от динамической связи.У вас есть требующая компенсация для их динамического связывания?Или, может быть, вы просто хотите экспортировать их, потому что вы заметили, что они не экспортируются и не знаете, почему нет?
Если вы все же хотите показать скрытые символы и не можете изменить исходный кодиз объектных файлов, заархивированных в libxxx.a
, ваш наименее худший вариант:
- Извлечь каждый объектный файл из
libxxx.a
- Врач, чтобы заменить
HIDDEN
на DEFAULT
видимость его глобальных определений - Поместите его в новый архив
libyyy.a
- Затем используйте
libyyy.a
вместо libxxx.a
.
The binutils
инструмент для обработки объектных файлов: objcopy
.Но objcopy
не имеет операций для непосредственного манипулирования динамической видимостью символа, и вам придется согласиться на обходной кладж, который «достигает эффекта», скрывая скрытые символы:
- С
objcopy --redefine-sym
, переименуйте каждый скрытый глобальный символ S
, скажем, __hidden__S
. - С помощью
objcopy --add-symbol
добавьте новый глобальный символ S
, который имеет то же значение, что и __hidden_S
, но получает DEFAULT
видимость по умолчанию.
заканчивается двумя символами с одним и тем же определением: исходным скрытым и новым скрытым псевдонимом для него.
Предпочтительнее всего этого означает простое и единственное изменение видимости символав объектном файле ELF , а средство - передать в библиотеку LIEF (форматы исполняемых файлов библиотеки) - Швейцарская армейская цепная пила для изменения объекта и исполняемого файла 1 .
Вот скрипт Python, который вызывает pylief
, модуль Python LIEF, чтобы отобразить скрытые глобалы в объектном файле ELF:
unhide.py
#!/usr/bin/python
# unhide.py - Replace hidden with default visibility on global symbols defined
# in an ELF object file
import argparse, sys, lief
from lief.ELF import SYMBOL_BINDINGS, SYMBOL_VISIBILITY, SYMBOL_TYPES
def warn(msg):
sys.stderr.write("WARNING: " + msg + "\n")
def unhide(objfile_in, objfile_out = None, namedsyms=None):
if not objfile_out:
objfile_out = objfile_in
binary = lief.parse(objfile_in)
allsyms = { sym.name for sym in binary.symbols }
selectedsyms = set([])
nasyms = { sym.name for sym in binary.symbols if \
sym.type == SYMBOL_TYPES.NOTYPE or \
sym.binding != SYMBOL_BINDINGS.GLOBAL or \
sym.visibility != SYMBOL_VISIBILITY.HIDDEN }
if namedsyms:
namedsyms = set(namedsyms)
nosyms = namedsyms - allsyms
for nosym in nosyms:
warn("No symbol " + nosym + " in " + objfile_in + ": ignored")
for sym in namedsyms & nasyms:
warn("Input symbol " + sym + \
" is not a hidden global symbol defined in " + objfile_in + \
": ignored")
selectedsyms = namedsyms - nosyms
else:
selectedsyms = allsyms
selectedsyms -= nasyms
unhidden = 0;
for sym in binary.symbols:
if sym.name in selectedsyms:
sym.visibility = SYMBOL_VISIBILITY.DEFAULT
unhidden += 1
print("Unhidden: " + sym.name)
print("{} symbols were unhidden".format(unhidden))
binary.write(objfile_out)
def get_args():
parser = argparse.ArgumentParser(
description="Replace hidden with default visibility on " + \
"global symbols defined in an ELF object file.")
parser.add_argument("ELFIN",help="ELF object file to read")
parser.add_argument("-s","--symbol",metavar="SYMBOL",action="append",
help="Unhide SYMBOL. " + \
"If unspecified, unhide all hidden global symbols defined in ELFIN")
parser.add_argument("--symfile",
help="File of whitespace-delimited symbols to unhide")
parser.add_argument("-o","--out",metavar="ELFOUT",
help="ELF object file to write. If unspecified, rewrite ELFIN")
return parser.parse_args()
def main():
args = get_args()
objfile_in = args.ELFIN
objfile_out = args.out
symlist = args.symbol
if not symlist:
symlist = []
symfile = args.symfile
if symfile:
with open(symfile,"r") as fh:
symlist += [word for line in fh for word in line.split()]
unhide(objfile_in,objfile_out,symlist)
main()
Использование:
$ ./unhide.py -h
usage: unhide.py [-h] [-s SYMBOL] [--symfile SYMFILE] [-o ELFOUT] ELFIN
Replace hidden with default visibility on global symbols defined in an ELF
object file.
positional arguments:
ELFIN ELF object file to read
optional arguments:
-h, --help show this help message and exit
-s SYMBOL, --symbol SYMBOL
Unhide SYMBOL. If unspecified, unhide all hidden
global symbols defined in ELFIN
--symfile SYMFILE File of whitespace-delimited symbols to unhide
-o ELFOUT, --out ELFOUT
ELF object file to write. If unspecified, rewrite
ELFIN
А вот сценарий оболочки:
unhide.sh
#!/bin/bash
OLD_ARCHIVE=$1
NEW_ARCHIVE=$2
OBJS=$(ar t $OLD_ARCHIVE)
for obj in $OBJS; do
rm -f $obj
ar xv $OLD_ARCHIVE $obj
./unhide.py $obj
done
rm -f $NEW_ARCHIVE
ar rcs $NEW_ARCHIVE $OBJS
echo "$NEW_ARCHIVE made"
которая принимает:
$1
= имя существующей статической библиотеки $2
= имя новой статической библиотеки
и создает $2
, содержащий объектные файлы из $1
, каждый из которых изменен с unhide.py
, чтобы показать все его скрытое глобальное определениеs.
Вернувшись к нашей иллюстрации, мы можем запустить:
$ ./unhide.sh libabde.a libnew.a
x - a.o
0 symbols were unhidden
x - b.o
Unhidden: bb
1 symbols were unhidden
x - de.o
Unhidden: ee
1 symbols were unhidden
libnew.a made
и подтвердить, что работал с:
$ readelf -s libnew.a | grep HIDDEN; echo Done
Done
$ readelf -s libnew.a | egrep '(aa|bb|dd|ee)'
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 aa
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 bb
11: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 dd
14: 0000000000000013 19 FUNC GLOBAL DEFAULT 1 ee
Наконец, если мы повторно связываем общую библиотеку сновый архив
$ gcc -o libabde.so -shared -Wl,--whole-archive libnew.a -Wl,--no-whole-archive
экспортируются все глобальные символы из архива:
$ readelf --dyn-syms libabde.so | egrep '(aa|bb|dd|ee)'
6: 0000000000001105 19 FUNC GLOBAL DEFAULT 12 aa
7: 000000000000112b 19 FUNC GLOBAL DEFAULT 12 dd
8: 0000000000001118 19 FUNC GLOBAL DEFAULT 12 bb
9: 000000000000113e 19 FUNC GLOBAL DEFAULT 12 ee
[1] Скачать библиотеки C / C ++ / Python
Debian / Ubuntu предоставляет пакет разработки для C / C ++ lief-dev
.