Visualizzazione post con etichetta C. Mostra tutti i post
Visualizzazione post con etichetta C. Mostra tutti i post

martedì 26 aprile 2011

A Guide to Undefined Behavior in C and C++, Part 1

A Guide to Undefined Behavior in C and C++, Part 1: "

Programming languages typically make a distinction between normal program actions and erroneous actions. For Turing-complete languages we cannot reliably decide offline whether a program has the potential to execute an error; we have to just run it and see.


In a safe programming language, errors are trapped as they happen. Java, for example, is largely safe via its exception system. In an unsafe programming language, errors are not trapped. Rather, after executing an erroneous operation the program keeps going, but in a silently faulty way that may have observable consequences later on. Luca Cardelli’s article on type systems has a nice clear introduction to these issues. C and C++ are unsafe in a strong sense: executing an erroneous operation causes the entire program to be meaningless, as opposed to just the erroneous operation having an unpredictable result. In these languages erroneous operations are said to have undefined behavior.


The C FAQ defines “undefined behavior” like this:


Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.


This is a good summary. Pretty much every C and C++ programmer understands that accessing a null pointer and dividing by zero are erroneous actions. On the other hand, the full implications of undefined behavior and its interactions with aggressive compilers are not well-appreciated. This post explores these topics.


A Model for Undefined Behavior


For now, we can ignore the existence of compilers. There is only the “C implementation” which — if the implementation conforms to the C standard — acts the same as the “C abstract machine” when executing a conforming program. The C abstract machine is a simple interpreter for C that is described in the C standard. We can use it to determine the meaning of any C program.


The execution of a program consists of simple steps such as adding two numbers or jumping to a label. If every step in the execution of a program has defined behavior, then the entire execution is well-defined. Note that even well-defined executions may not have a unique result due to unspecified and implementation-defined behavior; we’ll ignore both of these here.


If any step in a program’s execution has undefined behavior, then the entire execution is without meaning. This is important: it’s not that evaluating (1<<32) has an unpredictable result, but rather that the entire execution of a program that evaluates this expression is meaningless. Also, it’s not that the execution is meaningful up to the point where undefined behavior happens: the bad effects can actually precede the undefined operation.


As a quick example let’s take this program:


#include <limits.h>
#include <stdio.h>

int main (void)
{
printf ("%d\n", (INT_MAX+1) < 0);
return 0;
}

The program is asking the C implementation to answer a simple question: if we add one to the largest representable integer, is the result negative? This is perfectly legal behavior for a C implementation:


$ cc test.c -o test
$ ./test
1

So is this:


$ cc test.c -o test
$ ./test
0

And this:


$ cc test.c -o test
$ ./test
42

And this:


$ cc test.c -o test
$ ./test
Formatting root partition, chomp chomp

One might say: Some of these compilers are behaving improperly because the C standard says a relational operator must return 0 or 1. But since the program has no meaning at all, the implementation can do whatever it likes. Undefined behavior trumps all other behaviors of the C abstract machine.


Will a real compiler emit code to chomp your disk? Of course not, but keep in mind that practically speaking, undefined behavior often does lead to Bad Things because many security vulnerabilities start out as memory or integer operations that have undefined behavior. For example, accessing an out of bounds array element is a key part of the canonical stack smashing attack. In summary: the compiler does not need to emit code to format your disk. Rather, following the OOB array access your computer will begin executing exploit code, and that code is what will format your disk.


No Traveling


It is very common for people to say — or at least think — something like this:


The x86 ADD instruction is used to implement C’s signed add operation, and it has two’s complement behavior when the result overflows. I’m developing for an x86 platform, so I should be able to expect two’s complement semantics when 32-bit signed integers overflow.


THIS IS WRONG. You are saying something like this:


Somebody once told me that in basketball you can’t hold the ball and run. I got a basketball and tried it and it worked just fine. He obviously didn’t understand basketball.


(This explanation is due to Roger Miller via Steve Summit.)


Of course it is physically possible to pick up a basketball and run with it. It is also possible you will get away with it during a game. However, it is against the rules; good players won’t do it and bad players won’t get away with it for long. Evaluating (INT_MAX+1) in C or C++ is exactly the same: it may work sometimes, but don’t expect to keep getting away with it. The situation is actually a bit subtle so let’s look in more detail.


First, are there C implementations that guarantee two’s complement behavior when a signed integer overflows? Of course there are. Many compilers will have this behavior when optimizations are turned off, for example, and GCC has an option (-fwrapv) for enforcing this behavior at all optimization levels. Other compilers will have this behavior at all optimization levels by default.


There are also, it should go without saying, compilers that do not have two’s complement behavior for signed overflows. Moreover, there are compilers (like GCC) where integer overflow behaved a certain way for many years and then at some point the optimizer got just a little bit smarter and integer overflows suddenly and silently stopped working as expected. This is perfectly OK as far as the standard goes. While it may be unfriendly to developers, it would be considered a win by the compiler team because it will increase benchmark scores.


In summary: There’s nothing inherently bad about running with a ball in your hands and also there’s nothing inherently bad about shifting a 32-bit number by 33 bit positions. But one is against the rules of basketball and the other is against the rules of C and C++. In both cases, the people designing the game have created arbitrary rules and we either have to play by them or else find a game we like better.


