Поиск по сайту:

 


По базе:  

микроэлектроника, микросхема, микроконтроллер, память, msp430, MSP430, Atmel, Maxim, LCD, hd44780, t6963, sed1335, SED1335, mega128, avr, mega128  
  Главная страница > Статьи > Средства разработки

реклама

 




Мероприятия:




Библиотеки и карта компоновки

Некоторые часто используемые подпрограммы можно хранить в объектных файлах, а не компилировать каждый раз из исходных текстов. Множество объектных файлов собирается в статические библиотеки. Компоновщик работает с объектными файлами библиотеки напрямую, без необходимости извлечения отдельных объектных файлов. В статической библиотеке вместе с объектными модулями находится список, в котором содержатся имена функций и данных принадлежащих библиотеке. Посмотреть содержимое библиотеки можно утилитой arm-none-eabi-nm из инструментария. При разрешении имён, линковщик включит подходящие символы из первого подходящего объектного файла библиотеки и прекратит дальнейшее сканирование библиотеки. Если в библиотеке имеется несколько версий функции в разных объектных файлах, важен порядок расположения файлов в библиотеке.

Далее упражнение на создание и использование библиотек.


Рисунок 4

Пример программ намеренно создан простым. Библиотека libZXY.a содержит два модуля, AB.o и CD.o . В модулях sum.o и sub.o имеются ссыки на символы в модулях библиотеки. В свою очередь модуль main.o сылается на модули sum.o и sub.o. Тексты программ приведёны ниже.

	
Файл AB.c:
 int Bb = 5 ;
 int Ee = 1024 ;
 int Aa = 20 ;
Файл CD.c:
 int Cc = 20 ;
 int Dd = 5 ;
 const char Ff[]= "trash";
Файл sub.c:
extern Cc,Dd;
 int f_sub(void)
  { return (Cc - Dd);
  }
//==================
 int ff_sub(void)
   { return (2*Cc - 3*Dd);
    }
Файл sum.c:
extern Bb,Aa;
 int f_sum(void)
  { return (Bb + Aa);
   }
Файл main.c:
 int f_sum(void);
 int f_sub(void);
 int i;
 void main(void)
 {  i = f_sum();
    i = f_sub();
    for(;;);
 }
	

Командный файл компиляции:

	
#!/bin/bash

CFLAGS=" -mcpu=cortex-m3 -mthumb -fno-common -O0 "
arm-none-eabi-gcc  $CFLAGS -c ./Src/st.s -o ./Obj/st.o
arm-none-eabi-gcc  $CFLAGS -c ./Src/AB.c -o ./Obj/AB.o
arm-none-eabi-gcc  $CFLAGS -c ./Src/CD.c -o ./Obj/CD.o
arm-none-eabi-gcc  $CFLAGS -c ./Src/main.c -o ./Obj/main.o
arm-none-eabi-gcc  $CFLAGS -c ./Src/sub.c -o ./Obj/sub.o
arm-none-eabi-gcc  $CFLAGS -c ./Src/sum.c -o ./Obj/sum.o
 

Каталоги /Libraries и /Utilities скопированы без изменений из демо пакета. Из подкаталога /Project/Examples/RCC , файлы примеров скопированы в каталог Src.

Для удобства компиляции файлов демо проекта, я написал скрипт командного интерпретатора bash. Вот его текст:

	
#!/bin/bash

INCL="-ILibraries/CMSIS/CM3/CoreSupport/ \
-ILibraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x \
-ILibraries/STM32F10x_StdPeriph_Driver/inc \
-IUtilities \
-ISrc"

