<commit_before>/*
 * Copyright (c) 2006, Swedish Institute of Computer Science
 * All rights reserved. 
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 * 3. Neither the name of the Institute nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software 
 *    without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE. 
 *
 * @(#)$Id: cle.c,v 1.4 2007/04/25 15:41:02 bg- Exp $
 */

/*
 * The Contiki dynamic Link Editor (CLE), ELF version.
 */

#include <stdio.h>
#include <string.h>

#include "contiki.h"

#include "loader/cle.h"
#include "loader/elf32.h"
#include "loader/sym.h"

//#define NDEBUG
#include "lib/assert.h"

#ifdef NDEBUG
#define PRINTF(...) do {} while (0)
#else
#define PRINTF(...) printf(__VA_ARGS__)
#endif

#define NOLL 0

/*
 * Parse object file located at offset hdr reading data using function
 * pread. Save what is useful in info.
 */
int
cle_read_info(struct cle_info *info,
	      int (*pread)(void *, int, off_t),
	      off_t hdr)
{
  /*
   * Save stackspace by using a union!
   *
   * Beware that the contents of ehdr is gone when shdr is written!!!
   */
  union {
    struct elf32_ehdr ehdr;
    struct elf32_shdr shdr;
  } huge;
#define ehdr huge.ehdr
#define shdr huge.shdr

  off_t shoff; 
  cle_off strs;
  cle_half shnum;		/* number shdrs */
  cle_half shentsize;		/* sizeof shdr */
  cle_word strtabsize = 0;
  int i, ret;

  memset(info, 0x0, sizeof(*info));

  ret = pread(&ehdr, sizeof(ehdr), hdr);
  assert(ret > 0);

  /* Make sure that we have a correct and compatible ELF header. */
  if(memcmp(ehdr.e_ident, ELF_MAGIC_HEADER, ELF_MAGIC_HEADER_SIZE) != 0) {
    return CLE_BAD_HEADER;
  }

  shoff = hdr + ehdr.e_shoff;
  shentsize = ehdr.e_shentsize;
  shnum = ehdr.e_shnum;

  /* The string table section: holds the names of the sections. */
  ret = pread(&shdr, sizeof(shdr), shoff + shentsize*ehdr.e_shstrndx);
  assert(ret > 0);

  /* BEWARE THAT ehdr IS NOW OVERWRITTEN!!! */

  /*
   * Get a pointer to the actual table of strings. This table holds
   * the names of the sections, not the names of other symbols in the
   * file (these are in the symtab section).
   */
  strs = shdr.sh_offset;

  /*
   * The ".text" segment holds the actual code from the ELF file, the
   * ".data" segment contains initialized data, the ".bss" segment
   * holds the size of the unitialized data segment. The ".rela.text"
   * and ".rela.data" segments contains relocation information for the
   * contents of the ".text" and ".data" segments, respectively. The
   * ".symtab" segment contains the symbol table for this file. The
   * ".strtab" segment points to the actual string names used by the
   * symbol table.
   *
   * In addition to grabbing pointers to the relevant sections, we
   * also save the section index for resolving addresses in the
   * relocator code.
   */
  for(i = 0; i < shnum; ++i) {
    ret = pread(&shdr, sizeof(shdr), shoff);
    assert(ret > 0);
    
    /* The name of the section is contained in the strings table. */
    ret = pread(info->name, sizeof(info->name), hdr + strs + shdr.sh_name);
    assert(ret > 0);

    if(strncmp(info->name, ".text", 5) == 0) {
      info->textoff = shdr.sh_offset;
      info->textsize = shdr.sh_size;
      info->text_shndx = i;
    } else if(strncmp(info->name, ".rela.text", 10) == 0) {
      info->textrelaoff = shdr.sh_offset;
      info->textrelasize = shdr.sh_size;
    } else if(strncmp(info->name, ".data", 5) == 0) {
      info->dataoff = shdr.sh_offset;
      info->datasize = shdr.sh_size;
      info->data_shndx = i;
    } else if(strncmp(info->name, ".rela.data", 10) == 0) {
      info->datarelaoff = shdr.sh_offset;
      info->datarelasize = shdr.sh_size;
    } else if(strncmp(info->name, ".symtab", 7) == 0) {
      info->symtaboff = shdr.sh_offset;
      info->symtabsize = shdr.sh_size;
    } else if(strncmp(info->name, ".strtab", 7) == 0) {
      info->strtaboff = shdr.sh_offset;
      strtabsize = shdr.sh_size;
    } else if(strncmp(info->name, ".bss", 4) == 0) {
      info->bsssize = shdr.sh_size;
      info->bss_shndx = i;
    } else {
      info->name[sizeof(info->name) - 1] = 0;
      PRINTF("cle: unknown section %.12s\n", info->name);
    }

    /* Move on to the next section header. */
    shoff += shentsize;
  }

  if(info->symtabsize == 0) {
    return CLE_NO_SYMTAB;
  }
  if(strtabsize == 0) {
    return CLE_NO_STRTAB;
  }
  if(info->textsize == 0) {
    return CLE_NO_TEXT;
  }

  return CLE_OK;
}

