Смешивание объектов PIC и не-PIC в общей библиотеке - PullRequest
6 голосов
/ 30 ноября 2011

Этот вопрос относится к к этому , а также к его ответу.

Я только что обнаружил какое-то уродство в сборке, над которой я работаю.Ситуация выглядит примерно так (написано в формате gmake);обратите внимание, это особенно относится к 32-разрядной модели памяти на оборудовании sparc и x86:

OBJ_SET1  := some objects
OBJ_SET2  := some objects

# note: OBJ_SET2 doesn't get this flag
${OBJ_SET1} : CCFLAGS += -PIC

${OBJ_SET1} ${OBJ_SET2} : %.o : %.cc
  ${CCC} ${CCFLAGS} -m32 -o ${@} -c ${<}

obj1.o       : ${OBJ_SET1}
obj2.o       : ${OBJ_SET2}
sharedlib.so : obj1.o obj2.o
obj1.o obj2.o sharedlib.so :
  ${LINK} ${LDFLAGS} -m32 -PIC -o ${@} ${^}

Очевидно, что он может работать для смешивания объектов, скомпилированных с PIC и без него, в общем объекте (это использовалось длягода).Я не знаю достаточно о PIC, чтобы понять, хорошая ли это идея / умная, и я думаю, что в этом случае она не нужна, а скорее происходит, потому что кому-то было все равно, чтобы найти правильный способ сделать это при взломена новый материал для сборки.

Мой вопрос:

  1. Это безопасно
  2. Это хорошая идея
  3. Какие потенциальные проблемы могутпроисходят в результате
  4. Если я переключу все на PIC, есть ли какие-то неочевидные ошибки, за которыми я бы хотел следить.

1 Ответ

4 голосов
/ 05 апреля 2013

Забыл, я даже написал этот вопрос.

Некоторые объяснения в порядке:

  • Код не PIC может быть загружен ОС в любую позицию в памяти в [большинстве?] Современных ОС. После того, как все загружено, оно проходит фазу, которая фиксирует текстовый сегмент (где заканчивается исполняемый материал), чтобы он правильно обращался к глобальным переменным; чтобы выполнить это, текстовый сегмент должен быть доступен для записи.
  • Исполняемые данные PIC могут загружаться ОС один раз и совместно использоваться несколькими пользователями / процессами. Однако для того, чтобы ОС сделала это, текстовый сегмент должен быть доступен только для чтения, что означает отсутствие исправлений. Код скомпилирован для использования Глобальной таблицы смещений (GOT), чтобы он мог адресовать глобальные переменные относительно GOT, устраняя необходимость исправлений.
  • Если общий объект создается без PIC, хотя это настоятельно рекомендуется, то, по-видимому, это не обязательно; если операционная система должна исправить текстовый сегмент, то она вынуждена загрузить его в память, помеченную как чтение-запись ..., что препятствует совместному использованию между процессами / пользователями.
  • Если исполняемый бинарный файл создается / с / PIC, я не знаю, что происходит под капотом, но я был свидетелем того, как несколько инструментов стали нестабильными (таинственные сбои и тому подобное).

Ответы:

  • Смешивание PIC / non-PIC или использование PIC в исполняемых файлах может затруднить прогнозирование и отслеживание нестабильности. У меня нет технического объяснения почему.
    • ... чтобы включить ошибки сегмента, ошибки шины, повреждение стека и, возможно, многое другое.
  • Не-PIC в общих объектах, вероятно, не вызовет каких-либо серьезных проблем, хотя это может привести к увеличению объема используемой оперативной памяти, если библиотека будет использоваться много раз между процессами и / или пользователями.

обновление (4/17)

С тех пор я обнаружил причину некоторых аварий, которые я видел ранее. Для иллюстрации:

/*header.h*/
#include <map>
typedef std::map<std::string,std::string> StringMap;
StringMap asdf;

/*file1.cc*/
#include "header.h"

/*file2.cc*/
#include "header.h"

int main( int argc, char** argv ) {
  for( int ii = 0; ii < argc; ++ii ) {
    asdf[argv[ii]] = argv[ii];
  }

  return 0;
}

... тогда:

$ g++ file1.cc -shared -PIC -o libblah1.so
$ g++ file1.cc -shared -PIC -o libblah2.so
$ g++ file1.cc -shared -PIC -o libblah3.so
$ g++ file1.cc -shared -PIC -o libblah4.so
$ g++ file1.cc -shared -PIC -o libblah5.so

$ g++ -zmuldefs file2.cc -Wl,-{L,R}$(pwd) -lblah{1..5} -o fdsa
#     ^^^^^^^^^
#     This is the evil that made it possible
$ args=(this is the song that never ends);
$ eval ./fdsa $(for i in {1..100}; do echo -n ${args[*]}; done)

Этот конкретный пример может не закончиться сбоем, но в основном это ситуация, которая существовала в коде этой группы. Если он делает сбой, он, скорее всего, будет в деструкторе, обычно это ошибка с двойным освобождением.

Много лет назад они добавили -zmuldefs в свою сборку, чтобы избавиться от многократно определенных ошибок символов. Компилятор испускает код для запуска конструкторов / деструкторов на глобальных объектах. -zmuldefs вынуждает их жить в одном и том же месте в памяти, но он по-прежнему запускает конструкторы / деструкторы один раз для exe и каждой библиотеки, которая содержит заголовок, вызывающий сбой, - следовательно, double-free.

...