Example: Relocating addresses within an ELF file

This example that demonstrates how to use libelf operations to relocate addresses within the ELF file.

For the task steps, see Steps to relocate addresses within an ELF file.
In elfload.c, the following variables are hardcoded:

elfload.c

#include <stdlib.h>
#include "libelf/libelf.h"

/*********************************************************************
  Structure used to keep track of information within ELF
 *********************************************************************/
typedef unsigned long long int uint64;
typedef signed long long int   int64;
typedef char                   bool;

/*  ELF symbol details
*/
typedef struct ElfSymbol_s {
    char*               es_name;         /* ELF symbol: name              */
    uint64              es_value;        /*             value             */
    uint64              es_size;         /*             size              */
    unsigned char       es_type;         /*             type              */
    unsigned char       es_bind;         /*             bind              */
    unsigned char       es_other;        /*             other             */
    int64               es_shndx;        /*             ELF section index */
} *ElfSymbol;

/*  ELF file details
*/
typedef struct ElfDetails_s {
    Elf*                ed_elf;          /* ->ELF instance for CU          */
    bool                ed_is_64bit;     /* 64-bit: true                   */
                                         /* 32-bit: false                  */

    /* ELF Section details                                                 */
    Elf_Scn**           ed_elf_scns;     /* List of ->ELF scn objects      */
    char**              ed_scn_names;    /* List of ELF section names      */
    int64*              ed_infos;        /* List of section sh_info values */
    char**              ed_datas;        /* List of ->section data buffer  */
    uint64*             ed_data_sizes;   /* List of length of section data */
    int64               ed_n_elf_scns;   /* Number of ELF sections         */

    int64               ed_text_idx;     /* .text section index            */
    int64               ed_symtab_idx;   /* .symtab section index          */
    int64               ed_strtab_idx;   /* .strtab section index          */
    int64               ed_shstrtab_idx; /* .shstrtab section index        */

    /* ELF Symbol details                                                  */
    ElfSymbol           ed_symbols;      /* List of ->ELF symbol info      */
    uint64              ed_n_symbols;    /* Number of ELF symbols          */
} *ElfDetails;

/*--< Local Routines >-----------------------------------------------------*/

/* Examine ELF descriptor and find out all information necessary
   for relocating the .dbg.
   All information are stored in 'ret_details'.
   'ret_details' is deallocated with _load_elf_term
*/
static int
  _load_elf_file_details(
    Elf*                elf,             /* ->ELF instance for CU         I*/
    ElfDetails*         ret_details);    /* ->returned ELF file details   O*/

/* Terminate ELF loader processing, release resources
*/
static int
  _load_elf_term(
    ElfDetails          details);        /* ELF file details              I*/

/* Load 64-bit ELF symbol table
*/
static int
  _load_elf64_symbol_table(
    ElfDetails          details);        /* ELF file details              I*/

/* Load 32-bit ELF symbol table
*/
static int
  _load_elf32_symbol_table(
    ElfDetails          details);        /* ELF file details              I*/

/* Given the 16 byte raw MD5 signature, verify that it matches the loaded
   .dbg file
*/
static int
  _validate_MD5_signature(
    ElfDetails          details,         /* ELF file details              I*/
    unsigned char       digest[16]);     /* PPA2 MD5 signature            I*/

/* Relocate the ELF sections based on the information in 'reloc_adj'
*/
static int
  _relocate_elf_sections(
    ElfDetails          details,         /* ELF file details              I*/
    int64*              reloc_adj);      /* Adjustment array              I*/

/*--< Relocation main routines >-------------------------------------------*/
int
  relocate_elf_load_cu(
    Elf*                elf,             /* ->ELF instance for CU        IO*/
    unsigned char       md5_sig[16]);    /* MD5 signature                 I*/

