ELF questions

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
moonlightcheese
Posts: 17
Joined: Tue Jan 20, 2009 7:03 pm

ELF questions

Post by moonlightcheese »

i'm trying to gain a general (or rather, in depth) understanding of ELF binaries before i begin coding a file loader. i'm looking at Kosuke Ohtani's Prex operating system as a basis. i'm a bit confused about exactly how to load files into memory and make them run. i understand the concept of using phoff to find the program header, cycle through the program sections using phnum and p_memsz. however, i'm developing for a system without an MMU, the gameboy advance (ARM7TDMI).

i get confused in elf.c. i kind of understand how things get loaded into the virtual address space but it seems almost like ELF binaries assume a virtual address space. what if i just wanted to build a file loader just as proof of concept to teach myself using physical addresses?? i don't know how to force (or ask nicely) malloc to store data in a specific location for the entry point of the program or if that would be a plausible solution.

my second point of confusion is about the MMU. it looks like Ohtani has cycled through section headers to place data in memory for relocation... the functions 'load_reloc', 'relocate_section', and 'relocate_section_rel' and 'relocate_section_rela' are defined only if CONFIG_MMU is not defined (which i assume is set at compile time as this is architecture dependent). so load_exec is never called in the case of the gba?

i've got a plethora of questions but i'll stop here. i'm using the ARMELF reference and the source code as a reference and nothing more. any more tutorials or information about ELF and how to build a loader would be helpful. thanks in advance.
JohnnyTheDon
Member
Member
Posts: 524
Joined: Sun Nov 09, 2008 2:55 am
Location: Pennsylvania, USA

Re: ELF questions

Post by JohnnyTheDon »

Well, if you want to use physical addresses, you must link the elf at the physical address you want to use. You would also be better off not worrying about relocations, and just doing what the program headers tell you to do (ie load block of size n from x in file to y in memory and leave z amount of space left over).

And with elf, don't just use code from somewhere else. You really only need 2 structures for basic loading: a structure for the ELF header and a structure for program headers.
moonlightcheese
Posts: 17
Joined: Tue Jan 20, 2009 7:03 pm

Re: ELF questions

Post by moonlightcheese »

thanks! any advice on tutorials or info to start with? i've been poring over the ARMELF reference and trying to figure this out from someone else's code, which is somewhat complex.
User avatar
kasper
Posts: 19
Joined: Sun Apr 27, 2008 7:59 am
Location: The Netherlands, Amersfoort
Contact:

Re: ELF questions

Post by kasper »

Hi,

