1

By looking at this answer: https://stackoverflow.com/a/4671482/1770034 I can use dlsym to get a global variable in C. Is it possible to get a member from a struct.

I'm guessing that if I had the following code in a shared object:

header.h

struct myStruct
{
    int a;
    int b;
};

imp.c

struct myStruct structure = { 123, 456 };

I could include the same header.h file and cast the whole pointer to a struct myStruct*. struct myStruct * capturedStructure = (struct myStruct*) dlsym(handle, "structure");

However, is there a way to get the address to member directly. I'm guessing I'm unable to do something like: int* c = (int*) dlsym(handle, "structure.b");

Since dlsym allows a person to grab a function or global by itself (without a header), I'm hoping that I can also grab a member without requiring a header.

Questionable
  • 768
  • 5
  • 26
  • 4
    Pretty sure the answer is "No." That's because the member offset calculations are handled in the code, and don't appear in the symbol table. And of course you can't rely on the offset being constant for all versions of the library. – user3386109 Jan 03 '19 at 00:08
  • @user3386109 - Does that mean that for a given arch (ie: x86, x86_64) I am unable to use `int* b = (int*) (dlsym(handle, "structure") + 4);` I suppose the answer is "No" because the compiler may reorder my members to reduce padding between members. – Questionable Jan 03 '19 at 00:13
  • Using fixed offsets won't work in the long run. For example version 1 of the library has `int a; int b;` Then version 2 of the library adds another member `int a; int c; int b;`. The offset to `b` *will* change. So even if you have the header for the structure, you need to use the correct version of the header for each version of the library. – user3386109 Jan 03 '19 at 00:16
  • 2
    BTW, although the compiler may put padding between members, it is not allowed to reorder members. – user3386109 Jan 03 '19 at 00:18
  • @user3386109 - Thank you I didn't realize that. I thought that you needed to use `__attribute__((packed))` [in gcc] to prevent reordering, but that attribute really only prevents padding from being inserted and you assume responsibility of aligning the data correctly. – Questionable Jan 03 '19 at 00:24

2 Answers2

2

the address to member directly

The normal way looks like this:

struct myStruct *pnt = (struct myStruct*) dlsym(handle, "structure");
int *b = &pnt->b;

Now let's substitute s/pnt/((struct myStruct*) dlsym(handle, "structure"))/. That's:

int *b = &((struct myStruct*) dlsym(handle, "structure"))->b;

without the compiler having the structure defined? (from comments)

This could be a bit tricky, but we can do it. You would need to export another symbol:

const size_t offsetof_member_b_in_myStruct = offset(struct myStruct, b);

And then later in client code:

int *b = (int*)(
             (uintptr_t)dlsym(handle, "structure") + 
             *(size_t*)dlsym(handle, "offsetof_member_b_in_myStruct")
         );

I guess such API could be consistent, but feels bad. It's simpler to export the structure to client code. Maybe in a general case, better create a standard specifying memory layout of the structure you are interchanging with client code (thus you push the responsibility to clients to provide proper abstraction).

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • This will work, but does the symbol table keep track of the offsets so that I can access the member "b" without the compiler having the structure defined? I can manually do `int* b = (int*) (dlsym(handle, "structure") + 4);` – Questionable Jan 03 '19 at 00:11
  • `without the compiler having the structure defined?` - you can, but notice that this is wrong. Read about padding in structures. You can export `offsetof(struct myStruct, b)` in a variable and then `(int*)((uintptr_t)dlsym(handle, "structure") + offsetof(struct myStruct, b))` but it feels crude. It is wrong to assume that `offsetof(struct myStruct, b)` is equal to 4. – KamilCuk Jan 03 '19 at 00:13
  • Thank you. Yes, I didn't consider that the compiler may reorder the members to reduce padding between the elements. – Questionable Jan 03 '19 at 00:16
  • 3
    @Questionable A C compiler may _not_ reorder members of a struct. – Ctx Jan 03 '19 at 00:17
  • 1
    To me it seems a lot cleaner to export a "getter" that returns the value of `b`. – user3386109 Jan 03 '19 at 00:29
  • @user3386109 and another "setter" for setting it. – KamilCuk Jan 03 '19 at 00:30
  • While the the answer with the `size_t` and the `offsetof_member_b_in_mystruct` is a bit crude looking. I like it. It answers my question. It may not be super practical but it shows that it is possible. Thank you! – Questionable Jan 03 '19 at 00:38
1

With extern struct myStruct structure; there will be an entry in the symbol table pointing to structure and it will be keyed to the string structure.

To get the address of its b member, you simply do:

struct myStruct *p = dlsym(handle, "structure");
if(!p) fail();
int *pb = &p->b;
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142