Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pointers are not processed correctly when stored in global structure #14

Open
wwwweb opened this issue Nov 14, 2016 · 9 comments
Open

Comments

@wwwweb
Copy link

wwwweb commented Nov 14, 2016

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) {};
}
@wwwweb
Copy link
Author

wwwweb commented Nov 14, 2016

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

@olofk
Copy link
Member

olofk commented Nov 14, 2016

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

@olofk
Copy link
Member

olofk commented Nov 14, 2016

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

@wwwweb
Copy link
Author

wwwweb commented Nov 15, 2016

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) {};
}

@olofk
Copy link
Member

olofk commented Nov 25, 2016

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?

@wwwweb
Copy link
Author

wwwweb commented Nov 28, 2016

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.

@olofk
Copy link
Member

olofk commented Dec 2, 2016

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.

@olofk
Copy link
Member

olofk commented Dec 2, 2016

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

@wwwweb
Copy link
Author

wwwweb commented Dec 2, 2016

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:

mainelf_files.zip

Let me know if I can further help you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants