WHOPR Driver Design
This document proposes a driver design for WHOPR based on the linker. Although this document focuses on gold, a similar approach can also be implemented in GNU ld.
Design Philosophy
- The implementation provides complete transparency. Developers should be able to take advantage of LTO without having to modify existing build systems and/or Makefiles, all that's needed is to add an LTO option (-flto).
- Transparency is achieved through tight integration with the linker. Ideally, the linker communicates with LTO via a shared library (plugin), eliminating any dependencies between the source bases of linker and LTO, but other callback methods are also possible.
- For scalability, we expect that after IPA multiple backend invocations may/will follow. The system should be flexible enough to accommodate existing parallel build infrastructures.
- Debugability - debugging IPA and post-IPA problems can be complicated. The design offers ways to simplify the overall strategy.
Why in the Linker?
As of this writing, the pre-ld driver collect2 performs the LTO file identification. However, this is sub-optimal. The benefits of driving LTO from the linker are:
- The linker performs full symbol resolution. Therefore, it will only bring in objects that are necessary. This can greatly reduce build and library extraction times.
- Several build systems use ld -r to build components and/or shared libraries.
- The linker properly handles archives
The linker knows which functions and globals are externally referenced. LLVM's IPA page provides an extended example on why the integration in the linker is necessary to perform precise dead function elimination. The same chain of arguments holds for globals. LTO needs to know about externally referenced symbols.
- Less work - currently, collect2 needs to fork/exec 'nm' on every input file to determine whether it contains IR, which is not optimal.
Process Structure
The WHOPR design document outlines three drivers, LGEN (front-end driver), WPA (actual IPA), and LTRANS (backend / code generation). This section desribes how they call each other.
Front End: LGEN
LGEN is the independent FE driver, which produces files containing IR and which can be invoked via any parallel build infrastructure. Generation of IR is controlled by option -flto.
TODO: Right now, LGEN puts a specifically named symbol in the file to mark it as containing IR. This will change and a specifically named section will be added instead.
Link: Collect2, gold, plugin
The link is either started with the gcc/g++ drivers (which call collect2, which calls ld), or by calling ld (gold) directly. In the gcc/g++ drivers and in collect2, files are still treated as regular ELF files, nothing needs to be changed. This approach changes the currently implemented strategy on the LTO branch. collect2 fork/exec's the linker.
The linker is passed a command line argument specifying a plugin. A plugin is part of the compiler, so there will be one for each version of gcc. All of them should implement a fixed set of function interfaces. These functions are described below.
The linker performs regular symbol resolution. For each object file it touches, it calls a specific function in the plugin. This function returns 1 if it intends to claim a file (e.g. it contains IR), and 0 if it doesn't. The function is passed a pointer so that the plugin doesn't need to reopen the file or understand archives. If it claims the file, it also calls a linker interface to provide the global symbols.
The linker marks each claimed file in its internal data structures and continues with regular symbol resolution, until all references have been resolved.
At this point, the linker calls a function in the plugin to initiate the WPA phase, which will perform the optimizations and generate new object files to include in the link.
Plugin
The plugin munches the options passed to it. It already has a list of all input object files containing IR, as well as a list of the external references. Note, we could also pass in the list of all other regular object files to it. Some of these files might be located in an archive.
The plugin performs these actions:
- It creates and manages a temporary directory for all intermediate files.
- It manages the DEBUG facility. For example, to debug post-WPA problems, one needs the various outputs of WPA. In other words, intermediate files need to be kept. DEBUG should allow naming temporary directories, and control other DEBUG related behavior (e.g. dumping options).
- It extracts IR object files from archives and places them in the tmp directory. This may be done via fork/'exec'ing 'ar x ...', or directly calling linker helper functions. To avoid name collision, every generated and/or copied object file gets a running serial number. This way, when two files or archives from different directories participate in a link, no further name collision will occur.
- The plugin creates a REDO script which contains the exact command lines for the original link and WPA, as well as the environment as it was during the original build. The WPA command line contains all the options and the extracted IR files. REDO will also build an ld command line where archives are replaced with the extracted object files. This REDO script allows restarting WPA, and restarting the final link (with some magic). The redo script is essential for automatic triaging.
- If automatic triaging is used to identify performance regressions, a subtle corner case may arise related to code layout. This will be addressed later.
- The plugin constructs the command line for WPA (options + IR files) and fork/exec's it.
- The plugin "collects" resulting real object files and feeds them back to the linker.
Inter-Procedural Optimization: WPA
WPA parses command-line and does its thing. It will generate 1..N post IPA IR files for LTRANS. Depending on the model, the post-IPA IR files don't need to have symbol table. Single post-IPA files or groups of such files will be passed to LTRANS invocations. These invocations are independent and can be parallelized. WPA will create a list containing these file groups. For each group a list of specific command-line options to LTRANS can be specified, as well as its designated output file name, e.g.:
0.o base.a.threads.o -O3 1.o base.a.walltime.o inline-candidate-1.o inline-candidate-2.o 2.o myapp.o 2.o
WPA calls the parallel "LTRANS magic", which, by default, is a script in a default location, let's call it ltrans_ctrl. Command line options should allow to specify alternative scripts. The location of the tmp directory, the name of the control file, as well as all original command line options to WPA are being passed to ltrans_ctrl. It is ltrans_ctrl's role to support various existing build systems:
local build - parallel make
For local builds on multi-core machines, parallel make can be used efficiently, as it already does process management. For this scenario, ltrans_ctrl may call a script ltrans_parallel_make, which
- identifies the current platform (uname -a), finds and identifies 'make'
- generates a Makefile
invokes make -s -j x -f Makefile
To customize LTO for a specific installation, ltrans_parallel_make can be customized using the output from getconf _NPROCESSORS_ONLN to specify parallelism x as a default, and to use an environment variable to allow overriding. The generated Makefile might look like this:
goal: 0.o 1.o 2.o 0.o: base.a.threads.o ltrans -O3 -o 0.o base.a.threads.o 1.0: base.a.walltime.o inline-candidate-1.o inline-candidate-2.o ltrans -o 1.o base.a.walltime.o inline-candidate-1.o inline-candidate-2.o ...
This mechanism works for regular make and gmake, for which only the parameters need to change. There are issues that all generated file must be visible on all build machines for the dependency mechanism to work. This can usually be achieved by making sure the build happens on NFS, or by introducing pseudo targets and remote copy operations in the Makefile.
distributed build - distcc TBD - but should be similar. The related files will be copied to build server, ltrans will be invoked there, the resulting object file will be copies back. As a matter of fact, if there was an LTRANS wrapper script for that, the Makefile infrastructure could be reused. The wrapper script would have to:
- for a given target, select a build server.
- generate unique temporary name and directory on build server
- copy involved files to this location
- secure shell invoke ltrans with proper parameters
- scp back the resulting real .o
- srm tmp directory.
Final Link - ld
After all real object files have been generated, these files need to be passed to the linker via a plugin / linker interface that explicitly adds the new files to the linker's internal data structures.
Cleanup
The plugin cleans up all temporary directories, unless directed not to.
Plugin Interfaces
We're planning to use a fairly common design for the plug-in interface that is easily extensible with forward compatibility. This plug-in interface won't be specific to LTO, although the initial set of events and hooks will be. The plug-in itself will be a shared library with a single exported interface void onload(struct ld_plugin_tv *tv). The flow of control is as follows:
A command-line linker option directs the linker to load the plug-in library via dlopen, and the linker calls dlsym to find its onload function.
The linker calls the plug-in's onload function, passing it a transfer vector that provides access to the interfaces that the linker provides to plug-in libraries. The transfer vector is an array of structures, each with a tv_tag field that labels the entry, and a second field (a union of tv_ptr and tv_val) that contains a pointer or integer value.
Each interface provided by the linker is exported to the plug-in by an entry in this vector; the tv_tag field names the interface, and the tv_ptr field is a function pointer. The prototype for each interface is defined below. The transfer vector may also contain additional entries (e.g., the interface version number or the gold version number) that use the tv_val field.
The transfer vector is terminated by an entry with tv_tag = 0.
- The plug-in's start function then scans the transfer vector, and locates the interfaces it intends to use. Some of these interfaces will be registration interfaces, which allow the plug-in to register its interest in certain events. At this time, the plug-in calls these registration interfaces to register its interest in the "claim input file" event, in the "all symbols read" event, and in the "end-of-link cleanup" event, then returns to the linker.
- The linker then proceeds with the link as normal. As it encounters each input file, it calls the "claim file" event handler, passing it an open file descriptor, a filename, an offset within the file (in case the input file is an archive member), and a file handle. If the input file is an IR file, the event handler calls the linker's "add symbols" interface to supply the linker with a list of the file's global symbols, and returns a code indicating that it has claimed the file. If the input file is not an IR file, the event handler returns with a code indicating that it has not claimed the file, and the linker processes the file normally. Whether the file is claimed or not, the linker maintains responsibility for opening and closing the file as necessary.
- When the linker reaches the end of the first pass, it then calls the LTO hook event handler that was registered, passing it all of the details (see Section 3 below) that it needs. If the plug-in needs read access to any of the original claimed input files during this phase, it must call the linker's "get input file" interface to get the file descriptor (which may need to be reopened), and the "release input file" interface when done.
- The LTO plug-in then performs its magic, resulting in one or more additional object files that need to be added to the link. Once these object files are available, the plug-in calls the linker's "add input file" interface to add each object file, then returns to the linker.
- The linker processes the additional object files, adding their defined global symbols to the symbol table as replacements for the "placeholder" symbols in the IR files, and completes the link.
- At the end of the link, the linker calls the cleanup event handler so that the plug-in can remove the temporary object files (if desired).
Claiming the IR Files
Currently, the IR files contain both generated code and IR, with a set of special .gnu.lto_* sections for each function that contain various bits of IR. The sections are marked as SHT_PROGBITS sections, but are not allocated (the SHF_ALLOC bit is zero). One of the principles of ELF is that sections requiring special treatment should be distinguished by a special section type or flag; the linker should never have to look for magic section names. (This principle isn't always upheld in practice, but I'd prefer not to contribute to the list of violations.) Thus, we plan to mark the IR sections with a special section type, SHT_GNU_IRBITS (= 0x6fff4952).
On its own, the linker will know nothing about this special section type, and will process such sections according to the standard ELF rules for unrecognized sections (we will set the SHF_EXCLUDE flag so the sections will not be copied to the output file). When the plug-in claims the file, however, the linker will not make any assumptions at all about the format of the file, and the plug-in will have complete responsibility for reading symbols and section contents.
When claiming an input file, the plug-in must provide the global symbols both defined and referenced by that file to the linker. To do this, it calls the "add symbols" interface, passing an array of symbols. Each symbol has the following fields:
- Name
- Version
- Defined/Weak Def./Common/Undefined/Weak Undef.
- Symbol Visibility
- Size (for Common symbols)
- COMDAT key (null if not COMDAT)
The "All Symbols Read" Event
At the end of the first pass, the linker has read the symbol tables and section tables of all its input files. It has a (mostly) complete symbol table, and has assigned text and data sections from the input files to output sections (with the exception of sections in IR files). It has not yet processed relocations or assigned final addresses to anything. In fact, it's not really at the end of the first pass, since it will actually be waiting for additional object files to process as replacements for the IR files already encountered.
Of particular importance, the linker knows at this point which symbol definitions come from which input files, and must communicate the following information to the plug-in:
- In the case of a symbol that is defined in more than one IR file, WPA will need to know which definition to use and which definitions to ignore.
- In the case of a symbol that is defined in both an IR file and a regular object file, WPA will need to know if a definition from a regular object file has pre-empted a definition in an IR file so that it can ignore the IR for that definition.
- For a symbol defined in an IR file, WPA may want to know whether there are any references to that symbol from regular object files, so that it can drop the definition entirely if it proves unnecessary (e.g., all calls to a particular function are inlined).
- For an undefined symbol, WPA may want to know whether the symbol is defined in the main executable or in a shared library, or if the symbol remains undefined.
For these purposes, symbols are identified solely by the object file handle and a symbol table index (assigned by the plug-in) within that object file.
When the linker calls the "all symbols read" event handler, the handler can then use the linker's "get symbols" interface to get the binding information about the global symbols for each input object file. That interface returns a resolution code for each symbol that was provided by the "claim file" handler. Each element of the array identifies the resolution of the corresponding symbol:
UNDEF (still undefined at this point)
PREVAILING_DEF (this is the prevailing definition of the symbol, with references from regular objects)
PREVAILING_DEF_IRONLY (this is the prevailing definition of the symbol, with no references from regular objects)
PREVAILING_DEF_IRONLY_EXP (as above, but the symbol is exported to the dynamic symbol table)
PREEMPTED_REG (this definition was pre-empted by a definition in a regular object file)
PREEMPTED_IR (this definition was pre-empted by a definition in another IR file)
RESOLVED_IR (this symbol was resolved by a definition in another IR file)
RESOLVED_EXEC (this symbol was resolved by a definition in a regular object linked into the main executable)
RESOLVED_DYN (this symbol was resolved by a definition in a shared object)
Any symbol marked PREVAILING_DEF must be defined in one object file added to the link after WPA is done, or an undefined symbol error will result. Any symbol marked PREVAILING_DEF_IRONLY may be left undefined (provided all references to the symbol have been removed), and the linker will not issue an error. Any symbol marked PREVAILING_DEF_IRONLY_EXP may be left undefined if the compiler determines that any dynamically-loaded shared object that would reference the symbol will provide its own definition of the symbol (e.g., the symbol is always defined in a COMDAT group).
A one-to-one correspondence between IR input files and new object files produced by WPA is not required. The linker will accept any number of additional input files, provided that all required symbols have been defined.
Accessing the IR Sections from WPA
Given that WPA must run as a separate process rather than entirely within the plug-in shared library, an interactive method for reading the IR through the plug-in API isn't feasible. In order for WPA to locate archive members easily, the linker provides filename and offset fields in the information passed to the "claim file" hook.
Command-Line Arguments for the Plug-In
Linker options of the form
-plugin pathname [ -plugin-opt arg1 ] [ -plugin-opt arg2 ] ...
causes the linker to load a plug-in library named pathname, and pass the arguments "arg1", "arg2", etc., to the plug-in. The linker adds each argument to the transfer vector, with a tv_tag value of TV_TAG_ARGUMENT and a pointer to the string in tv_ptr.
The arguments will appear in the transfer vector in the same order in which they appear on the command line. If there is more than one plug-in, plug-in arguments will be passed to the plug-in named in the nearest preceding -plugin option.
COMDAT Sections
When an IR input file contains a template instantiation or an out-of-line inline function definition that would normally be placed in a COMDAT group, the plug-in must identify any such symbol definition as a COMDAT symbol by providing the key symbol that the compiler would have ordinarily used for the COMDAT group. (This will often be identical to the symbol name itself.) The linker will then be able to do its standard COMDAT processing on regular input files, discarding all COMDAT groups from regular objects if a corresponding definition is found in an IR input file.
When the WPA phase produces the definition of the COMDAT symbol in a new object file, that definition should not be in a COMDAT group.
Interface Specifications
This section defines the types and function prototypes for the plug-in interfaces.
Status
Each function returns a status value of type enum ld_plugin_status:
enum ld_plugin_status { LDPS_OK = 0, LDPS_NO_SYMS, // Attempt to get symbols that haven't been added. LDPS_BAD_HANDLE, // No claimed object associated with a given handle. LDPS_ERR };
Plug-in Transfer Vector
The plug-in transfer vector is an array of struct ld_plugin_tv:
struct ld_plugin_tv { int tv_tag; union { int tv_val; void *tv_ptr; } tv_u; };
The supported values of tv_tag are:
enum ld_plugin_tag { LDPT_NULL, LDPT_API_VERSION, LDPT_GOLD_VERSION, LDPT_LINKER_OUTPUT, LDPT_OPTION, LDPT_REGISTER_CLAIM_FILE_HOOK, LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK, LDPT_REGISTER_CLEANUP_HOOK, LDPT_ADD_SYMBOLS, LDPT_GET_SYMBOLS, LDPT_ADD_INPUT_FILE, LDPT_MESSAGE, LDPT_GET_INPUT_FILE, LDPT_RELEASE_INPUT_FILE, LDPT_ADD_INPUT_LIBRARY, LDPT_OUTPUT_NAME, LDPT_SET_EXTRA_LIBRARY_PATH, LDPT_GNU_LD_VERSION, LDPT_GET_VIEW, LDPT_GET_INPUT_SECTION_COUNT, LDPT_GET_INPUT_SECTION_TYPE, LDPT_GET_INPUT_SECTION_NAME, LDPT_GET_INPUT_SECTION_CONTENTS, LDPT_UPDATE_SECTION_ORDER, LDPT_ALLOW_SECTION_ORDERING, LDPT_GET_SYMBOLS_V2, LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS, LDPT_UNIQUE_SEGMENT_FOR_SECTIONS, LDPT_GET_SYMBOLS_V3, LDPT_GET_INPUT_SECTION_ALIGNMENT, LDPT_GET_INPUT_SECTION_SIZE, LDPT_REGISTER_NEW_INPUT_HOOK, LDPT_GET_WRAP_SYMBOLS, LDPT_ADD_SYMBOLS_V2, LDPT_GET_API_VERSION, };
API_VERSION: The version number of the API itself. I don't think it's actually necessary, since the set of interfaces will be enumerated in the transfer vector, and incompatible changes to any interfaces will be made by obsoleting the old tag and assigning a new tag. Having a version number, though, does make people feel warm and fuzzy.
GOLD_VERSION: Identifies the gold linker as the host application, and provides its version number. The version number is an integer formed from the major and minor version numbers as major * 100 + minor.
LINKER_OUTPUT: Identifies that a link operation is taking place, and provides the type of output file.
OPTION: For passing a command-line option through to the plug-in. tv_ptr is a char * to the option string (minus whatever syntax told the linker to pass it through).
REGISTER_CLAIM_FILE_HOOK: Function pointer to the linker interface that lets the plug-in register a handler to be called for each input file, before the linker checks for an archive file or ELF file.
REGISTER_ALL_SYMBOLS_READ_HOOK: Function pointer to the linker interface that lets the plug-in register a handler to be called at the end of pass 1. This handler will implement the WPA phase, returning only when all LTRANS passes have completed and the new object files are available.
REGISTER_CLEANUP_HOOK: Function pointer to the linker interface that lets the plug-in register a handler to be called at the end of the link.
ADD_SYMBOLS, ADD_SYMBOLS_V2: Function pointer to the linker interface that lets the plug-in send symbols from the input file to the linker. This would be called from the CLAIM_FILE_HOOK routine. Alternatively, the plug-in can provide the symbols through a parameter when the CLAIM_FILE_HOOK function returns. I'm not sure yet which will work best from gold's or the plug-in's point of view. ADD_SYMBOLS_V2 provides also information about symbol type (a function or a variable).
GET_SYMBOLS, GET_SYMBOLS_V2, GET_SYMBOLS_V3: Function pointer to the linker interface that lets the plug-in get symbol information from the linker during the WPA phase. The GET_SYMBOLS interface will never return the disposition code PREVAILING_DEF_IRONLY_EXP, but the GET_SYMBOLS_V2 interface may. Newer clients that are prepared to handle this new disposition code should use the V2 interface; older clients will be unaffected by the API change. The only difference for V3 is that it returns LDPS_NO_SYMS instead of LDPS_OK for the objects we never decided to include.
ADD_INPUT_FILE: Function pointer to the linker interface that adds a new ELF object file to the link. This interface will be called during the WPA phase as each object file becomes available.
MESSAGE: Function pointer to the linker interface that lets the plug-in print a warning or error message, possibly terminating the link gracefully.
GET_INPUT_FILE: Function pointer to the linker interface that obtains a (possibly re-opened) file descriptor for a claimed input file. The plug-in library must use this interface if it needs access to one of the original input files during the WPA phase (during the "all symbols read" processing).
RELEASE_INPUT_FILE: Function pointer to the linker interface that releases a file descriptor for a claimed input file. The plug-in library must call this interface for each file descriptor obtained by the "get input file" interface. It must release all such file descriptors before returning from the WPA phase.
ADD_INPUT_LIBRARY: Similar to ADD_INPUT_FILE, but causes the liker to search for the library. The argument should be the name of the library as passed to the linker in the -l option. For example: to add libc, pass "c".
OUTPUT_NAME: The value of the -o option passed to the linker.
SET_EXTRA_LIBRARY_PATH: Function pointer to add extra path the liker should search for libraries.
GNU_LD_VERSION: The version of the gnu linker.
GET_VIEW: Function pointer to get a memory view of a file.
When performing a link operation, the type of output file given in the LINKER_OUTPUT element is one of the following values:
enum ld_plugin_output_file_type { LDPO_REL, LDPO_EXEC, LDPO_DYN, };
GET_INPUT_SECTION_COUNT: Function pointer to the linker interface to find the number of sections in an object file.
GET_INPUT_SECTION_TYPE: Function pointer to the linker interface to find the type of a section.
GET_INPUT_SECTION_NAME: Function pointer to the linker interface to find the name of a section.
GET_INPUT_SECTION_CONTENTS: Function pointer to the linker interface to find the contents of a section.
UPDATE_SECTION_ORDER: Function pointer to the linker interface to inform the linker to layout a group of sections in a particular order.
ALLOW_SECTION_ORDERING: Function pointer to the linker interface to inform the linker that section ordering is desired.
ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS: Function pointer to the linker interface to inform the linker that a subset of sections should be laid out in a separate ELF segment.
UNIQUE_SEGMENT_FOR_SECTIONS: Function pointer to the linker interface to inform the linker to layout a group of sections in a separate segment in a particular order.
REGISTER_NEW_INPUT_HOOK: Function pointer to the linker interface that lets the plug-in register a handler to be called for each input file when in the replacement phase (after the all_symbols_read event).
Input Files
Each input file passed to the "claim file hook" handler is represented by struct ld_plugin_input_file:
struct ld_plugin_input_file { const char *name; int fd; off_t offset; off_t filesize; void *handle; };
name: The pathname of the input file.
fd: An open file descriptor for the input file. The current position in the file is undefined (i.e., the plug-in must call lseek before reading). The plug-in must leave the file descriptor open, but it is not required to maintain the current position in the file.
offset: The offset of the input file within an archive file. For a standalone input file, this field will be 0; for a member of an archive file, this will be the offset of the member within the file.
filesize: The size of the input file (for an archive member, the size of the member).
handle: An opaque handle assigned by the linker to the input file. The plug-in must use this handle to refer to the file when calling the ADD_SYMBOLS and GET_SYMBOLS interfaces.
Symbols
Each global symbol in an IR file is represented by struct ld_plugin_symbol:
struct ld_plugin_symbol { char *name; char *version; int def; int visibility; uint64_t size; char *comdat_key; int resolution; };
name: The name of the symbol.
version: The version name for the symbol (NULL if the symbol is not versioned).
def: A flag indicating the kind of symbol; one of the following values:
enum ld_plugin_symbol_kind { LDPK_DEF, LDPK_WEAKDEF, LDPK_UNDEF, LDPK_WEAKUNDEF, LDPK_COMMON, };
visibility: A flag indicating the visibility of the symbol; one of the following values:
enum ld_plugin_symbol_visibility { LDPV_DEFAULT, LDPV_PROTECTED, LDPV_INTERNAL, LDPV_HIDDEN, };
size: The size of the symbol (for common symbols).
comdat_key: The name of the key symbol for (what would be) the COMDAT group. Should be NULL for symbols that are not part of a COMDAT group.
resolution: A flag indicating the resolution of the symbol; one of the following values:
enum ld_plugin_symbol_resolution { LDPR_UNKNOWN = 0, LDPR_UNDEF, LDPR_PREVAILING_DEF, LDPR_PREVAILING_DEF_IRONLY, LDPR_PREEMPTED_REG, LDPR_PREEMPTED_IR, LDPR_RESOLVED_IR, LDPR_RESOLVED_EXEC, LDPR_RESOLVED_DYN, LDPR_PREVAILING_DEF_IRONLY_EXP, };
The LDPR_UNKNOWN code is used when the "claim file" handler is providing symbols to the linker via the linker's ADD_SYMBOLS interface. The linker's GET_SYMBOLS or GET_SYMBOLS_V2 interface writes the final disposition code into this field.
The LDPR_PREVAILING_DEF_IRONLY_EXP disposition was added after the original definition of this API. The original GET_SYMBOLS interface does not return this disposition code, returning instead LDPR_PREVAILING_DEF. The newer GET_SYMBOLS_V2 interface may return all disposition codes.
Onload
The linker calls this plug-in function when the library is loaded.
enum ld_plugin_status onload(struct ld_plugin_tv *tv);
Claim File Handler
The linker calls this plug-in function for each input file, after opening the file, but before attempting to identify it.
typedef enum ld_plugin_status (*ld_plugin_claim_file_handler) ( const struct ld_plugin_input_file *file, int *claimed);
If the plug-in claims the file, it must set *claimed to 1.
All Symbols Read Handler
The linker calls this plug-in function at the end of the first pass.
typedef enum ld_plugin_status (*ld_plugin_all_symbols_read_handler) ();
Cleanup Handler
The linker calls this plug-in function at the end of the link, so that the plug-in may remove temporary files, if necessary.
typedef enum ld_plugin_status (*ld_plugin_cleanup_handler) ();
New Input Handler
The linker will call this handler upon seeing new input files provided by a plug-in during the replacement phase (after all_symbols_read). The handler allows a plug-in to call API functions that require an input file handle for input files added by a plug-in.
typedef enum ld_plugin_status (*ld_plugin_new_input_handler) (const struct ld_plugin_input_file *file);
REGISTER_CLAIM_FILE_HOOK
The plug-in's onload function calls this linker function to register a "claim file" handler.
enum ld_plugin_status (*register_claim_file_hook) (ld_plugin_claim_file_handler *handler);
REGISTER_ALL_SYMBOLS_READ_HOOK
The plug-in's onload function calls this linker function to register an "all symbols read" handler.
enum ld_plugin_status (*register_all_symbols_read_hook) (ld_plugin_all_symbols_read_handler *handler);
REGISTER_CLEANUP_HOOK
The plug-in's onload function calls this linker function to register a cleanup handler.
enum ld_plugin_status (*register_cleanup_hook) (ld_plugin_cleanup_handler *handler);
ADD_SYMBOLS
The plug-in's "claim file" handler calls this linker function to provide a list of symbols to the linker.
enum ld_plugin_status (*add_symbols) (const void *handle, int nsyms, const struct ld_plugin_symbol *syms);
handle: The input file handle.
nsyms: The number of symbols in the array.
syms: The array of symbols. The disposition code for each symbol must be set to LDPD_UNKNOWN.
enum ld_plugin_status (*add_symbols_v2) (const void *handle, int nsyms, const struct ld_plugin_symbol *syms);
Version 2 of the above. The function fills also ld_plugin_symbol::symbol_type.
GET_INPUT_FILE
The plug-in's "all symbols read" handler calls this linker function to obtain an open file descriptor for an original claimed input file.
enum ld_plugin_status (*get_input_file) (void *handle, struct ld_plugin_input_file *file);
handle: The input file handle.
file: An empty ld_plugin_input_file structure. The "get input file" interface will fill in the fields of this structure, including an open file descriptor (quite possibly, but not necessarily, the same as before), the file name, the file offset (for archive members), and the file size.
RELEASE_INPUT_FILE
The plug-in's "all symbols read" handler calls this linker function to release a file descriptor obtained by the "get input file" interface.
enum ld_plugin_status (*release_input_file) (void *handle);
handle: The input file handle.
GET_VIEW
The plug-in call use this function to get a memory view of a file. This avoids having to mmap a file both in the linker and in the plugin.
enum ld_plugin_status (*ld_plugin_get_view) (const void *handle, const void **viewp);
handle: The input file handle.
view: Pointer to where the view will be stored.
GET_SYMBOLS
The plug-in's "all symbols read" handler calls this linker function to obtain the disposition codes for the object's symbols.
enum ld_plugin_status (*get_symbols) (void *handle, int nsyms, struct ld_plugin_symbol *syms);
handle: The input file handle.
nsyms: The number of symbols in the array.
syms: The array of symbols. The linker will set the disposition code for each symbol according to how the symbol was bound at the end of pass 1.
enum ld_plugin_status (*get_symbols_v2) (void *handle, int nsyms, struct ld_plugin_symbol *syms);
Version 2 of the above. The only difference is that this version is allowed to return the resolution code LDPR_PREVAILING_DEF_IRONLY_EXP.
enum ld_plugin_status (*get_symbols_v3) (void *handle, int nsyms, struct ld_plugin_symbol *syms);
Version 3 of the above. The only difference from v2 is that it returns LDPS_NO_SYMS instead of LDPS_OK for the objects we never decided to include.
ADD_INPUT_FILE
The plug-in's "all symbols read" handler calls this linker function to provide a new input object for the link. This object must be a regular object file.
enum ld_plugin_status (*add_input_file) (char *pathname);
pathname: The pathname of the object file.
The new object file typically will be a temporary file, and may not be removed until the end of the link, when the cleanup handler is called.
ADD_INPUT_LIBRARY
Like ADD_INPUT_FILE, but the linker will search for the library in its regular search paths.
enum ld_plugin_status (*ld_plugin_add_input_library) (char *pathname);
pathname: The name of the library as passed to the linker in the -l option.
MESSAGE
The plug-in may call this linker function at any time to issue a diagnostic message.
enum ld_plugin_status (*message) (int level, char *format, ...);
level: The severity level of the message; one of the following codes:
enum ld_plugin_level { LDPL_INFO, LDPL_WARNING, LDPL_ERROR, LDPL_FATAL, };
GET_INPUT_SECTION_COUNT
The plug-in's "claim file" handler calls this linker function to determine the number of sections in the input file.
enum ld_plugin_status (*get_input_section_count) (const void *handle, unsigned int *count)
handle: The input file handle.
count: A pointer to an unsigned integer whose contents will contain the section count after call completion.
GET_INPUT_SECTION_TYPE
The plug-in's "claim file" handler calls this linker function to determine the type of a section in an input file.
enum ld_plugin_status (*ld_plugin_get_input_section_type) (const struct ld_plugin_section section, unsigned int *type)
section: The input section.
type: A pointer whose contents is the section type on call completion.
GET_INPUT_SECTION_NAME
The plug-in's "claim file" handler calls this linker function to determine the name of a section in an input file.
enum ld_plugin_status (*ld_plugin_get_input_section_name) (const struct ld_plugin_section section, char **section_name_ptr);
section: The input section.
section_name_ptr: A pointer to a character buffer whose contents is the section name on call completion.
GET_INPUT_SECTION_CONTENTS
The plug-in's "claim file" handler calls this linker function to determine the contents of a section in an input file.
enum ld_plugin_status (*ld_plugin_get_input_section_contents) (const struct ld_plugin_section section, const unsigned char **section_contents, size_t* len);
section: The input section.
section_contents: A pointer to a unsigned character buffer whose contents is the section contents on call completion.
len: A pointer to size whose contents is the size of the contents buffer on call completion.
UPDATE_SECTION_ORDER
The plug-in's "read symbols" handler calls this linker function to specify the order in which a list of sections must be laid out in the final link.
enum ld_plugin_status (*ld_plugin_update_section_order) (const struct ld_plugin_section *section_list, unsigned int num_sections);
section_list: An array of sections in the order in which they should be laid out.
num_sections: An unsigned integer specifying the number of sections in the array.
ALLOW_SECTION_ORDERING
The plug-in's "claim file" handler calls this linker function to prepare for laying out a list of sections in a particular order.
enum ld_plugin_status (*ld_plugin_allow_section_ordering) (void);
ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS
The plug-in's "claim file" handler calls this linker function to prepare for laying out a list of sections in a separate ELF segment.
enum ld_plugin_status (*ld_plugin_allow_unique_segment_for_sections) (void);
UNIQUE_SEGMENT_FOR_SECTIONS
The plug-in's "read symbols" handler calls this linker function to specify a set of segments that should be laid out in a separate ELF segment with specified flags and alignment.
enum ld_plugin_status (*ld_plugin_unique_segment_for_sections) (const char* segment_name, uint64_t segment_flags, uint64_t segment_alignment, const struct ld_plugin_section * section_list, unsigned int num_sections);
segment_name: An identifier for the ELF segment. Actual ELF segments do not have names.
segment_flags: Segment flags.
segment_alignment: Segment alignment.
section_list: An array of sections which should be placed in the new segment and the order in which they should be laid out.
num_sections: Number of sections in section_list.
REGISTER_NEW_INPUT_HOOK
The plug-in's onload function calls this linker function to register an "new input" handler.
enum ld_plugin_status (*register_new_input_hook) (ld_plugin_new_input_handler *handler);
GET_WRAP_SYMBOLS
The linker's interface for getting the list of wrapped symbols using the --wrap option. This sets *NUM_SYMBOLS to number of wrapped symbols and *WRAP_SYMBOL_LIST to the list of wrapped symbols.
enum ld_plugin_status (*ld_plugin_get_wrap_symbols) (uint64_t *num_symbols, const char ***wrap_symbol_list);
GET_API_VERSION
The linker's interface for API version negotiation. A plug-in calls the function (with its IDENTIFIER and VERSION), plus minimal and maximal version of linker_api_version is provided. Linker then returns selected API version and provides its IDENTIFIER and VERSION. The returned value by linker must be in range [MINIMAL_API_SUPPORTED, MAXIMAL_API_SUPPORTED]. Identifier pointers remain valid as long as the plugin is loaded.
int (*ld_plugin_get_api_version) (const char *plugin_identifier, const char *plugin_version, int minimal_api_supported, int maximal_api_supported, const char **linker_identifier, const char **linker_version);
Issues
- Command line options: A FE file might be compiled with a special option, such as optimization level. Question: How is this information stored in the object file? How is the scenario handled where 2 IPA files are being compiled at different optimization levels. What does WPA do? There are many ways to do things - we need to decide on one.