Difference between revisions of "C runtime library"

From SEGGER Wiki
Jump to: navigation, search
m
m
(15 intermediate revisions by 3 users not shown)
Line 5: Line 5:
 
* Compiler-specific auxiliary functions
 
* Compiler-specific auxiliary functions
 
It goes hand in hand with the startup code, which contains the first instructions to be executed when program execution starts.
 
It goes hand in hand with the startup code, which contains the first instructions to be executed when program execution starts.
A C runtime library is required equally for Embedded Systems, where the entire programs is typically monolithic, so one single
+
A C runtime library is required equally for [[Embedded Systems]], where the entire program is typically monolithic, so one single
 
executable, as well as for larger systems such as Windows, Linux, MacOS, where the application runs on top of an Operating Systems
 
executable, as well as for larger systems such as Windows, Linux, MacOS, where the application runs on top of an Operating Systems
 
and typically also in virtual memory space.
 
and typically also in virtual memory space.
Line 12: Line 12:
 
The functions available in the C standard library are defined in the C-standard.
 
The functions available in the C standard library are defined in the C-standard.
 
Examples are:
 
Examples are:
  +
* memory and string functions, e.g. <code>strcpy</code>, <code>memcpy</code>, <code>memset</code>, <code>strcmp</code>.
* strcpy
 
  +
* formatted I/O functions, e.g. <code>printf</code>, <code>snprintf</code>, <code>scanf</code>, <code>sscanf</code>.
* memcpy
 
  +
* heap functions, e.g. <code>alloc</code>, <code>free</code>, <code>realloc</code>.
* memset
 
  +
* strcmp
 
* printf
 
* snprintf
 
* heap functions such as malloc, free, realloc
 
 
===Compiler-specific auxiliary functions===
 
===Compiler-specific auxiliary functions===
 
The compiler specific auxiliary functions are highly dependent on compiler and architecture. They typically supply functionality
 
The compiler specific auxiliary functions are highly dependent on compiler and architecture. They typically supply functionality
required by the C-standard, which can not easily be implemented on the given CPU.
+
required by the C standard, which cannot easily be implemented using single instructions on the target CPU.
Typical things are arithmetic routines. On a CPU which does not have a divide instruction, a divide routine has to be provided
+
Typical things are arithmetic routines. On a CPU which does not offer a divide instruction, a divide routine must be provided
in the library, so that the code required to perform this division does not need to be inserted everywhere where division is required,
+
in the library, so that the code required to perform this division does not need to be inserted inline where division is required,
 
as the division code might be quite large.
 
as the division code might be quite large.
Even where a processor has built-in multiplication and division instructions, they are often of powerful enough.
+
Even where a processor has built-in multiplication and division instructions, they are often not powerful enough.
Modern Compilers implement 64-bit variables, and the 64-bit arithmetic can not be easily performed by the processor in a single instruction.
+
Modern Compilers implement 64-bit variables, and the 64-bit arithmetic cannot easily be performed by the processor in a single instruction.
Auxiliary functions for 64-bit arithmetic, especially multiplication and division are required. These functions are actually used by a
+
Auxiliary functions for 64-bit arithmetic, especially multiplication and division, are required. These functions are actually used by cryptography algorithms, and their performance is important to the system performance of these algorithms.
  +
crypto algorithms, and their performance is important to the system performance of these algorithms.
 
 
==Implementation==
 
==Implementation==
 
A C runtime library can be implemented entirely in C (as high level language), but good implementations usually have various routines
 
A C runtime library can be implemented entirely in C (as high level language), but good implementations usually have various routines
 
implemented in Assembly language (ASM) for performance reasons.
 
implemented in Assembly language (ASM) for performance reasons.
Thechnically, other languages can be used (such as C++), but typically are not.
+
Technically, other languages can be used (such as C++), but typically are not.
  +
 
==Performance==
 
==Performance==
 
The performance of the application program does depend a lot on the C runtime library. Functions such as memcpy are used a lot by application programs,
 
The performance of the application program does depend a lot on the C runtime library. Functions such as memcpy are used a lot by application programs,
Line 38: Line 36:
 
Other functions, such as arithmetic functions, may also have a significant effect on the performance.
 
Other functions, such as arithmetic functions, may also have a significant effect on the performance.
 
Different implementations can have vastly different performance, with a difference of factor 10 or more.
 
Different implementations can have vastly different performance, with a difference of factor 10 or more.
A very simple memcpy implementation would copy bytes in a loop, one byte at a time, and be written in C, looking as below:
+
A very simple memcpy implementation would copy bytes in a loop, one byte at a time, and be written in C, as below:
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
   
Line 50: Line 48:
 
