0
\$\begingroup\$

I have created a simple C++ project using Studio 7.0.1006. Then using "Add Class", I added a simple class called CGPIO.

To this class I added a public method called Initialize. The code compiles and runs until I add the following code to main.cpp to instantiate a CGPIO object: CGPIO* pGPIO = new CGPIO().

Compiling that program results in an "undefined reference to '_sbrk'" error. A more involved program generated undefined reference to several other routine (e.g. _exit, _close, _read, etc.).

I have tried playing with linker settings - specifically Do not use standard files and Do not use default libraries. Selecting Do not use default libraries, the _sbrk error goes away, but a undefined reference to __libc_init_array shows up.

I could not find much information on C++ programs for the SAMDxx series. Is there an good working example to start from? Do I need to add a different library to the Linker settings?

\$\endgroup\$
1
  • \$\begingroup\$ Does malloc work? \$\endgroup\$ Jul 7, 2016 at 8:59

3 Answers 3

1
\$\begingroup\$

You mention Atmel's SMART ARM-based Microcontroller series D. Since these errors are appearing at compilation, the issue is unlikely to be limited to the series. I always search first for the error rather than for examples for a particular microcontroller. Manufacturer sample projects are better as a jumping-off point than an explanation of a problem, and there may not be much posted by others.

So, focusing on the error - a search finds the same errors on a different model, in a different environment. Also here. System calls (_sbrk) that would be defined in a Unix system may not be defined on a microcontroller, so yes, you need to add the definitions. Atmel has listed possibilities (#10): the rdimon and nosys libraries. Getting nosys into the project appears to solve the problem.

\$\endgroup\$
0
\$\begingroup\$

The new operator of C++ is based on dynamic memory allocation, for example via malloc (alternative implementations that use sbrk directly are also possible). The standard C library integrates a malloc implementation, but does not know, which memory to use. The sbrk function is used by the standard C library to acquire some memory from the underlying system. In a hosted environment the OS provides this function. But on a freestanding environment this function has to be defined by you. After all, you're the only one who knows what the memory layout should be.

Depending on the standard C library that you use, there are different options. I'm guessing that you're using newlib (very popular for embedded systems). In this case you could provide an alternative malloc implementation instead of providing the sbrk function and using the implementation of newlib. Following code should more or less work. This provides the reentrant version of malloc. From the top of my head, I'm not sure how the non-reentrant function definition or sbrk needs to look like.

#define HEAP_SIZE    512

uint8_t heap[HEAP_SIZE];
size_t heap_idx = 0;

// Not sure what this has to be called. Try some of the following:
// void * sbrk(int size) {
// void * _sbrk(int size) {
void * _malloc_r(struct _reent * re, size_t size) {
   void * ret = NULL;

   if (heap_idx + size < HEAP_SIZE)
   {
      ret = &heap[heap_idx];
      heap_idx += size;
   }

   return ret;
}

There is also example code in the newlib manual that uses all RAM thats not statically used as heap. Also there are other syscalls (exit, close, read...) that might be useful to implement. See chapter 12 'syscalls' in the newlib manual. That should also take care of the other undefined symbols. If you can't provide these syscalls (e.g. you don't have a filesystem), you can't use certain functions. For example fopen needs some syscalls like open, close, fstat...

For a fully functioning system, you probably also want to provide implementations for free, calloc and realloc. Also I have to add the boilerplate warning: dynamic allocation in embedded systems is incredibly dangerous, if you don't know exactly what you're doing, you should probably stick to static allocations. That's also possible with C++. For example, you could instantiate your class like this:

CGPIO pGPIO();

In this case the object is allocated on the stack and will be destroyed as soon as the variable goes out of scope. Little bonus: if you don't use new, you probably don't have to add a sbrk or malloc implementation.

\$\endgroup\$
2
  • \$\begingroup\$ "The new operator of C++ is based on dynamic memory allocation via malloc" I don't think that's a valid statement. new is independent of malloc. \$\endgroup\$
    – m.Alin
    Jul 7, 2016 at 9:25
  • \$\begingroup\$ You're right. new does not have to use malloc, but in many cases it does. \$\endgroup\$
    – erebos
    Jul 7, 2016 at 9:34
-1
\$\begingroup\$

The C language's malloc function and the C++ language's new operator both require a memory manager that can perform dynamic memory allocations at runtime. The memory manager is typically a combination of special hardware (the memory management unit, MMU) and special software executing (typically) within an operating system like Linux, Windows, MacOS, and so on. Without a memory manager, you cannot use C's malloc function nor C++'s new operator in your C/C++ programs.

Note that software written for embedded systems typically runs "right on the iron"—i.e., there is no operating system that (a) manages the hardware, and (b) "hosts" application-layer software written in C++. Consequently, when using C++ to create software for an unhosted embedded system environment, various C++ language features like the 'new' operator, virtual inheritance, etc., are typically unusable on those platforms. In fact, so many C++ language elements must be abandoned that you often end up with a program that is more like a C program than a C++ program.

\$\endgroup\$
2
  • \$\begingroup\$ This started out with a good explanation, but then went wrong in its conclusions. Systems that want to do these things merely need to implement these functions. In the simple case it is only a few lines of code, because allocating memory is easy. It's when you want to reuse memory that has been freed that things get difficult, but MMU-less systems do generally try to avoid the non-trivial cases of that. \$\endgroup\$ Jul 7, 2016 at 5:06
  • \$\begingroup\$ Good luck using the standard C++ library on an 8-bit micro using a home brew MMU with limited RAM. \$\endgroup\$ Jul 7, 2016 at 6:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.