Source file: /~heha/hs/avrpp.zip/src/loadelf.cpp

#include "avrpp.h"
#include <stdio.h>
#include <string.h>	// strcmp

struct ELFHDR {
 union{
  struct{
   byte magic[4];	// "\x7FELF"
   byte eclass;		// 1 for 32-bit
   byte data;		// 1 for little endian, 2 for big endian
   byte version;	// (1)
   byte pad[9];		// (0xFF, then 0x00)
  };
  dword dw[4];
 }ident;
 word type;
 enum{
  T_REL		=  1,	// object file
  T_EXEC	=  2,	// executable file
  T_DYN		=  3,	// shared object
  T_CORE	=  4,	// core file
 };
 word machine;
 enum{
  M_386		=  3,
  M_68K		=  4,
  M_PPC		= 20,
  M_68HC11	= 70,
  M_AVR		= 83,
  M_XTENSA	= 94,
  M_MSP430	=105,
  M_BLACKFIN	=106,
  M_C166	=116,
  M_8051	=165,
  M_AVR32	=185,
  M_STM8	=186,
  M_MCHP_PIC	=204,
 };
 dword version;		// (1) [following values seen for MSP430]
 dword entry;		// (00004400)	start address
 dword phoff;		// (00000034)	program header table follows this struct
 dword shoff;		// (0000AF78)	section header table
 dword flags;		// (00000036)	(machine dependent)
 word ehsize;		// (0034)	this size
 word phentsize;	// (0020)	header table entry size (32 byte)
 word phnum;		// (0002)	header table entries (2)
 word shentsize;	// (0028)	section header entry size (40 byte)
 word shnum;		// (000D)	section header entries (13)
 word shstrndx;		// (000A)	index of section name string table (10)
};

// Loaders (i.e. the OS) use the Program Header and ignore segment names.
struct ProgHdr32 {
 dword	type,		// Headers with type != PT_LOAD (=1) are ignored
	offset,		// offset of data bits in ELF file
	vaddr,		// not needed (VMA)
	paddr,		// offset where to load into flash (LMA)
	filesz,		// size of section in file (0 for .bss)
	memsz,		// not needed
	flags,		// not needed
	align;		// not needed
};

// Parsers (i.e. the linker, objcopy etc.) use the Section Header
struct SecHdr32{
 dword	name,
	type,
	flags,
	addr,
	offset,
	size,
	link,
	info,
	addralign,
	entsize;
};

// 240216: Parsing ELF file redesigned to use callback functions.

typedef bool(*on_proghdr32)(void*,const byte*,const ProgHdr32&);

static bool enum_proghdr32(const byte*fdata,dword fsize,on_proghdr32 cb,void*cbd=0) {
 if (fsize<32) return false;
 const ELFHDR&e=*reinterpret_cast<const ELFHDR*>(fdata);
 if (e.ident.dw[0]!=0x464C457F) return false;	// 'FLE\x7F'
 for (int i=0; i<e.phnum; i++) {	// call-back for every entry
  if (!cb(cbd,fdata,*reinterpret_cast<const ProgHdr32*>(fdata+e.phoff+i*e.phentsize))) break;
 }
 return true;
}

// avrpp now uses the program header and detects target area
// by von-Neumannized load address, seen on avr-gcc linker files.
// This way, section names don't matter, and you can have
// multiple sections scattered over the flash or eeprom area.
// (For scattering code or data, you must use a custom linker script.)
bool loadelf(const byte*fdata,dword fsize) {
 struct Lambda{
  static bool store_buffer_cb(void*,const byte*fdata,const ProgHdr32&ph) {
   if (ph.type==1 && ph.filesz) store_buffer(fdata+ph.offset,ph.filesz,ph.paddr);
   return true;
  }
 };
 return enum_proghdr32(fdata,fsize,Lambda::store_buffer_cb);
}

// TODO: Some of my ELF files have their .fuse and .signature section not in their program header!
// In this case, these sections must be loaded using the section header as I did it earlier.
// This happens in gcc-assembly programs using
//	.section .fuse,"",@progbits
// and can/should be avoided using
//	.section .fuse,"a",@progbits
// ("a" turns the "alloc" flag on, and @progbits instructs avr-objdump not to disassemble the bytes)
typedef bool(*on_sechdr32)(void*,const byte*,const char*,const SecHdr32&);

static bool enum_sechdr32(const byte*fdata,dword fsize,on_sechdr32 cb,void*cbd=0) {
 if (fsize<32) return false;
 const ELFHDR&e=*reinterpret_cast<const ELFHDR*>(fdata);
 if (e.ident.dw[0]!=0x464C457F) return false;	// 'FLE\x7F'
 const SecHdr32*nsh=reinterpret_cast<const SecHdr32*>(fdata+e.shoff+e.shstrndx*e.shentsize);	// name section header
 const char*names=reinterpret_cast<const char*>(fdata+nsh->offset);
 for (int i=0; i<e.shnum; i++) {
  if (!cb(cbd,fdata,names,*reinterpret_cast<const SecHdr32*>(fdata+e.shoff+i*e.shentsize))) break;
 }
 return true;
}

// Solely for the avr-gcc 5+ ".note.gnu.avr.deviceinfo" section,
// parsing the Section Header is necessary.
// So, device info avoids the need for #include <avr/signature.h> for target device stamping.
bool Deviceinfo::load(const byte*fdata,dword fsize) {
 struct FindDeviceInfo{	// a cumbersome way to imitate lambda function on MSVC6
  Deviceinfo*info;
  bool retval;
  static bool cb(void*cbd,const byte*fdata,const char*names,const SecHdr32&sh) {
	return reinterpret_cast<FindDeviceInfo*>(cbd)->cb(fdata,names,sh);}
  bool cb(const byte*fdata,const char*names,const SecHdr32&sh) {
   const char*n=names+sh.name;
   if (strcmp(n,".note.gnu.avr.deviceinfo")) return true;	// continue search
   const dword*s=reinterpret_cast<const dword*>(fdata+sh.offset);
   if (s[0]!=4) return false;
   if (s[2]!=1) return false;
   if (s[3]!=0x525641) return false;	// 'AVR\0'
   memcpy(info,s+4,6*4);		// copy device's memory bases and sizes for later check
   dword namelen=s[10];
   if (s[11]!=1) return false;
   n=reinterpret_cast<const char*>(s)+0x31;
   if (n[0]=='a' && n[1]=='t') {n+=2; namelen-=2;}	// avr-gcc prefixes names with "at" but avrpp internally does not so
   if (namelen>=15) return false;	// too long
   memcpy(info->device_name,n,namelen+1);	// ELF guarantees trailing '\0'
   retval=true;		// deliver success
   return false;	// abort search
  }
 }fdi={this,false};
 return enum_sechdr32(fdata,fsize,fdi.cb,&fdi) && fdi.retval;
}
Detected encoding: UTF-80