Loading ELF executable

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
Post Reply
lexected
Posts: 23
Joined: Wed Aug 14, 2013 11:48 am

Loading ELF executable

Post by lexected »

Hello everybody!

I have recently started to write some assembly lines for loading ELF kernel to memory. I read wiki page on this topic and started to write my own code, but I found out that don't understand some steps of loading. I have summarized them into two questions:
  • Wiki says that for loading executable to memory I need program headers. Loading using program headers works for me, but why not to use sections? They contain important same information like program headers (size, file offset, address) + access flags (W|X|Alloc). I can miss this information later, can't I?
  • Executable generated by linker uses /lib/ld-linux.so.2. Is there any way to change it for my operating system?
Thank you.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Loading ELF executable

Post by AJ »

Hi,

Generally, loading the section headers will work ok, but note that the section headers are optional in the ELF specification. You need to handle the case where the section headers do not exist. The best idea is to load using the program headers and then read any information you want to cache from the section headers if they exist (which they almost certainly will).

If I understand what you mean in your second point, you can overcome this by using a cross compiler (and eventually and OS Specific Toolchain) which are detailed on the Wiki.

Cheers,
Adam

[Edit: Correction - in what the ELF specification defines as a program file or executable file the section headers are optional. In a relocatable file the program headers are optional.]
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: Loading ELF executable

Post by sortie »

You don't want the section headers. The program loader (bootloader/kernel) is supposed to use the program headers that contains all the information the program loader needs. Simply follow all the LOAD entires that tells you what to memcpy where. Things are nicely page-aligned and such and is really straightforward to load.

The section headers are just debug information after the executable has been linked. They are not the canonical description of the ELF file and they are actually considerably harder to load. My first ELF loader used the section headers because I didn't realize the program headers were there, when I learned of them I deleted my unfinished buggy code and wrote a simple program header loader.

Note that the /lib/ld.so business is part of implementing shared libraries. Statically linked programs need no such dynamic linker as the program interpreter. If your kernel is linked dynamically, you are done it wrong. If your kernel is supposed to be interpreted by /lib/ld-linux.so.2, then you are doing it doubly wrong because you should be using a cross-compiler. Note that as AJ points out, if you use your own modified GCC that you have the ability to completely retool it to whatever you wish. The answer to all "I wish my compiler did something differently by default" quesions can be answered by just changing the compiler.
lexected
Posts: 23
Joined: Wed Aug 14, 2013 11:48 am

Re: Loading ELF executable

Post by lexected »

AJ: thank you, I think I understand it now.
sortie: thanks for information. I don't want to have dynamically linked kernel, I just wanted to know it is possible. When I finish kernel loader, I'll have a look on custom compiler.
h0bby1
Member
Member
Posts: 240
Joined: Wed Aug 21, 2013 7:08 am

Re: Loading ELF executable

Post by h0bby1 »

from what i understood of elf loaders, the sections part seem to be more usefull to parse the image as a file, it's this information most of elf dumping tools use, and the part used to load the executable into memory and run it are from the segment section, from the program header table

you need also the string table, and it can still be a good idea to parse the section and program header definition, because if i remember correctly, the strings or other informations can still be stored only in sections headers, all the symbols and information are not defined in the program headers part either

there are also many things to look into relocations, elf has many way to define them, there are a little dizains of way the reloction of a symbol can be made at runtime, but i think only 3 are commonly used, absolute relocation, relative relocation (relocation relative to the instruction), and it match some entry in the symbol tables with a relocation entry that need to be applied, there can be many things to consider regarding 'position independent code' , if you want to be able to load lef executable at arbitrary address location, even if they are not dynamically linked, otherwise you need to have a managment of virtual memory addressing to load the sections at the virtual address the elf specify in section headers

i use the segment memory model, and for now all the module share the same address space and use direct physical addresses everywhere in the whole kernel part, so i need to do many thing to relocate addresses into the binary files, but at least it make it very easy to manipulate dma address and memory mapped registers and to program the hardware and application can easily share pointers, but i'll need also a mode where it can load a module in a virtual address space

this is a good pdf about the elf format
http://www.skyfree.org/linux/references/ELF_Format.pdf
User avatar
dozniak
Member
Member
Posts: 723
Joined: Thu Jul 12, 2012 7:29 am
Location: Tallinn, Estonia

Re: Loading ELF executable

Post by dozniak »

section headers are useful for precise managing bits and pieces of the program, for example, .o files use sections to record particular functions and global variables - there's a section per function if you use -ffunction-sections for example.

program headers cover whole areas of the elf file on much coarse granularity based on the functional assignments - code, data, bss, etc. there's usually 3-6 program headers only.