/*
 * Update one reloc.
 *
 * Writing relocs is machine dependent and this function is MSP430
 * specific!
 */
#ifdef __MSP430__
static inline int
cle_upd_reloc(void *segmem, struct elf32_rela *rela, cle_addr addr)
{
  memcpy((char *)segmem + rela->r_offset, &addr, 2); /* Write reloc */
  return 0;
}
#else
static int
cle_upd_reloc(void *segmem, struct elf32_rela *rela, cle_addr addr);
#endif


/*
 * Relocate one segment that has been copied to the location pointed
 * to by segmem.
 *
 * Relocation info is read from offset reloff to (reloff + relsize)
 * and the start of the object file is at hdr. Data is read using
 * function pread.
 */
int
cle_relocate(struct cle_info *info,
	     int (*pread)(void *, int, off_t),
	     off_t hdr,		/* Offset to start of file. */
	     void *segmem,      /* Where segment is stored in memory. */
	     cle_off reloff,	/* .rela.<segment> start */
	     cle_word relsize)	/* .rela.<segment> size */
{
  struct elf32_rela rela;
  struct elf32_sym s;
  off_t off;
  cle_addr addr;
  int ret;
  
  for(off = hdr + reloff;
      off < hdr + reloff + relsize;
      off += sizeof(struct elf32_rela)) {
    ret = pread(&rela, sizeof(rela), off);
    assert(ret > 0);
    ret = pread(&s, sizeof(s),
	       hdr + info->symtaboff
	       + sizeof(struct elf32_sym)*ELF32_R_SYM(rela.r_info));
    assert(ret > 0);

    if(s.st_shndx == info->bss_shndx) {
      addr = (cle_addr)info->bss;
    } else if(s.st_shndx == info->data_shndx) {
      addr = (cle_addr)info->data;
    } else if(s.st_shndx == info->text_shndx) {
      addr = info->text;
    } else {
      addr = NOLL;
    }

    if(s.st_name == 0) {	/* No name, local symbol? */
      if(addr == NOLL) {
	return CLE_UNKNOWN_SEGMENT;
      }
    } else {
      ret = pread(info->name, sizeof(info->name),
		  hdr + info->strtaboff + s.st_name);
      assert(ret > 0);
      cle_addr sym = (cle_addr)sym_object(info->name);

      if(addr == NOLL && sym != NOLL) { /* Imported symbol. */
	addr = sym;
      } else if(addr != NOLL && sym == NOLL) { /* Exported symbol. */
	addr = addr + s.st_value;
      } else if(addr == NOLL && sym == NOLL) {
	PRINTF("cle: undefined reference to %.32s (%d)\n",
	       info->name, s.st_info);
	return CLE_UNDEFINED;	/* Or COMMON symbol. */
      } else if(addr != NOLL && sym != NOLL) {
	PRINTF("cle: multiple definitions of %.32s (%d)\n",
	       info->name, s.st_info);
	return CLE_MULTIPLY_DEFINED;
      }
    }

    addr += rela.r_addend;

    ret = cle_upd_reloc(segmem, &rela, addr);
    if(ret < 0) {
      return CLE_UNKNOWN_SEGMENT;
    }
  }
  return CLE_OK;
}

