Сохраните все экспортированные символы при создании общей библиотеки из статической библиотеки - PullRequest
0 голосов
/ 13 февраля 2019

Я создаю общую библиотеку из статической библиотеки, для которой у меня нет исходного кода.

Многие вопросы переполнения стека предоставляют ответов о том, как это сделать:

gcc -shared -o libxxx.so -Wl,--whole-archive libxxx.a -Wl,--no-whole-archive

Однако некоторые открытые функции статической библиотеки включены в общую библиотеку как скрытые функции:

$ 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 преобразован из экспортированного в скрытый.

Этоне единственный символ, который был скрыт в процессе:

$ 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.

Могу ли я что-нибудь сделать, чтобы иметьлинкер создает общую библиотеку со всеми открытыми символами из статической библиотеки, также открытыми в общей библиотеке?

1 Ответ

0 голосов
/ 14 февраля 2019

Результаты, которые вы наблюдаете, когда некоторые глобальные определения символов в некоторых объектных файлах, заархивированных в 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.

...