mirror of
https://github.com/darlinghq/darling.git
synced 2024-11-23 12:19:43 +00:00
ObjC runtime work: objc_msgSend and objc_msgSend_stret, ivars support, working class reg (w/o protocols), trivial benchmarking framework
This commit is contained in:
parent
db88d34643
commit
758e751594
92
benchmarks/runtest
Executable file
92
benchmarks/runtest
Executable file
@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
|
||||
BUILDSERVER="osx"
|
||||
CPPFLAGS=""
|
||||
DYLD="dyld"
|
||||
|
||||
set -e
|
||||
|
||||
failure() {
|
||||
tput setaf 1
|
||||
tput bold
|
||||
echo "Benchmark failure - the last command failed to execute"
|
||||
tput sgr0
|
||||
}
|
||||
|
||||
runtest() {
|
||||
trap failure ERR
|
||||
|
||||
source="$1"
|
||||
extension="${source##*.}"
|
||||
source_fn=$(echo "$source" | sed 's/\//_/g')
|
||||
|
||||
cflags="$(grep '// CFLAGS' "$source" || true)"
|
||||
cflags="-O3 $CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
|
||||
|
||||
case "$extension" in
|
||||
"cpp")
|
||||
darwin_tool="g++"
|
||||
native_tool="clang++"
|
||||
;;
|
||||
"c")
|
||||
darwin_tool="gcc"
|
||||
native_tool="clang"
|
||||
;;
|
||||
"m")
|
||||
darwin_tool="gcc"
|
||||
native_tool="clang"
|
||||
cflags_native="-l:libobjc.so.4 -lgnustep-base"
|
||||
cflags_darwin="-lobjc"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported file type: $extension"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
tput bold
|
||||
echo "====="
|
||||
echo "Running benchmark '$source'"
|
||||
echo "====="
|
||||
tput sgr0
|
||||
|
||||
cflags="$(grep '// CFLAGS' "$source" || true)"
|
||||
cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
|
||||
|
||||
echo "Copying the source code to Darwin..."
|
||||
scp "$source" "$BUILDSERVER:/tmp/$$.$source_fn" >/dev/null
|
||||
echo "Building the source code for Darwin..."
|
||||
ssh "$BUILDSERVER" "$darwin_tool $cflags $cflags_darwin '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'"
|
||||
echo "Copying the binary over..."
|
||||
scp "$BUILDSERVER:/tmp/$$.$source_fn.bin" "/tmp" >/dev/null
|
||||
ssh "$BUILDSERVER" "rm -f /tmp/$$.$source_fn*"
|
||||
|
||||
echo "Running Darwin binary..."
|
||||
tput bold
|
||||
time $DYLD "/tmp/$$.$source_fn.bin"
|
||||
tput sgr0
|
||||
rm -f "/tmp/$$.$source_fn.bin"
|
||||
|
||||
echo "Compiling native..."
|
||||
$native_tool $cflags $cflags_native "$source" -o "/tmp/$$.$source_fn.bin"
|
||||
echo "Running native binary..."
|
||||
tput bold
|
||||
time "/tmp/$$.$source_fn.bin"
|
||||
tput sgr0
|
||||
|
||||
rm -f "/tmp/$$.$source_fn.bin"
|
||||
}
|
||||
|
||||
if [[ "$0" == *runtest32 ]]; then
|
||||
CPPFLAGS="-m32"
|
||||
DYLD="dyld32"
|
||||
echo "32-bit mode"
|
||||
fi
|
||||
if [[ "$0" == *runtest64 ]]; then
|
||||
DYLD="dyld64"
|
||||
echo "64-bit mode"
|
||||
fi
|
||||
|
||||
for test in "$@"; do
|
||||
runtest "$test"
|
||||
done
|
||||
|
1
benchmarks/runtest32
Symbolic link
1
benchmarks/runtest32
Symbolic link
@ -0,0 +1 @@
|
||||
runtest
|
1
benchmarks/runtest64
Symbolic link
1
benchmarks/runtest64
Symbolic link
@ -0,0 +1 @@
|
||||
runtest
|
29
benchmarks/src/objc_msgSend.m
Normal file
29
benchmarks/src/objc_msgSend.m
Normal file
@ -0,0 +1,29 @@
|
||||
#include <stdio.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface helloclass : NSObject {
|
||||
}
|
||||
|
||||
+ (int)mult:(int)a :(int)b;
|
||||
@end
|
||||
|
||||
@implementation helloclass
|
||||
+ (int)mult:(int)a :(int)b
|
||||
{
|
||||
return a*b;
|
||||
}
|
||||
@end
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
volatile int j;
|
||||
for (i = 0; i < 10000000; i++)
|
||||
{
|
||||
j += [helloclass mult:1:2];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
37
benchmarks/src/objc_stret.m
Normal file
37
benchmarks/src/objc_stret.m
Normal file
@ -0,0 +1,37 @@
|
||||
#include <stdio.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
struct st
|
||||
{
|
||||
float f1;
|
||||
int buf[50];
|
||||
};
|
||||
|
||||
@interface helloclass : NSObject {
|
||||
}
|
||||
|
||||
+ (struct st)mult:(float)a :(float)b;
|
||||
@end
|
||||
|
||||
@implementation helloclass
|
||||
+ (struct st)mult:(float)a :(float)b
|
||||
{
|
||||
struct st s;
|
||||
s.f1 = a+b;
|
||||
return s;
|
||||
}
|
||||
@end
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 10000000; i++)
|
||||
{
|
||||
struct st v = [helloclass mult:1.0f:2.0f];
|
||||
v.f1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,15 @@ enable_language(ASM_NASM)
|
||||
#configure_file(config.h.in config.h)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -fPIC -ggdb")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -fPIC -ggdb")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fPIC")
|
||||
|
||||
set(objcdarwin_SRCS
|
||||
objc_msgSend.nasm
|
||||
objc_msgSend_fixup.nasm
|
||||
objc_msgSend_stret.nasm
|
||||
registers.nasm
|
||||
NameTranslate.cpp
|
||||
ClassRegister.cpp
|
||||
ClassRegister.mm
|
||||
)
|
||||
|
||||
add_library(objc.A.dylib SHARED ${objcdarwin_SRCS})
|
||||
|
@ -1,214 +0,0 @@
|
||||
#include "ClassRegister.h"
|
||||
#include "../dyld/public.h"
|
||||
#include "class.h"
|
||||
#include "method_list.h"
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
/*
|
||||
__DATA,__objc_classlist was __OBJC2,__class_list
|
||||
__DATA,__objc_catlist was __OBJC2,__category_list
|
||||
__DATA,__objc_protolist was __OBJC2,__protocol_list
|
||||
__DATA,__objc_msgrefs was __OBJC2,__message_refs
|
||||
__DATA,__objc_classrefs was __OBJC2,__class_refs
|
||||
__DATA,__objc_superrefs was __OBJC2,__super_refs
|
||||
__DATA,__objc_imageinfo was __OBJC,__image_info
|
||||
*/
|
||||
|
||||
#define SEG_OBJC_CLASSLIST_NEW "__DATA"
|
||||
#define SECT_OBJC_CLASSLIST_NEW "__objc_classlist"
|
||||
#define SEG_OBJC_CLASSLIST_OLD "__OBJC"
|
||||
#define SECT_OBJC_CLASSLIST_OLD "__class" // TODO: check if correct
|
||||
|
||||
static std::map<const void*, objc_class*> g_convertedClasses;
|
||||
static std::pair<uintptr_t, uintptr_t> g_cstringSection;
|
||||
|
||||
static void ProcessImageLoad(const struct mach_header* mh, intptr_t slide);
|
||||
static void ProcessImageUnload(const struct mach_header* mh, intptr_t slide);
|
||||
static void RegisterClass(const class_t* cls);
|
||||
static void RegisterClass(const old_class* cls);
|
||||
static old_class* PtrToClass(old_class_ptr ptr);
|
||||
|
||||
// Here we process Mach-O files that have been loaded before this native library
|
||||
// Then we register a handler to process all images loaded in the future
|
||||
__attribute__((constructor))
|
||||
void RegisterAlreadyLoadedClasses()
|
||||
{
|
||||
for (uint32_t i = 0; i < _dyld_image_count(); i++)
|
||||
{
|
||||
const struct mach_header* hdr = _dyld_get_image_header(i);
|
||||
ProcessImageLoad(hdr, 0);
|
||||
}
|
||||
|
||||
_dyld_register_func_for_add_image(ProcessImageLoad);
|
||||
_dyld_register_func_for_remove_image(ProcessImageUnload);
|
||||
|
||||
//std::cout << "Done registering\n";
|
||||
}
|
||||
|
||||
Class objcdarwin_class_lookup(const class_t* cls)
|
||||
{
|
||||
//std::cout << "Looking up " << cls << std::endl << std::flush;
|
||||
auto it = g_convertedClasses.find(cls);
|
||||
if (it != g_convertedClasses.end())
|
||||
return it->second;
|
||||
else
|
||||
return Class(cls); // assume native or instance ptr
|
||||
}
|
||||
|
||||
// This is very wrong. Does it have to be this way?s
|
||||
old_class* PtrToClass(old_class_ptr ptr)
|
||||
{
|
||||
if (ptr.ptrValue >= g_cstringSection.first && ptr.ptrValue < g_cstringSection.first+g_cstringSection.second)
|
||||
{
|
||||
//std::cout << ptr.cls << " found to be a string: " << ptr.name << std::endl;
|
||||
id cls = objc_getClass(ptr.name); // it is a string
|
||||
|
||||
assert(cls != nullptr);
|
||||
return (old_class*) cls;
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << ptr.cls << " found to be ptr to class.\n";
|
||||
return ptr.cls;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ListType> void ConvertMethodListGen(Class c, const ListType* list)
|
||||
{
|
||||
std::cout << list->count << " methods within\n";
|
||||
|
||||
for (size_t i = 0; i < list->count; i++)
|
||||
{
|
||||
auto* m = &list->method_list[i];
|
||||
|
||||
std::cout << "Method: selName: " << m->selName << "; types: " << m->types << "; impl: " << m->impl << std::endl;
|
||||
|
||||
SEL sel = sel_registerName(m->selName);
|
||||
class_addMethod(c, sel, reinterpret_cast<IMP>(m->impl), m->types);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterClass(const class_t* cls)
|
||||
{
|
||||
std::cout << "Processing ObjC class " << cls->data()->className;
|
||||
std::cout << std::endl;
|
||||
|
||||
// objc_allocateClassPair
|
||||
// objc_registerClassPair
|
||||
const class_t* meta = cls->isa;
|
||||
Class conv, super;
|
||||
if (g_convertedClasses.find(cls->superclass) != g_convertedClasses.end())
|
||||
super = g_convertedClasses[cls->superclass];
|
||||
else
|
||||
super = reinterpret_cast<objc_class*>(cls->superclass);
|
||||
|
||||
conv = objc_allocateClassPair(super, cls->data()->className, 0);
|
||||
|
||||
const class_ro_t* ro = cls->data();
|
||||
const class_ro_t* roMeta = meta->data();
|
||||
|
||||
if (ro->baseMethods)
|
||||
ConvertMethodListGen(conv, ro->baseMethods);
|
||||
if (roMeta->baseMethods)
|
||||
ConvertMethodListGen(conv->isa, roMeta->baseMethods);
|
||||
|
||||
conv->instance_size = ro->instSize;
|
||||
conv->isa->instance_size = roMeta->instSize;
|
||||
|
||||
objc_registerClassPair(conv);
|
||||
g_convertedClasses[cls] = conv;
|
||||
g_convertedClasses[meta] = conv->isa;
|
||||
}
|
||||
|
||||
void RegisterClass(const old_class* cls)
|
||||
{
|
||||
std::cout << "Processing old ObjC class " << cls->name;
|
||||
std::cout << std::endl;
|
||||
|
||||
const old_class* meta = PtrToClass(cls->isa);
|
||||
Class conv, super;
|
||||
old_class* psuper = PtrToClass(cls->super_class);
|
||||
|
||||
if (g_convertedClasses.find(psuper) != g_convertedClasses.end())
|
||||
super = g_convertedClasses[psuper];
|
||||
else
|
||||
super = reinterpret_cast<objc_class*>(psuper);
|
||||
|
||||
conv = objc_allocateClassPair(super, cls->name, 0);
|
||||
|
||||
std::cout << "Instance methods...\n";
|
||||
if (cls->methodList)
|
||||
ConvertMethodListGen(conv, cls->methodList);
|
||||
std::cout << "Class methods...\n";
|
||||
if (meta->methodList)
|
||||
ConvertMethodListGen(conv->isa, meta->methodList);
|
||||
|
||||
objc_registerClassPair(conv);
|
||||
|
||||
assert(id(conv) == objc_getClass(cls->name));
|
||||
assert(id(conv->isa) == objc_getMetaClass(cls->name));
|
||||
assert(class_isMetaClass(conv->isa));
|
||||
assert(!class_isMetaClass(conv));
|
||||
|
||||
g_convertedClasses[cls] = conv;
|
||||
g_convertedClasses[cls->name] = conv;
|
||||
g_convertedClasses[meta] = conv->isa;
|
||||
|
||||
/*
|
||||
SEL ss = sel_get_any_uid("doHello:::::::");
|
||||
std::cout << "objc_msg_lookup: " << conv << ' ' << ss << std::endl;
|
||||
void* p = (void*) objc_msg_lookup(id(conv), ss);
|
||||
|
||||
std::cout << "Impl: " << p << "; Sel: " << ss << std::endl;
|
||||
*/
|
||||
}
|
||||
|
||||
void ProcessImageLoad(const struct mach_header* mh, intptr_t)
|
||||
{
|
||||
unsigned long size;
|
||||
const class_t** classes;
|
||||
|
||||
classes = reinterpret_cast<const class_t**>(
|
||||
getsectdata(mh, SEG_OBJC_CLASSLIST_NEW, SECT_OBJC_CLASSLIST_NEW, &size)
|
||||
);
|
||||
|
||||
if (!classes)
|
||||
{
|
||||
// Try the old runtime
|
||||
const old_class* classes;
|
||||
|
||||
classes = reinterpret_cast<const old_class*>(
|
||||
getsectdata(mh, SEG_OBJC_CLASSLIST_OLD, SECT_OBJC_CLASSLIST_OLD, &size)
|
||||
);
|
||||
|
||||
if (classes)
|
||||
{
|
||||
// g_cstringSection
|
||||
unsigned long cstrLen;
|
||||
void* ptr;
|
||||
|
||||
ptr = getsectdata(mh, "__TEXT", "__cstring", &cstrLen);
|
||||
if (ptr)
|
||||
g_cstringSection = std::pair<uintptr_t,uintptr_t>(uintptr_t(ptr), cstrLen);
|
||||
else
|
||||
g_cstringSection = std::pair<uintptr_t,uintptr_t>(0, 0);
|
||||
|
||||
for (size_t i = 0; i < size / sizeof(old_class); i++)
|
||||
RegisterClass(classes+i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < size / sizeof(class_t*); i++)
|
||||
RegisterClass(classes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessImageUnload(const struct mach_header* mh, intptr_t)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
#ifndef CLASSREGISTER_H
|
||||
#define CLASSREGISTER_H
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <cstddef>
|
||||
#include <objc/runtime.h>
|
||||
|
||||
#ifndef INTPTR_MAX
|
||||
#error
|
||||
#endif
|
||||
|
||||
struct method_t
|
||||
{
|
||||
const char* selName;
|
||||
@ -20,6 +25,20 @@ struct method_list_t
|
||||
uint32_t entsize() const { return entsize_and_flags & ~uint32_t(3); }
|
||||
};
|
||||
|
||||
struct ivar_t
|
||||
{
|
||||
uintptr_t* offset;
|
||||
const char* name;
|
||||
const char* type;
|
||||
uint32_t alignment, size;
|
||||
};
|
||||
|
||||
struct ivar_list_t
|
||||
{
|
||||
uint32_t entsize, count;
|
||||
ivar_t ivar_list[];
|
||||
};
|
||||
|
||||
struct class_ro_t
|
||||
{
|
||||
uint32_t flags, instStart, instSize;
|
||||
@ -30,6 +49,7 @@ struct class_ro_t
|
||||
const char* className;
|
||||
const method_list_t* baseMethods; // instance methods for classes, static methods for metaclasses
|
||||
const void* baseProtocols;
|
||||
const ivar_list_t* ivars;
|
||||
|
||||
void* todo[2]; // TODO: two more pointers
|
||||
};
|
||||
@ -108,9 +128,9 @@ struct old_category
|
||||
|
||||
struct old_ivar
|
||||
{
|
||||
char *ivar_name;
|
||||
char *ivar_type;
|
||||
int ivar_offset;
|
||||
char *name;
|
||||
char *type;
|
||||
int offset;
|
||||
#ifdef __x86_64__
|
||||
int space;
|
||||
#endif
|
||||
@ -118,7 +138,7 @@ struct old_ivar
|
||||
|
||||
struct old_ivar_list
|
||||
{
|
||||
int ivar_count;
|
||||
int count;
|
||||
#ifdef __x86_64__
|
||||
int space;
|
||||
#endif
|
||||
|
46
src/libobjcdarwin/NameTranslate.cpp
Normal file
46
src/libobjcdarwin/NameTranslate.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <dlfcn.h>
|
||||
|
||||
extern "C" void* _objc_empty_cache = 0;
|
||||
extern "C" void* _objc_empty_vtable = 0;
|
||||
|
||||
namespace Darling
|
||||
{
|
||||
typedef bool (*DlsymHookFunc)(char* symName);
|
||||
void registerDlsymHook(DlsymHookFunc func);
|
||||
void deregisterDlsymHook(DlsymHookFunc func);
|
||||
};
|
||||
|
||||
static bool ClassTranslator(char* symName);
|
||||
|
||||
__attribute__((constructor))
|
||||
static void initTranslation()
|
||||
{
|
||||
Darling::registerDlsymHook(ClassTranslator);
|
||||
}
|
||||
|
||||
__attribute__((destructor))
|
||||
static void exitTranslation()
|
||||
{
|
||||
Darling::deregisterDlsymHook(ClassTranslator);
|
||||
}
|
||||
|
||||
static bool ClassTranslator(char* name)
|
||||
{
|
||||
if (char* off = strstr(name, "_$_"))
|
||||
{
|
||||
size_t offset = off - name;
|
||||
|
||||
memmove(name+1, name, strlen(name)+1);
|
||||
*name = '_'; // prefix with an underscore
|
||||
offset++;
|
||||
// remove $_
|
||||
memmove(name + offset, name + offset + 2, strlen(name + offset + 2)+1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -3,35 +3,14 @@ global __darwin_objc_msgSend
|
||||
extern objcdarwin_class_lookup
|
||||
extern objc_msg_lookup
|
||||
extern sel_get_any_uid
|
||||
extern objcdarwin_SaveRegisters
|
||||
extern objcdarwin_RestoreRegisters
|
||||
|
||||
%ifidn __OUTPUT_FORMAT__, elf64
|
||||
|
||||
BITS 64
|
||||
section text
|
||||
|
||||
SaveRegisters:
|
||||
pop r11
|
||||
|
||||
push r9
|
||||
push r8
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
|
||||
; save xmms?
|
||||
jmp r11
|
||||
|
||||
RestoreRegisters:
|
||||
pop r11
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop r8
|
||||
pop r9
|
||||
jmp r11
|
||||
|
||||
__darwin_objc_msgSend:
|
||||
; Procedure:
|
||||
; 1) get the converted GNU class from an Apple class
|
||||
@ -39,7 +18,7 @@ __darwin_objc_msgSend:
|
||||
; 3) run objc_msg_lookup
|
||||
; 4) jump to the pointer returned by objc_msg_lookup
|
||||
|
||||
call SaveRegisters
|
||||
call objcdarwin_SaveRegisters WRT ..plt
|
||||
call objcdarwin_class_lookup WRT ..plt
|
||||
mov [rsp], rax ; save the converted value
|
||||
|
||||
@ -49,11 +28,12 @@ __darwin_objc_msgSend:
|
||||
; rax now has the GNU selector
|
||||
; move rax to the second argument
|
||||
mov rsi, rax
|
||||
mov [rsp+8], rax
|
||||
; restore the first argument
|
||||
mov rdi, [rsp]
|
||||
call objc_msg_lookup WRT ..plt
|
||||
|
||||
call RestoreRegisters
|
||||
call objcdarwin_RestoreRegisters WRT ..plt
|
||||
jmp rax
|
||||
|
||||
%elifidn __OUTPUT_FORMAT__, elf
|
||||
@ -61,8 +41,6 @@ __darwin_objc_msgSend:
|
||||
BITS 32
|
||||
section text
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;; TODO!!!
|
||||
|
||||
__darwin_objc_msgSend:
|
||||
;enter 8
|
||||
|
||||
|
@ -1,58 +1,49 @@
|
||||
global objc_msgSend_fixup
|
||||
global __darwin_objc_msgSend_fixup
|
||||
|
||||
extern objcdarwin_class_lookup
|
||||
extern objc_msg_lookup
|
||||
extern sel_get_any_uid
|
||||
extern objcdarwin_SaveRegisters
|
||||
extern objcdarwin_RestoreRegisters
|
||||
|
||||
%ifidn __OUTPUT_FORMAT__, elf64
|
||||
|
||||
BITS 64
|
||||
section text
|
||||
|
||||
SaveRegisters:
|
||||
pop r11
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push r8
|
||||
push r9
|
||||
; save xmms?
|
||||
jmp r11
|
||||
;__darwin_objc_msgSend_fixed:
|
||||
; add rsi, 8
|
||||
; jmp __darwin_objc_msgSend WRT ..plt
|
||||
|
||||
RestoreRegisters:
|
||||
pop r11
|
||||
pop r9
|
||||
pop r8
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
jmp r11
|
||||
|
||||
objc_msgSend_fixup:
|
||||
__darwin_objc_msgSend_fixup:
|
||||
; Procedure:
|
||||
; 1) convert Apple selector to GNU
|
||||
; 2) run objc_msg_lookup
|
||||
; 3) save the method address into the second argument
|
||||
; 4) save the right selector into the second argument
|
||||
; 5) jump to the pointer returned by objc_msg_lookup
|
||||
; 1) get the converted GNU class from an Apple class
|
||||
; 2) convert Apple selector to GNU
|
||||
; 3) run objc_msg_lookup
|
||||
; 4) jump to the pointer returned by objc_msg_lookup
|
||||
|
||||
call objcdarwin_SaveRegisters WRT ..plt
|
||||
call objcdarwin_class_lookup WRT ..plt
|
||||
mov [rsp], rax ; save the converted value
|
||||
|
||||
call SaveRegisters
|
||||
; move the second argument into the first argument
|
||||
mov rdi, rsi
|
||||
add rdi, 8 ; selector name is the second pointer in struct
|
||||
mov rdi, [rsp+8]
|
||||
add rdi, 8 ; the selector itself is the second element of what we receive as SEL
|
||||
mov rdi, [rdi]
|
||||
call sel_get_any_uid WRT ..plt
|
||||
; rax now has the GNU selector
|
||||
mov rsi, [rsp+8] ; put the second argument back to rsi
|
||||
mov [rsi+8], rax ; store the GNU selector
|
||||
add rsi, 8 ; pass the selector itself
|
||||
; move rax to the second argument
|
||||
mov rsi, rax
|
||||
|
||||
; restore the first argument
|
||||
mov rdi, [rsp]
|
||||
call objc_msg_lookup WRT ..plt
|
||||
|
||||
; optimize the next call by fixing the function pointer
|
||||
mov rsi, [rsp+8]
|
||||
;mov [rsi], rax ; TODO: fixups not working, the target method still isn't getting the selector it expects
|
||||
|
||||
call RestoreRegisters
|
||||
; can we do it like this? don't we still need a proxy?
|
||||
mov [rsi], rax ; fix the pointer to make the call direct next time
|
||||
call objcdarwin_RestoreRegisters WRT ..plt
|
||||
jmp rax
|
||||
|
||||
%elifidn __OUTPUT_FORMAT__, elf
|
||||
@ -60,26 +51,36 @@ objc_msgSend_fixup:
|
||||
BITS 32
|
||||
section text
|
||||
|
||||
objc_msgSend_fixup:
|
||||
; swap the first two arguments
|
||||
mov ecx, [esp+4]
|
||||
xchg [esp+8], ecx
|
||||
add ecx, 4 ; move to the second struct element
|
||||
mov [esp+4], ecx
|
||||
|
||||
call sel_get_any_uid WRT ..plt
|
||||
|
||||
; eax now has the GNU selector
|
||||
; swap the arguments back
|
||||
__darwin_objc_msgSend_fixup:
|
||||
|
||||
mov ecx, [esp+4]
|
||||
mov [ecx], eax ; fix the selector in the original struct
|
||||
push ecx ; arg for func call
|
||||
|
||||
; swap the arguments back
|
||||
mov ecx, [esp+4]
|
||||
xchg [esp+8], ecx
|
||||
mov [esp+4], ecx
|
||||
call objc_msg_lookup WRT ..plt
|
||||
call objcdarwin_class_lookup ;WRT ..plt
|
||||
|
||||
add esp, 4 ; remove argument
|
||||
mov [esp+4], eax ; change the class id
|
||||
|
||||
mov ecx, [esp+8] ; second argument
|
||||
add ecx, 4 ; the selector itself is the second element of what we receive as SEL
|
||||
mov ecx, [ecx]
|
||||
push ecx
|
||||
|
||||
call sel_get_any_uid ;WRT ..plt
|
||||
|
||||
add esp, 4
|
||||
mov [esp+8], eax
|
||||
|
||||
push eax ; reuse the sel_get_any_uid retval
|
||||
mov eax, [esp+8]
|
||||
push eax ; class id
|
||||
|
||||
call objc_msg_lookup ;WRT ..plt
|
||||
add esp, 8
|
||||
|
||||
; optimize the next call by fixing the function pointer
|
||||
mov ecx, [esp+8]
|
||||
;mov [ecx], eax ; TODO: fixups not working, the target method still isn't getting the selector it expects
|
||||
|
||||
jmp eax
|
||||
|
||||
|
77
src/libobjcdarwin/objc_msgSend_stret.nasm
Normal file
77
src/libobjcdarwin/objc_msgSend_stret.nasm
Normal file
@ -0,0 +1,77 @@
|
||||
global __darwin_objc_msgSend_stret
|
||||
|
||||
extern objcdarwin_class_lookup
|
||||
extern objc_msg_lookup
|
||||
extern sel_get_any_uid
|
||||
extern objcdarwin_SaveRegisters
|
||||
extern objcdarwin_RestoreRegisters
|
||||
|
||||
%ifidn __OUTPUT_FORMAT__, elf64
|
||||
|
||||
BITS 64
|
||||
section text
|
||||
|
||||
; Compared to ordinary msgSend, arguments are shifted by one - first arg is the stret
|
||||
__darwin_objc_msgSend_stret:
|
||||
; Procedure:
|
||||
; 1) get the converted GNU class from an Apple class
|
||||
; 2) convert Apple selector to GNU
|
||||
; 3) run objc_msg_lookup
|
||||
; 4) jump to the pointer returned by objc_msg_lookup
|
||||
|
||||
call objcdarwin_SaveRegisters WRT ..plt
|
||||
mov rdi, rsi
|
||||
call objcdarwin_class_lookup WRT ..plt
|
||||
mov [rsp+8], rax ; save the converted value
|
||||
|
||||
; move the second argument into the first argument
|
||||
mov rdi, [rsp+16]
|
||||
call sel_get_any_uid WRT ..plt
|
||||
; rax now has the GNU selector
|
||||
; move rax to the second argument
|
||||
mov rsi, rax
|
||||
mov [rsp+16], rax
|
||||
; restore the first argument
|
||||
mov rdi, [rsp+8]
|
||||
call objc_msg_lookup WRT ..plt
|
||||
|
||||
call objcdarwin_RestoreRegisters WRT ..plt
|
||||
jmp rax
|
||||
|
||||
%elifidn __OUTPUT_FORMAT__, elf
|
||||
|
||||
BITS 32
|
||||
section text
|
||||
|
||||
__darwin_objc_msgSend_stret:
|
||||
|
||||
mov ecx, [esp+8]
|
||||
push ecx ; arg for func call
|
||||
|
||||
call objcdarwin_class_lookup ;WRT ..plt
|
||||
|
||||
add esp, 4 ; remove argument
|
||||
mov [esp+8], eax ; change the class id
|
||||
|
||||
mov ecx, [esp+12] ; second argument
|
||||
push ecx
|
||||
|
||||
call sel_get_any_uid ;WRT ..plt
|
||||
|
||||
add esp, 4
|
||||
mov [esp+12], eax
|
||||
|
||||
push eax ; reuse the sel_get_any_uid retval
|
||||
mov eax, [esp+12]
|
||||
push eax ; class id
|
||||
|
||||
call objc_msg_lookup ;WRT ..plt
|
||||
add esp, 8
|
||||
|
||||
jmp eax
|
||||
|
||||
%else
|
||||
|
||||
%error "Unsupported platform"
|
||||
|
||||
%endif
|
51
src/libobjcdarwin/registers.nasm
Normal file
51
src/libobjcdarwin/registers.nasm
Normal file
@ -0,0 +1,51 @@
|
||||
%ifidn __OUTPUT_FORMAT__, elf64
|
||||
|
||||
BITS 64
|
||||
section text
|
||||
|
||||
global objcdarwin_SaveRegisters
|
||||
global objcdarwin_RestoreRegisters
|
||||
|
||||
objcdarwin_SaveRegisters:
|
||||
pop r11
|
||||
|
||||
sub rsp, 128 ; 8*16
|
||||
movdqu [rsp], xmm0
|
||||
movdqu [rsp+16], xmm1
|
||||
movdqu [rsp+32], xmm2
|
||||
movdqu [rsp+48], xmm3
|
||||
movdqu [rsp+64], xmm4
|
||||
movdqu [rsp+80], xmm5
|
||||
movdqu [rsp+96], xmm6
|
||||
movdqu [rsp+112], xmm7
|
||||
|
||||
push r9
|
||||
push r8
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
|
||||
jmp r11
|
||||
|
||||
objcdarwin_RestoreRegisters:
|
||||
pop r11
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop r8
|
||||
pop r9
|
||||
movdqu xmm7, [rsp+112]
|
||||
movdqu xmm6, [rsp+96]
|
||||
movdqu xmm5, [rsp+80]
|
||||
movdqu xmm4, [rsp+64]
|
||||
movdqu xmm3, [rsp+48]
|
||||
movdqu xmm2, [rsp+32]
|
||||
movdqu xmm1, [rsp+16]
|
||||
movdqu xmm0, [rsp]
|
||||
add rsp, 128
|
||||
|
||||
jmp r11
|
||||
%endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
BUILDSERVER="osx"
|
||||
CPPFLAGS=""
|
||||
@ -6,26 +6,48 @@ DYLD="dyld"
|
||||
|
||||
set -e
|
||||
|
||||
failure() {
|
||||
tput setaf 1
|
||||
tput bold
|
||||
echo "Test failure - the last command failed to execute"
|
||||
tput sgr0
|
||||
}
|
||||
|
||||
runtest() {
|
||||
trap failure ERR
|
||||
|
||||
source="$1"
|
||||
extension="${source##*.}"
|
||||
source_fn=$(echo "$source" | sed 's/\//_/g')
|
||||
|
||||
cflags="$(grep '// CFLAGS' "$source" || true)"
|
||||
cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
|
||||
|
||||
case "$extension" in
|
||||
"cpp")
|
||||
tool="g++"
|
||||
darwin_tool="g++"
|
||||
native_tool="clang++"
|
||||
;;
|
||||
"c" | "m")
|
||||
tool="gcc"
|
||||
"c")
|
||||
darwin_tool="gcc"
|
||||
native_tool="clang"
|
||||
;;
|
||||
"m")
|
||||
darwin_tool="gcc"
|
||||
native_tool="clang"
|
||||
cflags_native="-l:libobjc.so.4 -lgnustep-base"
|
||||
cflags_darwin="-lobjc"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported file type: $extension"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
tput bold
|
||||
echo "====="
|
||||
echo "Running test '$source'"
|
||||
echo "====="
|
||||
tput sgr0
|
||||
|
||||
cflags="$(grep '// CFLAGS' "$source" || true)"
|
||||
cflags="$CPPFLAGS -w $(echo "$cflags" | cut -b 12-)"
|
||||
@ -33,7 +55,7 @@ runtest() {
|
||||
echo "Copying the source code to Darwin..."
|
||||
scp "$source" "$BUILDSERVER:/tmp/$$.$source_fn" >/dev/null
|
||||
echo "Building the source code for Darwin..."
|
||||
ssh "$BUILDSERVER" "$tool $cflags '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'"
|
||||
ssh "$BUILDSERVER" "$darwin_tool $cflags $cflags_darwin '/tmp/$$.$source_fn' -o '/tmp/$$.$source_fn.bin'"
|
||||
echo "Copying the binary over..."
|
||||
scp "$BUILDSERVER:/tmp/$$.$source_fn.bin" "/tmp" >/dev/null
|
||||
ssh "$BUILDSERVER" "rm -f /tmp/$$.$source_fn*"
|
||||
@ -43,25 +65,34 @@ runtest() {
|
||||
rm -f "/tmp/$$.$source_fn.bin"
|
||||
|
||||
echo "Compiling native..."
|
||||
$tool $cflags "$source" -o "/tmp/$$.$source_fn.bin"
|
||||
$native_tool $cflags $cflags_native "$source" -o "/tmp/$$.$source_fn.bin"
|
||||
echo "Running native binary..."
|
||||
out_native=$("/tmp/$$.$source_fn.bin")
|
||||
|
||||
if [ "$out_darwin" != "$out_native" ]; then
|
||||
echo "*** ERROR: Output doesn't match!"
|
||||
tput setaf 1
|
||||
tput bold
|
||||
echo "*** ERROR: Outputs do not match!"
|
||||
tput sgr0
|
||||
echo "---"
|
||||
tput setaf 3
|
||||
echo "Darwin build:"
|
||||
tput sgr0
|
||||
echo "---"
|
||||
echo "$out_darwin"
|
||||
echo "---"
|
||||
tput setaf 3
|
||||
echo "Native build:"
|
||||
tput sgr0
|
||||
echo "---"
|
||||
echo "$out_native"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "/tmp/$$.$source_fn.bin"
|
||||
|
||||
tput setaf 2
|
||||
echo "Everything OK, outputs match"
|
||||
tput sgr0
|
||||
}
|
||||
|
||||
if [[ "$0" == *runtest32 ]]; then
|
||||
@ -69,8 +100,19 @@ if [[ "$0" == *runtest32 ]]; then
|
||||
DYLD="dyld32"
|
||||
echo "32-bit mode"
|
||||
fi
|
||||
if [[ "$0" == *runtest64 ]]; then
|
||||
DYLD="dyld64"
|
||||
echo "64-bit mode"
|
||||
fi
|
||||
|
||||
for test in "$@"; do
|
||||
runtest "$test"
|
||||
done
|
||||
|
||||
tput bold
|
||||
tput setaf 2
|
||||
echo '====='
|
||||
echo 'ALL GOOD'
|
||||
echo '====='
|
||||
tput sgr0
|
||||
|
||||
|
1
tests/runtest64
Symbolic link
1
tests/runtest64
Symbolic link
@ -0,0 +1 @@
|
||||
runtest
|
13
tests/src/ncurses_acs.c
Normal file
13
tests/src/ncurses_acs.c
Normal file
@ -0,0 +1,13 @@
|
||||
// CFLAGS: -lncurses
|
||||
#include <curses.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int v;
|
||||
initscr();
|
||||
v = acs_map['x'];
|
||||
endwin();
|
||||
printf("%d\n", v);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
// CFLAGS: -lobjc
|
||||
#include <stdio.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@ -11,16 +10,27 @@
|
||||
@implementation helloclass
|
||||
- (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g
|
||||
{
|
||||
puts("Hello world");
|
||||
printf("Hello world, %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g);
|
||||
}
|
||||
@end
|
||||
|
||||
void run()
|
||||
{
|
||||
helloclass* c = [helloclass alloc];
|
||||
puts("After alloc");
|
||||
[c init];
|
||||
puts("After init");
|
||||
[c doHello:1:2:3:4:5:6:7];
|
||||
puts("After hello");
|
||||
[c release];
|
||||
puts("After release");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
helloclass* c = [[helloclass alloc] init];
|
||||
[c doHellox:1:2:3:4:5:6:7];
|
||||
[c release];
|
||||
return 0;
|
||||
run();
|
||||
run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
23
tests/src/objc_helloworld_static.m
Normal file
23
tests/src/objc_helloworld_static.m
Normal file
@ -0,0 +1,23 @@
|
||||
#include <stdio.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface helloclass : NSObject {
|
||||
}
|
||||
|
||||
+ (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g;
|
||||
@end
|
||||
|
||||
@implementation helloclass
|
||||
+ (void)doHello:(int)a :(int)b :(int)c :(int)d :(int)e :(int)f :(int)g
|
||||
{
|
||||
printf("Hello world, %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g);
|
||||
}
|
||||
@end
|
||||
|
||||
int main()
|
||||
{
|
||||
[helloclass doHello:1:2:3:4:5:6:7];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
34
tests/src/objc_ivars.m
Normal file
34
tests/src/objc_ivars.m
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface helloclass : NSObject {
|
||||
// We actually need to have two variables here, or the instance sizes won't match
|
||||
// It seems that the instance size is not rounded up to 4/8 for types generated at runtime
|
||||
@public int variable, var2;
|
||||
}
|
||||
|
||||
- (void)doHello;
|
||||
@end
|
||||
|
||||
@implementation helloclass
|
||||
// @synthesize variable;
|
||||
- (void)doHello
|
||||
{
|
||||
printf("Hello world, %d\n", variable);
|
||||
}
|
||||
@end
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("instance size: %d\n", (int) class_getInstanceSize([helloclass class]));
|
||||
helloclass* c = [helloclass new];
|
||||
puts("after alloc");
|
||||
c->variable = 3;
|
||||
puts("after ivarSet");
|
||||
[c doHello];
|
||||
puts("after doHello");
|
||||
[c release];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
32
tests/src/objc_stret.m
Normal file
32
tests/src/objc_stret.m
Normal file
@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
struct st
|
||||
{
|
||||
float f1;
|
||||
int buf[50];
|
||||
};
|
||||
|
||||
@interface helloclass : NSObject {
|
||||
}
|
||||
|
||||
+ (struct st)mult:(float)a :(float)b;
|
||||
@end
|
||||
|
||||
@implementation helloclass
|
||||
+ (struct st)mult:(float)a :(float)b
|
||||
{
|
||||
struct st s;
|
||||
s.f1 = a+b;
|
||||
return s;
|
||||
}
|
||||
@end
|
||||
|
||||
int main()
|
||||
{
|
||||
struct st v = [helloclass mult:1.0f:2.0f];
|
||||
printf("%f\n", v.f1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user