This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] Fix section corruption bug


On Tuesday 10 June 2014 11:48:15 Mark Wielaard wrote:
> On Mon, 2014-06-09 at 21:05 +0200, Thilo Schulz wrote:
> > When adding data to existing sections in ELF files, libelf may corrupt
> > those sections, i.e. overwrite the existing data if certain conditions
> > are met.
> > 
> > If an Elf_Scn structure has seen a call to elf_rawdata(scn) before but no
> > call to elf_getdata(scn), scn->read_data flag is set, but not
> > scn->data_list_rear.
> 
> Do you happen to have a small testcase that shows the buggy behavior?

Sure. This is an excerpt from the final program. A short word on what it is 
supposed to do in my practical application:
I am doing a project for the AVR platform, which is mixed C/assembly. For 
command parser functions that are called out of assembly into C code, I need 
to replace the final return statements with rjmps back to 
program code in an object file generated from my assembly.
So I want to  add new symbols and relocations to a cmd.o file.

Find as attachment a simple program, which checks for the presence of the two 
symbols cmd_response and cmd_noresponse and adds them if they don't exist.
Since the AVR architecture is 8 bit, I am only using Elf32_ structures 
throughout, so you may need to compile test .o objects with -m32 for the test 
program to work on them. 

> I was wondering whether we want to check scn->rawdata.s directly, or if
> we could rely on ELF_F_FILEDATA being set for scn->flags?

Seems reasonable though I don't know the code as well as you do I guess.

As a further note: A similar bug, albeit for slightly different reasons, occurs 
when adding relocations. Adding a relocation with elf_newdata() then 
elf_update() 
results in the old data being "forgotten" if there was no elf_getdata() call 
before to load that data into memory. The cause is a bit different because in 
this case, there was not a call to elf_rawdata() before and this still 
happened. I imagine, this might also be a problem for string tables.

