
Embedding with GNU: Newlib Bill Gatliff 12/28/2001 10:36 AM EST Embedding with GNU: Newlib newlib is often the most appropriate choice for a C runtime library in an embedded system. Read on to find out why. In previous articles in this series, I discussed how to use the GNU compiler and linker (February 2000) and the GNU debugger (September 1999), for embedded development. In this installment, I will introduce newlib, a C runtime library that is often an appropriate choice for embedded software built using GNU tools. Technically, the name newlib refers to a collection of source code assembled by Cygnus Solutions (now Red Hat) from several different origins. This point is interesting only for heritage and licensing purposes (the latter we will discuss momentarily), because the code itself is released, managed, and used as a single functional unit, just as you would expect from any collection of C code distributed under a single name and version number. Newlib is one of several options for small, GNU-friendly C runtime environments (see sidebar "Other Embedded C Runtimes"). It is unique, however, in that it is a mature and well-supported product with an active developer and user base. And its architecture addresses the needs of deeply embedded systems quite well. Other Embedded C Runtime Libraries Clibc (cvs.uclinux.org/) This is the C runtime library behind CLinux, a derivative of the Linux kernel that runs on CPUs without MMUs. This library has a long and convoluted history, with its origins in a version of the GNU C library that predates Linux itself. Clibc is a surprisingly small yet effective library-one can produce a 4KB "Hello, world!" application with CLibc, where the latest full GNU libc requires over 200KB to accomplish the same thing (although, honestly, this isn't exactly an apples-to-apples comparison). In addition, CLibc supports POSIX threads, a useful feature that newlib currently lacks. On the downside, CLibc is still very much under development, and it does not support most of targets currently supported by newlib. In fact, as of December 1, 2000, Clibc only supported x86-the m68k and arm targets were broken (external-lists.valinux.com/archives//sglibc/2000- December/000017.html). Clibc is also a library very much dedicated to a CLinux runtime environment, which makes it difficult to apply to other systems. sglibc (http://external-lists.valinux.com/lists/listinfo/sglibc) sglibc is a very recent attempt to reduce the size of the GNU glibc system. In fact, this project is so new, they don't even have a web page established yet. sglibc attacks the code bloat in glibc by using patches to discard major sections of unneeded or expensive functionality in the glibc source tree. There is considerable discussion underway on the mailing lists as to whether this is a reasonable direction or not, whether the results will be of any use to anyone other than projects needing a thrifty glibc (single- floppy Linux distributions, mostly), and whether the project's principal maintainer even wants to support embedded systems at all! Assuming they can work all of their differences out, sglibc could be the next big thing for embedded Linux and BSD systems, because the GNU C library is the largest single component in today's embedded Linux systems-even larger than the kernel itself. Any gains made here will make Linux increasingly attractive in small, resource-constrained devices. Licensing Because newlib is a collection of source code, it is distributed under the terms of several different licenses. All of the licensing is either public domain or BSD-like, which means that even proprietary applications can adopt newlib because its use does not require distribution of the end work's source code. For convenience, all of newlib's licenses are gathered up into the file COPYING.NEWLIB, which is included with the source code. Features Some of the library's features are useful enhancements to a "typical" embedded setting (whatever that is), while others allow newlib to be about as POSIX-like as a compact C runtime setting can be. A POSIX-like API can be a big help when porting applications to embedded hardware. printf vs. iprintf Newlib contains a complete implementation of the standard printf() and family. By the implementation's own admission, "this code is large and complicated" (vfprintf.c:144), but it is essential for systems that need full ANSI C input and output support, including capabilities for representing and parsing floating-point numbers. Many embedded systems do not use floating-point math, however, and great pains are taken in most embedded runtime libraries to cull this code-bloating functionality whenever possible. Newlib actually approaches this problem in two ways: A FLOATING_POINT macro that allows you to selectively disable floating-point support in each of the stdio library functions that can offer it A new iprintf() function that only knows how to display integer objects If an embedded system needs floating-point support in only a few of the standard input and output functions, newlib can be rebuilt to exclude floating point from places where it isn't needed. You can omit floating point for everything except scanf(), for example, by either undefining the FLOATING_POINT macro everywhere except in the scanf source file, or by modifying newlib's build process to do the same thing, in a more automated fashion. I'll have more to say on these options later, when we look at what it takes to port newlib. For situations where only integer output is required, newlib provides the iprintf() function: a version of printf built without FLOATING_POINT enabled. It behaves exactly like printf, except that it does not understand the %f, %F, %g, or %G type specifiers, and therefore has a much smaller code footprint than its full-featured big brother. More on stdio Newlib's standard input and output facilities are surprisingly complete. The complete C file API is also provided, complete with read and write buffering, seeking, and stream flushing capabilities. Variations like sprintf, fprintf, and vfprintf (takes va_list arguments) are also included, which makes a newlib environment look strikingly similar to one you'd expect to see in a more workstation-oriented programming environment. An unfortunate limitation of newlib's stdio library is that it requires at least a minimal malloc() for proper operation. When the printf() machinery detects a floating-point format specifier, it allocates a few bytes of memory to use as a workspace for constructing the text representation of the number. It's actually a poor design decision, because in other places, newlib statically allocates memory to accomplish the same thing. But that's the way it is. On the plus side, newlib includes a good dynamic memory allocator that is straightforward to set up and use. I have also built a malloc based on a fixed-size memory block allocator, to eliminate fragmentation worries in systems where this was a concern. That code appears in the porting section. Unix API Newlib also includes a lot of the familiar Unix API functions like open() and write(). Most of them map directly to the code stubs newlib uses to invoke external system resources, so their simplicity eliminates the need for malloc(). These stubs will be discussed later, in the section on porting. libm Newlib contains a complete IEEE math library called libm. In addition to offering the standard math functions like exp(), sin(), and pow(), this code also provides matherr(), a modifiable math error handler that the library invokes whenever a serious math-related error, such as an underflow or loss of precision, is detected. By customizing this function, you can handle these situations in whatever way is appropriate for your system. As a surprising bonus, libm also includes functions that take float parameters, instead of double. These extensions are named after their full-precision equivalents. For example, sinf() is the single-precision version of the sin() function. The reduced-precision functions have a considerable speed advantage over their IEEE-compliant double-precision conterparts, which can put some floating-point operations within reach of hardware that is too weak for full double-precision computations. Reentrancy Newlib's C and math libraries are reentrant when properly integrated into a multithreaded environment. The implementation is not obvious at first glance, so I'll spend a few paragraphs describing how it works. Once you know the details, it will be clear how to make sure you set it up properly in your system. The ANSI C standard specifies a global integer called errno, that the runtime library sets when an error occurs. Once set, errno's value persists until the application clears it, which simplifies error notification by the library but can create reentrancy problems when multiple execution threads are working in the library at the same time-neither thread knows who generated the error. Newlib addresses this problem by redefining errno as a macro that references a global pointer to a structure of type struct _reent (the pointer is called _impure_ptr). (Actually, the errno macro calls the function __errno(), which in turn references __impure_ptr[errno.c:11]. But you get the idea.) This structure contains the traditional errno value specified by ANSI, but it also contains a lot of other stuff, including signal handler pointers and file handles for standard input, output, and error streams. Newlib's errno macro hides all of this from the user, of course. To check for errors, an application only needs to refer to the value of errno, just like it would in any ANSI environment. To determine why an fopen() failed, for example, you still do this: fp = fopen( "myfile.txt", "rw" ); if (fp == NULL) { switch( errno ) { case EACCESS: /* we don't have permissions */ ..
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages6 Page
-
File Size-