Embedded Systems Architecture
上QQ阅读APP看书,第一时间看更新

Linker

The linker is a tool that can compose an executable program, resolving all the dependencies among the symbols used by the modules, represented by the collection of object files provided as input. The executable that is produced by the linker is an ELF executable. ELF stands for Executable and Linkable Format, and it is the default standard format for programs, objects, and shared libraries, and even GDB core dumps on many Unix and Unix-like systems. The format has been designed to store programs on disks and other media supports, so the host operating system is able to execute it by loading the instructions in RAM, and allocating the space for the program data. ELF files are pided into sections, each corresponding to specific areas in memory needed by the program to execute. The ELF file starts with a header, containing the pointer to the various sections within the file itself, containing the program's code and data. The sections necessary to describe an executable program provided by ELF, conventionally starting with a ., are:

  • .text: Containing the code of the program, accessed in read-only. It contains the executable instructions of the program. The functions compiled into object files are arranged by the linker into this section, and the program always executes instructions within this memory area.
  • .rodata: Containing the value of constants that cannot be altered at runtime. It is used by the compiler as the default section to store constants, and is used as a hint to the system executing the ELF that the section can be mapped in a read-only area in memory, because it is not allowed to modify the stored values at runtime.
  • .data: Containing the values of all the initialized variables of the program, accessible in the read/write mode at runtime. It is a section that contains all the variables, static or global, that have been initialized in the code. Before executing, this area is generally remapped to a writable location in RAM, and the content of the ELF is automatically copied during the initialization of the program, at runtime, before executing the main function.
  • .bss: A section reserved for uninitialized data, accessible in the read/write mode at runtime. It derives its name from an ancient assembly instruction of an old microcode written for IBM 704 in the 1950s. It was originally an acronym for Block Started by Symbol (BSS), used to reserve a fixed amount of uninitialized memory. In the ELF context, it contains all the uninitialized global and static symbols, which must be accessible in the read-write mode at runtime. Because there is no value assigned, the ELF file only describes the section in the header, but does not provide any content for it. The initialization code should ensure that all the variables in this section are set to zero before the execution of the main() function.

When building native software on the host machine, much of the complexity of the linking step is hidden, but the linker is actually configured by default to arrange the compiled symbols into specific sections, which can be later used by the operating system to assign the corresponding section in the process' virtual address space when executing the program. It is possible to create a working executable for the host machine by simply invoking gcc, this time without the -c option, providing the list of the object files that must be linked together to produce the ELF file. The -o option is used to specify the output file name, which otherwise would default to a.out:

$ gcc -o helloworld hello.o world.o

This command will try to build the helloworld file, which is an ELF executable for the host system, using the symbols previously compiled into the two objects.

In an embedded system, things change a bit, as booting a bare-metal application implies that the sections must be mapped to physical areas in memory at linking time. To instruct the linker to associate the sections to well-known physical addresses, a custom linker script file must be provided, describing the memory layout of the executable bare-metal application, and providing additional custom sections that may be required by the target system. A more detailed explanation of the linking step is provided later in this chapter.