Código de inicio completo para la inicialización de la región Cortex M3 .bss

He desarrollado, inspirado a partir de aquí , un código de inicio básico para arm cortex M3. Sin embargo, me encuentro con el siguiente problema: supongamos que declaro una variable global no inicializada, digamos de tipo char sin firmar en main.c

#include ...
unsigned char var; 
...
int main()
{
 ...
}

esto hace que la región .bss en STM32 f103 comience en _BSS_START=0x20000000 y finalice en _BSS_END = 0x20000001. Ahora, el código de inicio

    unsigned int * bss_start_p = &_BSS_START; 
    unsigned int * bss_end_p = &_BSS_END;

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }

intenta inicializar a cero toda la región .bss. Sin embargo, dentro de ese ciclo while, el puntero aumenta con 4 bytes, por lo tanto, después de un paso bss_start_p = 0x20000004, por lo tanto, siempre será diferente de bss_end_p, lo que conduce a un ciclo infinito, etc.

¿Hay alguna solución estándar para esto? ¿Se supone que debo "forzar" de alguna manera la dimensión de la región .bss para que sea un múltiplo de 4? ¿O debería usar un puntero a char sin firmar para caminar a través de la región .bss? Tal vez algo como:

    unsigned char * bss_start_p = (unsigned char *)(&_BSS_START); 
    unsigned char * bss_end_p = (unsigned char *)(&_BSS_END);

    while(bss_start_p != bss_end_p)
    {
        *bss_start_p = 0;
        bss_start_p++;
    }
