Proposed changes to class service::dynalink::Linker and related stuff.

Messages

Error messages about undefined symbols, conflicting symbols, etc., will have the symbols demangled if needed.

Up to 20 undefined symbols per module and up to 20 conflicting symbols per load attempt will be reported via Logger().error(). There will still be just one exception thrown in each of these cases but it will contain a generic failure message rather than a symbol.

Symbol lookup

The lookup() member function will by default mangle names given it before performing the search. A new, optional boolean flag will suppress this.

Module filenames

The branch information will be dropped from module filenames, e.g., foo.1.2.main.so will become foo.1.2.so.

References to modules by name

Member functions that need module references will take them as module names rather than as pointers, e.g., drop(). The basic name of a module is the part of the POSIX basename of its file that comes before the first period. For example, the basic name of /nfs/x/y/foo.1.2.so is "foo". In cases where the linker is presented with a module already in memory the user will provide the filename or basic name.

Debugging support

The relocate() member function will have a new boolean flag, "replace", that will cause the linker to automatically drop and, if possible, deallocate any current module of the same name. This will make the shell's runTask command simpler since it will not have to create its own linker instances nor keep track of modules.

relocate() will also have a new variant that takes a filename (for example, NFS or FAT32) for the module instead of a pointer to a memory region. This will become the preferred variant and will further simplify runTask.

A new member function getModuleInfo(const char* modname) will take the module's basic name and return a struct ModuleInfo which will contain at least the following:

  • Address of the text segment. const void*.
  • Address of the R/O data segment. const void*.
  • Address of the R/W data segment. void*.
  • Address of the entry point (or null if none). void (const*)(void*).

A GDB script will be able to use this information to create a valid add-symbol-file command which will now have to have the following form:

add-symbol-file <filename on host> <text addr> -s .rodata <rodata address> -s .data <data address>

Module segments

A module will be, as it is now, an ELF sharable object.

A module will contain three segments which hold all the module's loadable code and data:

  • An executable text segment with section name ".text".
  • A R/O data segment with section name ".rodata".
  • A R/w data segment with section name ".data".

Information for dynamic linking will be in a segment of type DYNAMIC which is nested inside .rodata.

No ELF header or ELF structural tables will be inside .text, .rodata or .data. They might turn up in their own segments which will not be moved out of the module's original ELF image.

The special section "rcework" will no longer be present. The linker will get the information it contained from the ELF header and symbol tables.

.bss et al.

The linker will now have to zero out the uninitialized (BSS) variables in .rodata and .data when a module is first loaded (when relocate() is called). These variables will no longer take up space in the ELF object.

Memory allocation

The Linker constructor will take three new arguments which are references to memory allocators which use the package service::memory. The allocators ar used for text, R/O data and R/W data, respectively. Each allocator has an alloc() member function which the linker uses to obtain descriptors for memory regions into which it can write. Each allocator also has a close() member function which the linker calls when it has copied all the relevant module content into a region. For each of the three loadable module segments the linker will do roughly the following:

Allocator a = the right allocator for this segment;
Descriptor d = a.alloc(segment.size);
memcpy(d.address, segment.address, segment.size);
Fixup the segment copy at d.address;
a.close(d);

For all the allocators the alloc() member function must return a descriptor to a region that is writable and at least as large as requested. The region must have no valid I-cache or D-cache entries.

For text regions the close() operation must store to memory and then invalidate all the region's D-cache entries before making the region executable and read-only. For R/O data regions it must at least store to memory the regions's D-cache entries before making the region read-only; it need not invalidate the entries. For R/W data close() need neither store nor invalidate the D-cache entries.

  • No labels