this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ObjC runtime work: objc_msgSend and objc_msgSend_stret, ivars support, working class reg (w/o protocols), trivial benchmarking framework

+587 -310
+92
benchmarks/runtest
··· 1 + #!/bin/bash 2 + 3 + BUILDSERVER="osx" 4 + CPPFLAGS="" 5 + DYLD="dyld" 6 + 7 + set -e 8 + 9 + failure() { 10 + tput setaf 1 11 + tput bold 12 + echo "Benchmark failure - the last command failed to execute" 13 + tput sgr0 14 + } 15 + 16 + runtest() { 17 + trap failure ERR 18 + 19 + source="$1" 20 + extension="${source##*.}" 21 + source_fn=$(echo "$source" | sed 's/\//_/g') 22 + 23 + cflags="$(grep '// CFLAGS' "$source" || true)" 24 + cflags="-O3 $CPPFLAGS -w $(echo "$cflags" | cut -b 12-)" 25 + 26 + case "$extension" in 27 + "cpp") 28 + darwin_tool="g++" 29 + native_tool="clang++" 30 + ;; 31 + "c") 32 + darwin_tool="gcc" 33 + native_tool="clang" 34 + ;; 35 + "m") 36 + darwin_tool="gcc" 37 + native_tool="clang" 38 + cflags_native="-l:libobjc.so.4 -lgnustep-base" 39 + cflags_darwin="-lobjc" 40 + ;; 41 + *) 42 + echo "Unsupported file type: $extension" 43 + exit 1 44 + esac 45 + 46 + tput bold 47 + echo "=====" 48 + echo "Running benchmark '$source'" 49 + echo "=====" 50 + tput sgr0 51 + 52 + cflags="$(grep '// CFLAGS' "$source" || true)" 53 + cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)" 54 + 55 + echo "Copying the source code to Darwin..." 56 + scp "$source" "$BUILDSERVER:/tmp/$$.$source_fn" >/dev/null 57 + echo "Building the source code for Darwin..." 58 + ssh "$BUILDSERVER" "$darwin_tool $cflags $cflags_darwin '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'" 59 + echo "Copying the binary over..." 60 + scp "$BUILDSERVER:/tmp/$$.$source_fn.bin" "/tmp" >/dev/null 61 + ssh "$BUILDSERVER" "rm -f /tmp/$$.$source_fn*" 62 + 63 + echo "Running Darwin binary..." 64 + tput bold 65 + time $DYLD "/tmp/$$.$source_fn.bin" 66 + tput sgr0 67 + rm -f "/tmp/$$.$source_fn.bin" 68 + 69 + echo "Compiling native..." 70 + $native_tool $cflags $cflags_native "$source" -o "/tmp/$$.$source_fn.bin" 71 + echo "Running native binary..." 72 + tput bold 73 + time "/tmp/$$.$source_fn.bin" 74 + tput sgr0 75 + 76 + rm -f "/tmp/$$.$source_fn.bin" 77 + } 78 + 79 + if [[ "$0" == *runtest32 ]]; then 80 + CPPFLAGS="-m32" 81 + DYLD="dyld32" 82 + echo "32-bit mode" 83 + fi 84 + if [[ "$0" == *runtest64 ]]; then 85 + DYLD="dyld64" 86 + echo "64-bit mode" 87 + fi 88 + 89 + for test in "$@"; do 90 + runtest "$test" 91 + done 92 +
+1
benchmarks/runtest32
··· 1 + runtest
+1
benchmarks/runtest64
··· 1 + runtest
+29
benchmarks/src/objc_msgSend.m
··· 1 + #include <stdio.h> 2 + #import <Foundation/NSObject.h> 3 + 4 + @interface helloclass : NSObject { 5 + } 6 + 7 + + (int)mult:(int)a :(int)b; 8 + @end 9 + 10 + @implementation helloclass 11 + + (int)mult:(int)a :(int)b 12 + { 13 + return a*b; 14 + } 15 + @end 16 + 17 + int main() 18 + { 19 + int i; 20 + volatile int j; 21 + for (i = 0; i < 10000000; i++) 22 + { 23 + j += [helloclass mult:1:2]; 24 + } 25 + 26 + return 0; 27 + } 28 + 29 +
+37
benchmarks/src/objc_stret.m
··· 1 + #include <stdio.h> 2 + #import <Foundation/NSObject.h> 3 + 4 + struct st 5 + { 6 + float f1; 7 + int buf[50]; 8 + }; 9 + 10 + @interface helloclass : NSObject { 11 + } 12 + 13 + + (struct st)mult:(float)a :(float)b; 14 + @end 15 + 16 + @implementation helloclass 17 + + (struct st)mult:(float)a :(float)b 18 + { 19 + struct st s; 20 + s.f1 = a+b; 21 + return s; 22 + } 23 + @end 24 + 25 + int main() 26 + { 27 + int i; 28 + for (i = 0; i < 10000000; i++) 29 + { 30 + struct st v = [helloclass mult:1.0f:2.0f]; 31 + v.f1; 32 + } 33 + 34 + return 0; 35 + } 36 + 37 +
+4 -1
src/libobjcdarwin/CMakeLists.txt
··· 14 14 #configure_file(config.h.in config.h) 15 15 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -fPIC -ggdb") 16 16 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -fPIC -ggdb") 17 + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fPIC") 17 18 18 19 set(objcdarwin_SRCS 19 20 objc_msgSend.nasm 20 21 objc_msgSend_fixup.nasm 22 + objc_msgSend_stret.nasm 23 + registers.nasm 21 24 NameTranslate.cpp 22 - ClassRegister.cpp 25 + ClassRegister.mm 23 26 ) 24 27 25 28 add_library(objc.A.dylib SHARED ${objcdarwin_SRCS})
-214
src/libobjcdarwin/ClassRegister.cpp
··· 1 - #include "ClassRegister.h" 2 - #include "../dyld/public.h" 3 - #include "class.h" 4 - #include "method_list.h" 5 - #include <iostream> 6 - #include <map> 7 - #include <cstring> 8 - #include <cassert> 9 - 10 - /* 11 - __DATA,__objc_classlist was __OBJC2,__class_list 12 - __DATA,__objc_catlist was __OBJC2,__category_list 13 - __DATA,__objc_protolist was __OBJC2,__protocol_list 14 - __DATA,__objc_msgrefs was __OBJC2,__message_refs 15 - __DATA,__objc_classrefs was __OBJC2,__class_refs 16 - __DATA,__objc_superrefs was __OBJC2,__super_refs 17 - __DATA,__objc_imageinfo was __OBJC,__image_info 18 - */ 19 - 20 - #define SEG_OBJC_CLASSLIST_NEW "__DATA" 21 - #define SECT_OBJC_CLASSLIST_NEW "__objc_classlist" 22 - #define SEG_OBJC_CLASSLIST_OLD "__OBJC" 23 - #define SECT_OBJC_CLASSLIST_OLD "__class" // TODO: check if correct 24 - 25 - static std::map<const void*, objc_class*> g_convertedClasses; 26 - static std::pair<uintptr_t, uintptr_t> g_cstringSection; 27 - 28 - static void ProcessImageLoad(const struct mach_header* mh, intptr_t slide); 29 - static void ProcessImageUnload(const struct mach_header* mh, intptr_t slide); 30 - static void RegisterClass(const class_t* cls); 31 - static void RegisterClass(const old_class* cls); 32 - static old_class* PtrToClass(old_class_ptr ptr); 33 - 34 - // Here we process Mach-O files that have been loaded before this native library 35 - // Then we register a handler to process all images loaded in the future 36 - __attribute__((constructor)) 37 - void RegisterAlreadyLoadedClasses() 38 - { 39 - for (uint32_t i = 0; i < _dyld_image_count(); i++) 40 - { 41 - const struct mach_header* hdr = _dyld_get_image_header(i); 42 - ProcessImageLoad(hdr, 0); 43 - } 44 - 45 - _dyld_register_func_for_add_image(ProcessImageLoad); 46 - _dyld_register_func_for_remove_image(ProcessImageUnload); 47 - 48 - //std::cout << "Done registering\n"; 49 - } 50 - 51 - Class objcdarwin_class_lookup(const class_t* cls) 52 - { 53 - //std::cout << "Looking up " << cls << std::endl << std::flush; 54 - auto it = g_convertedClasses.find(cls); 55 - if (it != g_convertedClasses.end()) 56 - return it->second; 57 - else 58 - return Class(cls); // assume native or instance ptr 59 - } 60 - 61 - // This is very wrong. Does it have to be this way?s 62 - old_class* PtrToClass(old_class_ptr ptr) 63 - { 64 - if (ptr.ptrValue >= g_cstringSection.first && ptr.ptrValue < g_cstringSection.first+g_cstringSection.second) 65 - { 66 - //std::cout << ptr.cls << " found to be a string: " << ptr.name << std::endl; 67 - id cls = objc_getClass(ptr.name); // it is a string 68 - 69 - assert(cls != nullptr); 70 - return (old_class*) cls; 71 - } 72 - else 73 - { 74 - //std::cout << ptr.cls << " found to be ptr to class.\n"; 75 - return ptr.cls; 76 - } 77 - } 78 - 79 - template<typename ListType> void ConvertMethodListGen(Class c, const ListType* list) 80 - { 81 - std::cout << list->count << " methods within\n"; 82 - 83 - for (size_t i = 0; i < list->count; i++) 84 - { 85 - auto* m = &list->method_list[i]; 86 - 87 - std::cout << "Method: selName: " << m->selName << "; types: " << m->types << "; impl: " << m->impl << std::endl; 88 - 89 - SEL sel = sel_registerName(m->selName); 90 - class_addMethod(c, sel, reinterpret_cast<IMP>(m->impl), m->types); 91 - } 92 - } 93 - 94 - void RegisterClass(const class_t* cls) 95 - { 96 - std::cout << "Processing ObjC class " << cls->data()->className; 97 - std::cout << std::endl; 98 - 99 - // objc_allocateClassPair 100 - // objc_registerClassPair 101 - const class_t* meta = cls->isa; 102 - Class conv, super; 103 - if (g_convertedClasses.find(cls->superclass) != g_convertedClasses.end()) 104 - super = g_convertedClasses[cls->superclass]; 105 - else 106 - super = reinterpret_cast<objc_class*>(cls->superclass); 107 - 108 - conv = objc_allocateClassPair(super, cls->data()->className, 0); 109 - 110 - const class_ro_t* ro = cls->data(); 111 - const class_ro_t* roMeta = meta->data(); 112 - 113 - if (ro->baseMethods) 114 - ConvertMethodListGen(conv, ro->baseMethods); 115 - if (roMeta->baseMethods) 116 - ConvertMethodListGen(conv->isa, roMeta->baseMethods); 117 - 118 - conv->instance_size = ro->instSize; 119 - conv->isa->instance_size = roMeta->instSize; 120 - 121 - objc_registerClassPair(conv); 122 - g_convertedClasses[cls] = conv; 123 - g_convertedClasses[meta] = conv->isa; 124 - } 125 - 126 - void RegisterClass(const old_class* cls) 127 - { 128 - std::cout << "Processing old ObjC class " << cls->name; 129 - std::cout << std::endl; 130 - 131 - const old_class* meta = PtrToClass(cls->isa); 132 - Class conv, super; 133 - old_class* psuper = PtrToClass(cls->super_class); 134 - 135 - if (g_convertedClasses.find(psuper) != g_convertedClasses.end()) 136 - super = g_convertedClasses[psuper]; 137 - else 138 - super = reinterpret_cast<objc_class*>(psuper); 139 - 140 - conv = objc_allocateClassPair(super, cls->name, 0); 141 - 142 - std::cout << "Instance methods...\n"; 143 - if (cls->methodList) 144 - ConvertMethodListGen(conv, cls->methodList); 145 - std::cout << "Class methods...\n"; 146 - if (meta->methodList) 147 - ConvertMethodListGen(conv->isa, meta->methodList); 148 - 149 - objc_registerClassPair(conv); 150 - 151 - assert(id(conv) == objc_getClass(cls->name)); 152 - assert(id(conv->isa) == objc_getMetaClass(cls->name)); 153 - assert(class_isMetaClass(conv->isa)); 154 - assert(!class_isMetaClass(conv)); 155 - 156 - g_convertedClasses[cls] = conv; 157 - g_convertedClasses[cls->name] = conv; 158 - g_convertedClasses[meta] = conv->isa; 159 - 160 - /* 161 - SEL ss = sel_get_any_uid("doHello:::::::"); 162 - std::cout << "objc_msg_lookup: " << conv << ' ' << ss << std::endl; 163 - void* p = (void*) objc_msg_lookup(id(conv), ss); 164 - 165 - std::cout << "Impl: " << p << "; Sel: " << ss << std::endl; 166 - */ 167 - } 168 - 169 - void ProcessImageLoad(const struct mach_header* mh, intptr_t) 170 - { 171 - unsigned long size; 172 - const class_t** classes; 173 - 174 - classes = reinterpret_cast<const class_t**>( 175 - getsectdata(mh, SEG_OBJC_CLASSLIST_NEW, SECT_OBJC_CLASSLIST_NEW, &size) 176 - ); 177 - 178 - if (!classes) 179 - { 180 - // Try the old runtime 181 - const old_class* classes; 182 - 183 - classes = reinterpret_cast<const old_class*>( 184 - getsectdata(mh, SEG_OBJC_CLASSLIST_OLD, SECT_OBJC_CLASSLIST_OLD, &size) 185 - ); 186 - 187 - if (classes) 188 - { 189 - // g_cstringSection 190 - unsigned long cstrLen; 191 - void* ptr; 192 - 193 - ptr = getsectdata(mh, "__TEXT", "__cstring", &cstrLen); 194 - if (ptr) 195 - g_cstringSection = std::pair<uintptr_t,uintptr_t>(uintptr_t(ptr), cstrLen); 196 - else 197 - g_cstringSection = std::pair<uintptr_t,uintptr_t>(0, 0); 198 - 199 - for (size_t i = 0; i < size / sizeof(old_class); i++) 200 - RegisterClass(classes+i); 201 - } 202 - } 203 - else 204 - { 205 - for (size_t i = 0; i < size / sizeof(class_t*); i++) 206 - RegisterClass(classes[i]); 207 - } 208 - } 209 - 210 - void ProcessImageUnload(const struct mach_header* mh, intptr_t) 211 - { 212 - // TODO 213 - } 214 -
+24 -4
src/libobjcdarwin/ClassRegister.h
··· 1 1 #ifndef CLASSREGISTER_H 2 2 #define CLASSREGISTER_H 3 + #define __STDC_LIMIT_MACROS 3 4 #include <stdint.h> 4 5 #include <cstddef> 5 6 #include <objc/runtime.h> 7 + 8 + #ifndef INTPTR_MAX 9 + #error 10 + #endif 6 11 7 12 struct method_t 8 13 { ··· 20 25 uint32_t entsize() const { return entsize_and_flags & ~uint32_t(3); } 21 26 }; 22 27 28 + struct ivar_t 29 + { 30 + uintptr_t* offset; 31 + const char* name; 32 + const char* type; 33 + uint32_t alignment, size; 34 + }; 35 + 36 + struct ivar_list_t 37 + { 38 + uint32_t entsize, count; 39 + ivar_t ivar_list[]; 40 + }; 41 + 23 42 struct class_ro_t 24 43 { 25 44 uint32_t flags, instStart, instSize; ··· 30 49 const char* className; 31 50 const method_list_t* baseMethods; // instance methods for classes, static methods for metaclasses 32 51 const void* baseProtocols; 52 + const ivar_list_t* ivars; 33 53 34 54 void* todo[2]; // TODO: two more pointers 35 55 }; ··· 108 128 109 129 struct old_ivar 110 130 { 111 - char *ivar_name; 112 - char *ivar_type; 113 - int ivar_offset; 131 + char *name; 132 + char *type; 133 + int offset; 114 134 #ifdef __x86_64__ 115 135 int space; 116 136 #endif ··· 118 138 119 139 struct old_ivar_list 120 140 { 121 - int ivar_count; 141 + int count; 122 142 #ifdef __x86_64__ 123 143 int space; 124 144 #endif
+46
src/libobjcdarwin/NameTranslate.cpp
··· 1 + #include <cstring> 2 + #include <memory> 3 + #include <dlfcn.h> 4 + 5 + extern "C" void* _objc_empty_cache = 0; 6 + extern "C" void* _objc_empty_vtable = 0; 7 + 8 + namespace Darling 9 + { 10 + typedef bool (*DlsymHookFunc)(char* symName); 11 + void registerDlsymHook(DlsymHookFunc func); 12 + void deregisterDlsymHook(DlsymHookFunc func); 13 + }; 14 + 15 + static bool ClassTranslator(char* symName); 16 + 17 + __attribute__((constructor)) 18 + static void initTranslation() 19 + { 20 + Darling::registerDlsymHook(ClassTranslator); 21 + } 22 + 23 + __attribute__((destructor)) 24 + static void exitTranslation() 25 + { 26 + Darling::deregisterDlsymHook(ClassTranslator); 27 + } 28 + 29 + static bool ClassTranslator(char* name) 30 + { 31 + if (char* off = strstr(name, "_$_")) 32 + { 33 + size_t offset = off - name; 34 + 35 + memmove(name+1, name, strlen(name)+1); 36 + *name = '_'; // prefix with an underscore 37 + offset++; 38 + // remove $_ 39 + memmove(name + offset, name + offset + 2, strlen(name + offset + 2)+1); 40 + 41 + return true; 42 + } 43 + 44 + return false; 45 + } 46 +
+5 -27
src/libobjcdarwin/objc_msgSend.nasm
··· 3 3 extern objcdarwin_class_lookup 4 4 extern objc_msg_lookup 5 5 extern sel_get_any_uid 6 + extern objcdarwin_SaveRegisters 7 + extern objcdarwin_RestoreRegisters 6 8 7 9 %ifidn __OUTPUT_FORMAT__, elf64 8 10 9 11 BITS 64 10 12 section text 11 13 12 - SaveRegisters: 13 - pop r11 14 - 15 - push r9 16 - push r8 17 - push rcx 18 - push rdx 19 - push rsi 20 - push rdi 21 - 22 - ; save xmms? 23 - jmp r11 24 - 25 - RestoreRegisters: 26 - pop r11 27 - pop rdi 28 - pop rsi 29 - pop rdx 30 - pop rcx 31 - pop r8 32 - pop r9 33 - jmp r11 34 - 35 14 __darwin_objc_msgSend: 36 15 ; Procedure: 37 16 ; 1) get the converted GNU class from an Apple class ··· 39 18 ; 3) run objc_msg_lookup 40 19 ; 4) jump to the pointer returned by objc_msg_lookup 41 20 42 - call SaveRegisters 21 + call objcdarwin_SaveRegisters WRT ..plt 43 22 call objcdarwin_class_lookup WRT ..plt 44 23 mov [rsp], rax ; save the converted value 45 24 ··· 49 28 ; rax now has the GNU selector 50 29 ; move rax to the second argument 51 30 mov rsi, rax 31 + mov [rsp+8], rax 52 32 ; restore the first argument 53 33 mov rdi, [rsp] 54 34 call objc_msg_lookup WRT ..plt 55 35 56 - call RestoreRegisters 36 + call objcdarwin_RestoreRegisters WRT ..plt 57 37 jmp rax 58 38 59 39 %elifidn __OUTPUT_FORMAT__, elf 60 40 61 41 BITS 32 62 42 section text 63 - 64 - ;;;;;;;;;;;;;;;;;;;;;;;;;;;; TODO!!! 65 43 66 44 __darwin_objc_msgSend: 67 45 ;enter 8
+52 -51
src/libobjcdarwin/objc_msgSend_fixup.nasm
··· 1 - global objc_msgSend_fixup 1 + global __darwin_objc_msgSend_fixup 2 2 3 + extern objcdarwin_class_lookup 3 4 extern objc_msg_lookup 4 5 extern sel_get_any_uid 6 + extern objcdarwin_SaveRegisters 7 + extern objcdarwin_RestoreRegisters 5 8 6 9 %ifidn __OUTPUT_FORMAT__, elf64 7 10 8 11 BITS 64 9 12 section text 10 13 11 - SaveRegisters: 12 - pop r11 13 - push rdi 14 - push rsi 15 - push rdx 16 - push rcx 17 - push r8 18 - push r9 19 - ; save xmms? 20 - jmp r11 21 - 22 - RestoreRegisters: 23 - pop r11 24 - pop r9 25 - pop r8 26 - pop rcx 27 - pop rdx 28 - pop rsi 29 - pop rdi 30 - jmp r11 14 + ;__darwin_objc_msgSend_fixed: 15 + ; add rsi, 8 16 + ; jmp __darwin_objc_msgSend WRT ..plt 31 17 32 - objc_msgSend_fixup: 18 + __darwin_objc_msgSend_fixup: 33 19 ; Procedure: 34 - ; 1) convert Apple selector to GNU 35 - ; 2) run objc_msg_lookup 36 - ; 3) save the method address into the second argument 37 - ; 4) save the right selector into the second argument 38 - ; 5) jump to the pointer returned by objc_msg_lookup 20 + ; 1) get the converted GNU class from an Apple class 21 + ; 2) convert Apple selector to GNU 22 + ; 3) run objc_msg_lookup 23 + ; 4) jump to the pointer returned by objc_msg_lookup 39 24 40 - call SaveRegisters 25 + call objcdarwin_SaveRegisters WRT ..plt 26 + call objcdarwin_class_lookup WRT ..plt 27 + mov [rsp], rax ; save the converted value 28 + 41 29 ; move the second argument into the first argument 42 - mov rdi, rsi 43 - add rdi, 8 ; selector name is the second pointer in struct 30 + mov rdi, [rsp+8] 31 + add rdi, 8 ; the selector itself is the second element of what we receive as SEL 32 + mov rdi, [rdi] 44 33 call sel_get_any_uid WRT ..plt 45 34 ; rax now has the GNU selector 46 - mov rsi, [rsp+8] ; put the second argument back to rsi 47 - mov [rsi+8], rax ; store the GNU selector 48 - add rsi, 8 ; pass the selector itself 35 + ; move rax to the second argument 36 + mov rsi, rax 37 + 49 38 ; restore the first argument 50 39 mov rdi, [rsp] 51 40 call objc_msg_lookup WRT ..plt 41 + 42 + ; optimize the next call by fixing the function pointer 43 + mov rsi, [rsp+8] 44 + ;mov [rsi], rax ; TODO: fixups not working, the target method still isn't getting the selector it expects 52 45 53 - call RestoreRegisters 54 - ; can we do it like this? don't we still need a proxy? 55 - mov [rsi], rax ; fix the pointer to make the call direct next time 46 + call objcdarwin_RestoreRegisters WRT ..plt 56 47 jmp rax 57 48 58 49 %elifidn __OUTPUT_FORMAT__, elf ··· 60 51 BITS 32 61 52 section text 62 53 63 - objc_msgSend_fixup: 64 - ; swap the first two arguments 54 + __darwin_objc_msgSend_fixup: 55 + 65 56 mov ecx, [esp+4] 66 - xchg [esp+8], ecx 67 - add ecx, 4 ; move to the second struct element 68 - mov [esp+4], ecx 57 + push ecx ; arg for func call 58 + 59 + call objcdarwin_class_lookup ;WRT ..plt 60 + 61 + add esp, 4 ; remove argument 62 + mov [esp+4], eax ; change the class id 63 + 64 + mov ecx, [esp+8] ; second argument 65 + add ecx, 4 ; the selector itself is the second element of what we receive as SEL 66 + mov ecx, [ecx] 67 + push ecx 69 68 70 - call sel_get_any_uid WRT ..plt 69 + call sel_get_any_uid ;WRT ..plt 71 70 72 - ; eax now has the GNU selector 73 - ; swap the arguments back 71 + add esp, 4 72 + mov [esp+8], eax 74 73 75 - mov ecx, [esp+4] 76 - mov [ecx], eax ; fix the selector in the original struct 74 + push eax ; reuse the sel_get_any_uid retval 75 + mov eax, [esp+8] 76 + push eax ; class id 77 77 78 - ; swap the arguments back 79 - mov ecx, [esp+4] 80 - xchg [esp+8], ecx 81 - mov [esp+4], ecx 82 - call objc_msg_lookup WRT ..plt 78 + call objc_msg_lookup ;WRT ..plt 79 + add esp, 8 80 + 81 + ; optimize the next call by fixing the function pointer 82 + mov ecx, [esp+8] 83 + ;mov [ecx], eax ; TODO: fixups not working, the target method still isn't getting the selector it expects 83 84 84 85 jmp eax 85 86
+77
src/libobjcdarwin/objc_msgSend_stret.nasm
··· 1 + global __darwin_objc_msgSend_stret 2 + 3 + extern objcdarwin_class_lookup 4 + extern objc_msg_lookup 5 + extern sel_get_any_uid 6 + extern objcdarwin_SaveRegisters 7 + extern objcdarwin_RestoreRegisters 8 + 9 + %ifidn __OUTPUT_FORMAT__, elf64 10 + 11 + BITS 64 12 + section text 13 + 14 + ; Compared to ordinary msgSend, arguments are shifted by one - first arg is the stret 15 + __darwin_objc_msgSend_stret: 16 + ; Procedure: 17 + ; 1) get the converted GNU class from an Apple class 18 + ; 2) convert Apple selector to GNU 19 + ; 3) run objc_msg_lookup 20 + ; 4) jump to the pointer returned by objc_msg_lookup 21 + 22 + call objcdarwin_SaveRegisters WRT ..plt 23 + mov rdi, rsi 24 + call objcdarwin_class_lookup WRT ..plt 25 + mov [rsp+8], rax ; save the converted value 26 + 27 + ; move the second argument into the first argument 28 + mov rdi, [rsp+16] 29 + call sel_get_any_uid WRT ..plt 30 + ; rax now has the GNU selector 31 + ; move rax to the second argument 32 + mov rsi, rax 33 + mov [rsp+16], rax 34 + ; restore the first argument 35 + mov rdi, [rsp+8] 36 + call objc_msg_lookup WRT ..plt 37 + 38 + call objcdarwin_RestoreRegisters WRT ..plt 39 + jmp rax 40 + 41 + %elifidn __OUTPUT_FORMAT__, elf 42 + 43 + BITS 32 44 + section text 45 + 46 + __darwin_objc_msgSend_stret: 47 + 48 + mov ecx, [esp+8] 49 + push ecx ; arg for func call 50 + 51 + call objcdarwin_class_lookup ;WRT ..plt 52 + 53 + add esp, 4 ; remove argument 54 + mov [esp+8], eax ; change the class id 55 + 56 + mov ecx, [esp+12] ; second argument 57 + push ecx 58 + 59 + call sel_get_any_uid ;WRT ..plt 60 + 61 + add esp, 4 62 + mov [esp+12], eax 63 + 64 + push eax ; reuse the sel_get_any_uid retval 65 + mov eax, [esp+12] 66 + push eax ; class id 67 + 68 + call objc_msg_lookup ;WRT ..plt 69 + add esp, 8 70 + 71 + jmp eax 72 + 73 + %else 74 + 75 + %error "Unsupported platform" 76 + 77 + %endif
+51
src/libobjcdarwin/registers.nasm
··· 1 + %ifidn __OUTPUT_FORMAT__, elf64 2 + 3 + BITS 64 4 + section text 5 + 6 + global objcdarwin_SaveRegisters 7 + global objcdarwin_RestoreRegisters 8 + 9 + objcdarwin_SaveRegisters: 10 + pop r11 11 + 12 + sub rsp, 128 ; 8*16 13 + movdqu [rsp], xmm0 14 + movdqu [rsp+16], xmm1 15 + movdqu [rsp+32], xmm2 16 + movdqu [rsp+48], xmm3 17 + movdqu [rsp+64], xmm4 18 + movdqu [rsp+80], xmm5 19 + movdqu [rsp+96], xmm6 20 + movdqu [rsp+112], xmm7 21 + 22 + push r9 23 + push r8 24 + push rcx 25 + push rdx 26 + push rsi 27 + push rdi 28 + 29 + jmp r11 30 + 31 + objcdarwin_RestoreRegisters: 32 + pop r11 33 + pop rdi 34 + pop rsi 35 + pop rdx 36 + pop rcx 37 + pop r8 38 + pop r9 39 + movdqu xmm7, [rsp+112] 40 + movdqu xmm6, [rsp+96] 41 + movdqu xmm5, [rsp+80] 42 + movdqu xmm4, [rsp+64] 43 + movdqu xmm3, [rsp+48] 44 + movdqu xmm2, [rsp+32] 45 + movdqu xmm1, [rsp+16] 46 + movdqu xmm0, [rsp] 47 + add rsp, 128 48 + 49 + jmp r11 50 + %endif 51 +
+49 -7
tests/runtest
··· 1 - #!/bin/sh 1 + #!/bin/bash 2 2 3 3 BUILDSERVER="osx" 4 4 CPPFLAGS="" ··· 6 6 7 7 set -e 8 8 9 + failure() { 10 + tput setaf 1 11 + tput bold 12 + echo "Test failure - the last command failed to execute" 13 + tput sgr0 14 + } 15 + 9 16 runtest() { 17 + trap failure ERR 18 + 10 19 source="$1" 11 20 extension="${source##*.}" 12 21 source_fn=$(echo "$source" | sed 's/\//_/g') 22 + 23 + cflags="$(grep '// CFLAGS' "$source" || true)" 24 + cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)" 13 25 14 26 case "$extension" in 15 27 "cpp") 16 - tool="g++" 28 + darwin_tool="g++" 29 + native_tool="clang++" 17 30 ;; 18 - "c" | "m") 19 - tool="gcc" 31 + "c") 32 + darwin_tool="gcc" 33 + native_tool="clang" 34 + ;; 35 + "m") 36 + darwin_tool="gcc" 37 + native_tool="clang" 38 + cflags_native="-l:libobjc.so.4 -lgnustep-base" 39 + cflags_darwin="-lobjc" 20 40 ;; 21 41 *) 22 42 echo "Unsupported file type: $extension" 23 43 exit 1 24 44 esac 25 45 46 + tput bold 26 47 echo "=====" 27 48 echo "Running test '$source'" 28 49 echo "=====" 50 + tput sgr0 29 51 30 52 cflags="$(grep '// CFLAGS' "$source" || true)" 31 53 cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)" ··· 33 55 echo "Copying the source code to Darwin..." 34 56 scp "$source" "$BUILDSERVER:/tmp/$$.$source_fn" >/dev/null 35 57 echo "Building the source code for Darwin..." 36 - ssh "$BUILDSERVER" "$tool $cflags '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'" 58 + ssh "$BUILDSERVER" "$darwin_tool $cflags $cflags_darwin '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'" 37 59 echo "Copying the binary over..." 38 60 scp "$BUILDSERVER:/tmp/$$.$source_fn.bin" "/tmp" >/dev/null 39 61 ssh "$BUILDSERVER" "rm -f /tmp/$$.$source_fn*" ··· 43 65 rm -f "/tmp/$$.$source_fn.bin" 44 66 45 67 echo "Compiling native..." 46 - $tool $cflags "$source" -o "/tmp/$$.$source_fn.bin" 68 + $native_tool $cflags $cflags_native "$source" -o "/tmp/$$.$source_fn.bin" 47 69 echo "Running native binary..." 48 70 out_native=$("/tmp/$$.$source_fn.bin") 49 71 50 72 if [ "$out_darwin" != "$out_native" ]; then 51 - echo "*** ERROR: Output doesn't match!" 73 + tput setaf 1 74 + tput bold 75 + echo "*** ERROR: Outputs do not match!" 76 + tput sgr0 52 77 echo "---" 78 + tput setaf 3 53 79 echo "Darwin build:" 80 + tput sgr0 54 81 echo "---" 55 82 echo "$out_darwin" 56 83 echo "---" 84 + tput setaf 3 57 85 echo "Native build:" 86 + tput sgr0 58 87 echo "---" 59 88 echo "$out_native" 60 89 exit 1 61 90 fi 62 91 rm -f "/tmp/$$.$source_fn.bin" 63 92 93 + tput setaf 2 64 94 echo "Everything OK, outputs match" 95 + tput sgr0 65 96 } 66 97 67 98 if [[ "$0" == *runtest32 ]]; then 68 99 CPPFLAGS="-m32" 69 100 DYLD="dyld32" 70 101 echo "32-bit mode" 102 + fi 103 + if [[ "$0" == *runtest64 ]]; then 104 + DYLD="dyld64" 105 + echo "64-bit mode" 71 106 fi 72 107 73 108 for test in "$@"; do 74 109 runtest "$test" 75 110 done 76 111 112 + tput bold 113 + tput setaf 2 114 + echo '=====' 115 + echo 'ALL GOOD' 116 + echo '=====' 117 + tput sgr0 118 +
+1
tests/runtest64
··· 1 + runtest
+13
tests/src/ncurses_acs.c
··· 1 + // CFLAGS: -lncurses 2 + #include <curses.h> 3 + 4 + int main() 5 + { 6 + int v; 7 + initscr(); 8 + v = acs_map['x']; 9 + endwin(); 10 + printf("%d\n", v); 11 + return 0; 12 + } 13 +
+16 -6
tests/src/objc_helloworld.m
··· 1 - // CFLAGS: -lobjc 2 1 #include <stdio.h> 3 2 #import <Foundation/NSObject.h> 4 3 ··· 11 10 @implementation helloclass 12 11 - (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g 13 12 { 14 - puts("Hello world"); 13 + printf("Hello world, %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g); 15 14 } 16 15 @end 17 16 17 + void run() 18 + { 19 + helloclass* c = [helloclass alloc]; 20 + puts("After alloc"); 21 + [c init]; 22 + puts("After init"); 23 + [c doHello:1:2:3:4:5:6:7]; 24 + puts("After hello"); 25 + [c release]; 26 + puts("After release"); 27 + } 28 + 18 29 int main() 19 30 { 20 - helloclass* c = [[helloclass alloc] init]; 21 - [c doHellox:1:2:3:4:5:6:7]; 22 - [c release]; 23 - return 0; 31 + run(); 32 + run(); 33 + return 0; 24 34 } 25 35 26 36
+23
tests/src/objc_helloworld_static.m
··· 1 + #include <stdio.h> 2 + #import <Foundation/NSObject.h> 3 + 4 + @interface helloclass : NSObject { 5 + } 6 + 7 + + (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g; 8 + @end 9 + 10 + @implementation helloclass 11 + + (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g 12 + { 13 + printf("Hello world, %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g); 14 + } 15 + @end 16 + 17 + int main() 18 + { 19 + [helloclass doHello:1:2:3:4:5:6:7]; 20 + return 0; 21 + } 22 + 23 +
+34
tests/src/objc_ivars.m
··· 1 + #include <stdio.h> 2 + #import <Foundation/NSObject.h> 3 + 4 + @interface helloclass : NSObject { 5 + // We actually need to have two variables here, or the instance sizes won't match 6 + // It seems that the instance size is not rounded up to 4/8 for types generated at runtime 7 + @public int variable, var2; 8 + } 9 + 10 + - (void)doHello; 11 + @end 12 + 13 + @implementation helloclass 14 + // @synthesize variable; 15 + - (void)doHello 16 + { 17 + printf("Hello world, %d\n", variable); 18 + } 19 + @end 20 + 21 + int main() 22 + { 23 + printf("instance size: %d\n", (int) class_getInstanceSize([helloclass class])); 24 + helloclass* c = [helloclass new]; 25 + puts("after alloc"); 26 + c->variable = 3; 27 + puts("after ivarSet"); 28 + [c doHello]; 29 + puts("after doHello"); 30 + [c release]; 31 + return 0; 32 + } 33 + 34 +
+32
tests/src/objc_stret.m
··· 1 + #include <stdio.h> 2 + #import <Foundation/NSObject.h> 3 + 4 + struct st 5 + { 6 + float f1; 7 + int buf[50]; 8 + }; 9 + 10 + @interface helloclass : NSObject { 11 + } 12 + 13 + + (struct st)mult:(float)a :(float)b; 14 + @end 15 + 16 + @implementation helloclass 17 + + (struct st)mult:(float)a :(float)b 18 + { 19 + struct st s; 20 + s.f1 = a+b; 21 + return s; 22 + } 23 + @end 24 + 25 + int main() 26 + { 27 + struct st v = [helloclass mult:1.0f:2.0f]; 28 + printf("%f\n", v.f1); 29 + return 0; 30 + } 31 + 32 +