Relocatable Module System

Introduction

The Revolution relocatable module system allows the efficient use of main memory by dynamically loading and unloading program modules within the game program. Unlike the dynamic link library of other operating systems, game programs are responsible for allocating and deallocating main memory and loading modules from the disk when using the Revolution relocatable module system.

The relocatable module system consists of a single static module (elf file) and multiple relocatable modules (rel files). Once the boot ROM loads the static module of the program, the static module can control the memory location of the relocatable module. The static module is built as a normal elf file. The static module contains common functions and common variables referenced by the relocatable modules.

Relocatable modules can call functions and refer to the variables defined in static modules. In addition, relocatable modules can call the functions of other relocatable modules loaded into main memory and reference variables. When the module is loaded, directly correcting the code and data sections of the modules resolves referencing among modules. For better runtime performance, no indirect table references can be made once cross module references are fixed. For example, function calls among modules are executed via a single branch instruction (bl Broadway instruction) in the same way as statically-linked functions.

A relocatable module is written in exactly the same manner as the main C/C++ code, except that it is built from a partially-linked elf file (plf). A plf file contains unresolved external symbols and debug information. The makerel tool provided with Revolution SDK converts the plf file into a relocatable module file used exclusively for Revolution. Relocatable module files specific to Revolution contain only normal program code, a data section, and a relocation instruction table to minimize the module size and increase efficiency during runtime. The relocation instruction table includes an 8-byte relocation instruction for code revised during runtime and for each data section. Each relocation instruction consists of a modified location offset, relocation type, target section number, and addend. During execution, a symbol table search that takes a long time is not carried out. (Symbol tables are removed by the makerel tool.)

The following sections describe how to make and use Revolution relocatable modules.

Making Relocatable Modules

Follow these five steps to make relocatable modules:

1.    Create the partially-linked elf files

The relocatable module system uses the partial link option of the static linker. Each relocatable module is made from a separate partially-linked file. Using the partial link option (-r), the linker can create an elf file that includes external reference symbols that are unresolved. The relocatable module system resolves these unresolved symbols during runtime.

Three optional functions can be defined for a relocatable module: _prolog, _epilog, and _unresolved_prolog and _epilog can be called from the static module or from the other relocatable modules loaded into the main memory. _unresolved is called when a function in the module, not yet loaded into the main memory, is called.

Note:The relocatable module can't have a small data section. The small data section base registers (r2 and r13) are reserved for the static module. To prevent the compiler from generating small data sections, specify the compiler options -sdata 0 -sdata2 0 when compiling the relocatable modules.

2.    Make FORCEACTIVE lists from plf files

Execute the makerel tool to generate a list of functions that the linker should not dead strip. At this time, a list of all partially-linked files is passed as an argument to the makerel tool. The list of functions is saved in the file import.lst.

% makerel partial-link-file.plf ...

'The contents of import.lst are merged into the linker command files.

The linker command file for the static module is generated by attaching the FORCEACTIVE list to the <Revolution/eppc.$(PLATFORM).lcf> file. The FORCEACTIVE list should contain all the functions listed in import.lst, OSLink and OSUnlink.

The linker command file for the relocatable module is generated by attaching the FORCEACTIVE list to the <revolution/eppc.lcf> file. The FORCEACTIVE list should contain all the functions included in import.lst, _prolog, _epilog and _unresolved.

3.    Build the program static modules

Link the program static module to the linker command file generated in the previous step. (Do not specify the partial link option.) All the static libraries should be linked with the static module.

4.    Rebuild the partially-linked elf files

Relink each relocatable module with the generated linker command file. This time, use the -strip_partial linker option to remove the functions and variables that the modules from the ELF file do not reference.

Additionally, the relocatable module can be reduced in size if the templates and weak symbols generated by the unexpanded inline functions use the EXCLUDEFILES linker command pseudo instruction.

5.    Generate the relocatable module file and module name string table file

Run the makerel tool to generate relocatable module files (rel) and the module name string table file (str). Supply, as arguments, the list of plf files and the elf file name of the static module of the program.

% makerel exec-type-file.elf partial-link-file.plf ...

The generated relocatable module files are copied to the game disc. The runtime debugger uses the module name string table to search the host PC for the partially-linked file that was used to generate the relocatable module. To use the debugger, you must also copy the module name string table file to the game disc.

Using Relocatable Modules

First, note that allocating and deallocating memory for loading relocatable modules and loading relocatable modules from game discs are performed in the game program. The relocatable module loaded into main memory will have type OSModuleHeader data followed by the module code and data sections.

// Load module (32 byte aligned)
DVDOpen("module.rel", &fileInfo);
length = OSRoundUp32B(DVDGetLength(&fileInfo));
module = (OSModuleHeader*) OSAllocFromArenaHi(length, 32);
DVDRead(&fileInfo, module, (s32) length, 0);

