#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;
}
Vorgefundene Kodierung: ASCII (7 bit) | 2
|