예제 #1
0
        public KThread(Kernel kernel, KModule module, KPartition partition, string name, uint entryAddress, int priority, KThreadAttributes attributes, uint stackSize)
        {
            Debug.Assert(partition != null);

            Kernel = kernel;

            Name            = name;
            EntryAddress    = entryAddress;
            InitialPriority = priority;
            Priority        = priority;
            Attributes      = attributes;
            Module          = module;

            State = KThreadState.Stopped;

            ExitWaiters       = new FastLinkedList <KThread>();
            NotifiedCallbacks = new FastLinkedList <KCallback>();

            //if( stackSize < 65535 )
            //{
            //    Log.WriteLine( Verbosity.Normal, Feature.Bios, "KThread: attempt to allocate thread with a stack of {0} - forcing up to the minimum of 64K", stackSize );
            //    stackSize = 65535;
            //}

            RunClocks = 0;
            InterruptPreemptionCount = 0;
            ThreadPreemptionCount    = 0;

            Partition  = partition;
            StackBlock = partition.Allocate(string.Format("Thread '{0}' Stack", name), KAllocType.High, 0, stackSize);
            Debug.Assert(StackBlock != null);
            TlsBlock = partition.Allocate(string.Format("Thread '{0}' TLS", name), KAllocType.High, 0, 0x4000);                 // 16k of thread local storage --- enough?
            Debug.Assert(TlsBlock != null);
        }
