Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I force the program to use unaligned addresses?

  1. I've heard reads and writes of aligned int's are atomic and safe, I wonder when does the system make non malloc'd globals unaligned other than packed structures and casting/pointer arithmetic byte buffers?

  2. [X86-64 linux] In all of my normal cases, the system always chooses integer locations that don't get word torn, for example, two byte on one word and the other two bytes on the other word. Can any one post a program/snip (C or assembly) that forces the global variable to unaligned address such that the integer gets torn and the system has to use two reads to load one integer value ?

    When I print the below program, the addresses are close to each other such that multiple variables are within 64bits but never once word tearing is seen (smartness in the system or compiler ?)

    #include <stdio.h>
    int a;
    char  b;
    char c;
    int      d;
    int e = 0;
    
    
    int isaligned(void *p, int N)
    {
        if (((int)p % N) == 0)
            return 1;
        else
            return 0;
    }
    
    int main()
    {
    
        printf("processor is %d byte mode \n", sizeof(int *));
        printf ( "a=%p/b=%p/c=%p/d=%p/f=%p\n", &a, &b, &c, &d, &e );
    
        printf ( " check for 64bit alignment of test result of 0x80 = %d \n", isaligned( 0x80, 64 ));
        printf ( " check for 64bit alignment of a result = %d \n", isaligned( &a, 64 ));
        printf ( " check for 64bit alignment of d  result = %d \n", isaligned( &e, 64 ));
    
    return 0;}
    

    Output:

    processor is 8 byte mode 
    a=0x601038/b=0x60103c/c=0x60103d/d=0x601034/f=0x601030
     check for 64bit alignment of test result of 0x80 = 1 
     check for 64bit alignment of a result = 0 
     check for 64bit alignment of d  result = 0 
    
  3. How does a read of a char happen in the above case ? Does it read from 8 byte aligned boundary (in my case 0x601030 ) and then go to 0x60103c ?

  4. Memory access granularity is always word size isn't it ?

Thx.

like image 847
resultsway Avatar asked Feb 18 '23 16:02

resultsway


2 Answers

1) Yes, there is no guarantee that unaligned accesses are atomic, because [at least sometimes, on certain types of processors] the data may be written as two separate writes - for example if you cross over a memory page boundary [I'm not talking about 4KB pages for virtual memory, I'm talking about DDR2/3/4 pages, which is some fraction of the total memory size, typically 16Kbits times whatever the width is of the actual memory chip - which will vary depending on the memory stick itself]. Equally, on other processors than x86, you get a trap for reading unaligned memory, which would either cause the program to abort, or the read be emulated in software as multiple reads to "fix" the unaligned read.

2) You could always make an unaligned memory region by something like this:

char *ptr = malloc(sizeof(long long) * number+1);
long long *unaligned = (long long *)&ptr[2];

for(i = 0; i < number; i++)
   temp = unaligned[i]; 

By the way, your alignment check checks if the address is aligned to 64 bytes, not 64 bits. You'll have to divide by 8 to check that it's aligned to 64 bits.

3) A char is a single byte read, and the address will be on the actual address of the byte itself. The actual memory read performed is probably for a full cache-line, starting at the target address, and then cycling around, so for example:

0x60103d is the target address, so the processor will read a cache line of 32 bytes, starting at the 64-bit word we want: 0x601038 (and as soon as that's completed the processor goes on to the next instruction - meanwhile the next read will be performed to fill the cacheline), then cacheline is filled with 0x601020, 0x601028, 0x601030. But should we turn the cache off [if you want your 3GHz latest x86 processor to be slightly slower than a 66MHz 486, disabling the cache is a good way to achieve that], the processor would just read one byte at 0x60103d.

4) Not on x86 processors, they have byte addressing - but for normal memory, reads are done on a cacheline basis, as explained above.

Note also that "may not be atomic" is not at all the same as "will not be atomic" - so you'll probably have a hard time making it go wrong by will - you really need to get all the timings of two different threads just right, and straddle cachelines, straddle memory page boundaries, and so on to make it go wrong - this will happen if you don't want it to happen, but trying to make it go wrong can be darn hard [trust me, I've been there, done that].

like image 159
Mats Petersson Avatar answered Feb 20 '23 12:02

Mats Petersson


  1. It probably doesn't, outside of those cases.

  2. In assembly it's trivial. Something like:

         .org 0x2
    myglobal:
         .word SOME_NUMBER
    

    But on Intel, the processor can safely read unaligned memory. It might not be atomic, but that might not be apparent from the generated code.

  3. Intel, right? The Intel ISA has single-byte read/write opcodes. Disassemble your program and see what it's using.

  4. Not necessarily - you might have a mismatch between memory word size and processor word size.

like image 26
Carl Norum Avatar answered Feb 20 '23 10:02

Carl Norum