This article includes a list of general references, but it lacks sufficient corresponding inline citations. (July 2024) |
Relocation is the process of assigning load addresses for position-dependent code and data of a program and adjusting the code and data to reflect the assigned addresses.[1][2] Prior to the advent of multiprocess systems, and still in many embedded systems, the addresses for objects are absolute starting at a known location, often zero. Since multiprocessing systems dynamically link and switch between programs it became necessary to be able to relocate objects using position-independent code. A linker usually performs relocation in conjunction with symbol resolution, the process of searching files and libraries to replace symbolic references or names of libraries with actual usable addresses in memory before running a program.
Relocation is typically done by the linker at link time, but it can also be done at load time by a relocating loader, or at run time by the running program itself. Some architectures avoid relocation entirely by deferring address assignment to run time; as, for example, in stack machines with zero address arithmetic or in some segmented architectures where every compilation unit is loaded into a separate segment.
Object files are segmented into various memory segment or section types. Example segment types include code segment (.text), initialized data segment (.data), uninitialized data segment (.bss), or others as established by the programmer, such as common segments, or named static segments.
The relocation table is a list of pointers created by the translator (a compiler or assembler) and stored in the object or executable file. Each entry in the table, or "fixup", is a pointer to an absolute address in the object code that must be changed when the loader relocates the program so that it will refer to the correct location. Fixups are designed to support relocation of the program as a complete unit. In some cases, each fixup in the table is itself relative to a base address of zero, so the fixups themselves must be changed as the loader moves through the table.[2]
In some architectures a fixup that crosses certain boundaries (such as a segment boundary) or that is not aligned on a word boundary is illegal and flagged as an error by the linker.[3]
Far pointers (32-bit pointers with segment:offset, used to address 20-bit 640 KB memory space available to DOS programs), which point to code or data within a DOS executable (EXE), do not have absolute segments, because the actual address of code/data depends on where the program is loaded in memory and this is not known until the program is loaded.
Instead, segments are relative values in the DOS EXE file. These segments need to be corrected, when the executable has been loaded into memory. The EXE loader uses a relocation table to find the segments which need to be adjusted.
With 32-bit Windows operating systems, it is not mandatory to provide relocation tables for EXE files, since they are the first image loaded into the virtual address space and thus will be loaded at their preferred base address.
For both DLLs and for EXEs which opt into address space layout randomization (ASLR), an exploit mitigation technique introduced with Windows Vista, relocation tables once again become mandatory because of the possibility that the binary may be dynamically moved before being executed, even though they are still the first thing loaded in the virtual address space.
When running native 64-bit binaries on Windows Vista and above, ASLR is mandatory[citation needed], and thus relocation sections cannot be omitted by the compiler.
The Executable and Linkable Format (ELF) executable format and shared library format used by most Unix-like systems allows several types of relocation to be defined.[4]
The linker reads segment information and relocation tables in the object files and performs relocation by:
The following example uses Donald Knuth's MIX architecture and MIXAL assembly language. The principles are the same for any architecture, though the details will change.
[…] Absolute code, and an absolute object module, is code that has been processed by LOC86 to run only at a specific location in memory. The Loader loads an absolute object module only into the specific location the module must occupy. Position-independent code (commonly referred to as PIC) differs from absolute code in that PIC can be loaded into any memory location. The advantage of PIC over absolute code is that PIC does not require you to reserve a specific block of memory. When the Loader loads PIC, it obtains iRMX 86 memory segments from the pool of the calling task's job and loads the PIC into the segments. A restriction concerning PIC is that, as in the PL/M-86 COMPACT model of segmentation […], it can have only one code segment and one data segment, rather than letting the base addresses of these segments, and therefore the segments themselves, vary dynamically. This means that PIC programs are necessarily less than 64K bytes in length. PIC code can be produced by means of the BIND control of LINK86. Load-time locatable code (commonly referred to as LTL code) is the third form of object code. LTL code is similar to PIC in that LTL code can be loaded anywhere in memory. However, when loading LTL code, the Loader changes the base portion of pointers so that the pointers are independent of the initial contents of the registers in the microprocessor. Because of this fixup (adjustment of base addresses), LTL code can be used by tasks having more than one code segment or more than one data segment. This means that LTL programs may be more than 64K bytes in length. FORTRAN 86 and Pascal 86 automatically produce LTL code, even for short programs. LTL code can be produced by means of the BIND control of LINK86. […]
[…] Laws: […] "dynamic relocation" of the OS. Can you tell us what that is and why it was important? […] Eubanks: […] what Gary did […] was […] mind boggling. […] I remember the day at the school he came bouncing into the lab and he said, I have figured out how to relocate. He took advantage of the fact that the only byte was always going to be the high order byte. And so he created a bitmap. […] it didn't matter how much memory the computer had, the operating system could always be moved into the high memory. Therefore, you could commercialize this […] on machines of different amounts of memory. […] you couldn't be selling a 64K CP/M and a 47K CP/M. It'd just be ridiculous to have a hard compile in the addresses. So Gary figured this out one night, probably in the middle of the night thinking about some coding thing, and this really made CP/M possible to commercialize. I really think that without that relocation it would have been a very tough problem. To get people to buy it, it'd seem complicated to them, and if you added more memory you'd have to go get a different operating system. […] Intel […] had the bytes reversed, right, for the memory addresses. But they were always in the same place, so you could relocate it on a 256 byte boundary, to be precise. You could therefore always relocate it with just a bitmap of where those […] Laws: Certainly the most eloquent explanation I've ever had of dynamic relocation […][9][10] (33 pages)
[…] Ever wonder how MOVCPM works? Since the BDOS and CCP is in high memory, above the user application, addresses have to be changed every time the system memory size is changed. Now that requires relocating addresses in 8080 code, since relative addressing is not part of the hardware. Without implementing a full-blown relocating assembler and loader, how does one go about this? It's actually pretty clever and MP/M even uses this scheme to construct its page-relocatable files. You simply assemble the source program twice with the second assembly origin 100H (256 bytes) higher than the first. The two binary images are then compared, byte for byte, and a map constructed of where pairs of bytes differ in value by exactly 100H. The result is a list of locations where the relocation value needs to be adjusted if the location of a program in memory is to be moved. MP/M calls this sort of file PRL (page relocatable), but I don't know that CP/M 2.2 ever coined a name for it. […]
[…] MOVCPM uses an early type of PRL format. Basically, CP/M is assembled twice; the second time is 100H bytes offset. The two binaries are compared and a bitmap constructed. A set bit implies that the high-order byte of an address is to be adjusted. Low order address bytes are not affected; hence, "Page relocatable file". Each byte in the bitmap corresponds to 8 bytes in the binary data. […] So everything to be moved in MOVCPM is part of the image and its relocation bitmap. […]
[…] I've referenced PRL files and how they originally got their start with MOVCPM , but became an integral part of MP/M and CP/M 3.0. But PRL files use a bit map in which every bit corresponds to a memory location; one bits indicate that a page relocation offset should be added to the corresponding memory location. If you have very few absolute memory references (as opposed to relative ones) you may want to employ a pointer list (2 bytes per reference) rather than a bitmap. This is unlikely in 8080 code which doesn't have relative jumps, but may be a consideration for Z80 code. The trick to quickly find this out is to assemble your program twice; the second time offset by 100H, then compare the two binaries. The advantage of run-time relocation is that you don't have to incur a penalty for code that attempts to get around the relocation issue--no "tricks"; just write straight code. […]
[…] A PRL file is a relocatable binary file, used by MP/M and CP/M Plus for various modules other than .COM files. The file format is also used for FID files on the Amstrad PCW. There are several file formats which use versions of PRL: SPR (System PRL), RSP (Resident System Process). LINK-80 can also produce OVL (overlay) files, which have a PRL header but are not relocatable. GSX drivers are in PRL format; so are Resident System Extensions (.RSX). […][11]
[…] The REL format is generated by Microsoft's M80 and Digital Research's RMAC. […]
[…] Out of the assembled Microsoft .REL files the linker has to generate a .PRL format executable for MP/M. The .PRL format is essentially a .COM file with some additional information to enable the program and its data to be relocated onto any page. What does a .PRL file look like? The first bytes are size of the program, followed by the program origin at 0x0100. Following the program, there is a bit-for-byte mask appended to allow the MP/M system to know which bytes in the program need to be changed when the program is relocated. How does the linker do that without disassembling the whole application? In advance the program is linked for two different origins 0x0100 and 0x0200, from the .REL objects. The linker trick is simply recognising which bytes in the two versions of the executable differ. These bytes are then recorded in the bit mask stored following the executable, and the final .PRL program is designed to run from 0x0100 plus its page offset. The same trick is done for the .RSP and .SPR executable files, except that both these formats forego the offset, and run from 0x0000 plus their page offset. […]