예제 #2
0
        public LoadResults LoadModule( ModuleType type, Stream moduleStream, LoadParameters parameters )
        {
            Elf32_Shdr*[] allocSections = new Elf32_Shdr*[ 256 ];
            Elf32_Shdr*[] relocSections = new Elf32_Shdr*[ 256 ];
            int allocSectionsCount = 0;
            int relocSectionsCount = 0;

            LoadResults results = new LoadResults();
            results.Successful = false;
            results.Imports = new List<StubImport>();
            results.Exports = new List<StubExport>();
            results.ExportNames = new List<string>();
            results.MissingImports = new FastLinkedList<DelayedImport>();

            Debug.Assert( moduleStream != null );
            Debug.Assert( moduleStream.CanRead == true );
            if( ( moduleStream == null ) ||
                ( moduleStream.CanRead == false ) )
                return results;

            Kernel kernel = _bios._kernel;
            ICpu cpu = kernel.Cpu;
            MemorySystem memory = kernel.MemorySystem;

            IntPtr pbuffer = IntPtr.Zero;
            try
            {
                // Load the entire module in to memory - nasty, but it works
                int length = ( int )moduleStream.Length;
                byte[] mbuffer = new byte[ length ];
                moduleStream.Read( mbuffer, 0, length );
                pbuffer = Marshal.AllocHGlobal( length );
                Debug.Assert( pbuffer != IntPtr.Zero );
                Marshal.Copy( mbuffer, 0, pbuffer, length );
                mbuffer = null;

                byte* buffer = ( byte* )pbuffer.ToPointer();

                // Get header
                Elf32_Ehdr* header = ( Elf32_Ehdr* )buffer;
                //Debug.Assert( header->e_magic == Elf32_Ehdr.Magic );
                if( header->e_magic != Elf32_Ehdr.Magic )
                {
                    Log.WriteLine( Verbosity.Critical, Feature.Loader, "Module header does not match: {0:X8} != {1:X8}", header->e_magic, Elf32_Ehdr.Magic );
                    return results;
                }
                Debug.Assert( header->e_machine == Elf32_Ehdr.MachineMips );
                if( header->e_machine != Elf32_Ehdr.MachineMips )
                    return results;

                /*switch( type )
                {
                case ModuleType.Boot:
                    //Debug.Assert( header->e_type == ELF_EXEC_TYPE );
                    break;
                case ModuleType.Prx:
                    //Debug.Assert( header->e_type == ELF_PRX_TYPE );
                    break;
                }*/

                results.EntryAddress = header->e_entry;
                bool needsRelocation = (
                    ( header->e_entry < 0x08000000 ) ||
                    ( header->e_type == ElfType.Prx ) );

                // p-hdrs
                //for( int n = 0; n < header->e_phnum; n++ )
                //{
                //    Elf32_Phdr* phdr = ( Elf32_Phdr* )( buffer + header->e_phoff + ( header->e_phentsize * n ) );
                //}

                // 0x08900000
                //uint defaultLoad = 0x08880000;
                uint defaultLoad = 0x08800000;
                //uint defaultLoad = uint.MaxValue;

                // s-hdrs
                uint lextents = defaultLoad;
                uint extents = 0;
                for( int n = 0; n < header->e_shnum; n++ )
                {
                    Elf32_Shdr* shdr = ( Elf32_Shdr* )( buffer + header->e_shoff + ( header->e_shentsize * n ) );

                    if( ( shdr->sh_flags & ShFlags.Allocate ) == ShFlags.Allocate )
                    {
                        allocSections[ allocSectionsCount++ ] = shdr;
                        if( ( shdr->sh_addr > 0 ) && ( shdr->sh_addr < lextents ) )
                            lextents = shdr->sh_addr;
                        uint upperBound = shdr->sh_addr + shdr->sh_size;
                        if( upperBound > extents )
                            extents = upperBound;
                    }
                    if( ( shdr->sh_type == ShType.SHT_REL ) ||
                        ( shdr->sh_type == ShType.SHT_PRXRELOC ) )
                        relocSections[ relocSectionsCount++ ] = shdr;
                }

                uint bssSize = this.GetBssSize( buffer );
                extents += bssSize;

                // Module info
                Elf32_Shdr* moduleInfoShdr = FindSection( buffer, ".rodata.sceModuleInfo" );
                Debug.Assert( moduleInfoShdr != null );
                PspModuleInfo* moduleInfo = ( PspModuleInfo* )( buffer + moduleInfoShdr->sh_offset );
                results.GlobalPointer = moduleInfo->gp;
                results.Name = new string( &moduleInfo->name );

                // See if this module is already implemented
                BiosModule existing = _bios.FindModule( results.Name );
                if( existing != null )
                {
                    Log.WriteLine( Verbosity.Normal, Feature.Loader, "attempting to load module {0} with BIOS implementation; ignoring", results.Name );
                    results.Successful = true;
                    results.Ignored = true;
                    return results;
                }
                else
                    Log.WriteLine( Verbosity.Normal, Feature.Loader, "adding new module {0}", results.Name );

                uint baseAddress = 0;
                KMemoryBlock moduleBlock = null;
                if( needsRelocation == true )
                {
                    if( type == ModuleType.Boot )
                        baseAddress = defaultLoad;
                    else
                    {
                        // Find the next block in RAM
                        moduleBlock = kernel.Partitions[ 2 ].Allocate( string.Format( "Module {0}", results.Name ), KAllocType.Low, 0, extents );
                        baseAddress = moduleBlock.Address;
                    }

                    results.EntryAddress += baseAddress;
                    results.LowerBounds = baseAddress;
                    results.UpperBounds = baseAddress + extents;
                }
                else
                {
                    Debug.Assert( type == ModuleType.Boot );
                    results.LowerBounds = lextents; //0x08900000;
                    results.UpperBounds = extents;
                }

                // Allocate space taken by module
                if( type == ModuleType.Boot )
                {
                    Debug.Assert( moduleBlock == null );
                    moduleBlock = kernel.Partitions[ 2 ].Allocate( string.Format( "Module {0}", results.Name ), KAllocType.Specific, results.LowerBounds, results.UpperBounds - results.LowerBounds );
                }
                else
                {
                    // Should be done above
                    Debug.Assert( moduleBlock != null );
                }
                Debug.Assert( moduleBlock != null );

                // Allocate sections in memory
                for( int n = 0; n < allocSectionsCount; n++ )
                {
                    Elf32_Shdr* shdr = allocSections[ n ];
                    uint address = baseAddress + shdr->sh_addr;
                    byte* pdest = memory.Translate( address );

                    switch( shdr->sh_type )
                    {
                        case ShType.SHT_NOBITS:
                            // Write zeros?
                            MemorySystem.ZeroMemory( pdest, shdr->sh_size );
                            break;
                        default:
                        case ShType.SHT_PROGBITS:
                            MemorySystem.CopyMemory( buffer + shdr->sh_offset, pdest, shdr->sh_size );
                            break;
                    }
                }

                // Zero out BSS if present
                if( bssSize > 0 )
                {
                    Elf32_Shdr* bssShdr = FindSection( buffer, ".bss" );
                    Debug.Assert( bssShdr != null );
                    if( bssShdr != null )
                    {
                        uint address = baseAddress + bssShdr->sh_addr;
                        byte* pdest = memory.Translate( address );
                        MemorySystem.ZeroMemory( pdest, bssSize );
                    }
                }

                // Perform relocations
                if( needsRelocation == true )
                {
                    List<HiReloc> hiRelocs = new List<HiReloc>( 10 );
                    uint Elf32_RelSize = ( uint )sizeof( Elf32_Rel );

                    // Find symbol table
                    Elf32_Shdr* symtabShdr = FindSection( buffer, ".symtab" );
                    byte* symtab = null;
                    if( symtabShdr != null )
                        symtab = buffer + symtabShdr->sh_offset;

                    // Gather relocations
                    for( int n = 0; n < header->e_shnum; n++ )
                    {
                        Elf32_Shdr* shdr = ( Elf32_Shdr* )( buffer + header->e_shoff + ( header->e_shentsize * n ) );
                        if( ( shdr->sh_type != ShType.SHT_REL ) &&
                            ( shdr->sh_type != ShType.SHT_PRXRELOC ) )
                            continue;

                        hiRelocs.Clear();

                        Elf32_Shdr* targetHdr = ( Elf32_Shdr* )( buffer + header->e_shoff + ( header->e_shentsize * shdr->sh_info ) );

                        // If the target is not allocated, do not perform the relocation
                        if( ( targetHdr->sh_flags & ShFlags.Allocate ) != ShFlags.Allocate )
                            continue;

                        uint count = shdr->sh_size / Elf32_RelSize;
                        for( uint m = 0; m < count; m++ )
                        {
                            Elf32_Rel* reloc = ( Elf32_Rel* )( buffer + shdr->sh_offset + ( sizeof( Elf32_Rel ) * m ) );
                            //uint rtype = ELF32_R_TYPE( reloc->r_info );
                            //uint symbolIndex = ELF32_R_SYM( reloc->r_info );
                            RelType rtype = ( RelType )( reloc->r_info & 0xFF );
                            uint symbolIndex = reloc->r_info >> 8;
                            uint offset = reloc->r_offset;

                            if( rtype == RelType.None )
                                continue;

                            uint basea = baseAddress;
                            offset += baseAddress;

                            if( shdr->sh_type == ShType.SHT_REL )
                            {
                                // Elf style - use symbol table
                                Elf32_Sym* sym = null;
                                if( symbolIndex != 0 )
                                {
                                    Debug.Assert( symtab != null );
                                    sym = ( Elf32_Sym* )( symtab + ( sizeof( Elf32_Sym ) * symbolIndex ) );
                                    basea += sym->st_value;
                                }
                            }
                            else if( shdr->sh_type == ShType.SHT_PRXRELOC )
                            {
                                // PRX style - crazy!
                                uint offsetHeaderN = symbolIndex & 0xFF;
                                uint valueHeaderN = ( symbolIndex >> 8 ) & 0xFF;
                                Debug.Assert( offsetHeaderN < header->e_phnum );
                                Debug.Assert( valueHeaderN < header->e_phnum );
                                Elf32_Phdr* offsetHeader = ( Elf32_Phdr* )( buffer + header->e_phoff + ( header->e_phentsize * offsetHeaderN ) );
                                Elf32_Phdr* valueHeader = ( Elf32_Phdr* )( buffer + header->e_phoff + ( header->e_phentsize * valueHeaderN ) );

                                basea += valueHeader->p_vaddr;
                                offset += offsetHeader->p_vaddr;
                            }

                            uint* pcode = ( uint* )memory.Translate( offset );
                            Debug.Assert( pcode != null );

                            switch( rtype )
                            {
                                case RelType.MipsHi16:
                                    {
                                        HiReloc hiReloc = new HiReloc();
                                        hiReloc.Value = basea;
                                        hiReloc.CodePointer = ( uint* )pcode;
                                        hiRelocs.Add( hiReloc );
                                    }
                                    break;
                                case RelType.MipsLo16:
                                    {
                                        uint code = *pcode;
                                        uint vallo = ( ( code & 0x0000FFFF ) ^ 0x00008000 ) - 0x00008000;
                                        while( hiRelocs.Count > 0 )
                                        {
                                            HiReloc hiReloc = hiRelocs[ hiRelocs.Count - 1 ];
                                            hiRelocs.RemoveAt( hiRelocs.Count - 1 );

                                            Debug.Assert( hiReloc.Value == basea );

                                            uint value2 = *hiReloc.CodePointer;
                                            uint temp = ( ( value2 & 0x0000FFFF ) << 16 ) + vallo;
                                            temp += basea;
                                            temp = ( ( temp >> 16 ) + ( ( ( temp & 0x00008000 ) != 0 ) ? ( uint )1 : ( uint )0 ) ) & 0x0000FFFF;
                                            value2 = ( uint )( ( value2 & ~0x0000FFFF ) | temp );

                                            //Debug.WriteLine( string.Format( "   Updating memory at 0x{0:X8} to {1:X8} (from previous HI16)", hiReloc.Address, value2 ) );
                                            *hiReloc.CodePointer = value2;
                                        }
                                        *pcode = ( uint )( ( code & ~0x0000FFFF ) | ( ( basea + vallo ) & 0x0000FFFF ) );
                                    }
                                    break;
                                case RelType.Mips26:
                                    {
                                        uint code = *pcode;
                                        uint addr = ( code & 0x03FFFFFF ) << 2;
                                        addr += basea;
                                        *pcode = ( code & unchecked( ( uint )~0x03FFFFFF ) ) | ( addr >> 2 );
                                    }
                                    break;
                                case RelType.Mips32:
                                    *pcode += basea;
                                    break;
                            }
                        }

                        Debug.Assert( hiRelocs.Count == 0 );
                        // Finish off hi relocs?
                    }
                }

                IDebugDatabase db = null;
                if( parameters.AppendDatabase == true )
                {
                    Debug.Assert( Diag.Instance.Database != null );
                    db = Diag.Instance.Database;
                    db.BeginUpdate();
                }

                // Assign ID now so we can use it for symbols/etc
                uint moduleId = _bios._kernel.AllocateID();
                results.ModuleID = moduleId;

                int variableExportCount = 0;
                int functionExportCount = 0;

                // Get exports
                uint PspModuleExportSize = ( uint )sizeof( PspModuleExport );
                uint pexports = moduleInfo->exports + ( ( needsRelocation == true ) ? baseAddress : 0 );
                uint exportCount = ( moduleInfo->exp_end - moduleInfo->exports ) / PspModuleExportSize;
                for( uint n = 0; n < exportCount; n++ )
                {
                    PspModuleExport* ex = ( PspModuleExport* )memory.Translate( pexports + ( PspModuleExportSize * n ) );
                    string name = null;
                    if( ex->name != 0x0 )
                        name = kernel.ReadString( ex->name );

                    // Ignore null names?
                    if( ex->name == 0x0 )
                        continue;
                    results.ExportNames.Add( name );

                    uint* pnid = ( uint* )memory.Translate( ex->exports );
                    uint* pvalue = ( uint* )memory.Translate( ex->exports + ( ( uint )( ex->func_count + ex->var_count ) * 4 ) );

                    for( int m = 0; m < ( ex->func_count + ex->var_count ); m++ )
                    {
                        uint nid = *( pnid + m );
                        uint value = *( pvalue + m );

                        StubExport stubExport = new StubExport();
                        if( m < ex->func_count )
                        {
                            stubExport.Type = StubType.Function;
                            Log.WriteLine( Verbosity.Verbose, Feature.Loader, "export func {0} 0x{1:X8}: {2:X8}", name, nid, value );
                            functionExportCount++;
                        }
                        else
                        {
                            stubExport.Type = StubType.Variable;
                            Log.WriteLine( Verbosity.Verbose, Feature.Loader, "export var {0} 0x{1:X8}: {2:X8}", name, nid, value );
                            variableExportCount++;
                        }
                        stubExport.ModuleName = name;
                        stubExport.NID = nid;
                        stubExport.Address = value;
                        results.Exports.Add( stubExport );
                    }
                }

                int nidGoodCount = 0;
                int nidNotFoundCount = 0;
                int nidNotImplementedCount = 0;
                int moduleNotFoundCount = 0;
                List<string> missingModules = new List<string>();

                // Get imports
                uint PspModuleImportSize = ( uint )sizeof( PspModuleImport );
                uint pimports = moduleInfo->imports + ( ( needsRelocation == true ) ? baseAddress : 0 );
                uint importCount = ( moduleInfo->imp_end - moduleInfo->imports ) / PspModuleImportSize;
                for( uint n = 0; n < importCount; n++ )
                {
                    PspModuleImport* im = ( PspModuleImport* )memory.Translate( pimports + ( PspModuleImportSize * n ) );
                    string name = null;
                    if( im->name != 0x0 )
                        name = kernel.ReadString( im->name );
                    Debug.Assert( name != null );

                    BiosModule module = _bios.FindModule( name );
                    if( module == null )
                        missingModules.Add( name );

                    uint* pnid = ( uint* )memory.Translate( im->nids );
                    uint* pfuncs = ( uint* )memory.Translate( im->funcs );

                    // Functions & variables at the same time
                    for( int m = 0; m < ( im->func_count + im->var_count ); m++ )
                    {
                        uint nid = *( pnid + m );
                        uint* pcode = ( pfuncs + ( m * 2 ) );

                        BiosFunction function = _bios.FindFunction( nid );

                        StubImport stubImport = new StubImport();
                        if( module == null )
                        {
                            stubImport.Result = StubReferenceResult.ModuleNotFound;
                            moduleNotFoundCount++;
                            results.MissingImports.Enqueue( new DelayedImport( stubImport ) );
                        }
                        else if( function == null )
                        {
                            Log.WriteLine( Verbosity.Normal, Feature.Loader, "{0} 0x{1:X8} not found (NID not present)", name, nid );
                            stubImport.Result = StubReferenceResult.NidNotFound;
                            nidNotFoundCount++;
                            results.MissingImports.Enqueue( new DelayedImport( stubImport ) );
                        }
                        else if( function.IsImplemented == false )
                        {
                            Log.WriteLine( Verbosity.Normal, Feature.Loader, "0x{1:X8} found and patched at 0x{2:X8} -> {0}::{3} (NI)", name, nid, ( uint )pcode, function.Name );
                            stubImport.Result = StubReferenceResult.NidNotImplemented;
                            nidNotImplementedCount++;
                        }
                        else
                        {
                            Log.WriteLine( Verbosity.Verbose, Feature.Loader, "0x{1:X8} found and patched at 0x{2:X8} -> {0}::{3}", name, nid, ( uint )pcode, function.Name );
                            stubImport.Result = StubReferenceResult.Success;
                            nidGoodCount++;
                        }

                        // Create dummy when needed
                        if( function == null )
                        {
                            function = new BiosFunction( module, nid );
                            _bios.RegisterFunction( function );
                        }

                        if( m < im->func_count )
                            stubImport.Type = StubType.Function;
                        else
                            stubImport.Type = StubType.Variable;
                        stubImport.ModuleName = name;
                        stubImport.Function = function;
                        stubImport.NID = nid;
                        stubImport.Address = ( uint )pcode;
                        results.Imports.Add( stubImport );

                        function.StubAddress = ( uint )( im->funcs + ( m * 2 ) * 4 );

                        // Perform fixup
                        {
                            uint syscall = cpu.RegisterSyscall( nid );
                            *( pcode + 1 ) = ( uint )( ( syscall << 6 ) | 0xC );
                        }

                        // Add to debug database
                        if( db != null )
                        {
                            Method method = new Method( moduleId, MethodType.Bios, function.StubAddress, 8, new BiosFunctionToken( stubImport.Function ) );
                            db.AddSymbol( method );
                        }
                    }
                }

                // If symbols are present, use those to add methods and variables
                // Otherwise, we need to try to figure them out (good luck!)
                if( db != null )
                {
                    // Find symbol table
                    Elf32_Shdr* symtabShdr = FindSection( buffer, ".symtab" );
                    if( ( symtabShdr != null ) &&
                        ( symtabShdr->sh_size > 0x100 ) )
                    {
                        byte* strtab = FindSectionAddress( buffer, symtabShdr->sh_link );

                        int symbolCount = ( int )symtabShdr->sh_size / sizeof( Elf32_Sym );
                        byte* symtab = buffer + symtabShdr->sh_offset;
                        byte* p = symtab;
                        for( int n = 0; n < symbolCount; n++, p += sizeof( Elf32_Sym ) )
                        {
                            Elf32_Sym* sym = ( Elf32_Sym* )p;
                            uint symType = sym->st_info & ( uint )0xF;
                            if( ( symType != 0x1 ) && ( symType != 0x2 ) )
                                continue;
                            if( sym->st_size == 0 )
                                continue;

                            Elf32_Shdr* shdr = FindSection( buffer, sym->st_shndx );
                            Debug.Assert( shdr != null );

                            string name = null;
                            if( sym->st_name != 0 )
                                name = this.GetName( strtab, ( int )sym->st_name );
                            uint address = baseAddress + sym->st_value;
                            if( needsRelocation == true )
                                address += shdr->sh_addr;
                            Symbol symbol = null;
                            if( symType == 0x1 )
                            {
                                // OBJECT
                                symbol = new Variable( moduleId, address, sym->st_size, name );
                            }
                            else if( symType == 0x2 )
                            {
                                // FUNC
                                symbol = new Method( moduleId, MethodType.User, address, sym->st_size, name );
                            }
                            if( symbol != null )
                                db.AddSymbol( symbol );
                        }

                        results.HadSymbols = true;

                        Log.WriteLine( Verbosity.Verbose, Feature.Loader, "Found {0} methods and {1} variables in the symbol table", db.MethodCount, db.VariableCount );
                    }
                    else
                    {
                        // No symbol table found - try to build the symbols
                        Elf32_Shdr* textShdr = FindSection( buffer, ".text" );
                        Debug.Assert( textShdr != null );
                        uint textAddress = baseAddress + textShdr->sh_addr;
                        byte* text = memory.TranslateMainMemory( ( int )textAddress );
                        uint size = textShdr->sh_size;
                        this.Analyze( moduleId, db, text, size, textAddress );

                        Log.WriteLine( Verbosity.Verbose, Feature.Loader, "Found {0} methods by analysis", db.MethodCount );
                    }

                    // End update, started above
                    db.EndUpdate();

                    //foreach( Method method in db.GetMethods() )
                    //    Debug.WriteLine( method.ToString() );
                }

            #if DEBUG
                // Print stats
                Log.WriteLine( Verbosity.Critical, Feature.Loader, "Load complete: {0}/{1} ({2}%) NIDs good; {3} not implemented, {4} not found, {5} in missing modules = {6} total", nidGoodCount, results.Imports.Count - moduleNotFoundCount, ( nidGoodCount / ( float )( results.Imports.Count - moduleNotFoundCount ) ) * 100.0f, nidNotImplementedCount, nidNotFoundCount, moduleNotFoundCount, results.Imports.Count );
                if( missingModules.Count > 0 )
                {
                    Log.WriteLine( Verbosity.Normal, Feature.Loader, "{0} missing modules (contain {1} NIDs ({2}% of total)):", missingModules.Count, moduleNotFoundCount, ( moduleNotFoundCount / ( float )results.Imports.Count ) * 100.0f );
                    foreach( string moduleName in missingModules )
                        Log.WriteLine( Verbosity.Normal, Feature.Loader, "\t\t{0}", moduleName );
                }
                if( ( functionExportCount > 0 ) || ( variableExportCount > 0 ) )
                    Log.WriteLine( Verbosity.Critical, Feature.Loader, "Exported {0} functions and {1} variables", functionExportCount, variableExportCount );
            #endif

                results.Successful = true;

                if( results.Successful == true )
                {
                    // If we export things, go back and find previously loaded modules that may need fixing up
                    if( ( functionExportCount > 0 ) || ( variableExportCount > 0 ) )
                    {
                        bool fixupResult = this.FixupDelayedImports( kernel, results );
                        Debug.Assert( fixupResult == true );
                    }

                    // If we are the boot load - we do some special stuff like run start, etc
                    // If we are a PRX, all that is taken care of for us by the user code making calls
                    if( type == ModuleType.Boot )
                    {
                        KModule module = new KModule( kernel, new BiosModule( results.Name, results.Exports.ToArray() ) );
                        module.UID = moduleId;
                        module.LoadParameters = parameters;
                        module.LoadResults = results;
                        kernel.UserModules.Add( module );
                        Debug.Assert( kernel.MainModule == null );
                        kernel.MainModule = module;
                        kernel.AddHandle( module );

                        // Allocate room for args
                        KMemoryBlock argsBlock = kernel.Partitions[ 2 ].Allocate( string.Format( "Module {0} args", results.Name ), KAllocType.High, 0, 0xFF ); // 256b of args - enough?

                        // Set arguments - we put these right below user space, and right above the stack
                        uint args = 0;
                        uint argp = argsBlock.Address;
                        string arg0 = parameters.Path.AbsolutePath.Replace( '\\', '/' ) + "/";
                        args += ( uint )arg0.Length + 1;
                        kernel.WriteString( argp, arg0 );
                        uint envp = argp;// + args;
                        // write envp??

                        // What we do here simulates what the modulemgr does - it creates a user mode thread that
                        // runs module_start (_start, etc) and then exits when complete.
                        // NOTE: If we are a PRX, the entry address will correspond to module_start, so we don't need to do anything!

                        // Create a thread
                        KThread thread = new KThread( kernel,
                            module,
                            kernel.Partitions[ 2 ],
                            "kmodule_thread",
                            results.EntryAddress,
                            0,
                            KThreadAttributes.User,
                            0x4000 );
                        thread.GlobalPointer = results.GlobalPointer;
                        kernel.AddHandle( thread );
                        thread.Start( args, argp );

                        // Setup handler so that we get the callback when the thread ends and we can kill it
                        cpu.SetContextSafetyCallback( thread.ContextID, new ContextSafetyDelegate( this.KmoduleThreadEnd ), ( int )thread.UID );

                        Log.WriteLine( Verbosity.Verbose, Feature.Loader, "starting kmodule loading thread with UID {0:X}", thread.UID );

                        // Schedule so that our thread runs
                        kernel.Schedule();

                        // If debugging, set start breakpoint
                        if( Diag.IsAttached == true )
                            Diag.Instance.Client.OnBootModuleLoaded( results.EntryAddress );

                        if( Diag.IsAttached == true )
                            Diag.Instance.Client.OnModuleLoaded();

                        //kernel.MemorySystem.DumpMainMemory( "startup.bin" );
                    }
                }
            }
            finally
            {
                if( pbuffer != IntPtr.Zero )
                    Marshal.FreeHGlobal( pbuffer );
            }

            return results;
        }
