programing

메모리의 지정된 절대 주소에 변수를 배치하는 방법(GCC 사용)

stoneblock 2023. 9. 21. 20:04

메모리의 지정된 절대 주소에 변수를 배치하는 방법(GCC 사용)

RealView ARMC 컴파일러는 변수 속성을 사용하여 주어진 메모리 주소에 변수를 배치하는 것을 지원합니다.at(address):

int var __attribute__((at(0x40001000)));
var = 4;   // changes the memory located at 0x40001000

GCC도 비슷한 변수 속성을 가지고 있습니까?

잘 모르겠지만 다음과 같은 해결 방법을 쉽게 만들 수 있습니다.

int *var = (int*)0x40001000;
*var = 4;

완전히 같은 것은 아니지만 대부분의 상황에서 완벽한 대체물이 됩니다.이것은 GCC 뿐만 아니라 어떤 컴파일러와도 작동할 것입니다.

GCC를 사용하는 경우 GNU ld도 사용한다고 가정합니다(물론 확실한 것은 아니지만), ld는 변수를 원하는 곳에 배치할 수 있도록 지원합니다.

링커가 그 일을 하도록 하는 것은 꽤 흔한 일이라고 생각합니다.

을 얻어,가 어떤 를 위한 @rib 이라면, , 의를 volatile포인터 정의로 이동합니다.그냥 RAM이면 상관없습니다.

섹션 특성과 이전 링커 스크립트를 사용하여 해당 섹션에 대한 원하는 주소를 정의할 수 있습니다.이것은 아마도 당신이 선택할 수 있는 것보다 더 엉망일 것입니다. 하지만 그것은 선택사항입니다.

실행 가능한 최소한의 링커 스크립트 예제

이 방법은 https://stackoverflow.com/a/4081574/895245 에서 언급했지만 이제 구체적인 예를 들어보겠습니다.

본전의

#include <stdio.h>

int myvar __attribute__((section(".mySection"))) = 0x9ABCDEF0;

int main(void) {
    printf("adr %p\n", (void*)&myvar);
    printf("val 0x%x\n", myvar);
    myvar = 0;
    printf("val 0x%x\n", myvar);
    return 0;
}

link.ld

SECTIONS
{
  .mySegment 0x12345678 : {KEEP(*(.mySection))}
}

깃허브 업스트림.

컴파일 및 실행:

gcc -fno-pie -no-pie -o main.out -std=c99 -Wall -Wextra -pedantic link.ld main.c
./main.out

출력:

adr 0x12345678
val 0x9abcdef0
val 0x0

그래서 우리는 원하는 주소에 그것이 놓여진 것을 봅니다.

GCC 매뉴얼에서 이 내용이 어디에 나와 있는지 찾을 수 없지만 다음 구문을 사용합니다.

gcc link.ld main.c

지정된 링커 스크립트를 사용되는 기본 스크립트에 추가하는 것 같습니다.

-fno-pie -no-pie이는 이제 우분투 툴체인이 기본적으로 PIE 실행 파일을 생성하도록 구성되어 있으며, 이로 인해 Linux 커널이 실행 파일을 매번 다른 주소에 배치하게 되므로 우리의 실험이 엉망이 되기 때문입니다.참고 항목:gcc 및 ld에서 위치 독립 실행 파일에 대한 -fPIE 옵션은 무엇입니까?

TODO: 컴파일은 경고를 생성합니다.

/usr/bin/x86_64-linux-gnu-ld: warning: link.ld contains output sections; did you forget -T?

내가 뭘 잘못하고 있나요?그것을 없애는 방법은?참고 항목:경고를 제거하는 방법: link.res에 출력 섹션이 포함되어 있습니다. -T를 잊어버리셨나요?

Ubuntu 18.10, GCC 8.2.0에서 테스트되었습니다.

당신은 당신의 질문에 답했습니다. 위의 당신의 링크에는 다음과 같이 적혀있습니다.

GNU GCC 컴파일러를 사용하면 절대 메모리 위치에 액세스하는 데 포인터 정의만 사용할 수 있습니다.예를 들어,

#define IOPIN0         (*((volatile unsigned long *) 0xE0028000))
IOPIN0 = 0x4;

그건 그렇고 http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Variable-Attributes.html#Variable%20Attributes

링커 파일을 편집할 필요 없이 메모리의 고정된 주소에 실제로 공간을 예약하는 솔루션은 다음과 같습니다.

    extern const uint8_t dev_serial[12];
    asm(".equ dev_serial, 0x1FFFF7E8");
/* or    asm("dev_serial = 0x1FFFF7E8"); */
    ...
        
    for (i = 0 ; i < sizeof(dev_serial); i++)
        printf((char *)"%02x ", dev_serial[i]);

GCC에서 변수를 특정 섹션에 배치할 수 있습니다.

__attribute__((section (".foo"))) static uint8_t * _rxBuffer;

아니면

static uint8_t * _rxBuffer __attribute__((section (".foo")));

그런 다음 GNU 링커 메모리 설정에서 섹션의 주소를 지정합니다.

.foo=0x800000 

저도 비슷한 문제가 있었습니다.나는 내가 정의한 구간의 변수를 특별한 오프셋으로 할당하고 싶었습니다.동시에 코드를 휴대용으로 사용하기를 원했습니다(C 코드에 명시적인 메모리 주소가 없음).그래서 링커 스크립트에서 RAM 섹션을 정의하고, 섹션의 길이가 같은 배열을 정의했습니다..noinit섹션은 0x0F 길이입니다.

uint8_t no_init_sec[0x0f] __attribute__ ((section (".noinit")));

이 배열은 이 섹션의 모든 위치를 매핑합니다.이 솔루션은 할당된 배열에서 사용되지 않는 위치가 데이터 메모리의 낭비되는 공간이 되므로 섹션이 큰 경우에는 적합하지 않습니다.

제 의견에 대한 정답은 미니멀 런너블 링커 스크립트 예제입니다.

그러나 여기에 언급되지 않은 것이 있습니다. 변수가 코드에 사용되지 않는 경우(예: version...과 같은 읽기 전용 데이터를 보유하는 경우) 'used' 속성을 추가해야 합니다.

https://stackoverflow.com/a/75468786/3887115 에서 제 답변을 참고하세요.

언급URL : https://stackoverflow.com/questions/4067811/how-to-place-a-variable-at-a-given-absolute-address-in-memory-with-gcc