Pointers are not processed correctly when stored in global structure
Opened this issue · 9 comments
Hi,
Following issue occurs on a mor1kx but I think it may be a toolchain problem.
When defining a byte array and casting parts of the byte array to a DWORD, the address of the first byte of the DWORD is probably not DWORD aligned (i.e. &byte_array[1] = 0x00000001 ). Normally, this is no problem and the DWORD pointer can be dereferenced correctly (i.e. p_u32 = (uint32_t *)&byte_array[1]; a=*p_u32;). When storing the DWORD pointer in a global structure, this does not work, the program simply hangs up when trying to dereference the pointer stored in the global struct. Please find below a program to show the problem. The program works fine compiled with standard gcc on a Linux machine but hangs up in Modelsim simulation when compiled with or1k-elf-gcc.
I am not sure to which branch of Openrisc development it belongs but it may be a toolchain problem as the dereferencing in general works when using the local struct. Also I am not sure if this is already fixed as I use an older (~1 year) version of mor1kx and toolchain.
Best regards,
Markus
#include <stdint.h>
#include <stdio.h>
//the structure contains a byte pointer and a DWORD pointer
typedef struct
{
uint8_t * p_struct_u8;
uint32_t * p_struct_u32;
} struct_pointers;
//the structure itself as global variable; does not work!
struct_pointers sp; //comment this line and uncomment the local struct definition to work correctly
int main(int argc, char *argv[])
{
//10 byte array
uint8_t v_array_u8 [10] = {1,2,3,4,5,6,7,8,9,10};
//pointer to byte
uint8_t * p_u8;
//pointer to DWORD
uint32_t * p_u32;
// struct_pointers sp; //the structure as local variable does work; uncomment this line and comment the global struct definition to work correctly
//point to first byte (DWORD aligned pointer address)
p_u8 = &v_array_u8[0];
p_u32 = (uint32_t *)&v_array_u8[0];
//store the pointers in the struct
sp.p_struct_u8 = p_u8;
sp.p_struct_u32 = p_u32;
printf("Addresses of pointers without struct DWORD aligned \n\r");
printf("p8: %#010x \n\r", (unsigned int)p_u8);
printf("p32: %#010x \n\r", (unsigned int)p_u32);
printf("Dereferenced pointers without struct \n\r");
printf("*p8: %#010x \n\r", (unsigned int)*p_u8);
printf("*p32: %#010x \n\r", (unsigned int)*p_u32);
printf("Addresses of pointers stored in struct DWORD aligned\n\r");
printf("sp8: %#010x \n\r", (unsigned int)sp.p_struct_u8);
printf("sp32: %#010x \n\r", (unsigned int)sp.p_struct_u32);
printf("Dereferenced pointers from struct \n\r");
printf("*sp8: %#010x \n\r", (unsigned int)*sp.p_struct_u8);
printf("*sp32: %#010x \n\r", (unsigned int)*sp.p_struct_u32);
//point to second byte (not DWORD aligned pointer address)
p_u8 = &v_array_u8[1];
p_u32 = (uint32_t *)&v_array_u8[1];
//store the pointers in the struct
sp.p_struct_u8 = p_u8;
sp.p_struct_u32 = p_u32;
printf("Addresses of pointers without struct not DWORD aligned \n\r");
printf("p8: %#010x \n\r", (unsigned int)p_u8);
printf("p32: %#010x \n\r", (unsigned int)p_u32);
printf("Dereferenced pointers without struct \n\r");
printf("*p8: %#010x \n\r", (unsigned int)*p_u8);
printf("*p32: %#010x \n\r", (unsigned int)*p_u32);
printf("Addresses of pointers stored in struct not DWORD aligned\n\r");
printf("sp8: %#010x \n\r", (unsigned int)sp.p_struct_u8);
printf("sp32: %#010x \n\r", (unsigned int)sp.p_struct_u32);
printf("Dereferenced pointers from struct \n\r");
printf("*sp8: %#010x \n\r", (unsigned int)*sp.p_struct_u8);
printf("*sp32: %#010x \n\r", (unsigned int)*sp.p_struct_u32); //hangs up here when dereferencing non DWORD aligned address
printf("Successfully finished! \n\r");
while(1) {};
}
Hi again,
just found out that this problem seems not related to struct but to global pointers in general. So if you just leave out the struct but define a global pointer p_u32 you get the same issue without using any struct.
Best regards,
Markus
ok... this is even stranger in my case. I tried to run it both with or1ksim and using icarus and the mor1kx-generic system. In both cases it exists after printing
Addresses of pointers without struct DWORD aligned
p8: 0x007fdfe4
p32: 0x007fdfe4
Dereferenced pointers without struct
*p8: 0x00000001
*p32: 0x01020304
Addresses of pointers stored in struct DWORD aligned
sp8: 0x007fdfe4
sp32: 0x007fdfe4
Dereferenced pointers from struct
*sp8: 0x00000001
*sp32: 0x01020304
Addresses of pointers without struct not DWORD aligned
p8: 0x007fdfe5
p32: 0x007fdfe5
Dereferenced pointers without struct
*p8: 0x00000002
No hanging. No errors. Nothing. Haven't looked at the traces yet though
Which toolchain are you using btw? I assumed newlib (or1k-elf) and in my case, there is an alignment exception that causes the program to exit when it tries to load (unsigned int)*p_u32
in preparation for the next printf
Thanks that you tried and confirmed the issue. Yes, I am using newlib. Modelsim does not exit but keeps running the hardware sim. In the wave there is almost no activity any more except the program counter toggling between two values.
Just to emphasize for someone who also wants to try: it seems no problem with the struct or the printf or the (unsigned int)-cast for the printf but with the global pointer access only. A more minimalistic program to show the problem is
#include <stdint.h>
#include <stdio.h>
//global DWORD pointer
uint32_t * p_u32;
int main(int argc, char *argv[])
{
//10 byte array
uint8_t v_array_u8 [10] = {1,2,3,4,5,6,7,8,9,10};
//dummy var, volatile to avoid optimization
uint32_t volatile a;
//pointer assignment
p_u32 = (uint32_t *)&v_array_u8[1];
printf("Starting alignment test... \n\r");
//the problematic operation
a = *p_u32;
printf("If you can see this line, the issue is solved! \n\r");
while(1) {};
}
Sorry for the delay. Forgot about this one.
I think the last example is actually illegal, as you're doing an unaligned word load. I see that the CPU (correctly) jumps to 0x600 which is the exception handler for unaligned accesses. The exception handler itself doesn't do anything clever to handle align exception, and just jumps to exit to terminate the program instead. This is the easiest thing to do, but it doesn't provide much information to the user. There are some other ways this could be done instead.
We could print out an error message with the address of the unaligned access. This is what we do in the linux kernel. We could also try to load the word byte by byte in the exception handler and this would fix the problem. I'm not sure however if there are any other implications to this that I'm unaware of, and someone would need to write the code for it.
I'm still a bit surprised however that the simulation just keeps running. I would have assumed that mor1kx-monitor should have caught this and stopped the simulation. Are you running this through FuseSoC, and if so, which system are you running?
Thanks for your response but I do not agree ;-) . Why should this last example be illegal?
It WORKS on openrisc when the pointer is local (i.e. moved into the main function). Then I can printf the pointer address and the result, see that the address LSBit is '1' (so not DWORD aligned) and get the right result (0x02030405). So I assume the code for the byte access is already there somewhere but it is not used when the pointer is global. (Does the compiler have other possibilities to optimize the local pointer which are not available to the global one?)
It also WORKS on Linux when compiling the example for a (64 bit Debian) Linux machine with "standard" gcc, no matter if global or local pointer. (Do you mean this with Linux kernel?)
I do not use FuseSoc but orpsoc only with some hardware accelerators plugged to the Wishbone interface. The simulation keeps running as it is a Modelsim hardware simulation which does not care if the openrisc state machine comes to an exception state but continues simulating the hardware.
ok, this is strange. I get an unaligned exception even if I move the pointer into the function. Maybe it's something that changed in gcc/binutils? Which versions are you using?
I would still argue that this is not valid code, but let's push that aside for now to instead figure out why this works with local pointers :)
With the Linux kernel, I mean a program compiled with one of the Linux toolchains (e.g. or1k-linux-musl), running under Linux on an OpenRISC CPU.
It would also be great if you could send me the elf files for the shorter example, with both local and global pointers so I can take a look at the disassembly
This is strange indeed.
My or1k-elf-gcc version is 4.9.2 .
My or1k-elf-ld version is 2.24.51.20130930 .
I added the two elf files to the following zip:
Let me know if I can further help you.