tw39124
tw39124

Reputation: 9203

Accessing linker defined symbol in C

I have a linker symbol (let's call it __BASE_ADDR), defined in a linker command script, which contains the address of the longword after a longword that I need to use (e.g. I need to check longword at address 0x0000000C and __BASE_ADDR is equal to 0x00000010). So in the code I try to use __BASE_ADDR and subtract 4 from it to access 0x0000000C.

My linker map output correctly shows the value of __BASE_ADDR to be 0x00000010. However when I try to access the location using the following C code it doesn't work:

extern unsigned char *__BASE_ADDR;

void someFunc( void )
{
    unsigned long Val = *((unsigned long *) (__BASE_ADDR - 4));

    /* Use Val for some comparison...*/
    /* ... */
}

Val gets set to the wrong value. What appears to be happening is that whatever longword is at __BASE_ADDR is taken as an address, 4 is subtracted, and the contents of the resulting address is put into Val.

But when I do the following, it does work fine and Val is set correctly to whatever resides at address 0x0000000C:

extern unsigned char __BASE_ADDR[];

void someFunc( void )
{
    unsigned long Val = *((unsigned long *) (__BASE_ADDR - 4));

    /* Use Val for some comparison...*/
    /* ... */
}

Can anyone shed light on this? I understand why you can't access a linker symbol's contents (it technically has no contents), but why would there be a difference between accessing the linker symbol as a pointer and accessing it as an array?

Thanks.

Upvotes: 4

Views: 7261

Answers (1)

Daniel Fischer
Daniel Fischer

Reputation: 183888

(e.g. I need to check longword at address 0x0000000C and __BASE_ADDR is equal to 0x00000010)

The symptoms say that 0x00000010 is not the value of __BASE_ADDR, but the address.

So

unsigned long Val = *((unsigned long *) (__BASE_ADDR - 4));

does indeed read the value at 0x00000010 (obtaining the value of __BASE_ADDR), subtracts four from it and interprets the result as the address of the unsigned long to store in val.

With

extern unsigned char __BASE_ADDR[];

the address of the array __BASEADDR is the same as the address of the array's first element, so in that case,

unsigned long Val = *((unsigned long *) (__BASE_ADDR - 4));
  • converts the array name __BASE_ADDR to a pointer to its first element, and the address that results in is the address of __BASE_ADDR (just a different type), 0x00000010;
  • then 4 is subtracted from that unsigned char*, resulting in the address 0x0000000C;
  • then that address is cast to unsigned long*, dereferenced and the value located there stored in val.

(The last point very probably invokes undefined behaviour, but you may be in a situation where you know it works, if you deal with fixed addresses.)

Upvotes: 5

Related Questions