## What do I need to implement to be able to use objects?

18 posts
So, when doing bare metal, many things are non existent a good example is the important malloc() function which you need to implement yourself since the compiler won't magically know how to allocate memory for you. I assume this is the same for new, what libraries, functions, API's.... do I need to implement before being able to do
Code: Select all
Object *object = new Object();
?
Posts: 67
Joined: Tue May 16, 2017 9:17 pm
With baremetal you "own" all the ram for the processor, you can use it at will, no reason to ask a system for more, you arent even taking it it is already taken it is yours just use it. As far as system backends to higher level languages, well you have no system so you have to figure out what needes to be added to fake the system if anything. And add it, or dont use languages that have (operating) system dependencies. Stick with Pascal and/or C (or both <grin>) for example.
Posts: 721
Joined: Sat May 26, 2012 5:32 pm
What does the compiler generate when you use "new"? Did you try it and examine the compiler output?
Posts: 721
Joined: Sat May 26, 2012 5:32 pm
Malloc is public domain there are any number of versions of it look up dmalloc and jemalloc.
Doug Lea's original versions are here
ftp://g.oswego.edu/pub/misc/malloc.c

The reason it doesn't exist is newlib the library the C compiler the Pi is using is used on many different systems.
https://en.wikipedia.org/wiki/Newlib

Like a number of embedded C systems like dietlibc and μClibc it was designed really for MMU_less embedded systems. What you generally do is use static allocation and manage memory yourself because the overhead to manage malloc is not trivial and memory fragmentation is always a problem.

The newlib system on the Pi issues an S_BRK which has a function prototype you just need to handle that function to do the allocation. It is fairly trivial look at Brian SideBotham or my Code which you have seen but neither of us deal with "free".
https://en.wikipedia.org/wiki/Sbrk

You probably also need to realize the deeper reasons under newlib that it has Reentrant and nonreentrant stubs.
http://www.embedded.com/electronics-blo ... lib-Part-2

ARM itself has a couple of versions of C/C++ and it's own performance library but it is a paid product and I think probably Greenhills and Keil will support the Pi.

If you want to try another version of C library you could try something like bionic
https://en.wikipedia.org/wiki/Bionic_(software)
The dynamic memory allocator is usually jemalloc, though it used to be dlmalloc, and still is on some memory-constrained devices; jemalloc gives much higher performance than dlmalloc, but at the cost of extra memory required for bookkeeping.
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
dwelch67 wrote:What does the compiler generate when you use "new"? Did you try it and examine the compiler output?

Pretty sure it will generate an _sbrk with the size being the the R0 register.
Last edited by LdB on Sun Jun 11, 2017 10:44 am, edited 1 time in total.
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
Using new and delete is relatively easy. You only have to implement the operators like here.
Posts: 266
Joined: Sat Apr 20, 2013 6:42 pm
Location: Germany
rst wrote:Using new and delete is relatively easy. You only have to implement the operators like here.

That won't work rst, malloc doesn't work because newlib has no _sbrk implementation so malloc won't work by default.
From circle you need all the code in
https://github.com/rsta2/circle/blob/ma ... /alloc.cpp
as well as all the memory setup stuff

If he used just that code on newlib it would call malloc which will issue an unhandled SBRK call.

Whichever way you go you need to setup the allocation functions

You are right about that is the easiest implementation and those stubs probably already exist in the c++ newlib compiler they just are not implemented at the lower level. I am pretty sure from memory the C++ compiler accepts "new" it just generates an unimplemented call.

Update: It was rather sketchy in the newlib documentation but I think it is implemented as
Code: Select all
void* operator new(size_t size) noexcept {     return malloc(size); }
Last edited by LdB on Sun Jun 11, 2017 10:53 am, edited 7 times in total.
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
This is my code for _sbrk on newlib which will allocate but never free. RPi_Heap is just a global uint32_t which I use to track heap end.
Code: Select all
.section .text._sbrk, "ax", %progbits.balign   4.globl _sbrk;         .type _sbrk, %function.syntax unified.arm_sbrk:        ldr     r2, =RPi_Heap        ldr     r1, =_end        ldr     r3, [r2]        cmp     r3, #0        moveq   r3, r1        add     r0, r3, r0        str     r0, [r2]        mov     r0, r3        bx      lr.balign   4.ltorg                              ;@ Tell assembler ltorg data for this code can go here.size   _sbrk, .-_sbrk

I define the heap at the end of linker directive file
Code: Select all
.Heap :    {     . = ALIGN(4);     _end = .; PROVIDE (end = .); /* Any memory from here is free to use so this is end of code and start of heap */    } > RAM

If you want to support free you need to track allocated blocks which is all the circle allocator is doing.
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
LdB wrote:
rst wrote:Using new and delete is relatively easy. You only have to implement the operators like here.

