Home > Mobile >  Multiple Threads Accessing Same Register Value ARM Assembly
Multiple Threads Accessing Same Register Value ARM Assembly

Time:04-27

I'm working with some ARM code experimenting with multiple threads which need to access the same register. I'm using C with asm calls. However, I keep running into a bus error. Here's an example of what I mean:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

int someVar = 0;

void setup(){
    __asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}

void loadAction(){
   __asm__("LDREX R1, [R7]\n\t");
}

int main(){
   setup();
   loadAction();
}

This works totally fine.

However, when I introduce threads, like this a bus error results:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

int someVar = 0;

void setup(){
    __asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}

void *loadAction(void *threadArg){
   __asm__("LDREX R1, [R7]\n\t");
}

int main(){
   pthread_t tid;
   setup();
   int i;
   for (i = 0; i < 1; i  ){
       pthread_create(&tid, NULL, loadAction, (void *)&tid);
   }
   pthread_exit(NULL);
   return 0;
}

My best guess for this issue is that the value in R7 is invalid because registers are not guaranteed to be preserved across subroutine calls. Perhaps in the first example, I'm just getting lucky and the value in R7 that is placed in setup() happens to remain, but the thread code causes the value in R7 to be clobbered.

If this is the case, is there any way that I could preserve R7? I could save and store to the stack, but multiple threads will be accessing it at once. Is there some sort of compilation flag I could pass in with gcc to ensure that the value of R7 loaded in setup() is accessed in loadAction()?

Thanks

CodePudding user response:

Every thread has its own register values. That is actually what makes a thread a thread. If two threads shared their register values (especially PC and SP) they would be the same thread.


And yes, registers generally aren't preserved across subroutine calls. The compiler uses them to store every value your code uses - they are not some special unusual thing that you can only access with inline assembly code. Depending on the calling convention being used in the program, the compiler may be obligated to save the old value of certain registers that it decides to use, and restore them back before the subroutine returns.

According to the linked Wikipedia page, on 32-bit ARM, r7 is one of those registers the compiler has to save and restore.

In this case the compiler hasn't decided to use r7 in the setup function (because there is no actual code in it that gets compiled); if setup did have a bunch of C code and the compiler decided to use r7, then it would save the old value at the beginning, load the old value at the end, use the register in the middle, and your load to r7 would overwrite whatever value the compiler thought was stored there, thus breaking the C code. And by the time loadAction ran on the same thread the old value would have been put back in r7.


There is a way to preserve a register in the C language and it's called a variable.

Instead of this:

// wrong code

void setup(){
    __asm__("LDR R7, =someVar\n\t"); // load someVar into R7
}

void *loadAction(void *threadArg){
   __asm__("LDREX R1, [R7]\n\t");
}

if you write it like this:

int *pSomeVar;

void setup(){
    pSomeVar = &someVar; // load someVar into pSomeVar
}

void *loadAction(void *threadArg){
    int value = *pSomeVar;
}

then the compiler will do whatever it takes to make sure that value gets from setup to loadAction.

  • Related