Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa6b7924e2 |
@@ -34,6 +34,7 @@ set(SYSTEM_SERVICES
|
||||
init
|
||||
debug
|
||||
termosh
|
||||
testOBJC
|
||||
)
|
||||
|
||||
set(VOLUME_ROOT "${CMAKE_BINARY_DIR}/StartupVolume")
|
||||
|
||||
@@ -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(termosh)
|
||||
add_subdirectory(testOBJC)
|
||||
add_subdirectory(termosh)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright (c) 2026 0xKarinyash
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
|
||||
target_compile_options(${NAME} PRIVATE
|
||||
$<$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:OBJC>>:${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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -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}")
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user