🌐
Atakua
atakua.org › old-wp › wp-content › uploads › 2015 › 03 › libelf-by-example-20100112.pdf pdf
libelf by Example Joseph Koshy January 12, 2010
libelf library can then interpret the rest of the ELF executable header ... The e type member determines the type of the ELF object. For example,
🌐
Leanpub
leanpub.com › libelfbyexample
Libelf by Example [Leanpub PDF/iPad/Kindle]
ar archives: their structure, and how to read the contents of these archives using the APIs provided by libelf. The example programs in this tutorial are written in the C programming language.
🌐
Scribd
scribd.com › document › 337260515 › Libelf-by-Example
Libelf by Example | PDF | Library (Computing) | String (Computer Science)
March 8, 2012 - Getting Started Let us dive in and get a taste of programming with libelf. ... Our first program (Program 1, listing 2.1) will open a filename presented to it on its command line and retrieve the file type as recognized by the ELF library. This example is covers the basics involved in using libelf; how to compile a program using libelf, how to initialize the library, how to report errors, and how to wind up.
🌐
GitHub
github.com › spurious › elftoolchain-mirror › blob › master › documentation › libelf-by-example › libelf-by-example.tex
elftoolchain-mirror/documentation/libelf-by-example/libelf-by-example.tex at master · spurious/elftoolchain-mirror
Along the way we will look at the way \library{libelf} handles ... These parameters are stored in the ELF executable header. Let us now ... ELF class and endianness for us. ... GELF(3) API set for this example.
Author   spurious
🌐
Tuto-computer
tuto-computer.com › home › others
Libelf by Example - Free Libelf tutorial in PDF
May 15, 2021 - - How data is stored inside ELF sections, the ELF Section Header Table, and how to traverse the sections in an ELF object. - How to create new ELF objects: the rules for ordering individual API calls, the default object layout implemented by libelf, and how to specify custom layouts.
🌐
GitHub
github.com › WolfgangSt › libelf
GitHub - WolfgangSt/libelf: libelf · GitHub
Installation is straightforward - the package is autoconf'ed. Just do ``cd libelf-0.8.12; ./configure; make; make install''. Header files will be installed in .../include/libelf/. If your system does not provide its own versions of libelf.h, nlist.h or gelf.h, ``make install'' will add the missing headers.
Starred by 87 users
Forked by 28 users
Languages   C 92.5% | Makefile 6.0%
🌐
GitHub
github.com › 0intro › libelf
GitHub - 0intro/libelf: Libelf is a simple library to read ELF files. · GitHub
Libelf is a simple library which provides functions to read ELF files.
Starred by 56 users
Forked by 14 users
Languages   C 99.0% | Makefile 1.0%
🌐
SourceForge
sourceforge.net › home › open source software › software development › build tools › elf tool chain › files
ELF Tool Chain - Browse /Documentation/libelf-by-example at SourceForge.net
README This release directory contains larger pieces of documentation such as tutorials. - libelf-by-example/ Contains released versions of the ``libelf by Example'' tutorial.
Top answer
1 of 1
9

Short Answer

You can't !

The functionality you are looking for is actually part of a build tool called "linker". Despite the fact that besides some unresolved symbol errors it throws from time to time its presence often goes unnoticed it's one of the most important components of any build-chain.

Long Answer

Here some ideas how to proceed in to somehow get your binary to run.

Restrictions

Any of the methods described below will only work if

  • The machine code doesn't contain any jumps with absolute addresses as patching them to the right destinations would require relocation info.

  • The program starts at the very beginning of the binary file used as input

    This should be easy to circumvent either by adding an additional (relative) jump instruction to the "right" spot at the start of the file of by using an offset into the binary data.

Suggested Workaround

In case of a very simple "self-contained" contained binary given just by a bunch of raw machine instructions without any external dependencies and without (!) any absolute jump instructions instead of doing it by hand it might be easier to just use an already existing linker instead.

Given a file consisting of the raw machine instructions (main.bin in the following example) the first step would involve generating a shared object (main.o in the example) from it:

objcopy -I binary -B i386 -O elf32-i386 --rename-section .data=.text main.bin main.o

Taking a look at the generated objects symbol table readelf -S:

