1 Commits

Author SHA1 Message Date
Karina aa6b7924e2 feat: custom objc runtime, msgSend, and HOTObject experiment 2026-02-01 18:03:08 +04:00
16 changed files with 364 additions and 7 deletions
+4
View File
@@ -0,0 +1,4 @@
If:
PathMatch: .*\.h
CompileFlags:
Add: [-x, objective-c]
+1
View File
@@ -34,6 +34,7 @@ set(SYSTEM_SERVICES
init
debug
termosh
testOBJC
)
set(VOLUME_ROOT "${CMAKE_BINARY_DIR}/StartupVolume")
+2
View File
@@ -7,7 +7,9 @@ set(CMAKE_C_EXTENSIONS OFF)
message(STATUS "Building termOS's userspace")
add_subdirectory(libobjc)
add_subdirectory(libterm)
add_subdirectory(init)
add_subdirectory(debug)
add_subdirectory(testOBJC)
add_subdirectory(termosh)
+31
View File
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.16)
project(libobjc LANGUAGES C ASM_NASM OBJC)
set(USER_C_FLAGS
-ffreestanding
-fno-builtin
-nostdlib
-nostdinc
-fno-stack-protector
-fno-pic
-fno-pie
-m64
-mno-red-zone
-mcmodel=small
-O2
)
set(LIBOBJC_SOURCES
src/runtime.c
src/lookup.c
src/msgSend.asm
src/exceptions.c
src/HOTObject.m
)
add_library(objc STATIC ${LIBOBJC_SOURCES})
target_compile_options(objc PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
target_include_directories(objc PUBLIC inc)
target_link_libraries(objc PRIVATE term)
+21
View File
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#pragma once
#include <Types.h>
typedef struct OBJCClass OBJCClass;
typedef struct OBJCObject OBJCObject;
typedef OBJCObject* id;
typedef OBJCClass* Class;
@interface HOTObject {
Class classPointer;
}
+ (id)alloc;
- (id)init;
- (id)free;
@end
+20
View File
@@ -0,0 +1,20 @@
#import <HOTObject.h>
extern id OBJCAllocateInstance(Class class);
extern void MemoryFree(void* pointer);
@implementation HOTObject
+ (id)alloc {
return OBJCAllocateInstance((Class)self);
}
- (id)init {
return self;
}
- (id)free {
MemoryFree(self);
return (id)0;
}
@end
+3
View File
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
+3
View File
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
+59
View File
@@ -0,0 +1,59 @@
; SPDX-License-Identifier: GPL-3.0-or-later
; Copyright (c) 2026 0xKarinyash
[bits 64]
section .text
global objc_msgSend
; id objc_msgSend(id self, SEL _cmd, ...)
; RDI = self, RSI = _cmd (pointer on OBJCSelector)
objc_msgSend:
test rdi, rdi
jz .return_nil
mov rax, [rdi]
.search_class:
; OBJClass:
; 0: metaClass, 8: parentClass, 16: name, 24: version, 32: info, 40: instanceSize, 48: methods
mov r10, [rax + 48] ; R10 = OBJCMethodList*
.search_method_list:
test r10, r10
jz .go_to_parent
; OBJCMethodList:
; 0: next, 8: methodCount, 12: padding, 16: methods[]
mov ecx, [r10 + 8] ; ECX = methodCount
test ecx, ecx
jz .next_list
lea r11, [r10 + 16] ; R11 start of methods[]
.loop_methods:
; OBJCMethod:
; 0: selector, 8: types, 16: functionPointer
mov r8, [r11]
cmp r8, rsi
je .found
add r11, 24
loop .loop_methods
.next_list:
mov r10, [r10] ; r10 = list->next
jmp .search_method_list
.go_to_parent:
mov rax, [rax + 8]
test rax, rax
jnz .search_class
jmp .return_nil
.found:
mov rax, [r11 + 16]
jmp rax
.return_nil:
xor rax, rax
xor rdx, rdx
ret
+121
View File
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#include <termOS.h>
typedef struct OBJCSelector OBJCSelector;
typedef struct OBJCMethod OBJCMethod;
typedef struct OBJCMethodList OBJCMethodList;
typedef struct OBJCClass OBJCClass;
typedef struct OBJCObject OBJCObject;
typedef struct OBJCSymbolTable OBJCSymbolTable;
typedef struct OBJCModule OBJCModule;
struct OBJCSelector {
const char* name;
const char* types;
};
struct OBJCMethod {
struct OBJCSelector* selector;
const char* types;
void* functionPointer; // pointer to function
};
struct OBJCMethodList {
struct OBJCMethodList* next;
Int32 methodCount;
struct OBJCMethod methods[];
};
struct OBJCClass {
OBJCClass* metaClass; // pointer to metaclass
OBJCClass* parentClass; // parent
const char* name;
Int64 version;
Int64 info;
Int64 instanceSize;
OBJCMethodList* methods;
};
struct OBJCObject {
OBJCClass* classPointer;
};
struct OBJCSymbolTable {
UInt32 selectorReferencesCount;
OBJCSelector* selectorReferences;
UInt16 classDefinitionCount;
UInt16 categoryDefinitionCount;
void* definitions[];
};
struct OBJCModule {
UInt64 version;
UInt64 size;
const char* name;
OBJCSymbolTable* symbolTable;
};
enum {
kOBJCRuntimeMaxClasses = 256
};
static OBJCClass* _classTable[kOBJCRuntimeMaxClasses];
static UInt32 _classCount = 0;
static OBJCClass* sOBJCGetClass(const char* name) {
for (UInt32 i = 0; i < _classCount; i++) {
if (StringCompare(_classTable[i]->name, name) == 0) return _classTable[i];
}
return nullptr;
}
static void sOBJCRegisterModule(OBJCModule* module) {
if (!module || !module->symbolTable) return;
OBJCSymbolTable* symbolTable = module->symbolTable;
for (UInt16 i = 0; i < symbolTable->classDefinitionCount; i++) {
OBJCClass* class = (OBJCClass*)symbolTable->definitions[i];
if (_classCount < kOBJCRuntimeMaxClasses) {
_classTable[_classCount++] = class;
}
}
}
// real
extern void* __objc_module_list_start;
extern void* __objc_module_list_end;
OBJCClass* objc_getClass(const char* name) {
return sOBJCGetClass(name);
}
void __objc_exec_class(OBJCModule* module) {
sOBJCRegisterModule(module);
}
OBJCClass* objc_lookup_class(const char* name) {
return sOBJCGetClass(name);
}
void _objc_init_runtime() {
void** currentModule = &__objc_module_list_start;
while (currentModule < &__objc_module_list_end) {
if (*currentModule) __objc_exec_class((OBJCModule*)*currentModule);
currentModule++;
}
}
OBJCObject* OBJCAllocateInstance(OBJCClass* class) {
if (!class) return nullptr;
OBJCObject* instance = (OBJCObject*)MemoryAllocate(class->instanceSize);
if (instance) {
MemorySet(instance, 0, class->instanceSize);
instance->classPointer = class;
}
return instance;
}
+24 -6
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(libterm LANGUAGES C ASM_NASM)
project(libterm LANGUAGES C ASM_NASM OBJC)
set(USER_C_FLAGS
-ffreestanding
@@ -37,18 +37,36 @@ target_compile_options(term PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
target_include_directories(term PUBLIC inc)
add_library(RuntimeEntryObject OBJECT src/RuntimeEntry.asm)
target_compile_options(RuntimeEntryObject PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
add_library(ObjCRuntimeEntryObject OBJECT src/ObjCRuntimeEntry.asm)
function(add_termos_executable NAME SOURCES)
set(HAS_OBJC FALSE)
foreach(SRC ${SOURCES})
get_filename_component(EXT ${SRC} EXT)
if(EXT STREQUAL ".m")
set(HAS_OBJC TRUE)
break()
endif()
endforeach()
add_executable(${NAME} ${SOURCES})
target_sources(${NAME} PRIVATE $<TARGET_OBJECTS:RuntimeEntryObject>)
if(HAS_OBJC)
target_sources(${NAME} PRIVATE $<TARGET_OBJECTS:ObjCRuntimeEntryObject>)
target_compile_options(${NAME} PRIVATE $<$<COMPILE_LANGUAGE:OBJC>:-fobjc-runtime=gnustep -fblocks>)
target_link_options(${NAME} PRIVATE -T ${libterm_SOURCE_DIR}/linker/objc.ld)
target_link_libraries(${NAME} PRIVATE objc)
else()
target_sources(${NAME} PRIVATE $<TARGET_OBJECTS:RuntimeEntryObject>)
endif()
target_compile_options(${NAME} PRIVATE
$<$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:OBJC>>:${USER_C_FLAGS}>
)
target_compile_options(${NAME} PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
target_link_libraries(${NAME} PRIVATE term)
target_link_options(${NAME} PRIVATE
-T ${libterm_SOURCE_DIR}/linker.ld
-T ${libterm_SOURCE_DIR}/linker/common.ld
-nostdlib
-static
)
+28
View File
@@ -0,0 +1,28 @@
SECTIONS
{
.objc_metadata :
{
. = ALIGN(8);
__objc_runtime_start = .;
KEEP(*(.objc_info))
__objc_module_list_start = .;
KEEP(*(.objc_module_info))
__objc_module_list_end = .;
KEEP(*(.objc_selectors))
KEEP(*(.objc_classes))
KEEP(*(.objc_category))
KEEP(*(.objc_class_refs))
KEEP(*(.objc_class_names))
KEEP(*(.objc_method_names))
KEEP(*(.objc_method_types))
KEEP(*(.objc_protocol_list))
KEEP(*(.objc_string_object))
KEEP(*(.objc_constant_string))
KEEP(*(.objc_data))
KEEP(*(.objc_list.*))
__objc_runtime_end = .;
}
}
INSERT AFTER .data;
@@ -0,0 +1,18 @@
; SPDX-License-Identifier: GPL-3.0-or-later
; Copyright (c) 2026 0xKarinyash
[bits 64]
section .text
global _start
extern main
extern OSServiceProcessExit
extern _objc_init_runtime
_start:
call _objc_init_runtime
call main
mov rdi, rax
call OSServiceProcessExit
+6
View File
@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.20)
project(termOSobjcdbg LANGUAGES OBJC)
file(GLOB_RECURSE testOBJC_SOURCES "src/*.m")
add_termos_executable(testOBJC "${testOBJC_SOURCES}")
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#import <termOS.h>
#import <HOTObject.h>
@interface Test : HOTObject
- (void)sayHi;
@end
@implementation Test
- (void)sayHi {
ConsolePrint("meow");
}
@end
int main() {
return 1;
Test *t = [[Test alloc] init];
[t sayHi];
return 0;
}