Page 1 of 1

Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Thu Oct 26, 2023 4:44 pm
by djixon
For example in Microchip C compiler you can declare some variable like

volatile uint8_t VAL @ 0x600

which tells C compiler to reserve one byte at address 0x600 in RAM for variable VAL. Is there a way to achieve same behavior in ESP-IDF?

Re: Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Thu Oct 26, 2023 9:38 pm
by MicroController
Is there a way to achieve same behavior in ESP-IDF?
The portable way to get the same behaviour is via pointers:

Code: Select all

volatile uint8_t* const p_val = (volatile uint8_t*)0x600;
...
if (*p_val == 0) {
  *p_val = 0xff;
}
Alternatively, the linker may be persuaded to allocate a symbol (variable) to a fixed address via custom linker scripts.

I generally recommend to use C++ instead of C. In this case, in C++ you could use a reference instead of a pointer which would be a bit easier/safer to use:

Code: Select all

volatile uint8_t& val {*((volatile uint8_t*)0x600)};
...
if(val == 0) {
  val = 0xff;
}
However, for the ESP's you usually use the IDF to do hardware register accesses for you; you can choose:
a) use the provided "high-level" drivers' API,
b) use the provided underlying "low-level" routines, or
c) use the provided macros for direct register access.

The driver API is fine for the vast majority of applications; the ESPs are pretty complex and so is the necessary boilerplate code which the drivers have implemented for you.

Re: Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Fri Oct 27, 2023 2:58 am
by ESP_Sprite
Just for clarity: Note that MicroControllers examples give you a pointer to the address, but don't reserve the address itself, you'd need linker shenanigans for that, plus possibly an adjustment to the heap allocator ranges. Generally, you can assume it's not supported. Can I ask what the reason you want to do this is? There's likely a better way to get what you want in esp-idf.

