从静态库创建共享库时,请保留所有导出的符号 [英] Keep all exported symbols when creating a shared library from a static library

查看:320
本文介绍了从静态库创建共享库时,请保留所有导出的符号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从没有源代码的静态库中创建共享库.

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.cb.c:

$ gcc -Wall -c a.c b.c

我们可以看到符号aaab在它们各自的对象文件中已定义并且是全局的:

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符号,而bbGLOBAL 具有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

bbee,它们是目标文件中具有HIDDEN可见性的GLOBALlibabde.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 with DEFAULT visibility on its global definitions
  • Put it into a new archive libyyy.a
  • Then use libyyy.a instead of libxxx.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 symbol S as, say, __hidden__S.
  • With objcopy --add-symbol, add a new global symbol S that has the same value as __hidden_S but gets DEFAULT 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