Skip to content

Instantly share code, notes, and snippets.

@JohnWong
Forked from OliverLetterer/XCDUUID.m
Created October 14, 2015 03:00
Show Gist options
  • Save JohnWong/c83a4f9df7ac0320643c to your computer and use it in GitHub Desktop.
Save JohnWong/c83a4f9df7ac0320643c to your computer and use it in GitHub Desktop.

Revisions

  1. @OliverLetterer OliverLetterer created this gist Jan 26, 2013.
    118 changes: 118 additions & 0 deletions XCDUUID.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,118 @@
    @implementation XCDUUID

    + (void) load
    {
    // query runtime if NSUUID class already exists, if so => done
    if (objc_getClass("NSUUID"))
    {
    return;
    }

    /**
    * The compiler hardcodes uses of the NSUUID class by referencing the _OBJC_CLASS_$_NSUUID label in objc2
    * if this label is Nil or doesnt exist, the class does not exist and cannot be allocated/used
    * NSUUIDClassRef is a pointer to this label
    * since one cannot access the _OBJC_CLASS_$_NSUUID label in C, one will have to get a pointer to this label via inline assembly and store the XCDUUID class in this label
    */
    Class *NSUUIDClassRef = NULL;

    // the following assembly stores a pointer to the _OBJC_CLASS_$_NSUUID label in NSUUIDClassRef based on the target platform
    #if TARGET_CPU_ARM // arm assembly
    __asm(
    /**
    ios runs on 32 bit arm platform
    one cannot move a 32 bit constant into a register, therefore the lower and upper 16 bit will have to be moved into the register for NSUUIDClassRef separately
    movw and movt are new in ARMv7 for exactly that purpose (http://blogs.arm.com/software-enablement/251-how-to-load-constants-in-assembly-for-arm-architecture/)
    for understanding the inline assembly: http://www.ethernut.de/en/documents/arm-inline-asm.html
    short explanation for the following assembly:
    there are 3 assembly instructions here:
    1) movw %0, :lower16:(L_OBJC_CLASS_NSUUID-(LPC0+4))
    2) movt %0, :upper16:(L_OBJC_CLASS_NSUUID-(LPC0+4))
    3) LPC0: add %0, pc
    %0 will be replaced with the register holding NSUUIDClassRef
    L_OBJC_CLASS_NSUUID is a label storing the _OBJC_CLASS_$_NSUUID pointer
    LPC0 is just a label for instruction 3
    pc is the program counter and is 4 bytes ahead of the current instruction, therefore pc in the third instruction is equal to LPC0+4
    */
    "movw %0, :lower16:(L_OBJC_CLASS_NSUUID-(LPC0+4))\n" // move the lower 16 bit of `L_OBJC_CLASS_NSUUID-(LPC0+4)` into NSUUIDClassRef
    "movt %0, :upper16:(L_OBJC_CLASS_NSUUID-(LPC0+4))\n" // move the upper 16 bit of `L_OBJC_CLASS_NSUUID-(LPC0+4)` into NSUUIDClassRef
    "LPC0: add %0, pc" : "=r"(NSUUIDClassRef) // NSUUIDClassRef = NSUUIDClassRef + pc

    /**
    the three instructions are therefore equivalent to
    NSUUIDClassRef = NSUUIDClassRef + pc
    <=>
    NSUUIDClassRef = L_OBJC_CLASS_NSUUID-(LPC0+4) + pc
    <=>
    NSUUIDClassRef = L_OBJC_CLASS_NSUUID-(LPC0+4) + LPC0+4
    <=>
    NSUUIDClassRef = L_OBJC_CLASS_NSUUID
    */
    );
    #elif TARGET_CPU_X86_64
    __asm("leaq L_OBJC_CLASS_NSUUID(%%rip), %0" : "=r"(NSUUIDClassRef));
    #elif TARGET_CPU_X86
    void *pc = NULL;
    __asm(
    "calll L0\n"
    "L0: popl %0\n"
    "leal L_OBJC_CLASS_NSUUID-L0(%0), %1" : "=r"(pc), "=r"(NSUUIDClassRef)
    );
    #else
    #error Unsupported CPU
    #endif
    /**
    check if we have a pointer to `_OBJC_CLASS_$_NSUUID` and if `_OBJC_CLASS_$_NSUUID` is Nil
    */
    if (NSUUIDClassRef && *NSUUIDClassRef == Nil)
    {
    /**
    `objc_duplicateClass(self, "NSUUID", 0)` dublicates `XCDUUID` and registers it with name `NSUUID` in the objc runtime
    the newly created class is then stored in `_OBJC_CLASS_$_NSUUID` (`_OBJC_CLASS_$_NSUUID = objc_duplicateClass(self, "NSUUID", 0);`)
    */
    *NSUUIDClassRef = objc_duplicateClass(self, "NSUUID", 0);
    }
    /**
    hardcoded references to `_OBJC_CLASS_$_NSUUID` by the compiler will now point to the newly allocated class
    */
    }

    __asm(
    #if defined(__OBJC2__) && __OBJC2__
    /**
    this is a data section for objc2 class references with the following attributes:
    * regular: "A regular section may contain any kind of data and gets no special processing from the link editor. This is the default section type. Examples of regular sections include program instructions or initialized data."
    * no_dead_strip: "The no_dead_strip section attribute specifies that a particular section must not be dead-stripped."
    Documentation can be found here: https://developer.apple.com/library/mac/#documentation/developertools/Reference/Assembler/040-Assembler_Directives/asm_directives.html
    */
    ".section __DATA,__objc_classrefs,regular,no_dead_strip\n"
    #if TARGET_RT_64_BIT
    ".align 3\n" // align the next label to 2^3 bytes = 64 bit for 64 bit platforms
    "L_OBJC_CLASS_NSUUID:\n" // the L_OBJC_CLASS_NSUUID label will store the _OBJC_CLASS_$_NSUUID label, which is weak referenced (see below)
    ".quad _OBJC_CLASS_$_NSUUID\n"
    #else
    ".align 2\n"
    "L_OBJC_CLASS_NSUUID:\n"
    ".long _OBJC_CLASS_$_NSUUID\n"
    #endif
    #else
    /**
    Data section for NSUUID earlier than objc2
    */
    ".section __TEXT,__cstring,cstring_literals\n"
    "L_OBJC_CLASS_NAME_NSUUID:\n"
    ".asciz \"NSUUID\"\n"
    ".section __OBJC,__cls_refs,literal_pointers,no_dead_strip\n"
    ".align 2\n"
    "L_OBJC_CLASS_NSUUID:\n"
    ".long L_OBJC_CLASS_NAME_NSUUID\n"
    #endif
    /**
    .weak_reference: "The .weak_reference directive causes symbol_name to be a weak undefined symbol present in the output file’s symbol table. This is used by the compiler when referencing a symbol with the weak_import attribute."
    */
    ".weak_reference _OBJC_CLASS_$_NSUUID\n"
    );
    @end