On Wednesday, 16 June 2021 at 21:42:41 UTC, Ali Çehreli wrote:
> On 6/16/21 8:47 AM, Doeme wrote:
> On Wednesday, 16 June 2021 at 13:36:07 UTC, Ali Çehreli wrote:
> On 6/16/21 2:27 AM, Doeme wrote:
> How does one get the address of a struct member?
Here is an experiment with offsetof and opDispatch:
Cool stuff!
I actually tried a very similar approach once, but it did not
work out,
since the D compilers refuse to do pointer arithmetic at
compile time :/
struct Foo{
ubyte bar;
}
void* membaddr(void *ptr, ulong offset){
return ptr+offset;
}
__gshared Foo foo;
void* bar = membaddr(&foo, foo.bar.offsetof);
//Error: cannot perform arithmetic on `void*` pointers at
compile time
>
I guess that the opDispatch-method will suffer from the same
issue...
No, opDispatch does not work either for compile time addresses.
Actually, it is news to me that the compiler can know (determine?) the address of a global variable. I thought the loador would determine the addresses, but apparently not. Is it really a constant compiled value in the case of C? Can you show an example please?
Ali
The compiler can, in deed, not know the address, but the linker can.
Example:
#include <stdio.h>
struct Foo{
int bar;
int baz;
};
struct Foo foo;
static const void *fooptr = &foo;
static const void *barptr = &foo.bar;
static const void *bazptr = &foo.baz;
int main(int argc, char **argv){
printf("Address of foo: %p\n", fooptr); //Address of foo: 0x55fbd8292030
printf("Address of bar: %p\n", barptr); //Address of bar: 0x55fbd8292030
printf("Address of bau: %p\n", bazptr); //Address of bau: 0x55fbd8292034
return 0;
}
We can see that the code actually outputs the right addresses.
If we investigate the object file passed down to the linker, we see:
$ gcc -c test.c
$ objdump -x test.o
[...]
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data.rel.local 0000000000000000 .data.rel.local
0000000000000000 l O .data.rel.local 0000000000000008 fooptr
0000000000000008 l O .data.rel.local 0000000000000008 barptr
0000000000000010 l O .data.rel.local 0000000000000008 bazptr
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 g O .bss 0000000000000008 foo
0000000000000000 g F .text 0000000000000070 main
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 *UND* 0000000000000000 printf
[...]
RELOCATION RECORDS FOR [.data.rel.local]:
OFFSET TYPE VALUE
0000000000000000 R_X86_64_64 foo
0000000000000008 R_X86_64_64 foo
0000000000000010 R_X86_64_64 foo+0x0000000000000004
[...]
This tells us that:
- There are 3 variables in the initialized .data.rel.local section, our pointers.
- There is one variable in the zeroed .bss section, our instance of struct Foo
- To the positions of the of our three pointers in the .data.rel.local section, there is being written the address of the symbol foo, foo, and lastly, foo+4.
Thus, the address is only known at link time, but it can be known by placing the right relocation commands to the object elf-file (i.e. relocation + offset, foo+4).