Use of redefining void pointer to pointer to an anonymous structure?

1.1k views Asked by At

I was working with UEFI driver-related code, and I came across this:

/* EFI headers define EFI_HANDLE as a void pointer, which renders type
* checking somewhat useless. Work around this bizarre sabotage
* attempt by redefining EFI_HANDLE as a pointer to an anonymous
* structure.
*/
#define EFI_HANDLE STUPID_EFI_HANDLE
#include <ipxe/efi/Uefi/UefiBaseType.h>
#undef EFI_HANDLE
typedef struct {} *EFI_HANDLE;

The full source code is in this path http://dox.ipxe.org/include_2ipxe_2efi_2efi_8h_source.html

This is my first encounter with anonymous structure, and I couldn't make out the logic of redefining a void * to a pointer to an anonymous structure. What kind of a hack the "bizzare sabotage attempt" hints at?

2

There are 2 answers

9
WhozCraig On BEST ANSWER

The library is using information hiding on the internal data object behind the address held in an EFI_HANDLE. But in doing so, they're making the code more susceptible to accidental bugs.

In C, void* is transparently cast to any other non-void* non-const data pointer type without warning (it's by language design).

Using a non-void pointer type ensures an EFI_HANDLE is only used where EFI_HANDLE belongs. The compiler's type-checking kicks you in the groin when you pass it somewhere else that isn't EFI_HANDLE , but rather a pointer to something else.

Ex: As void*, this will compile without warning or error

#include <string.h>

#define EFI_HANDLE void*

int main()
{
    EFI_HANDLE handle = NULL;

    strcpy(handle, "Something");
}

Changing the alias to:

typedef struct {} *EFI_HANDLE;

will reap the ensuing "incompatible pointer type" compile-time error.

Finally, as an anonymous struct, there is no pointless structure tag name adding to the already-polluted name space that you can use (accidently or nefariously).

1
2501 On

That isn't an anonymous structure, but a struct without a tag.

An anonymous structure can only exist as a member of another struct,
and it must also not have a tag1.

Defining a struct without any members is not allowed. The code you're looking at is using a compiler extension that permits this.

The library is doing this to hide the definition of the structure from the user, while maintaining type safety.

However there is a much better way to do this. If you have a hidden structure definition, you can still define an opaque pointer to it, that has a type, so it is type safe:

struct hidden    //defined in a file and not exposed
{
    int a;
};

void Hidden( struct hidden* );
void Other( struct other* );
struct hidden* a = NULL;    //doesn't see the definition of struct hidden
Hidden( a );    //it may be used 
Other( a );    //compiler error

1 (Quoted from: ISO/IEC 9899:201x 6.7.2.1 Structure and union specifiers 13)
An unnamed member whose type specifier is a structure specifier with no tag is called an anonymous structure; an unnamed member whose type specifier is a union specifier with no tag is called an anonymous union. The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous