Переопределение функций lin libC

#c #libc

#c #libc

Вопрос:

Предположим, что используется следующий простой C код:

file1.c

 #include <stdio.h>

char* gets(char* i){
  return i;
}
  

которое переопределяет собственную функцию libC gets .

Это отлично компилируется с gcc file1.c .

Мой вопрос в том, почему компоновщик не жалуется на duplicate symbols , поскольку эта функция также определена в самой libC?

Комментарии:

1. Примечание: gets() отсутствует в стандартной библиотеке C11 / C17.

2. компоновщик @chux предупреждает с помощью warning: the получения ‘функция опасна и ее не следует использовать` при ее использовании. Я использую C11.

3. Алекс, обязательно попробуй gcc -std=c11 -Wall -Wextra -Wconversion file1.c

4. предупреждение — это другой вопрос и связано с защитой от переполнения

5. предупреждение о gets() НЕ о защите от переполнения, а скорее о функции gets() , которая годами устаревала и была полностью удалена в последних двух версиях языка программирования C.

Ответ №1:

Поскольку в C можно переопределять функции из стандартных библиотек, проверьте это

Комментарии:

1. Переопределение функций стандартной библиотеки — это неопределенное поведение, хотя оно часто (но не всегда) «работает так, как ожидалось». Смотрите раздел 7.1.3 (Зарезервированные идентификаторы) стандарта C, в частности параграф 2.

Ответ №2:

добавьте опцию -весь архив на этапе компоновки, как показано ниже:

 gcc -c -ofile1.o file1.c
gcc -ofile1 -Wl,--whole-archive -lc file1.o -Wl,--no-whole-archive
  

Результат:

 file1.o: In function `gets':
file1.c:(.text 0x0): multiple definition of `gets'
file1.o: In function `main':
file1.c:(.text 0x18): undefined reference to `gets@@GLIBC_2.2.5'
collect2: error: ld returned 1 exit status
  

Ответ №3:

Вы столкнулись с неопределенным поведением, см. N1570 6.9(p5) External definition :

Если идентификатор, объявленный с внешней привязкой, используется в выражении (кроме как как часть операнда оператора sizeof or _Alignof , результатом которого является целочисленная константа), где-то во всей программе должно быть ровно одно внешнее определение идентификатора; в противном случае их должно быть не более одного.161)

Как можно видеть. Стандарт допускает 2 возможности:

  1. ровно одно определение
  2. не более одного определения.

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

Если вы взглянете на, objdump вы найдете plt раздел, подобный

 898:   e8 b3 fe ff ff          callq  750 <gets@plt>
  

В любом случае поведение не определено.