1

For example, I have the following structure,

struct thread {
    char *pName;
    ...        // number of members between may be different for different configurations
    int member;
    ...
};

For different configurations, there may be different members between pName and member, so member's offset is not the same for different configurations. And I want to use member's offset in assembly code, but don't want to hard-code it according to different configurations. I have tried to define the following C style macro, but GCC assembler doesn't recognize it,

#define OFFSET (&(((struct thread*)0)->member))

Is there any other way to do that?

Wanghz
  • 305
  • 2
  • 12
  • 2
    You need to know the ABI already to write asm by hand; there's no way for an assembler to get it on the fly from a C source file. You could write a C program that prints `.equ` definitions for struct offsets, which your `.S` files could `.include` or `#include`. – Peter Cordes Dec 13 '21 at 08:40
  • 1
    If there are different configurations, just pass the address of the member itself, from the C code. Then the assembly code doesn't need to know about the containing `struct`. – Weather Vane Dec 13 '21 at 08:50
  • 1
    In C, you can find the offset of members using the `offsetof` macro defined in `stddef.h`. There is no way to directly do the same thing in assembly as assemblers do not have the notion of “structures.” – fuz Dec 13 '21 at 09:38
  • @WeatherVane That's kind of a way. But in my situation, I actually want to pass the base address of the structure to assembly, and access multiple members according to their offsets. As you suggested, too many arguments need to be passed in. It's not quite a good idea. – Wanghz Dec 14 '21 at 01:02

2 Answers2

1

You can write a script that is invoked as a build step to create the file offsets.h.

The biggest difficulty with this is extracting the structure names and member names from the header files. The example below uses a very simple header file and a very simple awk script to get the names, just to show the idea. A real implementation of this would be dependent on how complex the header files are and how robust your parser needs to be. If the struct definitions in your header files have a very consistent layout, then it might not be much more complex than this example.

This is a complete and fully tested example.

Makefile

a.out: main.o asm.o
        cc main.o asm.o

demo_offsets.h: demo.h
        sh make_offsets.sh $<

main.o : demo.h
asm.o: demo_offsets.h

make_offsets.sh

set -e
tmp=make_tmp$$

cat << EOF > $tmp.c
#include <stdio.h>
#include <stddef.h>
#include "$1"
int main()
{
`awk -f awkfile $1`
}
EOF
cc -o $tmp $tmp.c
$tmp > $(basename ${1} .h)_offsets.h
rm $tmp.c $tmp

awkfile

/struct/ { s=$2 }
/[A-Za-z_];/ { m=gensub(".*([A-Za-z_]*);", "\\1", 1); printf "printf(\"#define %s_%s_offset %%lu\\n\", offsetof(struct %s, %s));\n", s, m, s, m; }

demo.h

struct demo {
    int x;
    int y;
};

main.c

#include "demo.h"

extern int f(const struct demo *);

int main()
{
    struct demo s = { 1, 2 };
    return f(&s);
}

asm.S

#include "demo_offsets.h"

.global f
f:
    mov demo_x_offset(%rdi), %eax
    add demo_y_offset(%rdi), %eax
    ret
prl
  • 11,716
  • 2
  • 13
  • 31
0

Your macro is the standard way to achieve what you need. The offsetof (that was suggested in the comments) does the same trick. Unfortunately it doesn't translate into a compile-time constant. It's a shame that the compiler doesn't provide the needed means for this.

So, you may try the following:

(1) Hard-code the offset (yes, that'd be different number for different configurations), and then write a sort of a unit-test that verifies its correctness, preferably during the build-time.

(2) You may rewrite your struct definition in the following manner:

   struct thread_base {
      char *pName;
      // ... more members added here ...
   };

   struct thread :public thread_base {
      int member;
   };

Then sizeof(thread_base) would practically be what you need, however it may be affected by padding. If sizeof(thread_base) comes-out a multiple of the assumed struct member alignment, then you are good. Otherwise you may adjust it accordingly, it's possible to achieve the compile-time constant expression.

valdo
  • 12,632
  • 2
  • 37
  • 67
  • 2
    Note that the macro OP provided is subject to undefined behaviour (attempting to perfom arithmetic on a pointer that does not point to an object). For this reason, the `offsetof` macro is commonly implemented using a built in function instead of pointer arithmetic on modern compilers. – fuz Dec 13 '21 at 12:20
  • This is a novel idea. But the structure in my case is defined by an RTOS, and if the definition changes, there will be a lot of work to do with the source code. – Wanghz Dec 14 '21 at 01:07