-- 
Best regards,
Thilo Schulz
/*
===========================================================================
Copyright (C) 2014 Essential Nature (Thilo Schulz)

Fix the command parse functions so as to replace ret
with an rjmp to the appropriate location
===========================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sysexits.h>
#include <string.h>
#include <errno.h>

#include <libelf.h>
#include <gelf.h>

int isbe = 0;
int found_resp, found_noresp;
Elf32_Sym fresp, fnoresp;

void do_help(char *progname)
{
  printf("Usage: %s [options] <filename>\n"
         "Modify the elf files for the command parser so as to use rjmp instead\n"
         "of ret instruction to return to main interrupt service routine.\n\n"
         "Options:\n"
         "  -h                          Print this help\n"
         "\n",
         progname);
}

Elf_Scn *get_section(Elf *obj, Elf32_Word sh_type, const char *secname)
{
  Elf_Scn *cursec;
  Elf32_Shdr *curhdr;
  size_t shstrndex;
  char *name;
  
  if(elf_getshdrstrndx(obj, &shstrndex))
  {
    fprintf(stderr, "Couldn't get list of section names: %s\n", elf_errmsg(-1));
    exit(EX_DATAERR);
  }

  cursec = NULL;
  
  while((cursec = elf_nextscn(obj, cursec)))
  {
    if(!(curhdr = elf32_getshdr(cursec)))
    {
      fprintf(stderr, "Couldn't get section header: %s\n", elf_errmsg(-1));
      exit(EX_DATAERR);
    }
    
    if(name = elf_strptr(obj, shstrndex, curhdr->sh_name))
    {
      if(curhdr->sh_type == sh_type && (!secname || !strcmp(name, secname)))
        return cursec;
    }
  }
  
  return NULL;
}

size_t add_stringtotable(Elf *obj, size_t tableind, char *str)
{
  Elf_Scn *strsec;
  Elf32_Shdr *strhdr;
  Elf_Data *data;
  size_t lastofs;

  if(!(strsec = elf_getscn(obj, tableind)))
  {
    fprintf(stderr, "Couldn't get section to string table %zd: %s\n", elf_errmsg(-1));
    exit(EX_DATAERR);
  }

  if(!(strhdr = elf32_getshdr(strsec)))
  {
    fprintf(stderr, "Couldn't get header of string table\n");
    exit(EX_DATAERR);
  }
  
  // Workaround for bug in libelf
//  data = elf_getdata(strsec, NULL);
  
  if(!(data = elf_newdata(strsec)))
  {
    fprintf(stderr, "Couldn't add data to string table: %s\n", elf_errmsg(-1));
    exit(EX_DATAERR);
  }
  
  data->d_align = 1;
  data->d_buf = str;
  data->d_size = strlen(str) + 1;
  data->d_type = ELF_T_BYTE;
  
  lastofs = strhdr->sh_size;
  strhdr->sh_size += data->d_size;
  
  return lastofs;
}

void add_undefinedsym(Elf *obj, Elf_Scn *scn, Elf32_Sym *dest, size_t tableind, char *str)
{
  Elf_Data *data;
  GElf_Sym undef;

  if(!(data = elf_newdata(scn)))
  {
    fprintf(stderr, "Couldn't add symbol %s to symbol table: %s\n", str, elf_errmsg(-1));
    exit(EX_DATAERR);
  }

  undef.st_name = add_stringtotable(obj, tableind, str);
  undef.st_value = 0;
  undef.st_size = 0;
  undef.st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE);
  undef.st_other = STV_DEFAULT;
  undef.st_shndx = 0;

  data->d_align = 4;
  data->d_buf = dest;
  data->d_size = sizeof(*dest);
  data->d_type = ELF_T_SYM;

  if(!gelf_update_sym(data, 0, &undef))
  {
    fprintf(stderr, "Couldn't add symbol %s to symbol table: %s\n", str, elf_errmsg(-1));
    exit(EX_DATAERR);
  }
}

int modify_elf(const char *fname)
{
  int fd;
  Elf *obj;
  Elf32_Ehdr *ehdr;
  Elf_Scn *symsec, *progsec;
  Elf32_Shdr *symsechdr;
  Elf_Data *data, *progdata;
  GElf_Sym sym;
  char *name;
  int numsym, index;
  
  if(elf_version(EV_CURRENT) == EV_NONE)
  {
    fprintf(stderr, "Could not initialize ELF library: %s\n", elf_errmsg(-1));
    return EX_SOFTWARE;
  }
  
  if((fd = open(fname, O_RDWR, 0)) < 0)
  {
    fprintf(stderr, "Couldn't open elf file: %s\n", strerror(errno));
    return EX_NOINPUT;
  }
  
  if(!(obj = elf_begin(fd, ELF_C_RDWR, NULL)))
  {
    fprintf(stderr, "Couldn't parse elf file: %s\n", elf_errmsg(-1));
    return EX_DATAERR;
  }

  if(!(ehdr = elf32_getehdr(obj)))
  {
    fprintf(stderr, "Failed to retrieve elf header: %s\n", elf_errmsg(-1));
    return EX_DATAERR;
  }

  if((ehdr->e_ident[EI_DATA] & ELFDATA2MSB))
    isbe = 1;

  symsec = get_section(obj, SHT_SYMTAB, ".symtab");
  
  if(!symsec)
  {
    fprintf(stderr, "No symbol section found\n");
    return EX_DATAERR;
  }
  
  data = NULL;
  if(!(data = elf_getdata(symsec, data)) || !data->d_size)
  {
    fprintf(stderr, "No data associated with symbol section\n");
    return EX_DATAERR;
  }

  if(!(symsechdr = elf32_getshdr(symsec)))
  {
    fprintf(stderr, "Couldn't get header of symbol section\n");
    return EX_DATAERR;
  }
  
  numsym = 0;
  found_resp = 0;
  found_noresp = 0;
  
  while(gelf_getsym(data, numsym, &sym))
  {
    name = elf_strptr(obj, symsechdr->sh_link, sym.st_name);

    if(!strcmp(name, "cmd_response"))
      found_resp = numsym;
    else if(!strcmp(name, "cmd_noresponse"))    
      found_noresp = numsym;

    numsym++;
  }
  
  /*
   * Make sure symbol table contains undefined symbols cmd_response and cmd_noresponse
   */

  if(!found_noresp)
  {
    add_undefinedsym(obj, symsec, &fnoresp, symsechdr->sh_link, "cmd_noresponse");
    found_noresp = numsym++;
  }

  if(!found_resp)
  {
    add_undefinedsym(obj, symsec, &fresp, symsechdr->sh_link, "cmd_response");
    found_resp = numsym++;
  }

  elf_update(obj, ELF_C_WRITE);
  
  elf_end(obj);
  
  if(close(fd) < 0)
  {
    fprintf(stderr, "Couldn't close elf file: %s\n", strerror(errno));
    return EX_NOINPUT;
  }
}

int main(int argc, char **argv)
{
  int opt;
  
   if(argc < 2)
  {
    if(argc < 1)
      do_help("elf-fixcmd");
    else
      do_help(argv[0]);

    return EX_OK;
  }
  
  while((opt = getopt(argc, argv, "h")) != -1)
  {
    switch(opt)
    {
      case 'h':
        do_help(argv[0]);
        return EX_OK;
      break;
      default:
        return EX_USAGE;
    }
  }
  
  return modify_elf(argv[optind]);
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]