Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The "run" command allows you to specify the  the Task name (up to four characters), scheduling priority and the size of the stack in bytes [run]

Anchor
refRun
refRun
.

From C or C++ code

The C function lnk_load() is the main entry point of the dynamic linker [linkerCall]

Anchor
refLinkerCall
refLinkerCall
. You give it the name of the shared object you want to load. The linker will attempt to find it and any other shared objects it needs according to the rules laid out in the next section.

...

Though each shared object contains a list of the sonames of all the other shared objects it depends on, the so-called needed-list, this doesn't tell the dynamic linker just what the object requires from the other objects. It could be user data, functions, classes, etc. Each of these items has a symbolic name which appears in a symbol table built into the shared object that defines them. The object that needs to use them also has the symbolic names in its table, though marked as "undefined", that is to say "not defined in this shared object". There's no indication of which shared object is the definer. Instead, for each undefined symbol in a newly-loaded object the dynamic linker searches for a definition in each of the objects on the first object's needed-list [scope]

Anchor
#refScope
#refScope
, taking the first definition that it finds. It doesn't check for duplication. The linker then puts the address of the definition it found into all the places in the first shared object that require it.

Info
titleSearch scope

For any given shared object the set of objects searched to satisfy its undefined references is called its scope. For DAT code the scope is the original shared object followed by the objects named in its needed-list. It may seem strange to search an object for its own undefined but it's necessary to handle some unusual cases. This scope rule is much simpler than the one employed by a Linux dynamic linker.

Initialization of newly loaded shared objects

The following initializations are performed in the order listed here. They're only performed once when the object is loaded.

Uninitialized variables

A shared object may define statically allocated variables that are given no explicit initial values in source code. Such variables take up no space in the shared object file; there's only a count of how much space they need.  Dynamic linking has to allocate space for these variables and, in accordance with the C and C++ standards, initialize that space to all zeros.

...

A shared object is divided into many  many named "sections" some of which have special meaning for the dynamic linker. One of these is named ".init" and contains pointers to functions that must be called before the shared object can be considered usable. Normally the .init section is filled by the compiler and contains pointers to functions that run static C++ constructors. With the "section" attribute attribute [attr]

Anchor
refAttr
refAttr
you can place pointers of your own in the .init section.

...

...

Attributes are a language extension offered by GCC.

Installation

If the shared object was loaded as a dependency, contains a global variable named lnk_options and that variable's (integer) value has the bit LNK_INSTALL set then the shared object's soname and location are recorded in the table of installed objects.

...

A shared object file's loadable content is divided into a small number of "segments". Each segment has a set of permission flags: (R)eadable, (W)riteable ans e(X)ecutable. In shared objects built for DAT systems there's normally one RX segment containing instructions and read-only data and one RW section containing non-constant data. The dynamic linker uses the CPU's memory management unit (MMU) to set the access type of the memory allocated to each segment to match the segment's permission flags.

Notes

Anchor
noteELF
noteELF
[ELF] The file format used is the standard Executable and Linkable Format used by Linux systems. The ELF standard defines three kinds of objects: compiler output (not directly executable), executable main programs and executable dynamic shared objects. DAT systems allow the loading and execution only of files in the third format.

Anchor
noteScope
noteScope
[scope] For any given shared object the set of objects searched to satisfy its undefined references is called its scope. For DAT code the scope is the original shared object followed by the objects named in its needed-list. It may seem strange to search an object for its own undefined symbols but it's necessary to handle some unusual cases. This scope rule is much simpler than the one employed by a Linux dynamic linker.

Anchor
noteAttr
noteAttr
[attr] Attributes are a language extension offered by GCC.

Anchor
noteRun
noteRun
[run] Shared object files containing Task code are given the filename extension ".exe". Argument strings may be passed to a task using an interface resembling the argc/argv interface for C main programs. Here's a run of our standard example task which prints its arguments and then suspends itself.

[/] # run -N foo -P 200 -S 10240 system:hello.exe -- doe re mi 
2014/06/04 18:20:12.081941: hello_task was called with 4 arguments @ AA0D25C: system:hello.exe doe re mi
[/] # stop foo
2014/06/04 18:20:59.750071: ending hello_task
[/] # help run
run          - Usage:  run [[-N taskName] [-P priority] [-S stacksize]] <namespa
            ce>:<exe> [-- task arguments]
              Execute the code in file pointed to by namespace:exe
              with RTEMS task name as <taskName> (4 characters)
              Optional arguments:
              -N taskName:     RTEMS task name       
              -P <priority>:   RTEMS priority.       
              -S <stacksize>:  RTEMS stack size.     
 

Anchor
noteLinkerCall
noteLinkerCall
[linkerCall] In this example code we load and execute a task in much the same way that the "run" shell command does, though with much simpler error handling: dbg_bugcheck() prints a message and then calls the RTEMS fatal error handler. Note that the "extra" argument can be NULL if you don't want more information than the status code for an error. See the header file "elf/lnkStatus.h" for the definition of the type lnk_Status.

Code Block
languagecpp
#include <inttypes.h>
#include <rtems.h>

#include "debug/print.h"
#include "elf/linker.h"
#include "task/Task.h"

void launch(void) {
  uint32_t status = STS_K_SUCCESS;
  Task_Attributes myAttr;
  myAttr.name = rtems_build_name('M', 'Y', 'T', 'K');
  myAttr.stack_size = 40 * 1024;
  myAttr.priority = 200;
  myAttr.attributes = RTEMS_DEFAULT_ATTRIBUTES;
  myAttr.modes = RTEMS_NO_PREEMPT;
  myAttr.image = NULL;
  myAttr.argc = 0;
  myAttr.argv = NULL;
  Ldr_elf* mytask = lnk_load("myspace:mytask.exe", &myAttr, &status, NULL);
  if (!mytask) {dbg_bugcheck("mytask.exe did not load. DIE!\n");}
  rtems_id id;
  Task_status tstat = Task_Run(mytask, &myAttr, myAttr.argc, myAttr.argv, &id);
  if (tstat) {dbg_bugcheck("mytask.exe did not run. DIE!\n");}
  // At this point you'll have launched a new thread running your task code.
}