0

I was trying to write my own boot loader on Atmel AVR Microcontroller. I have referred one of the code base from github. I would like to thank for the code base to ZEVERO

At primary level I understand the code base. but at line 224 I found a line Reference to the code

**if (pgm_read_word(0) != 0xFFFF) ((void(*)(void))0)();   //EXIT BOOTLOADER**

I understand the if condition part but when I was trying to understand the true statement part i.e.

**((void(*)(void))0)();**

code writer has given explanation to this is //EXIT BOOTLOADER

My first Question is what is the meaning of this complex declaration **((void(*)(void))0)();**

And Second Question is, does it Exit the execution of the code in Microcontroller.

Vardhman Patil
  • 517
  • 6
  • 22
  • 6
    No (it isn't directly exit code); it's probably going to crash, or otherwise execute the code at address 0 (which might be restart code). It is undefined behaviour, and therefore bad unless there is a context which gives it defined behaviour. – Jonathan Leffler Dec 19 '17 at 05:26
  • 4
    For the platform this code is written, it *might* be reset code. But this is in no way portable. Always use the standard [`exit`](https://www.tutorialspoint.com/c_standard_library/c_function_exit.htm) function. – Ajay Brahmakshatriya Dec 19 '17 at 05:29

4 Answers4

11

As @iBug pointed out, ((void(*)(void))0)(); invokes a function call on a NULL function pointer.

In effect, that transfers program control to memory address 0. Now, on a workstation, that would be colossal UB, most likely resulting in a segfault.

However, since the code in question is for a hardware bootloader, it's not UB, it (apparently) just exits the bootloader.

At the hardware level, almost everything is implementation dependent, and almost nothing is portable. You can't expect C code targeted at a specific hardware platform to be in any way representative of generally-accepted C patterns and practices.

Mark Benningfield
  • 2,800
  • 9
  • 31
  • 31
7

((void(*)(void))0)(); tries to call a NULL function pointer. User programs (not bootloaders) for AVR microcontrollers usually start execution at address 0. AVR-GCC's ABI uses an all-0-bit representation of NULL function pointers, so this call will (among other things) transfer execution to the user program. Essentially, it works as a slower version of __asm__ __volatile__("jmp 0");, and assumes that the user program's startup code will reinitialize the stack pointer anyway.

Calling through a NULL function pointer is undefined behavior, so there's no guarantee that this trick will work with other compilers, later versions of GCC, or even different optimization settings.

The if (pgm_read_word(0) != 0xFFFF) check before the call is probably to determine if a user program is present: program memory words that have been erased but not written will read as 0xFFFF, while most programs start with a JMP instruction to skip over the rest of the interrupt vector table, and the first word of a JMP instruction is never 0xFFFF.

0

As has been pointed out before, calling this function simply results in a jump to address 0.

As the code at this address is typically not defined by your own program, but rather by the specific environment, behavior totally depends on this environment.

Your question is tagged as AVM/Atmel: on AVRs, jumping to address 0 simply results in a restart (nearly same behavior as a hardware reset, but beware, the MCU will keep the interrupt enabled/disabled state as opposed to a "real" reset). A "cleaner" program might probably want to use the watchdog timer for a "real" reset (wdt_reset() et al).

tofro
  • 5,640
  • 14
  • 31
-1

It will simply call the address 0 as if it was a function returning void and taking no arguments. Or... less simply the address that is the bit pattern of the null pointer. Or even less simply, the behaviour is undefined so it might do anything unexpected.