예제 #3
0
        public KThread( Kernel kernel, KModule module, KPartition partition, string name, uint entryAddress, int priority, KThreadAttributes attributes, uint stackSize )
        {
            Debug.Assert( partition != null );

            Kernel = kernel;

            Name = name;
            EntryAddress = entryAddress;
            InitialPriority = priority;
            Priority = priority;
            Attributes = attributes;
            Module = module;

            State = KThreadState.Stopped;

            ExitWaiters = new FastLinkedList<KThread>();
            NotifiedCallbacks = new FastLinkedList<KCallback>();

            //if( stackSize < 65535 )
            //{
            //    Log.WriteLine( Verbosity.Normal, Feature.Bios, "KThread: attempt to allocate thread with a stack of {0} - forcing up to the minimum of 64K", stackSize );
            //    stackSize = 65535;
            //}

            RunClocks = 0;
            InterruptPreemptionCount = 0;
            ThreadPreemptionCount = 0;

            Partition = partition;
            StackBlock = partition.Allocate( string.Format( "Thread '{0}' Stack", name ), KAllocType.High, 0, stackSize );
            Debug.Assert( StackBlock != null );
            TlsBlock = partition.Allocate( string.Format( "Thread '{0}' TLS", name ), KAllocType.High, 0, 0x4000 ); // 16k of thread local storage --- enough?
            Debug.Assert( TlsBlock != null );
        }
