memcpy misses one byte when copying to struct

141 Views Asked by At

I am trying to extract a particular region of my message and interpret it as a struct.

void app_main(void)
{
  esp_err_t err;
  uint8_t injected_input[]={0xCE,0x33,0xE1,0x00,0x11,0x22,0x33,0x44,0x55,0x66};
  model_sensor_data_t stuff = {0};
  model_sensor_data_t* sensor_buf = &stuff;
  if (extract_sensor_data_msgA(injected_input, sensor_buf) == -1)
  {
    ESP_LOGE(TAG, "Error in extract_sensor_data_msgA");
  }
  ESP_LOGI(TAG, "extracted sensor data is 0x%12x", *sensor_buf);

}
typedef struct __attribute__((packed))
{
  uint8_t byte0;
  uint8_t byte1;
  uint8_t byte2;
  uint8_t byte3;
  uint8_t byte4;
} model_sensor_data_t;

int32_t extract_sensor_data_msgA(uint8_t *buf, model_sensor_data_t *sensor_buf)
{
  if (buf == NULL || sensor_buf == NULL)
  {
    return -1;
  }
  //do other checks, blah blah

  memcpy(sensor_buf, buf + 5, sizeof(model_sensor_data_t)); //problem lies here 
  return 0;
}


I expect to get CLIENT: extracted sensor data is 0x2233445566 but i am getting CLIENT: extracted sensor data is 0x 55443322

It seems to me there are two problems i need to fix. First one is the endianness issue as the extracted values are all flipped. The second problem is the memcpy with padding(?) concern. I thought the second problem would be fixed if i use attribute((packed)) but it doesn't seem to fix the second problem. Any kind soul can provide an alternative way for me to go about this so as to resolve it? I have referred to https://electronics.stackexchange.com/questions/617711/problems-casting-a-uint8-t-array-to-a-struct and C memcpy copies bytes with little endianness but i am still unsure how to resolve the issue.

1

There are 1 best solutions below

0
On BEST ANSWER
ESP_LOGI(TAG, "extracted sensor data is 0x%12x", *sensor_buf)

assuming this is going to a printf-family function (seems likely), it will be expecting a unsigned int as the argument, but you're passing a model_sensor_data_t, so you get undefined behavior.

What is probably happening is that an unsigned int is a 32-bit little-endian value being accessed in the bottom 32 bits of a register, while your calling convention will pass the model_sensor_data_t in a 64-bit register, so you're seeing the first 4 bytes as a little-endian unsigned. Alternately, printf is expecting a 32-bit value on the stack, and you are passing a 40-bit value (probably padded out to 8 bytes for alignment). Either way, it seems almost certain you're using a little-endian machine, such as an x86 of some flavor.

To print this properly, you need to print each byte. Something like

ESP_LOGI(TAG, "extracted sensor data is 0x%02x%02x%02x%02x%02x", sensor_buf->byte0,
         sensor_buf->byte1, sensor_buf->byte2, sensor_buf->byte3, sensor_buf->byte4);

will print the extracted data as a 40-bit big-endian hex value.