That won't work rst, malloc doesn't work because newlib has no _sbrk implementation so malloc won't work by default.
From circle you need all the code in
https://github.com/rsta2/circle/blob/ma ... /alloc.cpp
as well as all the memory setup stuff
I was talking about a pure bare metal setup without newlib. But yes, because this is only the C++ wrapper for new and delete you still need some memory allocator. This can be very simple _for the first tests_. Because the RPi has lots of memory, you can omit the free() functionality.
Posts: 266
Joined: Sat Apr 20, 2013 6:42 pm
Location: Germany
Yep and that is all there in circle and is a valid way to do it .. I agree with that .

He would have to be careful mixing with newlib because those functions will be defined and he will get clashes.

I tested it on arm-none-eabi-c++.exe from ARM and it does what I expected and "new" anything emits an _sbrk because of the newlib implementation.
Code: Select all
void test (void) {  int *object = new int();}

Produces output
Code: Select all
test():        mov     r0, #4        b       operator new(unsigned int)B:

You can see it has loaded register R0 with 4 for size of int and that redirects to malloc which calls an _sbrk. So it was all entirely predictable.

My stupidly simple _sbrk makes "new" automatically work just the free doesn't because it isn't implemented.
Last edited by LdB on Sun Jun 11, 2017 3:13 pm, edited 1 time in total.
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
newlib has _sbrk as well as all the other operating system connections that need to be made as part of your port, if you dont have an operating system AND you want to use newlib (which is not required) then you have to implement that backend. This is the entire point. You dont need newlib you just need to implement whatever your compiler is producing. malloc, _sbrk, memcpy, etc can be trivial to create or find others. Or dont put yourself in that position in the first place.

C++ can be used it just carries more backend baggage, or to say it another way you have to create more baggage to use it, you could use JAVA or Python as well, but also have a lot of baggage you have to create/use. This gets us back to the discussion of lower baggage languages like C and Pascal. You can then wish to use standard C library calls but many have a system component and you are back to roll your own or newlib or other c library, full circle on this discussion.

If you dont want to use Pascal you can use C and may have more baggage to carry depending on what your expectations are if you want to use C++ you have more baggage depending on what your expectations are, any other the language does this or that for you languages you have to implement that in bare metal. It starts to become a question of are you really wanting bare metal, and maybe you should be looking at an RTOS or other OS. If you want the OS experience and not the baremetal experience, then dont program baremetal...
Posts: 721
Joined: Sat May 26, 2012 5:32 pm
I am getting a flat out cant find new.

Code: Select all
pi.o: In function main':pi.cpp:(.text+0x14): undefined reference to operator new(unsigned int)'pi.cpp:(.text+0x30): undefined reference to operator new(unsigned int)'pi.cpp:(.text+0x54): undefined reference to operator new(unsigned int)'pi.o:(.ARM.exidx+0x0): undefined reference to __aeabi_unwind_cpp_pr1'

and reading more up on it that is what it is supposed to do call the functions new() and delete(). which do the alloc, so you can start by implementing those.
Posts: 721
Joined: Sat May 26, 2012 5:32 pm
Are you using gcc++ or g+ ... gcc won't know what "new" is as it's a C compiler
Last edited by LdB on Sun Jun 11, 2017 3:20 pm, edited 1 time in total.
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
But how is this different than calling malloc() in a normal C program which you dont need to do in baremetal, you can choose to do it if you want to build in that complication, but dont really need to...Sure if you try to pull in some useful library (zlib, jpeg, png, etc) you may have to implement some file and other functions, but you dont need newlib to do that, just implement them. On the other hand if you want an operating system use an operating system dont use baremetal. If you want to "understand" how all of this stuff works, then implement it yourself and see how deep and wide it goes dont go grap libraries (newlib) that add more libraries.
Posts: 721
Joined: Sat May 26, 2012 5:32 pm
The newlib is setup to deal with "new" gcc++ or G+ and redirects to malloc it is the default setup on the compiler. You are treating newlib as if it isn't setup as part of the compiler but it takes some effort to remove it. Half (probably more) of the standard C libraries call or use newlib by default on the Pi and are setup to it. Not saying you can't remove newlib but it's a whole lot more effort than fixing the simple problem. The idea of writing your own standard library files sort of defeats what standard library files are.

It's malloc that isn't setup in C on newlib which is why the problems are linked.

By simply providing _sbrk both malloc and new work.. at least on the arm versions of GCC. I don't imagine its any different on the official Pi version 4.8 or 4.9 or whatever it is. I am pretty sure malloc is implemented in the same way because I have seen people playing around redirecting printf on the official versions and they have to deal with _sbrk because it does a malloc.

