Failed to get function pointer location

I have a C code that I am targeting for AVR. The code is compiled using avr-gcc, mainly the gnu compiler with the correct backend.

What I'm trying to do is create a callback mechanism in one of my event driven / interrupt libraries, but I seem to have problems saving the value of the function pointer.

To get started, I have a static library. It has a header file ( twi_master_driver.h ) that looks like this:

 #ifndef TWI_MASTER_DRIVER_H_ #define TWI_MASTER_DRIVER_H_ #define TWI_INPUT_QUEUE_SIZE 256 // define callback function pointer signature typedef void (*twi_slave_callback_t)(uint8_t*, uint16_t); typedef struct { uint8_t buffer[TWI_INPUT_QUEUE_SIZE]; volatile uint16_t length; // currently used bytes in the buffer twi_slave_callback_t slave_callback; } twi_global_slave_t; typedef struct { uint8_t slave_address; volatile twi_global_slave_t slave; } twi_global_t; void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback); #endif 

Now the C file ( twi_driver.c ):

 #include <stdint.h> #include "twi_master_driver.h" void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback) { twi->slave.length = 0; twi->slave.slave_callback = slave_callback; twi->slave_address = slave_address; // temporary workaround <- why does this work?? twi->slave.slave_callback = twi->slave.slave_callback; } void twi_slave_interrupt_handler(twi_global_t *twi) { (twi->slave.slave_callback)(twi->slave.buffer, twi->slave.length); // some other stuff (nothing touches twi->slave.slave_callback) } 

Then I create these two files in a static library (.a) and create my main program ( main.c ) # include # include # include # include #include "twi_master_driver.h"

 // ...define microcontroller safe way for mystdout ... twi_global_t bus_a; ISR(TWIC_TWIS_vect, ISR_NOBLOCK) { twi_slave_interrupt_handler(&bus_a); } void my_callback(uint8_t *buf, uint16_t len) { uint8_t i; fprintf(&mystdout, "C: "); for(i = 0; i < length; i++) { fprintf(&mystdout, "%d,", buf[i]); } fprintf(&mystdout, "\n"); } int main(int argc, char **argv) { twi_init(2, &bus_a, &my_callback); // ...PMIC setup... // enable interrupts. sei(); // (code that causes interrupt to fire) // spin while the rest of the application runs... while(1){ _delay_ms(1000); } return 0; } 

I carefully trigger events that cause an interrupt and call the appropriate handler. Using some fprintfs, I can say that the location assigned to twi->slave.slave_callback in the twi_init function is different from the location in the twi_slave_interrupt_handler function.

Although numbers do not make sense, in twi_init value is 0x13b, and in twi_slave_interrupt_handler when printing, the value is 0x100.

By adding a commented bypass line to twi_driver.c :

 twi->slave.slave_callback = twi->slave.slave_callback; 

The problem goes away, but this is clearly a magical and undesirable solution. What am I doing wrong?

As far as I can tell, I noted the corresponding volatile variables, and I tried labeling other volatile portions and removing volatile markings. I came up with a workaround when I noticed that after executing the twi_init task, the fprintf expressions are deleted, after which the value is read differently later.

The problem is how I look at the pointer to the function, and especially the part of the program that accesses the value of the pointer (the function itself?) Is technically in a different thread.

Any ideas?

Changes:

  • typos allowed in the code.

  • links to actual files: http://straymark.com/code/ [test.c | twi_driver.c | twi_driver.h]

  • fwiw: compiler options: -Wall -Os -fpack-struct -fshort-enums -funsigned-char -funsigned-bitfields -mmcu=atxmega128a1 -DF_CPU=2000000UL

  • I tried the same code that was included directly (and not through the library) and I have the same problem.

Editing (round 2):

  • I removed all optimizations, without my "workaround" the code works as expected. Adding back -Os causes an error. Why is -O corrupting my code?
+4
source share
3 answers

Just guess, but what happens if you switch these two lines:

 twi->slave.slave_callback = slave_callback; twi->slave.length = 0; 

Does the -fpack-struct gcc flag fix the problem? I wonder if you came across an error when writing this length field overwrites part of the callback value.


It seems to me that with the -Os optimization (you can try combinations of individual optimizations included with -Os to see exactly which one calls it), the compiler does not emit the correct code to manipulate a uint16_t length field if it is not aligned by 2-byte the border. This happens when you include twi_global_slave_t inside twi_global_t , which is packed because the original uint8_t member twi_global_t forces the twi_global_slave_t structure twi_global_slave_t be placed on an odd address.

If you make this initial field twi_global_t a uint16_t , it will probably fix it (or you can turn off structure packing). Try the latest gcc build and see if this all happens - if so, you should create a minimal test case that shows the problem, so you can send an error report to the gcc project.

+2
source

It really looks like a stack / memory corruption issue. If you run avr-size in your elf file, what will you get? Make sure (data + bss) <RAM you have in pieces. These types of problems are very difficult to track. The fact that deleting / moving unrelated code changes behavior is a big red flag.

+1
source

Replace "& my_callback" with "my_callback" in the main () function.

Since different threads access the return address, try protecting it with a mutex or read / write lock.

If the callback function pointer is not available by the signal handler, then an β€œunstable” qualifier is not needed.

0
source

All Articles