1
0
mirror of https://github.com/adtools/clib2.git synced 2025-12-08 14:59:05 +00:00
Files
amiga-clib2/library/stdlib_main.c
Olaf Barthel 3f19d52557 - Fixed the stack swapping function which, for reasons unknown, ceased to work.
git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/clib2/trunk@14746 87f5fb63-7c3d-0410-a384-fd976d0f7a62
2004-10-02 15:56:13 +00:00

606 lines
15 KiB
C

/*
* $Id: stdlib_main.c,v 1.5 2004-10-02 15:56:13 obarthel Exp $
*
* :ts=4
*
* Portable ISO 'C' (1994) runtime library for the Amiga computer
* Copyright (c) 2002-2004 by Olaf Barthel <olsen@sourcery.han.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of Olaf Barthel nor the names of contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*#define DEBUG*/
#include "stdlib_headers.h"
#include "stdio_headers.h"
#include "time_headers.h"
/****************************************************************************/
#include <exec/execbase.h>
/****************************************************************************/
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
/****************************************************************************/
#include "macros.h"
/****************************************************************************/
typedef int (*init_func_ptr)(void);
typedef void (*exit_func_ptr)(void);
/****************************************************************************/
extern int main(int arg_c,char ** arg_v);
/****************************************************************************/
/* The SAS/C profiling hooks can be used to track call chains. Neat
trick, but not always necessary. Don't enable this unless you know
what you're doing... */
#if defined(__USE_SAS_PROFILING_FOR_MONITORING)
/****************************************************************************/
static BOOL show_profile_names = FALSE;
static int nest_level;
void ASM
_PROLOG(REG(a0,char * id))
{
nest_level++;
if(id != NULL && __program_name != NULL)
{
int i;
kprintf("[%s]",__program_name);
for(i = 0 ; i < nest_level ; i++)
kputc(' ');
kprintf("%s\n",id);
}
}
/****************************************************************************/
void ASM
_EPILOG(REG(a0,char * id))
{
if(nest_level > 0)
nest_level--;
}
/****************************************************************************/
#endif /* __USE_SAS_PROFILING_FOR_MONITORING */
/****************************************************************************/
static int
call_main(void)
{
/* Initialization functions; must be called exactly in this
* order because there are dependencies between the
* individual functions.
*/
static init_func_ptr init_functions[] =
{
__stdlib_init,
__stk_init,
__startup_init,
__stdio_init,
__machine_test,
__math_init,
__socket_init,
__wildcard_expand_init,
NULL
};
/* Finalization functions; these may be called
* essentially in any order. But this one makes the
* most sense (roll-back of the corresponding
* initialization functions).
*/
static exit_func_ptr exit_functions[] =
{
__stdlib_exit,
NULL
};
static size_t i;
ENTER();
/* This plants the return buffer for _exit(). */
if(setjmp(__exit_jmp_buf) != 0)
goto out;
SHOWMSG("calling init functions");
for(i = 0 ; init_functions[i] != NULL ; i++)
{
D(("calling init function #%ld",i));
if((*init_functions[i])() != OK)
{
SHOWMSG("that didn't work");
goto out;
}
}
SHOWMSG("now invoking the constructors");
/* Go through the constructor list */
_init();
SHOWMSG("done.");
/* If the SAS/C profiling code is set up for printing function
call chains, switch it on now. */
#if defined(__USE_SAS_PROFILING_FOR_MONITORING)
{
show_profile_names = TRUE;
}
#endif /* __USE_SAS_PROFILING_FOR_MONITORING */
/* After all these preparations, get this show on the road... */
exit(main((int)__argc,(char **)__argv));
out:
/* Switch off function name printing, if it was enabled. */
#if defined(__USE_SAS_PROFILING_FOR_MONITORING)
{
show_profile_names = FALSE;
}
#endif /* __USE_SAS_PROFILING_FOR_MONITORING */
/* If we end up here with the __stack_overflow variable
* set then the stack overflow handler dropped into
* longjmp() and _exit() did not get called. This
* means that we will have to show the error message
* and invoke _exit() all on our own.
*/
if(__stack_overflow)
{
SHOWMSG("we have a stack overflow");
/* Dump whatever is waiting to be written to the
* standard I/O streams, and make sure that no
* break signal is about to make things any more
* complicated than they already are.
*/
__check_abort_enabled = FALSE;
if(stdout != NULL)
fflush(stdout);
if(stderr != NULL)
fflush(stderr);
__show_error("Stack overflow detected");
if(setjmp(__exit_jmp_buf) == 0)
_exit(RETURN_FAIL);
}
/* If necessary, print stack size usage information. */
__stack_usage_exit();
SHOWMSG("invoking the destructors");
/* Go through the destructor list */
_fini();
SHOWMSG("done.");
SHOWMSG("calling the exit functions");
/* Any of the following cleanup routines may call
* _exit() by way of abort() or through a hook
* function. Which is why we redirect the exit
* return procedure.
*/
for(i = 0 ; exit_functions[i] != NULL ; i++)
{
D(("calling exit function #%ld",i));
if(setjmp(__exit_jmp_buf) == 0)
(*exit_functions[i])();
}
RETURN(__exit_value);
return(__exit_value);
}
/****************************************************************************/
static void ASM
detach_cleanup(REG(d0, LONG UNUSED unused_return_code),REG(d1, BPTR segment_list))
{
#if defined(__amigaos4__)
{
if(__IUtility != NULL)
DropInterface((struct Interface *)__IUtility);
}
#endif /* __amigaos4__ */
if(__UtilityBase != NULL)
CloseLibrary(__UtilityBase);
/* The following trick is necessary only under dos.library V40 and below. */
if(((struct Library *)DOSBase)->lib_Version < 50)
{
/* Now for the slightly shady part. We need to unload the segment
* list this program was originally loaded with. We have to close
* dos.library, though, which means that either we can close the
* library or unload the code, but not both. But there's a loophole
* in that we can enter Forbid(), unload the code, close the library
* and exit and nobody will be able to allocate this program's
* memory until after the process has been terminated.
*/
Forbid();
UnLoadSeg(segment_list);
}
#if defined(__amigaos4__)
{
if(IDOS != NULL)
DropInterface((struct Interface *)IDOS);
}
#endif /* __amigaos4__ */
if(DOSBase != NULL)
CloseLibrary(DOSBase);
}
/****************************************************************************/
static ULONG
get_stack_size(struct Task * tc)
{
ULONG upper,lower;
ULONG result;
/* How much stack size was provided? */
upper = (ULONG)tc->tc_SPUpper;
lower = (ULONG)tc->tc_SPLower;
result = upper - lower;
return(result);
}
/****************************************************************************/
int
_main(void)
{
struct Process * child_process = NULL;
struct WBStartup * startup_message;
struct Process * this_process;
int return_code = RETURN_FAIL;
ULONG current_stack_size;
APTR old_window_pointer;
SysBase = *(struct Library **)4;
#if defined(__amigaos4__)
{
/* Get exec interface */
IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)->MainInterface;
}
#endif /* __amigaos4__ */
/* Pick up the Workbench startup message, if available. */
this_process = (struct Process *)FindTask(NULL);
if(this_process->pr_CLI == ZERO)
{
struct MsgPort * mp = &this_process->pr_MsgPort;
WaitPort(mp);
startup_message = (struct WBStartup *)GetMsg(mp);
}
else
{
startup_message = NULL;
}
old_window_pointer = this_process->pr_WindowPtr;
if(__disable_dos_requesters)
this_process->pr_WindowPtr = (APTR)-1;
__WBenchMsg = startup_message;
/* We will need dos.library V37 and utility.library V37. */
DOSBase = (struct Library *)OpenLibrary("dos.library",37);
__UtilityBase = OpenLibrary("utility.library",37);
if(DOSBase == NULL || __UtilityBase == NULL)
{
__show_error("This program requires AmigaOS 2.04 or higher.");
goto out;
}
#if defined(__amigaos4__)
{
/* Get interfaces for DOS and Utility */
IDOS = (struct DOSIFace *)GetInterface(DOSBase, "main", 1, 0);
__IUtility = (struct UtilityIFace *)GetInterface(__UtilityBase, "main", 1, 0);
if (IDOS == NULL || __IUtility == NULL)
{
__show_error("This program requires AmigaOS 4.0 or higher.");
goto out;
}
}
#endif /* __amigaos4__ */
/* If a callback was provided which can fill us in on which
* minimum stack size should be used, invoke it now and
* store its result in the global __stack_size variable.
*/
if(__get_default_stack_size != NULL)
{
unsigned int size;
size = (*__get_default_stack_size)();
if(size > 0)
__stack_size = size;
}
/* How much stack size was provided? */
current_stack_size = get_stack_size(&this_process->pr_Task);
/* If this is a resident program, don't allow for the detach
* code to run. Same goes for launching the program from
* Workbench.
*/
if(__is_resident || startup_message != NULL)
{
__detach = FALSE;
}
else if (__check_detach != NULL)
{
/* Check if we may need to detach from the shell. */
__detach = (*__check_detach)();
}
/* The following code will be executed if the program is to keep
* running in the shell or was launched from Workbench.
*/
if(DO_NOT __detach)
{
/* Was a minimum stack size requested and do we
* need more stack space than was provided for?
*/
if(__stack_size > 0 && current_stack_size < (ULONG)__stack_size)
{
struct StackSwapStruct * stk;
unsigned int stack_size;
APTR new_stack;
/* Make the stack size a multiple of 32 bytes. */
stack_size = 32 + ((__stack_size + 31UL) & ~31UL);
/* Allocate the stack swapping data structure
and the stack space separately. */
stk = AllocVec(sizeof(*stk),MEMF_PUBLIC|MEMF_ANY);
if(stk == NULL)
goto out;
new_stack = AllocMem(stack_size,MEMF_PUBLIC|MEMF_ANY);
if(new_stack == NULL)
{
FreeVec(stk);
goto out;
}
/* Fill in the lower and upper bounds, then take care of
the stack pointer itself. */
stk->stk_Lower = new_stack;
stk->stk_Upper = (ULONG)(new_stack) + stack_size;
stk->stk_Pointer = (APTR)(stk->stk_Upper - 32);
/* If necessary, set up for stack size usage measurement. */
__stack_usage_init(stk);
return_code = __swap_stack_and_call(stk,(APTR)call_main);
FreeMem(new_stack, stack_size);
FreeVec(stk);
}
else
{
/* We have enough room to make the call or just don't care. */
return_code = call_main();
}
}
else
{
struct CommandLineInterface * cli = Cli();
struct TagItem tags[11];
UBYTE program_name[256];
unsigned int stack_size;
int i;
/* Now for the interesting part: detach from the shell we're
* currently executing in. This works only if the program is
* not reentrant and has not been launched from Workbench.
*/
stack_size = __stack_size;
if(stack_size < current_stack_size)
stack_size = current_stack_size;
if(stack_size < cli->cli_DefaultStack * sizeof(LONG))
stack_size = cli->cli_DefaultStack * sizeof(LONG);
GetProgramName(program_name,sizeof(program_name));
i = 0;
tags[i]. ti_Tag = NP_Entry;
tags[i++]. ti_Data = (ULONG)call_main;
tags[i]. ti_Tag = NP_StackSize;
tags[i++]. ti_Data = stack_size;
tags[i]. ti_Tag = NP_Name;
tags[i++]. ti_Data = (ULONG)program_name;
tags[i]. ti_Tag = NP_CommandName;
tags[i++]. ti_Data = (ULONG)program_name;
tags[i]. ti_Tag = NP_Cli;
tags[i++]. ti_Data = TRUE;
tags[i]. ti_Tag = NP_Arguments;
tags[i++]. ti_Data = (ULONG)GetArgStr();
tags[i]. ti_Tag = NP_ExitCode;
tags[i++]. ti_Data = (ULONG)detach_cleanup;
tags[i]. ti_Tag = NP_ExitData;
tags[i++]. ti_Data = (ULONG)cli->cli_Module;
/* dos.library V50 will free the segment list upon exit. */
if(((struct Library *)DOSBase)->lib_Version >= 50)
{
tags[i]. ti_Tag = NP_Seglist;
tags[i++]. ti_Data = (ULONG)cli->cli_Module;;
tags[i]. ti_Tag = NP_FreeSeglist;
tags[i++]. ti_Data = TRUE;
}
tags[i].ti_Tag = TAG_END;
Forbid();
child_process = CreateNewProc(tags);
if(child_process == NULL)
{
Permit();
PrintFault(IoErr(),program_name);
return_code = RETURN_FAIL;
goto out;
}
/* The standard I/O streams are no longer attached to a console. */
__no_standard_io = TRUE;
cli->cli_Module = ZERO;
return_code = RETURN_OK;
Permit();
}
out:
this_process->pr_WindowPtr = old_window_pointer;
if(child_process == NULL)
{
#if defined(__amigaos4__)
{
if(__IUtility != NULL)
{
DropInterface((struct Interface *)__IUtility);
__IUtility = NULL;
}
}
#endif /* __amigaos4__ */
if(__UtilityBase != NULL)
{
CloseLibrary(__UtilityBase);
__UtilityBase = NULL;
}
#if defined(__amigaos4__)
{
if(IDOS != NULL)
{
DropInterface((struct Interface *)IDOS);
IDOS = NULL;
}
}
#endif /* __amigaos4__ */
if(DOSBase != NULL)
{
CloseLibrary(DOSBase);
DOSBase = NULL;
}
}
if(startup_message != NULL)
{
Forbid();
ReplyMsg((struct Message *)startup_message);
}
return(return_code);
}
/****************************************************************************/
/* The following is automatically called by the main() function through code
* inserted by GCC. In theory, this could be removed by updating the machine
* definition, but for now we'll just keep this stub around. It is intended
* to call the constructor functions, but we do this in our own _main()
* anyway.
*/
#if defined(__GNUC__)
/****************************************************************************/
void __main(void);
/****************************************************************************/
void
__main(void)
{
}
/****************************************************************************/
#endif /* __GNUC__ */