Try malloc on your c compiler it should work but emit rubbish.
So look at output for this .. I have to use -O0 optimization or else it will get optimized away as object isn't used.
my abc.c file is
Code: Select all
#include <stdio.h>#include <stdlib.h>void test (void) {  int *object = (int*)malloc(4);}

I get this from this command
g:\pi\gcc_pi_6_2\bin\arm-none-eabi-gcc -Wall -O0 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -nostartfiles -specs=nosys.specs -ffunction-sections -Wl,-gc-sections -Wl,-T,rpi.ld abc.c -o kernel.elf -lc -lm -Wa,-a >list.txt
Code: Select all
  12                    .file   "abc.c"  13                    .section   .text.test,"ax",%progbits  14                    .align   2  15                    .global   test  16                    .syntax unified  17                    .arm  18                    .fpu vfp  19                    .type   test, %function  20                 test:  21                    @ args = 0, pretend = 0, frame = 8  22                    @ frame_needed = 1, uses_anonymous_args = 0  23 0000 00482DE9       push   {fp, lr}  24 0004 04B08DE2       add   fp, sp, #4  25 0008 08D04DE2       sub   sp, sp, #8  26 000c 0400A0E3       mov   r0, #4  27 0010 FEFFFFEB       bl   malloc  28 0014 0030A0E1       mov   r3, r0  29 0018 08300BE5       str   r3, [fp, #-8]  30 001c 00F020E3       nop  31 0020 04D04BE2       sub   sp, fp, #4  32                    @ sp needed  33 0024 0088BDE8       pop   {fp, pc}  34                    .size   test, .-test  35                    .ident   "GCC: (GNU Tools for ARM Embedded Processors 6-2017-q1-update) 6.3.1 20170215 (release) [AR

I use a generic linker file rpi.ld nothing special in it.
Then look at your malloc code it outputs ... that is all the problem is

Don't disagree with what you are saying it's just a whole lot of work for a very simple problem which you can solve by providing one function to the standard c files.

You have told him and if he wants to do it then fine ... for my part I think that is crazy unless you are doing something slick or special

Anyway I know what I would do, he is free to follow your advice if he thinks it is better.
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
dwelch67 wrote:But how is this different than calling malloc() in a normal C program which you dont need to do in baremetal, you can choose to do it if you want to build in that complication, but dont really need to...
The difference between new and malloc() is, that new does not only allocate some memory, but it creates an object from a class declaration and automatically calls its constructor function which ensures that the memory space is initialised as implemented by the constructor. On the opposite side delete not only frees the memory space. Prior to that it calls the destructor function of the class, which ensures that member variables which are allocated inside the class are freed as well.

This is one concept of object-oriented programming realised in C++. I personally like the concept of classes as an abstraction of data with functions belonging to it. If you do it right, it's like using Lego bricks and I think it is much easier to produce bigger software using this concept.
Posts: 266
Joined: Sat Apr 20, 2013 6:42 pm
Location: Germany
+10 to that rst

It can only be for the most basic types that new can be little more than a thin wrapper around malloc. It isn't guaranteed as there is nothing to stop a system from having two different heaps. Many implementations do combine the systems so they can run one heap and one allocation scheme for simplicity .... but don't assume it.

I guessed it might be true on the arm version of GCC because it's the easiest implementation. It makes it dead simple to get both systems to work as it has one heap to rule them all .

Code I used to check:
Code: Select all
extern "C" int kernel_main(int, int, int);#include <stdio.h>#include <stdlib.h>#include <stdint.h>         // C standard for uint8_t, uint16_t, uint32_t etc#include "rpi-smartstart.h"      // Has _sbrk definedint kernel_main (int, int, int) {   RPI_InitGraph(800, 600, 32);   int *object1 = new int;   *object1 = 7;   printf("object1: %8lX value: %i\r\n", (uint32_t)object1, *object1);   int *object2 = new int;   *object2 = 10;   printf("object2: %8lX value: %i\r\n", (uint32_t)object2, *object2);   while (1) {   }   return 0;}`

Compiled with
g:\pi\gcc_pi_6_2\bin\arm-none-eabi-g++ -Wall -O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -nostartfiles -specs=nosys.specs -ffunction-sections -Wl,-gc-sections -Wl,-T,rpi.ld abc.c smartstart.S -o kernel.elf -lc -lm
Produces expected result and GCC new is functioning correctly

I will try some class and object code tonight but pretty sure it will work seamlessly, no reason why it won't.
As I said trivial to get new to work for the c++ compiler my way .
Posts: 304
Joined: Wed Dec 07, 2016 2:29 pm
LdB wrote:It can only be for the most basic types that new can be little more than a thin wrapper around malloc.
Yes, when you do new for a built-in scalar type like int there is no constructor to be called. This is different for self defined classes. But a constructor is also not required then, you have to define one.
Posts: 266
Joined: Sat Apr 20, 2013 6:42 pm
Location: Germany