MACROS="-DSTM32F10X_MD_VL -DUSE_STDPERIPH_DRIVER"
OPT="-O0"
DEBUG="-g"
CFLAGS=" -mcpu=cortex-m3 -mthumb -fno-common  $OPT $DEBUG"
 echo "compiling st.s"
    arm-none-eabi-gcc $CFLAGS $INCL -c ./Src/st.s -o ./Obj/st.o
 echo "compiling system_stm32f10x.c"
  arm-none-eabi-gcc $MACROS  $CFLAGS \
  $INCL -c ./Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c \
       -o ./Obj/system_stm32f10x.o
 echo "compilimg stm32f10x_it.c"
    arm-none-eabi-gcc $MACROS $CFLAGS \
    $INCL -c ./Src/stm32f10x_it.c -o ./Obj/stm32f10x_it.o
 echo "compiling main.c"
    arm-none-eabi-gcc $MACROS $CFLAGS $INCL -c ./Src/main.c -o ./Obj/main.o
 echo "compiling STM32vldiscovery.c"
    arm-none-eabi-gcc $MACROS $CFLAGS \
    $INCL -c ./Utilities/STM32vldiscovery.c -o ./Obj/STM32vldiscovery.o
 echo "compiling stm32f10x_gpio.c"
   arm-none-eabi-gcc $MACROS $CFLAGS \
   $INCL -c ./Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.c\
   -o ./Obj/stm32f10x_gpio.o
 echo "compiling stm32f10x_flash.c"
   arm-none-eabi-gcc $MACROS $CFLAGS \
   $INCL -c ./Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_flash.c \
   -o ./Obj/stm32f10x_flash.o
 echo "compilig stm32f10x_rcc.c"
    arm-none-eabi-gcc $MACROS $CFLAGS \
    $INCL -c ./Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_rcc.c \
    -o ./Obj/stm32f10x_rcc.o
 echo "compiling stm32f10x_exti.c"
   arm-none-eabi-gcc $MACROS $CFLAGS \
   $INCL -c ./Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_exti.c\
   -o ./Obj/stm32f10x_exti.o
 echo "compiling misc.c"
   arm-none-eabi-gcc $MACROS $CFLAGS \
   $INCL -c ./Libraries/STM32F10x_StdPeriph_Driver/src/misc.c -o ./Obj/misc.o
		 

Объектные файлы AB.o и CD.o помещаем в библиотеку:

	
arm-none-eabi-ar -r libZXY.a ./Obj/AB.o ./Obj/CD.o
		 

Компонуем командой:

	
arm-none-eabi-ld -nostdlib  -Map=main.map  -Tl.ld \
 -o main.elf  ./Obj/st.o ./Obj/sub.o ./Obj/sum.o ./Obj/main.o ./libZXY.a
		 

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

В модуле sub.o имеются ссылки на символы в модуле библиотеки, но если библиотека указана раньше файла sub.o , то повторного сканирования библиотеки не произойдёт. А если библиотека указана после sub.o , то символы будут успешно разрешены. Когда список объектных файлов в командной строке заканчивается, то линковщиком формируется полный список не разрешённых ссылок и после сканирования библиотеки, которая находится в конце списка, они успешно разрешаются.

Но это не всё. Если библиотек несколько, то между отдельными модулями библиотек могут быть зависимости. Порядок указания библиотек тоже важен. Стандартные библиотеки компилятора, рекомендуется располагать в конце списка, в таком порядке:

	
libc.a   libm.a   libgcc.a
		 

Особый случай, когда зависимости между библиотеками циклические. Для решения подобных проблем, имеется универсальное средство. Список библиотек и объектных файлов можно задать в командной строке в виде:

	
--start-group libK.a libL.a file.o --end-group
		 

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

	
 GROUP(libK.a , libL.a , file.o).
		 

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

Благодаря флагу -Map=file.map , генерируется файл карты сборки проекта. Это текстовый файл с ценной информацией о компоновке программы.

Рассмотрим структуру сгенерированного файла карты сборки.

Первым идёт раздел с описанием включенных в процесс сборки библиотек. Строка из моей карты сборки:

	
 libZXY.a(AB.o)           ./Obj/sum.o (Bb)
		 

В строке сообщается, что в конечный образ программы включались секции из модуля AB.o входящего в библиотеку libZXY.a , из-за ссылок в модуле sum.o. В скобках показывается один из символов, виновников включения секций модуля AB.o - это символ Bb. Часто компилятор включает свои библиотеки по умолчанию и в этом разделе файла карты сборки, можно определить из каких библиотек, какие модули, были включены и почему.