/*
 * Search object file located at offset hdr using function
 * pread. Search for symbol named symbol and return its address after
 * relocation or NULL on failure.
 */
void *
cle_lookup(struct cle_info *info,
	   int (*pread)(void *, int, off_t),
	   off_t hdr,		/* Offset to start of file. */
	   const char *symbol)

{
  struct elf32_sym s;
  off_t a;
  cle_addr addr;
  int ret;

  for(a = hdr + info->symtaboff;
      a < hdr + info->symtaboff + info->symtabsize;
      a += sizeof(s)) {
    ret = pread(&s, sizeof(s), a);
    assert(ret > 0);

    if(s.st_name != 0) {
      ret = pread(info->name, sizeof(info->name),
		 hdr + info->strtaboff + s.st_name);
      assert(ret > 0);

      if(strcmp(info->name, symbol) == 0) { /* Exported symbol found. */
	if(s.st_shndx == info->bss_shndx) {
	  addr = (cle_addr)info->bss;
	} else if(s.st_shndx == info->data_shndx) {
	  addr = (cle_addr)info->data;
	} else if(s.st_shndx == info->text_shndx) {
	  addr = info->text;
	} else {
	  return NULL;		/* Really an error! */
	}
	return addr + s.st_value;
      }
    }
  }
  return NULL;
}<commit_msg>* Add GNUC AVR support.<commit_after>/*
 * Copyright (c) 2006, Swedish Institute of Computer Science
 * All rights reserved. 
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 * 3. Neither the name of the Institute nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software 
 *    without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE. 
 *
 * @(#)$Id: cle.c,v 1.5 2007/04/26 12:52:52 bg- Exp $
 */

/*
 * The Contiki dynamic Link Editor (CLE), ELF version.
 */

#include <stdio.h>
#include <string.h>

#include "contiki.h"

#include "loader/cle.h"
#include "loader/elf32.h"
#include "loader/sym.h"

#define NDEBUG
#include "lib/assert.h"

#ifdef NDEBUG
#define PRINTF(...) do {} while (0)
#else
#define PRINTF(...) printf(__VA_ARGS__)
#endif

#define NOLL 0

#ifdef __AVR__
/*
 * On the AVR, GNU C squeezes function addresses into 16 bits. Some of
 * this code is explicitly written to deal with this.
 */
#ifndef __GNUC__
#eror "You lose!!!"
#endif
#endif

/*
 * Parse object file located at offset hdr reading data using function
 * pread. Save what is useful in info.
 */