Re: Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Fri Oct 27, 2023 8:46 am
by djixon
@ESP-Sprite:
Sure, in microchip linking settings, there is an option for range of address reservation so (linker doesn't use it for anything) , for an example, if I want to reserve range (0x0600 - 0x3FFF) I have to explicitly exclude it, by setting -0x600-0x3FFF in linker parameters. That way C environment is "AWARE" that it can not be used for any kind of allocation (either stack or heap or whatever) so user can use it freely for combination of low level asm routines, data etc and be sure that C generated part of code will never try to allocate it.
So for an example, if (decreasing) re-entrant stack is allocated under that range, even flood fill will not be able to corrupt that data. Also buggy code (heap mem leakage or similar) won't be able to corrupt that data if heap is always allocated above that reserved range.

However by explicit usage of @ (on some other platforms _at_ ) address specifiers, you can access that reserved range from C environment (like it is allocated statically by C compiler). The main differences:
1. block is safer (from buggy code)
2. suitable for self-modifying code routines (for example to emulate computed GOTO or switch() in assembly etc.)
3. saves a lot of code space (suppose you have dozens C functions which differs just in one (or a few) asm instruction(s). In C you have to define all, and compiler will generate code for all of them, however self-modifying behavior of ASM and existence of such reserved block allows you to use just one "prototype" function compiled by C. At runtime, store it in such a block and do proper modifications at addresses of different instructions, because you have control over those addresses where the function will be stored, by such block reservation...)
etc.

Re: Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Fri Oct 27, 2023 9:24 am
by ESP_Sprite
djixon wrote:
Fri Oct 27, 2023 8:46 am
@ESP-Sprite:
Sure, in microchip linking settings, there is an option for range of address reservation so (linker doesn't use it for anything) , for an example, if I want to reserve range (0x0600 - 0x3FFF) I have to explicitly exclude it, by setting -0x600-0x3FFF in linker parameters. That way C environment is "AWARE" that it can not be used for any kind of allocation (either stack or heap or whatever) so user can use it freely for combination of low level asm routines, data etc and be sure that C generated part of code will never try to allocate it.
So for an example, if (decreasing) re-entrant stack is allocated under that range, even flood fill will not be able to corrupt that data. Also buggy code (heap mem leakage or similar) won't be able to corrupt that data if heap is always allocated above that reserved range.
That's a very weak protection, though. The C language itself still allows you to happily point to any random memory address and overwrite it, and bugs like use-after-free can still cause code to write to that memory. Plus generally I don't think there's that much use for a small memory region that does not get corrupted if the rest of your program can still go entirely off the rails: if you're thinking of some sort of watchdog, I'd advise using something like one of the RiscV ULPs that newer chips have for that.
2. suitable for self-modifying code routines (for example to emulate computed GOTO or switch() in assembly etc.)
3. saves a lot of code space (suppose you have dozens C functions which differs just in one (or a few) asm instruction(s). In C you have to define all, and compiler will generate code for all of them, however self-modifying behavior of ASM and existence of such reserved block allows you to use just one "prototype" function compiled by C. At runtime, store it in such a block and do proper modifications at addresses of different instructions, because you have control over those addresses where the function will be stored, by such block reservation...)
etc.
Yeah, sorry, self-modifying code is generally not a thing on the ESP32. Given that even the smallest chip comes with 2MiB of flash, and you can easily have an upgrade path to 32MiB or more of the stuff, the amount of memory saved by self-modifying code generally is simply not worth it.

Even worse: if you want to use self-modifying code, you need to defeat a security feature that most ESP32 chips have. By default, they make memory with executable code non-writable, and memory that can be written non-executable. This makes it harder to use security bugs like a buffer overflow: rather than use the buffer overflow to insert executable code into memory as well as overwrite a return pointer so the CPU will start executing the memory, an attacker now has to resort to much more complicated approaches like finding gadgets in existing code to do their thing.

Re: Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Fri Oct 27, 2023 9:37 am
by djixon
@ESP-Sprite:
Thank you for mention that code block is not writable at runtime where ever it resides. No single TRM says anything about that (at least I didn't see it). Good to know. Thx one more time.



That block reservation in RTC_FAST_RAM which exists in sdkconfig confused me suggesting that such thing might be possible.

Re: Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Fri Oct 27, 2023 2:15 pm
by MicroController
That block reservation in RTC_FAST_RAM which exists in sdkconfig confused me suggesting that such thing might be possible.
This reservation is not for protection against corruption or the like; it's more for making sure the data there survives a reboot, i.e. both the application and the bootloader need to be made aware not to use that section for other stuff.
And the "can only be accessed by the PRO-CPU" is also not an (optional) security feature but a hardware limitation for all of the RTC fast RAM.

Re: Is there a compatible syntax to a @ operator (like in Microchip C99 compiler)?

Posted: Sat Oct 28, 2023 8:03 am
by djixon
This reservation is not for protection against corruption or the like
It depends on how do you "see" a corruption. A "software guy" will see it just as an unexpected software malfunction which changed content of some memory location(s) (due to a bug in software)

However, a "hardware + software guy" will have somewhat different approach. First, he will check "pure" hardware possible reasons (like instability in power lines, validity of all voltages, noises in signal lines (lets say for PSRAM) etc. Once he is sure that everything is fine, he will continue searching a reason of corruption by asking, what parts of internal hardware design can access directly (or indirectly lets say via DMA for example) to some memory location(s). Can, lets say, an UART module or any other hardware module access to some location (without any software execution, just by hardware design). For example, if some hardware module is designed to use specific fixed range (or configurable range) of addresses in RAM (for buffers or what ever), once such module is "turned ON" that range is certainly not a good place for keeping any variables. Because, any potential misconfiguration (or incidental activation) of such a hardware module could affect content on those location(s). Also, potential bug(s) in internal hardware design, affecting such module could lead affecting data in that range. When on that already large list you add RTOS and all installed drivers to support different modules (which btw can also be potentially buggy) ... you see what I mean?

In that sense, ranges of addresses not accessible to, lets say, any hardware module, DMA etc. are somewhat safer place for global variables. In multi core designs, any improper lock or race conditions could also affect the content in the cache (which will be soon or later) reflected in a RAM. So ideal place for storing sensitive variables would be also place accessible by single core only.
That's why RTC_FAST_RAM is almost ideal for that. It can not be accessed by DMA or second APP CPU (only PRO CPU and ULP can access it).
Off course, it doesn't mean that a potentially buggy code can not store garbage in it. It only means that a potentially misconfigured module, DMA etc wont be able to "write" over your global variables.