Как избежать нескольких определений? Линкер проигнорировал определенный символ с некоторым исключением - PullRequest
1 голос
/ 09 мая 2019

Я работаю над встроенной системой, которая имеет свою собственную библиотеку (например, libc_alt ), реализующую некоторые функции из стандартного libc ( fopen, fclose, fread, fseek, ftell ), но не все (мне нужно memset, memcpy, longjmp, setjmp, и т. д. из стандарта libc ). Когда я пытаюсь дать обе библиотеки для компилятора

Предоставление libc_alt

# makefile.mk 
(SRC_C += \
    $(COMP_PATH_libc_alt)/stdio.c \
) 

Предоставление стандартной библиотеки

STD_LIBS += -lc -lgcc

На этапе компоновщика компилятор (arm-eabi-gcc) по праву жалуется на множественные определения функции fclose (). Мой вопрос таков: возможно ли дать указание компилятору исключить стандартные определения libc fclose () и использовать мое определение, написанное в $ (COMP_PATH_libc_alt) /stdio.c?

Или, как я могу указать компилятору сначала использовать stdio.c , а затем использовать стандартный libc stdio.c , игнорируя повторяющиеся определения функций?

например. после того, как он нашел определение fopen () в $ (COMP_PATH_libc_alt) /stdio.c; он будет игнорировать fopen () внутри стандартного libc. Таким образом я могу использовать как libc_alt fopen () , так и стандартную libc memcpy (), memset ()

ОБНОВЛЕНИЕ: Спасибо всем за ответы. @artless_noise Я разместил свой stdio.c до -lc. Что касается использования --wrap symbol, если я правильно понимаю, ссылка на fclose() изменится на __wrap_fclose(), и мне нужно изменить имя моего fclose() в моем stdio.c на __wrap__fclose() ; к сожалению, изменение "my" stdio.c не подлежит обсуждению. На самом деле, самое странное, что, поскольку я помещаю свой stdio.c перед -lc, LinaroGCC arm-eabi-gcc смог выбрать мое определение для fopen(), fseek(), ftell(), fread() (отлично). Но это дает multiple definition ошибка для fclose()

Согласно https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Link-Options.html; на ld -l; компоновщик должен был игнорировать повторяющийся символ в -lc, который идет после моего libc_alt.a. И это, за одним исключением fclose(), и я не знаю почему?

Имеет значение, где в команде вы пишете эту опцию; компоновщик ищет и обрабатывает библиотеки и объектные файлы в указанном порядке. Таким образом, foo.o -lz bar.o ищет библиотеку z после файла foo.o, но до bar.o. Если bar.o ссылается на функции в z, эти функции могут не загружаться. Компоновщик ищет в стандартном списке каталогов библиотеку, которая на самом деле является файлом с именем liblibrary.a. Затем компоновщик использует этот файл, как если бы он был указан точно по имени. В число найденных каталогов входят несколько стандартных системных каталогов плюс любые, которые вы указываете с помощью -L. Обычно файлы, найденные таким образом, являются библиотечными файлами - архивными файлами, членами которых являются объектные файлы. Компоновщик обрабатывает архивный файл, просматривая его на предмет элементов, которые определяют символы, на которые до сих пор ссылались, но не определяли . Но если найденный файл является обычным объектным файлом, он связывается обычным способом. Единственное различие между использованием параметра -l и указанием имени файла заключается в том, что -l окружает библиотеку lib и .a и ищет несколько каталогов.

Как я вызываю компоновщик (libc_alt.a содержит "мой" stdio.o)

gcc-linaro-7.1.1-2017.08-x86_64_arm-eabi/bin/arm-eabi-gcc 
-Xlinker --wrap=fclose -nostdlib -Xlinker --gc-sections 
-Xlinker --fatal-warnings -Xlinker --no-wchar-size-warning 
-Xlinker --no-enum-size-warning -Xlinker --build-id=none -T
/home/alice/Tools/Out/trusted_application.ld -Xlinker -pie -o 
/home/alice/Out/Bin/taMbedTLS.axf  /home/alice/Locals/Code/aes.o 
/home/alice/Locals/Code/sha1.o /home/alice/libraries/libc_alt.a -lc -lgcc

Использование nm, содержимое моего libc_alt.a

stdio.o:
         U __aeabi_uidiv
000000d0 t $d
0000004c t $d
00000030 t $d
00000010 N $d
00000001 T fclose
00000001 T fopen
00000001 T fread
00000001 T fseek
00000001 T ftell
         U memset
         U __stack_chk_fail
         U __stack_chk_guard
         U strchr
         U strcmp
         U strlen

stdlib.o:

Использование nm, содержимое Linaro LinaroGCC arm-eabi-gcc libc.a

lib_a-fclose.o:
00000000 t $a
00000110 t $d
00000010 N $d
00000100 T fclose
00000000 T _fclose_r
         U _free_r
         U _impure_ptr
         U __sflush_r
         U __sfp_lock_acquire
         U __sfp_lock_release
         U __sinit

lib_a-fopen.o:
00000000 t $a
000000fc t $a
000000e8 t $d
00000110 t $d
00000010 N $d
000000fc T fopen
00000000 T _fopen_r
         U _fseek_r
         U _impure_ptr
         U _open_r
         U __sclose
         U __sflags
         U __sfp
         U __sfp_lock_acquire
         U __sfp_lock_release
         U __sread
         U __sseek
         U __swrite

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Вы можете сделать это. Вам не нужно инструктировать компилятор / компоновщик каким-либо особым образом, просто свяжите ваши собственные материалы, и они будут использованы.

Вот игрушечный пример, показывающий, как вы можете получить свою собственную реализацию fread ().

Создайте файл fopen_test.c вот так:

#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode) {
  printf("My own fopen implementation, just returning NULL.\n");
  return NULL;
}

Теперь это можно скомпилировать так:

gcc -c fopen_test.c

, который генерирует объектный файл fopen_test.o.

Если у вас есть следующая основная программа в fopen_test_main.c:

#include <stdio.h>
int main() {
  printf("Calling fopen()\n");
  FILE* f = fopen("file.txt", "rb");
  printf("After fopen()\n");
  return 0;
}

тогда вы можете увидеть, как эта программа ведет себя по-разному, в зависимости от того, связаны ли вы с fopen_test.o объектным файлом.

Сначала попробуйте использовать стандартную реализацию fopen ():

$ gcc fopen_test_main.c
$ ./a.out

, который дает следующий вывод:

Calling fopen()
After fopen()

Теперь попробуйте то же самое, но связать его с объектным файлом, содержащим специальную реализацию fopen:

$ gcc fopen_test_main.c fopen_test.o
$ ./a.out

, что дает следующее:

Calling fopen()
My own fopen implementation, just returning NULL.
After fopen()

Итак, теперь реализация fopen () в fopen_test.c используется вместо стандартной реализации.

(Я не знаю, почему у вас есть жалобы на «множественные определения», потребуются более подробные сведения о том, что вы сделали, чтобы это выяснить.)

0 голосов
/ 11 мая 2019

Вы не можете сделать это на языке c.

Что вы можете сделать лучше всего, так это написать свои функции под разными именами.

Например, вместо fopen внедрите функцию fileopen в свой код и используйте ее там, где вам нужно.

...