Setup
Keil: uVision 5.17.0.0 ARM: LPC1768 A standard project with some UARTS and I2C "Use memory Layout form target" is checked (using the standard scatter file) "Use MicroLIB" is not checked
from startup_LPC17xx.s Heap_Size EQU 0x00002000
AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit
Problem Any call to malloc returns NULL, that is, the system can not allocate memory.
Why does not the mallco work, is my only options to 1) Use microLIB 2) Implement _init_alloc() and __rt_heap_extend() on my own
?
Regards Stefan
That really seems like the way you would allocate a heap in previous versions, but not in any recent.
See if this helps.
www.keil.com/.../ARMLINK_pge1362065977713.htm
i.e make an ARM_LIB_HEAP region and it will be initialized for use before main is called.
Third field in scatter file region description is the length of the region, not the end address.
RW_IRAM1 0x20000000 0x00040000 { ; RW data 256KB .ANY (+RW +ZI) }
Or if you want to go the path you are, you can implement the __user_initial_stackheap function yourself.
; User Initial Stack & Heap IF :DEF:__MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, = Heap_Mem LDR R1, =(Stack_Mem + Stack_Size) LDR R2, = (Heap_Mem + Heap_Size) LDR R3, = Stack_Mem BX LR
To Per Westermark
Accoring the searching I did before I made post it said that a main like this, "int main(int argc, char **argv)" require a initiation of the heap. That is, the function __user_initial_stackheap must/will be called unlike if "use microlib" is checked, then the main must look like this "void main(void)"
To Westonsupermare Pier
I have changed it, but it wont call the __user_initial_stackheap before calling main
To Robert McNamara
ARM_LIB_HEAP: Yes, i have tested it, it did not make any difference, it still wont call __user_initial_stackheap before main(...) __user_initial_stackheap is included, I used "new project" option the keil and it automatically produced that code, but it is not calle
To All Becosue "new project" produced startup_LPC17xx.s, with the stack, heap reserved and __user_initial_stackheap created (without changing the scatter file) I assumed everything should be working, but it seems not to be.
Any chance you are using C++?
No, it is pure C, one single main function I even recreated an empty project ("use microlib" unchecked) to test it again. When i step into the malloc function i can see that the malloc function consider the heap to be full and it wont call the __user_initial_stackheap In the map file the following exists so something is going on __user_initial_stackheap 0x000002cd Thumb Code 0 startup_lpc17xx.o(.text) __use_two_region_memory 0x000002f1 Thumb Code 2 heapauxi.o(.text) __rt_heap_escrow 0x000002f3 Thumb Code 2 heapauxi.o(.text) __rt_heap_expand 0x000002f5 Thumb Code 2 heapauxi.o(.text) __use_no_heap 0x000002f7 Thumb Code 2 hguard.o(.text) Is it possibly to get the source code to the c libraries that the linker automatically links in so i can compare it to the executed assembler instructions in "malloc" ? and that coudl help me to understand why it is not calling the __user_initial_stackheap Regards
What size is your heap?
How much are you trying to allocate in your main?
If you make the heap 0 in size, does it crash before main?
If you make the heap larger does it eventually work?
When you set your breakpoint in __user_initial_stackheap, make sure you also right click and then left click "show disassembly at ###" Then see if it stops in your __user_inital_stackheap.
startup_LPC17xx.s
Stack_Size EQU 0x00000800 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp ; <h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h> Heap_Size EQU 0x00000800 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit
Scatter file
LR_IROM1 0x00000000 0x00080000 { ; load region size_region ER_IROM1 0x00000000 0x00080000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM_STACK 0x10000000 0x2000 { ; RW data .ANY (STACK) } LO_ARM_TEST 0x10002000 UNINIT 0x20 { ; RW data .ANY (__LO_ARM_TEST_RAM_AREA) } RW_IRAM1 0x10002020 0x3FE0 { ; RW data .ANY (+RW +ZI) } RW_IRAM_HEAP 0x10006000 0x2000 { ; RW data .ANY (HEAP) } }
yes, i have the assembler view up as awell, i have a break point at __user_inital_stackheap and the main function The execution do
LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0
And then it will hit the main function without hitting __user_inital_stackheap. If i do v = malloc(0x10); it will return NULL but if i have called _init_alloc((uintptr_t)&__heap_base, (uintptr_t)&__heap_limit); before the malloc it will not return a NULL pointer and the pointer is in "right area"
"use microlib" is unchecked, my main function look like this
int main(int argc, char **argv)
I have not tested to set "If you make the heap 0 in size, does it crash before main?"
heap test of 0?
Try removing __user_initialize_stackheap and the EXPORT of it from the startup file. Do you get a link error?
Test1
Heap_Size EQU 0x00000000 Result: No compilation error It hangs inside _init_alloc call in the main function
Test2
Heap_Size EQU 0x00000000 ; EXPORT __user_initial_stackheap // _init_alloc((uintptr_t)&__heap_base, (uintptr_t)&__heap_limit); Result: No compilation error It never reach the main() function
Test3:
Heap_Size EQU 0x00000800 ; EXPORT __user_initial_stackheap // _init_alloc((uintptr_t)&__heap_base, (uintptr_t)&__heap_limit); Result: No compilation error It reach the main() function and malloc returns a valid pointer (that is, it points into the right area)
wtf, why would it work corretly with "standard" code comment out?
Regards
"That really seems like the way you would allocate a heap in previous versions, but not in any recent."
So maybe you got two separate functions that tries to initialize the heap.
I am using Keil: uVision 5.17.0.0 But ARM says that __user_initial_stackheap is a legacy thingy infocenter.arm.com/.../index.jsp and the, nowdays, the proper function should be __user_setup_stackheap() I gues the Keil runtime library get messaed up if it see the __user_initial_stackheap() and does not initilize heap (internally) nor does it call __user_initial_stackheap.
It seems that the only way is to hide __user_initial_stackheap from Keil so it does not see it and assume it have to initilize the heap itself. Note, if __user_initial_stackheap is comment out, it is neccessary as well to do the following at the begining of startup_LPC17xx.s
EXPORT __heap_base EXPORT __heap_limit
1) Stop making any calls to _init_alloc.
2) You should be getting a link error on TEST2, no compile error. See #4 below. This would only seem to be if you exported __heap_limit, __heap_base and __initial_sp. If you export these, it will compile and link fine using both microlib and standardlib, but will not actually work in standardlib mode. Did you somehow change the standard code in a way that these variables were exported when not using the microlib? You may notice in the map file that sys_stackheap_outer.o references a __user_inital_stackheap that resides in sys_stackheap_outer.o when these are exported. If you do not export these variables, it will reference the __user_initial_stackheap in YOUR startup.s file. This is why your __user_initial_stackheap is not being called. i.e. You are not using the "standard" code at all, you have modified it to not work and then complained about it not working.
3) Look at your map file
USING:__user_setup_stackheap, your map file should show __rtentry4.o referencing __user_setup_stackheap in your startup.s file. There is no reference to __user_initial_stackheap.
USING: __user_initial_stackheap your mapfile should show sys_stackheap_outer.o referenceing __user_initial_stackheap in your startup.s file. You will also see __user_setup_stackheap referenceing sys_stackheap_outer.o
4) I have never seen __heap_limit defined as a variable when not using the microlib. I think the "standard" startup code is very specific in NOT creating a __heap_limit or __heap_base variable when not using the microlib. Don't define this as a variable.
It works now as expected, it was all related to one of all of the added lines at the top of startup_LPC..
EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit
If any of them, even if the program only have "EXPORT __initial_sp" (and not the other two) the heap dont get initalized. If none of them are exported then the runtime library will call __user_initial_stackheap as expected.
It is neccessary to export a new/seperate variabel if the stack top is going to be used somewhere else, like this
EXPORT __initial_stack_top Stack_Size EQU 0x00000800 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp __initial_stack_top
One interesting observation was that if the program had
EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit The map file will contain __rtentry4.o(.ARM.Collect$$rtentry$$00000004) refers to sys_stackheap_outer.o(.text) for __user_setup_stackheap sys_stackheap_outer.o(.text) refers to startup_lpc17xx.o(.text) for __user_initial_stackheap But they was never called
Conclusive: Dont export any of __initial_sp, __heap_base or __heap_limit form the startup_XXXX.s