diff options
Diffstat (limited to 'compilation/elf.e')
| -rw-r--r-- | compilation/elf.e | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/compilation/elf.e b/compilation/elf.e new file mode 100644 index 0000000..f39a1a0 --- /dev/null +++ b/compilation/elf.e @@ -0,0 +1,179 @@ +~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~ ~~ Executable file format ~~ +~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~ +~ Before we do anything specific to the actual program we're building, we +~ do a lot of ELF-specific stuff to ensure that our output is in a format +~ Linux knows how to run. +~ +~ This relies on the label facility defined in compilation/labels.e. Make +~ sure to load that first. +~ +~ This file makes use of magic comments for the hex transform, described in +~ detail in compilation/transform.e, but its use is trivial and doesn't +~ require accounting conventions. + +~ ~~~~~~~~~~~~~~~~~~~~~ +~ ~~ ELF file header ~~ +~ ~~~~~~~~~~~~~~~~~~~~~ +~ +~ First, we output ELF's top-level file header. This header describes the +~ entire file. An ELF always has exactly one of this header, which is always +~ at the start of the file. +~ +~ The program we're building should call this word as the first output it +~ generates. +~ +~ The most interesting thing here is the entry pointer. +~ +~ It's also worth noticing that we set the ABI version to 3. An ABI version +~ of 0 (for the System V ABI) would work fine too, but 3 allows FreeBSD's +~ Linux compatibility features to kick in. + +: elf-file-header + ~ : blank-line + ~ : ELF file header + ~ : indent + ~ : * denotes mandatory fields according to breadbox + current-offset 3unroll + + ~ : 1 suppress + 0x7f pack8 s" ELF" pack-raw-string ~ : -4 *magic number + 2 pack8 ~ : -1 64-bit + 1 pack8 ~ : -1 little-endian + 1 pack8 ~ : -1 ELF header format v1 + 3 pack8 ~ : -1 Linux ABI + 0 pack64 ~ : -8 (padding) + ~ : blank-line + + 2 pack16 ~ : -2 *executable + 0x3e pack16 ~ : -2 *Intel x86-64 + 1 pack32 ~ : -4 ELF format version + ~ : blank-line + + L@' cold-start L@' origin + pack64 ~ : -8 *entry point + ~ This includes the origin, intentionally. + + L@' elf-program-header pack64 ~ : -8 *program header offset + ~ We place the program header immediately after the ELF header. This + ~ offset is from the start of the file. + 0 pack64 ~ : -8 section header offset + 0 pack32 ~ : -4 processor flags + ~ : blank-line + + L@' elf-header-size pack16 ~ : -2 ELF header size + L@' elf-program-header-size pack16 ~ : -2 *program header entry size + 1 pack16 ~ : -2 *number of program header entries + 0 pack16 ~ : -2 section header entry size + 0 pack16 ~ : -2 number of section header entries + 0 pack16 ~ : -2 section name string table index + + ~ Though hardcoding the size of this header would work fine, it's easier + ~ to use the label system to keep track of its size. The only place this is + ~ actually referenced is right here in the header. + current-offset 4 roll - L!' elf-header-size + L' elf-header-size set-label-non-offset + ~ : deindent + ; + + +~ ~~~~~~~~~~~~~~~~~~~~~~~~ +~ ~~ ELF program header ~~ +~ ~~~~~~~~~~~~~~~~~~~~~~~~ +~ +~ Second, we output ELF's program header, which lists the memory regions +~ ("segments") we want to have and where we want them to come from. There may +~ be any number of these entries, one per segment, , and they may be anywhere +~ in the file as long as they're consecutive. +~ +~ We list just a single region, which is the entire contents of the ELF file +~ from disk, and we put the program header immediately after the file header. +~ The program we're building should call this word as the second output it +~ generates. +~ +~ It would be more typical to use this header to ask the loader to give us +~ separate code and data segments, and perhaps a stack or heap, but this keeps +~ things simple, and we can create those things for ourselves later. +~ +~ We do have a little stack space available, though we don't explicitly +~ request any; the kernel allocates it for us as part of exec() so that it can +~ pass us argc and argv (which we ignore). That stack space will be at a +~ random address, different every time, because of ASLR; that's a neat +~ security feature, so we leave it as-is. Note that ASLR doesn't happen when +~ you run under gdb, so if you aren't seeing it, that's probably why. + +~ ~~ +: elf-program-header + ~ : blank-line + ~ : ELF program header + ~ : indent + ~ * denotes mandatory fields according to breadbox + current-offset L!' elf-program-header + current-offset 3unroll + + 1 pack32 ~ : -4 *"loadable" segment type + 0x05 pack32 ~ : -4 *read+execute permission + 0 pack64 ~ : -8 *offset in file + L@' origin pack64 ~ : -8 *virtual address + ~ required, but can be anything, subject to alignment + 0 pack64 ~ : -8 physical address (ignored) + ~ : blank-line + + L@' total-size pack64 ~ : -8 *size in file + L@' total-size pack64 ~ : -8 *size in memory + ~ : blank-line + + 0 pack64 ~ : -8 segment alignment + ~ for relocation, but this doesn't apply to us + + ~ As with the file header, we use the label system to keep track of the + ~ program header's size. + current-offset 4 roll - L!' elf-program-header-size + L' elf-program-header-size set-label-non-offset + ~ : deindent + ; + + +~ This alternate version of the program header isn't used by evoke.e, but +~ may be useful for other programs, such as hex.e. Its only difference from +~ the base version is that it adds the write permission. +: elf-program-header-writable + ~ : blank-line + ~ : ELF program header + ~ : indent + ~ * denotes mandatory fields according to breadbox + current-offset L!' elf-program-header + current-offset 3unroll + + 1 pack32 ~ : -4 *"loadable" segment type + 0x07 pack32 ~ : -4 *read+write+execute permission + 0 pack64 ~ : -8 *offset in file + L@' origin pack64 ~ : -8 *virtual address + ~ required, but can be anything, subject to alignment + 0 pack64 ~ : -8 physical address (ignored) + ~ : blank-line + + L@' total-size pack64 ~ : -8 *size in file + L@' total-size pack64 ~ : -8 *size in memory + ~ : blank-line + + 0 pack64 ~ : -8 segment alignment + ~ for relocation, but this doesn't apply to us + + ~ As with the file header, we use the label system to keep track of the + ~ program header's size. + current-offset 4 roll - L!' elf-program-header-size + L' elf-program-header-size set-label-non-offset + ~ : deindent + ; + +~ ~~~~~~~~~~~~~~~~ +~ ~~ That's it! ~~ +~ ~~~~~~~~~~~~~~~~ +~ +~ ELF is a simple format, really. Now you can output your own machine code +~ that you generate however you want; make sure to define the labels "origin" +~ and "cold-start". Origin will control the address the code loads at; +~ cold-start will be the first thing that runs. The origin is arbitrary, but +~ can't be zero. + |