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

 


По базе:  

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

реклама

 




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




Связывание двух модулей

Большинство программ создаётся из нескольких модулей. В нашем следующем примере, простейшая программа создаётся из 2-х модулей, которые затем связываются в один бинарный файл. Текст программы 1-го модуля из файла ex2_m.s:

	
@ main program example 2
    .syntax unified
    .cpu cortex-m3
    .thumb
 .equ  STACKINIT, 0x20002000
       .text
 .global start
    .word STACKINIT         @ stack pointer
    .word start + 1         @ reset
    .word _nmi_handler + 1  @ 
    .word _hard_fault  + 1  @ 
    .word _memory_fault + 1 @ 
    .word _bus_fault + 1    @ 
    .word _usage_fault + 1  @ 
 start:
    ldr r0 , =bar          @ load begin of array 
    ldr r1 , =ear          @ load end of array
    bl  sum                @ call proc
    ldr r1 , =var1         @ set address var1
    strb r3 , [r1]         @ save in memory
    mov r3, #1
    ldr r1 , =var2
    strb r3 , [r1]
 stop:         	        @ trap
	 b stop
        .section .rodata 
 bar:  .byte 1, 2, 3, 4, 5  @ array of numbers
 ear: 
        .text
     _dummy:      
     _nmi_handler:
     _hard_fault:
     _memory_fault:
     _bus_fault:
     _usage_fault:
        add r0, 1
        add r1, 1
        b _dummy
  .end
	 

Массив констант выделен в отдельную секцию, применена директива .section .rodata . Начало массива отмечено меткой bar , а конец отмечен меткой ear . Секция .rodata будет размещена в ROM. Адреса начала и конца массива передаются в процедуру sum через регистры. В этой процедуре выполняется суммирование элементов массива. Она определена в другом модуле. Результат возвращается в регистре r3 и сохраняется в переменных var1 и var2 . Эти переменные определены в секции .bss , а сама секция размещена в RAM.

После ассемблирования ex2_m.s , смотрим секции и символы объектного файла:

Символы Секции
         U sum
         U var1
         U var2
00000000 r bar
00000005 r ear
0000001c T start
00000030 t stop
00000034 t _bus_fault
00000034 t _dummy
00000034 t _hard_fault
00000034 t _memory_fault
00000034 t _nmi_handler
00000034 t _usage_fault
20002000 a STACKINIT
section            size   addr
.text              0x50    0x0
.data               0x0    0x0
.bss                0x0    0x0
.rodata             0x5    0x0
.ARM.attributes    0x21    0x0
.debug_line        0x4a    0x0
.debug_info        0x44    0x0
.debug_abbrev      0x14    0x0
.debug_aranges     0x20    0x0
Total             0x138

Секция .rodata имеет размер 5 байт. Символы типа U - это неопределённые символы, адреса не имеют. Переменные var1 и var2 , а так же процедура sum , определены в другом модуле, поэтому имеют тип U. Символы типа r - символы данных определённых в ROM, для чтения, но не для исполнения.

Текст второй программы из файла ex2_s.s:

	
@ subroutine example 2
    .syntax unified
    .cpu cortex-m3
    .thumb
 .global sum, var1, var2
     .text
  sum:
       mov r3, #0
 loop:       ldrb r2, [r0], #1
             add  r3, r2, r3
             cmp  r0, r1
             bne  loop
             mov  pc, lr
    .bss
      var1:  .skip 1
      var2:  .skip 1
    buffer:  .skip 32 0 
 .end
	

Метка sum задана как глобальная, это условие видимости метки из других модулей. Это же относится и к var1 , var2 . Для задания переменных в секции .bss используется директива .skip . Эта директива позволяет определять буфер произвольного размера. Переменные var1 и var2 по байту и buffer в 32 байта. Таблица символов и секций:

Символы Секции
00000000 T sum
00000000 B var1
00000001 B var2
00000002 b buffer
00000004 t loop
section            size   addr
.text              0x12    0x0
.data               0x0    0x0
.bss               0x22    0x0
.ARM.attributes    0x21    0x0
.debug_line        0x3a    0x0
.debug_info        0x44    0x0
.debug_abbrev      0x14    0x0
.debug_aranges     0x20    0x0
Total             0x107

Символ типа B - глобальный символ в области неинициализированных данных, именно там расположены var1 и var2. Символ типа b - локальный символ. Получаем исполнимый файл командой:

	
arm-none-eabi-ld  -Tl.ld ex2_m.o ex2_s.o -o ex2.elf
		 

Текст скрипта линковки:

	
MEMORY {
       ROM (RX): ORIGIN = 0, LENGTH = 128K
       RAM (RW): ORIGIN = 0x20000000 LENGTH = 8K
       }
SECTIONS {
          .text : {
            * (.text);   } > ROM
          .rodata : {
            * (.rodata); } > ROM
          .bss    :  {
            * (.bss);
                     } > RAM }
		 

Здесь приведен пример с использованием команды MEMORY, как и команда SECTIONS, это команда может встречаться единожды в скрипте. В фигурных скобках указаны названия областей памяти и атрибуты этих областей. Как легко догадаться атрибут RW - это область памяти с доступом чтения-записи, а атрибут X , означает возможность выполнения команд. Слово ORIGIN - начало области, LENGTH - её размер. Знаком ">" указывается, в какой области памяти расположить секции.

После секции кода следует секция данных "только чтение", а затем секция не инициализированных данных, располагающаяся в RAM.

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

Правильно скомпонованный Неправильно скомпонованный
0000001c T start
00000030 t stop
00000034 t _bus_fault
00000034 t _dummy
00000034 t _hard_fault
00000034 t _memory_fault
00000034 t _nmi_handler
00000034 t _usage_fault
00000050 T sum
00000054 t loop
00000062 r bar
00000067 r ear
20000000 B var1
20000001 B var2
20000002 b buffer
20002000 a STACKINIT
00000000 T sum
00000004 t loop
00000030 T start
00000044 t stop
00000048 t _bus_fault
00000048 t _dummy
00000048 t _hard_fault
00000048 t _memory_fault
00000048 t _nmi_handler
00000048 t _usage_fault
00000064 r bar
00000069 r ear
20000000 B var1
20000001 B var2
20000002 b buffer
20002000 a STACKINIT

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

	
MEMORY {
       ROM (RX): ORIGIN = 0, LENGTH = 128K
       RAM (RW): ORIGIN = 0x20000000 LENGTH = 8K
       }
SECTIONS {
          .text : { ex2_m.o (.text);
                    ex2_s.o (.text);  } > ROM
          .rodata :  {  * (.rodata); } > ROM
          .bss    :  {  * (.bss);  } > RAM }

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

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

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

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





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