Symbol table '.symtab' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _binary_main_bin_start
     3: 0000000c     0 NOTYPE  GLOBAL DEFAULT    1 _binary_main_bin_end
     4: 0000000c     0 NOTYPE  GLOBAL DEFAULT  ABS _binary_main_bin_size

You'll notice that the symbols _binary_..._start, _binary_..._end and _binary_..._size according to start, end and size of the input file were added. These can be used to hand the entry point to the executable down to the linker.

ld --entry=_binary_main_bin_start main.o -o main

should produce the executable you are looking for.

Manual Generation

Alternatively you might want to manually create an elf file just containing necessary information to get a running executable.

If you're not too familiar with the elf format you might want to take a look at the specs (available on: http://refspecs.linuxfoundation.org/). Also the manual page (man elf) is very exhaustive, so this might be good source of information too.

To keep it most simple the goal will be to just use what's absolutely necessary.

Taking a look into the specs you'll see the only component required under any circumstances is the elf header. A section header table is only required for shared objects, a program header table only for executables.

As we want to create an executable we'll only use the program header table with one single entry of type PT_LOAD describing the whole memory layout of the executable.

To meet alignment constrains the process image will contain the whole contents of the binary.(source: man elf).

... Loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size.

This being said it should be clear why the final layout of the elf file will look like this:

struct Binary {
  Elf32_Ehdr ehdr;
  Elf32_Phdr phdr;
  char code[];
};

Most fields of Elf32_Ehdr an Elf32_Phdr are fixed, so they can already be set in the initializer. The only fields that require later adjustments are the fields describing the sizes (.p_filesz and .p_memsz) of the loaded segment in the program header table entry.

Taking input from stdin and writing to stdout (thus used like ./a.out <main.bin >executable) this is the way the described setup could be implemented:

#include <stdio.h>
#include <stddef.h>
#include <elf.h>
#include <string.h>
#include <stdlib.h>

#define BUFFER_SIZE 1024
char buffer[BUFFER_SIZE];

void *read_all (int *filesize) {
  void *data = NULL;
  int offset = 0;
  int size = 0;

  while ((size = fread (buffer, 1, sizeof (buffer), stdin)) > 0) {
    if ((data = realloc (data, offset + size)) == NULL)
      exit (-1);
    memcpy (data + offset, buffer, size);
    offset += size;
  }
  *filesize = offset;
  return data;
}


#define LOAD_ADDRESS 0x8048000

struct Binary {
  Elf32_Ehdr ehdr;
  Elf32_Phdr phdr;
  char code[];
};