/*--< FUNCTION IMPLEMENTATION >--------------------------------------------*/
int main ()
{
  Elf* elf;
  FILE *fp;
  int rc;
  unsigned char md5[16] = { 0x3C, 0x2C, 0x96, 0x82,
                            0x22, 0xFF, 0xB7, 0x24,
                            0x2B, 0x52, 0x53, 0x00,
                            0x65, 0x01, 0xF6, 0x0F };

  elf_version (EV_CURRENT);

  fp = fopen ("mytest.dbg", "rb");
  elf = elf_begin_b (fp, ELF_C_READ, NULL);

  rc = relocate_elf_load_cu (elf, md5);
  printf ("rc should be zero: %d\n", rc);

  elf_end(elf);
}

#pragma convert ("ISO8859-1")
/* Load ELF file and relocate .text to given address(es)
*/
int
  relocate_elf_load_cu(
    Elf*                elf,             /* ->ELF instance for CU        IO*/
    unsigned char       md5_sig[16]) {   /* MD5 signature                 I*/
  ElfDetails          details;
  int64*              reloc_adj;         /* An array keeping track of
                                            address adjustment needed for
                                            each .text symid               */
  int                 i;
  int                 rc;

  /* Load ELF file section and symbol tables */
  rc = _load_elf_file_details(elf, &details;);
  if (rc) return rc;

  /* Validate MD5 signature */
  rc = _validate_MD5_signature(details, md5_sig);
  if (rc) return rc;

  /* TO BE FILLED IN: create reloc_adj array */
  /* This will relocate 0x37b8 to 0xDEADBEEF */
  reloc_adj = (int64*) calloc (sizeof(int64), details->ed_n_symbols);
  reloc_adj[2] = 0xDEADBEEF;

  /* Relocate the ELF sections based on the current section origins */
  rc = _relocate_elf_sections(details, reloc_adj);
  if (rc) return rc;

  /* Processing complete.  Remove temporary tables */
  rc = _load_elf_term(details);
  if (rc) return rc;

  /* Terminate reloc_adj */
  free (reloc_adj);

  return 0;
}

