Commit a9f447f5 authored by ROOL's avatar ROOL 🤖
Browse files

Add runtime hooks for C++ applications

Detail:
  Hooks for constructors and destructors using weak symbols referenced by
  cpplib when starting C++ applications. The weak symbols in the stubs are
  placed after the kernel init block, and a new "magic word" marker denotes the
  block is of 5 word length rather than 3 (since the block has historically
  been followed by the literal pool which can have function offset-like
  addresses in it). The top 16b of the word is an unlikely opcode, bottom 16b
  is table size.
  armsys.c, hostsys.h, clib/cl_entries.s
  * Move the init flags from stdlib.c into an _init_flags struct.
  * Move the finalisation function from stdlib.c into armsys namespace.
  * Save a word of workspace by moving the error recursion flag their too.
  * Call the new runtime functions for C++ if present.
  stdlib.c
  * Library finalisation and flags moved to armsys.c
  clib/cl_stub.s, h_workspace.s
  * Add magic word and 2x weak symbols to block
  kernel/k_body.s, kernel/k_data.s
  * Capture image descriptor pointer from stubs for later use
  * Pass the image descriptor to the init procs for their information
  clib/cl_init.s
  * Pass the image descriptor on to _clib_initialise()
  clib/cl_body.s
  * Inspect the image descriptor, if all 5 words are valid, copy into the
    function pointers for use by armsys C code
  clib/clibdata.s, clib/cl_spare.s, clib/cl_data.s
  * Adjust data size by 2 words for new entries, give back 1 word of flags
Admin:
  Tested with a simplistic 'hello world' type application (test/cppprog.c).
  ANSILib isn't expected to be used with C++ so is unchanged.
