Рубрики

Как линкеры разрешают глобальные символы, определенные в нескольких местах?

Во время компиляции компилятор экспортирует каждый глобальный символ в ассемблер как сильный или слабый, и ассемблер неявно кодирует эту информацию в таблице символов перемещаемого объектного файла. Функции и инициализированные глобальные переменные получают сильные символы. Неинициализированные глобальные переменные получают слабые символы.
Для следующих примеров программ buf, bufp0, main и swap являются сильными символами; bufp1 — слабый символ.

/ * main.c * /

 void swap();

 int buf[2] = {1, 2};

 int main()

 {

   swap();

   return 0;

 }

   

 / * swap.c * /

 extern int buf[];

   

 int *bufp0 = &buf[0];

 int *bufp1;

   

 void swap()

 {

   int temp;

   

   bufp1 = &buf[1];

   temp = *bufp0;

   *bufp0 = *bufp1;

   *bufp1 = temp;

}

Учитывая это понятие сильных и слабых символов, линкеры Unix используют следующие правила для работы с несколькими определенными символами:
Правило 1: несколько сильных символов (с одинаковыми именами переменных) не допускаются.
Правило 2: учитывая сильный символ и несколько слабых символов, выберите сильный символ.
Правило 3: учитывая несколько слабых символов, выберите любой из слабых символов.
Например, предположим, что мы пытаемся скомпилировать и связать следующие два модуля C:

/ * foo1.c * /       

int main()          

{                   

  return 0;       

}                  

   
/ * bar1.c * /

int main()

{

  return 0;

}

В этом случае компоновщик сгенерирует сообщение об ошибке, поскольку сильный символ main определяется несколько раз ( правило 1 ):

$ gcc foo1.c bar1.c
/tmp/cca015022.o: In function ‘main’:
/tmp/cca015022.o(.text+0x0): multiple definition of ‘main’
/tmp/cca015021.o(.text+0x0): first defined here

Аналогично, компоновщик сгенерирует сообщение об ошибке для следующих модулей, поскольку сильный символ x определяется дважды ( правило 1 ):

/ * foo2.c * /

int x = 15213;

int main()

{

  return 0;

}

   
/ * bar2.c * /

int x = 15213;

void f()

{
}

Однако, если x неинициализирован в одном модуле, компоновщик спокойно выберет сильный символ, определенный в другом ( правило 2 ), как в следующей программе:

/ * foo3.c * /
#include <stdio.h>

void f(void);

int x = 15213;

int main()

{

  f();

  printf("x = %d\n", x);

  return 0;

}

   
/ * bar3.c * /

int x;

void f()

{

  x = 15212;

}

Во время выполнения функция f () изменяет значение x с 15213 на 15212, что может стать нежелательным сюрпризом для автора функции main! Обратите внимание, что компоновщик обычно не указывает, что он обнаружил несколько определений x.

$ gcc -o gfg foo3.c bar3.c
$ ./gfg
x = 15212

То же самое может случиться, если есть два слабых определения x ( правило 3 ):

/ * Ас * /
#include <stdio.h>

void b(void);

  

int x;

int main()

{

    x = 2016;

    b();

    printf("x = %d ",x);

    return 0;

}
/*До нашей эры*/
#include <stdio.h>

  

int x;

  

void b()

{

    x = 2017;

      
}

Применение правил 2 и 3 может привести к некоторым коварным ошибкам во время выполнения, которые непонятны неосторожному программисту, особенно если дубликаты определений символов имеют разные типы.
Пример: «x» определяется как int в одном модуле и как double в другом.

/ * Ас * /
#include <stdio.h>

void b(void); 

  

int x = 2016;

int y = 2017;

int main()

{

    b();

    printf("x = 0x%x y = 0x%x \n", x, y);

    return 0;

}
/*До нашей эры*/

double x;

  

void b()

{

    x = -0.0;

}

Исполнение:

$ gcc a.c b.c -o geeksforgeeks
$ ./geeksforgeeks
x = 0x0 y = 0x80000000

Это тонкий и неприятный баг, особенно потому, что он происходит тихо, без предупреждения от системы компиляции и потому, что обычно он проявляется намного позже при выполнении программы, далеко от того места, где произошла ошибка. В большой системе с сотнями модулей такую ошибку чрезвычайно трудно исправить, особенно потому, что многие программисты не знают, как работают линкеры. В случае сомнений вызовите компоновщик с флагом, таким как флаг gcc -fno-common, который вызывает ошибку, если он встречает несколько определенных глобальных символов.

Источник: http://csapp.cs.cmu.edu/public/ch7-preview.pdf

Эта статья предоставлена Сахилом Раджпутом . Если вы как GeeksforGeeks и хотели бы внести свой вклад, вы также можете написать статью с помощью contribute.geeksforgeeks.org или по почте статьи contribute@geeksforgeeks.org. Смотрите свою статью, появляющуюся на главной странице GeeksforGeeks, и помогите другим вундеркиндам.

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

Рекомендуемые посты:

Как линкеры разрешают глобальные символы, определенные в нескольких местах?

0.00 (0%) 0 votes