C++ memory allocation with 'new' aborts to an infinite loop

Hello,

The short story

The problem is that attempts to allocate memory with `new` lead to an infinite loop somewhere in the C++ library.

I did read a few other posts about changing the heap size, so I changed it from the default 1024 to 8248. I also saw this post to use whatever RAM is left as the heap and I have edited the .xml file in my project accordingly: link . Both edits were tested independently of each other and did not yield expected results.

Now I was starting to think I was doing something silly, but this simple test added early in my `main()` function also led to the same problem.

int *i = new int();
*i = 5;
std::printf("We never get here because attempting to allocate with 'new' aborts to an inifinite loop\n");

The longer story

Some brief info about my setup.

* SDK: nRF5 12.3.0

* Device: nRF51822

* IDE: Segger Embedded Studio for ARM. Release 7.20 Build 2023050901.53220

* No exception support enabled in the libray.

I started out with the 'ble_app_template' for pca10028, reconfigured it for pca10026, and then added my application atop. The BLE initialization and softdevice-related code remained mostly unchanged. It was only reorganised to a separate C file and moved out of my `main.cpp`. I am confident that my application logic does what I expect it to do.

Up until this problem with `new`, other parts of my application work as expected. Somewhere else in my application, I am able to `std::malloc()` and `std::free()` some memory within the same scope i.e. curly braces. For more context, my application is reading data via UART from a sensor and writes to a display using SPI. Note that I am not trying to debug the logic of my application, but I am trying to fix the unexpected behaviouR when using the C++ `new` keyword. My expectation is that `new` returns a pointer which I can use and later `delete`.

Now I strongly suspect this is something with the C++ library because the softdevice initialises successfuly and begins advertising as a beacon. I am also able to see the beacon with nRF Connect Android application. It is only that any attempt to allocate memory with `new` in my application leads to the problem I have described in this post. I don't believe my application is running out of memory (heap) because even that simple 3 line test above also fails. So while the softdevice continues execution as normal, my application is stuck in a infinite loop.

My diagnosis and suspicions

I will only show below the parts I believe are relevant to the explanation for now. Happy to link you to more on request.

Please consider the C++ code below and the assembly that follows. In the function `get_new_handler` in the assembly, you will notice that the content of whatever is at address 0x200027F4 ends up in register R0. Using my debugger, it turns out that content at that address is zero. Because of this, we end up getting to the `abort` label which is literally a jump (branch) to itself.

I also read this which may or may not be related.

#include <cassert>
#include <cstdio>
#include <new>

static page_info_t page_info = { nullptr, VIEW_1_ACCELERATION_ONLY };

void view_init (void) {
  page_info.current_view_object = new(std::nothrow) User_View_1();
  assert(page_info.current_view_object != nullptr);
}

<operator new(unsigned int)>
    B510        push {r4, lr}               ; PC=0x00025E80
    1E04        subs r4, r0, #0
    D100        bne 0x00025E88
    3401        adds r4, #1
    0020        movs r0, r4
    F7FFFF89    bl 0x00025DA0 <malloc>
    2800        cmp r0, #0
    D109        bne 0x00025EA6
    F000F811    bl 0x00025EB8 <std::get_new_handler()>
    2800        cmp r0, #0                  ; PC=0x00025E96
    D006        beq 0x00025EA8
    4780        blx r0
    0020        movs r0, r4
    F7FFFF7F    bl 0x00025DA0 <malloc>
    2800        cmp r0, #0
    D0F5        beq 0x00025E92
    BD10        pop {r4, pc}                ; PC=0x00025EA6
    F7F8FB32    bl 0x0001E510 <abort>       ; PC=0x00025EA8
    E7EC        b 0x00025E88                ; PC=0x00025EAC
    46C0        nop                         ; PC=0x00025EAE
<operator new(unsigned int, std::nothrow_t const&)>
    B510        push {r4, lr}
    F7FFFFE5    bl 0x00025E80 <operator new(unsigned int)>
    BD10        pop {r4, pc}
<std::get_new_handler()>
    4B01        ldr r3, =0x20002714 <__cxa_new_handler>
    6818        ldr r0, [r3]
    4770        bx lr
    46C0        nop                     ; PC=0x00025EBE
    20002714    .word 0x20002714            

I hope to have a technical discussion about how to fix this. Happy it would contribute to the forum too. I'm open to corrections and my assumptions being updated.

Best.

  • PS:

    1) When I say the `abort` label is a jump to itself, I mean it is the infinite loop. Say the `abort` label is at PC=0x0001234, then the instruction at 0x00001234 is `b 0x00001234`.

    2) The entire call stack (from `view_init()`) is not shown because I think it is noise. I only showed the assembly that was created from using the `new` keyword in the C++ code above.

    3) I have a balanced library optimisation in my project settings.

  • Hi,

    I did read a few other posts about changing the heap size, so I changed it from the default 1024 to 8248.

    The nRF51 series, with 16 or 32 kB of RAM, have limited memory for a heap. Both the SoftDevice (at least 4.95 kB for the S130, total size depending on configuration) and the application (including application stack) requires RAM. Depending on which nRF51 SoC you use, SoftDevice configuration and application, you may or may not have 8 kB available for a heap.

    The problem is that attempts to allocate memory with `new` lead to an infinite loop somewhere in the C++ library.

    There were some changes to the C libraries in Segger Embedded Studio version 6.2. While I do not remember the details, it did affect nRF devices. It may be worth trying with an older version of SES.

    Please note that the PCA10026 is very old at this stage, and has not been listed under supported boards in the SDK for a long time. There may be other (board related, or SoC revision related) reasons why some things do not work.

    Regards,
    Terje

  • Thank you for the comment and history.

    Out of a total 16.0kB, the memory view in SES showed I had about 9.8kB of RAM to spare.
    Your comment compelled me to enable exception support and I infact noted a `std::bad_alloc` exception generated, much to my surprise.

    I didn't realise how much memory is needed to `new` a C++ object, even a siingle integer on the heap. I assumed the remaining 9.8kB would have been more than sufficient.

    I have modified my code to use more static allocation and shunned all dynamic C++ allocations. I have been able to progress after this.

    Thank you very much for your support.
    Best.

Related