17

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
achoora
  • 1,270
  • 1
  • 16
  • 32
  • 1
    Interesting detail is also the `#define` and `#undef` around `#include`. It would suggest that header uses `#define` instead of `typedef`, which might clue that there are also other WTFs in the code. That could explain the passive aggressive tone of the comment... – user694733 Dec 14 '16 at 10:20

2 Answers2

19

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).

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • It can even kick you repeatedly, depending on your compiler settings – StoryTeller - Unslander Monica Dec 14 '16 at 10:14
  • 1
    @StoryTeller Javascript also has this feature. http://softwareengineering.stackexchange.com/a/11812/168891 – user2752467 Dec 14 '16 at 17:57
  • I'm not sure I understand correctly. If you do this, what types _can_ `*EFI_HANDLE` now point to? – user2752467 Dec 14 '16 at 17:59
  • @JustinLardinois It points to a structure. It's a very well know concept called handle paradigm! It's all done for 2 reasons - type safety (verification at compilation time), and abstraction (like in OOP to separate internals from the interface); – Alex D Dec 14 '16 at 18:04
  • @Alex Am I understanding right then that a pointer to an unnamed `struct` type can point to any `struct` type? I tried googling "c handle paradigm" but didn't find any results on a concept with that name. – user2752467 Dec 15 '16 at 00:00
  • @JustinLardinois A pointer of non-void pointer type cannot be assigned an address unless an explicit cast is involved. Only `void*` implicitly casts to, and from, other arbitrary pointer types. The library authors, no doubt, have their own structure of data they use these "handles" to address by their own casts. What the address held within actually refers to isn't really important to you (and that is somewhat the point of all this). By using that struct typedef, it is no longer possible to pass an EFI_HANDLE to anything expecting something besides an EFI_HANDLE (like I did) without a cast. – WhozCraig Dec 15 '16 at 00:09
  • @JustinLardinois Compiler will give you a warning "incompatible types" and that is exactly the goal to get that warning. You of course can cast it but that's a different story because when you casted it means that you know what you are doing! – Alex D Dec 15 '16 at 04:57
  • @JustinLardinois Oh and btw, I forgot to add, google for opaque pointer or opaque structure in c. – Alex D Dec 15 '16 at 04:58
  • Ah, I see. I didn't catch from reading this answer that the point was to do an explicit cast. – user2752467 Dec 15 '16 at 18:36
7

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

2501
  • 25,460
  • 4
  • 47
  • 87
  • 2
    Worth mentioning is that in older versions of the C standard, "anonymous structure" did not exist as a separate term. For those versions, "anonymous" would just be interpreted as the ordinary English word meaning "without a name", which for structure types seems perfectly fair to interpret as "without a tag". –  Dec 14 '16 at 15:21