you cannot make a relocatable executable using program headers only - relocation information usually contains section offsets and you need section headers to be present.

elf format is fairly flexible in what you can use - say, .o files use sections only, but executables may use program headers only; it can also be possible to load the same executable using either program or section headers - the end result can be just the same.
Learn to read.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Loading ELF executable

Post by Owen »

dozniak wrote:you cannot make a relocatable executable using program headers only - relocation information usually contains section offsets and you need section headers to be present.
Wrong. The DT_REL[A] table contains virtual addresses.

Attempting to load an executable by its' section headers will go wrong when the application does non-trivial things. Besides, you need to process it by the program headers to do dynamic linking.
User avatar
dozniak
Member
Member
Posts: 723
Joined: Thu Jul 12, 2012 7:29 am
Location: Tallinn, Estonia

Re: Loading ELF executable

Post by dozniak »

Owen wrote:
dozniak wrote:you cannot make a relocatable executable using program headers only - relocation information usually contains section offsets and you need section headers to be present.
Wrong. The DT_REL[A] table contains virtual addresses.

Attempting to load an executable by its' section headers will go wrong when the application does non-trivial things. Besides, you need to process it by the program headers to do dynamic linking.
Wrong.

r_offset
This member gives the location at which to apply the relocation action. For a relocatable file, the value is the byte offset from the beginning of the section to the storage unit affected by the relocation. For an executable file or a shared object, the value is the virtual address of the storage unit affected by the relocation.

I'm talking about the relocatable file here (see first quote).
Learn to read.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Loading ELF executable

Post by Combuster »

And what you call a "relocatable executable" does not exist in ELF terms - it's just a horrible ambiguity in the standard where relocatable basically means unlinked. For all intents and purposes, it's an executable file containing relocation information, and not a relocatable file, and the associated part of the standard does not apply.

In fact, I do runtime relocations and the relocation section actually needs to operate on virtual addresses rather than offsets.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
h0bby1
Member
Member
Posts: 240
Joined: Wed Aug 21, 2013 7:08 am

Re: Loading ELF executable

Post by h0bby1 »

i do a whole lot of conversion on relocation information when i convert so and dll to my own format, and i make in sort that all the relocation information are stored relative to the position in the image file, like this i can easily load and run the code 'in place', without moving or allocating anything, and i can convert the relocations to sections ofset easily at load time as i know where the section are in the binary image, and it allow for the relocation information to be stored in a way that they represent something from inside of the binary file image, without needing the virtual ofset space

but i don't load .so and .dll directly in the os, i convert them to an inner format and the relocation are already translated, and i don't use virtual address at all anywhere, the binary image can be executed and 'linked' for execution in place no matter where the sections are located in memory, both so and dll have some structure that are made to be able to make the object relocatable, they are optional informations for dll at least, and so has many different way it can handle that, but both so and dll can include all the information that are needed for the whole executable to be relocatable

in the conversion process, i initialize structures to represent the virtual space in which the exe is supposed to be loaded from elf section info, then i can find in which section memory area the relocation have to point to, from there i can compute the ofset from section start, and store the final relocation address in the image file depending on the ofset of the section in the image file, it's not the most straight forward way to process elf information, but then it make the whole thing much easier and simpler to load and run as relocatable file

but i can admit it was rather tedious to convert the infos from elf headers in a way that make the image easily relocatable at load/execution time, but technically, even if relocation address are stored in virtual space, it's not very hard to compute the section ofset from the sections headers containing the sections base addresses and size, and to then apply relocation to any place the sections are actually loaded in memory
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Loading ELF executable

Post by Owen »

dozniak wrote:
Owen wrote:Wrong. The DT_REL[A] table contains virtual addresses.

Attempting to load an executable by its' section headers will go wrong when the application does non-trivial things. Besides, you need to process it by the program headers to do dynamic linking.
Wrong.

r_offset
This member gives the location at which to apply the relocation action. For a relocatable file, the value is the byte offset from the beginning of the section to the storage unit affected by the relocation. For an executable file or a shared object, the value is the virtual address of the storage unit affected by the relocation.

I'm talking about the relocatable file here (see first quote).
Understand the definitions of relocatable file vs executable file or shared object

The name "relocatable file" is terrible baggage from the days of a.out executable. A relocatable file is an unlinked object file.

Meanwhile executable files are the things that you can run.

Think: If you couldn't make an executable with relocations, why would the specification go through the trouble of defining how said relocations work? Why would the dynamic table have DT_REL/DT_RELA entries?
h0bby1
Member
Member
Posts: 240
Joined: Wed Aug 21, 2013 7:08 am