while (NumBytes > 0) {
 
while (NumBytes > 0) {
 
*pDest++ = *pSrc++;
 
*pDest++ = *pSrc++;
  +
--NumBytes;
 
}
 
}
 
return dest; // Actually a waste of time to return the original pointer but it seems that is what the C-standard demands
 
return dest; // Actually a waste of time to return the original pointer but it seems that is what the C-standard demands
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
A good implementation is done in assembly language, will load and store words (so typically 32-bits on a 32 bit processor) as much as possible,
+
A good implementation is done in assembly language, will load and store as many words as possible words (so typically 32 bits on a 32-bit processor), and use special instructions where possible and available, such a multi-word read and write. The resulting difference in performance can be huge, and since a lot of applications use memcpy heavily, the difference in performance can be equally huge.
  +
and in addition to that use any special instructions, such a multi-word read and write where possible and available.
 
The resulting difference in performance can be huge, and since a lot of applications use memcpy heavily, the difference in performance can be equally huge.
 
 
==Size of the C runtime library==
 
==Size of the C runtime library==
 
The size of the C runtime library is basically irrelevant. What is important is how much of it gets linked into the application.
 
The size of the C runtime library is basically irrelevant. What is important is how much of it gets linked into the application.
 
And this is where good and not so good implementations vary. A good implementation will add minimum space (for small applications,
 
And this is where good and not so good implementations vary. A good implementation will add minimum space (for small applications,
that can and should be no more than a few hundred bytes), where other implementation will not allow building applications below 20KB.
+
that can and should be no more than a few hundred bytes), where other implementations will not allow building applications below 20KB.
   
 
==Granularity==
 
==Granularity==
Ideally, the C runtime library has very fine granularity. This means that that only the functions actually referenced by the application are linked into
+
Ideally, the C runtime library has very fine granularity. This means that only the functions actually referenced by the application are linked into
 
the application.
 
the application.
  +
 
==Use of heap==
 
==Use of heap==
Good runtime libraries do not require a heap. This is important especially in Embedded Systems, where a lot of times the application program does not use the
+
Good runtime libraries do not require a heap. This is especially important in Embedded Systems, where a lot of times the application program does not use the
heap. If the runtime library needs the heap, than the heap is required just for the run time library. This involves linking in the additional code for the heap management of the run time
+
heap. If the runtime library needs the heap, then the heap is required just for the run time library. This involves linking in the additional code for the heap management of the run time
 
library, as well as setting aside some RAM to be used as heap.
 
library, as well as setting aside some RAM to be used as heap.
  +
 
==Implementations of the C runtime library==
 
==Implementations of the C runtime library==
There are lots of implementations available. Basically, every compiler comes with its own implementation, as it is not really fully usable without.
+
There are lots of implementations available. Basically, every compiler comes with its own implementation, as it is not really fully usable without it.
For GCC, various free ones are available, such as glibc, newlib and newlib-nano.
+
For [[GCC]], various free ones are available, such as glibc, newlib and newlib-nano.
 
There is also a commercial version available, with much better performance and smaller memory footprint:
 
There is also a commercial version available, with much better performance and smaller memory footprint:
 
https://wiki.segger.com/SEGGER_RunTime_Library
 
https://wiki.segger.com/SEGGER_RunTime_Library
 
