Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa6b7924e2 |
@@ -34,6 +34,7 @@ set(SYSTEM_SERVICES
|
|||||||
init
|
init
|
||||||
debug
|
debug
|
||||||
termosh
|
termosh
|
||||||
|
testOBJC
|
||||||
)
|
)
|
||||||
|
|
||||||
set(VOLUME_ROOT "${CMAKE_BINARY_DIR}/StartupVolume")
|
set(VOLUME_ROOT "${CMAKE_BINARY_DIR}/StartupVolume")
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ set(CMAKE_C_EXTENSIONS OFF)
|
|||||||
|
|
||||||
message(STATUS "Building termOS's userspace")
|
message(STATUS "Building termOS's userspace")
|
||||||
|
|
||||||
|
add_subdirectory(libobjc)
|
||||||
add_subdirectory(libterm)
|
add_subdirectory(libterm)
|
||||||
add_subdirectory(init)
|
add_subdirectory(init)
|
||||||
add_subdirectory(debug)
|
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)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(libterm LANGUAGES C ASM_NASM)
|
project(libterm LANGUAGES C ASM_NASM OBJC)
|
||||||
|
|
||||||
set(USER_C_FLAGS
|
set(USER_C_FLAGS
|
||||||
-ffreestanding
|
-ffreestanding
|
||||||
@@ -37,18 +37,36 @@ target_compile_options(term PRIVATE $<$<COMPILE_LANGUAGE:C>:${USER_C_FLAGS}>)
|
|||||||
target_include_directories(term PUBLIC inc)
|
target_include_directories(term PUBLIC inc)
|
||||||
|
|
||||||
add_library(RuntimeEntryObject OBJECT src/RuntimeEntry.asm)
|
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)
|
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})
|
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_libraries(${NAME} PRIVATE term)
|
||||||
|
|
||||||
target_link_options(${NAME} PRIVATE
|
target_link_options(${NAME} PRIVATE
|
||||||
-T ${libterm_SOURCE_DIR}/linker.ld
|
-T ${libterm_SOURCE_DIR}/linker/common.ld
|
||||||
-nostdlib
|
-nostdlib
|
||||||
-static
|
-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