/* Load ELF file section and symbol tables
*/
static int
  _load_elf_file_details(
    Elf*                elf,             /* ->ELF instance for CU         I*/
    ElfDetails*         ret_details) {   /* ->returned ELF file details   O*/

  ElfDetails          details;

  char*               ehdr_ident;

  Elf32_Ehdr*         ehdr32;
  Elf64_Ehdr*         ehdr64;

  Elf32_Shdr*         shdr32;
  Elf64_Shdr*         shdr64;

  Elf_Scn*            scn;

  Elf_Data*           data;

  char*               scn_name;

  Elf_Scn**           section_list;
  char**              name_list;
  char**              data_list;
  uint64*             data_size_list;
  int64*              info_list;

  int64               scn_idx,
                      n_elf_scns,
                      shstrtab_idx;

  int                 rc,
                      is_64bit,
                      elf_machine;

  /* Determine if 64-bit or 32-bit ELF file */
  if ((ehdr_ident = elf_getident(elf, NULL)) == NULL) {
    return -1; /* ERROR */
  }
  is_64bit = (ehdr_ident[EI_CLASS] == ELFCLASS64);

  /* Access the ELF file header */
  if (is_64bit) {
    if ((ehdr64 = elf64_getehdr(elf)) == NULL) {
      return -1; /* ERROR */
    }
    elf_machine  = ehdr64->e_machine;
    n_elf_scns   = ehdr64->e_shnum + 1;     /* Allow for section 0 */
    shstrtab_idx = ehdr64->e_shstrndx;
  }
  else {
    if ((ehdr32 = elf32_getehdr(elf)) == NULL) {
      return -1; /* ERROR */
    }
    elf_machine  = ehdr32->e_machine;
    n_elf_scns   = ehdr32->e_shnum + 1;     /* Allow for section 0 */
    shstrtab_idx = ehdr32->e_shstrndx;
  }

  /* Validate machine type */
  if (elf_machine != EM_S390) {
    return -1; /* ERROR */
  }

  /* Allocate the new ElfDetails object */
  if (n_elf_scns == 0) {
    return -1; /* ERROR */
  }

  details = (ElfDetails) calloc (sizeof(struct ElfDetails_s), 1);
  if (details == NULL) {
    return -2; /* out of memory */
  }

  /* Initialize the new object */
  details->ed_elf          = elf;
  details->ed_n_elf_scns   = n_elf_scns;
  details->ed_shstrtab_idx = shstrtab_idx;

  if (is_64bit) {
    details->ed_is_64bit = 1;
  }

  /* Allocate list object (array of Dwarf_Ptr) for the ELF sections */
  section_list = (Elf_Scn**) calloc (sizeof(Elf_Scn*), n_elf_scns);
  if (section_list == NULL) {
    return -2; /* out of memory */
  }
  details->ed_elf_scns = section_list;

  /* Allocate list object (array of char*) for the ELF section names */
  name_list = (char**) calloc (sizeof(char*), n_elf_scns);
  if (name_list == NULL) {
    return -2; /* out of memory */
  }
  details->ed_scn_names = name_list;

  /* Allocate list object (array of Dwarf_Ptr) for section data addrs */
  data_list = (char**) calloc (sizeof(char*), n_elf_scns);
  if (data_list == NULL) {
    return -2; /* out of memory */
  }
  details->ed_datas = data_list;

  /* Allocate addr object (array of uint64) for section data lengths */
  data_size_list = (uint64*) calloc (sizeof(uint64), n_elf_scns);
  if (data_size_list == NULL) {
    return -2; /* out of memory */
  }
  details->ed_data_sizes = data_size_list;

  /* Allocate addr object (array of int64) for section sh_info */
  info_list = (int64*) calloc (sizeof(int64), n_elf_scns);
  if (info_list == NULL) {
    return -2; /* out of memory */
  }
  details->ed_infos = info_list;

  /* Populate the ELF section lists */
  scn_idx = 0;
  scn     = NULL;
  while ((scn = elf_nextscn(elf,scn)) != NULL) {

    /* Save ELF section for section symbol lookup */
    scn_idx = elf_ndxscn(scn);
    if (scn_idx < n_elf_scns) {
      section_list[scn_idx] = scn;
    }
    else {
      return -1; /* ERROR */
    }

    /* Process ELF section header */
    if (is_64bit) {
      if ((shdr64 = elf64_getshdr(scn)) == NULL) {
        return -1; /* ERROR */
      }

      /* Get section name */
      if ((scn_name = elf_strptr(elf,
                                 shstrtab_idx,
                                 shdr64->sh_name)) == NULL) {
        return -1; /* ERROR */
      }

      info_list[scn_idx]   = shdr64->sh_info;
    }
    else {
      if ((shdr32 = elf32_getshdr(scn)) == NULL) {
        return -1; /* ERROR */
      }

      /* Get section name */
      if ((scn_name = elf_strptr(elf,
                                 shstrtab_idx,
                                 shdr32->sh_name)) == NULL) {
        return -1; /* ERROR */
      }

      info_list[scn_idx]   = shdr32->sh_info;
    }

    /* Note ELF Section names */
    name_list[scn_idx] = scn_name;

    /* Note index of ELF .text, .symtab, .strtab and .shstrtab sections */
    if (strcmp(scn_name,".text") == 0) {
      /* Validate .text is z/OS DWARF in ELF packing */
      if (is_64bit) {
        if (shdr64->sh_type != SHT_NOBITS) {
          return -1; /* ERROR */
        }
      }
      else {
        if (shdr32->sh_type != SHT_NOBITS) {
          return -1; /* ERROR */
        }
      }

      /* Validate there is only 1 .text section */
      if (details->ed_text_idx != 0) {
        return -1; /* ERROR */
      }
      details->ed_text_idx = scn_idx;
    }

    else if (strcmp(scn_name,".symtab") == 0) {
      /* Validate there is only 1 .symtab section */
      if (details->ed_symtab_idx != 0) {
        return -1; /* ERROR */
      }
      details->ed_symtab_idx = scn_idx;
    }

    else if (strcmp(scn_name,".strtab") == 0) {
      /* Validate there is only 1 .strtab section */
      if (details->ed_strtab_idx != 0) {
        return -1; /* ERROR */
      }
      details->ed_strtab_idx = scn_idx;
    }

    else if (strcmp(scn_name,".shstrtab") == 0) {
      /* Validate there is only 1 .shstrtab section */
      if (details->ed_shstrtab_idx != scn_idx) {
        return -1; /* ERROR */
      }
    }

    /* Prepare to read ELF section Data */
    if ((data = elf_getdata(scn, 0)) != NULL) {
      data_list[scn_idx]      = data->d_buf;
      data_size_list[scn_idx] = data->d_size;
    }
  }

  /* Ensure the file has all required sections */
  if ((details->ed_text_idx     == 0) ||
      (details->ed_symtab_idx   == 0) ||
      (details->ed_strtab_idx   == 0) ||
      (details->ed_shstrtab_idx == 0)) {
    return -1; /* ERROR */
  }

  /* Create the symbol table from the ELF .symtab section */
  if (details->ed_is_64bit) {
    rc = _load_elf64_symbol_table(details);
  }
  else {
    rc = _load_elf32_symbol_table(details);
  }
  if (rc) return rc;

  /* Return the ElfDetails object to the caller */
  *ret_details = details;

  return 0;
}

