‫Hello and welcome, and in this lecture, I'm going to teach you all about elf files.
‫So what are our files?
‫While ELF stands for executable linkable format, our files are essentially object files that contain
‫symbols that point to data or functions.
‫Object files can be linked to produce an executable file that can be loaded by our kernel.
‫Our files can also be dynamically linked at runtime.
‫I'm now going to explain all of these three points.
‫So the first thing I want to say is each part of your program that you compile produces an object file.
‫As you're probably aware, you can link those object files to create one giant object file.
‫OK, this is one file is the beauty about files is they can be used for compiling single files, object
‫files to Linklater and they can be used for executable code that can be loaded.
‫So when you compile your program into an elf file, the symbols that were exported reside in the file
‫with positioning information of where that symbol is located in the binary.
‫So what are symbols?
‫Well, for example, in an assembly file, if we mark a label as global, it becomes a symbol, OK?
‫And that label name becomes the symbol name inside that object file resides the label and an offset
‫to that symbols code or data pretending what the symbol points to.
‫So for example, in C program, symbols are functions and global variables.
‫So they're actually stored in the file by name.
‫OK, and obviously it's not C code in there.
‫It's machine code from the compiler.
‫See all the compiler assembly.
‫But the point is we can reference those different areas by name.
‫So if we want to find function printf for example, we can just look in the in the file for the standard
‫C library and find that print F function.
‫OK, so that's what our files are for.
‫And they're really essential because without them we can only load binary files.
‫Currently we load our entire kernel as a binary file.
‫Now that's OK, but it's not completely ideal.
‫It means that our kernel can't have external dependencies.
‫For example, if we make an elf loader, it will even be possible to have libraries in our kernel.
‫So object files can be linked to produce some scalable file that can be loaded by the kernel.
‫OK, and this executable file can be an elf file.
‫As I've said, we can combine many object files of different types by passing them to Olinka and then
‫produce the executable file as an output and I'll file the object.
‫Files will be statically linked together with all the symbols merging into one giant elf files.
‫So this is what I described earlier.
‫When you compile Testaccio, you compile A, B, C, that C we can link Testudo and ABC Dotto into one
‫giant elf file.
‫OK, that's executable.
‫OK, so this is what we're going to be doing throughout the course.
‫We're going to be compiling those files into our files, linking them together and then making our kernel
‫be able to load that file.
‫So Alfalfa's can be dynamically linked at runtime, so, for example, if we link against standard library,
‫it might contain symbols that are not known yet.
‫OK, because maybe we linked with a shared library.
‫OK, you've probably all heard of shared libraries before, radial files and so forth and so on.
‫So obviously, we don't know where the printf is.
‫OK, so it's the lotus job of resolving that because when we go to load the file, it searches through
‫the file for all the libraries that are acquired.
‫And I loads those libraries and resolves the symbols, the loda, the kernel load, the of executable
‫followed actually goes in and changes the code of the program, changes the addresses, OK, or it fills
‫a table.
‫It really depends on the type of relocation that's going on that determines what happens.
‫So the kernel is responsible for finding the unknown symbols from the loaded system libraries and if
‫it's unable to resolve this conflict, is responsible for refusing to load the file because we can't
‫load a nail file if we can't resolve all the dependencies, all the symbols it requires.
‫So the linking is done in memory at runtime for these unknown symbols.
‫So why do we need our files?
‫Well, without our files or something similar, we'd be responsible for linking our programs to a binary
‫file, much like we did with our kernel.
‫So when we link to the raw binary file, we need to be very careful in the order we link the object
‫files to ensure that the START routine is the first code to be run.
‫OK, so this is a good point because with a raw binary file, there's no header to tell us where the
‫program entry point is.
‫That means that the object file we want to run first has to be the first one we link in the Lenca.
‫OK, because if we don't link that one first, then it might run some other code, not the initialization
‫function, because we want the main function to be run right.
‫The start routine.
‫We want to start routine to be run, which loads the main function.
‫That's what we want.
‫But we can't have that with binary files where we can.
‫But we need to be careful how we position the object files like we did with our kernel.
‫OK, Kernel O is the first file to be compiled.
‫There's a reason for that.
‫If we didn't compile that one first, then it wouldn't be the one that runs first.
‫So when dealing with raw binary files, memory alignment issues are more problematic.
‫So as I've already explained to you, we have to be careful where we put our sections in the script
‫right for our kernel, because it's a real binary.
‫So as I've explained, the raw binary files have no header and we lack the entry routine information
‫so it doesn't tell us which function we need to run first, which symbol we need to execute first,
‫the entry point of the program.
‫So we're all binary files.
‫They can't have external symbols, OK, we can't do it.
‫We can't say that we want to link with this library because it's a binary file.
‫OK, so we can only link things statically to produce several binary.
‫That's it.
‫So the Rabani files can't be understood by the kernel.
‫It doesn't know what code is and it doesn't know what data is.
‫So we need to give right permissions to the entire memory range of that process.
‫So it allows for insecure programs to accidentally overwrite themselves or for a hacker to exploit a
‫program and gain right.
‫Access to areas of the program which shouldn't.
‫So the binary files, you can see why better alternatives exist.
‫So let's explain the link and process here then, so let's pretend we're making a game.
‫OK, we link main character and.
‫So now as so is a shared library.
‫OK, so Mindoro, it contains a star symbol, a main symbol and a menu function symbol.
‫So obviously, the star routine calls the main function, which is here.
‫And this is a menu function that I don't know, to launch the menu or something.
‫Right.
‫So these two AC functions, they start symbol here is assembly function.
‫But the el-Fadl doesn't care what it is.
‫It doesn't have to know, OK?
‫Now the compiler will hint to the alpha what it is, but by setting a type.
‫But the point is we don't have to be aware of this.
‫OK, the link, it doesn't care.
‫OK, so here we have characters, although this has the character set, name, function, character
‫GetSet type function in character, get type function.
‫Now these are all symbols.
‫OK, remember what I said.
‫A symbol is simply a label that has a memory address or an offset associated to it.
‫It points to a place in the executable file where this function could be found or symbol we'll call
‫it function.
‫So it's easier for you.
‫But symbols can be anything, global variables, functions and so on.
‫OK, so this label is associated with a position of where we can find that symbol or function, the
‫actual machine code for it, the compiled version.
‫So now we have lived on.
‫So this contains the printed function in the scanner function.
‫So when we combine all of these three together, we pass it to the linker outcomes are executable game
‫dorelle file.
‫So we have a stop function.
‫That's the program entry.
‫We have our main function, which is a main.
‫We have our menu function, which is in Manero.
‫We have all our character functions.
‫So they're all compiled into this one executable file.
‫OK, so character Dotto means although they link together and combine into one, however actually laid
‫to rest so is a little different because this is a shared library.
‫So what happens is the game durao file executable file, it just contains a reference to print everything
‫caniff.
‫So it basically says we know that we can find printf in the library.
‫So and we know we can find SCANA if and so, so we want you the loda to resolve those symbols for us.
‫Therefore unable to do that, then we're unable to load the program safely.
‫OK, because it depends on printf and Skynet.
‫OK, so how is the loading process work.
‫So we got a load game that if it goes into the loda we go to load the game drive and we realize it has
‫a dependency on Castellitto.
‫So so we go and load AEG Live Donoso and then we continue loading games.
‫Ralph.
‫So how does the resolving of symbols work, so game depends on printf.
‫OK, so it goes into S.G. Live today, so it finds the information for the printf symbol.
‫Now it knows where it should be in memory.
‫OK, and the Loda patch is game drouth.
‫OK, so one game that gets patched, it now has the address to the printed function.
‫OK, so there's many different types of patching that can go on.
‫It might set a value in a jump table.
‫And in case you didn't know, a jump table is basically an array of pointers.
‫OK, we simply fill the pointer in the table index.
‫It should be.
‫And then when someone calls printf in games around, it finds the address in the jump table and executes
‫it.
‫So the same applies for scanning the loader realizes game that needs kind of so happens.
‫It goes into Astudillo, so it finds the information for ganef and then Apache's the game, the health
‫code, OK, either by setting the table or doing something else, depending on the type of A needs to
‫be applied.
‫So then also the program looks like this.
‫So I start address is that virtual address CLX 400000 are main function.
‫Is that 400000 Azzurro menu function B zero character set name C zero type D zero.
‫Get Type E zero.
‫Yeah.
‫And then I'll print F function.
‫It is located at zero seven hundred thousand one hundred and Ashkenazic seven hundred thousand one a
‫zero.
‫OK, and I actually lived there so you can see that this is where the symbols are loaded.
‫So you can see that the loader has loaded ashed library and resolved those dependencies.
‫It's patched Gandalf whilst it was in memory to where the printer and scanner can be found.
‫So then the program runs as expected.
‫So although the code for PRINTF and scan F was never, ever linked into game drouth, information of
‫how to load the standard library and resolve those symbols was present.
‫So we were able to resolve that conflict.
‫As I'm sure you know, the benefits to shared libraries are that many processes can use the library
‫at the same time.
‫So we only have to load Usted live once and a hundreds of processes can use their printer scanner functions
‫and so on, even though the code for us to be limited to I was only allowed once.
‫OK, you may need separate data sections per library per process that is that is used in that library
‫because otherwise, if the shared library has global data every processor uses, the shared library
‫will cause the data to be overwritten and it will cause nasty things to happen.
‫But we don't have to concern ourselves with that now.
‫Yes, so as I just explained, the labor code only has to load at once and can be used by many processes.
‫And another benefit we don't have to worry about, the older we link the object files because the entry
‫point gets stored in the object file header and the entry point is specified in the linked script.
‫So inside the Alpha format, we have sections, dot text, dot data and so on, we have symbols such
‫as functions, global data, and we have data that tells us how we should resolve symbols from other
‫dependencies.
‫There's a bunch of other stuff, but that's all that matters for the time being.
‫So this is the layout of the file format.
‫So we have the header, we have the program had a table, we have our sections and in the section had
‫a table.
‫OK, dot text section generally contains code, dot data contains data and arrow data contains read-only
‫data.
‫The section head to table explains the sections and the type of data they hold.
‫And you can find that here.
‫So this is the C structure for the header and this is a terminal output from the the executable file
‫called Pelf is a very useful program.
‫I recommend installing it.
‫It's called Dump Health and we can basically dump on our files to the terminal like this.
‫So you can compare this header here with the C structure here.
‫So we can see the E type in this case is two, which means this is an executable file.
‫You can see the machine that it was built for.
‫You can see the alpha version.
‫This is very important.
‫You can see the entry points of the program entry.
‫Now, this is the virtual address, OK?
‫The virtual address runtime we should run this file on here.
‫We can see the program header offset.
‫That's fifty two bytes into the file, OK.
‫Now remember I told you here this is the program had a table.
‫OK, so that's 52 by sins of the father.
‫According to this S.H. officer, this is our section header so that's four thousand nine hundred forty
‫eight bytes into the file.
‫And we can see on this how to file.
‫It tells us that we have four program headers in our program, had a table and we have 13 section headers
‫in our section had a table.
‫Now this here, S.H. String Index, this represents the string table for section names, but more will
‫be explained to that shortly.
‫So, yes, here we can see the string table.
‫The format can have many string tables, a string tables, simply a bunch and alternate strings and
‫a series of one another.
‫OK, so essentially that's all the string table is.
‫It's basically a bunch of null, terminated strings.
‫And inside our section structure, we will have basically indexes into these string tables and we can
‫go into the string table and read the string straight out of there.
‫OK, so you can see that the exported symbol names are in this string table.
‫We can also see that some section names are also in this string table.
‫Now, I do need to let you know that you can have a lot of different types of string tables.
‫There isn't just one.
‫So that does complicate things a bit, but it's not so bad.
‫So the program, Heather, is responsible for helping the slowed the program program had us have different
‫types of point to different information.
‫So you can see P2P had put into pre PT load.
‫We only have to focus on Patillo for now.
‫And you can also see virtual addresses here as well, guys.
‫OK, so it assumes that we've loaded the program at this point.
‫OK, how do you know the lowest base address while you go through all of the program headers and you
‫find the lowest readdressed value and that is the page that you should map from for your process.
‫OK, because obviously these are virtual addresses.
‫So that's the structure for the Pineda's as well, guys.
‫OK.
‫So let's explain the program had a load type, so this specifies that the program had it describes a
‫laudable segment.
‫So if I just go back once again, you can see I mentioned there's different types, OK, if the program
‫header is pretty, but this is how it should be treated, I'm about to explain it.
‫So one of the types, Patillo, this specifies the program had to describe the vertical segment.
‫The peafowl size specifies the size of the segments.
‫So we can see here that's four thousand four hundred eighty eight bytes and size for this segment.
‫The Peevey address specifies the virtual address for the start of this segment.
‫OK, so Pete Load specifies loadable segments.
‫So loadable segments contain things like code data and so on.
‫Now you can see P flags here.
‫This means it's readable segment, a rideable segment and executable segment.
‫OK, so these flags are very important because they determine how we should set up the rules in paging.
‫If there's no right flag here, then we shouldn't give the process right axis.
‫So this segment that we're loading here.
‫So let's now describe s..
‫How does these describe sections in the file like dot text, dot data and so on.
‫So Dot S.H. name represents an index in the string table, OK, the dot string tables section.
‫And I should mention that when the section had a type is as dynamic.
‫The S.H. Link variable actually points to the index of the section of a string table that should be
‫used for the data pointed to by this offset in the section table.
‫OK, but I just want to let you know that that doesn't make sense to you now.
‫But if you come down this road, you'll remember this.
‫Yeah, the offset is the offset in the file, OK?
‫And it's from the beginning, OK, from the beginning of the file.
‫So that maps to the section segment data.
‫OK, so I call them the same thing.
‫Section segments, they're the same thing.
‫OK.
‫It points to that data.
‫OK, so in this case the DOT text section starts absolute address four thousand eighty six in the L
‫file and we can see the S.H. address is where it's loaded, where it should be loaded if we was to load
‫this section straight into memory.
‫OK, so that's a virtual address.
‫Now we generally use the program headers to load the executable file, but you can load through the
‫sections as well.
‫There's many different ways to load nail file.
‫So that's another thing I want you to be aware of so we can see in this case, the DOT text section
‫is waiting bytes in size.
‫So it's a very small program.
‫And that's how it looks here.
‫OK?
‫That's how it looks in the C structure.
‫So one more thing I want to talk about here before we go to the next slide to get the S.H. name, we
‫go into the string table and we basically access index 27, OK?
‫And that's how we do it.
‫And when we access index 27, that will give us the actual string.
‫So I'm going to show you how that works.
‫Now, if we want to find the string table, we go into the Elfreda, we find the essay string index
‫variable.
‫We can see that's 12.
‫So we call our string table function.
‫We pass in the header the Elfreda.
‫And all of that does is it gets the address, the header in memory, because we load the entire outfall
‫into memory, by the way, guys, because it's just easier to work with when we do that.
‫In an ideal situation, you would map it into memory, but we don't have any maps.
‫So we're going to load the whole thing into memory because it's just easier to work with that way.
‫OK, so what we do is we cast the header to a charge point to here, OK, we plus the offset of the
‫L section specified in the S.H. String Index variable here in the header.
‫OK, so that's very simple.
‫The header plus the offset of of the string section gives us the string table, the string table data,
‫OK, and this section is just another function.
‫The result sections by index essentially.
‫OK, so that's how that works guys.
‫So we end up having the string table return to us and then if I just go back to slides then we can pass
‫in Index 27 here for the same name.
‫So it's just like accessing an array.
‫OK, we access the string table index 27 and that will give us the actual string dot text by here.
‫OK, so that's how that works.
‫This is very simple.
‫OK, returns to shop on the array and then we can just access it by index twenty seven and then we have
‫the dot text name.
‫So that's how the string tables work.
‫OK, this is important.
‫You, you're completely aware that.
‫So one of the thing we can see that in the header we have signature, OK, we have signature here,
‫074 PLF.
‫So that's how we can identify this in our file.
‫So Alaf is very big, it has a lot going on, I suggest searching for the OUTFALLS specification PDF
‫on Google for a full explanation.
‫I have shows you here all you need to know to make a basic health load that doesn't depend on any libraries.
‫So we will only be able to load static lf libraries, at least for the moment.
‫So thanks for watching.
‫I hope that brought you up to speed with the Urfa format we will soon be implementing in our Florida.