Why Is Undefined Behavior Good?


The good thing — the only good thing! — about undefined behavior in C/C++ is that it simplifies the compiler’s job, making it possible to generate very efficient code in certain situations. Usually these situations involve tight loops. For example, high-performance array code doesn’t need to perform bounds checks, avoiding the need for tricky optimization passes to hoist these checks outside of loops. Similarly, when compiling a loop that increments a signed integer, the C compiler does not need to worry about the case where the variable overflows and becomes negative: this facilitates several loop optimizations. I’ve heard that certain tight loops speed up by 30%-50% when the compiler is permitted to take advantage of the undefined nature of signed overflow. Similarly, there have been C compilers that optionally give undefined semantics to unsigned overflow to speed up other loops.


Why Is Undefined Behavior Bad?


When programmers cannot be trusted to reliably avoid undefined behavior, we end up with programs that silently misbehave. This has turned out to be a really bad problem for codes like web servers and web browsers that deal with hostile data because these programs end up being compromised and running code that arrived over the wire. In many cases, we don’t actually need the performance gained by exploitation of undefined behavior, but due to legacy code and legacy toolchains, we’re stuck with the nasty consequences.


A less serious problem, more of an annoyance, is where behavior is undefined in cases where all it does is make the compiler writer’s job a bit easier, and no performance is gained. For example a C implementation has undefined behavior when:


An unmatched ‘ or ” character is encountered on a logical source line during tokenization.


With all due respect to the C standard committee, this is just lazy. Would it really impose an undue burden on C implementors to require that they emit a compile-time error message when quote marks are unmatched? Even a 30 year-old (at the time C99 was standardized) systems programming language can do better than this. One suspects that the C standard body simply got used to throwing behaviors into the “undefined” bucket and got a little carried away. Actually, since the C99 standard lists 191 different kinds of undefined behavior, it’s fair to say they got a lot carried away.


Understanding the Compiler’s View of Undefined Behavior


The key insight behind designing a programming language with undefined behavior is that the compiler is only obligated to consider cases where the behavior is defined. We’ll now explore the implications of this.


If we imagine a C program being executed by the C abstract machine, undefined behavior is very easy to understand: each operation performed by the program is either defined or undefined, and usually it’s pretty clear which is which. Undefined behavior becomes difficult to deal with when we start being concerned with all possible executions of a program. Application developers, who need code to be correct in every situation, care about this, and so do compiler developers, who need to emit machine code that is correct over all possible executions.


Talking about all possible executions of a program is a little tricky, so let’s make a few simplifying assumptions. First, we’ll discuss a single C/C++ function instead of an entire program. Second, we’ll assume that the function terminates for every input. Third, we’ll assume the function’s execution is deterministic; for example, it’s not cooperating with other threads via shared memory. Finally, we’ll pretend that we have infinite computing resources, making it possible to exhaustively test the function. Exhaustive testing means that all possible inputs are tried, whether they come from arguments, global variables, file I/O, or whatever.


The exhaustive testing algorithm is simple:



  1. Compute next input, terminating if we’ve tried them all

  2. Using this input, run the function in the C abstract machine, keeping track of whether any operation with undefined behavior was executed

  3. Go to step 1


Enumerating all inputs is not too difficult. Starting with the smallest input (measured in bits) that the function accepts, try all possible bit patterns of that size. Then move to the next size. This process may or may not terminate but it doesn’t matter since we have infinite computing resources.


For programs that contain unspecified and implementation-defined behaviors, each input may result in several or many possible executions. This doesn’t fundamentally complicate the situation.


OK, what has our thought experiment accomplished? We now know, for our function, which of these categories it falls into:



  • Type 1: Behavior is defined for all inputs

  • Type 2: Behavior is defined for some inputs and undefined for others

  • Type 3: Behavior is undefined for all inputs


Type 1 Functions


These have no restrictions on their inputs: they behave well for all possible inputs (of course, “behaving well” may include returning an error code). Generally, API-level functions and functions that deal with unsanitized data should be Type 1. For example, here’s a utility function for performing integer division without executing undefined behaviors:


int32_t safe_div_int32_t (int32_t a, int32_t b) {
if ((b == 0) || ((a == INT32_MIN) && (b == -1))) {
report_integer_math_error();
return 0;
} else {
return a / b;
}
}

Since Type 1 functions never execute operations with undefined behavior, the compiler is obligated to generate code that does something sensible regardless of the function’s inputs. We don’t need to consider these functions any further.


Type 3 Functions


These functions admit no well-defined executions. They are, strictly speaking, completely meaningless: the compiler is not even obligated to generate even a return instruction. Do Type 3 functions really exist? Yes, and in fact they are common. For example, a function that — regardless of input — uses a variable without initializing it is easy to unintentionally write. Compilers are getting smarter and smarter about recognizing and exploiting this kind of code. Here’s a great example from the Google Native Client project:


When returning from trusted to untrusted code, we must sanitize the return address before taking it. This ensures that untrusted code cannot use the syscall interface to vector execution to an arbitrary address. This role is entrusted to the function NaClSandboxAddr, in sel_ldr.h. Unfortunately, since r572, this function has been a no-op on x86.