int
cle_read_info(struct cle_info *info,
	      int (*pread)(void *, int, off_t),
	      off_t hdr)
{
  /*
   * Save stackspace by using a union!
   *
   * Beware that the contents of ehdr is gone when shdr is written!!!
   */
  union {
    struct elf32_ehdr ehdr;
    struct elf32_shdr shdr;
  } huge;
#define ehdr huge.ehdr
#define shdr huge.shdr

  off_t shoff; 
  cle_off strs;
  cle_half shnum;		/* number shdrs */
  cle_half shentsize;		/* sizeof shdr */
  cle_word strtabsize = 0;
  int i, ret;

  memset(info, 0x0, sizeof(*info));

  ret = pread(&ehdr, sizeof(ehdr), hdr);
  assert(ret > 0);

  /* Make sure that we have a correct and compatible ELF header. */
  if(memcmp(ehdr.e_ident, ELF_MAGIC_HEADER, ELF_MAGIC_HEADER_SIZE) != 0) {
    return CLE_BAD_HEADER;
  }

  shoff = hdr + ehdr.e_shoff;
  shentsize = ehdr.e_shentsize;
  shnum = ehdr.e_shnum;

  /* The string table section: holds the names of the sections. */
  ret = pread(&shdr, sizeof(shdr), shoff + shentsize*ehdr.e_shstrndx);
  assert(ret > 0);

  /* BEWARE THAT ehdr IS NOW OVERWRITTEN!!! */

  /*
   * Get a pointer to the actual table of strings. This table holds
   * the names of the sections, not the names of other symbols in the
   * file (these are in the symtab section).
   */
  strs = shdr.sh_offset;

  /*
   * The ".text" segment holds the actual code from the ELF file, the
   * ".data" segment contains initialized data, the ".bss" segment
   * holds the size of the unitialized data segment. The ".rela.text"
   * and ".rela.data" segments contains relocation information for the
   * contents of the ".text" and ".data" segments, respectively. The
   * ".symtab" segment contains the symbol table for this file. The
   * ".strtab" segment points to the actual string names used by the
   * symbol table.
   *
   * In addition to grabbing pointers to the relevant sections, we
   * also save the section index for resolving addresses in the
   * relocator code.
   */
  for(i = 0; i < shnum; ++i) {
    ret = pread(&shdr, sizeof(shdr), shoff);
    assert(ret > 0);
    
    /* The name of the section is contained in the strings table. */
    ret = pread(info->name, sizeof(info->name), hdr + strs + shdr.sh_name);
    assert(ret > 0);

    if(strncmp(info->name, ".text", 5) == 0) {
      info->textoff = shdr.sh_offset;
      info->textsize = shdr.sh_size;
      info->text_shndx = i;
    } else if(strncmp(info->name, ".rela.text", 10) == 0) {
      info->textrelaoff = shdr.sh_offset;
      info->textrelasize = shdr.sh_size;
    } else if(strncmp(info->name, ".data", 5) == 0) {
      info->dataoff = shdr.sh_offset;
      info->datasize = shdr.sh_size;
      info->data_shndx = i;
    } else if(strncmp(info->name, ".rela.data", 10) == 0) {
      info->datarelaoff = shdr.sh_offset;
      info->datarelasize = shdr.sh_size;
    } else if(strncmp(info->name, ".symtab", 7) == 0) {
      info->symtaboff = shdr.sh_offset;
      info->symtabsize = shdr.sh_size;
    } else if(strncmp(info->name, ".strtab", 7) == 0) {
      info->strtaboff = shdr.sh_offset;
      strtabsize = shdr.sh_size;
    } else if(strncmp(info->name, ".bss", 4) == 0) {
      info->bsssize = shdr.sh_size;
      info->bss_shndx = i;
    } else {
      info->name[sizeof(info->name) - 1] = 0;
      PRINTF("cle: unknown section %.12s\n", info->name);
    }

    /* Move on to the next section header. */
    shoff += shentsize;
  }

  if(info->symtabsize == 0) {
    return CLE_NO_SYMTAB;
  }
  if(strtabsize == 0) {
    return CLE_NO_STRTAB;
  }
  if(info->textsize == 0) {
    return CLE_NO_TEXT;
  }

  return CLE_OK;
}

/*
 * Update one reloc.
 *
 * Writing relocs is machine dependent and this function is MSP430
 * specific!
 */
#ifdef __MSP430__
static inline int
cle_upd_reloc(unsigned char *segmem, struct elf32_rela *rela, cle_addr addr)
{
  memcpy((char *)segmem + rela->r_offset, &addr, 2); /* Write reloc */
  return CLE_OK;
}
#else
static int
cle_upd_reloc(unsigned char *segmem, struct elf32_rela *rela, cle_addr addr);
#endif


/*
 * Relocate one segment that has been copied to the location pointed
 * to by segmem.
 *
 * Relocation info is read from offset reloff to (reloff + relsize)
 * and the start of the object file is at hdr. Data is read using
 * function pread.
 */
