Code execution changes
Introduction
With the introduction of a 32bit RISC OS environment it is necessary to safely and reliably differentiate between code developed for 26bit and 32bit environments. This ensures that only code which can be safely executed within that environment is ever run. Attempting to use 26bit instructions can have unexpected consequences and must be avoided.
Changes were made to RISC OS with the introduction of the StrongARM to ensure that future problems within absolute files could be addressed more easily. Whilst the most obvious of these changes was to cater for the split processor and data cache, other changes were defined which would in the future make it possible to safely identify changes.
Relocatable modules have been extended to include a flags word which indicates the safety of the code within a 32bit environment.
However, no changes were ever made to other areas of execution which are fixed by their design. In particular podule loader code, which was usually placed within the static memory on the podule, would not be 32bit safe. Changes have been introduced to ensure that such code can be executed safely whichever environment is in use.
Similarly, Utility code has not used any defined format and so it has never been possible to identify the execution format of such code. Utility code should now have a header to indicate how the code is to be executed.
Podule loader
Podule loader code should be written to be 32bit/26bit safe. If it has been written as safe then the header is extended to mark this safety. Because of the shortage of space on some podules, the marker is a single word. The new podule loader format is thus :
+0 Branch to 'read byte' code
+4 Branch to 'write byte' code
+8 Branch to 'card reset' code
+12 Branch to 'special podule loader' code
+16 magic value '32OK' (&4B4F3233)
The magic value indicates that the podule loader code can be correctly executed within a 32bit environment. Podules whose loaders do not have this word set will not be usable on a 32bit operating system.
Relocatable modules
Relocatable modules should be written to be 32bit/26bit safe where possible. The module header has been extended to include a flags word and extension area within the module. This is required on a 32bit OS, and desirable on a 26bit OS. Where not present, the value of the flags word will be considered to be 0. This flags word and extension data is given by an offset within the module's header. The new module header is thus :
+0 Offset to start code
+4 Offset to initialisation code
+8 Offset to finalisation code
+12 Offset to service code
+16 Offset to title string
+20 Offset to help string
+24 Offset to help and command table
+28 SWI chunk base
+32 Offset to SWI handler code
+36 Offset to SWI decoding table
+40 Offset to SWI decoding code
+44 Offset to MessageTrans file
+48 Offset to flags word and extension block
As a result, the offsets which were previously optional are now required. The operating system will check that all the offsets are valid and that the header is correctly formed for the system on which it is running before the module will be executed. These changes extend the already quite strong tests which the Kernel places on relocatable modules.
The format of the flags word and extension block is thus :
+0 Flags word
bit 0 = Module is 32bit safe
bit 1 = Module is initialised early during ROM startup
bits 2-31 = Reserved, must be 0 +4... reserved for future expansion
Bit 1 is for internal use. It should not be set on any module outside of the ROM and will be ignored if it is.
Modules which are not 32bit safe will be flagged as 'unsuitable' for the Operating system and will not be run. Such modules will not be loaded by the Kernel at runtime. Modules within podules or ROM which are not marked as being safe on a 32bit operating system will be marked with a special return code from OS_Module 19 and 20 (Enumerate ROM modules). The return codes from these calls are now :
+0 Flags word
bit 0 : Module is 32bit safe
bit 1 : Module is initialised early during ROM startup
bit 2-31 : Reserved, must be 0
+4... reserved for future expansion
It is expected that under a very limited set of circumstances writing modules to be safe in both 32bit and 26bit environments will be infeasible. Under such circumstances it is recommended that a 'loader' module be used to invoke which ever version is suitable for the system being run. However, code which is neutral is much preferred to this solution.
Absolutes
The 'Unknown compression' service call which was introduced with RISC OS 3.7 has been extended to allow extension modules to provide support for incompatible executable formats. In addition, the implementation of the execution of absolute (as well as utility and untyped files) has been moved out of FileSwitch and into new modules. Since RISC OS 3.7, absolute files without AIF headers have been deprecated as it is impossible to determine the execution format of an executable without such a header. The requirement to interwork between 26bit and 32bit environments safely means that unheadered absolute files are no longer supported by RISC OS.
The new AIF module handles the execution of absolute files.
The interface between FileSwitch and the AIF module is not private and may be extended by other clients to provide special features. However it should not be extended lightly as service calls from within the AIF module allow most extensions to be added without duplicating other functionality.
OS_FSControl 60 (Run absolute file)
On entry
R0 = 60
R1 = pointer to filename to run, or which is being run
R2 = pointer to arguments, or 0 if arguments follow the filename
R3 = length of file, or 0 if the file should be loaded
On exit
May return an error if the file cannot be executed
May generate an error (to the environment handler) once code has been loaded.
Will never return if code is executed
Re-entrancy: This call is re-entrant and may never return.
This entry point is used to execute an absolute file. It is provided by the AIF module. If the length in R3 is 0, the file will be checked for suitability, loaded and executed. If the length in R3 is non-0, the code has already been loaded at &8000 and OS_FSControl 2 issued by the caller, and the AIF module will only check the application for suitability and execute it. This latter, execution only form is used internally by the AIF module and may be used by other clients providing caching or monitoring facilities. Absolute files are always loaded at &8000. It is expected that the relocation code, if present, will move the code to a new location as necessary.
The AIF module performs a number of checks upon the file header (either before loading the whole file, or when executed directly) :
- The exit instruction must be a SWI OS_Exit.
- The four entry instructions (or three in the case of non-executable AIF) must be BL, xxxNV or NOP instructions.
- Once the file is decompressed (or initially if it is uncompressed), its size will must match the values given by the header.
- The debug type and the size of the debug area must match.
- The code and data bases must be word aligned.
- The relocation address must be word aligned.
- Once the file is decompressed (or initially if it is uncompressed), the destinations of the initial four instructions (or three instructions and one offset) must lie within the executable.
- The header must not declare that it is 26bit if it is executing on a 32bit operating system.
Note: Although non-executable AIF is examined and validated, it will be rejected at present as unsupported. Non-executable AIF may be supported in future if it is found to be useful.
Once the header's validity has been checked, the execution environment is examined. The memory limit (environment handler 0) must be high enough to support :
- the loading of the file
- the decompressed size declared within the header
- the decompressed size of the data region at the data base
- the decompressed size of the code region at the code base
- any relocation workspace which is declared
Note: It is not recommended that separate data and code bases, or relocating code be used except by very experienced developers.
Whilst the header sizes must match the source file, the AIF module will not load more of the source file than is necessary. If the file contains debug data but no debugging client is present, the data will not be loaded. It will also be excluded from the memory limit checks.
It is a requirement that the decompression code for absolutes be performed by support modules. The enforcement of this requirement means that files compressed by non-standard compression tools, for which a decompression module has not been provided, will no longer be runnable. This is an unavoidable consequence of the patching system.
The Unknown compression service calls for decompression (0) and patching (1) have been tightened to ensure that the processing of the file has been completed safely and to ensure that subsequent clients are informed of correct details. After the decompression service, the absolute file must have been decompressed. If it is still marked as compressed, an error will be raised. After both decompression and patching services, the size and execution address values returned (R3 and R4 respectively) must be correct and word aligned.
The following services have been added :
Service_UKCompression 2 (Non-AIF file execution)
On entry
R0 = 2 (reason code)
R1 = &B7 (service call)
R2 = &8000 (fixed load address)
R3 = size of executable if code has been loaded
0 if just determining whether a client is capable of handling such files
R4 = execution address
R5 = pointer to filename being executed
R6 = pointer to first 128 bytes of file
R7 = pointer to command line arguments
On exit
R1 = 0 if handled
R3 = new size of executable
R4 = new execution address
This service is issued when the AIF module identifies a file which has no AIF header. If the call is unclaimed then an error will be raised without loading the entire file. Clients which wish to handle Non-AIF executables should claim the service when R3 contains 0. They will subsequently be called with the correct size of the executable after the file has been loaded into the application space. Clients may claim the call and update the execution address to point to a suitable entry point. The SVC stack will be cleared and execution will begin at the execution address returned.
The filename and arguments supplied in R5 and R7 will have been separated if necessary from those present in the OS_FSControl call by the code which handles that vector entry point (the AIF module usually).
Service_UKCompression 3 (26bit on 32bit execution)
On entry
R0 = 3 (reason code)
R1 = &B7 (service call)
R2 = &8000 (fixed load address)
R3 = size of executable if code has been loaded
0 if just determining whether a client is capable of handling such files
R4 = execution address
R5 = pointer to filename being executed
R6 = pointer to first 128 bytes of file
R7 = pointer to command line arguments
On exit
R1 = 0 if handled
R3 = new size of executable
R4 = new execution address
This service is issued when the AIF module identifies a file which has indicated that it is 26bit, but the operating system is 32bit.
Clients which wish to handle execution of 26bit binaries on a 32bit-only system should claim the service when R3 contains 0. They will subsequently be called with the correct size of the executable after the file has been loaded into the application space. Clients may claim the call and update the execution address to point to a suitable entry point. The SVC stack will be cleared and execution will begin at the execution address returned.
No other services will be issued (eg Decompression, Patching, etc). Clients wishing to provide support for such operations should call the necessary services themselves.
The filename and arguments supplied in R5 and R7 will have been separated if necessary from those present in the OS_FSControl call by the code which handles that vector entry point (the AIF module usually).
Service_UKCompression 4 (Debugger state)
On entry
R0 = 4 (reason code)
R1 = &B7 (service call)
R2 = &8000 (fixed load address)
R3 = size of executable if code has been loaded
0 if just determining whether a client is capable of handling such files
R4 = execution address
R5 = pointer to filename being executed
On exit
R1 = 0 if handled
This service is issued to determine whether a debugging environment exists, and to invoke it where necessary.
Clients which wish to handle debugging of executables should claim this service call when issued with the size in R3 is 0. Claiming of this service call when the size in R3 is 0 indicates that the debug data within the executable is required and should be loaded.
Subsequently, a further call will be issued, after the 'Patch' (reason code 1) service, with the size in R3 non-0. This allows the debugger to insert any additional debugging instructions it requires.
The order of operations within the AIF execution on OS_FSControl 60 with R3=0 is thus :
- Load file header (128 bytes) If the file is shorter than this, an error will be reported.
- Check file header validity
- Non-AIF files are checked with Service_UKCompression 2. These will be loaded and executed at the execution address if claimed. Memory limit checks will be performed only on the size of the file.
- 26bit-on-32bit files are checked with Service_UKCompression 3. These will be loaded and executed at the execution address if claimed. Memory limit checks will be performed only on the size of the file.
- Check whether the memory limit allows the file to be loaded into memory.
- Non-executable files are rejected.
- Application starting is issued with OS_FSControl 2. From this point on, errors can only be generated with OS_GenerateError, rather than being returned to the caller.
- The file is loaded into memory
- Debugger presence is checked by issuing Service_UKCompression 4 and if claimed the debug data will be loaded.
- If the call is not claimed, and the module DDT is present, the debug data will be loaded. This is for Compatibility with extant debugger.
- The execution is re-invoked using OS_FSControl 60 with R3 set to the size of the executable.
The order of operations within the AIF execution on OS_FSControl 60 with R3<>0 continues from the above, but may have been invoked directly if executable caching is in place :
- Check file header validity (as above)
- Check whether the memory limit allows the file to be in memory. Whilst this may appear redundant as the file has already been loaded, it ensures that the extent checks within the header have been correctly interpreted.
- Compressed code is decompressed with Service_UKCompression 0.
- Check that the file has been decompressed.
- Check header file validity only (no non-AIF or 26bit-on-32bit support is provided).
- Check whether the memory limit allows the new size and header bases in case the header has been modified.
- Patchers are given an opportunity to modify the code with Service_UKCompression 1.
- Check header file validity only (no non-AIF or 26bit-on-32bit support is provided).
- Check whether the memory limit allows the new size and header bases in case the header has been modified.
- Debuggers are given an opportunity to place any hooks they require and to copy the debugging data with Service_UKCompression 4.
- Caches are synchronised with OS_SynchroniseCodeAreas.
- The SVC stack is flattened and marked as such with OS_TaskControl 0.
- USR mode is entered.
- The execution address is branched to.
Note that some of the initial checks are duplicates of those performed already by the OS_FSControl 60 with R3=0. This duplication is necessary as the entry may have been made without OS_FSControl 60 with R3=0 being called.
The ordering of the process is intentionally very defensive. Whilst it may seem that the process is more complex than is necessary, it should allow all correctly written applications to function unmodified.
- Applications which fail any of the tests will return errors, rather than fatal aborts or worse.
- Applications which have to be explicitly supported by legacy emulation systems are provided with the entry points necessary to interwork with the system in a reliable manner.
Untyped files
Untyped files (those with a load and an execution address) are no longer supported by RISC OS. Since such files cannot be marked as being 32bit safe, they will not be supported by the built-in modules. However it is possible for external clients to provide support for these files.
OS_FSControl 61 (Run untyped file)
On entry
R0 = 61
R1 = pointer to filename to run, or which is being run
R2 = pointer to arguments, or 0 if arguments follow the filename
R3 = length of file, or 0 if the file should be loaded
R4 = load address
R5 = execution address
On exit
May return an error if the file cannot be executed
May generate an error (to the environment handler) once code has been loaded.
Will never return if code is executed
Re-entrancy: This call is re-entrant and may never return.
This entry point is called by FileSwitch to execute a file which has a load and execution address. It is not claimed by any module within RISC OS and will therefore return an error. Clients wishing to support such files should claim the vector and handle the execution directly.
Utilities
Transient utilities have not previously been required to provide any header or indication of their format. With versions of FileSwitch after 2.67 it will be necessary to include a header on all transient utilities which are expected to run on 32bit systems. Utility files run on 26bit systems will not be required to carry this header, although they are strongly encouraged to do so as they will not execute on 32bit systems without one. The header format is simpler than that of an AIF header, and includes a pair of 'magic' values which should ensure that non-conformant utility files are not recognised as having this header :
+0 Entry instruction (B entry_point)
+4 Magic value 1 (DCD &79766748)
+8 Magic value 2 (DCD &216C6776)
+12 Read only size (DCD size_of_read_only_data)
+16 Read/write size (DCD size_of_read_write_data)
+20 26 or 32 depending on whether it is built for 26bit or 32bit
(DCD 26_or_32)
As a special case, the read only and read/write area sizes may be set to 0 to indicate that the extent of the file is the size for the regions. When omitted in this way, no size checks will be performed. It is strongly recommended that all utilities include the correct values in order to ensure that truncated, or over-long files can be detected. If supplied, both the read only and read/write size values must be word aligned.
Because transient utilities are relocatable by design, a suitable header can be added by automated tools. If such automation is performed, it should be assumed that the entire utility being processed is read/write. At the present time the TransientUtility module (which handles the execution of these files) does not enforce the read only and read/write regions.
FileSwitch issues the following OS_FSControl call to handle the execution of transient utilities.
OS_FSControl 62 (Run transient utility)
On entry
R0 = 62
R1 = pointer to filename to run, or which is being run
R2 = pointer to arguments, or 0 if arguments follow the filename
On exit
May return an error if the file cannot be executed
Will return, claiming the vector if successful
Re-entrancy: This call is re-entrant.
This entry point is used to execute a transient utility. It is provided by the TransientUtility module.
General application space execution changes
In the past it has been the practice to flatten the SVC stack on application execution by knowing the stack top address. This is a specialised operation, and one which is not expected to be required by most clients. With RISC OS 4 a defined mechanism was provided for reading the SVC stack top. With the modern versions of RISC OS, starting with version 4.42, this operation is now forbidden for all external clients. RISC OS internal components will be changed as soon as possible to remove this operation.
Common usages of this form of operation are :
- On aborts, to ensure that the stack state is safe (internal handlers)
- On application start (OS_FSControl 4, OS_Module 2)
- Within pre-emption of system calls (TaskWindow and others)
- Within context-switching operations (WindowManager and others)
A new API has been created to perform this operation :
OS_TaskControl 0 (Read address of stacks reset code)
On entry
R0 = 0 (reason code)
On exit
R0 = pointer to code to call to reset the SVC and IRQ stack state
This SWI returns the pointer to a function which can be used to flatten the SVC and IRQ stack and clear any associated state data. The code must be called from a privileged mode. On return R13_svc and R13_irq will have been reset to the top, and R0-R3 and R14 will be corrupt.
It is expected that clients will require a sequence similar to the following :
MOV r0, #0
SWI OS_TaskControl
STR r0, ResetStacksCode
...
; r4 = address to call
MOV lr, pc
LDR pc, ResetStacksCode
MRS r0, CPSR
AND r0, r0, #2_10000
MSR CPSR_c, r0 ; switch to USR mode
MOV pc, r4
Dependant on the application, the operation to read the code address may be issued immediately before calling.
Note: In all environments where the SVC stack is to be flattened, the IRQ stack should already be empty. This call ensures that should the IRQ stack have been used incorrectly it is also flattened.
Summary
- Podules loaders must be 32bit/26bit safe and have a '32OK' magic word.
- Modules must be 32bit/26bit and have a flags word to indicate that.
- Absolute files must be correctly AIF headered.
- Absolute files must be decompressed by extension modules.
- Untyped files are no longer supported.
- Transient utilities must be 32bit/26bit safe and have a new header added.
- The OS has been extended to provide APIs for external execution clients
- The OS has been extended to provide an API for resetting the stacks
|