int main (int argc, char *argv[]) {

  void *code;
  int code_size;

  struct Binary binary = {
    /* ELF HEADER */
    .ehdr = {
      /* general */
      .e_ident   = {
        ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
        ELFCLASS32, 
        ELFDATA2LSB,
        EV_CURRENT,
        ELFOSABI_LINUX,
      },
      .e_type    = ET_EXEC,
      .e_machine = EM_386,
      .e_version = EV_CURRENT,
      .e_entry   = LOAD_ADDRESS + (offsetof (struct Binary, code)),
      .e_phoff   = offsetof (struct Binary, phdr),
      .e_shoff   = 0,
      .e_flags   = 0,
      .e_ehsize   = sizeof (Elf32_Ehdr),
      /* program header */
      .e_phentsize = sizeof (Elf32_Phdr),
      .e_phnum     = 1,
      /* section header */
      .e_shentsize = sizeof (Elf32_Shdr),
      .e_shnum     = 0,
      .e_shstrndx  = 0
    },

    /* PROGRAM HEADER */
    .phdr = {
      .p_type   = PT_LOAD,
      .p_offset = 0,
      .p_vaddr = LOAD_ADDRESS,
      .p_paddr = LOAD_ADDRESS,
      .p_filesz = 0,
      .p_memsz = 0,
      .p_flags = PF_R | PF_X,
      .p_align = 0x1000
    }
  };

  if ((code = read_all (&code_size)) == NULL)
    return -1;

  /* fix program header */
  binary.phdr.p_filesz = sizeof (struct Binary) + code_size;
  binary.phdr.p_memsz = sizeof (struct Binary) + code_size;

  /* write binary */
  fwrite (&binary, sizeof (struct Binary), 1, stdout);
  fwrite (code, 1, code_size, stdout);

  free (code);

  return 0;
}
Find elsewhere
🌐
GNU
sourceware.org › elfutils
The elfutils project
libelf · elf32, elf64 and gelf functions to read, modify and create ELF files. libdw · dwarf, dwfl and dwelf functions to read DWARF, find separate debuginfo, symbols and inspect process state. libasm · asm and disasm functions to assemble and disassamble instructions (partial support for ...
🌐
The Mail Archive
mail-archive.com › elftoolchain-developers@lists.sourceforge.net › msg00017.html
[Elftoolchain-developers] a detailed example of libelf and libdwarf as "the APIs to nowhere" and two suggestions for improvement in their documentation
July 24, 2014 - This example shows how to use libelf to load an elf, get the number of entries in the program header table (which seem to be another name for what one might call "segment headers") using elf_getphdrnum(), and how to get the program header itself (a GElf_Phdr ) using gelf_getphdr().
🌐
GitHub
github.com › sergev › elftoolchain › tree › master › documentation › libelf-by-example
elftoolchain/documentation/libelf-by-example at master · sergev/elftoolchain
libelf-by-example.tex · libelf-by-example.tex · prog1.txt · prog1.txt · prog2.txt · prog2.txt · prog3.txt · prog3.txt · prog4.txt · prog4.txt · prog5.txt · prog5.txt · prog6.txt · prog6.txt · View all files ·
Author   sergev
🌐
GitHub
github.com › spurious › elftoolchain-mirror › tree › master › documentation › libelf-by-example
elftoolchain-mirror/documentation/libelf-by-example at master · spurious/elftoolchain-mirror
libelf-by-example.tex · libelf-by-example.tex · prog1.txt · prog1.txt · prog2.txt · prog2.txt · prog3.txt · prog3.txt · prog4.txt · prog4.txt · prog5.txt · prog5.txt · prog6.txt · prog6.txt · View all files ·
Author   spurious
🌐
Blogger
em386.blogspot.com › 2007 › 03 › quick-libelf-guide.html
EM_386: Quick LibELF Guide
March 14, 2007 - So I thought a good blog entry on how to use libelf would be beneficial to others. Below is a link to heavily commented C code on using libelf to read the sections and symbols of an ELF object. Libelf Example in C It's an example of how to read an ELF objects section header and symbol table.
🌐
SourceForge
sourceforge.net › home › browse › elf tool chain › news
ELF Tool Chain / News: Revised "libelf by Example" tutorial
March 8, 2012 - http://sourceforge.net/projects/elftoolchain/files/Documentation/libelf-by-example/20120308/ Posted by 2012-03-08
🌐
SourceForge
sourceforge.net › home › browse › elf tool chain › wiki
ELF Tool Chain / Wiki / libelf
This implementation of libelf implements the full SVR4 ELF(3)/GELF(3) API set, along with a few minor extensions.
🌐
Dbooks
dbooks.org › measurement and control of charged particle beams
Libelf by Example.pdf - Free download books
- How to create new ELF objects: the rules for ordering individual API calls, the default object layout implemented by libelf, and how to specify custom layouts. - ar archives: their structure, and how to read the contents of these archives using the APIs provided by libelf. The example programs in this tutorial are written in the C programming language.
Top answer
1 of 2
6

First, it would be a good idea during testing to replace the code fragment containing (naughty) shell code with something harmless, say:

unsigned char code[] = {
    0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */
    0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */
    0xCD, 0x80            /* int $0x80 */
};

On an i386 GNU/Linux system this revised code fragment causes the process to immediately exit with an exit code of 42.

The following ASCII art illustrates the layout of the ELF executable being built:

+----------------------------------+  <- LOADADDR (0x08048000)
|  The ELF Exec Header.            |  
+----------------------------------+
|  The ELF PHDR Table.             |
+----------------------------------+ <- ehdr->e_entry points here.
|  The ".text" section.            |
+----------------------------------+ <- The end of loadable region
|  The section name string table   |    for this object.
|  (optional).                     |
+----------------------------------+
|  Section headers:                |
|  - Header for section ".text".   |
|  - Section name string table     |
|    header.                       |
+----------------------------------+

The section name string table is optional. It helps to neaten the output of readelf.

#define LOADADDR    0x08048000

The executable will be loaded at the virtual address named by LOADADDR. The value for LOADADDR is system dependent---a value of 0x08048000 seems to work well on my system.

The executable code fragment is placed just after the PHDR table. The e_entry field of the ELF Executable Header holds the virtual address to which control will be transferred to. The value of the field should therefore be:

size_t ehdrsz, phdrsz;

ehdrsz = elf32_fsize(ELF_T_EHDR, 1, EV_CURRENT);
phdrsz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT);

