Огромный двоичный размер, в то время как ld Linking
У меня есть скрипт компоновщика, который связывает код для imx6q(cortex-A9):
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(Reset_Handler)
/* SEARCH_DIR(.) */
GROUP(libgcc.a libc.a)
/* INPUT (crtbegin.o crti.o crtend.o crtn.o) */
MEMORY {
/* IROM (rwx) : ORIGIN = 0x00000000, LENGTH = 96K */
IRAM (rwx) : ORIGIN = 0x00900000, LENGTH = 256K
IRAM_MMU (rwx): ORIGIN = 0x00938000, LENGTH = 24K
IRAM_FREE(rwx): ORIGIN = 0x00907000, LENGTH = 196K
DDR (rwx) : ORIGIN = 0x10000000, LENGTH = 1024M
}
/* PROVIDE(__cs3_heap_start = _end); */
SECTIONS {
.vector (ORIGIN(IRAM) + LENGTH(IRAM) - 144 ):ALIGN (32) {
__ram_vectors_start = . ;
. += 72 ;
__ram_vectors_end = . ;
. = ALIGN (4);
} >IRAM
. = ORIGIN(DDR);
.text(.) :ALIGN(8) {
*(.entry)
*(.text)
/* __init_array_start = .; */
/* __init_array_end = .; */
. = ALIGN (4);
__text_end__ = .;
} >DDR
.data :ALIGN(8) {
*(.data .data.*)
__data_end__ = .;
}
.bss(__data_end__) : {
. = ALIGN (4);
__bss_start__ = .;
*(.shbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
__bss_end__ = .;
}
/* . += 10K; */
/* . += 5K; */
top_of_stacks = .;
. = ALIGN (4);
. += 8;
free_memory_start = .;
.mmu_page_table : {
__mmu_page_table_base__ = .;
. = ALIGN (16K);
. += 16K;
} >IRAM_MMU
_end = .;
__end = _end;
PROVIDE(end = .);
}
Когда я построил, размер двоичного файла составляет всего 6 КБ. Но я не могу добавить любую инициализированную переменную. Когда я добавляю инициализированную переменную, размер двоичного файла увеличивается до ~246 МБ. Это почему? Я попытался связать сегмент данных в расположении после текстового раздела, указав точное местоположение и указав>DDR для сегмента данных. Даже если кажется, что это уменьшает размер двоичного файла до 6 КБ, двоичный файл не загружается. Как я могу сохранить свой код в DDR и данные, bss, стек и кучу во внутреннем оперативном памяти, с легким двоичным размером?
Я читал в другой ветке, что "использование тега MEMORY в скрипте компоновщика должно решить проблему потери памяти". Как это можно сделать?
скрипт компоновщика тратит впустую мою память
Пожалуйста, спросите, нужно ли что-нибудь еще. У меня нет опыта работы со скриптом компоновщика. Пожалуйста помоги
Вывод readelf --sections двоичного файла без инициализированных данных выглядит следующим образом:
There are 19 section headers, starting at offset 0xd804:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .vector NOBITS 0093ff80 007f80 000048 00 WA 0 0 32
[ 2] .text PROGBITS 10000000 008000 0016fc 00 AX 0 0 8
[ 3] .text.vectors PROGBITS 100016fc 0096fc 000048 00 AX 0 0 4
[ 4] .text.proc PROGBITS 10001744 009744 000034 00 AX 0 0 4
[ 5] .bss NOBITS 0093ffc8 007fc8 000294 00 WA 0 0 4
[ 6] .mmu_page_table NOBITS 00938000 008000 004000 00 WA 0 0 1
[ 7] .comment PROGBITS 00000000 009778 00001f 01 MS 0 0 1
[ 8] .ARM.attributes ARM_ATTRIBUTES 00000000 009797 00003d 00 0 0 1
[ 9] .debug_aranges PROGBITS 00000000 0097d8 000108 00 0 0 8
[10] .debug_info PROGBITS 00000000 0098e0 0018a7 00 0 0 1
[11] .debug_abbrev PROGBITS 00000000 00b187 00056f 00 0 0 1
[12] .debug_line PROGBITS 00000000 00b6f6 00080e 00 0 0 1
[13] .debug_frame PROGBITS 00000000 00bf04 000430 00 0 0 4
[14] .debug_str PROGBITS 00000000 00c334 0013dd 01 MS 0 0 1
[15] .debug_ranges PROGBITS 00000000 00d718 000020 00 0 0 8
[16] .shstrtab STRTAB 00000000 00d738 0000cb 00 0 0 1
[17] .symtab SYMTAB 00000000 00dafc 000740 10 18 60 4
[18] .strtab STRTAB 00000000 00e23c 000511 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
и вывод двоичного файла readelf --sections с заданными инициализированными данными,
There are 20 section headers, starting at offset 0xd82c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .vector NOBITS 0093ff80 007f80 000048 00 WA 0 0 32
[ 2] .text PROGBITS 10000000 008000 0016fc 00 AX 0 0 8
[ 3] .text.vectors PROGBITS 100016fc 0096fc 000048 00 AX 0 0 4
[ 4] .text.proc PROGBITS 10001744 009744 000034 00 AX 0 0 4
[ 5] .data PROGBITS 0093ffc8 007fc8 000004 00 WA 0 0 8
[ 6] .bss NOBITS 0093ffcc 007fcc 000294 00 WA 0 0 4
[ 7] .mmu_page_table NOBITS 00938000 008000 004000 00 WA 0 0 1
[ 8] .comment PROGBITS 00000000 009778 00001f 01 MS 0 0 1
[ 9] .ARM.attributes ARM_ATTRIBUTES 00000000 009797 00003d 00 0 0 1
[10] .debug_aranges PROGBITS 00000000 0097d8 000108 00 0 0 8
[11] .debug_info PROGBITS 00000000 0098e0 0018b6 00 0 0 1
[12] .debug_abbrev PROGBITS 00000000 00b196 000580 00 0 0 1
[13] .debug_line PROGBITS 00000000 00b716 00080e 00 0 0 1
[14] .debug_frame PROGBITS 00000000 00bf24 000430 00 0 0 4
[15] .debug_str PROGBITS 00000000 00c354 0013dd 01 MS 0 0 1
[16] .debug_ranges PROGBITS 00000000 00d738 000020 00 0 0 8
[17] .shstrtab STRTAB 00000000 00d758 0000d1 00 0 0 1
[18] .symtab SYMTAB 00000000 00db4c 000770 10 19 62 4
[19] .strtab STRTAB 00000000 00e2bc 000513 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
Надеюсь, этого достаточно...!!!
Примечание: я использую arm-none-eabi-gcc для компоновки.
1 ответ
Если у вас нет опыта работы со сценариями компоновщика, то либо используйте тот, который просто работает, либо создайте или заимствуйте более простой. Вот простой, и это должно продемонстрировать, что, скорее всего, происходит.
MEMORY
{
bob : ORIGIN = 0x00001000, LENGTH = 0x100
ted : ORIGIN = 0x00002000, LENGTH = 0x100
alice : ORIGIN = 0x00003000, LENGTH = 0x100
}
SECTIONS
{
.text : { *(.text*) } > bob
.data : { *(.text*) } > ted
.bss : { *(.text*) } > alice
}
Первая программа
.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
это не настоящая программа, просто создание нескольких байтов в сегменте - это все.
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00001000 001000 00000c 00 AX 0 0 4
12 байт в.text, который находится по адресу 0x1000 в памяти, и это именно то, что мы сказали.
Если я использую -objcopy a.elf -O binary a.bin, я получаю 12-байтовый файл, как и ожидалось, "двоичный" формат файла - это образ памяти, начиная с первого адреса, который имеет некоторое содержимое в адресном пространстве, и заканчивая последний байт содержимого в адресном пространстве. поэтому вместо 0x1000+12 байт двоичный файл равен 12 байтам, и пользователь должен знать, что он должен быть загружен с 0x1000.
Так что измени это немного:
.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
.data
some_data: .word 0x12345678
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00001000 001000 00000c 00 AX 0 0 4
[ 2] .data PROGBITS 00002000 002000 000004 00 WA 0 0 1
Теперь у нас есть 12 байтов в 0x1000 и 4 байта в 0x2000, поэтому двоичный -O должен дать нам одно изображение памяти от первого определенного байта до последнего, чтобы было 0x1000+4.
Конечно, достаточно 4100 байт, это именно то, что он сделал.
.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
.data
some_data: .word 0x12345678
.bss
some_more_data: .word 0
который дает
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00001000 001000 00000c 00 AX 0 0 4
[ 2] .data PROGBITS 00002000 002000 000004 00 WA 0 0 1
[ 3] .bss NOBITS 00003000 003000 000004 00 WA 0 0 1
Теперь я получил только 4100-байтовый файл, и это на самом деле не удивительно, предполагается, что начальная загрузка собирается обнулить.bss, так что "двоичный" файл не вырос.
Есть близкие отношения. Дизайн системного уровня. Между скриптом компоновщика и загрузчиком. Похоже, что вы пытаетесь сделать (просто ram no rom), вы, вероятно, можете воспользоваться намного более простым скриптом компоновщика, наравне с тем, что у меня есть, но если вас волнует, что.bss обнуляется, то есть некоторые приемы, которые вы можете сделать использовать:
MEMORY
{
ram : ORIGIN = 0x00001000, LENGTH = 0x3000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.text*) } > ram
.data : { *(.text*) } > ram
}
Убедитесь, что есть хотя бы один элемент.data, и ваш "двоичный файл" будет иметь полный образ с bss, уже обнуленным, загрузчик просто должен установить указатель (и) стека и перейти к основному (если это для C).
В любом случае, надеюсь, вы увидите, что скачок с 12 байтов до 4100 байтов произошел из-за добавления элемента.data и "двоичного" формата, необходимого для заполнения "двоичного" файла так, чтобы файл представлял собой образ памяти с самого низкого уровня. адрес с данными на самый высокий адрес с данными (от 0x1000 до 0x2000+sizeof(.data)-1 в этом случае). Измените скрипт компоновщика, 0x1000 и 0x2000, и все это изменится. Поменяйте их местами.text в 0x2000 и.data в 0x1000, теперь "двоичный" файл должен иметь размер 0x2000-0x1000+sizeof(.text), а не 0x2000-0x1000+sizeof(.data). или 0x100C байтов вместо 0x1004. вернитесь к первому сценарию компоновщика и сделайте.data равным 0x20000000, теперь "двоичным" будет 0x20000000-0x1000+sizeof(.data), потому что именно столько информации, включая заполнение, требуется для создания образа памяти в одном файле.
Скорее всего, так и происходит. Как показано здесь, размер файла изменился с 12 байтов до 4100, просто добавив одно слово данных.
РЕДАКТИРОВАТЬ.
Ну, если вы загрузите данные, то ваша инициализированная переменная не будет инициализирована, это так просто
без знака int x = 5;
не будет 5, если вы сбрасываете (NOLOAD) .data.
Как уже было сказано и заявлено, вы можете поместить данные в сектор.text, а затем использовать дополнительный скрипт компоновщика foo, чтобы программа начальной загрузки обнаружила эти данные.
ПАМЯТЬ { bob: ORIGIN = 0x00001000, ДЛИНА = 0x100 ted: ORIGIN = 0x00002000, LENGTH = 0x100 alice: ORIGIN = 0x00003000, LENGTH = 0x100 } РАЗДЕЛЫ {.text: { (.text)}> боб. Данные: { (.te)}> ted AT> bob.bss: { (.text)}> alice AT> bob}
Это создает 16-байтовый "двоичный" файл. 12 байтов инструкции и 4 байта данных. Но вы не знаете, где находятся данные, если не сделаете какое-то жесткое кодирование, что является плохой идеей. Вот где такие вещи, как bss_start и bss_end находятся в вашем скрипте компоновщика.
что-то вроде этого
MEMORY
{
bob : ORIGIN = 0x00001000, LENGTH = 0x100
ted : ORIGIN = 0x00002000, LENGTH = 0x100
alice : ORIGIN = 0x00003000, LENGTH = 0x100
}
SECTIONS
{
.text : { *(.text*) } > bob
.data : {
__data_start__ = .;
*(.data*)
} > ted AT > bob
__data_end__ = .;
__data_size__ = __data_end__ - __data_start__;
.bss : { *(.text*) } > alice AT > bob
}
.text
.globl _start
_start:
mov r0,r1
mov r1,r2
b .
hello:
.word __data_start__
.word __data_end__
.word __data_size__
.data
some_data: .word 0x12345678
что дает нам.
Disassembly of section .text:
00001000 <_start>:
1000: e1a00001 mov r0, r1
1004: e1a01002 mov r1, r2
1008: eafffffe b 1008 <_start+0x8>
0000100c <hello>:
100c: 00002000 andeq r2, r0, r0
1010: 00002004 andeq r2, r0, r4
1014: 00000004 andeq r0, r0, r4
Disassembly of section .data:
00002000 <__data_start__>:
2000: 12345678 eorsne r5, r4, #120, 12 ; 0x7800000
и набор инструментов / компоновщик создает и заполняет эти определенные имена в скрипте компоновщика, а затем заполняет их в вашем коде, когда разрешает эти внешние объекты. Тогда вашей начальной загрузке нужно использовать эти переменные (и больше того, что я не указал здесь, где найти.data в.text, вы знаете из вышеприведенного, что есть 4 байта, и они должны быть в 0x2000, но где в 0x1000 .text площадь этих 4 байтов? Больше скриптов компоновщика foo. Также обратите внимание, что скрипты компоновщика gnu очень чувствительны относительно того, где вы определяете эти переменные. До или после волнистых скобок могут быть разные результаты.
Вот почему я упоминал, что вы использовали баран. Если это цель, основанная на ROM, и вы хотите.data и обнулить.bss, тогда вам в значительной степени нужно поместить.data, размер и расположение.bss в область flash / rom, а загрузчик должен скопировать и обнулить. В качестве альтернативы вы можете не использовать.data или.bss
unsigned int x=5;
unsigned int y;
вместо
unsigned int x;
unsigned int y;
...
x=5;
y=0;
Да, это не так эффективно с точки зрения размера двоичного файла, но сценарии компоновщика очень сильно зависят от цепочки инструментов, и, например, со временем, например, с изменением языка / правил сценария компоновщика, то, что работало в предыдущей основной версии GNU ld, не обязательно работает с текущей или Затем мне пришлось перестроить мой минимальный скрипт компоновщика за эти годы.
Как показано здесь, вы можете использовать инструменты командной строки, чтобы поэкспериментировать с настройками и местоположениями и посмотреть, что произвел набор инструментов.
В итоге это звучит так, как будто вы добавили некоторую информацию в.data, но затем заявите, что хотите ее ЗАГРУЗИТЬ, в основном это означает, что.data там не используется / ваши переменные не инициализированы правильно, поэтому зачем менять код, чтобы все это происходило только чтобы это не сработало? Либо имейте.data и используйте его правильно, имейте правильную пару сценариев начальной загрузки и сценария компоновщика, или, если это ram, просто упакуйте все это в то же самое пространство памяти RAM, или не используйте используемый вами двоичный формат, используйте elf или ihex или srec или другое.
Еще одна хитрость, зависящая от вашей системы, заключается в том, чтобы собрать двоичный файл для оперативной памяти, все упакованный, а затем иметь другую программу, которая оборачивает этот двоичный файл, запускается из рома и копирует в оперативную память и скачки. Возьмите вышеприведенную 16-байтовую программу, напишите другую, которая включает эти 16 байтов из этой сборки, и скопируйте их в 0x1000, а затем разветвите в 0x1000. В зависимости от системы, технологии и интерфейса flash / rom, которые вы, возможно, пожелаете сделать в любом случае, система из моей повседневной работы использует spi-флэш-память для загрузки, которая, как известно, имеет проблемы с чтением и... spi.. Таким образом, самое быстрое, самое чистое, самое надежное решение - это выполнить копирование, прежде чем делать что-либо еще. Сделать скрипт компоновщика намного проще как бесплатный побочный эффект.