3
\$\begingroup\$

Is there anything slightly different between the two calls? Which is the "better" version?

\$\endgroup\$
  • 3
    \$\begingroup\$ What does the C version compile to? You aren't comparing like for like. \$\endgroup\$ – David Nov 28 '14 at 23:31
3
\$\begingroup\$

One specifically performs a JMP instruction, and the other treats it as a function call.

Depending on the chip you're programming for it will most probably use a different instruction than JMP, and it will most probably alter some registers in some way that the JMP wouldn't.

For instance, on MIPS, it would use a JAL instruction (Jump And Link), and the return address would be placed in $ra.

\$\endgroup\$
2
\$\begingroup\$

You don't say which microcontroller you are using, but that looks to me very much like a jump to a reset vector, causing a software reset on the device. The first form injects an assembly language opcode that creates a (blind) jump into that address, causing the control flow to immediately divert to the beginning of address space from the function it's written in.

The second form creates a function pointer to a void function, and calls that function (the last parentheses). Instead of jumping blind to address zero, this mechanism creates a new stack frame and at the very least pushes a return address to the stack so that the code can return from any function that is compiled to address zero or vectored from address zero.

If the address zero is indeed a reset vector on the target microcontroller, then diverting the program flow in any way to that address will run the startup routines, which resets the stack and very commonly also initializes any initialized variables in the system. So in that case it really makes no difference if the caller has created a return stack frame or not - the system will just reset and never return to the caller.

In the other case, if the microcontroller doesn't start its execution from address zero, then that address could contain any function or vector to a function, that conceivably might return. In that case it's important to write the call as a function pointer. A good example of this kind of system would be a microcontroller that has program RAM at address zero and program ROM at some other address such as 0x8000. At hardware reset the software execution might start at address 0x8000 instead of 0x0000 and the address 0x0000 might contain for example a zero pointer call trap, an old programming trick used to detect function calls to missing methods or to methods of uninitialized objects.

The reset case is the more common case, and the real difference (and the reason to use the function pointer style) might be that the C compiler doesn't support the asm keyword. But any C compiler will be able to compile the function pointer, so that style is also more portable.

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

The first statement will perform a jump to address zero; the second might perform a call to address zero, but it could just as well do something else. For example, an implementation might define a method NULL_DEREFERENCE_TRAP() with a default implementation while(1);, and have any attempt to call a null pointer invoke that function rather than actually jumping to location zero. If the processor has a watchdog, the while(1); loop may result in the watchdog triggering and restarting the system, but the system might hang for a second or so first.

Additionally, depending upon how system initialization code is written, it would be possible that startup code might check whether certain things which hardware would normally force to a certain state on reset are in fact in that state, and use the fact that they aren't as an indication that the system is being "soft-restarted". Triggering a watchdog could prevent such mechanisms from working as intended.

Finally, one should be aware that there is no requirement that the storage format for ((void(*)(void))0x1234) have any relationship to the storage format of the number 0x1234. If a system wanted to trap null-pointer accesses, but still provide a means of creating a pointer to a hardware register at address zero, and if no legitimate address had bit 31 set, it could legitimately (from the standpoint of the C standard) say that the bit pattern stored for any pointer will be the address xor'ed with 0x80000000. In such a system, casting 0 to a pointer would yield an invalid pointer (as it should), but trapping null access would require that a pointer to physical address zero be constructed by some means other than casting an integer zero.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy