Держите родаты рядом с функцией, которая их создала

Я пытаюсь сделать так, чтобы местоположение раздела .rodata оставалось с соответствующим расположением памяти функций. Я использую компилятор/компоновщик GNU, чистое железо, plain-jane c, с микроконтроллером STM32L4A6.

У меня есть специальная плата, использующая контроллер STM32L4A6 с 1 мегабайтом флэш-памяти, разделенным на 512–2 КБ страниц. Каждую страницу можно индивидуально стереть и запрограммировать с помощью функции, работающей в ОЗУ. Я хотел бы воспользоваться преимуществами этой тонкой организации флэш-памяти для создания встроенного приложения встроенного ПО, которое можно было бы обновлять на лету, изменяя или добавляя отдельные функции в коде. Моя схема состоит в том, чтобы выделить отдельную страницу флэш-памяти для каждой функции, которую, возможно, когда-либо потребуется изменить или создать. Это чрезвычайно расточительно для флэш-памяти, но я никогда не буду использовать более ~ 10%, поэтому я могу позволить себе быть расточительным. Я добился в этом некоторого прогресса и теперь могу внести существенные изменения в работу своего приложения, загрузив очень маленькие фрагменты двоичного кода. Эти «патчи» часто даже не требуют перезагрузки системы.

Проблема, с которой я сталкиваюсь, заключается в том, что когда функция содержит какие-либо постоянные данные, такие как литеральная строка, она оказывается в разделе .rodata. Мне нужно, чтобы родата для данной функции оставалась в той же области, что и функция, которая ее создала. Кто-нибудь знает, как я могу заставить .rodata, созданный в функции, оставаться привязанным к той же функции во флэш-памяти? Например, может быть, .rodata из этой функции можно расположить сразу после самой функции? Может быть, мне нужно использовать -ffunction-sections или что-то в этом роде? Я просмотрел различные руководства по компоновщику, но до сих пор не могу понять, как это сделать. Ниже приведено начало моего скрипта компоновщика. Я не знаю, как включить функцию .rodata в отдельные разделы страницы.

Пример функции:

#define P018 __attribute__((long_call, section(".txt018")))
P018 int Function18(int A, int B){int C = A*B; return C;}

Лучшим примером, показывающим мою проблему, было бы следующее:

#define P152 __attribute__((long_call, section(".txt152")))
P152 void TestFunc(int A){printf("%d Squared Is: %d\r\n",A,A*A);}

В этом случае двоичный эквивалент «%d Squared Is: %d\r\n» можно найти в .rodata вместе со всеми другими литеральными строками в моей программе. Я бы предпочел, чтобы он находился в разделе .txt152.

Фрагмент скрипта компоновщика (в основном генерируется из простой консольной программы.)

MEMORY
{
    p000 (rx)      : ORIGIN = 0x08000000, LENGTH = 0x8000

    p016 (rx)      : ORIGIN = 0x08008000, LENGTH = 0x800
    p017 (rx)      : ORIGIN = 0x08008800, LENGTH = 0x800
    p018 (rx)      : ORIGIN = 0x08009000, LENGTH = 0x800
.
.
.
    p509 (rx)      : ORIGIN = 0x080fe800, LENGTH = 0x800
    p510 (rx)      : ORIGIN = 0x080ff000, LENGTH = 0x800
    p511 (rx)      : ORIGIN = 0x080ff800, LENGTH = 0x800

    ram (rwx)      : ORIGIN = 0x20000000, LENGTH = 256K
    ram2 (rw)      : ORIGIN = 0x10000000, LENGTH = 64K
}