```
usar menos que. bootstraps están escritos en ensamblador por una razón. en primer lugar, ahora ha creado un problema de .data. es una cosa de gallina y huevo para usar/asumir que C funciona, confía en .text, .bss y .data como mínimo, pero está escribiendo código C que asegura que el código C funcionará, usando cosas en código C que requiere un bootstrap posiblemente escrito en código C que se basa en el funcionamiento de C.
el código para copiar .data es muy similar a .bss, pero si lo escribes como el código anterior, entonces necesitas copiar .data para poder copiar .data.

Respuestas (4)

Como sospecha, esto sucede porque el tipo de datos int sin firmar tiene un tamaño de 4 bytes. Cada *bss_start_p = 0;declaración borra cuatro bytes del área bss.

El rango de memoria bss debe alinearse correctamente. Simplemente puede definir _BSS_START y _BSS_END para que el tamaño total sea un múltiplo de cuatro, pero esto generalmente se maneja permitiendo que la secuencia de comandos del enlazador defina las ubicaciones de inicio y finalización.

Como ejemplo, aquí está la sección del enlazador en uno de mis proyectos:

.bss (NOLOAD) : ALIGN(4)
{
    __bss_start__ = .;
    *(.bss)
    . = ALIGN(4);
    __bss_end__ = .;
} >RAM

Las ALIGN(4)declaraciones se encargan de las cosas.

Además, es posible que desee cambiar

while(bss_start_p != bss_end_p)

a

while(bss_start_p < bss_end_p).

Esto no evitará el problema (ya que podrías estar borrando de 1 a 3 bytes más de lo que deseas), pero podría minimizar el impacto :)

@CMarius Después de reflexionar, creo que su idea de puntero de caracteres funcionaría muy bien, aunque requeriría más ciclos. Pero no estoy seguro de si habrá problemas posteriores con la siguiente área de memoria desalineada, por lo que no lo mencionaré en mi respuesta ...
while(bss_start_p < bss_end_p - 1)seguido de una limpieza de bytes del rango de memoria restante eliminaría la última preocupación.

La solución estándar es memset():

#include <string.h>
memset(&_BSS_START, 0, &_BSS_END - &_BSS_START)

Si no puede usar la biblioteca estándar, entonces tendrá que decidir si está bien en su caso redondear el tamaño del área de memoria hasta 4 bytes y continuar usando unsigned int *; o si necesita ser estricto al respecto, en cuyo caso necesitaría usar unsigned char *.

Si redondea el tamaño, como en su primer ciclo, entonces bss_start_ppuede terminar siendo mayor que, bss_end_ppero eso es fácil de manejar con una comparación menor que <en lugar de una prueba de desigualdad.

Por supuesto, también podría llenar la mayor parte del área de la memoria con transferencias de 32 bits, y solo los últimos bytes con transferencias de 8 bits, pero eso es más trabajo por poca ganancia, particularmente aquí cuando es solo una pieza de código de inicio.

Muy de acuerdo con el uso de memset(). Pero la alineación a 4 bytes es más o menos imprescindible. Entonces por qué no hacerlo?
De ninguna manera, la forma o la forma es la solución estándar para que Bootstrap use Memset, eso es una locura.
no usas el mismo idioma para iniciar ese idioma
el código de arranque y la secuencia de comandos del enlazador están muy casados, le resultará común que la secuencia de comandos del enlazador alinee y dimensione el .bss en al menos un límite de 4 bytes para mejorar el relleno (en el arranque) en 4x sobre byte a la vez instrucciones (asumiendo (mínimo) buses de 32 bits, lo cual es típico para arm pero hay excepciones)
@old_timer, la función C estándar para configurar la memoria en un valor particular es memset(), y C es lo que parecen estar programando. La implementación simple de memset()también es más o menos ese bucle, no es como si dependiera de mucho más. Dado que es un microcontrolador, también asumo que no hay un enlace dinámico o algo así (y mirando el enlace, no lo hay, es solo una llamada main()después de ese ciclo de puesta a cero), por lo que el compilador debería ser capaz de entrar memset()allí junto con cualquier otra función (o para implementarlo en línea).
Por supuesto, para situaciones personalizadas, soluciones personalizadas, pero pidieron una forma estándar.
esta es una pregunta de inicio de arranque además de una pregunta de menos que vs igual, simplemente no usa memset para el arranque de C por definición, no usa el idioma que está arrancando con el idioma que está arrancando porque necesitará arrancar ese código con algún otro arranque para que pueda arrancar el idioma con el idioma. o puede hacerlo como todos los demás y usar algún otro lenguaje, lo que significa lenguaje ensamblador para C boostraps, sin hacer llamadas a funciones C (incluso si memset está optimizado en asm). escribes las tres/cuatro líneas de código en asm.
el operador también confía en que .data se inicialice antes que .bss, espera que usen un memcpy para inicializar .data para que puedan inicializar .data y luego cómo van a inicializar .data confiando en .data para inicializar .¿datos? Del mismo modo, no tiene absolutamente ningún sentido usar C para arrancar C. .data también se trata en unas pocas líneas de ensamblaje, ya que no hay muchos otros lenguajes ligeros para usar en lugar de asm que tampoco requieran su propio arranque.
sí, con una mayor comprensión del idioma y de lo que está sucediendo aquí, es fácil no confiar en .data para iniciar .bss para el código como se muestra. Aún más fácil no intentar usar C para arrancar C. (muévalo a .text/.rodata dependiendo de la cadena de herramientas)
@old_timer No "arranca C", solo arranca su programa. Seguramente no puede hacer todo lo que quiera en el código de arranque, pero siempre que el código no dependa del contenido de .bss, todo debería funcionar.
@old_timer, ¿Cuál es exactamente la magia en "bootstrapping" aquí? Si observa el código en el enlace (en "Código de inicio C"), verá que solo hay una simple llamada main()en la parte inferior de la función de inicio que, por ejemplo, borra bss. Ahora, suponiendo que uno pueda llamar memset()desde main()¿qué hace la diferencia mágica aquí? Parece solo una llamada de función, no puedo decir cómo marcaría la diferencia.
@old_timer, en cuanto al arranque de un lenguaje... No creo que C puro en un microcontrolador tenga mucho estado interno para arrancar. Claro, en un sistema operativo real, la biblioteca estándar necesitaría inicializarse stdiny otros, y organizarse argcy argvestar disponible para main, pero no creo que se usen comúnmente en MCU. (Es posible que haya olvidado algo, claro). Y te das cuenta de que ya están usando C en la función de inicio, ¿sí? Si cree que, para empezar, no deberían hacer eso, publique una respuesta en ese sentido (oh, veo que ya lo hizo, bien).

Solo cambia !=a <. Ese suele ser un mejor enfoque de todos modos, ya que trata problemas como este.

Hay innumerables otros sitios y ejemplos. Muchos miles, si no decenas de miles. Existen las conocidas bibliotecas c con scripts de enlace y código boostrap, newlib, glibc en particular, pero hay otras que puede encontrar. Bootstraping C con C no tiene sentido.

Su pregunta ha sido respondida, está tratando de hacer una comparación exacta de cosas que podrían no ser exactas, podría no comenzar en un límite conocido o terminar en un límite conocido. Por lo tanto, puede hacer menos que, pero si el código no funcionó con una comparación exacta, eso significa que está poniendo a cero más allá de .bss en la siguiente sección, lo que puede o no causar que sucedan cosas malas, así que simplemente reemplace con menos que no. la solución.

Así que aquí va TL; DR está bien. No inicias un idioma con ese idioma, puedes salirte con la tuya seguro, pero estás jugando con fuego cuando haces eso. Si recién está aprendiendo a hacer esto, debe tener cuidado, no la suerte tonta o los hechos que aún no ha descubierto.

El script del enlazador y el código de arranque tienen una relación muy íntima, están casados, unidos por la cadera, no se desarrolla uno sin el otro, lo que lleva a un fracaso masivo. Y desafortunadamente, el script del enlazador está definido por el enlazador y el lenguaje ensamblador definido por el ensamblador, por lo que, a medida que cambie las cadenas de herramientas, tendrá que volver a escribir ambos. ¿Por qué lenguaje ensamblador? No necesita arranque, los lenguajes compilados generalmente lo necesitan. C lo hace si no desea limitar su uso del idioma. Comenzaré con algo muy simple que tiene requisitos mínimos específicos de la cadena de herramientas, no asuma que las variables .bss son cero (hace que el código sea menos legible si la variable nunca se inicializa en ese idioma , intente evitar esto, no es cierto para las variables locales, por lo que debe estar al tanto de cuándo lo usa. Entonces, ¿por qué estamos hablando de .bss y .data? (las variables globales son buenas para el trabajo de este nivel, pero ese es otro tema)) la otra regla para la solución simple es no inicializar variables en la declaración, hacerlo en el código. sí, quema más flash, generalmente tiene mucho, no todas las variables se inicializan con constantes de todos modos que terminan consumiendo instrucciones.

Puede notar por el diseño de cortex-m que pueden haber estado pensando que no hay ningún código de arranque, por lo que no hay compatibilidad con .data ni .bss. La mayoría de las personas que usan globales no pueden vivir sin ellos, así que aquí va:

Podría hacer esto más mínimo pero un ejemplo funcional mínimo para todos los cortex-ms usando la cadena de herramientas gnu, no recuerdo qué versiones puede comenzar con 5.xx o más hasta el 9.xx actual. Cambié los scripts del enlazador en algún lugar alrededor de 3. xx o 4.xx a medida que aprendí más y como gnu cambié algo que rompió mi primero.

oreja:

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

punto de entrada en el código C:

void bounce ( unsigned int );

unsigned int a;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

guión del enlazador.

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

Todos estos podrían ser más pequeños y seguir funcionando, agregue algunas cosas adicionales aquí solo para verlo en el trabajo.

compilación y enlace optimizados.

00000000 <_start>:
   0:   20001000
   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

00000014 <reset>:
  14:   f000 f804   bl  20 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
    ...

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

para algunos proveedores, desea usar 0x08000000 o 0x01000000 u otras direcciones similares, ya que el flash se asigna allí y se refleja en 0x00000000 en algunos modos de arranque. algunos solo tienen una parte del flash reflejada en 0x00000000, por lo que desea que la tabla de vectores apunte al espacio flash de la aplicación, no a cero. ya que está basado en tablas vectoriales, todo funciona.

primero tenga en cuenta que los cortex-ms son solo máquinas de pulgar y, por alguna razón, impusieron una dirección de función de pulgar, lo que significa que lsbit es impar. Conozca sus herramientas, las directivas .thumb_func le dicen al ensamblador gnu que la siguiente etiqueta es una dirección de función de pulgar. hacer el +1 en la tabla conducirá al fracaso, no caiga en la tentación de hacerlo, hágalo bien. hay otras formas de ensamblador gnu para declarar una función, este es el enfoque mínimo.

   4:   00000015
   8:   0000001b
   c:   0000001b
  10:   0000001b

no arrancará si no obtienes la tabla de vectores correctamente.

Podría decirse que solo necesita el vector del puntero de la pila (puede poner cualquier cosa allí si desea configurar el puntero de la pila usted mismo en el código) y el vector de reinicio. Puse cuatro aquí sin ninguna razón en particular. Suele poner 16 pero quería acortar este ejemplo.

Entonces, ¿qué es lo mínimo que debe hacer un bootstrap de C? 1. establecer el puntero de la pila 2. cero .bss 3. copiar .data 4. bifurcarse o llamar al punto de entrada C

el punto de entrada de C suele llamarse main(). pero algunas cadenas de herramientas ven main() y agregan basura adicional a su código. Intencionalmente uso un nombre diferente. YMMV.

la copia de .data no es necesaria si todo está basado en ram. al ser un microcontrolador cortex-m, es técnicamente posible pero poco probable, por lo que se necesita la copia de .data ... si hay .data.

Mi primer ejemplo y un estilo de codificación es no confiar en .data ni .bss, como en este ejemplo. Arm se encargó del puntero de la pila, por lo que lo único que queda es llamar al punto de entrada. Me gusta tenerlo para que el punto de entrada pueda regresar, muchas personas argumentan que nunca deberías hacer eso. podrías hacer esto entonces:

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word centry
.word done
.word done
.word done

y no regresar de centry () y no tener código de controlador de reinicio.

00000020 <centry>:
  20:   2207        movs    r2, #7
  22:   b510        push    {r4, lr}
  24:   4b04        ldr r3, [pc, #16]   ; (38 <centry+0x18>)
  26:   2007        movs    r0, #7
  28:   601a        str r2, [r3, #0]
  2a:   f7ff fff7   bl  1c <bounce>
  2e:   2000        movs    r0, #0
  30:   bc10        pop {r4}
  32:   bc02        pop {r1}
  34:   4708        bx  r1
  36:   46c0        nop         ; (mov r8, r8)
  38:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000

el enlazador ha puesto las cosas donde le pedimos. Y en general tenemos un programa totalmente funcional.

Entonces, primero trabaje en el script del enlazador:

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

enfatizando que los nombres rom y ram no tienen significado, solo conectan los puntos para el enlace entre secciones.

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:
    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    bx lr

.align
.word __data_rom_start__
.word __data_start__
.word __data_end__
.word __data_size__

agregue algunos elementos para que podamos ver lo que hicieron las herramientas

void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

agregue algunos artículos para colocar en esas secciones. y obten

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000001b    andeq   r0, r0, r11, lsl r0
   c:   0000001b    andeq   r0, r0, r11, lsl r0
  10:   0000001b    andeq   r0, r0, r11, lsl r0

00000014 <reset>:
  14:   f000 f80c   bl  30 <centry>
  18:   e7ff        b.n 1a <done>

0000001a <done>:
  1a:   e7fe        b.n 1a <done>

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

00000030 <centry>:
  30:   2207        movs    r2, #7
  32:   b510        push    {r4, lr}
  34:   4b04        ldr r3, [pc, #16]   ; (48 <centry+0x18>)
  36:   2007        movs    r0, #7
  38:   601a        str r2, [r3, #0]
  3a:   f7ff ffef   bl  1c <bounce>
  3e:   2000        movs    r0, #0
  40:   bc10        pop {r4}
  42:   bc02        pop {r1}
  44:   4708        bx  r1
  46:   46c0        nop         ; (mov r8, r8)
  48:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

aquí están las cosas que estamos buscando en ese experimento (no tenga en cuenta ninguna razón para cargar o ejecutar ningún código... conozca sus herramientas, apréndalas)

  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

Entonces, lo que aprendimos aquí es que la posición de las variables es muy sensible en los scripts de gnu linker. tenga en cuenta la posición de data_rom_start frente a data_start , pero ¿por qué funciona data_end ? Dejaré que lo averigües. Ya entiendo por qué uno podría no querer tener que meterse con los scripts del enlazador y simplemente llegar a la programación simple ...

así que otra cosa que aprendimos aquí es que el enlazador alineó data_rom_start para nosotros, no necesitábamos un ALIGN(4) allí. ¿Deberíamos suponer que eso siempre funcionará?

También tenga en cuenta que se rellenó al salir, tenemos 5 bytes de .data pero lo rellenó a 8. Sin ningún ALIGN(), ya podemos hacer la copia usando palabras. Según lo que vemos con esta cadena de herramientas en mi computadora hoy, ¿podría ser cierto para el pasado y el futuro? Quién sabe, incluso con los ALIGN que deben verificar periódicamente para confirmar que alguna versión nueva no rompió las cosas, lo harán de vez en cuando.

de ese experimento pasemos a esto solo para estar seguros.

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   . = ALIGN(4);
   __data_end__ = .;
   } > ted AT > bob
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   . = ALIGN(4);
   __bss_end__ = .;
   } > ted
   __bss_size__ = __bss_end__ - __bss_start__;

}

moviendo los extremos hacia adentro para que sea consistente con lo que hacen otras personas. Y eso no lo cambió:

0000001c <bounce>:
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

una prueba rápida más:

.globl bounce
bounce:
    nop
    bx lr

donación

0000001c <bounce>:
  1c:   46c0        nop         ; (mov r8, r8)
  1e:   4770        bx  lr
  20:   0000004c    andeq   r0, r0, r12, asr #32
  24:   20000000    andcs   r0, r0, r0
  28:   20000008    andcs   r0, r0, r8
  2c:   00000008    andeq   r0, r0, r8

no es necesario rellenar entre rebote y .align

Ohh, claro, ahora recuerdo por qué no puse el _fin__ dentro. porque NO FUNCIONA.

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(4);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

   . = ALIGN(4);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

}

un código simple, pero muy portátil para casarse con este script de enlace

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
bss_zero:
    stmia r1!,{r2}
    sub r0,#4
    bne bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3}
    stmia r2!,{r3}
    sub r0,#4
    bne data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__

donación

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   0000003d    andeq   r0, r0, sp, lsr r0
   c:   0000003d    andeq   r0, r0, sp, lsr r0
  10:   0000003d    andeq   r0, r0, sp, lsr r0

00000014 <reset>:
  14:   480c        ldr r0, [pc, #48]   ; (48 <blen>)
  16:   2800        cmp r0, #0
  18:   d004        beq.n   24 <bss_zero_done>
  1a:   490a        ldr r1, [pc, #40]   ; (44 <bstart>)
  1c:   2200        movs    r2, #0

0000001e <bss_zero>:
  1e:   c104        stmia   r1!, {r2}
  20:   3804        subs    r0, #4
  22:   d1fc        bne.n   1e <bss_zero>

00000024 <bss_zero_done>:
  24:   480b        ldr r0, [pc, #44]   ; (54 <dlen>)
  26:   2800        cmp r0, #0
  28:   d005        beq.n   36 <data_copy_done>
  2a:   4908        ldr r1, [pc, #32]   ; (4c <rstart>)
  2c:   4a08        ldr r2, [pc, #32]   ; (50 <dstart>)

0000002e <data_copy>:
  2e:   c908        ldmia   r1!, {r3}
  30:   c208        stmia   r2!, {r3}
  32:   3804        subs    r0, #4
  34:   d1fb        bne.n   2e <data_copy>

00000036 <data_copy_done>:
  36:   f000 f80f   bl  58 <centry>
  3a:   e7ff        b.n 3c <done>

0000003c <done>:
  3c:   e7fe        b.n 3c <done>

0000003e <bounce>:
  3e:   46c0        nop         ; (mov r8, r8)
  40:   4770        bx  lr
  42:   46c0        nop         ; (mov r8, r8)

00000044 <bstart>:
  44:   20000008    andcs   r0, r0, r8

00000048 <blen>:
  48:   00000004    andeq   r0, r0, r4

0000004c <rstart>:
  4c:   00000074    andeq   r0, r0, r4, ror r0

00000050 <dstart>:
  50:   20000000    andcs   r0, r0, r0

00000054 <dlen>:
  54:   00000008    andeq   r0, r0, r8

00000058 <centry>:
  58:   2207        movs    r2, #7
  5a:   b510        push    {r4, lr}
  5c:   4b04        ldr r3, [pc, #16]   ; (70 <centry+0x18>)
  5e:   2007        movs    r0, #7
  60:   601a        str r2, [r3, #0]
  62:   f7ff ffec   bl  3e <bounce>
  66:   2000        movs    r0, #0
  68:   bc10        pop {r4}
  6a:   bc02        pop {r1}
  6c:   4708        bx  r1
  6e:   46c0        nop         ; (mov r8, r8)
  70:   20000008    andcs   r0, r0, r8

Disassembly of section .data:

20000000 <c>:
20000000:   00000005    andeq   r0, r0, r5

20000004 <b>:
20000004:   00000004    andeq   r0, r0, r4

Disassembly of section .bss:

20000008 <a>:
20000008:   00000000    andeq   r0, r0, r0

podemos parar allí o seguir adelante. Si inicializamos en el mismo orden que la secuencia de comandos del enlazador, está bien si pasamos a lo siguiente, ya que aún no hemos llegado allí. y stm/ldm solo se requieren/desean usar direcciones alineadas con palabras, por lo que si cambia a:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

con bss primero en el script del enlazador, y sí, quiere ble, no bls.

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3804        subs    r0, #4
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000004    andcs   r0, r0, r4

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

esos bucles irán más rápido. ahora no sé si los buses ahb pueden tener 64 bits de ancho o no, pero para un brazo de tamaño completo, querrá alinear estas cosas en los límites de 64 bits. un ldm/stm de cuatro registros en un límite de 32 bits pero no en un límite de 64 bits se convierte en tres transacciones de bus separadas, donde alineadas en un límite de 64 bits hay una única transacción que ahorra varios relojes por instrucción.

ya que estamos haciendo baremetal y somos completamente responsables de todo lo que podemos poner, digamos bss primero, luego datos, luego, si tenemos un montón, entonces la pila crece de arriba hacia abajo, por lo que si ponemos a cero bss y derramamos algo siempre que comencemos en el lugar correcto que está bien, no estamos usando esa memoria todavía. luego copiamos .data y podemos verterlos en el montón, eso está bien, montón o no, hay mucho espacio para la pila, por lo que no estamos pisando a nadie/nada (siempre y cuando nos aseguremos de que lo hagamos en el script del enlazador). si existe alguna preocupación, haga que ALIGN() sea más grande para que estemos siempre dentro de nuestro espacio para estos rellenos.

así que mi solución simple, tómalo o déjalo. Bienvenido a corregir cualquier error, no ejecuté esto en el hardware ni en mi simulador...

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > bob

    .rodata : { *(.rodata*) } > bob

   . = ALIGN(8);
   .bss  : {
   __bss_start__ = .;
   *(.bss*)
   } > ted
   . = ALIGN(4);
   __bss_end__ = .;
   __bss_size__ = __bss_end__ - __bss_start__;

   . = ALIGN(8);
   __data_rom_start__ = .;
   .data : {
    __data_start__ = .;
    *(.data*)
   } > ted AT > bob
   . = ALIGN(4);
   __data_end__ = .;
   __data_size__ = __data_end__ - __data_start__;

}



.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20000800
.word reset
.word done
.word done
.word done

.thumb_func
reset:

    ldr r0,blen
    cmp r0,#0
    beq bss_zero_done
    ldr r1,bstart
    mov r2,#0
    mov r3,#0
    mov r4,#0
    mov r5,#0
bss_zero:
    stmia r1!,{r2,r3,r4,r5}
    sub r0,#16
    ble bss_zero
bss_zero_done:

    ldr r0,dlen
    cmp r0,#0
    beq data_copy_done
    ldr r1,rstart
    ldr r2,dstart
data_copy:
    ldmia r1!,{r3,r4,r5,r6}
    stmia r2!,{r3,r4,r5,r6}
    sub r0,#16
    ble data_copy
data_copy_done:

    bl centry
    b done

.thumb_func
done:   b .

.thumb_func
.globl bounce
bounce:
    nop
    bx lr

.align
bstart: .word __bss_start__
blen:   .word __bss_size__
rstart: .word __data_rom_start__
dstart: .word __data_start__
dlen:   .word __data_size__


void bounce ( unsigned int );

unsigned int a;

unsigned int b=4;
unsigned char c=5;

int centry ( void )
{
    a = 7;
    bounce(a);
    return(0);
}

arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-ld -o hello.elf -T flash.ld flash.o centry.o
arm-none-eabi-objdump -D hello.elf > hello.list
arm-none-eabi-objcopy hello.elf hello.bin -O binary

Ponlo todo junto y obtienes:

Disassembly of section .text:

00000000 <_start>:
   0:   20000800    andcs   r0, r0, r0, lsl #16
   4:   00000015    andeq   r0, r0, r5, lsl r0
   8:   00000043    andeq   r0, r0, r3, asr #32
   c:   00000043    andeq   r0, r0, r3, asr #32
  10:   00000043    andeq   r0, r0, r3, asr #32

00000014 <reset>:
  14:   480d        ldr r0, [pc, #52]   ; (4c <blen>)
  16:   2800        cmp r0, #0
  18:   d007        beq.n   2a <bss_zero_done>
  1a:   490b        ldr r1, [pc, #44]   ; (48 <bstart>)
  1c:   2200        movs    r2, #0
  1e:   2300        movs    r3, #0
  20:   2400        movs    r4, #0
  22:   2500        movs    r5, #0

00000024 <bss_zero>:
  24:   c13c        stmia   r1!, {r2, r3, r4, r5}
  26:   3810        subs    r0, #16
  28:   ddfc        ble.n   24 <bss_zero>

0000002a <bss_zero_done>:
  2a:   480b        ldr r0, [pc, #44]   ; (58 <dlen>)
  2c:   2800        cmp r0, #0
  2e:   d005        beq.n   3c <data_copy_done>
  30:   4907        ldr r1, [pc, #28]   ; (50 <rstart>)
  32:   4a08        ldr r2, [pc, #32]   ; (54 <dstart>)

00000034 <data_copy>:
  34:   c978        ldmia   r1!, {r3, r4, r5, r6}
  36:   c278        stmia   r2!, {r3, r4, r5, r6}
  38:   3810        subs    r0, #16
  3a:   ddfb        ble.n   34 <data_copy>

0000003c <data_copy_done>:
  3c:   f000 f80e   bl  5c <centry>
  40:   e7ff        b.n 42 <done>

00000042 <done>:
  42:   e7fe        b.n 42 <done>

00000044 <bounce>:
  44:   46c0        nop         ; (mov r8, r8)
  46:   4770        bx  lr

00000048 <bstart>:
  48:   20000000    andcs   r0, r0, r0

0000004c <blen>:
  4c:   00000004    andeq   r0, r0, r4

00000050 <rstart>:
  50:   20000008    andcs   r0, r0, r8

00000054 <dstart>:
  54:   20000004    andcs   r0, r0, r4

00000058 <dlen>:
  58:   00000008    andeq   r0, r0, r8

0000005c <centry>:
  5c:   2207        movs    r2, #7
  5e:   b510        push    {r4, lr}
  60:   4b04        ldr r3, [pc, #16]   ; (74 <centry+0x18>)
  62:   2007        movs    r0, #7
  64:   601a        str r2, [r3, #0]
  66:   f7ff ffed   bl  44 <bounce>
  6a:   2000        movs    r0, #0
  6c:   bc10        pop {r4}
  6e:   bc02        pop {r1}
  70:   4708        bx  r1
  72:   46c0        nop         ; (mov r8, r8)
  74:   20000000    andcs   r0, r0, r0

Disassembly of section .bss:

20000000 <a>:
20000000:   00000000    andeq   r0, r0, r0

Disassembly of section .data:

20000004 <c>:
20000004:   00000005    andeq   r0, r0, r5

20000008 <b>:
20000008:   00000004    andeq   r0, r0, r4

tenga en cuenta que esto funciona con arm-none-eabi- y arm-linux-gnueabi y las otras variantes ya que no se usaron cosas ghee whiz.

Descubrirás cuando mires a tu alrededor que la gente se volverá loca con las cosas geniales de ghee en sus scripts de enlace, cosas enormes y monstruosas del fregadero de la cocina. Es mejor simplemente saber cómo hacerlo (o mejor cómo dominar las herramientas para que pueda controlar lo que sucede) en lugar de confiar en las cosas de otra persona y no saber dónde se va a romper porque no entiende y/o quiere investigar él.

como regla general, no arranque un lenguaje con el mismo lenguaje (bootstrap en este sentido significa ejecutar código sin compilar un compilador con el mismo compilador) desea usar un lenguaje más simple con menos arranque. Es por eso que C se realiza en ensamblaje, no tiene requisitos de arranque, solo comienza desde la primera instrucción después del reinicio. JAVA, seguro que puede escribir el jvm en C y arrancar ese C con asm y luego arrancar el JAVA si lo desea con C, pero también ejecutar el JAVA en C también.

Debido a que controlamos las suposiciones sobre estos bucles de copia, son, por definición, más estrictos y limpios que memcpy/memset ajustados a mano.

Tenga en cuenta que su otro problema fue este:

unsigned int * bss_start_p = &_BSS_START; 
unsigned int * bss_end_p = &_BSS_END;

si estos son locales bien, no hay problema, si estos son globales, entonces necesita .data inicializados primero para que funcionen y si intenta ese truco para hacer .data, entonces fallará. Variables locales, bien, eso funcionará. si por alguna razón decidiste hacer locales estáticos (globales locales como me gusta llamarlos) entonces estás nuevamente en problemas. Sin embargo, cada vez que realiza una tarea en una declaración, debe pensar en ello, cómo se implementa y si es seguro/sano. Cada vez que asume que una variable es cero cuando no se declara, el mismo trato, si una variable local no se supone que es cero, si es global, entonces lo es. si nunca asume que son cero, entonces nunca tendrá que preocuparse.

Impresionante, esta es la segunda vez que excedo el número máximo de caracteres en una respuesta....
Esta pregunta pertenece a stackoverflow, no a la ingeniería eléctrica.
Además, confiar en un enlace externo en su pregunta no es una buena forma, si el enlace desaparece antes de la pregunta, es posible que la pregunta no tenga sentido.
En este caso, su título y contenido son suficientes para saber que está intentando arrancar C en un microcontrolador en particular y está deambulando por la inicialización de .bss y .data
pero en este caso han sido engañados por un sitio web muy informativo.
Además de todo esto, usar llamadas de biblioteca C en baremetal es un problema. un gran porcentaje de las llamadas a la biblioteca C requieren un sistema. Baremetal por definición significa sin sistema. Incluso si se limita a las llamadas que no requieren un sistema, es posible que deba tragar más que solo esas, y es posible que deba hacer un trabajo adicional para obtener esas llamadas que no está utilizando para compilar para que puedan inflar su binario sin errores de compilación. .
Baremetal significa que está o debería limitarse en cuanto al soporte de idioma que tiene o al que está acostumbrado. Con ese pensamiento, deja de usar .data y asume que .bss es cero y puedes hacer la vida mucho más simple. entre una y cinco líneas de código de bootstrap. Hecho. Todo C después de eso, agradable, limpio, puro C, sin preocupaciones/dolores/llamadas del sistema que se interpongan en el camino.
¡Hay mucha información útil aquí! Gracias por tomarse el tiempo para escribir esta respuesta :-)
No hay problema, diviértete.