int
cle_relocate(struct cle_info *info,
	     int (*pread)(void *, int, off_t),
	     off_t hdr,		/* Offset to start of file. */
	     void *segmem,      /* Where segment is stored in memory. */
	     cle_off reloff,	/* .rela.<segment> start */
	     cle_word relsize)	/* .rela.<segment> size */
{
  struct elf32_rela rela;
  struct elf32_sym s;
  off_t off;
  cle_addr addr;
  int ret;
  
  for(off = hdr + reloff;
      off < hdr + reloff + relsize;
      off += sizeof(struct elf32_rela)) {
    ret = pread(&rela, sizeof(rela), off);
    assert(ret > 0);
    ret = pread(&s, sizeof(s),
	       hdr + info->symtaboff
	       + sizeof(struct elf32_sym)*ELF32_R_SYM(rela.r_info));
    assert(ret > 0);

    if(s.st_shndx == info->bss_shndx) {
      addr = (cle_addr)(uintptr_t)info->bss;
    } else if(s.st_shndx == info->data_shndx) {
      addr = (cle_addr)(uintptr_t)info->data;
    } else if(s.st_shndx == info->text_shndx) {
      addr = info->text;
    } else {
      addr = NOLL;
    }

    if(s.st_name == 0) {	/* No name, local symbol? */
      if(addr == NOLL) {
	return CLE_UNKNOWN_SEGMENT;
      }
    } else {
      ret = pread(info->name, sizeof(info->name),
		  hdr + info->strtaboff + s.st_name);
      assert(ret > 0);
      cle_addr sym = (cle_addr)(uintptr_t)sym_function(info->name);
#ifdef __AVR__
      if(sym != NOLL)
	sym = sym << 1;
#endif
      if(sym == NOLL)
	sym = (cle_addr)(uintptr_t)sym_object(info->name);

      if(addr == NOLL && sym != NOLL) { /* Imported symbol. */
	addr = sym;
      } else if(addr != NOLL && sym == NOLL) { /* Exported symbol. */
	addr = addr + s.st_value;
      } else if(addr == NOLL && sym == NOLL) {
	PRINTF("cle: undefined reference to %.32s (%d)\n",
	       info->name, s.st_info);
	return CLE_UNDEFINED;	/* Or COMMON symbol. */
      } else if(addr != NOLL && sym != NOLL) {
	PRINTF("cle: multiple definitions of %.32s (%d)\n",
	       info->name, s.st_info);
	return CLE_MULTIPLY_DEFINED;
      }
    }

    addr += rela.r_addend;

    ret = cle_upd_reloc(segmem, &rela, addr);
    if(ret != CLE_OK) {
      return ret;
    }
  }
  return CLE_OK;
}

/*
 * Search object file located at offset hdr using function
 * pread. Search for symbol named symbol and return its address after
 * relocation or NULL on failure.
 */
void *
cle_lookup(struct cle_info *info,
	   int (*pread)(void *, int, off_t),
	   off_t hdr,		/* Offset to start of file. */
	   const char *symbol)

{
  struct elf32_sym s;
  off_t a;
  cle_addr addr;
  int ret;

  for(a = hdr + info->symtaboff;
      a < hdr + info->symtaboff + info->symtabsize;
      a += sizeof(s)) {
    ret = pread(&s, sizeof(s), a);
    assert(ret > 0);

    if(s.st_name != 0) {
      ret = pread(info->name, sizeof(info->name),
		 hdr + info->strtaboff + s.st_name);
      assert(ret > 0);

      if(strcmp(info->name, symbol) == 0) { /* Exported symbol found. */
	if(s.st_shndx == info->bss_shndx) {
	  addr = (cle_addr)(uintptr_t)info->bss;
	} else if(s.st_shndx == info->data_shndx) {
	  addr = (cle_addr)(uintptr_t)info->data;
	} else if(s.st_shndx == info->text_shndx) {
	  addr = info->text;
#ifdef __AVR__
	  return (void *)(uintptr_t)((addr + s.st_value) >> 1);
#endif
	} else {
	  return NULL;		/* Really an error! */
	}

	return (void *)(uintptr_t)(addr + s.st_value);
      }
    }
  }
  return NULL;
}

#if defined(__AVR__) && defined(__GNUC__)
#define R_AVR_NONE             0
#define R_AVR_32               1
#define R_AVR_7_PCREL          2
#define R_AVR_13_PCREL         3
#define R_AVR_16               4
#define R_AVR_16_PM            5
#define R_AVR_LO8_LDI          6
#define R_AVR_HI8_LDI          7
#define R_AVR_HH8_LDI          8
#define R_AVR_LO8_LDI_NEG      9
#define R_AVR_HI8_LDI_NEG     10
#define R_AVR_HH8_LDI_NEG     11
#define R_AVR_LO8_LDI_PM      12
#define R_AVR_HI8_LDI_PM      13
#define R_AVR_HH8_LDI_PM      14
#define R_AVR_LO8_LDI_PM_NEG  15
#define R_AVR_HI8_LDI_PM_NEG  16
#define R_AVR_HH8_LDI_PM_NEG  17
#define R_AVR_CALL            18