/* ... */

ehdr->e_entry = LOADADDR + ehdrsz + phdrsz;

Code segments would use a data type of ELF_T_BYTE and a section type of SHT_PROGBITS, with an alignment of 1.

if ((scn = elf_newscn(e)) == NULL)
    errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1));

if ((data = elf_newdata(scn)) == NULL)
    errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1));

data->d_align = 1;
data->d_off = 0LL;
data->d_buf = code;
data->d_type = ELF_T_BYTE;
data->d_size = sizeof(code);
data->d_version = EV_CURRENT;

The sh_addr field of the section header table entry holds the virtual address of the start of the section's data.

if ((shdr = elf32_getshdr(scn)) == NULL)
   errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1));

shdr->sh_name = 1;      /* Offset of ".text", see below. */
shdr->sh_type = SHT_PROGBITS;
shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
shdr->sh_addr = LOADADDR + ehdrsz + phdrsz;

The sole entry in the ELF Program Header table covers the area to be loaded, starting with the ELF header and including the executable code.

if ((phdr = elf32_newphdr(e,1)) == NULL)
   errx(EX_SOFTWARE,"elf32_newphdr %s\n", elf_errmsg(-1));

phdr->p_type = PT_LOAD;
phdr->p_offset = 0;
phdr->p_filesz = ehdrsz + phdrsz + sizeof(code);
phdr->p_memsz = phdr->p_filesz;
phdr->p_vaddr = LOADADDR;
phdr->p_paddr = phdr->p_vaddr;
phdr->p_align = 4;
phdr->p_flags = PF_X | PF_R;

A section name string table is optional, and makes for nicer output from readelf. A hand-rolled string table suffices:

unsigned char strtab[] = {
    0, '.', 't', 'e', 'x', 't', 0,
    '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0
};

The code to add the string table to the executable is:

/*
 * Allocate a string table for section names.
 */
if ((scn = elf_newscn(e)) == NULL)
   errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1));

if ((data = elf_newdata(scn)) == NULL)
   errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1));

data->d_align = 1;
data->d_off = 0LL;
data->d_buf = strtab;
data->d_type = ELF_T_BYTE;
data->d_size = sizeof(strtab);
data->d_version = EV_CURRENT;

if ((shdr = elf32_getshdr(scn)) == NULL)
   errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1));   

shdr->sh_name = 7;      /* Offset of ".shstrtab". */
shdr->sh_type = SHT_STRTAB;
shdr->sh_flags = SHF_STRINGS;

With these changes the ELF binary created by your program should be runnable.

% cc a.c -lelf
% ./a.out foo
% ./foo; echo $?
42

The structure of the generated executable would be as follows:

% readelf -a foo
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048054
  Start of program headers:          52 (bytes into file)
  Start of section headers:          116 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         1
  Size of section headers:           40 (bytes)
  Number of section headers:         3
  Section header string table index: 2
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048054 000054 00000c 00  AX  0   0  1
  [ 2] .shstrtab         STRTAB          00000000 000060 000011 00   S  0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00060 0x00060 R E 0x4
 Section to Segment mapping:
  Segment Sections...
   00     .text 
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
2 of 2
3

The reason kernel refuses to run your program is quite simple:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000034 0x08040800 0x08040800 0x00021 0x00021 R E 0x4

This is invalid executable because the kernel can not mmap your .text with offset 0x34 at virtual address 0x08040800. The file offset and the VirtAddr must have the same alignment.

Usually the first LOAD segment just includes the ELF header itself, i.e. has Offset of 0 (you would want to set size to 0x55 (== 0x21 + 0x34)). Alternatively, you can arrange for Offset to stay at 0x000034 and have a VirtAddr of 0x08040834.