В разделе "Memory Configuration" описана использованная конфигурация памяти. Затем описание загруженных файлов. После описания загруженных файлов, состав секций и вклад каждого объектного модуля в выходную секцию, с описанием символов видимых извне. Ниже, фрагмент файла карты:

	
 .text           0x00000000       0xf4
 *(.text)
 .text          0x00000000       0x6c ./Obj/st.o
                0x0000001c                start
		 

Выходная секция .text , начинается с адреса 0x0 , размер 0xf4. С отступом ниже, входная секция из файла st.o , размером 0x6c. Ещё ниже символ start из этой входной секции и его значение. И так далее, все входные секции с указанием размеров и символов.

В некоторых случаях, компоновщик генерирует секции заглушки кода, на английском языке, linker stabs. Например, для некоторых архитектур ARM, для перехода из режима ARM в THUMB, необходимы такие заглушки. Иногда такие вставки требуются для обхода некоторых различий в системе команд различных реализаций ARM. Секции, отмеченные в карте сборки, как linker stabs, относятся к этой категории, в конечный образ они не входят, так как имеют нулевой размер.

При анализе файла карты сборки, можно сделать вывод, что все секции объектного файла используются как входные, даже если данные и код этих секций не используются в других объектных файлах. Секция .rodata модуля CD.o загружена, хотя ссылок на строковую константу Ff из других модулей нет. Легко обнаружить в двоичном образе программы строку "trash" - значение константы Ff. Функция ff_sub файла sub.c , не используется в других частях программы, но занесена в выходную секцию.

Флаг компоновщика --gc-sections , заставляет его не использовать входные секции, код и данные которых, нигде не используются в программе. Если при линковке задать этот флаг, то в конечном образе программы не будет строки "trash". А в файле карты компоновки, появится раздел "Discarded input sections". В этом разделе перечислены входные секции всех файлов использованных в процессе сборки и рядом размер отброшенной секции. Если размер секции ноль - секция не отброшена. Такое, несколько странное представление информации. Из карты сборки видно, что 8 байт входной секции .rodata , модуля CD.o не вошли в выходной файл. В то же время, если во входной секции имеются объекты, как используемые, так и не используемые в других частях программы, то в этом случае секция используется целиком, примером служит функция ff_sub модуля sub.o. Интересно, что если не задать точку входа, к примеру, в скрипте линкера, командой ENTRY( start ), где метка start , объявлена как глобальная в стартовом модуле, то линкер отбросит все секции. Конечный образ будет нулевого размера.

Для более тонкой чистки программы, на предмет не нужного кода и данных, применяют флаги компилятора, -function-sections и -fdata-sections. В этом случае компилятор помещает каждую функцию в отдельную секцию. А затем при компоновке, ненужные секции исключаются из сборки, конечно, если указан флаг линкера -gc-sections . Перекомпилируем программы с новыми флагами и посмотрим, какие образовались секции:

	
 arm-none-eabi-size -A ./sub.o
section           size   addr
.text                0      0
.data                0      0
.bss                 0      0
.text.f_sub         32      0
.text.ff_sub        40      0
.comment           113      0
.ARM.attributes     51      0
Total              236
		 

Компилятор поместил каждую функцию в отдельную секцию, а имя создал составное, добавив к названию секции, название функции через точку. Секции модуля с данными:

	
 arm-none-eabi-size  -A  ./AB.o
section           size   addr
.text                0      0
.data                0      0
.bss                 0      0
.data.Bb             4      0
.data.Ee             4      0
.data.Aa             4      0
.comment           113      0
.ARM.attributes     51      0
Total              176
		 

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

Для успешной сборки, необходимо доработать скрипт линкера. В разделе SECTIONS , после описания входных секций, добавить описание секций с шаблоном "*", вот так это выглядит для секции .text:

	
    .text :
    * (.text);
    * (.text*);
		 

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

<-- Предыдущая страница Оглавление Следующая страница -->





 
Впервые? | Реклама на сайте | О проекте | Карта портала
тел. редакции: +7 (995) 900 6254. e-mail:info@eust.ru
©1998-2023 Рынок Микроэлектроники