static int
cle_upd_reloc(unsigned char *segmem, struct elf32_rela *rela, cle_addr addr)
{
  unsigned char *instr = segmem + rela->r_offset;
  unsigned char byte;

  switch(ELF32_R_TYPE(rela->r_info)) {
  default:
    PRINTF("cle_upd_reloc: unsupported relocation type: %d\n",
	   ELF32_R_TYPE(rela->r_info));
    return CLE_UNKNOWN_RELOC;

#if VERIFY_BEFORE_ENABLE
  case R_AVR_7_PCREL:		/* 2 */
    /* Reloc in bits 0x03f8 (0000 00kk kkkk k000). */
    byte = (addr - rela->r_offset - 2)/2;
    instr[0] = (instr[0] & 0x07) | (byte << 3);	/* 0xf8 */
    instr[1] = (instr[1] & 0xfc) | (byte >> 5);	/* 0x03 */
    return CLE_OK;

  case R_AVR_13_PCREL:		/* 3 */
    /* Reloc in bits 0x0fff (0000 kkkk kkkk kkkk). */
    addr = (addr - rela->r_offset - 2)/2;
    instr[0] = addr;
    instr[1] = (instr[1] & 0xf0) | ((addr >> 8) & 0x0f);
    return CLE_OK;
#endif

  case R_AVR_CALL:		/* 18 */
    addr = addr >> 1;
    instr[2] = addr;
    instr[3] = addr >> 8;
    return CLE_OK;
    
  case R_AVR_16:		/* 4 */
    instr[0] = addr;
    instr[1] = addr >> 8;
    return CLE_OK;

  case R_AVR_16_PM:		/* 5 */
    addr = addr >> 1;
    instr[0] = addr;
    instr[1] = addr >> 8;
    return CLE_OK;

    /*
     * Remaining relocs all have immediate value in bits 0x0f0f.
     */
  case R_AVR_LO8_LDI:		/* 6 */
    byte = addr;
    break;
    
  case R_AVR_HI8_LDI:		/* 7 */
    byte = addr >> 8;
    break;

  case R_AVR_HH8_LDI:		/* 8 */
    byte = addr >> 16;
    break;

  case R_AVR_LO8_LDI_NEG:	/* 9 */
    byte = (-addr);
    break;

  case R_AVR_HI8_LDI_NEG:	/* 10 */
    byte = (-addr) >> 8;
    break;

  case R_AVR_HH8_LDI_NEG:	/* 11 */
    byte = (-addr) >> 16;
    break;

  case R_AVR_LO8_LDI_PM:	/* 12 */
    byte = addr >> 1;
    break;

  case R_AVR_HI8_LDI_PM:	/* 13 */
    byte = addr >> 9;
    break;

  case R_AVR_HH8_LDI_PM:	/* 14 */
    byte = addr >> 17;
    break;

  case R_AVR_LO8_LDI_PM_NEG:	/* 15 */
    byte = (-addr) >> 1;
    break;

  case R_AVR_HI8_LDI_PM_NEG:	/* 16 */
    byte = (-addr) >> 9;
    break;

  case R_AVR_HH8_LDI_PM_NEG:	/* 17 */
    byte = (-addr) >> 17;
    break;
  }
  /* Relocation in bits 0x0f0f (0000 kkkk 0000 kkkk). */
  instr[0] = (instr[0] & 0xf0) | (byte & 0x0f);
  instr[1] = (instr[1] & 0xf0) | (byte >> 4);

  return CLE_OK;
}
#endif /* __AVR__ */
{'fraction_non_alphanumeric': 0.1283826024828079, 'fraction_numerical': 0.017772617665446102, 'mean_word_length': 2.8268967874231032, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 185, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3345790', 'n_tokens_mistral': 8667, 'n_tokens_neox': 8072, 'n_words': 2398}