예제 #4
0
        public LoadResults LoadModule(ModuleType type, Stream moduleStream, LoadParameters parameters)
        {
            Elf32_Shdr *[] allocSections      = new Elf32_Shdr *[256];
            Elf32_Shdr *[] relocSections      = new Elf32_Shdr *[256];
            int            allocSectionsCount = 0;
            int            relocSectionsCount = 0;

            LoadResults results = new LoadResults();

            results.Successful     = false;
            results.Imports        = new List <StubImport>();
            results.Exports        = new List <StubExport>();
            results.ExportNames    = new List <string>();
            results.MissingImports = new FastLinkedList <DelayedImport>();

            Debug.Assert(moduleStream != null);
            Debug.Assert(moduleStream.CanRead == true);
            if ((moduleStream == null) ||
                (moduleStream.CanRead == false))
            {
                return(results);
            }

            Kernel       kernel = _bios._kernel;
            ICpu         cpu    = kernel.Cpu;
            MemorySystem memory = kernel.MemorySystem;

            IntPtr pbuffer = IntPtr.Zero;

            try
            {
                // Load the entire module in to memory - nasty, but it works
                int    length  = ( int )moduleStream.Length;
                byte[] mbuffer = new byte[length];
                moduleStream.Read(mbuffer, 0, length);
                pbuffer = Marshal.AllocHGlobal(length);
                Debug.Assert(pbuffer != IntPtr.Zero);
                Marshal.Copy(mbuffer, 0, pbuffer, length);
                mbuffer = null;

                byte *buffer = ( byte * )pbuffer.ToPointer();

                // Get header
                Elf32_Ehdr *header = ( Elf32_Ehdr * )buffer;
                //Debug.Assert( header->e_magic == Elf32_Ehdr.Magic );
                if (header->e_magic != Elf32_Ehdr.Magic)
                {
                    Log.WriteLine(Verbosity.Critical, Feature.Loader, "Module header does not match: {0:X8} != {1:X8}", header->e_magic, Elf32_Ehdr.Magic);
                    return(results);
                }
                Debug.Assert(header->e_machine == Elf32_Ehdr.MachineMips);
                if (header->e_machine != Elf32_Ehdr.MachineMips)
                {
                    return(results);
                }

                /*switch( type )
                 * {
                 * case ModuleType.Boot:
                 *      //Debug.Assert( header->e_type == ELF_EXEC_TYPE );
                 *      break;
                 * case ModuleType.Prx:
                 *      //Debug.Assert( header->e_type == ELF_PRX_TYPE );
                 *      break;
                 * }*/

                results.EntryAddress = header->e_entry;
                bool needsRelocation = (
                    (header->e_entry < 0x08000000) ||
                    (header->e_type == ElfType.Prx));

                // p-hdrs
                //for( int n = 0; n < header->e_phnum; n++ )
                //{
                //    Elf32_Phdr* phdr = ( Elf32_Phdr* )( buffer + header->e_phoff + ( header->e_phentsize * n ) );
                //}

                // 0x08900000
                //uint defaultLoad = 0x08880000;
                uint defaultLoad = 0x08800000;
                //uint defaultLoad = uint.MaxValue;

                // s-hdrs
                uint lextents = defaultLoad;
                uint extents  = 0;
                for (int n = 0; n < header->e_shnum; n++)
                {
                    Elf32_Shdr *shdr = ( Elf32_Shdr * )(buffer + header->e_shoff + (header->e_shentsize * n));

                    if ((shdr->sh_flags & ShFlags.Allocate) == ShFlags.Allocate)
                    {
                        allocSections[allocSectionsCount++] = shdr;
                        if ((shdr->sh_addr > 0) && (shdr->sh_addr < lextents))
                        {
                            lextents = shdr->sh_addr;
                        }
                        uint upperBound = shdr->sh_addr + shdr->sh_size;
                        if (upperBound > extents)
                        {
                            extents = upperBound;
                        }
                    }
                    if ((shdr->sh_type == ShType.SHT_REL) ||
                        (shdr->sh_type == ShType.SHT_PRXRELOC))
                    {
                        relocSections[relocSectionsCount++] = shdr;
                    }
                }

                uint bssSize = this.GetBssSize(buffer);
                extents += bssSize;

                // Module info
                Elf32_Shdr *moduleInfoShdr = FindSection(buffer, ".rodata.sceModuleInfo");
                Debug.Assert(moduleInfoShdr != null);
                PspModuleInfo *moduleInfo = ( PspModuleInfo * )(buffer + moduleInfoShdr->sh_offset);
                results.GlobalPointer = moduleInfo->gp;
                results.Name          = new string( &moduleInfo->name );

                // See if this module is already implemented
                BiosModule existing = _bios.FindModule(results.Name);
                if (existing != null)
                {
                    Log.WriteLine(Verbosity.Normal, Feature.Loader, "attempting to load module {0} with BIOS implementation; ignoring", results.Name);
                    results.Successful = true;
                    results.Ignored    = true;
                    return(results);
                }
                else
                {
                    Log.WriteLine(Verbosity.Normal, Feature.Loader, "adding new module {0}", results.Name);
                }

                uint         baseAddress = 0;
                KMemoryBlock moduleBlock = null;
                if (needsRelocation == true)
                {
                    if (type == ModuleType.Boot)
                    {
                        baseAddress = defaultLoad;
                    }
                    else
                    {
                        // Find the next block in RAM
                        moduleBlock = kernel.Partitions[2].Allocate(string.Format("Module {0}", results.Name), KAllocType.Low, 0, extents);
                        baseAddress = moduleBlock.Address;
                    }

                    results.EntryAddress += baseAddress;
                    results.LowerBounds   = baseAddress;
                    results.UpperBounds   = baseAddress + extents;
                }
                else
                {
                    Debug.Assert(type == ModuleType.Boot);
                    results.LowerBounds = lextents;                     //0x08900000;
                    results.UpperBounds = extents;
                }

                // Allocate space taken by module
                if (type == ModuleType.Boot)
                {
                    Debug.Assert(moduleBlock == null);
                    moduleBlock = kernel.Partitions[2].Allocate(string.Format("Module {0}", results.Name), KAllocType.Specific, results.LowerBounds, results.UpperBounds - results.LowerBounds);
                }
                else
                {
                    // Should be done above
                    Debug.Assert(moduleBlock != null);
                }
                Debug.Assert(moduleBlock != null);

                // Allocate sections in memory
                for (int n = 0; n < allocSectionsCount; n++)
                {
                    Elf32_Shdr *shdr    = allocSections[n];
                    uint        address = baseAddress + shdr->sh_addr;
                    byte *      pdest   = memory.Translate(address);

                    switch (shdr->sh_type)
                    {
                    case ShType.SHT_NOBITS:
                        // Write zeros?
                        MemorySystem.ZeroMemory(pdest, shdr->sh_size);
                        break;

                    default:
                    case ShType.SHT_PROGBITS:
                        MemorySystem.CopyMemory(buffer + shdr->sh_offset, pdest, shdr->sh_size);
                        break;
                    }
                }

                // Zero out BSS if present
                if (bssSize > 0)
                {
                    Elf32_Shdr *bssShdr = FindSection(buffer, ".bss");
                    Debug.Assert(bssShdr != null);
                    if (bssShdr != null)
                    {
                        uint  address = baseAddress + bssShdr->sh_addr;
                        byte *pdest   = memory.Translate(address);
                        MemorySystem.ZeroMemory(pdest, bssSize);
                    }
                }

                // Perform relocations
                if (needsRelocation == true)
                {
                    List <HiReloc> hiRelocs      = new List <HiReloc>(10);
                    uint           Elf32_RelSize = ( uint )sizeof(Elf32_Rel);

                    // Find symbol table
                    Elf32_Shdr *symtabShdr = FindSection(buffer, ".symtab");
                    byte *      symtab     = null;
                    if (symtabShdr != null)
                    {
                        symtab = buffer + symtabShdr->sh_offset;
                    }

                    // Gather relocations
                    for (int n = 0; n < header->e_shnum; n++)
                    {
                        Elf32_Shdr *shdr = ( Elf32_Shdr * )(buffer + header->e_shoff + (header->e_shentsize * n));
                        if ((shdr->sh_type != ShType.SHT_REL) &&
                            (shdr->sh_type != ShType.SHT_PRXRELOC))
                        {
                            continue;
                        }

                        hiRelocs.Clear();

                        Elf32_Shdr *targetHdr = ( Elf32_Shdr * )(buffer + header->e_shoff + (header->e_shentsize * shdr->sh_info));

                        // If the target is not allocated, do not perform the relocation
                        if ((targetHdr->sh_flags & ShFlags.Allocate) != ShFlags.Allocate)
                        {
                            continue;
                        }

                        uint count = shdr->sh_size / Elf32_RelSize;
                        for (uint m = 0; m < count; m++)
                        {
                            Elf32_Rel *reloc = ( Elf32_Rel * )(buffer + shdr->sh_offset + (sizeof(Elf32_Rel) * m));
                            //uint rtype = ELF32_R_TYPE( reloc->r_info );
                            //uint symbolIndex = ELF32_R_SYM( reloc->r_info );
                            RelType rtype       = ( RelType )(reloc->r_info & 0xFF);
                            uint    symbolIndex = reloc->r_info >> 8;
                            uint    offset      = reloc->r_offset;

                            if (rtype == RelType.None)
                            {
                                continue;
                            }

                            uint basea = baseAddress;
                            offset += baseAddress;

                            if (shdr->sh_type == ShType.SHT_REL)
                            {
                                // Elf style - use symbol table
                                Elf32_Sym *sym = null;
                                if (symbolIndex != 0)
                                {
                                    Debug.Assert(symtab != null);
                                    sym    = ( Elf32_Sym * )(symtab + (sizeof(Elf32_Sym) * symbolIndex));
                                    basea += sym->st_value;
                                }
                            }
                            else if (shdr->sh_type == ShType.SHT_PRXRELOC)
                            {
                                // PRX style - crazy!
                                uint offsetHeaderN = symbolIndex & 0xFF;
                                uint valueHeaderN  = (symbolIndex >> 8) & 0xFF;
                                Debug.Assert(offsetHeaderN < header->e_phnum);
                                Debug.Assert(valueHeaderN < header->e_phnum);
                                Elf32_Phdr *offsetHeader = ( Elf32_Phdr * )(buffer + header->e_phoff + (header->e_phentsize * offsetHeaderN));
                                Elf32_Phdr *valueHeader  = ( Elf32_Phdr * )(buffer + header->e_phoff + (header->e_phentsize * valueHeaderN));

                                basea  += valueHeader->p_vaddr;
                                offset += offsetHeader->p_vaddr;
                            }

                            uint *pcode = ( uint * )memory.Translate(offset);
                            Debug.Assert(pcode != null);

                            switch (rtype)
                            {
                            case RelType.MipsHi16:
                            {
                                HiReloc hiReloc = new HiReloc();
                                hiReloc.Value       = basea;
                                hiReloc.CodePointer = ( uint * )pcode;
                                hiRelocs.Add(hiReloc);
                            }
                            break;

                            case RelType.MipsLo16:
                            {
                                uint code  = *pcode;
                                uint vallo = ((code & 0x0000FFFF) ^ 0x00008000) - 0x00008000;
                                while (hiRelocs.Count > 0)
                                {
                                    HiReloc hiReloc = hiRelocs[hiRelocs.Count - 1];
                                    hiRelocs.RemoveAt(hiRelocs.Count - 1);

                                    Debug.Assert(hiReloc.Value == basea);

                                    uint value2 = *hiReloc.CodePointer;
                                    uint temp   = ((value2 & 0x0000FFFF) << 16) + vallo;
                                    temp  += basea;
                                    temp   = ((temp >> 16) + (((temp & 0x00008000) != 0) ? ( uint )1 : ( uint )0)) & 0x0000FFFF;
                                    value2 = ( uint )((value2 & ~0x0000FFFF) | temp);

                                    //Debug.WriteLine( string.Format( "   Updating memory at 0x{0:X8} to {1:X8} (from previous HI16)", hiReloc.Address, value2 ) );
                                    *hiReloc.CodePointer = value2;
                                }
                                *pcode = ( uint )((code & ~0x0000FFFF) | ((basea + vallo) & 0x0000FFFF));
                            }
                            break;

                            case RelType.Mips26:
                            {
                                uint code = *pcode;
                                uint addr = (code & 0x03FFFFFF) << 2;
                                addr += basea;
                                *pcode = (code & unchecked (( uint )~0x03FFFFFF)) | (addr >> 2);
                            }
                            break;

                            case RelType.Mips32:
                                *pcode += basea;
                                break;
                            }
                        }

                        Debug.Assert(hiRelocs.Count == 0);
                        // Finish off hi relocs?
                    }
                }

                IDebugDatabase db = null;
                if (parameters.AppendDatabase == true)
                {
                    Debug.Assert(Diag.Instance.Database != null);
                    db = Diag.Instance.Database;
                    db.BeginUpdate();
                }

                // Assign ID now so we can use it for symbols/etc
                uint moduleId = _bios._kernel.AllocateID();
                results.ModuleID = moduleId;

                int variableExportCount = 0;
                int functionExportCount = 0;

                // Get exports
                uint PspModuleExportSize = ( uint )sizeof(PspModuleExport);
                uint pexports            = moduleInfo->exports + ((needsRelocation == true) ? baseAddress : 0);
                uint exportCount         = (moduleInfo->exp_end - moduleInfo->exports) / PspModuleExportSize;
                for (uint n = 0; n < exportCount; n++)
                {
                    PspModuleExport *ex   = ( PspModuleExport * )memory.Translate(pexports + (PspModuleExportSize * n));
                    string           name = null;
                    if (ex->name != 0x0)
                    {
                        name = kernel.ReadString(ex->name);
                    }

                    // Ignore null names?
                    if (ex->name == 0x0)
                    {
                        continue;
                    }
                    results.ExportNames.Add(name);

                    uint *pnid   = ( uint * )memory.Translate(ex->exports);
                    uint *pvalue = ( uint * )memory.Translate(ex->exports + (( uint )(ex->func_count + ex->var_count) * 4));

                    for (int m = 0; m < (ex->func_count + ex->var_count); m++)
                    {
                        uint nid   = *(pnid + m);
                        uint value = *(pvalue + m);

                        StubExport stubExport = new StubExport();
                        if (m < ex->func_count)
                        {
                            stubExport.Type = StubType.Function;
                            Log.WriteLine(Verbosity.Verbose, Feature.Loader, "export func {0} 0x{1:X8}: {2:X8}", name, nid, value);
                            functionExportCount++;
                        }
                        else
                        {
                            stubExport.Type = StubType.Variable;
                            Log.WriteLine(Verbosity.Verbose, Feature.Loader, "export var {0} 0x{1:X8}: {2:X8}", name, nid, value);
                            variableExportCount++;
                        }
                        stubExport.ModuleName = name;
                        stubExport.NID        = nid;
                        stubExport.Address    = value;
                        results.Exports.Add(stubExport);
                    }
                }

                int           nidGoodCount           = 0;
                int           nidNotFoundCount       = 0;
                int           nidNotImplementedCount = 0;
                int           moduleNotFoundCount    = 0;
                List <string> missingModules         = new List <string>();

                // Get imports
                uint PspModuleImportSize = ( uint )sizeof(PspModuleImport);
                uint pimports            = moduleInfo->imports + ((needsRelocation == true) ? baseAddress : 0);
                uint importCount         = (moduleInfo->imp_end - moduleInfo->imports) / PspModuleImportSize;
                for (uint n = 0; n < importCount; n++)
                {
                    PspModuleImport *im   = ( PspModuleImport * )memory.Translate(pimports + (PspModuleImportSize * n));
                    string           name = null;
                    if (im->name != 0x0)
                    {
                        name = kernel.ReadString(im->name);
                    }
                    Debug.Assert(name != null);

                    BiosModule module = _bios.FindModule(name);
                    if (module == null)
                    {
                        missingModules.Add(name);
                    }

                    uint *pnid   = ( uint * )memory.Translate(im->nids);
                    uint *pfuncs = ( uint * )memory.Translate(im->funcs);

                    // Functions & variables at the same time
                    for (int m = 0; m < (im->func_count + im->var_count); m++)
                    {
                        uint  nid   = *(pnid + m);
                        uint *pcode = (pfuncs + (m * 2));

                        BiosFunction function = _bios.FindFunction(nid);

                        StubImport stubImport = new StubImport();
                        if (module == null)
                        {
                            stubImport.Result = StubReferenceResult.ModuleNotFound;
                            moduleNotFoundCount++;
                            results.MissingImports.Enqueue(new DelayedImport(stubImport));
                        }
                        else if (function == null)
                        {
                            Log.WriteLine(Verbosity.Normal, Feature.Loader, "{0} 0x{1:X8} not found (NID not present)", name, nid);
                            stubImport.Result = StubReferenceResult.NidNotFound;
                            nidNotFoundCount++;
                            results.MissingImports.Enqueue(new DelayedImport(stubImport));
                        }
                        else if (function.IsImplemented == false)
                        {
                            Log.WriteLine(Verbosity.Normal, Feature.Loader, "0x{1:X8} found and patched at 0x{2:X8} -> {0}::{3} (NI)", name, nid, ( uint )pcode, function.Name);
                            stubImport.Result = StubReferenceResult.NidNotImplemented;
                            nidNotImplementedCount++;
                        }
                        else
                        {
                            Log.WriteLine(Verbosity.Verbose, Feature.Loader, "0x{1:X8} found and patched at 0x{2:X8} -> {0}::{3}", name, nid, ( uint )pcode, function.Name);
                            stubImport.Result = StubReferenceResult.Success;
                            nidGoodCount++;
                        }

                        // Create dummy when needed
                        if (function == null)
                        {
                            function = new BiosFunction(module, nid);
                            _bios.RegisterFunction(function);
                        }

                        if (m < im->func_count)
                        {
                            stubImport.Type = StubType.Function;
                        }
                        else
                        {
                            stubImport.Type = StubType.Variable;
                        }
                        stubImport.ModuleName = name;
                        stubImport.Function   = function;
                        stubImport.NID        = nid;
                        stubImport.Address    = ( uint )pcode;
                        results.Imports.Add(stubImport);

                        function.StubAddress = ( uint )(im->funcs + (m * 2) * 4);

                        // Perform fixup
                        {
                            uint syscall = cpu.RegisterSyscall(nid);
                            *(pcode + 1) = ( uint )((syscall << 6) | 0xC);
                        }

                        // Add to debug database
                        if (db != null)
                        {
                            Method method = new Method(moduleId, MethodType.Bios, function.StubAddress, 8, new BiosFunctionToken(stubImport.Function));
                            db.AddSymbol(method);
                        }
                    }
                }

                // If symbols are present, use those to add methods and variables
                // Otherwise, we need to try to figure them out (good luck!)
                if (db != null)
                {
                    // Find symbol table
                    Elf32_Shdr *symtabShdr = FindSection(buffer, ".symtab");
                    if ((symtabShdr != null) &&
                        (symtabShdr->sh_size > 0x100))
                    {
                        byte *strtab = FindSectionAddress(buffer, symtabShdr->sh_link);

                        int   symbolCount = ( int )symtabShdr->sh_size / sizeof(Elf32_Sym);
                        byte *symtab      = buffer + symtabShdr->sh_offset;
                        byte *p           = symtab;
                        for (int n = 0; n < symbolCount; n++, p += sizeof(Elf32_Sym))
                        {
                            Elf32_Sym *sym     = ( Elf32_Sym * )p;
                            uint       symType = sym->st_info & ( uint )0xF;
                            if ((symType != 0x1) && (symType != 0x2))
                            {
                                continue;
                            }
                            if (sym->st_size == 0)
                            {
                                continue;
                            }

                            Elf32_Shdr *shdr = FindSection(buffer, sym->st_shndx);
                            Debug.Assert(shdr != null);

                            string name = null;
                            if (sym->st_name != 0)
                            {
                                name = this.GetName(strtab, ( int )sym->st_name);
                            }
                            uint address = baseAddress + sym->st_value;
                            if (needsRelocation == true)
                            {
                                address += shdr->sh_addr;
                            }
                            Symbol symbol = null;
                            if (symType == 0x1)
                            {
                                // OBJECT
                                symbol = new Variable(moduleId, address, sym->st_size, name);
                            }
                            else if (symType == 0x2)
                            {
                                // FUNC
                                symbol = new Method(moduleId, MethodType.User, address, sym->st_size, name);
                            }
                            if (symbol != null)
                            {
                                db.AddSymbol(symbol);
                            }
                        }

                        results.HadSymbols = true;

                        Log.WriteLine(Verbosity.Verbose, Feature.Loader, "Found {0} methods and {1} variables in the symbol table", db.MethodCount, db.VariableCount);
                    }
                    else
                    {
                        // No symbol table found - try to build the symbols
                        Elf32_Shdr *textShdr = FindSection(buffer, ".text");
                        Debug.Assert(textShdr != null);
                        uint  textAddress = baseAddress + textShdr->sh_addr;
                        byte *text        = memory.TranslateMainMemory(( int )textAddress);
                        uint  size        = textShdr->sh_size;
                        this.Analyze(moduleId, db, text, size, textAddress);

                        Log.WriteLine(Verbosity.Verbose, Feature.Loader, "Found {0} methods by analysis", db.MethodCount);
                    }

                    // End update, started above
                    db.EndUpdate();

                    //foreach( Method method in db.GetMethods() )
                    //    Debug.WriteLine( method.ToString() );
                }

#if DEBUG
                // Print stats
                Log.WriteLine(Verbosity.Critical, Feature.Loader, "Load complete: {0}/{1} ({2}%) NIDs good; {3} not implemented, {4} not found, {5} in missing modules = {6} total", nidGoodCount, results.Imports.Count - moduleNotFoundCount, (nidGoodCount / ( float )(results.Imports.Count - moduleNotFoundCount)) * 100.0f, nidNotImplementedCount, nidNotFoundCount, moduleNotFoundCount, results.Imports.Count);
                if (missingModules.Count > 0)
                {
                    Log.WriteLine(Verbosity.Normal, Feature.Loader, "{0} missing modules (contain {1} NIDs ({2}% of total)):", missingModules.Count, moduleNotFoundCount, (moduleNotFoundCount / ( float )results.Imports.Count) * 100.0f);
                    foreach (string moduleName in missingModules)
                    {
                        Log.WriteLine(Verbosity.Normal, Feature.Loader, "\t\t{0}", moduleName);
                    }
                }
                if ((functionExportCount > 0) || (variableExportCount > 0))
                {
                    Log.WriteLine(Verbosity.Critical, Feature.Loader, "Exported {0} functions and {1} variables", functionExportCount, variableExportCount);
                }
#endif

                results.Successful = true;

                if (results.Successful == true)
                {
                    // If we export things, go back and find previously loaded modules that may need fixing up
                    if ((functionExportCount > 0) || (variableExportCount > 0))
                    {
                        bool fixupResult = this.FixupDelayedImports(kernel, results);
                        Debug.Assert(fixupResult == true);
                    }

                    // If we are the boot load - we do some special stuff like run start, etc
                    // If we are a PRX, all that is taken care of for us by the user code making calls
                    if (type == ModuleType.Boot)
                    {
                        KModule module = new KModule(kernel, new BiosModule(results.Name, results.Exports.ToArray()));
                        module.UID            = moduleId;
                        module.LoadParameters = parameters;
                        module.LoadResults    = results;
                        kernel.UserModules.Add(module);
                        Debug.Assert(kernel.MainModule == null);
                        kernel.MainModule = module;
                        kernel.AddHandle(module);

                        // Allocate room for args
                        KMemoryBlock argsBlock = kernel.Partitions[2].Allocate(string.Format("Module {0} args", results.Name), KAllocType.High, 0, 0xFF);                               // 256b of args - enough?

                        // Set arguments - we put these right below user space, and right above the stack
                        uint   args = 0;
                        uint   argp = argsBlock.Address;
                        string arg0 = parameters.Path.AbsolutePath.Replace('\\', '/') + "/";
                        args += ( uint )arg0.Length + 1;
                        kernel.WriteString(argp, arg0);
                        uint envp = argp;                        // + args;
                        // write envp??

                        // What we do here simulates what the modulemgr does - it creates a user mode thread that
                        // runs module_start (_start, etc) and then exits when complete.
                        // NOTE: If we are a PRX, the entry address will correspond to module_start, so we don't need to do anything!

                        // Create a thread
                        KThread thread = new KThread(kernel,
                                                     module,
                                                     kernel.Partitions[2],
                                                     "kmodule_thread",
                                                     results.EntryAddress,
                                                     0,
                                                     KThreadAttributes.User,
                                                     0x4000);
                        thread.GlobalPointer = results.GlobalPointer;
                        kernel.AddHandle(thread);
                        thread.Start(args, argp);

                        // Setup handler so that we get the callback when the thread ends and we can kill it
                        cpu.SetContextSafetyCallback(thread.ContextID, new ContextSafetyDelegate(this.KmoduleThreadEnd), ( int )thread.UID);

                        Log.WriteLine(Verbosity.Verbose, Feature.Loader, "starting kmodule loading thread with UID {0:X}", thread.UID);

                        // Schedule so that our thread runs
                        kernel.Schedule();

                        // If debugging, set start breakpoint
                        if (Diag.IsAttached == true)
                        {
                            Diag.Instance.Client.OnBootModuleLoaded(results.EntryAddress);
                        }

                        if (Diag.IsAttached == true)
                        {
                            Diag.Instance.Client.OnModuleLoaded();
                        }

                        //kernel.MemorySystem.DumpMainMemory( "startup.bin" );
                    }
                }
            }
            finally
            {
                if (pbuffer != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pbuffer);
                }
            }

            return(results);
        }