0

I have an ARM M55 program running using QEMU (machine = mps3-an547). I've used this document to define where the __ROM_BASE is in my linker script (0x1000_0000) - all is working fine.

Unfortunately, when attempting to compile a larger program, I get errors complaining that the flash size is too small. Having looked at the document linked above, I can see that there is another code region at 0x1100_0000 with a size of 2MB: enter image description here

I changed my linker script __ROM_BASE and __ROM_SIZE accordingly but the resulting image won't boot with QEMU.

I think I may need to use the device loader argument when calling QEMU but I can't work out how. I've tried: -device loader,data=0x11000000,data-len=0x111F_FFFF (complains about wrong data-len format)

This should set the program counter (but is this the same as the start of my code region? -device loader,addr=0x11000000,cpu-num=0 (crashes)

I've seen this answer but don't have the experience to understand parts of the question (e.g. what do they mean by 'Everything in my ELF is at 0x40004000'?

I'm clearly do not understand a few things regarding the memory map and how code is loaded into QEMU so any pointers are much appreciated.

My original linker script:

/******************************************************************************
 * @file     gcc_arm.ld
 * @brief    GNU Linker Script for Cortex-M based device
 * @version  V2.2.0
 * @date     16. December 2020
 ******************************************************************************/
/*
 * Copyright (c) 2009-2020 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 *-------- <<< Use Configuration Wizard in Context Menu >>> -------------------
 */

/*---------------------- Flash Configuration ----------------------------------
  <h> Flash Configuration
    <o0> Flash Base Address <0x0-0xFFFFFFFF:8>
    <o1> Flash Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
  -----------------------------------------------------------------------------*/
/* See https://developer.arm.com/documentation/dai0547/latest?_ga=2.157798205.688811587.1624957483-616249991.1623083451
*/
__ROM_BASE = 0x10000000;
__ROM_SIZE = 512K;

/*--------------------- Embedded RAM Configuration ----------------------------
  <h> RAM Configuration
    <o0> RAM Base Address    <0x0-0xFFFFFFFF:8>
    <o1> RAM Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
 -----------------------------------------------------------------------------*/
/* See https://developer.arm.com/documentation/dai0547/latest?_ga=2.157798205.688811587.1624957483-616249991.1623083451
*/
__RAM_BASE = 0x30000000;
__RAM_SIZE = 512K;

/*--------------------- Stack / Heap Configuration ----------------------------
  <h> Stack / Heap Configuration
    <o0> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
    <o1> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
  -----------------------------------------------------------------------------*/
__STACK_SIZE = 0x0000F000;
__HEAP_SIZE  = 0x00000400;

/*
 *-------------------- <<< end of configuration section >>> -------------------
 */

/* ARMv8-M stack sealing:
   to use ARMv8-M stack sealing set __STACKSEAL_SIZE to 8 otherwise keep 0
 */
__STACKSEAL_SIZE = 0;


MEMORY
{
  FLASH (rx)  : ORIGIN = __ROM_BASE, LENGTH = __ROM_SIZE
  RAM   (rwx) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE
}

/* Linker script to place sections and symbol values. Should be used together
 * with other linker script that defines memory regions FLASH and RAM.
 * It references following symbols, which must be defined in code:
 *   Reset_Handler : Entry of reset handler
 *
 * It defines following symbols, which code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __copy_table_start__
 *   __copy_table_end__
 *   __zero_table_start__
 *   __zero_table_end__
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __StackLimit
 *   __StackTop
 *   __stack
 *   __StackSeal      (only if ARMv8-M stack sealing is used)
 */
ENTRY(Reset_Handler)

SECTIONS
{
  .text :
  {
    KEEP(*(.vectors))
    *(.text*)

    KEEP(*(.init))
    KEEP(*(.fini))

    /* .ctors */
    *crtbegin.o(.ctors)
    *crtbegin?.o(.ctors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
    *(SORT(.ctors.*))
    *(.ctors)

    /* .dtors */
    *crtbegin.o(.dtors)
    *crtbegin?.o(.dtors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
    *(SORT(.dtors.*))
    *(.dtors)

    *(.rodata*)

    KEEP(*(.eh_frame*))
  } > FLASH

  /*
   * SG veneers:
   * All SG veneers are placed in the special output section .gnu.sgstubs. Its start address
   * must be set, either with the command line option �--section-start� or in a linker script,
   * to indicate where to place these veneers in memory.
   */
/*
  .gnu.sgstubs :
  {
    . = ALIGN(32);
  } > FLASH
*/
  .ARM.extab :
  {
    *(.ARM.extab* .gnu.linkonce.armextab.*)
  } > FLASH

  __exidx_start = .;
  .ARM.exidx :
  {
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
  } > FLASH
  __exidx_end = .;

  .copy.table :
  {
    . = ALIGN(4);
    __copy_table_start__ = .;

    LONG (__etext)
    LONG (__data_start__)
    LONG ((__data_end__ - __data_start__) / 4)

    /* Add each additional data section here */
/*
    LONG (__etext2)
    LONG (__data2_start__)
    LONG ((__data2_end__ - __data2_start__) / 4)
*/
    __copy_table_end__ = .;
  } > FLASH

  .zero.table :
  {
    . = ALIGN(4);
    __zero_table_start__ = .;
    /* Add each additional bss section here */
/*
    LONG (__bss2_start__)
    LONG ((__bss2_end__ - __bss2_start__) / 4)
*/
    __zero_table_end__ = .;
  } > FLASH

  /**
   * Location counter can end up 2byte aligned with narrow Thumb code but
   * __etext is assumed by startup code to be the LMA of a section in RAM
   * which must be 4byte aligned
   */
  __etext = ALIGN (4);

  .data : AT (__etext)
  {
    __data_start__ = .;
    *(vtable)
    *(.data)
    *(.data.*)

    . = ALIGN(4);
    /* preinit data */
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP(*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);

    . = ALIGN(4);
    /* init data */
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP(*(SORT(.init_array.*)))
    KEEP(*(.init_array))
    PROVIDE_HIDDEN (__init_array_end = .);

    . = ALIGN(4);
    /* finit data */
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP(*(SORT(.fini_array.*)))
    KEEP(*(.fini_array))
    PROVIDE_HIDDEN (__fini_array_end = .);

    KEEP(*(.jcr*))
    . = ALIGN(4);
    /* All data end */
    __data_end__ = .;

  } > RAM

  /*
   * Secondary data section, optional
   *
   * Remember to add each additional data section
   * to the .copy.table above to asure proper
   * initialization during startup.
   */
/*
  __etext2 = ALIGN (4);

  .data2 : AT (__etext2)
  {
    . = ALIGN(4);
    __data2_start__ = .;
    *(.data2)
    *(.data2.*)
    . = ALIGN(4);
    __data2_end__ = .;

  } > RAM2
*/

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

  /*
   * Secondary bss section, optional
   *
   * Remember to add each additional bss section
   * to the .zero.table above to asure proper
   * initialization during startup.
   */
/*
  .bss2 :
  {
    . = ALIGN(4);
    __bss2_start__ = .;
    *(.bss2)
    *(.bss2.*)
    . = ALIGN(4);
    __bss2_end__ = .;
  } > RAM2 AT > RAM2
*/

  .heap (COPY) :
  {
    . = ALIGN(8);
    __end__ = .;
    PROVIDE(end = .);
    . = . + __HEAP_SIZE;
    . = ALIGN(8);
    __HeapLimit = .;
  } > RAM

  .stack (ORIGIN(RAM) + LENGTH(RAM) - __STACK_SIZE - __STACKSEAL_SIZE) (COPY) :
  {
    . = ALIGN(8);
    __StackLimit = .;
    . = . + __STACK_SIZE;
    . = ALIGN(8);
    __StackTop = .;
  } > RAM
  PROVIDE(__stack = __StackTop);

  /* ARMv8-M stack sealing:
     to use ARMv8-M stack sealing uncomment '.stackseal' section
   */
/*
  .stackseal (ORIGIN(RAM) + LENGTH(RAM) - __STACKSEAL_SIZE) (COPY) :
  {
    . = ALIGN(8);
    __StackSeal = .;
    . = . + 8;
    . = ALIGN(8);
  } > RAM
*/

  /* Check if data + heap + stack exceeds RAM limit */
  ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
  ASSERT(__StackTop <= ORIGIN(RAM) + LENGTH(RAM), "RAM overflowed")
}

and the Make build goal:

$(BINARY): $(OBJECTS) $(LIBS)
    @$(CXX) $^ -T $(LINKER_SCRIPT) $(LDARGS) -o $@

where the LINKER_SCRIPT points to the above linker script.

Make goal for running the image in QEMU:

run: $(BINARY)
    @$(QEMU_DIR) \
        -machine $(MACHINE_NAME) \
        -cpu $(PROCESSOR_NAME) \
        -m $(RAM_SIZE) \
        -nographic \
        -semihosting-config enable=on,target=native \
        -kernel $(BINARY)
cberk1
  • 35
  • 6
  • "Alias with row id 3" suggests that this isn't actually another 2 MB of memory, it's the same 2 MB mapped at a second location. If so then you can't populate it with different contents. – Nate Eldredge Mar 23 '23 at 13:46

1 Answers1

1

What was the mechanism you were using to load your file before that worked for small programs with the ROM_BASE setting of 0x1000_0000 ?

If you changed your linker script, and now it doesn't work, then the most likely explanation is not "now I need to use a different mechanism to load the file into QEMU" but instead "there is a problem with my change to the linker script". You don't tell us anything about what is in your linker script, so it's hard to say, but one guess is that perhaps your change has meant that the vector table (which must be a at a fixed location in memory) is no longer where it should be.

The generic loader syntax is specified in the QEMU documentation, but you probably don't need to use it. For "load a single ELF file" it will behave basically the same way as the "-kernel" option (which I suspect is what you were using before). (Your specific issues with it are because you're trying to use the syntax for "write this value I specify on the command line into memory" and for "set the PC", which are both pretty low level things you don't need to do for your use case.)

Now that you've provided your linker script, I can see that indeed it puts the vector table in the .text section (this is what the KEEP(*(.vectors)) line is doing). That means that the ROM_BASE value must be the address where the CPU looks for the vector table (which on this board is either 0x0 or the alias of that address at 0x1000_0000). If you want to put the bulk of your code into a different location, that will require more major changes to the linker script, to put the vector table into low memory and the remainder of the things in the text section into the other larger area of memory.

Peter Maydell
  • 9,707
  • 1
  • 19
  • 25
  • Hi Peter, thank you for taking the time to give some advice. I've edited the post with the linker script and the make goals which I'm using the link the image and run in QEMU. Thanks for the advice wrt loader, I'll keep poking around! – cberk1 Mar 23 '23 at 14:14
  • Thanks; I've edited the answer to talk about the linker script. The short answer is "you can't just change the ROM_BASE value in this linker script, or it won't boot" – Peter Maydell Mar 23 '23 at 16:29
  • Hi Peter, thanks for the additional info. It seems I'll have to do some more work to get this working. Thanks again for your time :) – cberk1 Mar 23 '23 at 18:18