Search code examples
gcclinkerelfmemory-alignmentpowerpc

SegFault due to an extended 64 bit variable


I'm trying to fix an issue after extending a 32 bit global variable to a 64 bit one. unfortunately the program crashes with that extended 64 bits. When I compared the generated outputs elfdump.txt, for both : the generated elfdump.txt before and after the global variable extension. I discovered that the program headers are now 6 rather than 5 with that extended 64 bit variable, while they remain the same when the application is running fine with that 32 bit global variable.

32 bit global variable output:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x010000 0x00010000 0x00010000 0x34920 0x34920 R E 0x10000
  LOAD           0x054010 0x40004010 0x00044920 0x0144f 0x0144f RW  0x10000
  LOAD           0x055460 0x40005460 0x00045d6f 0x0000c 0x0000c RW  0x10000
  LOAD           0x060000 0x00080000 0x00080000 0x000b0 0x000b0 R   0x10000
  LOAD           0x06546c 0x4000546c 0x4000546c 0x00000 0x04c80 RW  0x10000
 Section to Segment mapping:
  Segment Sections...
   00     .rcw .init .FlashProgram .FlashErase .FlashDriver .text .flash_data .rodata .isrvectbl .xcptn 
   **01**     .backupram .adapdata **.data** 
   02     .ctors 
   03     calconst 
   04     .bss 

64 bit global variable output:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x010000 0x00010000 0x00010000 0x34920 0x34920 R E 0x10000
  LOAD           0x054010 0x40004010 0x00044920 0x0003c 0x0003c RW  0x10000
  LOAD           0x054050 0x40004050 0x0004495c 0x013db 0x013db RW  0x10000
  LOAD           0x05542c 0x4000542c 0x00045d37 0x0000c 0x0000c RW  0x10000
  LOAD           0x060000 0x00080000 0x00080000 0x000b0 0x000b0 R   0x10000
  LOAD           0x065438 0x40005438 0x40005438 0x00000 0x04c7c RW  0x10000

the second LOAD has 0x0003c FileSiz and 0x0003 of cMemSiz which is wrongly mapped. and the following LOAD is an extra LOAD by result.

 Section to Segment mapping:
  Segment Sections...
   00     .rcw .init .FlashProgram .FlashErase .FlashDriver .text .flash_data .rodata .isrvectbl .xcptn 
   01     .backupram .adapdata 
   **02     .data** 
   03     .ctors 
   04     calconst 
   05     .bss 

segment section 01 has been divided into two segments 01 and 02.


Updates

Initialized global/static variables values get allocated in .data segment it turned out that I have an alignment problem, in the beginning, the data section was 4 byte aligned, starting from the address 0x4000404c :

  • [20] .data PROGBITS 4000404c 05404c 001413 00 WA 0 0 4

and then when I changed that variable,the alignment has became 8.

  • [20] .data PROGBITS 40004050 054050 00141b 00 WA 0 0 8

Then the .data segment address (0x4000404c) has been shifted by 4 bytes, because of the alignment(0x40004050 mod 8 = 0 )

I'm using the compiler version gcc 8.1.0 , and visual studio 2005 IDE, and an MPC5644A MCU, Power architecture.


Solution

  • This is an answer to my own question. I will write some updates as well and please correct me if I’m saying something wrong.

    The variable before was a 32 bit variable.

    unsigned int Var1 = 0;
    
    void function (unsigned int var2 )
    {
       Var1 |= var2;
    }
    

    And then I extended it:

    unsigned long long Var1 = 0x0ULL;
    
    void function (unsigned long long var2 )
    {
       Var1 |= var2;
    }
    

    Alternatively, the host can read/write the SPRAM by 8-, 16-, or 32-bit accesses in aligned addresses. (Section 24.5.2.3 Parameter access, reference manual MPC5644ARM).

    While the compiler automatically sets the type alignment to the largest alignment that can ever be used for any data type on the target machine for which you are compiling, which is our extended variable (8 bytes) boundary, that's why during the compilation, 4 padding bytes were required to align the start of the data segment to perform that 8 bytes alignment. (this is my understanding).

    My conclusion:

    The (initialized) global data variables are almost by definition in the data section, since the alignment was 4 bytes in the data section, and the VA is 4 bytes aligned, on the other hand, the host can read any 32 bits variable, and everything worked fine. Please find attached a screenshot:

    Section Header when the variable was 32 bit

    So the alignment was 4 boundaries.

    Program Header when the variable was 32 bit

    You can see that the small data sections are gathered together in the program header so single-instruction offsets can access them all.

    Then we extended that variable

    Section Header with extended 64 bit global variable

    When we extended that global variable, that small data segment split into two, and I found that the VA of data section changed. a 4 bytes padding has been performed by the compiler to ensure the new alignment (8 bytes boundaries).

    Header Section with 64 global variable

    The compiler automatically sets the type alignment to the largest it can ever use for any data type.

    That's why that small data segment split into two:

    Header Program with 64 global variable

    And then I tried to force that section to be 4 bytes aligned, to ensure that the access to that variable would be fine and no exception will occur, exactly like you said.

    .data ALIGN (4): AT (NEXT_LOAD_ADDR)
    {
    __DATASTART = . ;
    *(.data)
    *(.data.*)
    *(.gnu.linkonce.d*)
    CONSTRUCTORS
    } > int_sram
    NEXT_LOAD_ADDR = NEXT_LOAD_ADDR + SIZEOF(.data);
    

    And the bug has been fixed:

    Section Header with the alignment fix Program Header with the alignment fix

    The size of the data section has also been increased by 4 bytes, 1413 - 4 (32 bits global variable) + 8 (64 bits global variable) = 1417 bytes