The OSModuleHeader consists of the common OSModuleInfo structure and other header specific data. The OSModuleHeader version number can be determined by OSModuleInfo.version. The current version is version 2.

// Allocate bss area (32 byte aligned)
bss = OSAllocFromArenaHi(module->bssSize, 32);
OSLink(&module->info, bss);
((void (*)(void)) moduleHeader->prolog)(); 

The OSLink function uses the pointer to OSModuleInfo structure and the pointer to the region to be used as the bss region (the section initialized by 0) as arguments. The OSLink function links the specified module with the static module, as well as with the other relocatable modules already loaded into the main memory. The required sizes of bss region are defined in OSModuleHeader.bssSize. The OSLink function initializes the specified bss region by 0. Whether to call the prolog function (a member variable of the OSModuleHeader structure) after linking the module is determined in the game program. The OSLink function only converts prolog and epilog from the offset within the section to the actual address in the virtual address space.

((void (*)(void)) moduleHeader->epilog)();
OSUnlink(&module->info);

If the loaded module becomes unnecessary, call OSUnlink so that the occupied memory space can be used for another purpose. Whether to call the epilog function (a member variable of the OSModuleHeader structure) immediately before unlinking the module is determined in the game program.

To use the program debugger, first the module name string table that the makerel tool generates must be loaded into main memory and the start address must be registered by calling OSSetStringTable.

Specifying Relocatable Modules

The OSLinkFixed function links the specified module and deallocates some memory used by the relocatable module. Once the module is linked with OSLinkFixed, the memory space after the fixSize of the OSModuleHeader structure can be used for any purpose (e.g., for the bss region). The memory copy of the module, however, cannot be reused with OSLink[Fixed] even after it is unlinked.
Note: The location specified by fixSize is converted from the file offset to the virtual address when the module is linked. 

The following code is an example of how to use the memory region after the location specified by fixSize as the module's bss region.

// Allocate bss area
bss = (u8*) module + module->fixSize;
bss = (u8*) OSRoundUp32B(bss);
if (bss + module->bssSize < OSGetArenaLo())
{
    OSSetArenaLo((void*) OSRoundUp32B(bss + module->bssSize));
}
else
{
    OSAllocFromArenaLo(module->bssSize - ((u8*) OSGetArenaLo() - bss), 32);
}
OSLinkFixed(&module->info, bss);

Required Alignment for Relocatable Modules

Relocatable modules and bss sections have address-alignment constraints. A relocatable module must be aligned with the maximum alignment value required in the following sections: init, text, ctor, dtor, rodata, and data. A bss section must be aligned with the maximum alignment value required by data item within the bss section. Typically, relocatable modules and bss sections require 8-byte alignments. However, if the ATTRIBUTE_ALIGN(32) macro is used, the requirement is extended to 32-byte alignment. 

Note: The relocatable module file version number is 2. The alignment constraints required for .rel files can be checked by using the reldump command line tool provided under /X86/bin. If OSModuleInfo.version is 2, the OSLink function also checks alignment constraints during execution.

Sample Program

A sample program that uses the relocatable module system is under /revolution/build/demos/reldemo. makefile in this sample automatically executes the procedure to make relocatable modules.

Note:Restrictions on the Placement of Modules

The Broadway split instruction (bx) only supports jumps in a range of +/1 32 Mbytes. As a result, the module loaded in internal main memory (MEM1) cannot call the REL module placed in external main memory (MEM2). The converse is also true.

Note:Using C++ Global Constructors in Relocatable Modules

If using C++ global constructors in relocatable modules, you must manually call global constructors and destructors from the module _prolog and _epilog, respectively, as shown in the following code: 

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*voidfunctionptr) (void); /* ptr to function returning void */
__declspec(section ".init") extern voidfunctionptr _ctors[];
__declspec(section ".init") extern voidfunctionptr _dtors[];

void _prolog(void);
void _epilog(void);
void _unresolved(void);

#ifdef __cplusplus
}
#endif

void _prolog(void)
{
    voidfunctionptr *constructor;

    /*
     *  call static initializers
     */
    for (constructor = _ctors; *constructor; constructor++) {
        (*constructor)();
    }
}

void _epilog(void)
{
    voidfunctionptr *destructor;

    /*
     *  call destructors
     */
    for (destructor = _dtors; *destructor; destructor++) {
        (*destructor)();
    }
}

In addition, make sure that each relocatable module is linked to global_destructor_chain.c (under $(CWFOLDER)/PowerPC_EABI_Support/Runtime/Src). Linking to this file guarantees that each global destructor is called every time the _epilog function of the module is called. Otherwise, module global variables in each module are linked to the global destructor chain in the static module, and unless the static module exits, destructors are never called.Be aware that since Runtime.PPCEABI.H.a uses small data sections and contains unnecessary functions, relocatable modules can't be linked to it.

Revision History

2006/03/01 Initial version.


CONFIDENTIAL