-- What happened?


During a routine refactoring, code that once read


aligned_tramp_ret = tramp_ret & ~(nap->align_boundary - 1);


was changed to read


return addr & ~(uintptr_t)((1 << nap->align_boundary) - 1);


Besides the variable renames (which were intentional and correct), a shift was introduced, treating nap->align_boundary as the log2 of bundle size.


We didn't notice this because NaCl on x86 uses a 32-byte bundle size. On x86 with gcc, (1 << 32) == 1. (I believe the standard leaves this behavior undefined, but I'm rusty.) Thus, the entire sandboxing sequence became a no-op.


This change had four listed reviewers and was explicitly LGTM'd by two. Nobody appears to have noticed the change.


-- Impact


There is a potential for untrusted code on 32-bit x86 to unalign its instruction stream by constructing a return address and making a syscall. This could subvert the validator. A similar vulnerability may affect x86- 64.


ARM is not affected for historical reasons: the ARM implementation masks the untrusted return address using a different method.


What happened? A simple refactoring put the function containing this code into Type 3. The person who sent this message believes that x86-gcc evaluates (1<<32) to 1, but there’s no reason to expect this behavior to be reliable (in fact it is not on a few versions of x86-gcc that I tried). This construct is definitely undefined and of course the compiler can do done anything it wants. As is typical for a C compiler, it chose to simply not emit the instructions corresponding to the undefined operation. (A C compiler’s #1 goal is to emit efficient code.) Once the Google programmers gave the compiler the license to kill, it went ahead and killed. One might ask: Wouldn’t it be great if the compiler provided a warning or something when it detected a Type 3 function? Sure! But that is not the compiler’s priority.


The Native Client example is a good one because it illustrates how competent programmers can be suckered in by an optimizing compiler’s underhanded way of exploiting undefined behavior. A compiler that is very smart at recognizing and silently destroying Type 3 functions becomes effectively evil, from the developer’s point of view.


Type 2 Functions


These have behavior that is defined for some inputs and undefined for others. This is the most interesting case for our purposes. Signed integer divide makes a good example:


int32_t unsafe_div_int32_t (int32_t a, int32_t b) {
return a / b;
}

This function has a precondition; it should only be called with arguments that satisfy this predicate:


(b != 0) && (!((a == INT32_MIN) && (b == -1)))

Of course it’s no coincidence that this predicate looks a lot like the test in the Type 1 version of this function. If you, the caller, violate this precondition, your program’s meaning will be destroyed. Is it OK to write functions like this, that have non-trivial preconditions? In general, for internal utility functions this is perfectly OK as long as the precondition is clearly documented.


Now let’s look at the compiler’s job when translating this function into object code. The compiler performs a case analysis:



  • Case 1: (b != 0) && (!((a == INT32_MIN) && (b == -1)))

    Behavior of / operator is defined → Compiler is obligated to emit code computing a / b

  • Case 2: (b == 0) || ((a == INT32_MIN) && (b == -1))

    Behavior of / operator is undefined → Compiler has no particular obligations


Now the compiler writers ask themselves the question: What is the most efficient implementation of these two cases? Since Case 2 incurs no obligations, the simplest thing is to simply not consider it. The compiler can emit code only for Case 1.


A Java compiler, in contrast, has obligations in Case 2 and must deal with it (though in this particular case, it is likely that there won’t be runtime overhead since processors can usually provide trapping behavior for integer divide by zero).


Let’s look at another Type 2 function:


int stupid (int a) {
return (a+1) > a;
}

The precondition for avoiding undefined behavior is:


(a != INT_MAX)

Here the case analysis done by an optimizing C or C++ compiler is:



  • Case 1: a != INT_MAX

    Behavior of + is defined → Computer is obligated to return 1

  • Case 2: a == INT_MAX

    Behavior of + is undefined → Compiler has no particular obligations


Again, Case 2 is degenerate and disappears from the compiler’s reasoning. Case 1 is all that matters. Thus, a good x86-64 compiler will emit:


stupid:
movl $1, %eax
ret

If we use the -fwrapv flag to tell GCC that integer overflow has two’s complement behavior, we get a different case analysis:



  • Case 1: a != INT_MAX

    Behavior is defined → Computer is obligated to return 1

  • Case 2: a == INT_MAX

    Behavior is defined → Compiler is obligated to return 0


Here the cases cannot be collapsed and the compiler is obligated to actually perform the addition and check its result:


stupid:
leal 1(%rdi), %eax
cmpl %edi, %eax
setg %al
movzbl %al, %eax
ret

Similarly, an ahead-of-time Java compiler also has to perform the addition because Java mandates two’s complement behavior when a signed integer overflows (I’m using GCJ for x86-64):


_ZN13HelloWorldApp6stupidEJbii:
leal 1(%rsi), %eax
cmpl %eax, %esi
setl %al
ret

This case-collapsing view of undefined behavior provides a powerful way to explain how compilers really work. Remember, their main goal is to give you fast code that obeys the letter of the law, so they will attempt to forget about undefined behavior as fast as possible, without telling you that this happened.


A Fun Case Analysis


About a year ago, the Linux kernel started using a special GCC flag to tell the compiler to avoid optimizing away useless null-pointer checks. The code that caused developers to add this flag looks like this (I’ve cleaned up the example just a bit):


static void __devexit agnx_pci_remove (struct pci_dev *pdev)
{
struct ieee80211_hw *dev = pci_get_drvdata(pdev);
struct agnx_priv *priv = dev->priv;

if (!dev) return;

  ... do stuff using dev ...
}

The idiom here is to get a pointer to a device struct, test it for null, and then use it. But there’s a problem! In this function, the pointer is dereferenced before the null check. This leads an optimizing compiler (for example, gcc at -O2 or higher) to perform the following case analysis:



  • Case 1: dev == NULL

    “dev->priv” has undefined behavior → Compiler has no particular obligations

  • Case 2: dev != NULL

    Null pointer check won’t fail → Null pointer check is dead code and may be deleted


As we can now easily see, neither case necessitates a null pointer check. The check is removed, potentially creating an exploitable security vulnerability.


Of course the problem is the use-before-check of pci_get_drvdata()’s return value, and this has to be fixed by moving the use after the check. But until all such code can be inspected (manually or by a tool), it was deemed safer to just tell the compiler to be a bit conservative. The loss of efficiency due to a predictable branch like this is totally negligible. Similar code has been found in other parts of the kernel.


Living with Undefined Behavior


In the long run, unsafe programming languages will not be used by mainstream developers, but rather reserved for situations where high performance and a low resource footprint are critical. In the meantime, dealing with undefined behavior is not totally straightforward and a patchwork approach seems to be best:



  • Enable and heed compiler warnings, preferably using multiple compilers

  • Use static analyzers (like Clang’s, Coverity, etc.) to get even more warnings

  • Use compiler-supported dynamic checks; for example, gcc’s -ftrapv flag generates code to trap signed integer overflows

  • Use tools like Valgrind to get additional dynamic checks

  • When functions are “type 2″ as categorized above, document their preconditions and postconditions

  • Use assertions to verify that functions’ preconditions are postconditions actually hold

  • Particularly in C++, use high-quality data structure libraries


Basically: be very careful, use good tools, and hope for the best.


"

sabato 2 aprile 2011

Il C++ sta per avere un nuovo standard

Il C++ sta per avere un nuovo standard: "



Finalmente, dopo tantissimi anni di lavoro, il linguaggio C++ sta per essere aggiornato dato che l’ISO steering committee per il linguaggio ha approvato l’ultima proposta per il nuovo standard.


L’ISO/IEC Information Technology Task Force (ITTF) avrà valutato il lavoro presentato, se non ci saranno complicazioni tali da sollevare un’obiezione, il nuovo C++ sarà standard entro l’anno e sarà chiamato C++ 2011. Questo segna un passo molto importante per la crescita del linguaggio che oggi si trova al terzo posto nella classifica dei più utilizzati subito dopo Java e C.


Le migliorie e le nuove funzionalità sono molte tali da poter quasi definire il C++ 2011 un nuovo linguaggio anche se mantiene le sue radici storiche. È già possibile provare alcune delle nuove caratteristiche grazie alle estensioni che alcuni compilatori già includono, come il GCC, o attraverso librerie esterne come Boost.





Tra le caratteristiche più importanti si potrebbero citare:



Via | Cio



Il C++ sta per avere un nuovo standard é stato pubblicato su ossblog alle 16:59 di mercoledì 30 marzo 2011."

mercoledì 21 luglio 2010

A Practical Introduction to Data Structures and Algorithm Analysis Third Edition (C++ Version)

A Practical Introduction to Data Structures and Algorithm Analysis Third Edition (C++ Version): "This book provides a thorough and comprehensive treatment of fundamental data structures and the principles of algorithm analysis. Focuses on the principles required to select or design the data structure that will best solve the problem."

domenica 4 luglio 2010

Firmware-Specific Bug #5: Heap Fragmentation

Firmware-Specific Bug #5: Heap Fragmentation: "Dynamic memory allocation is not widely used by embedded software developers—and for good reasons. One of those is the problem of fragmentation of the heap.

All data structures created via C’s malloc() standard library routine or C++’s new keyword live on the heap. The heap is a specific area in RAM of a pre-determined maximum size. Initially, each allocation from the heap reduces the amount of remaining “free” space by the same number of bytes. For example, the heap in a particular system might span 10 KB starting from address 0x20200000. An allocation of a pair of 4-KB data structures would leave 2 KB of free space.

The storage for data structures that are no longer needed can be returned to the heap by a call to free() or use of the delete keyword. In theory this makes that storage space available for reuse during subsequent allocations. But the order of allocations and deletions is generally at least pseudo-random—leading the heap to become a mess of smaller fragments.

To see how fragmentation can be a problem, consider what would happen if the first of the above 4 KB data structures is free. Now the heap consists of one 4-KB free chunk and another 2-KB free chunk; they are not adjacent and cannot be combined. So our heap is already fragmented. Despite 6 KB of total free space, allocations of more than 4 KB will fail.

Fragmentation is similar to entropy: both increase over time. In a long running system (i.e., most every embedded system ever created), fragmentation may eventually cause some allocation requests to fail. And what then? How should your firmware handle the case of a failed heap allocation request?

Best Practice: Avoiding all use of the heap may is a sure way of preventing this bug. But if dynamic memory allocation is either necessary or convenient in your system, there is an alternative way of structuring the heap that will prevent fragmentation. The key observation is that the problem is caused by variable sized requests.

If all of the requests were of the same size, then any free block is as good as any other—even if it happens not to be adjacent to any of the other free blocks. Thus it is possible to use multiple “heaps”—each for allocation requests of a specific size—can using a “memory pool” data structure.

If you like you can write your own fixed-sized memory pool API. You’ll just need three functions:


  • handle = pool_create(block_size, num_blocks) - to create a new pool (of size M chunks by N bytes);

  • p_block = pool_alloc(handle) - to allocate one chunk (from a specified pool); and

  • pool_free(handle, p_block).



But note that many real-time operating systems (RTOSes) feature a fixed-size memory pool API. If you have access to one of those, use it instead of the compiler's malloc() and free() or your own implementation.

Firmware-Specific Bug #4

Firmware-Specific Bug #6 (coming soon)"

Reading a register for its side effects in C and C++

Reading a register for its side effects in C and C++: "Although today's post is the first real post on the new EmbeddedGurus, it's special for another reason. This post is being jointly written with John Regehr. John is an Associate Professor of Computer Science at the University of Utah and maintains an excellent blog, Embedded in Academia which I heartily recommend. This blog posting grew out of a lengthy email exchange which started with John alerting me to some blatant plagiarism of my work and then evolved (dissolved?) into what you find here. John is also posting this article on his blog.

Anyway, enough preamble, on to the topic at hand.

Once in awhile one finds oneself having to read a device register, but without needing nor caring what the value of the register is. A typical scenario is as follows. You have written some sort of asynchronous communications driver. The driver is set up to generate an interrupt upon receipt of a character. In the ISR, the code first of all examines a status register to see if the character has been received correctly (e.g. no framing, parity or overrun errors). If an error has occurred, what should the code do? Well, in just about every system we have worked on, it is necessary to read the register that contains the received character -- even though the character is useless. If you don't perform the read, then you will almost certainly get an overrun error on the next character. Thus you find yourself in the position of having to read a register even though its value is useless. The question then becomes, how does one do this in C? In the following examples, assume that SBUF is the register holding the data to be discarded and that SBUF is understood to be volatile. The exact semantics of the declaration of SBUF vary from compiler to compiler.

If you are programming in C and if your compiler correctly supports the volatile qualifier, then this simple code suffices:
void cload_reg1 (void)
{
SBUF;
}

This certainly looks a little strange, but it is completely legal C and should generate the requisite read, and nothing more. For example, at the -Os optimization level, the MSP430 port of GCC gives this code:
cload_reg1:
mov &SBUF, r15
ret

Unfortunately, there are two practical problems with this C code. First, quite a few C compilers incorrectly translate this code, although the C standard gives it an unambiguous meaning. We tested the code on a variety of general-purpose and embedded compilers, and present the results below. These results are a little depressing.

The second problem is even scarier. The problem is that the C++ standard is not 100% clear about what the code above means. On one hand, the standard says this:
In general, the semantics of volatile are intended to be the same in C++ as they are in C.

A number of C++ compilers, including GCC and LLVM, generate the same code for cload_reg1() when compiling in C++ mode as they do in C mode. On the other hand, several high-quality C++ compilers, such as those from ARM, Intel, and IAR, turn the function cload_reg1() into object code that does nothing. We discussed this issue with people from the compiler groups at Intel and IAR, and both gave essentially the same response. Here we quote (with permission) from the Intel folks:
The operation that turns into a load instruction in the executable code is what the C++ standard calls the lvalue-to-rvalue conversion; it converts an lvalue (which identifies an object, which resides in memory and has an address) into an rvalue (or just value; something whose address can't be taken and can be in a register). The C++ standard is very clear and explicit about where the lvalue-to-rvalue conversion happens. Basically, it happens for most operands of most operators - but of course not for the left operand of assignment, or the operand of unary ampersand, for example. The top-level expression of an expression statement, which is of course not the operand of any operator, is not a context where the lvalue-to-rvalue conversion happens.

In the C standard, the situation is somewhat different. The C standard has a list of the contexts where the lvalue-to-rvalue conversion doesn't happen, and that list doesn't include appearing as the expression in an expression-statement.

So we're doing exactly what the various standards say to do. It's not a matter of the C++ standard allowing the volatile reference to be optimized away; in C++, the standard requires that it not happen in the first place.

We think the last sentence sums it up beautifully. How many readers were aware that the semantics for the volatile qualifier are significantly different between C and C++? The additional implication is that as shown below, GCC, the Microsoft compiler, and Open64, when compiling C++ code, are in error.

We asked about this on the GCC mailing list and received only one response which was basically 'Why should we change the semantics, since this will break working code?' This is a fair point. Frankly speaking, the semantics of volatile in C are a bit of mess and C++ makes the situation much worse by permitting reasonable people to interpret it in two totally different ways.

Experimental Results


To test C and C++ compilers, we compiled the following two functions to object code at a reasonably high level of optimization:
extern volatile unsigned char foo;

void cload_reg1 (void)
{
foo;
}

void cload_reg2 (void)
{
volatile unsigned char sink;
sink = foo;
}

For embedded compilers that have built-in support for accessing hardware registers, we tested two additional functions where as above, SBUF is understood to be a hardware register defined by the semantics of the compiler under test:
void cload_reg3 (void)
{
SBUF;
}

void cload_reg4 (void)
{
volatile unsigned char sink;
sink = SBUF;
}

The results were as follows.

GCC


We tested version 4.4.1, hosted on x86 Linux and also targeting x86 Linux, using optimization level -Os. The C compiler loads from foo in both cload_reg1() and cload_reg2() . No warnings are generated. The C++ compiler shows the same behavior as the C compiler.

Intel Compiler


We tested icc version 11.1, hosted on x86 Linux and also targeting x86 Linux, using optimization level -Os. The C compiler emits code loading from foo for both cload_reg1() and cload_reg2(), without giving any warnings. The C++ compiler emits a warning 'expression has no effect' for cload_reg1() and this function does not load from foo. cload_reg2() does load from foo and gives no warnings.

Sun Compiler


We tested suncc version 5.10, hosted on x86 Linux and also targeting x86 Linux, using optimization level -O. The C compiler does not load from foo in cload_reg1(), nor does it emit any warning. It does load from foo in cload_reg2(). The C++ compiler has the same behavior as the C compiler.

x86-Open64


We tested opencc version 4.2.3, hosted on x86 Linux and also targeting x86 Linux, using optimization level -Os. The C compiler does not load from foo in cload_reg1(), nor does it emit any warning. It does load from foo in cload_reg2(). The C++ compiler has the same behavior as the C compiler.

LLVM / Clang


We tested subversion rev 98508, which is between versions 2.6 and 2.7, hosted on x86 Linux and also targeting x86 Linux, using optimization level -Os. The C compiler loads from foo in both cload_reg1() and cload_reg2() .
A warning about unused value is generated for cload_reg1(). The C++ compiler shows the same behavior as the C compiler.

CrossWorks for MSP430


We tested version 2.0.8.2009062500.4974, hosted on x86 Linux, using optimization level -O. This compiler supports only C. foo was not loaded in cload_reg1(), but it was loaded in cload_reg2().

IAR for AVR


We tested version 5.30.6.50191, hosted on Windows XP, using maximum speed optimization. The C compiler performed the load in all four cases. The C++ compiler did not perform the load for cload_reg1() or cload_reg3(),
but did for cload_reg2() and cload_reg4().

Keil 8051


We tested version 8.01, hosted on Windows XP, using optimization level 8, configured to favor speed. The Keil compiler failed to generate the required load in cload_reg1() (but did give at least give a warning), yet did perform the load in all other cases including cload_reg3() suggesting that for the Keil compiler, its IO register (SFR) semantics are treated differently to volatile variable semantics.

HI-TECH for PIC16


We tested version 9.70, hosted on Windows XP, using Global optimization level 9, configured to favor speed. This was very interesting in that the results were almost a mirror image to the Keil compiler. In this case the load was performed in all cases except cload_reg3(). Thus the HI-TECH semantics for IO registers and volatile variables also appears to be different - just the opposite to Keil! No warnings was generated by the Hi-TECH compiler when it failed to generate code.

Microchip Compiler for PIC18


We tested version 3.35, hosted on Windows XP, using full optimization level. This rounded out the group of embedded compilers quite nicely in that it didn't perform the load in either cload_reg1() or cload_reg3() - but did in the rest. It also failed to warn about the statements having no effect. This was the worst performing of all the compilers we tested.

Summary


The level of non-conformance with the C compilers, together with the genuine uncertainty as to what the C++ compilers should do provides a real quandary. If you need the most efficient code possible, then you have no option other than to investigate what your compiler does. If you are looking for a generally reliable and portable solution, then the methodology in cload_reg2() is probably your best bet. However it would be just that: a bet. Naturally, we (and the other readers of this blog) would be very interested to hear what your compiler does. So if you have a few minutes, please run the sample code through your compiler and let us know the results.

Acknowledgments


We'd like to thank Hans Boehm at HP, Arch Robison at Intel, and the compiler groups at both Intel and IAR for their valuable feedback that helped us construct this post. Any mistakes are, of course, ours.
Home"

sabato 3 aprile 2010

Efficient C Tip #11 - Avoid passing parameters by using more small functions

Efficient C Tip #11 - Avoid passing parameters by using more small functions: "This is the eleventh in a series of tips on writing efficient C for embedded systems. Today's topic will, I suspect, be slightly controversial. This post is based upon two basic observations:

  1. Passing parameters to functions is costly.

  2. Conditional branch instructions can be very costly on CPUs that have instruction caches (even with branch prediction).


I don't think that too many people will disagree with me on the above. Despite this I too often see a style of coding that incurs these costs unnecessarily. I think it's best illustrated by a (real world) example. The issue is one that will be familiar to most of you.  An embedded system contains a number of discrete LEDs (say 3), and the requirement is to write some code to allow higher level code to either turn on, turn off, or toggle a particular LED. The way I often see this coded is as follows:
typedef enum
{
LED1, LED2, LED3
} LED_NO;

typedef enum
{
LED_OFF, LED_ON, LED_TOGGLE
} LED_ACTION;

void led(LED_NO led_no, LED_ACTION led_action)
{
switch (led_no)
{
case LED1:
switch (led_action)
{
case LED_OFF:
PORTB_PORTB0 = 0;
break;

    case LED_ON:
PORTB_PORTB0 = 1;
break;

    case LED_TOGGLE:
PORTB_PORTB0 ^= 1;
break;

    default:
break;
}
break;

 case LED2:
...
}

So what's wrong with this you ask? Well in a nutshell the parameters passed to the function are used strictly to control the order of execution. There is no code common to any pair or group of parameters. When faced with a situation such as this, I instead implement the code as a large number of very small functions. For example:
void led1_Off(void)
{
PORTB_PORTB1 = 0;
}

void led1_On(void)
{
PORTB_PORTB1 = 1;
}

void led1_Toggle(void)
{
PORTB_PORTB1 ^= 1;
}
...

Let's compare the two approaches.

Efficiency


This blog posting is supposedly about efficiency, so let's start with the results. I coded these two approaches up together with a main() function that exercised all 9 possible combination's. I then turned full speed optimization on and looked at the results for an AVR processor.

Single function approach: 78 bytes for main(), 94 bytes for the LED code. Execution time 208 cycles.

Multiple function approach: 42 bytes for main(), 54 bytes for the LED code. Execution time 96 cycles.

Clearly my approach is significantly more efficient.

Usability


By usability I'm referring to the case where someone else needs to use your code. They know they need to say toggle LED2 so they hunt around and find the file led.h. The question is, once they have opened up led.h, how quickly can they determine what they have to do in order to toggle LED2? In the single function case they are presented with just one function (which is a plus), but then they have to locate the enumerations and work out the parameters that need to be passed to the function (which is a minus). In the multiple function case, they have to search through a list of functions looking for the correct one. However once they have found it, it's very clear what the function does.

For me, I think it is a toss up between the two approaches as to which is more usable.

Maintainability


In this case the multiple function approach is the big winner. To see how this is, consider what happens to the single function case when one adds an LED or adds an action. The single function case just explodes in size, whereas with the multi-function approach one simply adds more very simple functions.

Conclusions


If you buy my analysis then clearly the multi-function approach is superior in both efficiency and maintainability - two areas that are dear to my heart. Now granted this is a fairly extreme example. However in my experience if you look through a reasonable amount of code you will soon discover a function that essentially does one thing or another based upon a function parameter. When you locate such a function you might want to try breaking it into two functions in the manner described here - I think you'll be pleased with the results.  Previous Tip

****
As the readership of this blog has grown I must say I have been really impressed with the many insightful comments that have been posted. I know I learn a lot from them, and so I suspect, do a lot of the other readers. Thus for those of you that have commented in the past - thank you. For those of you yet to post a comment, I encourage you to take the plunge!

Home"

Effective C Tip #8 - Structure Comparison

Effective C Tip #8 - Structure Comparison: "
This is the eighth in a series of tips on writing effective C. Today's topic concerns how best to compare two structures (naturally of the same type) for equality. The conventional wisdom is that the only safe way to do this is by explicit member by member comparison, and that one should avoid doing a straight bit (actually byte) comparison using memcmp() because of the problem of padding bytes.

To see why this argument is advanced, one must understand that a compiler is free to place pad bytes between members of a structure so as produce more favorable alignment of the data in memory. Furthermore, the compiler is not obligated to initialize these pad bytes to any particular value. This code fragment illustrates the problem:
typedef struct
{
 uint8_t x;
 uint8_t pad1;  /* Compiler added padding */
 uint8_t y;
 uint8_t pad2;  /* Compiler added padding */

} COORD;

void foo(void)
{
 COORD p1 p2;
 p1.x = p2.x = 3;
 p1.y = p2.y = 4;
 /* Note pad bytes are not initialized */
 if (memcmp(&p1, &p2, sizeof(p1)) != 0)
 {
   /* We may get here */
 }
 ...
}


Thus, it's clear that to avoid these kinds of problems, we must do a member by member comparison. However, before you rush off and start writing these member by member comparison functions, you need to be aware of a gigantic weakness with this approach. To see what I mean, consider the comparison function for my COORD structure. A reasonable implementation might look like this:

bool are_equal(COORD *p1, COORD *p2)
{
  return ((p1->x == p2->x) && (p1->y == p2->y));
}

void foo(void)
{

 COORD p1 p2;
 p1.x = p2.x = 3;
 p1.y = p2.y = 4;

 if (!are_equal(&p1, &p2))
 {
   /* We should never get here */
 }
 ...
}


Now consider what happens if I add a third member z to the COORD structure. My structure definition and function foo() become:
typedef struct
{
 uint8_t x;
 uint8_t pad1;  /* Compiler added padding */
 uint8_t y;
 uint8_t pad2;  /* Compiler added padding */
 uint8_t z;
 uint8_t pad3;  /* Compiler added padding */
} COORD;

void foo(void)
{

 COORD p1 p2;
 p1.x = p2.x = 3;
 p1.y = p2.y = 4;
 p1.z = 6;
 p2.z = 5;


 if (!are_equal(&p1, &p2)
 {
   /* We will not get here */
 }
 ...
}

The problem is that I now have to remember to also update the comparison function. Now clearly in a simple case like this, it isn't a big deal. However, in the real world where you might have a 500 line file, with the comparison function buried miles away from the structure declaration, it is way too easy to forget to update the comparison function. The compiler is of no help. Furthermore it's my experience that all too often these sorts of problems can exist for a long time before they are caught. Thus the bottom line, is that member by member comparison has its own set of problems.

So what do I suggest? Well, I think the following is a reasonable approach:
  1. If there is no way that your structure can change (presumably because of outside constraints such as hardware), then use a member by member comparison.
  2. If you are working on a system where structure members are aligned on byte boundaries (which is true to the best of my knowledge for all 8 bit processors, and also most 16 bit processors), then use memcmp(). However, you need to think about doing this very carefully if there is the possibility of the code being ported to a platform where alignment is not on an 8 bit boundary.
  3. If you are working on a system that aligns on a non 8 bit boundary, then you must either use member by member comparison, or take steps to ensure that all the bytes of a structure are initialized using memset() before you start assigning values to the actual members. If you do this, then you can probably use memcmp() with a reasonable amount of confidence.
  4. If speed is a priority, then clearly memcmp() is the way to go. Just make sure you aren't going to fall into a pothole as you blaze down the road.
Before I leave this topic, I should mention a few esoteric things for you to consider.

If you use the memcmp() approach you are checking for bit equality rather than value equality. Now most of the time they are the same. Sometimes however, they are not. To illustrate this, consider a structure that contains a parameter that is a boolean. If in one structure the parameter has a value of 1, and in the other structure it has a value of 2, then clearly they differ at the bit level, but are essentially the same at a value level. What should you do in this case? Well clearly it's implementation dependent. It does however illustrate the perils of structure comparison.

Finally I should mention issues associated with structures that contain pointers. CS guys like to distinguish between deep and shallow structure comparison. I rarely write code where a deep comparison is required, and so for me it's mostly a non-issue.

Previous Tip
Home"

venerdì 2 aprile 2010

Different Bit Types in Different Registers

Different Bit Types in Different Registers: "Several years ago I came across a register that, at first glance, seemed to be a typical register with several read/write bits. Upon closer inspection, I saw that one bit behaved differently, depending on its state and whether I was writing a 1 or a 0 to it. Since firmware had to handle that one bit differently from the other bits in that register, I saw that it would be difficult for firmware to safely handle both types in the same register. I was able to reduce the risk of mishandling that register by writing macros with generous comments. However, I could not ensure that everybody in the future would know that it was not a typical read/write register and treat it as such.

Although the engineer had valid reasons for designing the register that way, he or she had not anticipated the impact on firmware of mixing different types of bits in the same register. To avoid complexity and risk of firmware defects, different types of bits should be located in different registers. To see why, let’s examine how firmware manages two types of bits – read/write bits and interrupt bits.

With read/write bits, firmware sets and clears bits when needed. It typically first reads the register, modifies the desired bit, then writes the modified value back out. Here is a sample code fragment:

tmp = ReadReg (regA);  /* Get the register contents */
tmp |= 0x01; /* Set bit 0 */
RegWrite (regA, tmp); /* Write it back out */


In the case of interrupt bits, firmware often writes a value with one bit set to acknowledge the desired interrupt while leaving any other pending interrupts untouched.

RegWrite (regB, 0x10); /* Ack bit 4 */


Mixing the two types of bits in the same register could cause problems. Using the read/write code on interrupt bits causes pending interrupts to inadvertently be acknowledged. Using the interrupt code on read/write bits clears all read/write bits that used to be set. Firmware must take special care to ensure that it does not inadvertently change the wrong bits.

Here is a code fragment that acknowledges bit 4 while taking care not to acknowledge other pending interrupts or modify any read/write bits. In this example, read/write bits are located in positions 0x0f and interrupt bits are located in positions 0x70.

tmp = RegRead (regC);  /* Get the register contents */
tmp &= 0x0f; /* Keep R/W bits but zero any intr bits */
tmp |= 0x10; /* Set bit 4 to ack */
RegWrite (regC, tmp); /* Write it back out */


Acknowledging an interrupt changed from a one-step to a four-step operation. A similar code fragment is needed to modify desired read/write bits while leaving alone any pending interrupts.

While there is a way for firmware to safely handle this, it is out of the ordinary and prone to firmware defects. Combining different types of bits into the same register may save registers but it adds unnecessary burden and complexity to firmware. Looking ahead and anticipating the firmware impact can lead to a more reliable and robust solution of placing different types of bits in separate registers.



Best Practice: Segregate different types of bits (read/write, read-only, interrupt, etc.) into different registers.




If necessary, read-only bits could be combined with any one of the other types of bits. This is acceptable because no matter how the other bits are handled, firmware writes to the register will not affect the read-only bits.

Until the next bit..."

domenica 21 febbraio 2010

No Bugs: Delivering Error-Free Code in C and C++

No Bugs: Delivering Error-Free Code in C and C++: "This book presents techniques to stop many kinds of bugs from being included in a program. It also discusses how to test programs to find bugs."