从静态库创建共享库时,请保留所有导出的符号 [英] Keep all exported symbols when creating a shared library from a static library
问题描述
我正在从没有源代码的静态库中创建共享库.
I am creating a shared library from a static library for which I do not have the source code.
Many Stack Overflow questions provide answers on how to do that:
gcc -shared -o libxxx.so -Wl,--whole-archive libxxx.a -Wl,--no-whole-archive
但是,静态库的一些公共函数作为共享库中的隐藏函数包括在内:
However, some public functions of the static library are included as hidden functions in the shared library:
$ nm --defined-only libxxx.a | grep __intel_cpu_indicator_init
0000000000000000 T __intel_cpu_indicator_init
$ nm libxxx.so | grep __intel_cpu_indicator_init
00000000030bb160 t __intel_cpu_indicator_init
__ intel_cpu_indicator_init符号从导出变为隐藏.
The __intel_cpu_indicator_init symbol went from exported to hidden.
它不是该过程中唯一隐藏的符号:
It is not the only symbol that was hidden in the process:
$ nm libxxx.a | grep ' T ' | wc -l
37969
$ nm libxxx.so | grep ' T ' | wc -l
37548
$ nm libxxx.a | grep ' t ' | wc -l
62298
$ nm libxxx.so | grep ' t ' | wc -l
62727
请注意,37969 + 62298 = 100267和37548 + 62727 = 100275.
Note that 37969 + 62298 = 100267 and 37548 + 62727 = 100275.
我可以做些什么使链接程序生成一个共享库,其中包含静态库中的所有公共符号,并且在共享库中也是公共的吗?
Is there anything I can do to have the linker produce a shared library with all public symbols from the static library also public in the shared library ?
推荐答案
当某些
使用函数属性编译在libxxx.a
中归档的目标文件.
或变量属性
What you observe results when some of the global symbol definitions in some of
the object files archived in libxxx.a
were compiled with the function attribute
or variable attribute visibility("hidden")
此属性的作用是,当目标文件包含 全局符号定义链接到共享库中:
This attribute has the effect that when the object file containing the the global symbol definition is linked into a shared library:
- 在输出共享库的静态符号表(
.symtab
)中,符号的链接从全局更改为局部, 这样,当该共享库与其他任何库链接时,链接器将看不到符号的定义. - 将符号定义 not 添加到输出共享库的 dynamic 符号表(
.dynsym
)中(默认情况下为) 因此,当将共享库加载到进程中时,加载器同样无法找到该符号的定义.
- The linkage of the symbol is changed from global to local in the static symbol table (
.symtab
) of the output shared library, so that when that shared library is linked with anything else, the linker cannot see the definition of the symbol. - The symbol definition is not added to the dynamic symbol table (
.dynsym
) of the output shared library (which by default it would be) so that when the shared library is loaded into a process, the loader is likewise unable to find a definition of the symbol.
简而言之,出于动态链接的目的,隐藏了目标文件中的全局符号定义.
In short, the global symbol definition in the object file is hidden for the purposes of dynamic linkage.
使用以下方法进行检查:
Check this out with:
$ readelf -s libxxx.a | grep HIDDEN
,我希望您能获得未导出的全局符号的点击量.如果没有的话 您无需再阅读,因为我对您所看到的内容没有其他解释 而且不会指望我建议不要用脚射击您的解决方法.
and I expect you to get hits for the unexported global symbols. If you don't, you need read no further because I have no other explanation of what you see and wouldn't count on any workaround I suggested not to shoot you in the foot.
这是一个例子:
交流
#include <stdio.h>
void aa(void)
{
puts(__func__);
}
b.c
#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
在它们各自的对象文件中已定义并且是全局的:
And we can see that symbols aa
and ab
are defined and global in their respective object files:
$ 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
但是我们也可以观察到这种差异:
But we can also observe this difference:
$ 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
是具有DEFAULT
可见性的GLOBAL
符号,而bb
是GLOBAL
具有HIDDEN
可见性的符号.
aa
is a GLOBAL
symbol with DEFAULT
visibility and bb
is a GLOBAL
symbol with HIDDEN
visibility.
我们将以不同的方式编译de.c
:
We'll compile de.c
differently:
$ gcc -Wall -fvisibility=hidden -c de.c
在这里,我们指示编译器将任何符号都隐藏起来
可见性,除非为其指定了抵消性visibility
属性
它在源代码中.因此,我们看到:
Here, we're instructing the compiler that any symbol shall be given hidden
visibility unless a countervailing visibility
attribute is specified for
it in the source code. And accordingly we see:
$ 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
在静态库中归档这些目标文件绝不会改变它们:
Archiving these object files in a static library changes them in no way:
$ ar rcs libabde.a a.o b.o de.o
然后,如果我们将它们全部链接到共享库中:
And then if we link all of them into a shared library:
$ 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
,它们是目标文件中具有HIDDEN
可见性的GLOBAL
在libabde.so
的静态符号中为LOCAL
,并且完全不存在
从其动态符号表中获取.
bb
and ee
, which were GLOBAL
with HIDDEN
visibility in the object files,
are LOCAL
in the static symbol of libabde.so
and are absent altogether
from its dynamic symbol table.
鉴于此,您可能希望重新评估您的任务:
在libxxx.a
中的目标文件中具有隐藏可见性的符号具有
之所以被隐藏是因为编译它们的人有理由
希望将他们隐藏在动态联系之外.您是否有需要的反补贴
导出它们以进行动态链接?还是您只想导出它们,因为
您已经注意到它们没有导出,也不知道为什么不这样做?
The symbols that have been given hidden visibility in the object files in libxxx.a
have
been hidden because the person who compiled them had a reason for
wishing to conceal them from dynamic linkage. Do you have a countervailing need
to export them for dynamic linkage? Or do you maybe just want to export them because
you've noticed that they're not exported and don't know why not?
但是,如果您想取消隐藏隐藏的符号,并且无法更改源代码
在libxxx.a
中归档的目标文件中,最不希望采取的措施是:
If you nonetheless want to unhide the hidden symbols, and cannot change the source code
of the object files archived in libxxx.a
, your least worst resort is to:
- 从
libxxx.a
提取每个目标文件
- 用它对其全局定义的可见性替换为
DEFAULT
来替换HIDDEN
- 将其放入新的存档
libyyy.a
- 然后使用
libyyy.a
而不是libxxx.a
.
- Extract each object file from
libxxx.a
- Doctor it to replace
HIDDEN
withDEFAULT
visibility on its global definitions - Put it into a new archive
libyyy.a
- Then use
libyyy.a
instead oflibxxx.a
.
用于篡改目标文件的binutils
工具为 objcopy
.
但是objcopy
没有任何操作可以直接操纵广告的动态可见性
一个符号,那么您就必须解决实现效果的l回曲折"
的"隐藏隐藏的符号:
The binutils
tool for doctoring object files is objcopy
.
But objcopy
has no operations to directly manipulate the dynamic visibility of
a symbol and you'd have to settle for a circuitous kludge that "achieves the effect
of" unhiding the hidden symbols:
- 使用
objcopy --redefine-sym
,将每个隐藏的全局符号S
重命名为__hidden__S
. - 使用
objcopy --add-symbol
,添加一个新的全局符号S
,该符号的值与__hidden_S
相同 但默认情况下会显示DEFAULT
可见性.
- With
objcopy --redefine-sym
, rename each hidden global symbolS
as, say,__hidden__S
. - With
objcopy --add-symbol
, add a new global symbolS
that has the same value as__hidden_S
but getsDEFAULT
visibility by default.
最后出现两个具有相同定义的符号:原始隐藏的一个 以及一个新的未隐藏的别名.
ending up with two symbols with the same definition: the original hidden one and a new unhidden alias for it.
更可取的是,一种简单而又改变符号在显示器中的可见性的方法. 一个ELF目标文件,并且一种方法是提交 LIEF库(仪器可执行格式库)- Swiss Army Chainsaw用于对象和可执行文件的更改 1 .
Preferable to that would a means of simply and solely changing the visibility of a symbol in an ELF object file, and a means is to hand in the LIEF library (Library to Instrument Executable Formats) - Swiss Army Chainsaw for object and executable file alterations1.
这是一个Python脚本,它调用LIEF Python模块pylief
取消隐藏
ELF对象文件中的隐藏全局变量:
Here is a Python script that calls on pylief
, the LIEF Python module, to unhide the
hidden globals in an ELF object file:
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
这是一个shell脚本:
And here is a shell script:
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
=新静态库的名称
$1
= Name of an existing static library$2
= Name for a new static library
并创建包含来自$1
的目标文件的$2
,每次修改
使用unhide.py
取消隐藏其所有隐藏的全局定义.
and creates $2
containing the object files from $1
, each modified
with unhide.py
to unhide all of its hidden global definitions.
返回我们的插图,我们可以运行:
Back with our illustration, we can run:
$ ./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
最后,如果我们将共享库与新档案重新链接
Finally if we relink the shared library with the new archive
$ gcc -o libabde.so -shared -Wl,--whole-archive libnew.a -Wl,--no-whole-archive
存档中的所有全局符号都已导出:
all of the global symbols from the archive are exported:
$ 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库
[1] Download C/C++/Python libraries
Debian/Ubuntu提供了C/C ++开发包lief-dev
.
Debian/Ubuntu provides C/C++ dev package lief-dev
.
这篇关于从静态库创建共享库时,请保留所有导出的符号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!