diff --git a/.gitignore b/.gitignore index 0c34684..fc4ee18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.o *.tmp -*.bin \ No newline at end of file +*.bin +bin +obj +.vs \ No newline at end of file diff --git a/Executable.sln b/Executable.sln new file mode 100644 index 0000000..4694daf --- /dev/null +++ b/Executable.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32210.238 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "noruntime", "source\noruntime.csproj", "{BF2D0032-2C24-4527-AE52-54A679B15D9D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BF2D0032-2C24-4527-AE52-54A679B15D9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF2D0032-2C24-4527-AE52-54A679B15D9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF2D0032-2C24-4527-AE52-54A679B15D9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF2D0032-2C24-4527-AE52-54A679B15D9D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {56486BA0-B053-49F2-BD72-F1A9E95FD7EE} + EndGlobalSection +EndGlobal diff --git a/Makefile b/Makefile deleted file mode 100644 index c23f54f..0000000 --- a/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -SDIR := source -ODIR := build -CFILES := $(wildcard $(SDIR)/*.c) -CLIBFILES := $(wildcard $(SDIR)/lib/*.c) -CC := gcc -OBJCOPY := objcopy -LD = ld -OBJS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(CFILES)) -LOBJS := $(patsubst $(SDIR)/lib/%.c, $(ODIR)/%.o, $(CLIBFILES)) - -CFLAGS = -std=gnu99 -Os -nostdlib -m32 -ffreestanding -LDFLAGS = -Ttext=0x0 -m elf_i386 --entry main - -TARGET = $(shell basename $(CURDIR)).bin - -$(TARGET): $(ODIR) $(OBJS) $(LOBJS) - $(LD) $(ODIR)/*.o $(SDIR)/linker/aura.ld -o $(ODIR)/program.tmp $(LDFLAGS) - $(OBJCOPY) -O binary $(ODIR)/program.tmp $(ODIR)/program.bin - rm -f $(ODIR)/program.tmp - -$(ODIR)/%.o: $(SDIR)/%.c - $(CC) -c -o $@ $< $(CFLAGS) - -$(ODIR)/%.o: $(SDIR)/lib/%.c - $(CC) -c -o $@ $< $(CFLAGS) - -$(ODIR): - @mkdir $@ - -.PHONY: clean - -clean: - rm -f $(TARGET) $(ODIR)/*.o $(ODIR)/program.bin diff --git a/source/EEType.cs b/source/EEType.cs new file mode 100644 index 0000000..697c14a --- /dev/null +++ b/source/EEType.cs @@ -0,0 +1,1304 @@ +using System; +using System.Runtime.InteropServices; + +namespace Internal.Runtime +{ + internal enum EETypeElementType + { + // Primitive + Unknown = 0x00, + Void = 0x01, + Boolean = 0x02, + Char = 0x03, + SByte = 0x04, + Byte = 0x05, + Int16 = 0x06, + UInt16 = 0x07, + Int32 = 0x08, + UInt32 = 0x09, + Int64 = 0x0A, + UInt64 = 0x0B, + IntPtr = 0x0C, + UIntPtr = 0x0D, + Single = 0x0E, + Double = 0x0F, + + ValueType = 0x10, + // Enum = 0x11, // EETypes store enums as their underlying type + Nullable = 0x12, + // Unused 0x13, + + Class = 0x14, + Interface = 0x15, + + SystemArray = 0x16, // System.Array type + + Array = 0x17, + SzArray = 0x18, + ByRef = 0x19, + Pointer = 0x1A, + } + + [Flags] + internal enum EETypeFlags : ushort + { + /// + /// There are four kinds of EETypes, defined in Kinds. + /// + EETypeKindMask = 0x0003, + + /// + /// This flag is set when m_RelatedType is in a different module. In that case, _pRelatedType + /// actually points to an IAT slot in this module, which then points to the desired EEType in the + /// other module. In other words, there is an extra indirection through m_RelatedType to get to + /// the related type in the other module. When this flag is set, it is expected that you use the + /// "_ppXxxxViaIAT" member of the RelatedTypeUnion for the particular related type you're + /// accessing. + /// + RelatedTypeViaIATFlag = 0x0004, + + /// + /// This type was dynamically allocated at runtime. + /// + IsDynamicTypeFlag = 0x0008, + + /// + /// This EEType represents a type which requires finalization. + /// + HasFinalizerFlag = 0x0010, + + /// + /// This type contain GC pointers. + /// + HasPointersFlag = 0x0020, + + /// + /// Type implements ICastable to allow dynamic resolution of interface casts. + /// + ICastableFlag = 0x0040, + + /// + /// This type is generic and one or more of its type parameters is co- or contra-variant. This + /// only applies to interface and delegate types. + /// + GenericVarianceFlag = 0x0080, + + /// + /// This type has optional fields present. + /// + OptionalFieldsFlag = 0x0100, + + // Unused = 0x0200, + + /// + /// This type is generic. + /// + IsGenericFlag = 0x0400, + + /// + /// We are storing a EETypeElementType in the upper bits for unboxing enums. + /// + ElementTypeMask = 0xf800, + ElementTypeShift = 11, + + /// + /// Single mark to check TypeKind and two flags. When non-zero, casting is more complicated. + /// + ComplexCastingMask = EETypeKindMask | RelatedTypeViaIATFlag | GenericVarianceFlag + }; + + internal enum EETypeKind : ushort + { + /// + /// Represents a standard ECMA type + /// + CanonicalEEType = 0x0000, + + /// + /// Represents a type cloned from another EEType + /// + ClonedEEType = 0x0001, + + /// + /// Represents a parameterized type. For example a single dimensional array or pointer type + /// + ParameterizedEEType = 0x0002, + + /// + /// Represents an uninstantiated generic type definition + /// + GenericTypeDefEEType = 0x0003, + } + + + [StructLayout(LayoutKind.Sequential)] + internal struct ObjHeader + { + // Contents of the object header + private IntPtr _objHeaderContents; + } + + // [StructLayout(LayoutKind.Sequential)] + // internal unsafe struct EEInterfaceInfo { + // [StructLayout(LayoutKind.Explicit)] + // private unsafe struct InterfaceTypeUnion { + // [FieldOffset(0)] + // public EEType* _pInterfaceEEType; + // [FieldOffset(0)] + // public EEType** _ppInterfaceEETypeViaIAT; + // } + + // private InterfaceTypeUnion _interfaceType; + + // internal EEType* InterfaceType { + // get { + // if ((unchecked((uint)_interfaceType._pInterfaceEEType) & IndirectionConstants.IndirectionCellPointer) != 0) { + //#if TARGET_64BIT + // EEType** ppInterfaceEETypeViaIAT = (EEType**)(((ulong)_interfaceType._ppInterfaceEETypeViaIAT) - IndirectionConstants.IndirectionCellPointer); + //#else + // EEType** ppInterfaceEETypeViaIAT = (EEType**)(((uint)_interfaceType._ppInterfaceEETypeViaIAT) - IndirectionConstants.IndirectionCellPointer); + //#endif + // return *ppInterfaceEETypeViaIAT; + // } + + // return _interfaceType._pInterfaceEEType; + // } + //#if TYPE_LOADER_IMPLEMENTATION + // set + // { + // _interfaceType._pInterfaceEEType = value; + // } + //#endif + // } + // } + + // [StructLayout(LayoutKind.Sequential)] + // internal unsafe struct DispatchMap { + // [StructLayout(LayoutKind.Sequential)] + // internal unsafe struct DispatchMapEntry { + // internal ushort _usInterfaceIndex; + // internal ushort _usInterfaceMethodSlot; + // internal ushort _usImplMethodSlot; + // } + + // private uint _entryCount; + // private DispatchMapEntry _dispatchMap; // at least one entry if any interfaces defined + + // public bool IsEmpty { + // get { + // return _entryCount == 0; + // } + // } + + // public uint NumEntries { + // get { + // return _entryCount; + // } + //#if TYPE_LOADER_IMPLEMENTATION + // set + // { + // _entryCount = value; + // } + //#endif + // } + + // public int Size { + // get { + // return sizeof(uint) + sizeof(DispatchMapEntry) * (int)_entryCount; + // } + // } + + // public DispatchMapEntry* this[int index] { + // get { + // fixed (DispatchMap* pThis = &this) + // return (DispatchMapEntry*)((byte*)pThis + sizeof(uint) + (sizeof(DispatchMapEntry) * index)); + // } + // } + // } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct EEType + { + private const int POINTER_SIZE = 8; + private const int PADDING = 1; // _numComponents is padded by one Int32 to make the first element pointer-aligned + internal const int SZARRAY_BASE_SIZE = POINTER_SIZE + POINTER_SIZE + (1 + PADDING) * 4; + + [StructLayout(LayoutKind.Explicit)] + private unsafe struct RelatedTypeUnion + { + // Kinds.CanonicalEEType + [FieldOffset(0)] + public EEType* _pBaseType; + [FieldOffset(0)] + public EEType** _ppBaseTypeViaIAT; + + // Kinds.ClonedEEType + [FieldOffset(0)] + public EEType* _pCanonicalType; + [FieldOffset(0)] + public EEType** _ppCanonicalTypeViaIAT; + + // Kinds.ArrayEEType + [FieldOffset(0)] + public EEType* _pRelatedParameterType; + [FieldOffset(0)] + public EEType** _ppRelatedParameterTypeViaIAT; + } + + //private static unsafe class OptionalFieldsReader { + // internal static uint GetInlineField(byte* pFields, EETypeOptionalFieldTag eTag, uint uiDefaultValue) { + // if (pFields == null) + // return uiDefaultValue; + + // bool isLastField = false; + // while (!isLastField) { + // byte fieldHeader = NativePrimitiveDecoder.ReadUInt8(ref pFields); + // isLastField = (fieldHeader & 0x80) != 0; + // EETypeOptionalFieldTag eCurrentTag = (EETypeOptionalFieldTag)(fieldHeader & 0x7f); + // uint uiCurrentValue = NativePrimitiveDecoder.DecodeUnsigned(ref pFields); + + // // If we found a tag match return the current value. + // if (eCurrentTag == eTag) + // return uiCurrentValue; + // } + + // // Reached end of stream without getting a match. Field is not present so return default value. + // return uiDefaultValue; + // } + //} + + ///// + ///// Gets a value indicating whether the statically generated data structures use relative pointers. + ///// + //internal static bool SupportsRelativePointers { + // [Intrinsic] + // get { + // throw new NotImplementedException(); + // } + //} + + private ushort _usComponentSize; + private ushort _usFlags; + private uint _uBaseSize; + private RelatedTypeUnion _relatedType; + private ushort _usNumVtableSlots; + private ushort _usNumInterfaces; + private uint _uHashCode; + + // vtable follows + + // These masks and paddings have been chosen so that the ValueTypePadding field can always fit in a byte of data. + // if the alignment is 8 bytes or less. If the alignment is higher then there may be a need for more bits to hold + // the rest of the padding data. + // If paddings of greater than 7 bytes are necessary, then the high bits of the field represent that padding + private const uint ValueTypePaddingLowMask = 0x7; + private const uint ValueTypePaddingHighMask = 0xFFFFFF00; + private const uint ValueTypePaddingMax = 0x07FFFFFF; + private const int ValueTypePaddingHighShift = 8; + private const uint ValueTypePaddingAlignmentMask = 0xF8; + private const int ValueTypePaddingAlignmentShift = 3; + + internal ushort ComponentSize + { + get + { + return _usComponentSize; + } + } + + //internal ushort GenericArgumentCount { + // get { + // Debug.Assert(IsGenericTypeDefinition); + // return _usComponentSize; + // } + //} + + //internal ushort Flags { + // get { + // return _usFlags; + // } + //} + + internal uint BaseSize + { + get + { + return _uBaseSize; + } + } + + //internal ushort NumVtableSlots { + // get { + // return _usNumVtableSlots; + // } + //} + + //internal ushort NumInterfaces { + // get { + // return _usNumInterfaces; + // } + //} + + //internal uint HashCode { + // get { + // return _uHashCode; + // } + //} + + private EETypeKind Kind + { + get + { + return (EETypeKind)(_usFlags & (ushort)EETypeFlags.EETypeKindMask); + } + } + + //internal bool HasOptionalFields { + // get { + // return ((_usFlags & (ushort)EETypeFlags.OptionalFieldsFlag) != 0); + // } + //} + + //// Mark or determine that a type is generic and one or more of it's type parameters is co- or + //// contra-variant. This only applies to interface and delegate types. + internal bool HasGenericVariance + { + get + { + return ((_usFlags & (ushort)EETypeFlags.GenericVarianceFlag) != 0); + } + } + + //internal bool IsFinalizable { + // get { + // return ((_usFlags & (ushort)EETypeFlags.HasFinalizerFlag) != 0); + // } + //} + + //internal bool IsNullable { + // get { + // return ElementType == EETypeElementType.Nullable; + // } + //} + + internal bool IsCloned + { + get + { + return Kind == EETypeKind.ClonedEEType; + } + } + + //internal bool IsCanonical { + // get { + // return Kind == EETypeKind.CanonicalEEType; + // } + //} + + internal bool IsString + { + get + { + // String is currently the only non-array type with a non-zero component size. + return ComponentSize == sizeof(char) && !IsArray && !IsGenericTypeDefinition; + } + } + + internal bool IsArray + { + get + { + EETypeElementType elementType = ElementType; + return elementType == EETypeElementType.Array || elementType == EETypeElementType.SzArray; + } + } + + internal static class WellKnownEETypes + { + // Returns true if the passed in EEType is the EEType for System.Object + // This is recognized by the fact that System.Object and interfaces are the only ones without a base type + internal static unsafe bool IsSystemObject(EEType* pEEType) + { + if (pEEType->IsArray) + return false; + return (pEEType->NonArrayBaseType == null) && !pEEType->IsInterface; + } + + // Returns true if the passed in EEType is the EEType for System.Array. + // The binder sets a special CorElementType for this well known type + internal static unsafe bool IsSystemArray(EEType* pEEType) + { + return (pEEType->ElementType == EETypeElementType.SystemArray); + } + } + + internal int ArrayRank + { + get + { + int boundsSize = (int)this.ParameterizedTypeShape - SZARRAY_BASE_SIZE; + if (boundsSize > 0) + { + // Multidim array case: Base size includes space for two Int32s + // (upper and lower bound) per each dimension of the array. + return boundsSize / (2 * sizeof(int)); + } + return 1; + } + } + + internal bool IsSzArray + { + get + { + return ElementType == EETypeElementType.SzArray; + } + } + + //internal bool IsGeneric { + // get { + // return ((_usFlags & (ushort)EETypeFlags.IsGenericFlag) != 0); + // } + //} + + internal bool IsGenericTypeDefinition + { + get + { + return Kind == EETypeKind.GenericTypeDefEEType; + } + } + + //internal EEType* GenericDefinition { + // get { + // Debug.Assert(IsGeneric); + // if (IsDynamicType || !SupportsRelativePointers) + // return GetField>(EETypeField.ETF_GenericDefinition).Value; + + // return GetField>(EETypeField.ETF_GenericDefinition).Value; + // } + //} + + //[StructLayout(LayoutKind.Sequential)] + //private readonly struct GenericComposition { + // public readonly ushort Arity; + + // private readonly EETypeRef _genericArgument1; + // public EETypeRef* GenericArguments { + // get { + // return (EETypeRef*)Unsafe.AsPointer(ref Unsafe.AsRef(in _genericArgument1)); + // } + // } + + // public GenericVariance* GenericVariance { + // get { + // // Generic variance directly follows the last generic argument + // return (GenericVariance*)(GenericArguments + Arity); + // } + // } + //} + + //internal uint GenericArity { + // get { + // Debug.Assert(IsGeneric); + // if (IsDynamicType || !SupportsRelativePointers) + // return GetField>(EETypeField.ETF_GenericComposition).Value->Arity; + + // return GetField>(EETypeField.ETF_GenericComposition).Value->Arity; + // } + //} + + //internal EETypeRef* GenericArguments { + // get { + // Debug.Assert(IsGeneric); + // if (IsDynamicType || !SupportsRelativePointers) + // return GetField>(EETypeField.ETF_GenericComposition).Value->GenericArguments; + + // return GetField>(EETypeField.ETF_GenericComposition).Value->GenericArguments; + // } + //} + + //internal GenericVariance* GenericVariance { + // get { + // Debug.Assert(IsGeneric); + + // if (!HasGenericVariance) + // return null; + + // if (IsDynamicType || !SupportsRelativePointers) + // return GetField>(EETypeField.ETF_GenericComposition).Value->GenericVariance; + + // return GetField>(EETypeField.ETF_GenericComposition).Value->GenericVariance; + // } + //} + + //internal bool IsPointerType { + // get { + // return ElementType == EETypeElementType.Pointer; + // } + //} + + //internal bool IsByRefType { + // get { + // return ElementType == EETypeElementType.ByRef; + // } + //} + + internal bool IsInterface + { + get + { + return ElementType == EETypeElementType.Interface; + } + } + + //internal bool IsAbstract { + // get { + // return IsInterface || (RareFlags & EETypeRareFlags.IsAbstractClassFlag) != 0; + // } + //} + + //internal bool IsByRefLike { + // get { + // return (RareFlags & EETypeRareFlags.IsByRefLikeFlag) != 0; + // } + //} + + //internal bool IsDynamicType { + // get { + // return (_usFlags & (ushort)EETypeFlags.IsDynamicTypeFlag) != 0; + // } + //} + + //internal bool HasDynamicallyAllocatedDispatchMap { + // get { + // return (RareFlags & EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag) != 0; + // } + //} + + //internal bool IsParameterizedType { + // get { + // return Kind == EETypeKind.ParameterizedEEType; + // } + //} + + //// The parameterized type shape defines the particular form of parameterized type that + //// is being represented. + //// Currently, the meaning is a shape of 0 indicates that this is a Pointer, + //// shape of 1 indicates a ByRef, and >=SZARRAY_BASE_SIZE indicates that this is an array. + //// Two types are not equivalent if their shapes do not exactly match. + internal uint ParameterizedTypeShape + { + get + { + return _uBaseSize; + } + } + + internal bool IsRelatedTypeViaIAT + { + get + { + return ((_usFlags & (ushort)EETypeFlags.RelatedTypeViaIATFlag) != 0); + } + } + + //internal bool RequiresAlign8 { + // get { + // return (RareFlags & EETypeRareFlags.RequiresAlign8Flag) != 0; + // } + //} + + //internal bool IsICastable { + // get { + // return ((_usFlags & (ushort)EETypeFlags.ICastableFlag) != 0); + // } + //} + + ///// + ///// Gets the pointer to the method that implements ICastable.IsInstanceOfInterface. + ///// + //internal IntPtr ICastableIsInstanceOfInterfaceMethod { + // get { + // Debug.Assert(IsICastable); + + // byte* optionalFields = OptionalFieldsPtr; + // if (optionalFields != null) { + // const ushort NoSlot = 0xFFFF; + // ushort uiSlot = (ushort)OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ICastableIsInstSlot, NoSlot); + // if (uiSlot != NoSlot) { + // if (uiSlot < NumVtableSlots) + // return GetVTableStartAddress()[uiSlot]; + // else + // return GetSealedVirtualSlot((ushort)(uiSlot - NumVtableSlots)); + // } + // } + + // EEType* baseType = BaseType; + // if (baseType != null) + // return baseType->ICastableIsInstanceOfInterfaceMethod; + + // Debug.Assert(false); + // return IntPtr.Zero; + // } + //} + + ///// + ///// Gets the pointer to the method that implements ICastable.GetImplType. + ///// + //internal IntPtr ICastableGetImplTypeMethod { + // get { + // Debug.Assert(IsICastable); + + // byte* optionalFields = OptionalFieldsPtr; + // if (optionalFields != null) { + // const ushort NoSlot = 0xFFFF; + // ushort uiSlot = (ushort)OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ICastableGetImplTypeSlot, NoSlot); + // if (uiSlot != NoSlot) { + // if (uiSlot < NumVtableSlots) + // return GetVTableStartAddress()[uiSlot]; + // else + // return GetSealedVirtualSlot((ushort)(uiSlot - NumVtableSlots)); + // } + // } + + // EEType* baseType = BaseType; + // if (baseType != null) + // return baseType->ICastableGetImplTypeMethod; + + // Debug.Assert(false); + // return IntPtr.Zero; + // } + //} + + //internal bool IsValueType { + // get { + // return ElementType < EETypeElementType.Class; + // } + //} + + //internal bool HasGCPointers { + // get { + // return ((_usFlags & (ushort)EETypeFlags.HasPointersFlag) != 0); + // } + //} + + //internal bool IsHFA { + // get { + // return (RareFlags & EETypeRareFlags.IsHFAFlag) != 0; + // } + //} + + //internal uint ValueTypeFieldPadding { + // get { + // byte* optionalFields = OptionalFieldsPtr; + + // // If there are no optional fields then the padding must have been the default, 0. + // if (optionalFields == null) + // return 0; + + // // Get the value from the optional fields. The default is zero if that particular field was not included. + // // The low bits of this field is the ValueType field padding, the rest of the byte is the alignment if present + // uint ValueTypeFieldPaddingData = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ValueTypeFieldPadding, 0); + // uint padding = ValueTypeFieldPaddingData & ValueTypePaddingLowMask; + // // If there is additional padding, the other bits have that data + // padding |= (ValueTypeFieldPaddingData & ValueTypePaddingHighMask) >> (ValueTypePaddingHighShift - ValueTypePaddingAlignmentShift); + // return padding; + // } + //} + + //internal uint ValueTypeSize { + // get { + // Debug.Assert(IsValueType); + // // get_BaseSize returns the GC size including space for the sync block index field, the EEType* and + // // padding for GC heap alignment. Must subtract all of these to get the size used for locals, array + // // elements or fields of another type. + // return BaseSize - ((uint)sizeof(ObjHeader) + (uint)sizeof(EEType*) + ValueTypeFieldPadding); + // } + //} + + //internal uint FieldByteCountNonGCAligned { + // get { + // // This api is designed to return correct results for EETypes which can be derived from + // // And results indistinguishable from correct for DefTypes which cannot be derived from (sealed classes) + // // (For sealed classes, this should always return BaseSize-((uint)sizeof(ObjHeader)); + // Debug.Assert(!IsInterface && !IsParameterizedType); + + // // get_BaseSize returns the GC size including space for the sync block index field, the EEType* and + // // padding for GC heap alignment. Must subtract all of these to get the size used for the fields of + // // the type (where the fields of the type includes the EEType*) + // return BaseSize - ((uint)sizeof(ObjHeader) + ValueTypeFieldPadding); + // } + //} + + //internal EEInterfaceInfo* InterfaceMap { + // get { + // fixed (EEType* start = &this) { + // // interface info table starts after the vtable and has _usNumInterfaces entries + // return (EEInterfaceInfo*)((byte*)start + sizeof(EEType) + sizeof(void*) * _usNumVtableSlots); + // } + // } + //} + + //internal bool HasDispatchMap { + // get { + // if (NumInterfaces == 0) + // return false; + // byte* optionalFields = OptionalFieldsPtr; + // if (optionalFields == null) + // return false; + // uint idxDispatchMap = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.DispatchMap, 0xffffffff); + // if (idxDispatchMap == 0xffffffff) { + // if (HasDynamicallyAllocatedDispatchMap) + // return true; + // else if (IsDynamicType) + // return DynamicTemplateType->HasDispatchMap; + // return false; + // } + // return true; + // } + //} + + //internal DispatchMap* DispatchMap { + // get { + // if (NumInterfaces == 0) + // return null; + // byte* optionalFields = OptionalFieldsPtr; + // if (optionalFields == null) + // return null; + // uint idxDispatchMap = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.DispatchMap, 0xffffffff); + // if (idxDispatchMap == 0xffffffff && IsDynamicType) { + // if (HasDynamicallyAllocatedDispatchMap) { + // fixed (EEType* pThis = &this) + // return *(DispatchMap**)((byte*)pThis + GetFieldOffset(EETypeField.ETF_DynamicDispatchMap)); + // } + // else + // return DynamicTemplateType->DispatchMap; + // } + + // return ((DispatchMap**)TypeManager.DispatchMap)[idxDispatchMap]; + // } + //} + + //// Get the address of the finalizer method for finalizable types. + //internal IntPtr FinalizerCode { + // get { + // Debug.Assert(IsFinalizable); + + // if (IsDynamicType || !SupportsRelativePointers) + // return GetField(EETypeField.ETF_Finalizer).Value; + + // return GetField(EETypeField.ETF_Finalizer).Value; + // } + //} + + //internal EEType* BaseType { + // get { + // if (IsCloned) { + // return CanonicalEEType->BaseType; + // } + + // if (IsParameterizedType) { + // if (IsArray) + // return GetArrayEEType(); + // else + // return null; + // } + + // Debug.Assert(IsCanonical); + + // if (IsRelatedTypeViaIAT) + // return *_relatedType._ppBaseTypeViaIAT; + // else + // return _relatedType._pBaseType; + // } + //} + + internal EEType* NonArrayBaseType + { + get + { + if (IsCloned) + { + // Assuming that since this is not an Array, the CanonicalEEType is also not an array + return CanonicalEEType->NonArrayBaseType; + } + + if (IsRelatedTypeViaIAT) + { + return *_relatedType._ppBaseTypeViaIAT; + } + + return _relatedType._pBaseType; + } + } + + internal EEType* NonClonedNonArrayBaseType + { + get + { + if (IsRelatedTypeViaIAT) + { + return *_relatedType._ppBaseTypeViaIAT; + } + + return _relatedType._pBaseType; + } + } + + internal EEType* RawBaseType + { + get + { + //Debug.Assert(!IsParameterizedType, "array type not supported in NonArrayBaseType"); + //Debug.Assert(!IsCloned, "cloned type not supported in NonClonedNonArrayBaseType"); + //Debug.Assert(IsCanonical, "we expect canonical types here"); + //Debug.Assert(!IsRelatedTypeViaIAT, "Non IAT"); + + return _relatedType._pBaseType; + } + } + + internal EEType* CanonicalEEType + { + get + { + // cloned EETypes must always refer to types in other modules + if (IsRelatedTypeViaIAT) + return *_relatedType._ppCanonicalTypeViaIAT; + else + return _relatedType._pCanonicalType; + } + } + + //internal EEType* NullableType { + // get { + // Debug.Assert(IsNullable); + // Debug.Assert(GenericArity == 1); + // return GenericArguments[0].Value; + // } + //} + + ///// + ///// Gets the offset of the value embedded in a Nullable<T>. + ///// + //internal byte NullableValueOffset { + // get { + // Debug.Assert(IsNullable); + + // // Grab optional fields. If there aren't any then the offset was the default of 1 (immediately after the + // // Nullable's boolean flag). + // byte* optionalFields = OptionalFieldsPtr; + // if (optionalFields == null) + // return 1; + + // // The offset is never zero (Nullable has a boolean there indicating whether the value is valid). So the + // // offset is encoded - 1 to save space. The zero below is the default value if the field wasn't encoded at + // // all. + // return (byte)(OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.NullableValueOffset, 0) + 1); + // } + //} + + internal EEType* RelatedParameterType + { + get + { + if (IsRelatedTypeViaIAT) + return *_relatedType._ppRelatedParameterTypeViaIAT; + else + return _relatedType._pRelatedParameterType; + } + } + + //internal unsafe IntPtr* GetVTableStartAddress() { + // byte* pResult; + + // // EETypes are always in unmanaged memory, so 'leaking' the 'fixed pointer' is safe. + // fixed (EEType* pThis = &this) + // pResult = (byte*)pThis; + + // pResult += sizeof(EEType); + // return (IntPtr*)pResult; + //} + + //private static IntPtr FollowRelativePointer(int* pDist) { + // int dist = *pDist; + // IntPtr result = (IntPtr)((byte*)pDist + dist); + // return result; + //} + + //internal IntPtr GetSealedVirtualSlot(ushort slotNumber) { + // Debug.Assert((RareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0); + + // fixed (EEType* pThis = &this) { + // if (IsDynamicType || !SupportsRelativePointers) { + // uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); + // IntPtr* pSealedVirtualsSlotTable = *(IntPtr**)((byte*)pThis + cbSealedVirtualSlotsTypeOffset); + // return pSealedVirtualsSlotTable[slotNumber]; + // } + // else { + // uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots); + // int* pSealedVirtualsSlotTable = (int*)FollowRelativePointer((int*)((byte*)pThis + cbSealedVirtualSlotsTypeOffset)); + // IntPtr result = FollowRelativePointer(&pSealedVirtualsSlotTable[slotNumber]); + // return result; + // } + // } + //} + + //internal byte* OptionalFieldsPtr { + // get { + // if (!HasOptionalFields) + // return null; + + // if (IsDynamicType || !SupportsRelativePointers) + // return GetField>(EETypeField.ETF_OptionalFieldsPtr).Value; + + // return GetField>(EETypeField.ETF_OptionalFieldsPtr).Value; + // } + //} + + //internal EEType* DynamicTemplateType { + // get { + // Debug.Assert(IsDynamicType); + // uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicTemplateType); + // fixed (EEType* pThis = &this) { + // return *(EEType**)((byte*)pThis + cbOffset); + // } + // } + //} + + //internal IntPtr DynamicGcStaticsData { + // get { + // Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); + // uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicGcStatics); + // fixed (EEType* pThis = &this) { + // return (IntPtr)((byte*)pThis + cbOffset); + // } + // } + //} + + //internal IntPtr DynamicNonGcStaticsData { + // get { + // Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0); + // uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicNonGcStatics); + // fixed (EEType* pThis = &this) { + // return (IntPtr)((byte*)pThis + cbOffset); + // } + // } + //} + + //internal DynamicModule* DynamicModule { + // get { + // if ((RareFlags & EETypeRareFlags.HasDynamicModuleFlag) != 0) { + // uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicModule); + // fixed (EEType* pThis = &this) { + // return *(DynamicModule**)((byte*)pThis + cbOffset); + // } + // } + // else { + // return null; + // } + // } + //} + + //internal TypeManagerHandle TypeManager { + // get { + // IntPtr typeManagerIndirection; + // if (IsDynamicType || !SupportsRelativePointers) + // typeManagerIndirection = GetField(EETypeField.ETF_TypeManagerIndirection).Value; + // else + // typeManagerIndirection = GetField(EETypeField.ETF_TypeManagerIndirection).Value; + + // return *(TypeManagerHandle*)typeManagerIndirection; + // } + //} + + //internal unsafe EETypeRareFlags RareFlags { + // get { + // // If there are no optional fields then none of the rare flags have been set. + // // Get the flags from the optional fields. The default is zero if that particular field was not included. + // return HasOptionalFields ? (EETypeRareFlags)OptionalFieldsReader.GetInlineField(OptionalFieldsPtr, EETypeOptionalFieldTag.RareFlags, 0) : 0; + // } + //} + + //internal int FieldAlignmentRequirement { + // get { + // byte* optionalFields = OptionalFieldsPtr; + + // // If there are no optional fields then the alignment must have been the default, IntPtr.Size. + // // (This happens for all reference types, and for valuetypes with default alignment and no padding) + // if (optionalFields == null) + // return IntPtr.Size; + + // // Get the value from the optional fields. The default is zero if that particular field was not included. + // // The low bits of this field is the ValueType field padding, the rest of the value is the alignment if present + // uint alignmentValue = (OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ValueTypeFieldPadding, 0) & ValueTypePaddingAlignmentMask) >> ValueTypePaddingAlignmentShift; + + // // Alignment is stored as 1 + the log base 2 of the alignment, except a 0 indicates standard pointer alignment. + // if (alignmentValue == 0) + // return IntPtr.Size; + // else + // return 1 << ((int)alignmentValue - 1); + // } + //} + + internal EETypeElementType ElementType + { + get + { + return (EETypeElementType)((_usFlags & (ushort)EETypeFlags.ElementTypeMask) >> (ushort)EETypeFlags.ElementTypeShift); + } + } + + //public bool HasCctor { + // get { + // return (RareFlags & EETypeRareFlags.HasCctorFlag) != 0; + // } + //} + + //public uint GetFieldOffset(EETypeField eField) { + // // First part of EEType consists of the fixed portion followed by the vtable. + // uint cbOffset = (uint)(sizeof(EEType) + (IntPtr.Size * _usNumVtableSlots)); + + // // Then we have the interface map. + // if (eField == EETypeField.ETF_InterfaceMap) { + // Debug.Assert(NumInterfaces > 0); + // return cbOffset; + // } + // cbOffset += (uint)(sizeof(EEInterfaceInfo) * NumInterfaces); + + // uint relativeOrFullPointerOffset = (IsDynamicType || !SupportsRelativePointers ? (uint)IntPtr.Size : 4); + + // // Followed by the type manager indirection cell. + // if (eField == EETypeField.ETF_TypeManagerIndirection) { + // return cbOffset; + // } + // cbOffset += relativeOrFullPointerOffset; + + // // Followed by the pointer to the finalizer method. + // if (eField == EETypeField.ETF_Finalizer) { + // Debug.Assert(IsFinalizable); + // return cbOffset; + // } + // if (IsFinalizable) + // cbOffset += relativeOrFullPointerOffset; + + // // Followed by the pointer to the optional fields. + // if (eField == EETypeField.ETF_OptionalFieldsPtr) { + // Debug.Assert(HasOptionalFields); + // return cbOffset; + // } + // if (HasOptionalFields) + // cbOffset += relativeOrFullPointerOffset; + + // // Followed by the pointer to the sealed virtual slots + // if (eField == EETypeField.ETF_SealedVirtualSlots) + // return cbOffset; + + // EETypeRareFlags rareFlags = RareFlags; + + // // in the case of sealed vtable entries on static types, we have a UInt sized relative pointer + // if ((rareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0) + // cbOffset += relativeOrFullPointerOffset; + + // if (eField == EETypeField.ETF_DynamicDispatchMap) { + // Debug.Assert(IsDynamicType); + // return cbOffset; + // } + // if ((rareFlags & EETypeRareFlags.HasDynamicallyAllocatedDispatchMapFlag) != 0) + // cbOffset += (uint)IntPtr.Size; + + // if (eField == EETypeField.ETF_GenericDefinition) { + // Debug.Assert(IsGeneric); + // return cbOffset; + // } + // if (IsGeneric) { + // cbOffset += relativeOrFullPointerOffset; + // } + + // if (eField == EETypeField.ETF_GenericComposition) { + // Debug.Assert(IsGeneric); + // return cbOffset; + // } + // if (IsGeneric) { + // cbOffset += relativeOrFullPointerOffset; + // } + + // if (eField == EETypeField.ETF_DynamicModule) { + // return cbOffset; + // } + + // if ((rareFlags & EETypeRareFlags.HasDynamicModuleFlag) != 0) + // cbOffset += (uint)IntPtr.Size; + + // if (eField == EETypeField.ETF_DynamicTemplateType) { + // Debug.Assert(IsDynamicType); + // return cbOffset; + // } + // if (IsDynamicType) + // cbOffset += (uint)IntPtr.Size; + + // if (eField == EETypeField.ETF_DynamicGcStatics) { + // Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0); + // return cbOffset; + // } + // if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0) + // cbOffset += (uint)IntPtr.Size; + + // if (eField == EETypeField.ETF_DynamicNonGcStatics) { + // Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0); + // return cbOffset; + // } + // if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0) + // cbOffset += (uint)IntPtr.Size; + + // if (eField == EETypeField.ETF_DynamicThreadStaticOffset) { + // Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0); + // return cbOffset; + // } + // if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0) + // cbOffset += 4; + + // Debug.Assert(false, "Unknown EEType field type"); + // return 0; + //} + + //public ref T GetField(EETypeField eField) { + // fixed (EEType* pThis = &this) + // return ref Unsafe.AddByteOffset(ref Unsafe.As(ref *pThis), (IntPtr)GetFieldOffset(eField)); + //} + } + + //// Wrapper around EEType pointers that may be indirected through the IAT if their low bit is set. + //[StructLayout(LayoutKind.Sequential)] + //internal unsafe struct EETypeRef { + // private byte* _value; + + // public EEType* Value { + // get { + // if (((int)_value & IndirectionConstants.IndirectionCellPointer) == 0) + // return (EEType*)_value; + // return *(EEType**)(_value - IndirectionConstants.IndirectionCellPointer); + // } + // } + //} + + //// Wrapper around pointers + //[StructLayout(LayoutKind.Sequential)] + //internal readonly struct Pointer { + // private readonly IntPtr _value; + + // public IntPtr Value { + // get { + // return _value; + // } + // } + //} + + //// Wrapper around pointers + //[StructLayout(LayoutKind.Sequential)] + //internal unsafe readonly struct Pointer where T : unmanaged { + // private readonly T* _value; + + // public T* Value { + // get { + // return _value; + // } + // } + //} + + //// Wrapper around pointers that might be indirected through IAT + //[StructLayout(LayoutKind.Sequential)] + //internal unsafe readonly struct IatAwarePointer where T : unmanaged { + // private readonly T* _value; + + // public T* Value { + // get { + // if (((int)_value & IndirectionConstants.IndirectionCellPointer) == 0) + // return _value; + // return *(T**)((byte*)_value - IndirectionConstants.IndirectionCellPointer); + // } + // } + //} + + //// Wrapper around relative pointers + //[StructLayout(LayoutKind.Sequential)] + //internal readonly struct RelativePointer { + // private readonly int _value; + + // public unsafe IntPtr Value { + // get { + // return (IntPtr)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + _value); + // } + // } + //} + + //// Wrapper around relative pointers + //[StructLayout(LayoutKind.Sequential)] + //internal unsafe readonly struct RelativePointer where T : unmanaged { + // private readonly int _value; + + // public T* Value { + // get { + // return (T*)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + _value); + // } + // } + //} + + //// Wrapper around relative pointers that might be indirected through IAT + //[StructLayout(LayoutKind.Sequential)] + //internal unsafe readonly struct IatAwareRelativePointer where T : unmanaged { + // private readonly int _value; + + // public T* Value { + // get { + // if ((_value & IndirectionConstants.IndirectionCellPointer) == 0) { + // return (T*)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + _value); + // } + // else { + // return *(T**)((byte*)Unsafe.AsPointer(ref Unsafe.AsRef(in _value)) + (_value & ~IndirectionConstants.IndirectionCellPointer)); + // } + // } + // } + //} + + //[StructLayout(LayoutKind.Sequential)] + //internal struct DynamicModule { + // // Size field used to indicate the number of bytes of this structure that are defined in Runtime Known ways + // // This is used to drive versioning of this field + // private int _cbSize; + + // // Pointer to interface dispatch resolver that works off of a type/slot pair + // // This is a function pointer with the following signature IntPtr()(IntPtr targetType, IntPtr interfaceType, ushort slot) + // private IntPtr _dynamicTypeSlotDispatchResolve; + + // // Starting address for the the binary module corresponding to this dynamic module. + // private IntPtr _getRuntimeException; + + // public IntPtr DynamicTypeSlotDispatchResolve { + // get { + // unsafe { + // if (_cbSize >= sizeof(IntPtr) * 2) { + // return _dynamicTypeSlotDispatchResolve; + // } + // else { + // return IntPtr.Zero; + // } + // } + // } + // } + + // public IntPtr GetRuntimeException { + // get { + // unsafe { + // if (_cbSize >= sizeof(IntPtr) * 3) { + // return _getRuntimeException; + // } + // else { + // return IntPtr.Zero; + // } + // } + // } + // } + //} +} \ No newline at end of file diff --git a/source/Lib.c b/source/Lib.c new file mode 100644 index 0000000..221ff9e --- /dev/null +++ b/source/Lib.c @@ -0,0 +1,21 @@ +#include "Lib.h" + +int printf(const char *string, ...) +{ + asm volatile ("mov $0x01, %%eax\n" //Print function + "mov %0, %%edi\n" //mov string pointer to ESI + "int $0x48\n" //interrupt 0x48 (Aura API) + : + : "r"(string) + : "edi", "eax", "memory"); + return 1; +} + +void clear() +{ + asm volatile ("mov $0x02, %%eax\n" //Clear function + "int $0x48\n" //interrupt 0x48 (Aura API) + : + : + : "eax", "memory"); +} \ No newline at end of file diff --git a/source/lib/stdio.h b/source/Lib.h similarity index 59% rename from source/lib/stdio.h rename to source/Lib.h index 95d37c7..1df533a 100644 --- a/source/lib/stdio.h +++ b/source/Lib.h @@ -2,6 +2,7 @@ #define __STDIO_H__ int printf(const char *, ...); -char *fgets(char *string); +void clear(); +int strlen(char const *str); -#endif +#endif \ No newline at end of file diff --git a/source/Makefile b/source/Makefile new file mode 100644 index 0000000..380a162 --- /dev/null +++ b/source/Makefile @@ -0,0 +1,54 @@ +.PHONY: build run clean debug + +ILC=/mnt/c/Users/valentinbreiz/.nuget/packages/runtime.win-x64.microsoft.dotnet.ilcompiler/7.0.0-alpha.1.21430.2/tools/ilc.exe +DOTNET = dotnet +CC = gcc + +CFLAGS = -std=gnu99 -Os -nostdlib -m32 -ffreestanding -fno-PIC +LDFLAGS = -m i386pe --nmagic -no-PIE --entry Main + +OBJ_DIR=obj + +C_SOURCES = $(wildcard *.c) +OS_SOURCES = $(wildcard *.cs) + +OS_DLL=bin/Debug/net6.0/noruntime.dll +OS_OBJ=obj/noruntime.o +EXECUTABLE=bin/noruntime.exe + +_OBJ = $(C_SOURCES:.c=.o) +OBJ = $(patsubst %,$(OBJ_DIR)/%,$(_OBJ)) + +build: $(EXECUTABLE) + @echo Done building executable!; + +$(EXECUTABLE): $(OS_OBJ) $(OBJ) + @echo Linking $(EXECUTABLE); \ + ld $(LDFLAGS) --output=$(EXECUTABLE) $(OBJ_DIR)/Lib.o $(OS_OBJ) + +$(OS_OBJ): $(OS_DLL) + @echo Converting CIL to native code; \ + $(ILC) --targetarch x86 --systemmodule noruntime --out $(OS_OBJ) $(OS_DLL) + +$(OBJ_DIR): $(OBJ_DIR) + @mkdir $(OBJ_DIR) + +$(OBJ_DIR)/%.o: $(C_SOURCES) + @echo Assembling $@; \ + $(CC) -c -o $@ $< $(CFLAGS) + +$(OS_DLL): $(OS_SOURCES) + @echo Building executable dlls; \ + $(DOTNET) restore && \ + $(DOTNET) build + +clean: + rm -rf *.log + rm -rf *.map + rm -rf obj/ + rm -rf bin/ + rm -rf *.o + +re: clean build + +print-% : ; @echo $* = $($*) \ No newline at end of file diff --git a/source/Program.cs b/source/Program.cs new file mode 100644 index 0000000..d9e78ea --- /dev/null +++ b/source/Program.cs @@ -0,0 +1,13 @@ +namespace SampleProgram +{ + public class Program + { + [System.Runtime.RuntimeExport("Main")] + static void Main() + { + //Console.Clear(); + Console.WriteLine("Hello from a Portable Executable!\n"); + Console.WriteLine("Compiled using NativeAOT and .NET 6\n"); + } + } +} \ No newline at end of file diff --git a/source/Runtime.cs b/source/Runtime.cs new file mode 100644 index 0000000..b256c77 --- /dev/null +++ b/source/Runtime.cs @@ -0,0 +1,475 @@ +using Internal.Runtime; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#region A couple very basic things +namespace System +{ + public sealed class FlagsAttribute : Attribute { } + + public sealed class AttributeUsageAttribute : Attribute + { + public AttributeTargets ValidOn { get; set; } + public bool AllowMultiple { get; set; } + public bool Inherited { get; set; } + + public AttributeUsageAttribute(AttributeTargets validOn) + { + ValidOn = validOn; + Inherited = true; + } + + public AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited) + { + ValidOn = validOn; + AllowMultiple = allowMultiple; + Inherited = inherited; + } + } + + public enum AttributeTargets + { + Assembly = 0x0001, + Module = 0x0002, + Class = 0x0004, + Struct = 0x0008, + Enum = 0x0010, + Constructor = 0x0020, + Method = 0x0040, + Property = 0x0080, + Field = 0x0100, + Event = 0x0200, + Interface = 0x0400, + Parameter = 0x0800, + Delegate = 0x1000, + ReturnValue = 0x2000, + GenericParameter = 0x4000, + + All = Assembly | Module | Class | Struct | Enum | Constructor | + Method | Property | Field | Event | Interface | Parameter | + Delegate | ReturnValue | GenericParameter + } + + public unsafe class Object + { + // The layout of object is a contract with the compiler. + internal unsafe EEType* m_pEEType; + + [StructLayout(LayoutKind.Sequential)] + private class RawData + { + public byte Data; + } + + internal ref byte GetRawData() + { + return ref Unsafe.As(this).Data; + } + + internal uint GetRawDataSize() + { + return m_pEEType->BaseSize - (uint)sizeof(ObjHeader) - (uint)sizeof(EEType*); + } + + public Object() { } + ~Object() { } + + + public virtual bool Equals(object o) + => false; + + public virtual int GetHashCode() + => 0; + + public virtual string ToString() + => "{object}"; + + + public virtual void Dispose() + { + var obj = this; + //Allocator.Free(Unsafe.As(ref obj)); CALL FREE + } + } + public struct Void { } + + // The layout of primitive types is special cased because it would be recursive. + // These really don't need any fields to work. + public struct Boolean { } + public struct Char { } + public struct SByte { } + public struct Byte { } + public struct Int16 { } + public struct UInt16 { } + public struct Int32 { } + public struct UInt32 { } + public struct Int64 { } + public struct UInt64 { } + public unsafe struct IntPtr + { + void* _value; + + public IntPtr(void* value) { _value = value; } + public IntPtr(int value) { _value = (void*)value; } + public IntPtr(uint value) { _value = (void*)value; } + public IntPtr(long value) { _value = (void*)value; } + public IntPtr(ulong value) { _value = (void*)value; } + + [Intrinsic] + public static readonly IntPtr Zero; + + //public override bool Equals(object o) + // => _value == ((IntPtr)o)._value; + + public bool Equals(IntPtr ptr) + => _value == ptr._value; + + //public override int GetHashCode() + // => (int)_value; + + public static explicit operator IntPtr(int value) => new IntPtr(value); + public static explicit operator IntPtr(uint value) => new IntPtr(value); + public static explicit operator IntPtr(long value) => new IntPtr(value); + public static explicit operator IntPtr(ulong value) => new IntPtr(value); + public static explicit operator IntPtr(void* value) => new IntPtr(value); + public static explicit operator void*(IntPtr value) => value._value; + + public static explicit operator int(IntPtr value) + { + var l = (long)value._value; + + return checked((int)l); + } + + public static explicit operator long(IntPtr value) => (long)value._value; + public static explicit operator ulong(IntPtr value) => (ulong)value._value; + + public static IntPtr operator +(IntPtr a, uint b) + => new IntPtr((byte*)a._value + b); + + public static IntPtr operator +(IntPtr a, ulong b) + => new IntPtr((byte*)a._value + b); + + public static bool operator ==(IntPtr a, IntPtr b) + { + return a._value == b._value; + } + + public static bool operator !=(IntPtr a, IntPtr b) + { + return !(a._value == b._value); + } + } + public struct UIntPtr { } + public struct Single { } + public struct Double { } + + public abstract class ValueType { } + public abstract class Enum : ValueType { } + + public struct Nullable where T : struct { } + + public sealed unsafe class String + { + [Intrinsic] + public static readonly string Empty = ""; + + + // The layout of the string type is a contract with the compiler. + int _length; + internal char _firstChar; + + + public int Length + { + [Intrinsic] + get { return _length; } + internal set { _length = value; } + } + + public unsafe char this[int index] + { + [Intrinsic] + get + { + return Unsafe.Add(ref _firstChar, index); + } + + set + { + fixed (char* p = &_firstChar) p[index] = value; + } + } + } + + public abstract class Array { } + public abstract class Delegate { } + public abstract class MulticastDelegate : Delegate { } + + public struct RuntimeTypeHandle { } + public struct RuntimeMethodHandle { } + public struct RuntimeFieldHandle { } + + public class Attribute { } + + namespace Runtime.CompilerServices + { + public class RuntimeHelpers + { + public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int); + } + + public static class RuntimeFeature + { + public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention); + } + + internal sealed class IntrinsicAttribute : Attribute { } + + public static unsafe class Unsafe + { + [Intrinsic] + public static extern ref T Add(ref T source, int elementOffset); + + [Intrinsic] + public static extern ref TTo As(ref TFrom source); + + [Intrinsic] + public static extern T As(object value) where T : class; + + [Intrinsic] + public static extern void* AsPointer(ref T value); + + [Intrinsic] + public static extern ref T AsRef(void* pointer); + + public static ref T AsRef(IntPtr pointer) + => ref AsRef((void*)pointer); + + [Intrinsic] + public static extern int SizeOf(); + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T AddByteOffset(ref T source, IntPtr byteOffset) + { + for (; ; ); + } + + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ref T AddByteOffset(ref T source, nuint byteOffset) + { + return ref AddByteOffset(ref source, (IntPtr)(void*)byteOffset); + } + } + + public sealed class MethodImplAttribute : Attribute + { + public MethodImplAttribute(MethodImplOptions methodImplOptions) { } + } + + public enum MethodImplOptions + { + Unmanaged = 0x0004, + NoInlining = 0x0008, + NoOptimization = 0x0040, + AggressiveInlining = 0x0100, + AggressiveOptimization = 0x200, + InternalCall = 0x1000, + } + } +} + +namespace System.Runtime.InteropServices +{ + public class UnmanagedType { } + + sealed class StructLayoutAttribute : Attribute + { + public StructLayoutAttribute(LayoutKind layoutKind) + { + } + } + + public sealed class DllImportAttribute : Attribute + { + public string EntryPoint; + public CharSet CharSet; + public bool SetLastError; + public bool ExactSpelling; + public CallingConvention CallingConvention; + public bool BestFitMapping; + public bool PreserveSig; + public bool ThrowOnUnmappableChar; + + public string Value { get; } + + public DllImportAttribute(string dllName) + { + Value = dllName; + } + } + + internal enum LayoutKind + { + Sequential = 0, // 0x00000008, + Explicit = 2, // 0x00000010, + Auto = 3, // 0x00000000, + } + + public enum CharSet + { + None = 1, // User didn't specify how to marshal strings. + Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars. + Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars. + Auto = 4, // Marshal Strings in the right way for the target system. + } + + public enum CallingConvention + { + Winapi = 1, + Cdecl = 2, + StdCall = 3, + ThisCall = 4, + FastCall = 5, + } + + public sealed class FieldOffsetAttribute : Attribute + { + public FieldOffsetAttribute(int offset) + { + Value = offset; + } + + public int Value { get; } + } +} +#endregion + +#region Things needed by ILC +namespace System +{ + namespace Runtime + { + internal sealed class RuntimeExportAttribute : Attribute + { + public RuntimeExportAttribute(string entry) { } + } + } + + class Array : Array { } +} + +namespace Internal.TypeSystem +{ + public enum ExceptionStringID { } +} + +namespace Internal.Runtime.CompilerHelpers +{ + using Internal.TypeSystem; + using System.Runtime; + + // A class that the compiler looks for that has helpers to initialize the + // process. The compiler can gracefully handle the helpers not being present, + // but the class itself being absent is unhandled. Let's add an empty class. + class StartupCodeHelpers + { + // A couple symbols the generated code will need we park them in this class + // for no particular reason. These aid in transitioning to/from managed code. + // Since we don't have a GC, the transition is a no-op. + [RuntimeExport("__fail_fast")] + static void FailFast() { while (true) ; } + + [RuntimeExport("RhpReversePInvoke")] + static void RhpReversePInvoke(IntPtr frame) { } + [RuntimeExport("RhpReversePInvokeReturn")] + static void RhpReversePInvokeReturn(IntPtr frame) { } + [RuntimeExport("RhpReversePInvoke2")] + static void RhpReversePInvoke2(System.IntPtr frame) { } + [RuntimeExport("RhpReversePInvokeReturn2")] + static void RhpReversePInvokeReturn2(System.IntPtr frame) { } + [RuntimeExport("RhpPInvoke")] + static void RhpPInvoke(IntPtr frame) { } + [RuntimeExport("RhpPInvokeReturn")] + static void RhpPInvokeReturn(IntPtr frame) { } + + [RuntimeExport("RhpFallbackFailFast")] + static void RhpFallbackFailFast() { while (true) ; } + + [RuntimeExport("RhpAssignRef")] + static unsafe void RhpAssignRef(void** address, void* obj) + { + *address = obj; + } + + [RuntimeExport("RhpByRefAssignRef")] + static unsafe void RhpByRefAssignRef(void** address, void* obj) + { + *address = obj; + } + + [RuntimeExport("RhpCheckedAssignRefEAX")] + static unsafe void RhpCheckedAssignRefEAX(void** address, void* obj) + { + *address = obj; + } + + [RuntimeExport("RhpCheckedAssignRef")] + static unsafe void RhpCheckedAssignRef(void** address, void* obj) + { + *address = obj; + } + + [RuntimeExport("memset")] + static unsafe void MemSet(byte* ptr, int c, int count) + { + for (byte* p = ptr; p < ptr + count; p++) + *p = (byte)c; + } + + [RuntimeExport("memcpy")] + static unsafe void MemCpy(byte* dest, byte* src, ulong count) + { + for (ulong i = 0; i < count; i++) dest[i] = src[i]; + } + } + + public static class ThrowHelpers + { + public static void ThrowInvalidProgramException(ExceptionStringID id) { } + public static void ThrowInvalidProgramExceptionWithArgument(ExceptionStringID id, string methodName) { } + public static void ThrowOverflowException() { } + public static void ThrowIndexOutOfRangeException() { } + public static void ThrowTypeLoadException(ExceptionStringID id, string className, string typeName) { } + } +} +#endregion + +#region NET Runtime + +public unsafe class Console +{ + [DllImport("*")] + static extern int printf(char* text); + + [DllImport("*")] + static extern void clear(); + + public static void Clear() + { + clear(); + } + + public static void WriteLine(string str) + { + fixed (char *unsafeStr = str) + { + printf(unsafeStr); + } + } +} + +#endregion \ No newline at end of file diff --git a/source/lib/stdio.c b/source/lib/stdio.c deleted file mode 100644 index 64e7e7c..0000000 --- a/source/lib/stdio.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "stdio.h" - -int printf(const char *string, ...) -{ - int len; - - len = strlen(string); - asm volatile ( - "mov %1, %%edx\n" - "mov %0, %%ecx\n" - "mov $0x1, %%ebx\n" - "mov $0x4, %%eax\n" - "int $0x48\n" - "mov $0x1, %%eax\n" - "int $0x48\n" - : "=r"(len) - : "r"(string) - : "edx", "ebx", "eax" , "ecx", "memory" - ); - return (0); -} - -int strlen(char const *str) -{ - int counter = 0; - - while (str[counter] != 0) { - counter++; - } - return counter; -} \ No newline at end of file diff --git a/source/lib/stdlib.c b/source/lib/stdlib.c deleted file mode 100644 index bdca5a7..0000000 --- a/source/lib/stdlib.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "stdlib.h" - -asm (".code32\n" - "call main\n" - "ret\n"); diff --git a/source/lib/stdlib.h b/source/lib/stdlib.h deleted file mode 100644 index 9b94028..0000000 --- a/source/lib/stdlib.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __STDLIB_H__ -#define __STDLIB_H__ - -int printf(const char *, ...); -char *fgets(char *string); - -#endif diff --git a/source/linker/aura.ld b/source/linker/aura.ld deleted file mode 100644 index f621857..0000000 --- a/source/linker/aura.ld +++ /dev/null @@ -1 +0,0 @@ -OUTPUT_FORMAT(binary) diff --git a/source/main.c b/source/main.c deleted file mode 100644 index 5da1c56..0000000 --- a/source/main.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "lib/stdlib.h" - -int main(void) -{ - printf("Hello world from a flat binary!"); - return 0; -} diff --git a/source/noruntime.csproj b/source/noruntime.csproj new file mode 100644 index 0000000..d263671 --- /dev/null +++ b/source/noruntime.csproj @@ -0,0 +1,39 @@ + + + + Exe + net6.0 + x86 + true + + true + true + v4.0.30319 + false + false + + noruntime + Main + NATIVE + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/nuget.config b/source/nuget.config new file mode 100644 index 0000000..5211eeb --- /dev/null +++ b/source/nuget.config @@ -0,0 +1,8 @@ + + + + + + + +