2016-11-09 21 views
0

를 연결 LD 내가 imx6q 위해 (코어 텍스 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 MB로 점프됩니다. 왜 그런가요? 정확한 위치를 지정하고 데이터 세그먼트에> DDR을 제공함으로써 텍스트 섹션 다음의 위치에 데이터 세그먼트를 연결하려고했습니다. 바이너리 크기를 6KB로 줄이는 것처럼 보일지라도 이진 파일은 부팅에 실패합니다. 가벼운 바이너리 크기로 내부 램 자체에 DDR, 데이터, bss, 스택 및 힙에 내 코드를 보관하는 방법은 무엇입니까?

다른 스레드에서 "링커 스크립트에서 MEMORY 태그를 사용하면 메모리 낭비 문제를 해결해야합니다"라는 메시지가 읽 혔습니다. 어떻게 수행 할 수 있습니까?

linker script wastes my memory

Plese 아무것도가 다른 필요한 경우 요청 않습니다. 필자는 링커 스크립트에 대한 경험이 없습니다.

다음과 같다 주어진없이 초기화 된 데이터와 바이너리의 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을 사용하고 있습니다.

+0

바이너리에'readelf --sections '의 출력을 게시 할 수 있습니까? –

+0

출력이 추가되었습니다. 두 번째 경우 바이너리의 크기는 ~ 246MB가되어 이전의 6KB보다 작아집니다. – Ajeesh

+1

그것은 분명합니다 (비슷한 문제 (또는 Q/A)가 SO에 있음). 바이너리가'DDR (256MB) - IRAM (9MB) ~ = 256MB' 인'IRAM LENGTH = 256K'는 246MB로 증가합니다. [VMA/LMA에 대해 읽어보십시오.] (https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html) 검색 용어로 사용하십시오.** 이진 **은 연속 바이너리이어야합니다. 섹션 정보가 없습니다. 문제가 보이십니까? gnu LD 스크립트는 이것을 처리하기 위해'AT' 타입 지시자와 LMA/VMA를 가지고 있습니다. –

답변

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 바이너리 a.bin을 사용하면 예상 한대로 12 바이트 파일을 얻습니다. "바이너리"파일 형식은 주소에 내용이있는 첫 번째 주소부터 시작하는 메모리 이미지입니다 공백으로 끝나고 주소 공간의 내용의 마지막 바이트로 끝납니다. 따라서 0x1000 + 12 바이트 대신 사용자가 0x1000에로드해야한다는 것을 알고 있어야하는 이진수는 12 바이트입니다.

그래서 조금 이것을 변경 :

.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 

지금 우리가 0x1000 인 12 바이트 0x2000 인에서 4 바이트를, 그래서 -O 진은 마지막에 첫 번째로 정의 된 바이트에서 우리에게 하나 개의 메모리 이미지를주고있다 0x1000 + 4가됩니다.

확실히 정확히 4100 바이트입니다.나는 단지 4100 바이트 파일을 가지고 지금

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 

를 제공하고, 그 사실은 놀라운 일이 아니다

.text 
.globl _start 
_start: 
mov r0,r1 
mov r1,r2 
b . 
.data 
some_data: .word 0x12345678 
.bss 
some_more_data: .word 0 

, 그 didnt한다 "는 성장 그래서 부트 스트랩 제로 .bss라고에가는 것을 가정한다 바이너리 "파일.

친밀한 관계가 있습니다. 시스템 레벨 디자인. 링커 스크립트와 부트 스트랩 사이. 당신이하려고하는 것처럼 보이면 (단지 램이 없다), 링커 스크립트를 사용하면 훨씬 간단한 링커 스크립트를 사용할 수 있습니다.하지만 .bss가 0이되면 걱정할 필요가 있습니다. 사용 :

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 (.data)가 아닌 0x2000-0x1000 + sizeof (.text)가되어야합니다. 또는 0x1004 대신 0x100C 바이트. 첫 번째 링커 스크립트로 돌아가서 .data를 0x20000000으로 만드십시오. "바이너리"는 0x20000000-0x1000 + sizeof (.data)가 될 것입니다. 이는 패딩을 포함한 많은 정보가 단일 파일에서 메모리 이미지를 만드는 데 필요하기 때문입니다.

그럴 가능성이 가장 높습니다. 여기에 설명 된 것처럼 파일 크기는 단순히 한 단어의 데이터를 추가하여 12 바이트에서 4100으로 증가했습니다.

EDIT. 당신이 다음 초기화 변수가 늘 초기화 데이터를 무부하 잘 경우

는, 그것이 그렇게 간단

부호 INT X = 5;

은 (NOLOAD) .data를 버리면 5가되지 않습니다.

다시 말하지만 .text 섹터에 데이터를 넣은 다음 더 많은 링커 스크립트 foo를 사용하여 부트 스트랩에서 해당 데이터를 찾을 수 있습니다.

{MEMORY 밥 : ORIGIN = 0x00001000, LENGTH = 0x100에서 테드 : ORIGIN = 0x00002000, LENGTH = 0x100에서 앨리스 : ORIGIN = 0x00003000, LENGTH = 0x100에서 SECTIONS} { 는 .text {(는 .text)}> 밥 .DATA {(는 .text)}> 테드 AT> 밥 .bss라고 {(는 .text)}> 앨리스 AT> 밥 }

16 바이트 "2 진"파일을 작성합니다. 명령의 12 바이트와 .data의 4 바이트. 그러나 나쁜 생각 인 하드 코딩을하지 않으면 데이터가 어디에 있는지 알 수 없습니다.여기서는 bss_startbss_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를 찾으려면 .data에서 .data를 찾을 수 있습니다. 위에서 4 바이트가 있고 0x2000에 착륙해야합니다. 영역은 그 4 바이트가 발견 되었습니까? 더 많은 링커 스크립트 foo. gnu 링커 스크립트는 여러분이 그 변수를 정의하는 곳에서 매우 민감합니다. 꺽쇠 괄호 앞뒤에 다른 결과가있을 수 있습니다.

이것이 내가 언급 한 이유입니다. 당신이 램을 사용하고있는 것처럼 보였습니다. 이것은 ROM 기반의 타겟이고 .data와 .bss를 원한다면 .bss의 .data와 크기와 위치를 flash/rom 영역에두고 부트 스트랩에 .data 또는 .bss를 사용하지 않도록 선택할 수도 있습니다.

(210)

대신

unsigned int x; 
unsigned int y; 
... 
x=5; 
y=0; 

예는 효율적 이진 크기 현명하지만, 링커 스크립트는 매우 툴체인 의존하고 시간이 지남에 따라, 예를 들어 GNU와 링커 스크립트 langauge는/규칙 변경, 이전에 근무하고 있습니다되지 않은 것 gnu ld의 주요 버전은 필연적으로 현재 또는 다음 버전에서 작동합니다. 결과적으로 수년 동안 최소한의 링커 스크립트를 다시 설계해야했습니다.

여기에 설명 된 것처럼 명령 줄 도구를 사용하여 설정 및 위치를 실험하고 도구 체인이 생성 한 것을 확인할 수 있습니다.

요점은 .data에 정보를 추가 한 것처럼 들리지만 기본적으로 NOLOAD를 사용하려는 상태입니다. 기본적으로 .data isnt/변수가 올바르게 초기화되지 않았으므로 코드가 변경되어 이 모든 일은 어쨌든 작동하지 않을뿐입니다. 어느 쪽이든 .data를 가지고 있고 그것을 올바르게 사용하고, 올바른 부트 스트랩과 링커 스크립트 쌍을 가지고 있거나, 램이면 그냥 같은 램 공간에 모두 넣거나, 사용하고있는 "바이너리"형식을 사용하지 말고, elf를 사용하거나 ihex 또는 srec 또는 기타.

시스템에 따라 또 다른 트릭은 RAM 용 바이너리를 빌드 한 다음 모든 패키지를 쌓은 다음이 바이너리를 감싸는 다른 프로그램이 롬과 복사본에서 램과 점프로 실행되도록하는 것입니다. 위의 16 바이트 프로그램을 가져 와서 해당 빌드에서 16 바이트를 포함하는 다른 프로그램을 작성하고 0x1000에 복사 한 다음 0x1000으로 분기하십시오. 어쨌든 시스템과 플래시/롬 기술 및 인터페이스에 따라 어쨌든이 작업을 수행 할 수도 있습니다. 하루 종일 작업 한 시스템은 읽기 장애 문제가있는 것으로 알려진 부팅 플래시를 사용합니다. 가장 빠르고, 가장 깨끗하고, 가장 신뢰할 수있는 솔루션은 다른 작업을하기 전에 복사 점프를하는 것입니다. 자유로운 부작용으로 링커 스크립트를 훨씬 쉽게 만들 수 있습니다.

+0

문제가있는 것 같습니다. 데이터 섹션에 (NOLOAD)를 추가했을 때 사라졌습니다. 어쨌든 확실한가요? – Ajeesh

+1

'NOLOAD '는 데이터 섹션이 바이너리의 일부가 아니라는 것을 의미합니다. 해야 할 일 (BSS가 없거나 초기화 된 메모리가없는 경우)은 기본 이미지에 초기 데이터 값을 넣고 부팅 코드를 사용하여 최종 위치에 복사합니다. (모든 어셈블러가 참조해야하는 실제 최종 절대 주소 인 LMA로드 메모리 주소 및 VMA 가상 메모리 주소) –