/* Terminate ELF loader processing, release resources
*/
static int
  _load_elf_term(
    ElfDetails          details) {       /* ELF file details              I*/

  /* Delete the resources for this ElfDetails object */
  if (details->ed_elf_scns != NULL) {
    free (details->ed_elf_scns);
  }

  if (details->ed_datas != NULL) {
    free (details->ed_datas);
  }

  if (details->ed_data_sizes != NULL) {
    free (details->ed_data_sizes);
  }

  if (details->ed_symbols != NULL) {
    free (details->ed_symbols);
  }

  free (details);

  return 0;
}

/* Load 64-bit ELF symbol table
*/
static int
  _load_elf64_symbol_table(
    ElfDetails          details) {       /* ELF file details              I*/
  Elf*                elf;

  Elf64_Shdr*         shdr64;
  Elf64_Sym*          symtab;

  ElfSymbol           symbols,
                      cur_sym;

  uint64              link,
                      shstrtab_idx;

  uint64              n_symbols,
                      i;

  elf = details->ed_elf;
  if (elf == NULL) {
    return -1; /* ERROR */
  }

  shstrtab_idx = details->ed_shstrtab_idx;

  /* Allocate the array of ElfSymbol objects */
  n_symbols = (details->ed_data_sizes[details->ed_symtab_idx]) /
                                              sizeof(Elf64_Sym);
  if (n_symbols == 0) {
    return -1; /* ERROR */
  }
  symbols = (ElfSymbol) calloc (sizeof(struct ElfSymbol_s), n_symbols);
  if (symbols == NULL) {
    return -2; /* Out of memory */
  }
  details->ed_symbols   = symbols;
  details->ed_n_symbols = n_symbols;

  /* Process the 64-bit .symtab section */
  cur_sym = symbols;
  symtab  = (Elf64_Sym*)(details->ed_datas[details->ed_symtab_idx]);
  link    = details->ed_strtab_idx;
  for (i = 0;
       i < n_symbols;
       i++, cur_sym++, symtab++) {
    cur_sym->es_value = symtab->st_value;
    cur_sym->es_size  = symtab->st_size;
    cur_sym->es_type  = ELF64_ST_TYPE(symtab->st_info);
    cur_sym->es_bind  = ELF64_ST_BIND(symtab->st_info);
    cur_sym->es_other = symtab->st_other;
    cur_sym->es_shndx = symtab->st_shndx;

    if (symtab->st_name == 0) {
      if (cur_sym->es_type == STT_SECTION) {
        if (cur_sym->es_shndx == SHN_UNDEF) {
          cur_sym->es_name = "undef";
        }
        else if (cur_sym->es_shndx == SHN_ABS) {
          cur_sym->es_name = "abs";
        }
        else if (cur_sym->es_shndx == SHN_COMMON) {
          cur_sym->es_name = "common";
        }
        else if (cur_sym->es_shndx < details->ed_n_elf_scns) {
          /* Get ELF section header */
          shdr64 = elf64_getshdr(details->ed_elf_scns[cur_sym->es_shndx]);
          if (shdr64 == NULL) {
            return -1; /* ERROR */
          }

          /* Get ELF section name */
          cur_sym->es_name = elf_strptr(details->ed_elf,
                                        shstrtab_idx,
                                        shdr64->sh_name);
        }
        else {
          cur_sym->es_name = "<Unknown section="">";
        }
      }
      else {
        /* Not section... note NULL */
        cur_sym->es_name = "<NULL>";
      }
    }
    else {
      cur_sym->es_name = elf_strptr(details->ed_elf,
                                    link,
                                    symtab->st_name);
    }

    if (cur_sym->es_name == NULL) {
      return -1; /* ERROR */
    }
  }

  return 0;
}