Re: Loading ELF executable

Post by h0bby1 »

normally you can do most of the job with program header, even for handling the whole lot of relocation, the only thing i need from the sections is the size of the whole symbol table, because with program header you are just supposed to access the symbol table via an id contained in the relocation table, and the size of the whole symbol table shouldn't matter at all, but i still need to parse the whole symbol table to find the list of exported symbols, which is all symbols of the table for which st_value is set, other are unresolved symbols that need to be imported, and i didn't find a way to find the list of exported symbols using only the informations in program headers, but normally they contain everything you need to load, relocate, resolve and run the elf image

in the document yes they refer to relocatable files as 'non executable files', during the process of linking, the linker must make in sort that all the symbols used by all part of the program are assigned a location, and that all reference to a symbols are resolved and that the operand of any opcode involing an address, either jmps/calls or memory access, are set to a value that is meaningfull at runtime, if you compile a single file, the linking process should be pretty simple, but once you use several files, and extern reference, the linker must make sure any symbol used in any part of the program to be linked is assigned a location

if you don't want to bother at all with relocation, you can just set up the memory as it's suggested by elf program headers, and all the memory location in the binary machine code will point at the right place , and you just have to resolve imported symbols, which are entry in the relocation table who make reference to a symbol of which the value is not set

but if you want to load the sections in another place, then you need to parse the relocation table and edit the machine code to change the address it contain to point at the actual location the symbol is loaded at runtime, it's what relocations table are for, technically you can do at least part of the job of the linker at runtime as well with relocation information, and you could change the location of any symbol at runtime, or replace the address of any symbol to anything

normally, at least with some options, any memory access in a binary file either for jmp/calls or access to any symbol should have an entry in the relocation table, in sort that you can relocate the whole executable to any place you want

the elf format can be rather tricky with relocation, it's why i made the choice to rather use my internal binary object format, at least for the most critical module that can be hard to debug, like this i know what to expect from it, and i don't depend on compilation/linking options or fancy format that elf can support, because there are several way relocation can be handled

elf can support the use of GOT table, which is table that contain an ofset used to compute the actual symbol address at runtime, and the loader is supposed to fill/edit the got table at runtime, the whole system look rather complex to handle, but i didn't met any elf that make use of the got table, i think it's only used if gcc is used with 'position independant code' (-fpic) related flags
-fpic
Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT). The dynamic loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part of the operating system). If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead. (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000. The 386 has no such limit.)

Position-independent code requires special support, and therefore works only on certain machines. For the 386, GCC supports PIC for System V but not for the Sun 386i. Code generated for the IBM RS/6000 is always position-independent.
R_386_NONE 0 none none
R_386_32 1 word32 S + A
R_386_PC32 2 word32 S + A - P
R_386_GOT32 3 word32 G + A - P
R_386_PLT32 4 word32 L + A - P
R_386_COPY 5 none none
R_386_GLOB_DAT 6 word32 S
R_386_JMP_SLOT 7 word32 S
R_386_RELATIVE 8 word32 B + A
R_386_GOTOFF 9 word32 S + A - GOT
R_386_GOTPC 10 word32 GOT + A - P
the only relocation i encoutered for now are R_386_32/R_386_PC32 and R_386_RELATIVE , the two first are similar except the second one is relative to the address of the code using the address, it is a relative relocation, the first is absolute relocation and the address is to be replaced with the absolute address of the symbol, with the second type, the address must be replaced with the absolute address of the symbol minus the location at which the relocation take place, the address stored is relative to the instruction, like for relative call/jmps, the last one is not supposed to be related to symbol reference, but rather must point at specific location relative to the place the object was loaded , not sure what they are exactly used for, but there are a few of them in most elf images

the others (R_386_GOT32,R_386_PLT32,R_386_GLOB_DAT,R_386_JMP_SLOT,R_386_GOTOFF,R_386_GOTPC) are related to the got, i don't handle at all the got/plt related things, but again there are many way elf objects can be linked, and many options that can impact what you should be doing when loading an elf file, which is again why i prefer to use my own binary object file, and even editing name mangling for exported symbol to have a cross plateform way to do dynamic linking that is independant of the compiler/linker, and i can debug all the elf parsing involved to make up my own binary format from visual studio or gcc,if you want to load elf file directly, you should make sure they are compiled/linked in a way that fit what you expect, because elf format allow for lot of possibility regarding how the image is organised and how you should load it, specially if it's compiled with position independant code, you have to set up the value in the GOT

