Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC - Struct defining members in specific offsets

Tags:

c++

c

gcc

Is there a way in gcc, where I could define a struct with a specific member in a specific offset?

I want to define a struct in the following way:

struct {
.offset(0xDC) //or something equivalent
   int bar;
} foo;

and then the following statement:

int a = foo.bar

will be identical to the statement:

int a = *(int*)((char*)&foo + 0xDC);

* UPDATE *

Some background: I want to access members in an exported struct which I have no proper definition, it has many members - which I care about only a handful of them, and their offset (the struct original definition) is a bit different on each target platform (I need to compile my code for a few different platforms)

I have considered the padding option mentioned on the comments here, but it requires me to do some annoying calculations each time I want to add a member. for example:

strcut {
.offset(0xDC)
    int bar;
.offset(0xF4)
    int moo;
}foo;

is simpler then:

struct __attribute__ ((__packed__)) struct {
   char pad1[0xD8];
   int bar;
   char pad2[0x18];
   int moo;
}foo;

and that without taking into consideration the fact the sizeof(int) can change from platform to platform

like image 242
t_z Avatar asked Nov 23 '11 08:11

t_z


5 Answers

You should have a look at __attribute__ ((__packed__)).

In your case you would write:

struct __attribute__ ((__packed__)) {
   char unused[0xDC];
   int bar;
} foo;

If you could explain what you're trying to do there may be other, possibly more elegant, solutions.

like image 73
INS Avatar answered Nov 08 '22 14:11

INS


A solution using padding and union as proposed in other answers:

#include <stdio.h>

#define OFF_STRUCT(name, members) union { members } name

#define OFF_MEMB(offset, member)                 \
        struct __attribute__ ((__packed__)) {    \
                char pad[offset];                \
                member;                          \
        }

int main(int argc, char *argv[])
{
        OFF_STRUCT(foo,
                OFF_MEMB(0xD8, int bar);
                OFF_MEMB(0x18, double moo);
                OFF_MEMB(0x1, int bee);
        );

        printf("offset: 0x%x 0x%x 0x%x\n",
                (void*)&foo.bar - (void*)&foo,
                (void*)&foo.moo - (void*)&foo,
                (void*)&foo.bee - (void*)&foo
        );
}

Output:

offset: 0xd8 0x18 0x1
like image 39
calandoa Avatar answered Nov 08 '22 14:11

calandoa


You can always pad it with bytes and make sure that you tell gcc not to align your structs (as this may throw off the offsets). In that case you would need a member like char pad_bytes[num_pad_bytes];. Although is there a reason to really do this? You can always calculate the offset of a member of a struct by doing some pointer arithmetic. Note: you may want to use the uint8_t type to pad instead of char as some compilers may actually pad char (which is usually a byte) to the size of a word.

Calculating offset is as simple as

size_t offset = (size_t)&((struct_type *)0)->member);

All this does is simply return a pointer to where member would be in struct_type if the struct were at 0x00 in memory (which it can never be) but since we use 0 as a base then the offset is simply the reference returned by the & operator.

like image 2
Jesus Ramos Avatar answered Nov 08 '22 15:11

Jesus Ramos


I'd write a class that wraps a pointer:

class whateverPtr {
    unsigned char *p;

public:
    whateverPtr(void *p) : p(reinterpret_cast<unsigned char *>(p) { }

    uint32_t getBar() const { return read_uint32(0xdc); }

private:
    uint32_t read_uint32(unsigned int offset) {
        return p[offset] |
            (p[offset + 1] << 8) |
            (p[offset + 2] << 16) |
            (p[offset + 3] << 24);
    }
};

This is fully portable and will work as expected on bigendian architectures. Signed integers are a bit trickier as you need to get the encoding right.

like image 1
Simon Richter Avatar answered Nov 08 '22 14:11

Simon Richter


I have considered the padding option mentioned on the comments here, but it requires me to do some annoying calculations each time I want to add a member.

Regarding annoying calculations, you can just generate the declaration of your struct using your preferred scripting language:

struct = { 0xdc : (4, 'int bar'),
           0xf4 : (4, 'int moo') }

def print_struct_decl (name, decl):
        print "struct __attribute__ ((packed)) %s {" % name
        off = 0
        i = 0;
        for o in sorted (decl.keys()):
                print "\tchar pad%d [%d];" % (i, o - off)
                i = i + 1
                off = off + o + decl[o][0]
                print "\t%s;" % decl[o][1]
        print "};"

print_struct_decl ("whatever", struct)

Outputs:

struct __attribute__ ((packed)) whatever {
    char pad0 [220];
    int bar;
    char pad1 [20];
    int moo;
};
like image 2
chill Avatar answered Nov 08 '22 14:11

chill