/* Load 32-bit ELF symbol table
*/
static int
  _load_elf32_symbol_table(
    ElfDetails          details) {       /* ELF file details              I*/
  Elf*                elf;

  Elf32_Shdr*         shdr32;
  Elf32_Sym*          symtab;

  ElfSymbol           symbols,
                      cur_sym;

  uint64      link,
                      shstrtab_idx;

  uint64      n_symbols,
                      i;

  elf = details->ed_elf;
  if (elf == NULL) {
    return -1; /* ERROR */
  }

  shstrtab_idx = details->ed_shstrtab_idx;

  /* Allocate the array of ElfSymbol objects */
  n_symbols = (details->ed_data_sizes[details->ed_symtab_idx]) /
                                              sizeof(Elf32_Sym);
  if (n_symbols == 0) {
    return -1; /* ERROR */
  }

  symbols = (ElfSymbol) calloc (sizeof(struct ElfSymbol_s), n_symbols);
  if (symbols == NULL) {
    return -2; /* Out of memory */
  }
  details->ed_symbols   = symbols;
  details->ed_n_symbols = n_symbols;

  /* Process the 32-bit .symtab section */
  cur_sym = symbols;
  symtab  = (Elf32_Sym*)(details->ed_datas[details->ed_symtab_idx]);
  link    = details->ed_strtab_idx;
  for (i = 0;
       i < n_symbols;
       i++, cur_sym++, symtab++) {
    cur_sym->es_value = symtab->st_value;
    cur_sym->es_size  = symtab->st_size;
    cur_sym->es_type  = ELF32_ST_TYPE(symtab->st_info);
    cur_sym->es_bind  = ELF32_ST_BIND(symtab->st_info);
    cur_sym->es_other = symtab->st_other;
    cur_sym->es_shndx = symtab->st_shndx;

    if (symtab->st_name == 0) {
      if (cur_sym->es_type == STT_SECTION) {
        if (cur_sym->es_shndx == SHN_UNDEF) {
          cur_sym->es_name = "undef";
        }
        else if (cur_sym->es_shndx == SHN_ABS) {
          cur_sym->es_name = "abs";
        }
        else if (cur_sym->es_shndx == SHN_COMMON) {
          cur_sym->es_name = "common";
        }
        else if (cur_sym->es_shndx < details->ed_n_elf_scns) {
          /* Get ELF section header */
          shdr32 = elf32_getshdr(details->ed_elf_scns[cur_sym->es_shndx]);
          if (shdr32 == NULL) {
            return -1; /* ERROR */
          }

          /* Get ELF section name */
          cur_sym->es_name = elf_strptr(details->ed_elf,
                                        shstrtab_idx,
                                        shdr32->sh_name);
        }
        else {
          cur_sym->es_name = "<Unknown section="">";
        }
      }
      else {
        /* Not section... note NULL */
        cur_sym->es_name = "<NULL>";
      }
    }
    else {
      cur_sym->es_name = elf_strptr(details->ed_elf,
                                    link,
                                    symtab->st_name);
    }

    if (cur_sym->es_name == NULL) {
      return -1; /* ERROR */
    }
  }

  return 0;
}

