Relocatable Module System

Introduction

Using the Wii relocatable module system allows the efficient use of main memory by dynamically loading and releasing program modules during the game program. Unlike the dynamic link library of other operating systems, the Wii relocatable module system places the responsibility of allocating and deallocating main memory and loading modules from the disk on the game program.

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

Relocatable modules are able to call the functions and reference the variables contained in the static module. In addition, relocatable modules can call the functions and reference the variables of other relocatable modules already loaded into main memory. Referencing across modules is resolved by directly revising the code and data sections of a module when it is loaded. For this reason, once cross-module references are resolved, the program runs more efficiently without any unnecessary indirect references during runtime. For example, function calls across modules are executed via only a single branch instruction (bl Broadway instruction) in the same way as statically-linked functions.

A program using relocatable modules (REL files) can be written in exactly the same manner as a normal C or C++ program. However, relocatable modules are created from partially-linked ELF files (PLF files). A PLF file contains unresolved external symbols and debug information. The makerel tool provided with the Revolution SDK converts a PLF file into a relocatable module file for exclusive use by the Wii. To minimize the module size and increase efficiency during runtime, Wii-exclusive relocatable module files contain only normal program code, a data section, and a relocation instruction table. The relocation instruction table includes an 8-byte relocation instruction for each section of code and data revised during runtime. Each relocation instruction consists of a modified location offset, relocation type, target section number, and addend. A symbol table search, which takes time, is not carried out during runtime (symbol tables are removed by the makerel tool).

The following sections describe how to build and use Wii 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. By using the partial link option (-r), the linker can create an ELF file that includes unresolved external reference symbols. The relocatable module system resolves these unresolved symbols during runtime.

Each relocatable module can define three optional functions: _prolog, _epilog, and _unresolved. _prolog and _epilog can be called from static modules and from other relocatable modules loaded into main memory. _unresolved is automatically called in place of functions in modules that have not yet been loaded into main memory.

Note:The relocatable module cannot have a small data section. The reason is because 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. Generate FORCEACTIVE lists from all partially linked 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 function list is saved in the file named 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, as well as _prolog, _epilog, and _unresolved.

3. Build the program static modules.

Link the program's static module to the linker command file generated in the previous step (do not specify the partial link option). Link all static libraries to the static module.

4. Rebuild the partially-linked ELF files.

Relink each relocatable module with the generated linker command file. This time, specify the "-strip_partial" linker option to delete functions and variables from the ELF file that are not referenced by any module.

Additionally, the relocatable module can be made smaller by deleting the templates and the weak symbols generated by unexpanded inline functions using the EXCLUDEFILES linker command pseudo instruction.

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

Run the makerel tool to generate relocatable module (.rel) files and the module name string table (.str) file. At this time, pass the list of all partially-linked files and the ELF file name of the static module as arguments to the makerel tool.

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

Copy the generated relocatable module files to the game disc. The module name string table is used by the runtime debugger 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 by the game program. The relocatable modules loaded into main memory are the data following the OSModuleHeader-type data, and consist of 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 data specific to each header version. The OSModuleHeader version number is 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 arguments of the OSLink function are pointers to OSModuleInfo and the region being used as the bss area (zero-initialized data section). The OSLink function links the specified module with the static module or with relocatable modules that have already been loaded. The required size for the bss area is given by OSModuleHeader.bssSize. The OSLink function zero-clears the specified bss area. The game program determines whether to call the prolog function (a member variable of the OSModuleHeader structure) after linking the module. The OSLink function only converts prolog and epilog from the offset within the section to the actual address in virtual address space.

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

If a loaded module becomes unnecessary, call the OSUnlink function so that the memory used by that module can be used for another purpose. The game program determines whether to call the epilog function (a member variable of the OSModuleHeader structure) immediately before unlinking the module.

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

Specifying Relocatable Modules

The OSLinkFixed function links the specified module and releases part of the memory that was used by the relocatable module. Once the module is linked with OSLinkFixed, the memory space after the position specified by fixSize in the OSModuleHeader structure can be used for any purpose (for example, for the bss area). However, modules in memory cannot be reused with OSLink[Fixed] even if they are unlinked. Note that the fixSize mark is converted to the virtual address from the file offset 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 section.

// 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 Conditions for Relocatable Modules

Both relocatable modules and bss sections have address alignment constraints. A relocatable module must be aligned to the maximum alignment value required by the following sections: .init, .text, .ctor, .dtor, .rodata, and .data. A bss section must be aligned to the maximum alignment value required by the data items within the .bss section. Typically, both relocatable modules and bss sections require 8-byte alignment. However, if the ATTRIBUTE_ALIGN(32) macro is used, the required alignment extends 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 in /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 in /revolution/build/demos/reldemo. The makefile in this sample automatically executes the procedure to make relocatable modules.

Note: Restrictions on the Placement of Modules

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

Note: Using C++ Global Constructors in Relocatable Modules

When using C++ global constructors in relocatable modules, you must manually call the global constructors and destructors from the module's _prolog or _epilog. Code examples for each case follow below.

#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 (in $(CWFOLDER)/PowerPC_EABI_Support/Runtime/Src). Linking to this file guarantees that the module's various global destructors are called every time the module's _epilog function is called. Otherwise, module global variables in each module are linked to the global destructor chain in the static module, and if no static module exists, destructors are never called.?Because Runtime.PPCEABI.H.a uses small data sections and contains unnecessary functions, relocatable modules cannot be linked to it.

Revision History

2006/03/01 Initial version.


CONFIDENTIAL