.. role:: switch(samp) .. |GNAT_Users_Guide| replace:: :title:`GNAT User's Guide for Native Platforms` .. index:: VxWorks Topics .. _VxWorks_Topics: ============== VxWorks Topics ============== This appendix covers topics related to GNAT for various versions of the VxWorks RTOS. .. _Common_VxWorks_Topics: Common VxWorks Topics ===================== This section describes topics that are relevant to all GNAT for VxWorks configurations. Note that the GNAT tool prefix depends on the platform, as follow: * for Workbench / VxWorks7 (up to SR0540), it is ``-wrs-vxworks7-``; * for Workbench / VxWorks 7 (SR0600 or later), it is ``-wrs-vxworks7r2-``; In all cases above, ```` is the CPU being targeted (Eg: ``arm``, ``i586``, etc). For VxWorks 7 platforms, instructions not providing a specifc release version (e.g. "SR0600 or later") are applicable to all supported versions of VxWorks 7. Generally speaking, outside of the difference above in tool prefix, AdaCore tried to provide a consistent experience regardless of the VxWorks 7 version. .. _VxWorks_VSB_Requirements: VxWorks VSB Requirements ------------------------ New in SR0660 is the redefinition of `time_t` from `long` to `long long` by default, in preparation for the so-called Epochalypse of 2038 when the number of seconds from the Unix Epoch will exceed the value that can be stored in 31 bits. GNAT has been updated to use this new definition by default in the 22.x release. **IMPORTANT NOTE:** For users of SR0650 and earlier, who wish to upgrade to the 22.x release of GNAT, and who are compiling for 32-bit targets, the runtime will not be compatible with the default size of `time_t`. If this is the case, there are two options: * Upgrade to SR0660 or later. * Rebuild the GNAT runtime. The runtime rebuild option requires a modification to a runtime package, a process of rebuilding and an adjustment of the environment to use that runtime for building the user application. See /adainclude/libada.gpr for detailed instructions for rebuilding the runtime. When configuring the VSB, make sure that the selected linker is `LD` and not `LLD`, as currently, we do not support using the latter one to build Ada code. .. _VxWorks_Kernel_Requirements: VxWorks Kernel Requirements --------------------------- Tasking requires the following kernel components: * INCLUDE_TLS (included by default on VxWorks 7) * DKM_TLS_SIZE set on VxWorks 7 SRO610 or later. To know how to compute the value to set, refer to the `tlsLib documentation` in the WindRiver `Kernel API Reference: CORE Libraries` GNAT.Sockets require the following kernel components: * INCLUDE_GETNAMEINFO * INCLUDE_GETADDRINFO .. _Executing_a_Program_on_VxWorks: Executing a Program on VxWorks ------------------------------ Executing an application involves loading it onto the target, running it, and then (if re-execution is needed) unloading it. This model and the instructions below apply to Workbench / VxWorks 7 when building an application for execution in the kernel space. RTP's are treated somewhat differently. These cases are described later in this Guide. .. _Loading_and_Running_the_Program: Loading and Running the Program ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ An Ada program is loaded and run in the same way as a C program. In order to load and run a program, we assume that the target has access to the disk of the host containing the required module and that the host shell's working directory has been set to the directory containing it. The commands are typed into the Workbench host shell. The ``windsh`` prompt is the ``->`` sequence. In this example, the module is named ``hello``, and when executed it results in the display of the ``Hello World`` string. For VxWorks 7: :: -> vf0=open("/vio/0",2,0) new symbol "vf0" added to symbol table. vf0 = 0x2cab48: value = 12 = 0xc -> ioGlobalStdSet(1,vf0) value = 1 = 0x1 -> ld < hello.out value = 665408 = 0xa2740 -> hello Hello World value = 0 = 0x0 -> The first two commands redirect output to the host shell window. They are only needed if the target server was started without the :switch:`-C` option. The third command loads the module, which is the file :file:`hello` created previously by a ``gprbuild`` command. The ``Hello World`` program comprises a procedure named ``hello``, and this is the name entered for the procedure in the target server's symbol table when the module is loaded. To execute the procedure, type the symbol name ``hello`` into ``windsh`` as shown in the last command above. Depending on circumstances, ``sp`` or ``taskspawn`` should be used to spawn the module in a new task. These are generally used for finer control over execution options. Note that by default the entry point of an Ada program is the name of the main Ada subprogram in a VxWorks environment. It is possible to use an alternative name; see the description of switches for gpr package ``Binder`` for details. .. _Unloading_the_Program: Unloading the Program ^^^^^^^^^^^^^^^^^^^^^ It is important to remember that you must unload a program once you have run it. You cannot load it once and run it several times. If you don't follow this rule, your program's behavior can be unpredictable, and it will most likely crash. This effect is due to the implementation of Ada's *elaboration* semantics. The compilation unit elaboration phase comprises a *static* elaboration and a *dynamic* elaboration. On a native platform they both take place when the program is run. Thus rerunning the program will repeat the complete elaboration phase, and the program will run correctly. On VxWorks, the process is a bit different. The static elaboration phase for a DKM is handled by the dynamic loader (typically when you type ``ld < program_name`` in ``windsh``). The dynamic phase takes place when the program is run. If the program is run twice and has not been unloaded and then reloaded, the second time it is run, the static elaboration phase is skipped. Variables initialized during the static elaboration phase may have been modified during the first execution of the program. Thus the second execution isn't performed on a completely initialized environment. This is not an issue for SKMs and RTPs. Note that in C programs, elaboration isn't systematic. Multiple runs without reload might work, but, even with C programs, if there is an elaboration phase, you will have to unload your program before re-running it. .. index:: Mixed-language programming (for VxWorks) .. _mixed_language_programming_gnatpro_for_vxworks: Mixed-Language Programming on VxWorks using GNAT Exclusively ------------------------------------------------------------ On VxWorks, the general requirements and recommendations provided in the section :ref:`mixed_language_programming_gnatpro` also apply here, and should therefore be observed. The GPRbuild knowledge base also contains the appropriate linker options for producing a module for the configuration that you select. In order to take advantage of that, for VxWorks 7 ``VSB_DIR`` must be defined. .. index:: Mixed-language programming (for VxWorks) .. _mixed_language_programming_vxworks: Mixed-Language Programming using the VxWorks C/C++ Compilers ------------------------------------------------------------ Use of the VxWorks C/C++ compilers for mixed Ada and C/C++ applications is supported, but only when built through GNATbench or when using the VxWorks 7 wrtool with an Ada Project generated by GNATbench. `GNATbench` is the IDE supported in the context of VxWorks. GNATbench and GNAT shall be of the same version. GNATbench X is not guaranteed to work with GNAT Y if X != Y. The use of GNATbench for mixed language applications is documented in the GNATbench help files available within Workbench. Please also review the :ref:`Interoperability_with_VxWorks_Compilers` section for issues to be aware of when using both the GNAT Pro Ada compiler and the VxWorks C/C++ compilers. .. _Interoperability_with_VxWorks_Compilers: Interoperability with VxWorks C/C++ Compilers --------------------------------------------- VxWorks is packaged with one or more C/C++ compilers depending on the VxWorks version and target. GNAT Pro supports Ada-only and mixed-language applications using the VxWorks GNU, DIAB, and LLVM toolchains subject to the limitations described in this section. The ICC toolchain is not supported. The details of mixing GNAT Pro Ada with the VxWorks toolchains is generally taken care by GNATbench for pure Ada applications and mixed Ada and C/C++ applications. Similarly for Ada-only applications built outside of Workbench using GPRbuild. An exception to this is exception handling, where special care must be taken, and which is documented in a subsequent section (:ref:`vxworks_exception_handling`). .. index:: Known incompatibilities between LLVM and GNAT (for VxWorks) .. _known_incompatibilities_llvm_gnat_vxworks: Known Incompatibilities on VxWorks between LLVM and GNAT -------------------------------------------------------- Starting with VxWorks version 22.06, all VxWorks C/C++ Compilers are LLVM based (formerly PowerPC and PowerPC64 were GNU based). In 22.06, an ABI incompatibility exists on PowerPC with respect to the passing and return of Complex arguments, so any attempt to use Complex library functions from GNAT (some uses are part of the runtime library and can't be avoided) will result in unpredictable program behavior. The workaround is to not use Complex types. Reportedly this will be fixed in version 23.03. The WRS defect is TCLLVM-501. .. _vxworks_exception_handling: Exception Handling ------------------ Exception handling for Ada-only applications works as expected on VxWorks without any special configuration for either the application or the kernel. However, this is not true for mixed Ada/C++ VxWorks applications where two different compilers are involved: the GNAT Pro Ada compiler and the VxWorks C++ compiler. Making two different compilers cooperate is always a challenge, but exception handling is particularly sensitive as both compilers provide their own exception runtime support. This section introduces the integration options available for Ada and Ada/C++ applications. It is followed by an advanced look at exception handling on VxWorks and the issues a mixed Ada/C++ application can encounter. .. _vxworks_exception_integration: Exception Integration Schemes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Three different integration schemes are provided by GNATbench: * EXCEPTIONS_GPRBUILD_ONLY: exception propagation only for pure Ada applications. * EXCEPTIONS_SEPARATE_DOMAINS: exception propagation is supported for mixed language applications but exceptions cannot propagate through the different languages. C++ exceptions only go through C++ frames, and if they hit an Ada frame then the application stops with an error. Same behavior for Ada exceptions. * EXCEPTIONS_FULL: exceptions raised in one language can be handled in either language: C++ exceptions can be handled in Ada, Ada exceptions can be caught in C++. The availability of the three integration options is dependent on the VxWorks C++ compiler: .. _vxworks_diab_integration: DIAB """" Exception handling is not supported when mixing GNAT Pro Ada and Diab C++. Due to this limitation GNATbench only supports EXCEPTIONS_GPRBUILD_ONLY. Mixed-language applications are still possible with the No_Exception_Propagation restriction. Integration with DIAB is not supported on arm platform. .. _vxworks_gnu_integration: GNU """ GNAT and Wind River G++ are both GNU-based and consequently support: * EXCEPTIONS_SEPARATE_DOMAINS: provides the most robust solution by using GNAT libraries for Ada and VxWorks G++ libraries for C++. * EXCEPTIONS_FULL: being able to go through different languages requires using the same library for all languages, and in this case it must be Wind River's G++'s libraries. In our experience this option works properly on VxWorks 7; however Wind River does not guarantee full compatibility of the VxWorks library with the exception information that GNAT generates. Even though both are GNU-based, they rely on different versions of GCC and it is still possible to get incompatibilities. When using EXCEPTION_FULL you will need to validate it in your own context. Note that ``-Os`` is not supported with EXCEPTIONS_FULL: gcc library routines to save floating-point registers (``_savefpr_``) have a different suffix on vxWorks (``_savefpr__l``). We suggest to use ``-O2`` instead of ``-Os``. .. _vxworks_llvm_integration: LLVM """" LLVM support is similar to GNU: * EXCEPTIONS_SEPARATE_DOMAINS: provides the more robust solution. * EXCEPTIONS_FULL: a possible option at your own risk. Wind River provides a GNU-compatible layer with no guarantees: ``UnwindLevel1-gcc-ext.c``. Contact Wind River support to learn more about this option. .. _vxworks_propagation_exception_schemes: Exception Propagation Schemes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To understand the rationale and use of the three different exception integration schemes, a brief examination of how exception propagation works is required. Generally, there are two schemes for exception propagation: * Setjump/Longjump (SJLJ): each task maintains a record of where it should jump if an exception is raised, updated each time the task enters a block that contains a handler. * Zero Cost Exceptions (ZCX): when an exception is raised, the runtime computes a backtrace from dwarf unwind information and determines where the proper handler is. No task-specific record is used. The design of the ZCX scheme results in low runtime overhead during normal program execution but suffers from costly overheads when an exception is raised and propagated, due to the complexity of unwinding mechanism. For most applications this is an acceptable trade-off, but would not be suitable for applications: * frequently raising exceptions; * being certified due to the complexity of the unwinding mechanism. Given the above trade-offs, the GNAT Pro Ada runtimes for VxWorks use the ZCX scheme, except for the cert and ravenscar-cert runtimes, where SJLJ is used due to the certification context. The remainder of this section will focus on ZCX and mixed Ada/C++ exceptions, due to the peculiarities of this exception scheme on VxWorks. .. _vxworks_zcx_propagation: Zero Cost Exceptions """""""""""""""""""" Underpinning ZCX are two tables located in dedicated ELF sections: * DWARF unwind info table (.eh_frames); * Exception handlers table (.gcc_except_table) Exception propagation is performed by the compiler runtime library, libgcc, using these tables. When an exception occurs, libgcc uses these tables to unwind the stack and jump to the corresponding exception handler when an exception is raised. The stack is unwound by generating a backtrace using the information from the unwind info table, while the exception handler table provides the address of the appropriate handler to jump to. The application unwind table is constructed by the linker concatenating the ``.eh_frame`` sections from the application’s individual object files. The start of the table is marked by the symbol EH_FRAME_BEGIN and a sentinel defined by libgcc marks the end. In practice, the first object file in the link order (typically ``crtbegin.o``) defines the symbol, while the sentinel is defined in the last object file (typically ``crtend.o``). For libgcc to propagate exceptions, the application unwind table has to be registered with libgcc. This is done by the application calling the register_frame_info function with the address of the unwind table. This registration must be done in the early stages of execution, before running any user or runtime code that may raise an exception. To achieve this, GNAT and the GCC C++ compilers leverages the C++ static constructor mechanism as the DKM loader and RTP startup code executes the application’s constructors prior to executing the application code (including the elaboration code of an Ada application). .. _zcx_on_vxworks: ZCX on VxWorks ^^^^^^^^^^^^^^ The challenge when using both the GNAT Pro Ada and VxWorks C++ compilers lies with building an unwind table that is understandable to both the Ada and C++ runtimes and then registering this table with the correct libgcc via a C++ static constructor. To understand the complexity involved and the issues that can be run into, this section details the workflow the VxWorks GNU and LLVM C++ compilers use to build and register the unwind table. GNAT Pro Ada closely follows these workflows to try to achieve compatibility with the C++ compilers. Note for ARM32 targets: both the VxWorks C++ and GNAT Pro Ada compilers follow the “Exception Handling ABI for the ARM architecture”, ensuring compatibility between the compilers through the ARM ABI. Note for Diab: due to the differences between the GCC and Diab workflows, exception handling is not supported when mixing GNAT Pro Ada and Diab C++. Consequently, the Diab workflow is not covered here. .. _gnu_dkm_exception_workflow: GNU DKM Exception Workflow """""""""""""""""""""""""" When building a DKM with the VxWorks GNU toolchain in Workbench, the registration of C++ static constructors for the DKM is done through a TCL script called munch.tcl. This script generates the file ctdt.c containing: * a null-terminated list of C++ static constructors named ``_ctors``; * a null-terminated list of C++ static destructors named ``_dtors``; * an additional constructor to call register_frame_info. The DKM loader will then executed the constructors in the ``_ctors`` list upon load of the module. .. _llvm_dkm_exception_workflow: LLVM DKM Exception Workflow """""""""""""""""""""""""""" The VxWorks LLVM toolchain differs from the GNU toolchain with each object file recording its C++ static constructors in the corresponding ``.ctors.`` section (organized by priority). The final link merges these sections into a single ``.ctors`` section with corresponding symbols ``__CTOR_LIST__`` and ``__CTOR_END__``. While munch is no longer used, the ``_ctors`` list still exists with only one element: ``__exec_ctors``, a routine that executes the constructors contained within the ``.ctors`` section. .. _rtp_exception_workflow: RTP Exception Workflow """""""""""""""""""""" For RTPs, each object file records its C++ static constructors in the corresponding ``.ctors.`` section (organized by priority). The final link merges these sections into a single ``.ctors`` section with corresponding symbols ``__CTOR_LIST__`` and ``__CTOR_END__``. The RTP startup code executes the constructors contained within the ``.ctors`` section before executing the RTPs application code. .. _mixing_ada_and_cpp_in_workbench: Mixing Ada and C++ in Workbench ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The build workflow differs depending on the level of integration (EXCEPTIONS_SEPARATE_DOMAINS or EXCEPTIONS_FULL) and on the kind of VxWorks application (DKM, RTP, or SKM). In GNATbench, the complexity documented in this section is handled by gnat.makefile. In most cases, this support is transparent. However, many mixed-language applications have a complex architecture and not all cases can be supported out of the box. For these applications, gnat.makefile can be modified to suit the application as GNATbench will not overwrite this file. .. _selecting_a_libgcc: Selecting libgcc """""""""""""""" Two libgcc’s are available in the context of VxWorks: the native libgcc provided with the operating system and the libgcc provided with GNAT. The choice of libgcc depends on the integration strategy chosen. Strategy EXCEPTION_FULL is only supported with the VxWorks libgcc. This is also the option that one would choose in a certification context, when using the libgcc provided with a certified version of VxWorks. Strategy EXCEPTION_SEPARATE_DOMAIN would use GNAT's libgcc. This option has the advantage of avoiding interoperability issues between the two libgcc’s. .. _mixing_Cpp_and_Ada_exceptions: Mixing Ada and C++ Exceptions """"""""""""""""""""""""""""" Ada and C++ use the same ELF section to save the unwind information: ``.eh_frame``. There are two options: merging them (EXCEPTION_FULL), or keep them separated (EXCEPTION_SEPARATE_DOMAINS). In case of EXCEPTION_FULL, it is important that GNAT's ``crtend`` is not included in the link, otherwise it will introduce an additional sentinel frame into the unwind table that will hide part of the C++ unwind information. Suppressing ``crtend`` is performed through the linker -nostdlib switch, which GNATbench does automatically through ``gnat.makefile``. For EXCEPTION_SEPARATE_DOMAINS, the two sections are kept separate by renaming GNAT's ``.eh_frame`` section using ``objcopy --rename-section``. Again, this is done automatically by GNATbench in most cases. For advanced usage, please see gnat.makefile. When both libgcc’s are included, we also need to handle symbol duplication. gnat.makefile does this by hiding every symbol except the module entry point and the routines to register frames. If you need to make other symbols visible, you may add other calls to ``objcopy -G`` or provide multiple ``-G`` argument in a call to ``objcopy``. Changes to the VxWorks DKM Exception Workflow """"""""""""""""""""""""""""""""""""""""""""" The constructor to call register_frame_info in ctdt.c is always resolved from the VxWorks libgcc, so cannot be used when also using the GNAT libgcc. In other words, the strategy EXCEPTION_SEPARATE_DOMAINS cannot use this constructor. When this additional constructor is not usable, GNAT provides an alternative static constructor that calls GNAT's register_frame_info. GNATbench for Workbench makes use of this alternative constructor transparent: munch is called automatically when building the project from Workbench, and gnat.makefile passes the link option ``-noauto-register`` to avoid generating ``_ctors/_dtors``, letting munch generate it. However, for troubleshooting exception issues it is useful to have a look at ctdt.c and see whether _ctors contains the list of C++ static constructors and whether the constructor for register_frame_info is in the list. Changes to the VxWorks RTP Workflow """"""""""""""""""""""""""""""""""" In Workbench, the Ada part of the application will be built during a first (partial) link phase, then Workbench will do the final link. libc/libc_internal must be linked only once. GNAT provides an option ``-nolibc`` to remove these libraries when linking the Ada part of the application. This option is added automatically by ``gnat.makefile``. .. _Kernel_Configuration_for_VxWorks: Kernel Configuration for VxWorks -------------------------------- When configuring your VxWorks kernel we recommend including the target shell. If you omit it from the configuration, you may get undefined symbols at load time, e.g. :: -> ld < hello.exe Loading hello.exe Undefined symbols: mkdir Generally, such undefined symbols are harmless since these are used by optional parts of the GNAT run time. However if running your application generates a VxWorks exception or illegal instruction, you should reconfigure your kernel to resolve these symbols. On VxWorks 7, ZCX (see below) is activated by default. In this mode, proper transformation of stack overflow signal deliveries into Ada exceptions requires increasing the kernel exception stack to at least 10*1024 bytes, which may be achieved by modifying the ``taskKerExcStackSize`` global variable before tasks are spawned. This variable is defined in :file:`$WIND_BASE/target/h/private/taskLibP.h`. This affects all the tasks spawned after the change, and is typically needed for user stack-checking purposes. On PowerPC 64-bit this global variable needs to be 32*1024 bytes for proper stack overflow handling. .. _Main_Task_Attributes_and_the_Application_Stub: Main Task Attributes and the Application Stub --------------------------------------------- Several characteristics of the application main can only be controlled from the VxWorks environment: main stack size, main priority, and main task attributes. Often the default values are fine. In other cases one must explicitly set the desired values outside of the application source code. The system call ``taskSpawn()`` can be used to provide explicit values for the task name string, its VxWorks priority, stack size and other options (such as the floating point type). For applications requiring modification of these attributes that are invoked from the host or target shell, the system call can be used directly to spawn the application main task. For applications started using a Workbench launcher, these attributes can be set in the launcher. For applications being linked into a kernel image, one should use an application stub for this purpose. Although it can have any name, the default is ``usrAppInit()`` and is placed in a file in the project called :file:`usrAppInit.c`. By defining the build macro USER_APPL_INIT one can use a routine with a different stub or go directly to the Ada main subroutine if no attribute modifications are needed. Here is a sample usrAppInit.c suitable for starting an Ada application linked into a VxWorks kernel image: .. code-block:: c #include #include /* Ada binder-generated main - ADA_MAIN is passed as a macro from the makefile */ extern void ADA_MAIN(void); void usrAppInit() { int stackSize = 0x80000; /* For e500v2 processors, use VX_SPE_TASK instead */ int spawnFlags = VX_FP_TASK; /* MAIN_TASK_NAME is passed as a macro from the makefile */ char * mainTaskName = (char *) MAIN_TASK_NAME; /* VxWorks priority = 255 - Ada priority */ int priority = 100; /* Spawn Ada environment task */ taskSpawn(mainTaskName, priority, spawnFlags, stackSize, (FUNCPTR) ADA_MAIN, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } ``ADA_MAIN`` is the name of the Ada main subprogram in lower case (i.e. the symbol for the entry point of the Ada application). ``MAIN_TASK_NAME`` is the name you would like associated with the Ada environment task for the application. These are typically passed using a :switch:`-D=` switch to the compiler in the makefile for the application component. The stack size can be varied to suit the needs of the environment task. It must be spawned with at least option ``VX_FP_TASK`` or one of the other floating point type options, depending on your board. .. _Stack_Overflow_Checking_on_VxWorks: Stack Overflow Checking on VxWorks ---------------------------------- GNAT does not perform stack overflow checking by default. This means that if the main environment task or some other task exceeds the available stack space, then unpredictable behavior will occur. To activate stack checking, compile all units with the gcc option *-fstack-check*. For example: :: $ -gcc -c -fstack-check package1.adb Units compiled with this option will generate extra instructions to check that any use of the stack (for subprogram calls or for declaring local variables) does not exceed the available stack space. If the space is exceeded, then a ``Storage_Error`` exception is raised. For declared tasks, the stack size is always controlled by the size given in an applicable ``Storage_Size`` pragma (or is set to the default size if no pragma is used). For the environment task, the stack size is defined by the ``stackSize`` argument when spawning the Ada program via the ``taskSpawn()`` routine. If no stack size is specified, the stack size is set to the VxWorks default value. Use of stack overflow checking requires that a buffer be added to the application stack requirement for each task, including the environment task, in order to handle stack overflows gracefully. The size of this buffer varies between 12KB and 16KB depending on architecture, e.g. a 64-bit architecture requires a larger buffer. For VxWorks 7, *-fstack-check* is implemented using stack probing (more efficient). For VxWorks 7 kernel applications, the kernel must be built with ``INCLUDE_PROTECT_TASK_STACK`` enabled. .. index:: Interrupt Handling (VxWorks) .. _Interrupt_Handling_for_VxWorks: Interrupt Handling for VxWorks ------------------------------ Starting with VxWorks 7, the use of pragma Attach_Handler and of the function intConnect() is not supported anymore to connect to a hardware interrupt. Instead, you should use the VxWorks VxBus subsystem. For real-time processes, a VxBus device should be borrowed from the kernel space by using ``vxbRtpDevAcquire``. When the process is done with the device, it should call ``vxbRtpDevRelease`` on the borrowed device, releasing it back to the kernel. For more information on VxBus, please consult the documentation in the :title:`VxWorks BSP and Drivers Guide`. Below you will find two examples using the VxWorks VxBus API, in two different contexts : * Interrupt procedure handler * Interrupt procedure that signals a pending task All handlers must explicitly perform any required hardware cleanups, such as issuing an end-of-interrupt if necessary. In the examples below, the interrupt generation routine is a dummy function, the actual code to generate an interrupt will depend on the device and on the hardware used. * Interrupt procedure handlers Please note that there are restriction to interrupt handlers, like the following : If the handler is to execute floating point instructions, the statements involved must be bracketed by a pair of calls to ``fppSave`` and ``fppRestore`` defined in ``Interfaces.VxWorks``. As the compiler may use floating point registers for non-floating-point operations (such as memory block copy) it is highly recommended to either save and restore floating point registers or to inspect code generated for the interrupt handler. As stack checking doesn't work within an interrupt handler, be sure that units containing interrupt handlers are not compiled with *-fstack-check*. As there is no task identity when executing an interrupt handler, any Ada run time library code that depends on knowing the current task's identity must not be used. This includes tasking constructs (except for most subprograms of ``Ada.Synchronous_Task_Control``), concatenation and other functions with unconstrained results, and exception propagation. In general, it is a good idea to save and restore the handler that was installed prior to application startup. Please refer to the VxWorks documentation, specifically the :title:`VxWorks Programmer's Manual`. Example: .. code-block:: ada with Interfaces.VxWorks; use Interfaces.VxWorks; with System; package P is Count : Natural := 0; pragma Atomic (Count); procedure Handler (Parameter : System.Address); end P; package body P is procedure Handler (parameter : System.Address) is S : Status; begin Count := Count + 1; -- Acknowledge interrupt, hardware specific S := acknowledgeInterrupt (); end Handler; end P; with Interfaces.VxWorks; use Interfaces.VxWorks; with Ada.Text_IO; use Ada.Text_IO; with P; use P; procedure Useint is task T; S : Status; task body T is begin for I in 1 .. 10 loop Put_Line ("Generating an interrupt..."); delay 1.0; -- Generate interrupt, hardware specific S := generateInterrupt(); end loop; end T; -- Acquire device pointer and allocate interrupt resource Device = vxbDevAcquireByName ("example_device", 0); Interrupt_Resource := vxbResourceAlloc (Device, VXB_RES_IRQ, 0); begin S := vxbIntConnect (Device, Interrupt_Resource, Handler'Access, Null_Address); S := vxbIntEnable (Device, Interrupt_Resource); for I in 1 .. 10 loop delay 2.0; Put_Line ("value of count:" & P.Count'Img); end loop; S := vxbIntDisable (Device, Interrupt_Resource); end Useint; * Interrupt procedure that signals a pending task A variation on the example above that allows for less restrictive handler code is to separate the interrupt processing into two levels. The first level is the same as in the previous section. Here we perform simple hardware actions and signal a task pending on a Suspension_Object (defined in ``Ada.Synchronous_Task_Control``) to perform the more complex and time-consuming operations. The routine ``Set_True`` signals a task whose body loops and pends on the suspension object using ``Suspend_Until_True``. The suspension object is declared in a scope global to both the handler and the task. This approach can be thought of as a slightly higher-level application of the ``C`` example using a binary semaphore given in the VxWorks Programmer's Manual. In fact, the implementation of ``Ada.Synchronous_Task_Control`` is a very thin wrapper around a VxWorks binary semaphore. This approach has a latency between the interrupt handler and the protected procedure approach. There are no restrictions in the Ada task code, while the handler code has the same restrictions as any other interrupt handler. Example: .. code-block:: ada with System; package Sem_Handler is Count : Natural := 0; pragma Atomic (Count); -- Interrupt handler providing "immediate" handling procedure Handler (Param : System.Address); -- Task whose body provides "deferred" handling task Receiver is pragma Interrupt_Priority (System.Interrupt_Priority'First + Level + 1); end Receiver; end Sem_Handler; with Ada.Synchronous_Task_Control; use Ada.Synchronous_Task_Control; package body Sem_Handler is SO : Suspension_Object; task body Receiver is begin loop -- Wait for notification from immediate handler Suspend_Until_True (SO); -- Interrupt processing Count := Count + 1; end loop; end Receiver; procedure Handler (Param : System.Address) is S : STATUS; begin -- Acknowledge interrupt, hardware specific S := acknowledgeInterrupt (); -- Signal the task Set_True (SO); end Handler; end Sem_Handler; with Ada.Text_IO; use Ada.Text_IO; with Sem_Handler; use Sem_Handler; procedure Useint is S : STATUS; task T; task body T is begin for I in 1 .. 10 loop Put_Line ("Generating an interrupt..."); delay 1.0; -- Generate interrupt, hardware specific S := generateInterrupt (); end loop; end T; Device = vxbDevAcquireByName ("example_device", 0); Interrupt_Resource := vxbResourceAlloc (Device, VXB_RES_IRQ, 0); begin S := vxbIntConnect (Device, Interrupt_Resource, Handler'Access, Null_Address); S := vxbIntEnable (Device, Interrupt_Resource); for I in 1 .. 10 loop delay 2.0; Put_Line ("value of Count:" & Sem_Handler.Count'Img); end loop; S := vxbIntDisable (Device, Interrupt_Resource); abort Receiver; end Useint; .. index:: Relocation issues for PowerPC VxWorks targets .. index:: PowerPC VxWorks, relocation issues .. index:: VxWorks PowerPC, relocation issues .. _Handling_Relocation_Issues_for_PowerPC_Targets: Handling Relocation Issues for PowerPC Targets ---------------------------------------------- This section summarizes the issue of greater than 24bit relocations (which can occur in very large applications), how it is solved by default, and the drawbacks of the solution. The issues and solutions are different for kernel mode apps vs RTPs. .. _Kernel_Mode: Kernel Mode ^^^^^^^^^^^ .. _Summary: Summary """"""" The runtime is built with the -mlongcall option, but it is no longer on by default all compilations. This switch morphs a 24bit relocation into a 32bit (PowerPC) or 64bit (PowerPC64) absolute reference, resulting in a per-call performance penalty and bloat in the runtime object code, without penalizing the entire application. This allows supporting large programs that would need long calls across the board while not enforcing the effects of the switch on the application code that doesn't have such needs (mlongcall compiled code and mno-longcall compiled code can be intermixed). A user requiring the absolute addressing mode (for example for a very large application) will need to explicitly specify the -mlongcall switch for the parts of the application that require it. .. index:: Embedded ABI (for VxWorks on PowerPC) .. index:: EABI (for VxWorks on PowerPC) .. _Technical_Details: Technical Details """"""""""""""""" VxWorks on the PowerPC follows the variation of the SVR4 ABI known as the Embedded ABI (EABI). In order to save space and time in embedded applications, the EABI specifies that the default for subprogram calls should be the branch instruction with relative addressing using an immediate operand. The immediate operand to this instruction (relative address) is 24 bits wide. It is sign extended and 2#00# is appended for the last 2 bits (all instructions must be on a 4 byte boundary). The resulting 26 bit offset means that the target of the branch must be within +/- 32 Mbytes of the PC-relative branch instruction. When loading a program, VxWorks completes the linking phase by resolving all of the unresolved references in the object being loaded. When one of those references is a relative address in a branch instruction, and the linker determines that the target is more than 32 Mbytes away from the branch, the error occurs. This only happens when the BSP is configured to use more than 32 MBytes of memory. The VxWorks kernel is loaded into low memory addresses, and the error usually occurs when the target loader is used (because it loads objects into high memory, and thus calls from the program to the VxWorks kernel can be too far). One way to solve this problem is to use the Workbench host loader; this will place programs in low memory, close to the kernel. The use of the :switch:`-mlongcall` option for gcc causes the compiler to construct the absolute address of a subprogram in a register and use a branch instruction with absolute addressing mode. To see the effect of :switch:`-mlongcall`, consider the following small example: .. code-block:: ada procedure Proc is procedure Imported_Proc; pragma Import (Ada, Imported_Proc); begin Imported_Proc; end; If you compile `Proc` with the option :switch:`-mno-longcall`, the following code is generated: :: _ada_proc: ... bl imported_proc ... In contrast, here is the result with the default :switch:`-mlongcall`: :: _ada_proc: ... addis 9,0,imported_proc@ha addi 0,9,imported_proc@l mtlr 0 blrl ... For additional information on this issue, please refer to WRS' SPRs 6040, 20257, and 22767. .. _RTPs: RTPs ^^^^ The :switch:`-mlongcall` option is off by default for all compilations. Instead the linker switch :switch:`--relax` is on by default. During linking, an out of range call is detected and space is allocated at the end if each input object for only those "long calls" that are necessary, so the computational and code bloat is minimized. No user intervention is required. .. _Calling_exported_Ada_procedures_from_the_VxWorks_shell: Calling exported Ada procedures from the VxWorks shell ------------------------------------------------------ If you need to call Ada subprograms from outside your Ada application (for example, from the host or target shell, from a driver or a C or C++ application), you will in general need to make the executing task known to the Ada run-time library. A typical situation is the need to enable or disable traces in your Ada application from the shell by calling an Ada procedure that sets the value of a Boolean controlling the traces. The problem is the following: when you call the Ada subprogram, it may raise an exception on the target. This could be through normal execution, or it could be caused solely by the fact that the thread executing the subprogram has not been made known to the Ada run-time library. There are a number of situations in Ada applications where the run-time library must be able to determine what task is running, and must be able to access some Ada-specific data structures in order to do its job. If a VxWorks task has not been registered, the 'id' that is accessed by the run-time library will be invalid, and will reference invalid data (or invalid memory). The solution is to use the ``GNAT.Threads.Register_Thread`` and ``GNAT.Threads.Unregister_Thread`` routines, so that the task which called the subprogram is known to the Ada run-time. This way, when the Ada task id is requested at some point in the procedure's execution, a valid value will be fetched, and the necessary data structures will have been allocated and properly initialized. The following small example shows how to use this feature: .. code-block:: ada package Traces is procedure Put_Trace (Info : String); -- Print Info on the standard output if the traces are on procedure Trace_On; pragma Export (C, Trace_On, "trace_on"); -- Activate the traces procedure Trace_Off; pragma Export (C, Trace_Off, "trace_off"); -- Deactivate the traces end Traces; with System; use System; with Ada.Text_IO; use Ada.Text_IO; with GNAT.Threads; use GNAT.Threads; package body Traces is Trace : Boolean := False; --------------- -- Put_Trace -- --------------- procedure Put_Trace (Info : String) is begin if Trace then Put_Line (Info); end if; end Put_Trace; -------------- -- Trace_On -- -------------- procedure Trace_On is Id : System.Address; begin Id := Register_Thread; Trace := True; Unregister_Thread; end Trace_On; --------------- -- Trace_Off -- --------------- procedure Trace_Off is Id : System.Address; begin Id := Register_Thread; Trace := False; Unregister_Thread; end Trace_Off; end Traces; with Traces; use Traces; procedure Increment is Value : Integer := 1; begin for J in 1 .. 60 loop Value := Value + 1; Put_Trace (Integer'Image (Value)); delay 1.0; end loop; end Increment; After having compiled and loaded the application on your target, spawn it and control the traces by calling ``trace_on`` and ``trace_off`` from the shell. You should see numbers displayed in the VxWorks console when the traces are on. :: -> sp increment task spawned: id = 3b0dcd0, name = s1u0 value = 61922512 = 0x3b0dcd0 -> trace_on value = 0 = 0x0 -> trace_off value = 0 = 0x0 .. _Simulating_Command_Line_Arguments_for_VxWorks: Simulating Command Line Arguments for VxWorks --------------------------------------------- The GNAT implementation of ``Ada.Command_Line`` relies on the standard C symbols ``argv`` and ``argc``. The model for invoking kernel 'programs' under VxWorks does not provide these symbols. For kernel programs the typical method for invoking a program under VxWorks is to call the ``sp`` function in order to spawn a thread in which to execute a designated routine (in GNAT, this is the implicit main generated by gnatbind). ``sp`` provides the capability of pushing a variable number of arguments onto the stack when the function is invoked. But this does not work for the implicit Ada main, because it has no way of knowing how many arguments might be required. This eliminates the possibility of using ``Ada.Command_Line`` for kernel applications. However there is no such restriction for either command line arguments nor for environment variables for RTP mode 'programs'. For more information see the VxWorks documentation for ``rtpLib/rtpSpawn()``. One way to solve this problem is to define symbols in the VxWorks environment, then import them into the Ada application. For example, we could define the following package that imports two symbols, one an int and the other a string: .. code-block:: ada with Interfaces.C.Strings; use Interfaces.C.Strings; package Args is -- Define and import a variable for each argument Int_Arg : Interfaces.C.Int; String_Arg : Chars_Ptr; private pragma Import (C, Int_Arg, "intarg"); pragma Import (C, String_Arg, "stringarg"); end Args; An Ada unit could then use the two imported variables ``Int_Arg`` and ``String_Arg`` as follows: .. code-block:: ada with Args; use Args; with Interfaces.C.Strings; use Interfaces.C, Interfaces.C.Strings; with Ada.Text_IO; use Ada.Text_IO; procedure Argtest is begin Put_Line (Int'Image (Int_Arg)); Put_Line (Value (String_Arg)); end Argtest; When invoking the application from the shell, one will then set the values to be imported, and spawn the application, as follows: :: -> intarg=10 -> stringarg="Hello" -> sp (argtest) .. index:: System.Traceback .. index:: Ada.Exceptions.Traceback .. _Traceback_support_on_VxWorks: Traceback Support on VxWorks ---------------------------- Call stack traceback computation is supported on all VxWorks runtimes. Such computation is sometimes triggered implicitly, for instance when a program which was compiled with binder switch `-E` reports an unhandled exception. In addition, traceback computation can also occur through the use of dedicated units provided by your runtime, such as `Ada.Exceptions.Traceback` or, for runtimes that support it, `GNAT.Traceback`. Traceback Computation When Using Restricted Runtimes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If your application meets all of the following criteria, then you need to compile your application with `-fno-omit-frame-pointer`: * Your application depends on System.Traceback, indicating that it may have to compute some tracebacks; * Your application is built using a restricted runtime (e.g. `light` or `light-tasking`); * Your application is targeting one of the following architectures: - AArch64 - ARM - X86_64 This requirement allows a significant reduction in complexity of the traceback computation algorithm, thus making it more practical to provide this capability to projects where certification is a factor. .. index:: addr2line .. index:: vxaddr2line .. _Using_addr2line_on_VxWorks: Using addr2line on VxWorks -------------------------- For general information about addr2line, see "Getting Internal Debugging Information" in the |GNAT_Users_Guide|. .. _Differences_between_VxWorks_and_native_platforms: Differences between VxWorks and native platforms ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Using addr2line on VxWorks, where modules are often dynamically loaded, is a bit different than on native platforms. When dealing with dynamically loaded modules, one needs to determine the offset at which the module has been loaded. This allows addr2line to correlate the target addresses of the stack trace with the addresses in the load module. On VxWorks, there are three ways a module can be located into target memory: * it is dynamically linked and loaded (DKM) * it is statically linked with the kernel (SKM) * it is linked into its own virtual address space (RTP) In all cases, the addresses used in the module are different from the ones used within the target memory address space under VxWorks. In contrast, on a native system, the executable has the same addresses in the object file and in memory, as virtual addresses are used. There is no need to collate the addresses on target with those in the object module. While RTPs do execute in their own virtual address spaces, the ``adainit`` address still needs to be determined as described below. As addr2line uses the addresses in the module, we need to adjust the addresses returned as a traceback at run-time so that they can be correctly interpreted by addr2line. To do this manually, we would follow a procedure like this: A symbol that is always present in the module is determined in order to compute the offset between the addresses at run-time and the addresses in the module. Its address in the module is subtracted from its address in memory, and the computed delta is added to each address in the traceback. .. _Using_target-vxaddr2line: Using -vxaddr2line ^^^^^^^^^^^^^^^^^^^^^^^^^^ Manually performing the computation just described is tedious, so a tool has been introduced to automate the process: -vxaddr2line, where is the name of target (e.g. powerpc-wrs-vxworks7r2 on PowerPC VxWorks SR0660 and above). All that needs to be done is to give the name of the module, the address of the symbol ``adainit`` in memory and the traceback values as parameters. The computation is then done automatically, and the result is transmitted to addr2line, which returns the symbolic traceback in the usual manner. .. _An_example: An example ^^^^^^^^^^ The example session of this section is for powerpc-wrs-vxworks. Replace this target name by the target you are using. Consider the following simple source code : :: 1 2 procedure CE is 3 4 procedure Raise_Constraint_Error is 5 begin 6 raise Constraint_Error; 7 end; 8 9 procedure Sub_2 (Should_Raise_CE : Boolean) is 10 begin 11 if Should_Raise_CE then 12 Raise_Constraint_Error; 13 end if; 14 end; 15 16 procedure Sub_1 is 17 begin 18 Sub_2 (Should_Raise_CE => True); 19 end; 20 begin 21 Sub_1; 22 end; * Build the example with gprbuild, providing the -E binder argument in this case so that a raw backtrace is returned for the unhandled exception. -g is required in any case because addr2line uses debug information to perform the symbolic transcription. :: $ gprbuild --target=powerpc-wrs-vxworks -g ce -bargs -E Compile [Ada] ce.adb Bind [gprbind] ce.bexch [Ada] ce.ali Link [link] ce.adb * Load and run the resulting module on the target board. It raises the expected unhandled exception and generates the associated raw backtrace: :: -> ld < ce.out Loading /ce.out | value = 591824 = 0x907d0 -> sp ce task spawned: id = 1b8aae0, name = s2u0 value = 28879584 = 0x1b8aae0 -> Execution terminated by unhandled exception Exception name: CONSTRAINT_ERROR Message: ce.adb:6 Call stack traceback locations: 0x3b8394 0x3b83e0 0x3b8420 0x3b8458 0x3b82f0 0x19a184 Now convert the backtrace into symbolic references... * Determine the address of a reference symbol of the module (we use the address of ``adainit``), which we obtain by calling the VxWorks ``lkup`` function for SKMs and DKMs: :: -> lkup "adainit" adainit 0x003b81d0 text (ce.out) value = 0 = 0x0 * For RTPs, adainit can be found using in a command shell: :: powerpc-wrs-vxworks-nm foo.rtp | grep adainit - * We now have the information needed to run ``vxaddr2line``: :: powerpc-wrs-vxworks-vxaddr2line ce.out 0x003b81d0 0x3b8394 ... 0x19a184 ------ ---------- --------------------- exec file Ref.addr. Run-Time backtrace This gives the following: :: 000001C0 at .../ce.adb:6 0000020C at .../ce.adb:12 0000024C at .../ce.adb:18 00000284 at .../ce.adb:21 0000011C at .../b~ce.adb:88 [meaningless output for the last address] The last line is not shown here because it designates stub code within VxWorks and is not part of the user's application. .. _Removal_of_Unused_Code_and_Data: Removal of Unused Code and Data ------------------------------- GNAT for VxWorks supports unused code and data elimination. For a complete description of this functionality, please refer to the GNAT User's Guide. The use of this functionality requires some extra care for VxWorks. GNAT for VxWorks performs partial linking by default on all VxWorks versions, except in RTP mode. Because of this partial linking, the unused code and data elimination requires the use of :switch:`-e` / :switch:`--entry` ``ld`` option to work correctly. The use of these options is described in the GNAT User's Guide. For example, in order to compile my_program.adb, you can use the following command line: :: $ gprbuild --target=powerpc-wrs-vxworks7r2 my_program -cargs -ffunction-sections \ > -fdata-sections -largs -Wl,--gc-sections -Wl,--entry=my_program Compile [Ada] my_program.adb Bind [gprbind] my_program.bexch [Ada] my_program.ali Link [link] my_program.adb .. _VxWorks_Debugging: Debugging --------- The Workbench debugger can be used for all targets supported by Workbench 3.3.x or Workbench 4. In order to display the value of an Ada object in Workbench, right-click on one of its occurrences in the source editor and select ``Add Watch Expression...``. The encoded name of the entity will be displayed in a dialog window: for example, a variable named Alpha declared in package Ownship will be shown as ``ownship__alpha``. After clicking on OK, you will see that the corresponding expression has been added to the Expression view. From there, you will be able to browse the content of your object. Workbench also displays the value of local variables in Ada subprograms; these are shown by the view ``Variables``. For more information about the Workbench debugger, see :title:`Wind River WorkBench User's Guide`. .. _Frequently_Asked_Questions_for_VxWorks: Frequently Asked Questions for VxWorks -------------------------------------- * When I run my program twice on the board, it does not work. Why not? Ada programs generally require elaboration and finalization, so the compiler creates a wrapper procedure whose name is the same as the Ada name of the main subprogram, which invokes the elaboration and finalization routines before and after your program executes. But the static part of the elaboration is handled while loading the program itself, so if you launch it twice this part of the elaboration will not be performed the second time. This affects the proper elaboration of the GNAT run-time, and thus it is mandatory to reload your program before relaunching it. * Can I load a collection of subprograms rather than a standalone program? It is possible to write Ada programs with multiple entry points that can be called from the VxWorks shell (or from a C or C++ application). To do this, generate an externally-callable Ada subsystem (see "Binding with Non-Ada Main Programs" in the |GNAT_Users_Guide|). If you use this method, you need to call ``adainit`` before calling any Ada entry point. * Does the debugger provide a way to stop when an exception is raised? The Workbench debugger does not provide gdb's 'catch exception'. You may emulate it by set breakpoints on the following functions: ``__gnat_debug_raise_exception`` for catching any exception, ``__gnat_unhandled_exception`` for catching only unhandled exceptions and ``__gnat_debug_raise_assert_failure`` to catch assert failures. * When I load a large application from the VxWorks shell using the *ld* command, the load hangs and never finishes. How can I load large executables? This is a classic VxWorks problem when using the default ``rsh`` communication method. Using NFS instead should work. Use the ``nfsShowMount`` command to verify that your program is in a NFS mounted directory. * When I load a large application from the debugger using the wtx target connection, the load never finishes, why? Make sure that the memory cache size parameter of the target server is large enough. (``target -m big_enough_size``, or Memory cache size box in GUI.) * When I spawn my program under the VxWorks kernel shell, interactive input does not work, why? When a task is spawned from a kernel shell with the ``sp()`` or ``taskSpawn()`` command, it shares the same standard I/O file descriptors as the shell. Since the shell has a higher priority than the task, the shell receives the interactive input. The recommended method to pass input to such a task is via file redirection: :: -> # here you can have interactive input -> main -> # here you cannot -> sp main -> # neither here -> taskSpawn("ess",100,0,8000000,main) -> # but you can input from a file: -> taskSpawn("Bae",100,0,8000000,main) < input_file This approach is preferred because it removes the contention between the task and the shell over the interactive input. VxWorks includes the ability for the standard input of the shell to be redirected to allow a task to access the interactive input unimpeded, an approach that may be suitable for some applications. See `Redirecting Shell IO` in the :title:`VxWorks Kernel Shell Users Guide` for more information. * The ``errno`` of the task(s) of my Ada application is not null even though my application is running correctly. Is that normal? Yes. As explained in the :title:`VxWorks OS Libraries API Reference` in the ``errnoLib`` section: "most VxWorks functions return ERROR when they detect an error, or NULL in the case of functions returning pointers. In general, they set an error status that describes the nature of the error". There are a large number of calls to VxWorks functions in the Ada run-time. Whenever a system call returns a value indicating an error, the error status is set to a non-zero value. So despite the checking of the return value is to determine an appropriate action, ``errno`` can still be non-null. Resetting the error status in the Ada run-time each time a VxWorks function is called would add unnecessary system call overhead and would not avoid masking error status information that may appear in user code. So this approach would not help. It is a good practice not to rely on the error status value to detect errors. It should be only used to get more information on errors that have already been detected by checking the code returned by the VxWorks function. To be sure to get the error status corresponding to the error detected in the code, the use of the debugger is recommended. * I cannot display Ada objects in the debugger, why? On Workbench 3.3.x, Ada debugging is not enabled in the WB debugger. To enable it, you must set the environment variable ``DFW_ENABLE_ADA_SYMBOL_SEARCH`` to 1. You need only set this environment variable once, as long as it does not become undefined later. * I get undefined references errors when trying to use GNAT.Sockets with an RTP, and my kernel configuration is correct. What should I do ? If you are building an RTP from Workbench, using the exception integration scheme EXCEPTIONS_FULL or EXCEPTIONS_SEPARATE_DOMAINS, you need to add `-lnet` to the `LIBS` Build Property variable, like so : * Right click on your RTP project * Click on `Properties` * Select the `Build Properties` menu on the left * Go to the `Variables` tab * Scroll down until you find the `LIBS` variable * Add `-lnet` to it * When using a 23 compiler with VxWorks 7 SR06XX or VxWorks 21.03, I get an undefined reference to `__stdioFp` when trying to load my kernel module, what should I do ? The stdio implementation changed starting with VxWorks 21.07, and now relies on a function `__stdioFp` that is missing from earlier versions. To remove the undefined reference, add the following C file to your project. It will provide the missing symbol, emulating the `prior to 21.07` behaviour by returning the matching FILE pointer : :: #include FILE* __stdioFp(int fp) { switch(fp) { case 0: return &_Stdin; case 1: return &_Stdout; case 2: return &_Stderr; default: /* fp can be 0, 1 or 2, so we shouldn't end up here. */ return NULL; } } Additionally, you need to add `-Wl.-u__stdioFp` to your call to gprbuild or `-u__stdioFp` to the Ada Default_Switches attribute in the Linker package of your project file, and add 'C' to the list of languages. .. _SMP_Support: SMP Support ----------- The OS supports symmetric multi-processing (SMP) for specific target boards. GNAT includes run-time libraries supporting DKM, SKM and RTP applications running on a SMP kernel. When using these libraries, the VxWorks kernel must be configured with ``__thread`` support. These libraries are also compatible for use on uniprocessor kernels. Processor affinity is controlled using ``pragma Task_Info``, by declaring a discriminant of type ``System.Task_Info.Task_Info_Type`` for a task type and giving it a value on task creation, or by using Ada 2012 ``pragma CPU``. For the first two options, allowable values range from 0 to n-1, where n is the number of processors available. A value of zero sets the processor affinity to VxWorks logical processor 0, and so on. For ``pragma CPU``, allowable values range from 1 to n, and the affinity in VxWorks terms is set to the logical processor with an ID one less than the specified value. Out of range values will cause creation of the task to fail. By default, when the above mechanisms are not used, tasks inherit the processor affinity of the task that creates them (but see the notes on RTPs in the VxWorks user manuals). .. index:: Workbench / VxWorks 7 Topics .. _Workbench_VxWorks_7_Topics: Workbench / VxWorks 7 Topics ============================ This section describes topics that are specific to VxWorks 7. It introduces the basic features provided by the GNAT toolchain for this target, and shows how to use them. It also describes which functionalities are implemented, and summarizes commonly encountered known issues. Full lists of known issues by platform and version are available in GNATtracker in the Documentation panel. .. _Using_GNAT_for_VxWorks_7: Using GNAT for VxWorks 7 ------------------------ GNAT for VxWorks 7 here designates both: * GNAT Pro Ada * GNAT Pro C. Note that GNAT Pro Ada comes with a C compiler that is supported only for the following uses: * generating Ada bindings from C headers using -fdump-ada-spec; * rebuilding the Ada run-time library. GNAT for VxWorks 7 comes with the recommended development environment: * GNATbench, the GNAT plug-in for Workbench (Wind River's IDE based on Eclipse). Workbench with the GNATbench plug-in provides a more VxWorks-centric environment. Note that interfacing GNAT with Wind River C++ compilers is only supported when building from GNATbench. See section :ref:`Interoperability_with_VxWorks_compilers` for more details. .. _Building_a_VxWorks_7_Application: Building a VxWorks 7 Application -------------------------------- GNAT for VxWorks 7 supports the following build interfaces: * Workbench for three types of modules: * downloadable kernel modules (DKM) * statically linked kernel modules (SKM) * real time process modules (RTP) .. index:: Building a VxWorks 7 Application from Workbench .. _Building_a_VxWorks_7_Application_from_Workbench: Building a VxWorks 7 Application from Workbench ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The GNATbench Ada Development User Guide describes how to use GNATbench within Workbench. This document, like all Workbench user guides, is accessible via the "Help Contents" sub-menu entry under the top-level Workbench "Help" menu. See the "Building" chapter for all the topics related to building Ada projects within Workbench. This chapter includes general material, such as an overview of the predefined and GNATbench-defined build commands, as well as specific information that is essential to building with GNATbench. When building a downloadable kernel module (DKM) using GNATbench, make sure that the ``Linker.Switches ()`` attribute includes ``"-noauto-register"``. This switch prevents conflicts with the OS-provided static constructors and destructors that are added into DKM projects by Workbench when it builds an application image. You should also note that the GNATbench documentation includes a number of tutorials that provide detailed, step-by-step instructions and screen-shots showing how to create and build VxWorks projects. All of these tutorials are located under the top-level node of the GNATbench Ada Development User Guide. .. index:: VxWorks 7 RTPs and kernel modules .. _VxWorks_7_RTPs_and_kernel_modules: VxWorks 7 RTPs and kernel modules ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To support both RTPs and kernel modules, two different run-time libraries are provided. For example, you can build an Ada kernel module (DKM) from the ``demo1`` example using the following command: :: $ gprbuild --target=powerpc-wrs-vxworks7 -g --RTS=kernel demo1 Compile [Ada] demo1.adb [Ada] gen_list.adb [Ada] instr.adb Bind [gprbind] demo1.bexch [Ada] demo1.ali Link [link] demo1.adb This will build a relocatable object that you can download into the kernel context. The *-g* switch adds debugging information to the module. If you are building the same DKM within GNATbench, add the following into the Linker package in your root GPR file: .. code-block:: gpr package Linker is for Switches ("Ada") use (..., "-noauto-register", "-r", ...); end Linker; The linker switch prevents conflicts with the munching step performed by Workbench on relocatable DKMs. For SKMs (which will be statically linked into the VxWorks image), use: :: $ gprbuild --target=powerpc-wrs-vxworks7 -g --RTS=kernel demo1 -largs -nostdlib -r Compile [Ada] demo1.adb [Ada] gen_list.adb [Ada] instr.adb Bind [gprbind] demo1.bexch [Ada] demo1.ali Link [link] demo1.adb In this case, the *-nostdlib* switch prevents conflicts between the GNAT Pro version of :file:`libgcc.a` and that provided by VxWorks, by suppressing the inclusion of the former. The *-r* switch allows a partial link that will be resolved when linked into the image. To build an Ada RTP module, you will use the RTP run-time library. The compiler will automatically find the VxWorks 7 RTP libraries using the VSB_DIR environment variable. This variable should point to the root of the VxWorks Source Build for your target. The previous example may be built as follows: :: $ gprbuild --target=powerpc-wrs-vxworks7 -g --RTS=rtp demo1 -largs -nostdlib -r Compile [Ada] demo1.adb [Ada] gen_list.adb [Ada] instr.adb Bind [gprbind] demo1.bexch [Ada] demo1.ali Link [link] demo1.adb Note that use of gprconfig/gprbuild will insert the necessary includes, library references and *-mrtp* switches for compilation and linking automatically. None of the above special switches need to be provided when using Workbench with the GNATbench plugin to perform builds. By default, an RTP is built statically, hence including all the necessary parts in the generated RTP file. It is also possible to build dynamically linked RTPs. To do that, you first need to specify the library as dynamic. This is done by adding the following in the library project file: :: for Library_Kind use "dynamic"; Then you have to specify that the RTP will use dynamic libraries. This is done by adding the following to the RTP project file: :: package Compiler is for Default_Switches ("ada") use ("-non-static"); end Compiler; package Linker is for Default_Switches ("ada") use ("-non-static"); end Linker; .. _VxWorks_7_SMP_Support: VxWorks 7 SMP Support --------------------- VxWorks 7 supports symmetric multi-processing (SMP) and all GNAT runtimes are built with SMP support. But these runtimes can also be used with uniprocessor kernels. When using these libraries, the VxWorks kernel must be configured with ``__thread`` support. Processor affinity is controlled by using ``pragma Task_Info``, by declaring a discriminant of type ``System.Task_Info.Task_Info_Type`` for a task type and giving it a value on task creation, or by using Ada 2012 ``pragma CPU``. For the first two options, allowable values range from 0 to n-1, where n is the number of processors available. A value of zero sets the processor affinity to VxWorks logical processor 0, and so on. For ``pragma CPU``, allowable values range from 1 to n, and the affinity in VxWorks terms is set to the logical processor with an ID one less than the specified value. Out of range values will cause creation of the task to fail. By default, when the above mechanisms are not used, tasks inherit the processor affinity of the task that creates them (but see the notes on RTPs in the VxWorks user manuals). .. _Debugging_an_Application_on_VxWorks_7: Debugging an Application on VxWorks 7 ------------------------------------- GNAT for VxWorks 7 generates debug information following the dwarf standard; this can be interpreted by the Workbench debugger. To use this debug technology, please consult the Workbench manual. .. _Using_VxSim_on_VxWorks_7: Using VxSim on VxWorks 7 ------------------------ For RTPs on VxWorks 7, an additional specification of *__wrs_rtp_base* must be provided for VxSim, the value of which depends on the architecture and Host OS. Refer to the BSP documentation for . This can be accomplished either explicitly as shown below, or by the setting of the *WRS_RTP_BASE* environment variable to . .. code-block:: gpr package Linker is for Leading_Switches ("Ada"|"C++"|"C") use (..., "-Wl,__rtp_base=", ...); end Linker; On VxWorks 7 (SR0600 and above) for x86_64, when targetting VxSim-64, the entire application should be built using the large code model (``-mcmodel=large``). Hence, the ``kernel`` or ``rtp-large`` runtime must be used because they have been built using this code model. When using the ``rtp-large`` runtime, gprbuild will automatically add ``-mcmodel=large`` when compiling your code. When using the ``kernel`` runtime, you should add that switch manually in the ``.gpr`` file: .. code-block:: gpr package Compiler is for Default_Switches ("Ada"|"C++"|"C") use (..., "-mcmodel=large", ...); end Compiler; package Linker is for Default_Switches ("Ada"|"C++"|"C") use (..., "-mcmodel=large", ...); end Linker; .. index:: Known GNAT C++ Limitations on VxWorks 7 .. _Known_GNAT_CXX_Limitations_VxWorks_7: Known GNAT C++ Limitations on VxWorks 7 --------------------------------------- * GNAT C++ is not supported with kernels built using the Safety Subset. * On 64-bit PowerPC, variadic virtual methods are only supported in RTP mode. .. index:: Helix Virtualization Platform Topics .. _Helix_Virtualization_Platform_Topics: Helix Virtualization Platform Topics ==================================== This section provides information specific to the GNAT cross-development system for targets on the Helix and Helix Cert Virtualization Platform. For a list of supported versions, see the release notes. .. index:: ARINC-653 .. _Helix_Virtualization_Platform_Topics_Introduction: Introduction ------------ GNAT for Helix targets lets you develop applications with the following Ada run-time libraries: .. index:: Light Run-Time Library (for Helix) * Applications for VxWorks guests using APEX can be built with the restricted certifiable run-time library (``rts-light``). Application partitions may include either the full or partial APEX components, or just standard VxWorks threads. This section describes the mechanics of building and debugging Ada ARINC 653-based applications for Helix, and also treats other issues relevant to application development in the various contexts listed above. .. _Running_and_Debugging_Applications: Running and Debugging Applications ---------------------------------- The "GNATbench for WRS Workbench User's Guide" describes how to prepare for debugging using Ada in the Workbench IDE and debugger. .. _Application_Design_Considerations: Application Design Considerations --------------------------------- As a general comment, one should never mix APEX processes, POSIX threads or Ada tasks in a single application partition. Each of these entities is part of a distinct high-level model for developing concurrent applications; mixing them will lead to confusion and unpredictable application behavior. Before starting, one should know what 'foreign threads' are. 'Foreign threads' refer to threads that are not created by the Ada run-time environment, and therefore are not known to it without taking additional bookkeeping steps. In order to facilitate seamless operation of such 'foreign threads' when they execute Ada code, they need to be registered with the Ada run-time. On some native platforms, GNAT accomplishes this automatically; another alternative is to use System.Threads (which can be used for kernel tasks on a Helix guest). Since Helix applications utilizing the ARINC 653 standard are expected to comprise mainly APEX processes, the way to perform registration of these processes depends on the binding in use. There are two types of bindings that differ in how they treat foreign threads. One type of bindings, the so-called "thin" bindings, require manual thread registration. These bindings are provided for Helix targets. The System.Threads package can be used to provide manual registration. The other type of bindings, the "thick" bindings, allow for registration through the ``Apex_Processes.Create_Process`` routine. By using these bindings, and following the recommended thread registration approach, one can ensure that exception handling and other operations such as functions with unconstrained results and the process-level health-monitoring handler are executed correctly. .. index:: Helix APEX Application Structure .. _General_APEX_Application_Structure_on_Helix: General APEX Application Structure on Helix ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For applications that are to execute in application partitions, there is a typical structure. There is usually one 'main' application that will perform initializations: allocating data, creating and starting various APEX processes, and finally setting the partition into 'normal' mode. Once the partition has entered normal mode, the vThread or kernel task executing the main is suspended, no more dynamic allocation is allowed, and the APEX processes defined within the partition are scheduled according to the characteristics defined for them within the main application. For an Ada application, this means that the main subprogram is expected to perform initializations as described, and then to suspend until the partition is restarted. This suspension occurs when ``Apex_Processes.Set_Partition_Mode (Normal);`` is called. All of the work of the application is performed by the subsidiary processes. These processes communicate with each other and with processes in other partitions using the APEX interface. C, C++ and Ada code can be mixed within an application, and there are various approaches available to manage the code. GNAT project files are the best method for combining mixed-language code within a partition. Note that more complex organization of applications within a partition (e.g. multiple initialization threads) are possible, but require more complex setup and coordination. Refer to Wind River's :title:`Helix Platform Cert Edition Guest Safety Programmer's Guide`. .. index:: ARINC-653 APEX Binding (for Helix) .. _The_ARINC-653_APEX_Binding: The ARINC-653 APEX Binding ^^^^^^^^^^^^^^^^^^^^^^^^^^ The Ada binding to the ARINC-653 APEX (APplication EXecutive) comes with the compiler. The bindings are precompiled for each valid combination of run-time library and binding variant. Sources are found under :file:`/include/apex/apex[_variant]/src/`. Static libraries that can be linked to are in :file:`/lib/apex/--/libapex.a`. For example, for the Aarch64 architecture, and the use of the light run-time and the ARINC Part 1-4 standard for Ada 95 thin bindings, the location of the library to link is :file:`/lib/apex/aarch64-wrs-vxworks7r2-light-apex_p1-4_thin_95/libapex.a`. A GNAT project file to include in a project needing the APEX bindings is located at :file:`/lib/gnat/apex.gpr`. :file:`gprbuild` knows about this location, so to access this project file, add .. code-block:: gpr with "apex"; to the top of your project file. This project file eliminates the need to explicitly include the desired APEX library into the ``gnatlink`` switches. This project file takes three scenario variables: * PLATFORM - the target triplet for your platform, e.g. aarch64-wrs-vxworks7r2. * RUNTIME - the run-time library to use: light. * BINDING - the APEX binding to use: apex_p1-4_thin_83 or apex_p1-4_thin_95. The bindings and valid combinations of RUNTIME and BINDING are described next. Several bindings are provided: * The standard Ada 83 APEX binding, designated by ``BINDING=apex_p1-4_thin_83``. This binding is compatible with the light run-time library. * The standard Ada 95 APEX binding, designated by ``BINDING=apex_p1-4_thin_95``. This binding is compatible with the light run-time library. Additionally there are couple of suffixes needed to the ``apex`` part of the binding name, depending on the version of Helix being used. This is to accommodate a bug in the VxWorks library: * For SR0631, at least, ``_HELIX-2443`` to address a library bug. The substring following the hyphen is the number of the Wind River Software Problem Report. For example, the value of ``BINDING`` for Ada 95 style APEX for the light runtime and Helix SR0631 would be ``apex_HELIX-2443_thin_95`` . The easiest way to set the binding is to add the project file :file:`/lib/gnat/apex.gpr` into the user project files via a context clause. GNAT will search and locate the path to this file if the environment variable ``GPR_PROJECT_PATH`` is defined to include :file:`/lib/gnat/` (this should be the case by default). It is strongly recommended that you use the bindings provided with the product rather than alternatives, as the provided bindings have been extensively tested for correctness. To access and use the apex project in a user project file: .. code-block:: gpr with 'apex'; project My_Project is ... for Source_Dirs use (); -- To discriminate on eg variable Runtime: case apex.Runtime is when "cert" => ... end case; end My_Project; .. rubric:: Using Ada Processes The standard way to define an APEX process in C is to create an attributes structure containing the process information (i.e. its name, entry point, priority, etc.). This record is then passed to the APEX ``CREATE_PROCESS`` routine. It works the same way in Ada. One defines a record of type ``Apex_Processes.Process_Attribute_Type`` describing the process. This record is then given as a parameter to ``Apex_Processes.Create_Process``. .. index:: Secondary Stack The APEX / Ada thick binding adds the defaulted parameter ``Secondary_Stack_Size`` to the procedure ``Apex_Processes.Create_Process``. In order to support the use of unconstrained function results, a data structure called the secondary stack is allocated to each APEX process that executes Ada code. (The secondary stack is actually a mark/release heap). This parameter allows the user to specify the size of this data structure (in bytes). If the parameter is defaulted, the APEX binding will allocate a secondary stack with a size equal to one-fourth of the requested stack size for the process. This data structure is allocated out of the primary stack, which will have had its allocation increased by one-fourth, so that the original requested stack allocation is honored. Calls to ``Apex_Processes.Get_Process_Status`` will return the size of the primary stack minus the size of the secondary stack (i.e. the size originally requested by the application developer). Note that an APEX process created via the APEX Ada binding can query its secondary stack 'high-water mark' using ``GNAT.Secondary_Stack_Info.SS_Get_Max``. This package is described in the :title:`GNAT Reference Manual`. All APEX processes that execute Ada code, regardless of whether their bodies are largely in C or C++, must use the Ada binding routines to create the process or query its process status, or to create a process-level health monitoring handler. These routines and their parameters (for C) are described in :file:`apex_processes.ads` and :file:`apex_health_monitoring.ads`. .. _Implementation_of_a_Last_Chance_Handler: Implementation of a Last Chance Handler ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Ada run-time library provided with the GNAT for Helix includes the concept of a last chance handler. This routine is called when an application terminates due to the occurrence of an unhandled Ada exception. The light run-time library requires an implementation of the last chance handler to be written by the application developer. The handler can be written by the application developer in order to provide such capabilities as raising an error to the health monitor, for example. The following treatments apply: .. index:: Last_Chance_Handler The profile of the Light last chance handler is: .. code-block:: ada procedure Last_Chance_Handler (Source_Location : System.Address; Line : Integer); pragma Export (C, Last_Chance_Handler, "__gnat_last_chance_handler"); .. index:: pragma Discard_Names The ``Source_Location`` parameter is a C null-terminated string representing the source location of the ``raise`` statement, as generated by the compiler, or a zero-length string if ``pragma Discard_Names`` is used. The ``Line`` parameter (when nonzero) represents the line number in the source. When ``Line`` is zero, the line number information is provided in ``Source_Location`` itself. Again, any Ada or C routine that exports ``__gnat_last_chance_handler`` and matches the designated profile may be used here. The recommended mechanism for replacing the last chance handler is to execute ``gprbuild`` or the corresponding C compiler on the file providing ``__gnat_last_chance_handler``. The resulting ``.o`` file should then be included in the linker directives for the Ada main application. .. _Process-Level_Health_Monitoring_Handler: Process-Level Health Monitoring Handler ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The APEX routine ``Apex_Health_Monitoring.Create_Handler`` allows an application developer to provide a process-level handler for health monitoring events. The task that executes this routine will receive the requested stack size, plus 1/4 of the requested size for a secondary stack. This allows such routines to be written in Ada, though they need not be.