parent 1ad41a2d
Pipeline #8074 passed with stages
in 48 seconds
......@@ -44,6 +44,8 @@ extern int _fprintf_lf(FILE *fp, const char *fmt, ...);
extern int _sprintf(char *buff, const char *fmt, ...);
extern int _sprintf_lf(char *buff, const char *fmt, ...);
extern _kernel_oserror *_kernel_peek_last_oserror(void);
extern PROC __rt_cpp_init;
extern PROC __rt_cpp_final;
/* HIDDEN EXPORTS */
void _main(char *s, int (*main)(int, char **));
......@@ -133,6 +135,15 @@ time_t time(time_t *timer)
/* system dependent I/O routines ... */
char *decimal_point = ".";
static struct _init_flags {
char alloc_finalised;
char io_finalised;
char getenv_finalised;
char error_recursion;
} _init_flags;
/* Riscos has a second distinguished FILEHANDLE value, to indicate that */
/* a file is a keyboard and/or vdu, which can't be read or written using */
/* Riscos file operations (or at any rate, couldn't when the library was */
......@@ -264,7 +275,6 @@ bool _sys__assert(const char *s, const char *expr, const char *func, const char
return _desktop_report(buffer, NULL);
}
static int _error_recursion;
int _sys_msg_1(const char *s, const char *but)
{
if (istty(stderr->__file))
......@@ -274,14 +284,14 @@ int _sys_msg_1(const char *s, const char *but)
}
/* write out s carefully for intimate system use. */
if ((stderr->__flag & _IOWRITE) && !_error_recursion)
if ((stderr->__flag & _IOWRITE) && !_init_flags.error_recursion)
{
_error_recursion = 1;
_init_flags.error_recursion = 1;
fputc('\n', stderr);
while (*s >= ' ')
fputc(*s++, stderr);
fputc('\n', stderr);
_error_recursion = 0;
_init_flags.error_recursion = 0;
}
else
{ _ttywrite((unsigned char *)"\n", 1, 0);
......@@ -656,7 +666,7 @@ int system(const char *string)
#ifdef DDE
type = CHAIN;
#else
_lib_shutdown();
_armsys_lib_shutdown();
_kernel_system(string, CHAIN);
/* which never returns */
#endif
......@@ -689,7 +699,7 @@ int system(const char *string)
_kernel_swi(DDEUtils_FlushCL, &r, &r);
}
if (type == CHAIN)
_lib_shutdown();
_armsys_lib_shutdown();
rc = _kernel_system(string, type);
if (cmd_string != NULL)
free(cmd_string);
......@@ -708,14 +718,15 @@ int system(const char *string)
#undef CHAIN
}
char *decimal_point = ".";
void _armsys_lib_init(void)
{ char *stdinfile = TTYFILENAME,
*stdoutfile = TTYFILENAME,
*stderrfile = TTYFILENAME;
_getenv_value = NULL;
_error_recursion = 0;
_init_flags.alloc_finalised = 0;
_init_flags.io_finalised = 0;
_init_flags.getenv_finalised = 0;
_init_flags.error_recursion = 0;
#ifdef DDE
if ((_kernel_osbyte(129, 0, 255) & 0xFF) >= 0xA8)
_cli_limit = 1024; /* Ursula long command lines */
......@@ -731,6 +742,35 @@ void _armsys_lib_init(void)
_raise_stacked_interrupts(); /* enable SIGINT */
if (!_kernel_client_is_module())
_initio(stdinfile, stdoutfile, stderrfile);
if (__rt_cpp_init) __rt_cpp_init(); /* C++ runtime initialisation */
}
void _armsys_lib_shutdown(void)
{ int isquick;
int ismodule = _kernel_client_is_module(); /* ie is module app, so not */
/* total shutdown */
/* despatch any user registered exits */
isquick = _exit_call_exit_fns();
/* ensure no recursion if finalisation fails */
if (__rt_cpp_final)
{ __rt_cpp_final(); /* C++ runtime finalisation */
__rt_cpp_final = NULL;
}
if (!_init_flags.getenv_finalised && !ismodule)
{ _init_flags.getenv_finalised = 1;
_terminate_getenv();
}
if (!_init_flags.alloc_finalised)
{ _init_flags.alloc_finalised = 1;
_terminate_user_alloc();
}
if (!_init_flags.io_finalised && !isquick)
{ _init_flags.io_finalised = 1;
_terminateio();
}
if (ismodule) /* Want terminateio again for module part */
_init_flags.io_finalised = 0;
}
void _main(char *s, int (*main)(int, char **))
......
......@@ -107,7 +107,6 @@ static union { vprocp p; int i; } _qxitvector[EXIT_LIMIT] = { 0 };
static struct {
char number_of_exit_functions;
char number_of_qxit_functions;
char alloc_finalised, io_finalised, getenv_finalised;
} exit_s;
void _exit_init(void)
......@@ -127,32 +126,14 @@ void _exit_init(void)
exit_s.number_of_exit_functions = 0;
exit_s.number_of_qxit_functions = 0;
}
exit_s.alloc_finalised = 0; exit_s.io_finalised = 0; exit_s.getenv_finalised = 0;
}
int atexit(vprocp func)
{
if (exit_s.number_of_exit_functions >= EXIT_LIMIT) return 1; /* failure */
_exitvector[exit_s.number_of_exit_functions++].i =
(int) func + ((_kernel_processor_mode() & 0xF) != 0);
return 0; /* success */
}
int at_quick_exit(vprocp func)
int _exit_call_exit_fns(void)
{
if (exit_s.number_of_qxit_functions >= EXIT_LIMIT) return 1; /* failure */
_qxitvector[exit_s.number_of_qxit_functions++].i =
(int) func + ((_kernel_processor_mode() & 0xF) != 0);
return 0; /* success */
}
void _lib_shutdown(void)
{
int mode = ((_kernel_processor_mode() & 0xF) != 0);
int isquick = (exit_s.number_of_qxit_functions != 0) &&
(exit_s.number_of_exit_functions == 0);
int ismodule = _kernel_client_is_module(); /* ie is module app, so not */
/* total shutdown */
int mode = ((_kernel_processor_mode() & 0xF) != 0);
while (exit_s.number_of_qxit_functions!=0) {
vprocp fn = _qxitvector[--exit_s.number_of_qxit_functions].p;
int flags = _qxitvector[exit_s.number_of_qxit_functions].i;
......@@ -169,15 +150,23 @@ void _lib_shutdown(void)
fn = (vprocp) ((unsigned) fn &~ 3);
_call_client_0(fn);
}
/* ensure no recursion if finalisation fails */
if (!exit_s.getenv_finalised && !ismodule)
{ exit_s.getenv_finalised = 1; _terminate_getenv(); }
if (!exit_s.alloc_finalised)
{ exit_s.alloc_finalised = 1; _terminate_user_alloc(); }
if (!exit_s.io_finalised && !isquick)
{ exit_s.io_finalised = 1; _terminateio(); }
if (ismodule) /* Want terminateio again for module part */
exit_s.io_finalised = 0;
return isquick; /* Report whether this is a quick exit */
}
int atexit(vprocp func)
{
if (exit_s.number_of_exit_functions >= EXIT_LIMIT) return 1; /* failure */
_exitvector[exit_s.number_of_exit_functions++].i =
(int) func + ((_kernel_processor_mode() & 0xF) != 0);
return 0; /* success */
}
int at_quick_exit(vprocp func)
{
if (exit_s.number_of_qxit_functions >= EXIT_LIMIT) return 1; /* failure */
_qxitvector[exit_s.number_of_qxit_functions++].i =
(int) func + ((_kernel_processor_mode() & 0xF) != 0);
return 0; /* success */
}
void exit(int n)
......
......@@ -22,6 +22,7 @@
;
GET h_stack.s
GET h_workspc.s
GET Hdr:Wimp
GET Hdr:TaskWindow
......@@ -76,6 +77,7 @@ $Label
IMPORT |_signal_real_handler|
IMPORT |_armsys_lib_init|
IMPORT |_armsys_lib_shutdown|
IMPORT |_sys_msg|
IMPORT |_sys_msg_1|
......@@ -216,25 +218,37 @@ $Label
GET clib/h_signal.s
|_clib_initialise|
FunctionEntry "r1,r2"
LoadStaticBase ip, a4
; a1 -> top of stack
; a2 -> start of code area
; a3 -> end of code area
; a4 -> client image descriptor
FunctionEntry
LoadStaticBase ip, lr
MOV r0, #-1 ; read max app space size
SWI XOS_ReadDynamicArea
ADDVC r2, r2, r0 ; if call works then get end of app space
MOVVS r2, #&01000000 ; if call fails then assume 16M
STR r2, [ip, #O_app_space_end]
LDR a1, [a4, #kib_MagicWord]
LDR a2, =kib_MagicType5
TEQ a1, a2 ; is it a 5 entry descriptor?
LDREQ a1, [a4, #kib_CPPInit]
LDREQ a2, [a4, #kib_CPPFinal]
STREQ a1, [ip, #O___rt_cpp_init]
STREQ a2, [ip, #O___rt_cpp_final]
MOV a4, #0
STRB a4, [ip, #O_inSignalHandler]
BL |_armsys_lib_init|
MOV a1, #0
Return "r1,r2"
Return
[ ModeMayBeNonUser
IMPORT |_lib_shutdown|
EXPORT |_clib_finalisemodule|
|_clib_finalisemodule|
FunctionEntry "a1"
BL |_lib_shutdown|
BL |_armsys_lib_shutdown|
MOV a1, #0
BL |_exit|
LDR r1, [sp], #4 ; free the RMA block addressed by
......@@ -336,8 +350,7 @@ UncaughtTrapHandler Keep
Finalise Keep
; (There'd better be a stack set up).
IMPORT |_lib_shutdown|
B |_lib_shutdown|
B |_armsys_lib_shutdown|
TrapHandler Keep
MOV a4, a1
......
......@@ -38,4 +38,6 @@ inSignalHandler VariableByte
dummybyte VariableByte
InitByte 1 ; to force the module version of the data area
; not to be zero-initialised, so it comes first
__rt_cpp_init ExportedVariable
__rt_cpp_final ExportedVariable
END
......@@ -208,7 +208,7 @@
Entry _clib_finalisemodule, , , unveneered
Entry _clib_version, imported, , unveneered
Entry _CLib_Finalise, imported, _lib_shutdown, unveneered
Entry _CLib_Finalise, imported, _armsys_lib_shutdown, unveneered
Entry tmpnam, imported, , unveneered
Entry _swi, imported, , unveneered
......
......@@ -49,10 +49,11 @@ CLanguageString
|__main|
Initialise
; argument for _armsys_lib_init:
; argument for _clib_initialise:
; top of stack,
; start of code area,
; end of code area,
; client's image descriptor
[ SharedLibrary
; Here, SharedLibrary means stub. In this case, we have to enable Wimp-Slot
; extension by _kernel_alloc, as for compatibility with old binaries it has
......@@ -62,13 +63,14 @@ Initialise
; it is on by default, so doesn't need to be poked. So we still work with
; version 6, we poke it directly, using its old address. This position
; is in the middle of registerDump on newer libraries, so it is harmless.
LDR a1, =StaticData + 0x115 ; was |_kernel_kallocExtendsWS|
LDR a3, =StaticData + 0x115 ; was |_kernel_kallocExtendsWS|
LDR a2, [sl, #SL_Client_Offset]
ADD a1, a1, a2
ADD a3, a3, a2
MOV a2, #1
STRB a2, [a1] ; enable new _kernel_alloc behaviour
STRB a2, [a3] ; enable new _kernel_alloc behaviour
]
FunctionEntry
MOV a4, a1 ; client kernel init block
ADD a1, sp, #4
LDR a2, =rtskBase
LDMIB a2, {a2, a3}
......
......@@ -25,7 +25,7 @@
EXPORT |CLib_data_end|
% 103*4
% 102*4
|CLib_data_end|
END
......@@ -69,6 +69,8 @@ SharedLibrary SETL {TRUE}
IMPORT |Image$$ZI$$Base|
IMPORT |__root_stack_size|, WEAK
IMPORT |_kernel_init_flags|, WEAK
IMPORT |__cpp_initialise|, WEAK
IMPORT |__cpp_finalise|, WEAK
[ :LNOT::DEF:AnsiLib
IMPORT |Stub$$Init$$Base|
]
......@@ -412,10 +414,13 @@ RMEnsure6
]
]
& kib_MagicType5 ; 5 entries in init block
|_k_init_block|
& |Image$$RO$$Base|
& |RTSK$$Data$$Base|
& |RTSK$$Data$$Limit|
& |__cpp_initialise|
& |__cpp_finalise|
LTORG
......
......@@ -19,11 +19,11 @@
; forced on the shared library's clients:-
;
; size-of-static-data-in-part-of-C-library-written-in-C +
; sizeof-static-data-in-cl_spare == ClibSpace == 491
; sizeof-static-data-in-cl_spare == ClibSpace
;
; Copyright (C) Acorn Computers Ltd., 1988.
;
ClibSpace Variable 490
ClibSpace Variable 488
END
......@@ -79,15 +79,15 @@ extern char *_strerror(int n, char *v);
* this is done into the array v.
*/
extern int _interrupts_off;
extern void _raise_stacked_interrupts(void);
extern void _postmortem(char *msg, int mflag);
extern void _mapstore(void);
extern void _write_profile(char *filename);
extern void _sysdie(const char *s);
extern void _init_alloc(void), _initio(char *,char *,char *),
_terminateio(void), _lib_shutdown(void), _signal_init(void),
_terminateio(void), _armsys_lib_shutdown(void), _signal_init(void),
_exit_init(void);
extern int _exit_call_exit_fns(void);
extern void _armsys_lib_init(void);
extern int _signal_real_handler(int sig);
......
......@@ -128,7 +128,7 @@ PSR32UNDMode * &0000001B
PSR32Privileged * &0000000F
; A RTS$$Data area
; An RTSK$$Data area
^ 0
lang_size # 4
lang_codeBase # 4
......@@ -369,11 +369,13 @@ uwb_size # 0
STR r5, [r1, #SC_mark]
LoadStaticBase v6, ip
STR r0, [v6, #O_imageDesc]
STR r1, [v6, #O_heapBase]
STR r1, [v6, #O_rootStackChunk]
LDMIA r0, {r0-r2}
CMP r3, #0 ; if module, imagebase (in RMA) isn't
MOVNE r0, #Application_Base ; interesting
ASSERT O_imageBase = 0
STMIA v6, {r0-r2}
; Copy the argument string (in SWI mode), so we can access it
......@@ -535,10 +537,11 @@ CallInitProcs Keep
LDR v4, [v2, #lang_size]
CMP v4, #lang_Init
BLE NoInitProc
LDR a1, [v2, #lang_Init]
CMP a1, #0
LDR ip, [v2, #lang_Init]
CMP ip, #0
BEQ NoInitProc
CallClient a1
LDR a1, [v6, #O_imageDesc] ; client's image descriptor
CallClient ip
CMP a1, #0
MOVNE v1, a1
NoInitProc
......
......@@ -76,7 +76,9 @@ eventCode Variable
eventUserR13 Variable
eventRegisters Variable 12 ; r0-r10 and r13
unused2 Variable 11
unused2 Variable 10
imageDesc Variable
heapTop Variable
heapLimit Variable
......
......@@ -26,4 +26,13 @@ ClientFlag_APCS_A * 1 :SHL: 0 ; client is using APCS-A
ClientFlag_APCS_32 * 1 :SHL: 1 ; client is using APCS-32 (26 or 32 bit mode)
ClientFlag_Sys32 * 1 :SHL: 2 ; system is running in a 32-bit mode
^ -4 ; Offsets in a kernel init block
kib_MagicWord # 4
kib_MagicType5 * &fedc0005
kib_ROBase # 4
kib_DataBase # 4
kib_DataLimit # 4
kib_CPPInit # 4
kib_CPPFinal # 4
END
/* Copyright 2022 RISC OS Open Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
void __cpp_initialise(void)
{
printf("C++ init\n");
}
void __cpp_finalise(void)
{
printf("C++ final\n");
}
int main(void)
{
/* Test that the pre-init and post-exit functions get called */
printf("This is main\n");
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment