Search code examples
objective-ccc-preprocessorobjective-c-category

Generate random identifier in C Preprocessor to avoid duplicate linker symbols


I'm trying to solve Can Xcode tell me if I forget to include a category implementation in my target?, and I came up with the following solution:

NSObject+Foo.h

extern int volatile canary;
void canaryCage() {
  canary = 0;
}

NSObject+Foo.m

int canary = 0;

Now, if I #import "NSObject+Foo.h" in a source file, I'll get a linker error if that NSObject+Foo.m wasn't also included in my target.

However, every time I #import "NSObject+Foo.h" I generate a duplicate _canaryCage symbol. I can't use __COUNTER__ because I only #import "NSObject+Foo.h" in implementation files. I need canaryCage to be unique across my whole symbol table.

I need something like:

#define CONCAT(x, y) x##y
#define CONCAT2(x, y) CONCAT(x, y)
extern int volatile canary;
void CONCAT2(canaryCage, __RANDOM__)() {
  canary = 0;
}

This way, if I have source files like:

Bar.m

#import "NSObject+Foo.h"

Baz.m

#import "NSObject+Foo.h"

I'll get symbols like _canaryCage9572098740753234521 and _canaryCage549569815492345, which won't conflict. I also don't want to enable --allow-multiple-definition in ld because I want other duplicate symbol definitions to cause an error. I don't want to use canaryCage for anything but a marker that I forgot to link a source file whose header I #imported.


Solution

  • This answer was close, but it resulted in canaryCage being optimized away because it was dead code.

    Solution:

    NSObject+Foo.h

    extern int canary;
    __attribute__((constructor)) static void canaryCage() {
      canary = 0;
    }
    

    NSObject+Foo.m

    int canary = 0;
    

    Unfortunately, this adds some overhead every time the category is imported, but the overhead is very minimal. If anyone knows a way to prevent canaryCage from being stripped, I'll happily mark their answer as correct.