SECTIONS 
{
    .vectors : 
    { 
        KEEP(*(.isr_vector .isr_vector.*))
    } > p000

    .txt016 : { *(.txt016) } > p016  /* first usable 2k page following 32k p000 */ 
    .txt017 : { *(.txt017) } > p017
    .txt018 : { *(.txt018) } > p018
.
.
.
    .txt509 : { *(.txt509) } > p509
    .txt510 : { *(.txt510) } > p510
    .txt511 : { *(.txt511) } > p511

    .text :
    {
        *(.text .text.* .gnu.linkonce.t.*)        
        *(.glue_7t) *(.glue_7)                      
        *(.rodata .rodata* .gnu.linkonce.r.*)       
    } > p000      
.
.
.

Если кому-то интересно, вот мой код RAM для выполнения операции стирания/программирования

__attribute__((long_call, section(".data")))
void CopyPatch
(
        unsigned short Page,
        unsigned int NumberOfBytesToFlash,
        unsigned char *PatchBuf
)
{
    unsigned int            i;
    unsigned long long int  *Flash;

    __ASM volatile ("cpsid i" : : : "memory");                  //disable interrupts
    Flash = (unsigned long long int *)(FLASH_BASE + Page*2048); //set flash memory pointer to Page address
    GPIOE->BSRR = GPIO_BSRR_BS_1;                               //make PE1(LED) high
    FLASH->KEYR = 0x45670123;                                   //unlock the flash
    FLASH->KEYR = 0xCDEF89AB;                                   //unlock the flash
    while(FLASH->SR & FLASH_SR_BSY){}                           //wait while flash memory operation is in progress
    FLASH->CR = FLASH_CR_PER | (Page << 3);                     //set Page erase bit and the Page to erase
    FLASH->CR |= FLASH_CR_STRT;                                 //start erase of Page
    while(FLASH->SR & FLASH_SR_BSY){}                           //wait while Flash memory operation is in progress
    FLASH->CR = FLASH_CR_PG;                                    //set flash programming bit
    for(i=0;i<(NumberOfBytesToFlash/8+1);i++)
    {
        Flash[i] = ((unsigned long long int *)PatchBuf)[i];     //copy RAM to FLASH, 8 bytes at a time
        while(FLASH->SR & FLASH_SR_BSY){}                       //wait while flash memory operation is in progress
    }
    FLASH->CR = FLASH_CR_LOCK;                                  //lock the flash
    GPIOE->BSRR = GPIO_BSRR_BR_1;                               //make PE1(LED) low
    __ASM volatile ("cpsie i" : : : "memory");                  //enable interrupts
}

person ICAVER    schedule 16.04.2020    source источник
comment
Похоже, что ваша примерная функция не генерирует никаких .rodata Я подделал один с помощью char *last; int fnc(int a,int b) { int c = a * b; last = "fnc"; return c } Вместо того, чтобы это делал я, у вас есть функция, которая показывает все интересующие вас пограничные случаи?   -  person Craig Estey    schedule 17.04.2020
comment
Спасибо, Крейг, я отредактировал свой пост, добавив дополнительный пример функции, иллюстрирующий проблему.   -  person ICAVER    schedule 17.04.2020


Ответы (1)


Ладно... Извините за задержку, но я должен был немного подумать об этом...

Я не уверен, что вы можете сделать это [полностью] с помощью одного скрипта компоновщика. Возможно это возможно, но я думаю, что есть более простой/надежный способ [с небольшой дополнительной подготовкой]

Метод, который я использовал раньше, заключается в компиляции с -S для получения файла .s. Изменить/исказить это. А затем скомпилируйте модифицированный .s

Обратите внимание, что у вас могут возникнуть проблемы с глобальным значением, например:

int B;

Это попадет в раздел .comm исходного кода asm. Это может быть не идеально.

Для инициализированных данных:

int B = 23;

Вы можете добавить атрибут раздела, чтобы принудительно отнести его к специальному разделу. В противном случае он окажется в разделе .data.

Таким образом, я мог бы избежать разделов .comm и/или .bss в пользу всегда использования инициализированных данных. Это потому, что .comm имеет ту же проблему, что и .rodata (т. е. он заканчивается одним большим пятном).

В любом случае, ниже приведен пошаговый процесс.


Я помещаю макросы имени раздела в общий файл (например) sctname.h:

#define _SCTJOIN(_pre,_sct)         _pre #_sct

#define _TXTSCT(_sct)       __attribute__((section(_SCTJOIN(".txt",_sct))))
#define _DATSCT(_sct)       __attribute__((section(_SCTJOIN(".dat",_sct))))

#ifdef SCTNO
#define TXTSCT              _TXTSCT(SCTNO)
#define DATSCT              _DATSCT(SCTNO)
#endif

Вот слегка измененная версия вашего файла .c (например, module.c):

#include <stdio.h>

#ifndef SCTNO
#define SCTNO   152
#endif
#include "sctname.h"

int B DATSCT = 23;

TXTSCT void
TestFunc(int A)
{
    printf("%d Squared Is: %d\r\n", A, A * A * B);
}

Чтобы создать файл .s, мы делаем:

cc -S -Wall -Werror -O2 module.c

Фактическое имя/номер раздела можно указать в командной строке:

cc -S -Wall -Werror -O2 -DSCTNO=152 module.c

Это дает нам module.s:

    .file   "module.c"
    .text
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d Squared Is: %d\r\n"
    .section    .txt152,"ax",@progbits
    .p2align 4,,15
    .globl  TestFunc
    .type   TestFunc, @function
TestFunc:
.LFB11:
    .cfi_startproc
    movl    %edi, %edx
    movl    %edi, %esi
    xorl    %eax, %eax
    imull   %edi, %edx
    movl    $.LC0, %edi
    imull   B(%rip), %edx
    jmp printf
    .cfi_endproc
.LFE11:
    .size   TestFunc, .-TestFunc
    .globl  B
    .section    .dat152,"aw"
    .align 4
    .type   B, @object
    .size   B, 4
B:
    .long   23
    .ident  "GCC: (GNU) 8.3.1 20190223 (Red Hat 8.3.1-2)"
    .section    .note.GNU-stack,"",@progbits

Теперь нам нужно прочитать .s и изменить его. Я создал perl-скрипт, который делает это (например, rofix):

#!/usr/bin/perl

master(@ARGV);
exit(0);

sub master
{
    my(@argv) = @_;

    $root = shift(@argv);

    $root =~ s/[.][^.]+$//;

    $sfile = "$root.s";
    $ofile = "$root.TMP";

    open($xfsrc,"<$sfile") or
        die("rofix: unable to open '$sfile' -- $!\n");

    open($xfdst,">$ofile") or
        die("rofix: unable to open '$sfile' -- $!\n");

    $txtpre = "^[.]txt";
    $datpre = "^[.]dat";

    # find the text and data sections
    seek($xfsrc,0,0);
    while ($bf = <$xfsrc>) {
        chomp($bf);

        if ($bf =~ /^\s*[.]section\s(\S+)/) {
            $sctcur = $1;
            sctget($txtpre);
            sctget($datpre);
        }
    }

    # modify the data sections
    seek($xfsrc,0,0);
    while ($bf = <$xfsrc>) {
        chomp($bf);

        if ($bf =~ /^\s*[.]section\s(\S+)/) {
            $sctcur = $1;
            sctfix();
            print($xfdst $bf,"\n");
            next;
        }

        print($xfdst $bf,"\n");
    }

    close($xfsrc);
    close($xfdst);

    system("diff -u $sfile $ofile");

    rename($ofile,$sfile) or
        die("rofix: unable to rename '$ofile' to '$sfile' -- $!\n");
}

sub sctget
{
    my($pre) = @_;
    my($sctname,@sct);

    {
        last unless (defined($pre));

        @sct = split(",",$sctcur);

        $sctname = shift(@sct);
        last unless ($sctname =~ /$pre/);

        printf("sctget: FOUND %s\n",$sctname);

        $sct_lookup{$pre} = $sctname;
    }
}

sub sctfix
{
    my($sctname,@sct);
    my($sctnew);

    {
        last unless ($sctcur =~ /^[.]rodata/);

        $sctnew = $sct_lookup{$txtpre};
        last unless (defined($sctnew));

        @sct = split(",",$sctcur);

        $sctname = shift(@sct);
        $sctname .= $sctnew;

        unshift(@sct,$sctname);
        $sctname = join(",",@sct);

        $bf = sprintf("\t.section\t%s",$sctname);
    }
}

Разница между старым и новым module.s заключается в следующем:

sctget: FOUND .txt152
sctget: FOUND .dat152
--- module.s    2020-04-20 19:02:23.777302484 -0400
+++ module.TMP  2020-04-20 19:06:33.631926065 -0400
@@ -1,6 +1,6 @@
    .file   "module.c"
    .text
-   .section    .rodata.str1.1,"aMS",@progbits,1
+   .section    .rodata.txt152,"aMS",@progbits,1
 .LC0:
    .string "%d Squared Is: %d\r\n"
    .section    .txt152,"ax",@progbits

Итак, теперь создайте .o с помощью:

cc -c module.s

Для make-файла это может быть что-то вроде [с некоторыми подстановочными знаками]:

module.o: module.c
    cc -S -Wall -Werror -O2 module.c
    ./rofix module.s
    cc -c module.s

Теперь вы можете добавить соответствующие места размещения в свой скрипт компоновщика для [вашего исходного раздела] .txt152 и нового .rodata.txt152.

И раздел инициализированных данных .dat152

Обратите внимание, что фактические соглашения об именах произвольны. Если вы хотите изменить их, просто измените rofix [и скрипт компоновщика] соответствующим образом.


Вот вывод readelf -a для module.o:

Обратите внимание, что есть еще раздел .rela.txt152!?!?

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          808 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         15
  Section header string table index: 14

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000000  0000000000000000  AX       0     0     1
  [ 2] .data             PROGBITS         0000000000000000  00000040
       0000000000000000  0000000000000000  WA       0     0     1
  [ 3] .bss              NOBITS           0000000000000000  00000040
       0000000000000000  0000000000000000  WA       0     0     1
  [ 4] .rodata.txt152    PROGBITS         0000000000000000  00000040
       0000000000000014  0000000000000001 AMS       0     0     1
  [ 5] .txt152           PROGBITS         0000000000000000  00000060
       000000000000001a  0000000000000000  AX       0     0     16
  [ 6] .rela.txt152      RELA             0000000000000000  00000250
       0000000000000048  0000000000000018   I      12     5     8
  [ 7] .dat152           PROGBITS         0000000000000000  0000007c
       0000000000000004  0000000000000000  WA       0     0     4
  [ 8] .comment          PROGBITS         0000000000000000  00000080
       000000000000002d  0000000000000001  MS       0     0     1
  [ 9] .note.GNU-stack   PROGBITS         0000000000000000  000000ad
       0000000000000000  0000000000000000           0     0     1
  [10] .eh_frame         PROGBITS         0000000000000000  000000b0
       0000000000000030  0000000000000000   A       0     0     8
  [11] .rela.eh_frame    RELA             0000000000000000  00000298
       0000000000000018  0000000000000018   I      12    10     8
  [12] .symtab           SYMTAB           0000000000000000  000000e0
       0000000000000150  0000000000000018          13    11     8
  [13] .strtab           STRTAB           0000000000000000  00000230
       000000000000001c  0000000000000000           0     0     1
  [14] .shstrtab         STRTAB           0000000000000000  000002b0
       0000000000000078  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

There are no section groups in this file.

There are no program headers in this file.

There is no dynamic section in this file.

Relocation section '.rela.txt152' at offset 0x250 contains 3 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
00000000000a  00050000000a R_X86_64_32       0000000000000000 .rodata.txt152 + 0
000000000011  000c00000002 R_X86_64_PC32     0000000000000000 B - 4
000000000016  000d00000004 R_X86_64_PLT32    0000000000000000 printf - 4

Relocation section '.rela.eh_frame' at offset 0x298 contains 1 entry:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000020  000600000002 R_X86_64_PC32     0000000000000000 .txt152 + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table '.symtab' contains 14 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS module.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    7
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    9
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT   10
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    8
    11: 0000000000000000    26 FUNC    GLOBAL DEFAULT    5 TestFunc
    12: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    7 B
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

No version information found in this file.
person Craig Estey    schedule 20.04.2020
comment
Хорошо, большое спасибо за это! Я думаю, что вижу, что вы делаете в своем коде Perl, спасибо, что включили разницу между старым и новым .s. Я мог бы перевести ваш perl на c. Кроме того, спасибо за то, что показали способ работы с глобальными переменными — я намеревался избегать их использования, но они, безусловно, удобны в качестве флагов в ISR. Теперь я должен быть в состоянии объединить .rodata.txt??? а .txt??? в отдельные разделы в моем скрипте компоновщика. В настоящее время я использую IDE на основе Eclipse, поэтому мне потребуется некоторое время, чтобы все изменить в командной строке -> makefile. Я дам вам знать, когда у меня все получится. - person ICAVER; 22.04.2020