For detailed information visit the [https://www.segger.com/products/development-tools/runtime-library/ SEGGER Runtime Library product page on segger.com].
 
For detailed information visit the [https://www.segger.com/products/development-tools/runtime-library/ SEGGER Runtime Library product page on segger.com].
  +
  +
== Example standardised library ==
  +
  +
Arm have documented the APIs required [https://developer.arm.com/docs/ihi0043/latest?_ga=2.224160987.1506853196.1533541889-405231439.1528186050] for a standard-conforming compiler to interoperate with other EABI-compliant compilers. This enables linking of libraries that are compiled with compiler A to be used by compiler and linker B '''if both A and B conform to the same EABI.'''
  +
  +
There are many functions in the ABI, such as floating-point emulation for processors that lack a floating-point unit, or 64-bit multiplication and division for compilers that do not expand these operations inline.
  +
  +
A language processor's runtime system must provide implementations of these functions even if the compiler never calls them. This enables, for instance, a library written for a Cortex-M3 device (that uses calls to the floating point helpers) to be linked with an application written for a Cortex-M4 device (with floating point unit and no calls to helpers). There will be a small penalty for using floating-point helpers in the Cortex-M3 code rather than floating-point instructions, but the code continues to work.

Revision as of 10:35, 4 November 2019

The C runtime library is a collection of subroutines that are needed to build a program in C. The subroutines can basically be put into 2 categories:

  • C standard library
  • Compiler-specific auxiliary functions

It goes hand in hand with the startup code, which contains the first instructions to be executed when program execution starts. A C runtime library is required equally for Embedded Systems, where the entire program is typically monolithic, so one single executable, as well as for larger systems such as Windows, Linux, MacOS, where the application runs on top of an Operating Systems and typically also in virtual memory space.

Content of C runtime library

C standard library

The functions available in the C standard library are defined in the C-standard. Examples are:

  • memory and string functions, e.g. strcpy, memcpy, memset, strcmp.
  • formatted I/O functions, e.g. printf, snprintf, scanf, sscanf.
  • heap functions, e.g. alloc, free, realloc.

Compiler-specific auxiliary functions

The compiler specific auxiliary functions are highly dependent on compiler and architecture. They typically supply functionality required by the C standard, which cannot easily be implemented using single instructions on the target CPU. Typical things are arithmetic routines. On a CPU which does not offer a divide instruction, a divide routine must be provided in the library, so that the code required to perform this division does not need to be inserted inline where division is required, as the division code might be quite large. Even where a processor has built-in multiplication and division instructions, they are often not powerful enough. Modern Compilers implement 64-bit variables, and the 64-bit arithmetic cannot easily be performed by the processor in a single instruction. Auxiliary functions for 64-bit arithmetic, especially multiplication and division, are required. These functions are actually used by cryptography algorithms, and their performance is important to the system performance of these algorithms.

Implementation

A C runtime library can be implemented entirely in C (as high level language), but good implementations usually have various routines implemented in Assembly language (ASM) for performance reasons. Technically, other languages can be used (such as C++), but typically are not.

Performance

The performance of the application program does depend a lot on the C runtime library. Functions such as memcpy are used a lot by application programs, either directly (visible to the programmers, the application program directly calling memcpy) or even indirectly (by code generated by the compiler, for example to copy a structure). Other functions, such as arithmetic functions, may also have a significant effect on the performance. Different implementations can have vastly different performance, with a difference of factor 10 or more. A very simple memcpy implementation would copy bytes in a loop, one byte at a time, and be written in C, as below:

void *memcpy( void *dest, const void *src, size_t NumBytes) {
  char *pDest;
  char *pSrc;

  pDest = (char*)dest;  // Copy to byte size pointers to make the loop look prettier. Note that most compilers optimize things away anyhow
  pSrc  = (char*)src;

  while (NumBytes > 0) {
    *pDest++ = *pSrc++;
    --NumBytes;
  }
  return dest;          // Actually a waste of time to return the original pointer but it seems that is what the C-standard demands
}

A good implementation is done in assembly language, will load and store as many words as possible words (so typically 32 bits on a 32-bit processor), and use special instructions where possible and available, such a multi-word read and write. The resulting difference in performance can be huge, and since a lot of applications use memcpy heavily, the difference in performance can be equally huge.

Size of the C runtime library

The size of the C runtime library is basically irrelevant. What is important is how much of it gets linked into the application. And this is where good and not so good implementations vary. A good implementation will add minimum space (for small applications, that can and should be no more than a few hundred bytes), where other implementations will not allow building applications below 20KB.

Granularity

Ideally, the C runtime library has very fine granularity. This means that only the functions actually referenced by the application are linked into the application.

Use of heap

Good runtime libraries do not require a heap. This is especially important in Embedded Systems, where a lot of times the application program does not use the heap. If the runtime library needs the heap, then the heap is required just for the run time library. This involves linking in the additional code for the heap management of the run time library, as well as setting aside some RAM to be used as heap.

Implementations of the C runtime library

There are lots of implementations available. Basically, every compiler comes with its own implementation, as it is not really fully usable without it. For GCC, various free ones are available, such as glibc, newlib and newlib-nano. There is also a commercial version available, with much better performance and smaller memory footprint: https://wiki.segger.com/SEGGER_RunTime_Library For detailed information visit the SEGGER Runtime Library product page on segger.com.

Example standardised library

Arm have documented the APIs required [1] for a standard-conforming compiler to interoperate with other EABI-compliant compilers. This enables linking of libraries that are compiled with compiler A to be used by compiler and linker B if both A and B conform to the same EABI.

There are many functions in the ABI, such as floating-point emulation for processors that lack a floating-point unit, or 64-bit multiplication and division for compilers that do not expand these operations inline.

A language processor's runtime system must provide implementations of these functions even if the compiler never calls them. This enables, for instance, a library written for a Cortex-M3 device (that uses calls to the floating point helpers) to be linked with an application written for a Cortex-M4 device (with floating point unit and no calls to helpers). There will be a small penalty for using floating-point helpers in the Cortex-M3 code rather than floating-point instructions, but the code continues to work.