normally i think to have 'true relocatable shared object', one should compile with gcc '-fPIC' and use the got table, but even without using the GOT the regular relocation table already contain all the infos you need to relocate the whole image and any reference to any symbol anywhere you want, it just make it more tricky to handle as all relocation address must be translated from virtual address space in order to find the address it has within the memory area the section has be loaded in, or something simpler could probably be done like just compute the ofset between the actual base image, and the virtual address at which the base image should be loaded, and just adding this ofset to the relocation, but it will probably not work if the relative address from a section to another change and that there are cross section relocation
Last edited by h0bby1 on Fri Aug 30, 2013 10:33 am, edited 2 times in total.
User avatar
dozniak
Member
Member
Posts: 723
Joined: Thu Jul 12, 2012 7:29 am
Location: Tallinn, Estonia

Re: Loading ELF executable

Post by dozniak »

Owen wrote:Understand the definitions of relocatable file vs executable file or shared object
I'm not objecting to your definition, but for my purposes I load and run the terrible baggage file. I might change my approach later, but so far this is how it was implemented - with the lack of shared libraries and any sort of loader this is sufficient.
Learn to read.
h0bby1
Member
Member
Posts: 240
Joined: Wed Aug 21, 2013 7:08 am

Re: Loading ELF executable

Post by h0bby1 »

dozniak wrote:
Owen wrote:Understand the definitions of relocatable file vs executable file or shared object
I'm not objecting to your definition, but for my purposes I load and run the terrible baggage file. I might change my approach later, but so far this is how it was implemented - with the lack of shared libraries and any sort of loader this is sufficient.
you can do by ignoring part of the spec, and implementing what can be called a broken loader, but it's not a very good idea imo, it can be ok if you still can detect any function that the elf image present that you don't handle, and being able to exit cleanly with a meaningfull error message if some feature that are defined in the elf format spec that you handle is present, but otherwise i don't like the idea to implement stuff that are supposed to handle a format but can break with a file that is valid according to the specification, or just ignoring some feature that can be present according to the spec, because then it's not even truly handling elf file format, it's handling a loosly defined subset of what the elf format can support

maybe there should be some specification that could be made to describe some specific reduced functionality of elf object, that compiler would have to conform to in order to procude this particular elf format, and then specying exactly that you support this particular format in the loader with a spec that compiler/linker can understand, but otherwise it's not really implementing an elf loader, and then it should be better not to use elf format at all if you don't want to implement the whole specification, because it can lead to many problems that can be hard to detect and debug if something is done incorectly when loading the file, and it can break silently or some wrong thing can be done at load time that you won't even know of
h0bby1
Member
Member
Posts: 240
Joined: Wed Aug 21, 2013 7:08 am

Re: Loading ELF executable

Post by h0bby1 »

if you just have to load a binary file at fixed location and you don't need dynamic linking, you don't even really need to use elf format, and you could just go by flat binary format, the only inconvenient is that there is no standard way to know where any data is located within the binary image, and entry point is supposed to be at the start of the file, but you could as well make a table of pointer at the start of the binary file to hold pointer to the symbol you need to run the thing, like the address of the entry point or any address of any data you want to be able to access, and parsing this table from a fixed location within the flat binary file at load time, it would be much simpler than to use the elf format, elf format is mostly usefull to provide dynamic linking and relocation informations, if you don't need this, you don't need to use elf format, and going with a flat binary file would be simpler, the only advantage you'd have using elf format in this configuration is to have unitialized data section, of which space is allocated at load time, which can save up space in the image, but otherwise you don't need to have to deal with elf format to load a statically linked binary image at fixed location


even if you think you don't need the feature, you can't make sure it won't be present in a .so and that you won't have to deal with it at some point, or that some developper or building process could use some fancy options in some circumstances that would have an impact on the way you should load the image, from there you have no way to know if a .so present in the system will actually conform to what you can support, and it can be a dangerous thing to do

i'm maybe a bit paranoic with that, but i worked on some project that involved people using different dev environment, like some module built with visual studio, others with code warrior, other with dev cpp or borland, and i know there are many tricky things that can break binary compatibility between compilers and environement, either name mangling, calling conventions, with c++ also with virtual class, and issue with the libc and crt, linker and compilers can do many unexpected weird things sometime, and elf format is very flexible and can allow many different configuration, from the spec elf is even supposed to have special handling for libc and class contructors/destructor, i don't know how much of that an elf loader should be aware of, but if you plan to allow the use of .so of which you don't control the building process, or having to use .so made on different system or development environement, it's better to be able to handle any case the specification define, or then to use another format that is more suited to what you really need and that you can implement fully
Post Reply