LINUX.ORG.RU

Почему сильный символ может не перекрывать слабый символ?

 , , strong symbol, weak symbol


0

1

Есть либа с со слабым символом, который одновременно используется и экспортится. И есть либа с такимже но сильным символом. Так вот при вызове символа из основного приложения используется сильная версия, а при вызове из первой либы - преимущественно слабая. Что может быть?

★★★★★
Ответ на: комментарий от sf

Прям на сейчас - нету. Но могу сказать что речь идет о перекрытии одного из слабых символов glibc. write() подходячий пример

cvv ★★★★★
() автор топика
Последнее исправление: cvv (всего исправлений: 1)
Ответ на: комментарий от cvv

Обычно glibc не позволяет заменить свои внутренности через LD_PRELOAD или weak/strong override. Чтобы не сломать bootstrap (ld.so: настройка релокаций), чтобы работать чуть быстрее (без лишнего jmp в PLT) и др.

Как это работает.

Для write glibc-2.23 определяет __write и реэкспортирует его как write.

$ nm -D /lib/libc.so.6 | grep write
00000000000d9530 W write
00000000000d9530 W __write

__write является __atrribute_((visibility(«hidden»))) символом локальным для модуля glibc.

В результате gcc ренерит вызов функции __write не через PLT (call __write@PLT), а по «абсолютному» (на x86 инструкция call принимает смещение относительно IP) адресу: call __write.

PLT бы позволил заменить символ через динамическую релокацию (через weak/strong override или LD_PRELOAD).

Но статический вызов резолвится на стадии сборки самой libc.so.6. Легко проверить что все вызовы __write не идут через PLT (или аналог):

$ objdump -R -d /lib/libc.so.6 | fgrep '__write'
   208f1:	e9 3a 8c 0b 00       	jmpq   d9530 <__write>
   2c0a6:	e8 85 d4 0a 00       	callq  d9530 <__write>
   6fa5b:	e8 d0 9a 06 00       	callq  d9530 <__write>
00000000000d9530 <__write>:
   d9537:	75 10                	jne    d9549 <__write+0x19>
   ...

В том же файле код самой __write:

00000000000d9530 <__write>:
   d9530:       83 3d 49 c2 2b 00 00    cmpl   $0x0,0x2bc249(%rip)        # 395780 <argp_program_version_hook+0x1e0>
   d9537:       75 10                   jne    d9549 <__write+0x19>
   d9539:       b8 01 00 00 00          mov    $0x1,%eax
   d953e:       0f 05                   syscall 

Для вызова через PLT релокации должны иметь вид write@plt. Как в yes:

$ objdump -r -R -d /bin/yes | fgrep -A4 write@plt

00000000004012c0 <write@plt>:
  4012c0:	ff 25 9a 5d 20 00    	jmpq   *0x205d9a(%rip)        # 607060 <__ctype_b_loc@plt+0x205af0>

--
  40171b:	e8 a0 fb ff ff       	callq  4012c0 <write@plt>
  401720:	48 83 f8 ff          	cmp    $0xffffffffffffffff,%rax
  401724:	75 ea                	jne    401710 <__ctype_b_loc@plt+0x1a0>
  401726:	31 ff                	xor    %edi,%edi
  401728:	ba 05 00 00 00       	mov    $0x5,%edx

$ objdump -R /bin/yes | grep write
0000000000607060 R_X86_64_JUMP_SLOT  write

Немного оффтопа:

Обычные (не hidden) extern C/C++-символы идут через PLT даже для функций внутри одной библиотеки. Это мешает LTO инлайнить функции между модулями одной библиотеки. Новые GCC имеют флаг -fno-semantic-interposition, чтобы иногда отключать это поведение:

http://hubicka.blogspot.com.by/2015/04/GCC5-IPA-LTO-news.html

sf ★★★
()
Ответ на: комментарий от sf

Благодарю. Это многое прояснило. Хотя раньше я без проблем перекрывал не-PLT вызовы и это мне создавало сложно - решаемые проблемы. Я намекаю на gcc 3.3.xxx и близкие

cvv ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.