/* Given the 16 byte raw MD5 signature, verify that it matches the loaded
   .dbg file
*/
static int
  _validate_MD5_signature(
    ElfDetails          details,         /* ELF file details              I*/
    unsigned char       digest[16]) {    /* PPA2 MD5 signature            I*/
  ElfSymbol           symbols,
                      cur_sym;

  unsigned char       md5_chars[32+1];
  unsigned char*      sym_name;

  uint64              n_symbols,
                      i,
                      pos;

  symbols   = details->ed_symbols;
  n_symbols = details->ed_n_symbols;
  if ((symbols   == NULL) ||
      (n_symbols ==    0)) {
    return -1; /* ERROR */
  }

  /* Generate text for MD5 signature portion of symbol */
  for (i = 0, pos = 0; i < 16; i++ ) {
    const char *  convstring = "0123456789ABCDEF";
    char          top_nibble,
                  bottom_nibble;

    top_nibble     = digest[i] >> 4;
    bottom_nibble  = digest[i] & 0x0F;
    md5_chars[pos] = convstring[top_nibble];
    pos++;
    md5_chars[pos] = convstring[bottom_nibble];
    pos++;
  }
  md5_chars[pos] = 0x00;

  /* Scan the symbol table for the first symobl in .text that resemble MD5 signature */
  for (i = 0, cur_sym = symbols;
       i < n_symbols;
       i++,   cur_sym++) {
    const int sym_name_len = strlen(cur_sym->es_name);
    sym_name = cur_sym->es_name;
    if (cur_sym->es_shndx == details->ed_text_idx &&
        sym_name_len >= 32 &&
        !strcmp(sym_name+sym_name_len-32, md5_chars)) {
      /* matching MD5 signature found */
      return 0;
    }
  }

  /* MD5 signature not found */
  return -1;
}

