I am facing a weird issue. I am passing a uint64_t
offset to a function on a 32-bit architecture(Cortex R52). The registers are all 32-bit.
This is the function. :
void read_func(uint8_t* read_buffer_ptr, uint64_t offset, sizeof(read_buffer)
main(){
// read_buffer : memory where something is read.
// read_buffer_ptr : points to read_buffer structure where value should be stored after reading value.
read_func(read_buffer_ptr, 0, sizeof(read_buffer);
}
In this function, the value stored in offset
is not zero but some random values which I also see in the registers(r5, r6
). Also, when I use offset
as a 32-bit value, it works perfectly fine.
Can you please let me know why this could be happening? Are registers not enough?
CodePudding user response:
- Cortex-R52 has 32 bits address bus and offset cannot be 64 bits. In calculations only lower 32bits will be used as higher ones will not have any effect.
example:
uint64_t foo(void *buff, uint64_t offset, uint64_t size)
{
unsigned char *cbuff = buff;
while(size--)
{
*(cbuff offset) = size & 0xff;
}
return offset (uint32_t)cbuff;
}
void *z1(void);
uint64_t z2(void);
uint64_t z3(void);
uint64_t bar(void)
{
return foo(z1(), z2(), z3());
}
foo:
push {r4, lr}
ldr lr, [sp, #8] //size
ldr r1, [sp, #12] //size
mov ip, lr
add r4, r0, r2 // cbuff offset calculation r3 is ignored as not needed - processor has only 32bits address space.
.L2:
subs ip, ip, #1 //size--
sbc r1, r1, #0 //size--
cmn r1, #1
cmneq ip, #1
bne .L3
add r0, r0, lr
adds r0, r0, r2
adc r1, r3, #0
pop {r4, pc}
.L3:
strb ip, [r4], #1
b .L2
bar:
push {r0, r1, r4, r5, r6, lr}
bl z1
mov r4, r0 // buff
bl z2
mov r6, r0 // offset
mov r5, r1 // offset
bl z3
mov r2, r6 // offset
strd r0, [sp] // size passed on the stack
mov r3, r5 // offset
mov r0, r4 // buff
bl foo
add sp, sp, #8
pop {r4, r5, r6, pc}
As you see resister r2 & r3 contain the offset, r0 - buff and size is on the stack.
CodePudding user response:
The prototype posted is invalid, it should be:
void read_func(uint8_t *read_buffer_ptr, uint64_t offset, size_t size);
What happens when you pass a 64-bit argument on a 32-bit architecture is implementation defined:
- either 8 bytes of stack space are used to pass the value
- or 2 32-bit registers are used to pass the least significant part and the most significant part
- or a combination of both depending on the number of arguments
- or some other scheme appropriate for the target CPU
In your code you pass 0
which has type int
and presumably has only 32 bits. This is not a problem if the prototype for read_func
was correct and parsed before the function call, otherwise the behavior is undefined and a C99 compiler should not even compile the code, but may compilers will just issue a warning and generate bogus code.