Once the linker has completed the symbol resolution step, it has associated each symbol reference in the code with exactly one symbol definition. At this point, the linker knows the exact sizes of the code and data sections in its input object modules. It is now ready to begin the relocation step, where it merges the input modules and assigns run-time addresses to each symbol.
The pseudo code for the linker's relocation algorithm is presented below:
foreach section s {
foreach relocation entry r {
refptr = s + r.offset; // ptr to reference to be relocated
// Relocate a PC-relative reference
if (r.type == R_386_PC32) {
refaddr = ADDR(s) + r.offset; // ref’s runtime address
*refptr = (unsigned) (ADDR(r.symbol) + *refptr - refaddr);
}
// Relocate an absolute reference
if (r.type == R_386_32)
*refptr = (unsigned) (ADDR(r.symbol) + *refptr);
}
}
Lines 1 and 2 iterate over each sections and each relocation entry r associated with each section. Assume that when the algorithm runs, the linker has already chosen run-time addresses for each section (denoted ADDR(s)) and each symbol (denoted ADDR(r.symbol)). Line 3 computes the address in thes array of the 4-byte reference that needs to be relocated. If this reference uses PC-relative addressing, then it is relocated by lines 5-9. If the reference uses absolute addressing, then it is relocated by lines 11-13.
Source:
Randal E. Bryant, David R. O'Hallaron(2011). COMPUTER SYSTEMS A Programmer's Perspective (Second Edition).Beijing: China Machine Press.