/* Relocate the ELF sections based on the relocation adjustments array
   'reloc_adj' is an array containing adjustments that needs to be
   made to each corresponding relocation entry.
   For example:
   Typical .symtab entries:
    Sym  2: value= 0x000, ...,  name= .MD5_3FD489E1D88CB743682E3A44875A1765
    Sym  3: value= 0x010, ...,  name= func1
    Sym  4: value= 0x020, ...,  name= func2
    Sym  5: value= 0x050, ...,  name= func3
   If all relocation base on sym 2, and it needs to adjust to 0xDEADBEEF, then
   'reloc_adj' would contain:
   { 0, 0, 0xDEADBEEF, 0, 0, 0 }
           ^-- index 2 correspond to sym 2
*/
static int
  _relocate_elf_sections(
    ElfDetails          details,         /* ELF file details              I*/
    int64*              reloc_adj) {     /* .text relocation adjustments  I*/
  ElfSymbol           symbols,
                      cur_sym;

  uint64              reloc_offset;
  uint64              reloc_sym;
  int64               reloc_scn;

  char**              scn_names;
  int64*              infos;

  unsigned int        reloc_type;

  char*               scn_name,
      *               relscn_name,
      *               sym_name,
      *               reloc_scn_name,
      *               reloc_name;

  char**              datas;
  uint64*             data_sizes;

  int64               relscn_idx;

  char*               reloc_data;
  char*               relscn_data;
  uint64              reloc_data_size,
                      reloc_data_off,
                      relscn_data_size;

  int64               n_elf_scns,
                      change;

  uint64              n_symbols,
                      i;

  n_elf_scns   = details->ed_n_elf_scns;
  n_symbols    = details->ed_n_symbols;
  scn_names    = details->ed_scn_names;
  symbols      = details->ed_symbols;
  datas        = details->ed_datas;
  data_sizes   = details->ed_data_sizes;
  infos        = details->ed_infos;

  if ((n_symbols    ==    0) ||
      (n_elf_scns   ==    0) ||
      (scn_names    == NULL) ||
      (symbols      == NULL) ||
      (datas        == NULL) ||
      (data_sizes   == NULL)) {
    return -1; /* ERROR */
  }

  /* Scan section lists, processing SHT_REL-format relocation sections */
  for (i = 1;
       i < n_elf_scns;
       i++) {

    /* Check for ELF SHT_REL-format section */
    scn_name = scn_names[i];
    if (strncmp(scn_name, ".rel.",5) == 0) {

      /* Access relocation section info */
      reloc_data      = datas[i];
      reloc_data_size = data_sizes[i];

      /* Access data section info */
      relscn_idx       = infos[i];
      relscn_name      = scn_names[relscn_idx];
      relscn_data      = datas[relscn_idx];
      relscn_data_size = data_sizes[relscn_idx];

      if (details->ed_is_64bit) {
        /* Relocate all R_390_64 type relocation entries */
        for (reloc_data_off = 0;
             reloc_data_off < reloc_data_size;
             reloc_data_off += sizeof(Elf64_Rel)) {
          Elf64_Rel* p = (Elf64_Rel*)(reloc_data + reloc_data_off);

          reloc_offset = p->r_offset;

          reloc_sym = ELF64_R_SYM(p->r_info);
          if (reloc_sym >=  n_symbols) {
            return -1; /* ERROR */
          }

          cur_sym   = symbols + reloc_sym;
          reloc_scn = cur_sym->es_shndx;
          if (reloc_scn >=  n_elf_scns) {
            return -1; /* ERROR */
          }

          reloc_type = ELF64_R_TYPE(p->r_info);
          switch (reloc_type) {

            case R_390_NONE :
                 /* No adjustment required... likely DWARF info */
                 break;

            case R_390_32 : {
                   /* Check for relocation adjustment */
                   signed int*    relscn_ptr;
                   signed int     reloc_item;

                   change = reloc_adj[reloc_sym];
                   if (change != 0) {
                     relscn_ptr  = (signed int*)(relscn_data + reloc_offset);
                     reloc_item  = *relscn_ptr;
                     *relscn_ptr = reloc_item + change;
                   }
                 }
                 break;

            case R_390_64 : {
                   /* Check for relocation adjustment */
                   int64* relscn_ptr;
                   int64  reloc_item;

                   change = reloc_adj[reloc_sym];
                   if (change != 0) {
                     relscn_ptr  = (int64*)(relscn_data + reloc_offset);
                     reloc_item  = *relscn_ptr;
                     *relscn_ptr = reloc_item + change;
                   }
                 }
                 break;

            default :
                 return -1; /* ERROR */
          }
        }
      }

      else {
        /* Relocate all R_390_32 type relocation entries */
        for (reloc_data_off = 0;
             reloc_data_off < reloc_data_size;
             reloc_data_off += sizeof(Elf32_Rel)) {
          Elf32_Rel* p = (Elf32_Rel*)(reloc_data + reloc_data_off);

          reloc_offset = p->r_offset;

          reloc_sym = ELF32_R_SYM(p->r_info);
          if (reloc_sym >=  n_symbols) {
            return -1; /* ERROR */
          }

          cur_sym   = symbols + reloc_sym;
          fflush(NULL);
          reloc_scn = cur_sym->es_shndx;
          if (reloc_scn >=  n_elf_scns) {
            return -1; /* ERROR */
          }

          reloc_type = ELF32_R_TYPE(p->r_info);
          switch (reloc_type) {

            case R_390_NONE :
                 /* No adjustment required... likely DWARF info */
                 break;

            case R_390_32 : {
                   /* Check for relocation adjustment */
                   signed int*    relscn_ptr;
                   signed int     reloc_item;

                   change = reloc_adj[reloc_sym];
                   if (change != 0) {
                     relscn_ptr  = (signed int*)(relscn_data + reloc_offset);
                     reloc_item  = *relscn_ptr;
                     *relscn_ptr = reloc_item + change;
                   }
                 }
                 break;

            case R_390_64 : {
                   /* Check for relocation adjustment */
                   int64* relscn_ptr;
                   int64  reloc_item;

                   change = reloc_adj[reloc_sym];
                   if (change != 0) {
                     relscn_ptr  = (int64*)(relscn_data + reloc_offset);
                     reloc_item  = *relscn_ptr;
                     *relscn_ptr = reloc_item + change;
                   }
                 }
                 break;

            default :
                 return -1; /* ERROR */
          }
        }
      }
    }

  }

  return 0;
}
#pragma convert (0)