Created
January 26, 2013 17:05
-
-
Save OliverLetterer/4643294 to your computer and use it in GitHub Desktop.
With this gist, I, as a total assembly noob, am trying to understand the XCDUUID runtime hack (https://github.com/0xced/NSUUID/blob/1.0.1/NSUUID.m#L167-L221) to be able to use NSUUID on iOS < 6.0.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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 |
The comments are pretty accurate, well done! To answer your question: you can not simply reference the L_OBJC_CLASS_NSUUID
label because of position independent executables. If you don't write it PC-relative (with the LPC0
label) you will get this linker warning and PIE will be disabled:
ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in +[XCDUUID load] from ~/Library/Developer/Xcode/DerivedData/MyApp-hjrsiejfyzwurlckipecpujunlyh/Build/Intermediates/MyApp.build/Debug-iphoneos/MyApp.build/Objects-normal/armv7/NSUUID.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Open questions:
TARGET_CPU_ARM
, can one simply say?