Jump to content

Слабый символ

Слабый символ обозначает специально аннотированный символ во время связывания исполняемого и связываемого формата (ELF) объектных файлов . По умолчанию, без каких-либо аннотаций, символ в объектном файле является строгим . Во время связывания сильный символ может переопределить слабый символ с тем же именем. Напротив, при наличии двух сильных символов с одинаковым именем компоновщик разрешает символ в пользу первого найденного. Такое поведение позволяет исполняемому файлу переопределять стандартные библиотечные функции, такие как malloc (3). При связывании двоичного исполняемого файла слабо объявленный символ не нуждается в определении. Для сравнения: (по умолчанию) объявленный строгий символ без определения вызывает ошибку ссылки на неопределенный символ.

Слабые символы не упоминаются стандартами языков C или C++; как таковые, вставка их в код не очень переносима. Даже если две платформы поддерживают один и тот же или похожий синтаксис для обозначения символов как слабых, семантика может различаться в тонких моментах, например, теряют ли слабые символы во время динамического связывания во время выполнения свою семантику или нет. [1]

Синтаксис

[ редактировать ]

Коллекция компиляторов GNU и компилятор Solaris Studio C используют один и тот же синтаксис для аннотирования символов как слабых, а именно специальный #pragma , #pragma weakи, альтернативно, атрибут функции и переменной, __attribute__((weak)). [2] [3] [4] [5] [6] [7]

// function declaration
#pragma weak power2

int power2(int x);
// function declaration

int __attribute__((weak)) power2(int x);

  // or

int power2(int x) __attribute__((weak));

// variable declaration;
extern int __attribute__((weak)) global_var;

Поддержка инструментов

[ редактировать ]

Команда nm идентифицирует слабые символы в объектных файлах, библиотеках и исполняемых файлах. В Linux символ слабой функции отмечается буквой «W», если доступно слабое определение по умолчанию, и буквой «w», если его нет. Слабо определенные символы переменных отмечаются буквами «V» и «v». В Solaris «nm» печатает «WEAK» вместо «GLOB» для слабого символа.

Следующие примеры работают в Linux и Solaris с GCC и Solaris Studio.

Статический пример

[ редактировать ]

основной.с :

#include <stdio.h>
#include <stdlib.h>

#include "power_slow.h"

int main(int argc, char **argv)
{
  fprintf(stderr, "power3() = %d\n", power3(atoi(argv[1])));
  return 0;
}

power_slow.h:

#ifndef POWER2_SLOW_H
#define POWER2_SLOW_H

// alternative syntax
// #pragma weak power2
int
  __attribute__((weak))
    power2(int x)
      // alternatively after symbol
      // __attribute__((weak))
  ;

int power3(int x);

#endif

power_slow.c :

#include <stdio.h>
#include "power_slow.h"

int power2(int x)
{
  fprintf(stderr, "slow power2()\n");
  return x*x;
}

int power3(int x)
{
  return power2(x)*x;
}

мощность.с :

#include <stdio.h>
int power2(int x)
{
  fprintf(stderr, "fast power2()\n");
  return x*x;
}

Команды сборки:

cc -g -c -o main.o main.c
cc -g -c -o power_slow.o power_slow.c
cc -g -c -o power.o power.c
cc  main.o power_slow.o         -o slow
cc  main.o power_slow.o power.o -o fast

Выход:

$ ./slow 3
slow power2()
power3() = 27
$ ./fast 3
fast power2()
power3() = 27

При удалении слабого атрибута и повторном выполнении команд сборки последняя завершается с ошибкой со следующим сообщением об ошибке (в Linux):

multiple definition of `power2'

Предпоследний все еще преуспевает, и ./slow имеет тот же выход.

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

Общий пример

[ редактировать ]

Взяв файл main.c из предыдущего примера и добавив:

#ifndef NO_USER_HOOK
void user_hook(void)
{
  fprintf(stderr, "main: user_hook()\n");
}
#endif

Замена power_slow.c на:

#include <stdio.h>
#include "power_slow.h"

void __attribute__((weak)) user_hook(void);
#ifdef ENABLE_DEF
void user_hook(void)
{
  fprintf(stderr, "power_slow: user_hook()\n");
}
#endif

int power2(int x)
{
  if (user_hook) // only needed ifndef ENABLE_DEF
    user_hook();
  return x*x;
}

int power3(int x)
{
  return power2(x)*x;
}

Команды сборки:

cc -g -c -o main.o main.c
cc -g -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main

cc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main2

cc -g -DNO_USER_HOOK -c -o main.o main.c
cc -g -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main3

cc -g -DNO_USER_HOOK -c -o main.o main.c
cc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc  main.o -L`pwd` -Wl,-R`pwd` -lpowerslow -o main4

Выход:

$ ./main 3
main: user_hook()
power3() = 27
$ ./main2 3
main: user_hook()
power3() = 27
$ ./main3 3
power3() = 27
$ ./main4 3
power_slow: user_hook()
power3() = 27

Удаление слабого атрибута и повторное выполнение команд сборки не приводит к ошибкам сборки и приводит к тому же результату (в Linux) для main и main2. Команды сборки для main3 приведет к появлению следующих предупреждений и сообщений об ошибках (в Linux):

warning: the address of ‘user_hook’ will always evaluate as ‘true’
libpowerslow.so: undefined reference to `user_hook'