(A small warning: I might not be thinking clear at the moment, so if this doesn't make sense do what JonnyTheDon or others advise you.)
JohnnyTheDon wrote:Well, if you want to use physical addresses, you must link the elf at the physical address you want to use.
An other approach is to compile your binaries as relocatable (shared object files) and have your ELF loader load them at an available physical address range. This allows you to load multiple binaries without the binary's creator having to know a fixed address range in advance. The binary will contain a number of relocation entries. Your loader will use these to set the absolute addresses in the loaded image.

I did this once for some firmware on a SH2 platform without a MMU. (I can't find my notes about it at the moment). I don't about any tutorials that use this dynamic loading without MMU as I just described.

Kasper
JohnnyTheDon
Member
Member
Posts: 524
Joined: Sun Nov 09, 2008 2:55 am
Location: Pennsylvania, USA

Re: ELF questions

Post by JohnnyTheDon »

Yeah, PIC will work if ARM supports it (idk if it does). As for code, I unfortunately can't provide any because my code uses elf64, which is almost the same as elf32 except the headers are structured differently and addresses are twice as long. I recommend just reading the ELF specs and writing C (or whatever language you are using) structures with it. From there you just use the values in the program headers an input to a series of memcpy()s.
quok
Member
Member
Posts: 490
Joined: Wed Oct 18, 2006 10:43 pm
Location: Kansas City, KS, USA

Re: ELF questions

Post by quok »

Please remember that relocatable code and position independent code (PIC) are different.

With relocatable code, you do address fixups at load time with a loader/linker capable of doing such things. Relocatable codes does not have to be relocated to run, the default addresses work, but they may be moved. Offsets between sections are always the same.

Position independent code, on the other hand, normally uses an offset table where every memory reference is looked up in that offset table. ELF shared objects are generally position independent, not relocatable. They don't have a load address, you load it anywhere and use the GOT (Global Offset Table). PIC allows offsets between sections to be different, and allow you to do nice security conscious things like use address space randomization. It does use an extra register though, so some people may claim PIC code to be slower on i386 because of the limited amount of registers. To others, it's a tradeoff for the extra security PIC allows you to put in place.

Additionally, executables themselves may be relocatable, or position independent. LD will emit relocation tables for an executable if you specify -q or --emit-relocs when linking the final executable. A position independent executable is generally compiled with -fpie or -fPIE (and linked with -pie or --pic-executable), and not -fpic or -fPIC (or linked with -shared) as one would use when compiling a shared object.

Note that by their very nature, all object files are relocatable.
Laksen
Member
Member
Posts: 140
Joined: Fri Nov 09, 2007 3:30 am
Location: Aalborg, Denmark

Re: ELF questions

Post by Laksen »

I load my ELF objects with relocations. This is for x86 for now only however. This is, like your situation, to get around not using the MMU.

I emit a relocatable object from ld by specifying the -r flag. This will generate a relocatable object which can contain unresolved references. To load it you allocate space for each section header which contains data and fix all entries that require relocation by looping through all REL(A) section items afterwards

I have attached the source to my loader, if you want a second look at a very naive and inefficient way to handle it. It's written in bad object pascal, so it might be hard to read
Attachments

[The extension pas has been deactivated and can no longer be displayed.]

http://j-software.dk | JPasKernel - My Object Pascal kernel
moonlightcheese
Posts: 17
Joined: Tue Jan 20, 2009 7:03 pm

Re: ELF questions

Post by moonlightcheese »

thanks for the responses!

i started to get some code put together and i think i have the data placed in memory properly and now i need a better understanding of interrupt vectors and how they work. i understand the concept of interrupts but not really the implementation. right now, i'm not so much worried about saving registers for switching context. i just want to begin execution of a process image. i'm going to be scouring the net for information on this, but i'm pretty clueless as to direction on where to look for info.

here's the loader:

Code: Select all

void run_prog(FILE* fn) {
	Elf32_Ehdr *ehdr;
	Elf32_Phdr *phdr;
	Elf32_Shdr *shdr;
	Elf32_Ehdr *gba;
	void *addr;
	unsigned long size;
	int i=0;
	
	ehdr = (Elf32_Ehdr *)(fn);
	phdr = (Elf32_Phdr *)((u_long)ehdr+ehdr->e_phoff); //+192 bytes
	shdr = (Elf32_Shdr *)((u_long)ehdr+ehdr->e_shoff);
	
	//loop to find the total size of memory we need to allocate
	for(i=0; i<(int)ehdr->e_phnum; i++, phdr++) {
		if(phdr->p_type != PT_LOAD)
			size+=(phdr->p_memsz);
	}
	
	//reset the program header pointer and ask for the memory we need
	phdr = (Elf32_Phdr *)((u_long)ehdr+ehdr->e_phoff);
	addr=malloc(size);
	
	for (i = 0; i < (int)ehdr->e_phnum; i++, phdr++) {
		if (phdr->p_type != PT_LOAD)
			continue;

		if (size == 0)
			continue;

		if (phdr->p_memsz > 0) {
			fseek(fn, (fpos_t)phdr->p_offset, SEEK_SET);
			fread(addr, phdr->p_memsz, 32, fn);
			continue;
		}
	}
}
i haven't tried it yet, not having built the interrupt handling yet, so if i'm doing anything wrong, let me know please :D

afaik, all i need to do is send an interrupt that branches to the address stored in addr right?
User avatar
Tomaka17
Member
Member
Posts: 67
Joined: Thu Oct 02, 2008 8:20 am

Re: ELF questions

Post by Tomaka17 »

Code: Select all

//loop to find the total size of memory we need to allocate
for(i=0; i<(int)ehdr->e_phnum; i++, phdr++) {
   if(phdr->p_type != PT_LOAD)
      size+=(phdr->p_memsz);
}
This won't work because sections may not be consecutive in memory

For example the first may be loaded at 0x30000 with size 0x600, and the second at 0x31000 (not necessarily 0x30600)

You need something like:

Code: Select all

unsigned int lowestoffset = 0xffffffff;
unsigned int highestoffset = 0x0;
for(i=0; i<(int)ehdr->e_phnum; i++, phdr++) {
   if(phdr->p_type != PT_LOAD) {
      if (phdr->p_memoff < lowestoffset) lowestoffset = phdr->p_memoff;
      if (phdr->p_memoff + phdr->p_memsz > highestoffset) highestoffset = phdr->p_memoff + phdr->p_memsz;
   }
}
size = highestoffset - lowestoffset;
MysteriOS
Currently working on: TCP/IP
moonlightcheese
Posts: 17
Joined: Tue Jan 20, 2009 7:03 pm

Re: ELF questions

Post by moonlightcheese »

Tomaka17 wrote:

Code: Select all

//loop to find the total size of memory we need to allocate
for(i=0; i<(int)ehdr->e_phnum; i++, phdr++) {
   if(phdr->p_type != PT_LOAD)
      size+=(phdr->p_memsz);
}
This won't work because sections may not be consecutive in memory

For example the first may be loaded at 0x30000 with size 0x600, and the second at 0x31000 (not necessarily 0x30600)

You need something like:

Code: Select all

unsigned int lowestoffset = 0xffffffff;
unsigned int highestoffset = 0x0;
for(i=0; i<(int)ehdr->e_phnum; i++, phdr++) {
   if(phdr->p_type != PT_LOAD) {
      if (phdr->p_memoff < lowestoffset) lowestoffset = phdr->p_memoff;
      if (phdr->p_memoff + phdr->p_memsz > highestoffset) highestoffset = phdr->p_memoff + phdr->p_memsz;
   }
}
size = highestoffset - lowestoffset;
the ELF specification says there isn't a 'memoff' member of Elf32_Phdr. i assume you mean p_offset?

just to clarify, according to the spec, i think p_filesz is the file segment size and the p_memsz member is the size that segment should occupy in memory, with any area left over being zero filled, with filesz>=memsz... is this correct?

i think that's right, but how can i build a concurrent process image using multiple memory requests? in other words, how can i ensure the memory segments are placed next to each other if i loop through the segments and request memory for each segment separately?
moonlightcheese
Posts: 17
Joined: Tue Jan 20, 2009 7:03 pm

Re: ELF questions

Post by moonlightcheese »

berkus wrote:You do if (p_type != PT_LOAD) which in turn selects all other sections rather than ones you want to have loaded.

You need to set up page mapping or segmentation in such a way that process' virtual address at the point where you're loading the segment corresponds to the p_vaddr of this segment in ELF file.

Code: Select all

 p_vaddr equals 0, p_filesz equals 0x200, p_memsz equals 0x400
                         virt.addrs+----------------------------+   phys. addrs
+-------------+---+                |                            |
| ELF file    |   +----+           | memory process image       |
| segment     |        +---------->| 0                 0x200000 |<------+
|             |        loaded      |                            |       |
+-------------+--------+           |                            |       |
                       +---------->| 0x200              0x200200|       +-- malloc()ed
                                   |                            |       |
                                   |   zeroed ...               |       |
                                   | 0x400              0x200400|<------+
                                   +----------------------------+
(sorry for quick ascii drawing)

Also, the standard says that PT_LOAD segments will be ordered: Loadable segment entries in the program header table appear in ascending order, sorted on the p_vaddr member. So all you need to do is find first and last PT_LOAD segments and note their addresses (in case you know your linker will put them adjacent this is all you need to know). For your own OS control it using a custom linker file.

[update] Also, Program Loading and Dynamic Linking section in Book III of the ELF specification contains some details on loading the program files.
yea i was trying to do this without virtual addressing but it seems a bit impossible...
moonlightcheese
Posts: 17
Joined: Tue Jan 20, 2009 7:03 pm

Re: ELF questions

Post by moonlightcheese »

berkus wrote:It should be possible with PIC code, I think.
sorry for being an idiot but my level of understanding on this subject is kinda dull...

i don't understand how the ELF files get loaded into virtual addresses and actually run. i envision a "process image" as a congruent set of bytes in memory, with the process knowing exactly where the data it needs is, using offsets within the process image to execute. introducing virtual addresses screws this up by placing the process image rather haphazardly in memory (physically). the process image has no way of using virtual addressing itself (right?) so how does it run? i just don't understand it... how does the process execute all spread out in physical memory like that?

what i also don't understand are some of these members of Elf32_Phdr. i tried cycling through the program headers gathering information and it seems like i just can't get anything useful from the program headers. i tried this code just to see if the ELF file was in order, and things just don't look right:

Code: Select all

		scanKeys();
		if(keysDown() & KEY_L){
			for(i=0; i<=ehdr->e_phnum; i++) {
				if(phdr->p_memsz==0)
					continue;
				iprintf("%u\n  m:%u->o:%u\n", (int)phdr, phdr->p_memsz, phdr->p_offset);
				iprintf("  v:%u fs:%u\n", (int)phdr->p_vaddr, (int)phdr->p_filesz);
				iprintf("  t:%u fg:%u\n", (int)phdr->p_type, (int)phdr->p_flags);
				phdr+=(ehdr->e_phentsize); //e_phentsize divided by 32, bitshifts are faster than divide
			}
		}
and it printed out this garbage (i only give the last entries to keep it succinct):

Code: Select all

83855784
  m:3505836278->o:2551552021
  v:4027379724 fs:4293984240
  t:4027379724 fg:4293591018
83872168
  m:3505836278->o:2551552021
  v:4027379724 fs:4293984240
  t:4027379724 fg:4293591018
what's worse is, the last time i compiled it these values changed completely, which isn't too disconcerting, but fs(p_filesz) and fg(p_flags) were the same! in all cases, p_type is way out of line. is this even a real ELF file? note that this binary was compiled with devKitPro using gba libraries and these are making .gba and .elf files, with the .gba files (runs on gba using flashcart) being roughly 2/3 the size of the .elf file. the .elf file is the only file that contains the ELF magic. i can't find the ELF in any bytes in the .gba file so i've been using the .elf.

so is this even an ELF file? am i doing something wrong?
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: ELF questions

Post by Combuster »

an ELF file is an executable, a binary with all the setup info included. It still needs some setup before it can run, but you have more information about what it does and need.

a GBA file is a rom - a flat binary. Basically ready to be started. All the ELF data has been parsed and applied to create a ready-to-run application image in the way the gameboy expects it.
"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 ]
moonlightcheese
Posts: 17
Joined: Tue Jan 20, 2009 7:03 pm

Re: ELF questions

Post by moonlightcheese »

Combuster wrote:an ELF file is an executable, a binary with all the setup info included. It still needs some setup before it can run, but you have more information about what it does and need.

a GBA file is a rom - a flat binary. Basically ready to be started. All the ELF data has been parsed and applied to create a ready-to-run application image in the way the gameboy expects it.
i assumed as much as this... but the compiler makes a .gba and a .elf file, which seems to be invalid from the output of the code in the previous post. an operating system has been written for the gba that loads elf files, so i should be able to load them, but i just wanted to make sure my conjecture was correct (about the .elf files made with devKitPro gba libraries being useless) before i go finding other cross compilers to build elf binaries i can actually use.
User avatar
Firestryke31
Member
Member
Posts: 550
Joined: Sat Nov 29, 2008 1:07 pm
Location: Throw a dart at central Texas
Contact:

Re: ELF questions

Post by Firestryke31 »

I'm not sure that the ELFs produced by dKA are useless, seeing as (last I tried) VBA can load and run them. Then again it's been some time since I last did GBA development...
Owner of Fawkes Software.
Wierd Al wrote: You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?
Post Reply