Предупреждение выдается компилятором, поскольку он может статически определить, что в if (user_hook) выражение user_hook всегда оценивается как true, поскольку содержит запись таблицы переходов ELF. Сообщение об ошибке выдается компоновщиком. Сборка для main4 включает то же предупреждение, но без ошибки связи.

Варианты использования

[ редактировать ]

Слабые символы можно использовать в качестве механизма для предоставления реализаций функций по умолчанию, которые могут быть заменены более специализированными (например, оптимизированными) во время компоновки. Затем реализация по умолчанию объявляется слабой, и для определенных целей в командную строку компоновщика добавляются объектные файлы со строго объявленными символами.

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

Другой вариант использования слабых символов — обеспечение обратной двоичной совместимости .

Ограничения

[ редактировать ]

В системах-потомках UNIX System V во время выполнения программы динамический компоновщик разрешает определения слабых символов как сильные. Например, двоичный файл динамически связывается с библиотеками libfoo.so и libbar.so. libfoo определяет символ f и объявляет его слабым. libbar также определяет f и объявляет его сильным. В зависимости от порядка библиотеки в командной строке ссылки (т.е. -lfoo -lbar) динамический компоновщик использует слабую версию f из libfoo.so, хотя во время выполнения доступна сильная версия. ГНУ ld предоставляет переменную среды LD_DYNAMIC_WEAK чтобы обеспечить слабую семантику для динамического компоновщика. [1] [8]

При использовании таких конструкций, как

#pragma weak func
void func();

void bar()
{
  if (func)
    func();
}

, в зависимости от компилятора и используемого уровня оптимизации, компилятор может интерпретировать условие как всегда истинное (поскольку func может рассматриваться как неопределенный с точки зрения стандартов). [7] Альтернативой приведенной выше конструкции является использование системного API для проверки того, func определен (например, dlsym с RTLD_DEFAULT). Вышеуказанная проверка может также завершиться неудачно по другим причинам, например, если func содержит запись таблицы переходов elf. [9]

Использование слабых символов в статических библиотеках имеет другую семантику, чем в общих, т.е. в статической библиотеке поиск символа останавливается на первом символе – даже если он просто слабый и в архив библиотеки также включен объектный файл с сильным символом. В Linux опция компоновщика --whole-archive меняет это поведение. [10]

Предполагается, что атрибут слабой функции будет использоваться в объявлениях функций. Использование его в определении функции может привести к неожиданным результатам, в зависимости от компилятора и уровня оптимизации. [11]

В Solaris слабые символы также используются внутри ядра. Общая часть ядра (называемая genunix) определяет слабые функции, которые переопределяются в специфичной для платформы части ядра (называемой unix), такие как процедуры виртуальной памяти. Компоновщик времени выполнения ядра устанавливает адреса этих функций, когда ядро ​​объединяется в памяти во время загрузки. Однако это не работает для загружаемых модулей ядра — слабый символ в ядре не заменяется символом модуля ядра при загрузке модуля.

[ редактировать ]

Условные конструкции препроцессора C (CPP) также можно использовать для переключения между различными версиями символа. Отличие от слабых символов состоит в том, что слабые символы интерпретируются компоновщиком. CPP запускается во время компиляции каждой единицы трансляции перед компилятором C.

Процесс сборки (например, make) может быть реализован условным образом, так что в зависимости от цели создаются просто разные версии символа или используются и компонуются разные (специализированные) библиотеки.

См. также

[ редактировать ]
  1. ^ Jump up to: а б Дреппер, Ульрих (7 июня 2000 г.). «слабая управляемость» .
  2. ^ «Руководство GCC, 6.58.9 Слабые прагмы» .
  3. ^ «Руководство GCC, 6.30 Объявление атрибутов функций» . ГНУ . Проверено 29 мая 2013 г.
  4. ^ «Руководство GCC, 6.36. Указание атрибутов переменных» .
  5. ^ «Oracle Solaris Studio 12.3: Руководство пользователя C, 2.11.27 слабая» .
  6. ^ «Oracle Solaris Studio 12.3: Руководство пользователя C, 2.9 Поддерживаемые атрибуты» .
  7. ^ Jump up to: а б «Руководство по компоновщику и библиотекам Oracle Solaris 11 Express 11/10, 2.11 Слабые символы» .
  8. ^ Дреппер, Ульрих (октябрь 2011 г.). «Как писать общие библиотеки (версия 4.1.2), 1.5.2 Перемещение символов, страница 6» (PDF) .
  9. ^ «Слабое связывание и общие библиотеки Linux» .
  10. ^ «Страница руководства GNU LD» .
  11. ^ Кишка, Ян (23 мая 2006 г.). «Re: чрезмерная оптимизация слабых атрибутов в версии 4.1» .
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: b06f3ed6a6ac99169d4954db6ea45cf5__1688802240
URL1:https://arc.ask3.ru/arc/aa/b0/f5/b06f3ed6a6ac99169d4954db6ea45cf5.html
Заголовок, (Title) документа по адресу, URL1:
Weak symbol - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)