#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "esptool2.h"
#include "esptool2_elf.h"
#define IMAGE_PADDING 16
#define SECTION_PADDING 4
#define IROM_SECTION_PADDING 4096
#define CHECKSUM_INIT 0xEF
#define BIN_MAGIC_IROM 0xEA
#define BIN_MAGIC_FLASH 0xE9
typedef struct {
Elf32_Addr addr;
Elf32_Word size;
} Section_Header;
typedef struct {
unsigned char magic;
unsigned char count;
unsigned char byte2;
unsigned char byte3;
Elf32_Addr entry;
} Image_Header;
static const char PADDING[IROM_SECTION_PADDING] = {0};
static bool debugon = false;
static bool quieton = false;
void print(const char* format, ...) {
va_list args;
if (!quieton) {
va_start(args, format);
vprintf(format, args);
va_end(args);
}
}
void debug(const char* format, ...) {
va_list args;
if (debugon) {
va_start(args, format);
vprintf(format, args);
va_end(args);
}
}
void error(const char* format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}
bool WriteElfSection(MyElf_File *elf, FILE *outfile, char* name, bool headed,
bool zeroaddr, int padto, unsigned char *chksum) {
int i, pad = 0;
bool ret = false;
unsigned char *bindata = 0;
Section_Header sechead;
MyElf_Section *sect;
sect = GetElfSection(elf, name);
if(!sect) {
error("Error: Section '%s' not found in elf file.\r\n", name);
goto end_function;
}
sechead.addr = (zeroaddr ? 0 : sect->address);
sechead.size = sect->size;
if (padto) {
pad = sechead.size % padto;
if (pad > 0) {
pad = padto - pad;
sechead.size += pad;
}
}
debug("Adding section '%s', addr: 0x%08x, size: %d (+%d bytes(s) padding).\r\n",
name, sect->address, sect->size, pad);
bindata = GetElfSectionData(elf, sect);
if (!bindata) {
goto end_function;
}
if((headed && fwrite(&sechead, 1, sizeof(sechead), outfile) != sizeof(sechead))
|| fwrite(bindata, 1, sect->size, outfile) != sect->size
|| (pad > 0 && fwrite(PADDING, 1, pad, outfile) != pad)) {
error("Error: Failed to write section '%s' to image file.\r\n", name);
goto end_function;
}
if(chksum) {
for(i = 0; i < (int)sect->size; i++) {
*chksum ^= bindata[i];
}
}
ret = true;
end_function:
if (bindata) free(bindata);
return ret;
}
bool ExportElfSection(char *infile, char *outfile, char *name) {
bool ret = false;
FILE *fd = 0;
MyElf_File *elf = 0;
elf = LoadElf(infile);
if (!elf) {
goto end_function;
}
fd = fopen(outfile, "wb");
if(!fd) {
error("Error: Can't open output file '%s' for writing.\r\n", outfile);
goto end_function;
}
ret = WriteElfSection(elf, fd, name, false, false, false, 0);
end_function:
if (fd) fclose(fd);
UnloadElf(elf);
return ret;
}
bool CreateHeaderFile(char *elffile, char *imagefile, char *sections[], int numsec) {
bool ret = false;
int i;
unsigned int j, len;
FILE *outfile = 0;
MyElf_File *elf = 0;
MyElf_Section *sect;
unsigned char *bindata = 0;
char name[31];
elf = LoadElf(elffile);
if (!elf) {
goto end_function;
}
outfile = fopen(imagefile, "wb");
if(outfile == NULL) {
error("Error: Failed to open output file '%s' for writing.\r\n", imagefile);
goto end_function;
}
fprintf(outfile, "const uint32 entry_addr = 0x%08x;\r\n", elf->header.e_entry);
for (i = 0; i < numsec; i++) {
sect = GetElfSection(elf, sections[i]);
if(!sect) {
error("Error: Section '%s' not found in elf file.\r\n", sections[i]);
goto end_function;
}
strncpy(name, sect->name, 31);
len = strlen(name);
for (j = 0; j < len; j++) {
if (name[j] == '.') name[j] = '_';
}
debug("Adding section '%s', addr: 0x%08x, size: %d.\r\n", sections[i], sect->address, sect->size);
fprintf(outfile, "\r\nconst uint32 %s_addr = 0x%08x;\r\nconst uint32 %s_len = %d;\r\nconst uint8 %s_data[] = {",
name, sect->address, name, sect->size, name);
bindata = GetElfSectionData(elf, sect);
if (!bindata) {
goto end_function;
}
for (j = 0; j < sect->size; j++) {
if (j % 16 == 0) fprintf(outfile, "\r\n 0x%02x,", bindata[j]);
else fprintf(outfile, " 0x%02x,", bindata[j]);
}
fprintf(outfile, "\r\n};\r\n");
free(bindata);
bindata = 0;
}
ret = true;
end_function:
if (outfile) fclose(outfile);
if (elf) UnloadElf(elf);
if (bindata) free(bindata);
return ret;
}
bool CreateBinFile(char *elffile, char *imagefile, int bootver, unsigned char mode,
unsigned char clock, unsigned char size, bool iromchksum, char *sections[], int numsec) {
bool ret = false;
int i, pad, len;
unsigned char chksum = CHECKSUM_INIT;
unsigned char *data = 0;
FILE *outfile = 0;
MyElf_File *elf = 0;
Image_Header imghead;
elf = LoadElf(elffile);
if (!elf) {
goto end_function;
}
outfile = fopen(imagefile, "wb");
if(outfile == NULL) {
error("Error: Failed to open output file '%s' for writing.\r\n", imagefile);
goto end_function;
}
imghead.byte2 = mode;
imghead.byte3 = ((size << 4) | clock) & 0xff;
imghead.entry = elf->header.e_entry;
debug("Size = %02x\r\n", size);
debug("Byte2 = %02x\r\n", imghead.byte2);
debug("Byte3 = %02x\r\n", imghead.byte3);
debug("Entry = %08x\r\n", imghead.entry);
if (bootver == 2) {
imghead.magic = BIN_MAGIC_IROM;
imghead.count = 4; if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) {
error("Error: Failed to write header to image file.\r\n");
goto end_function;
}
if(!WriteElfSection(elf, outfile, ".irom0.text", true, true, IROM_SECTION_PADDING, (iromchksum ? &chksum : 0))) {
goto end_function;
}
}
imghead.magic = BIN_MAGIC_FLASH;
imghead.count = numsec;
if(fwrite(&imghead, 1, sizeof(imghead), outfile) != sizeof(imghead)) {
error("Error: Failed to write header to image file.\r\n");
goto end_function;
}
for (i = 0; i < numsec; i++) {
if(!WriteElfSection(elf, outfile, sections[i], true, false, SECTION_PADDING, &chksum)) {
goto end_function;
}
}
len = ftell(outfile) + 1;
pad = len % IMAGE_PADDING;
if (pad > 0) {
pad = IMAGE_PADDING - pad;
debug("Padding image with %d byte(s).\r\n", pad);
if(fwrite(PADDING, 1, pad, outfile) != pad) {
error("Error: Failed to write padding to image file.\r\n");
goto end_function;
}
}
if(fwrite(&chksum, 1, 1, outfile) != 1) {
error("Error: Failed to write checksum to image file.\r\n");
goto end_function;
}
if(bootver == 1) {
len = 0x10000 - ftell(outfile);
debug("Adding boot v1.1 padding, %d bytes of '0xff'.\r\n", len);
data = (unsigned char*)malloc(len);
memset(data, 0xff, len);
if(fwrite(data, 1, len, outfile) != len) {
error("Error: Failed to write boot v1.1 spacer.\r\n");
goto end_function;
}
if(!WriteElfSection(elf, outfile, ".irom0.text", false, false, 0, 0)) {
goto end_function;
}
}
ret = true;
end_function:
if (outfile) fclose(outfile);
if (data) free(data);
if (elf) UnloadElf(elf);
return ret;
}
int main(int argc, char *argv[]) {
int i;
char *infile;
char *outfile;
int numstr;
bool binfile = false;
bool libfile = false;
bool headerfile = false;
bool paramerror = false;
bool iromchksum = false;
int bootver = 0;
unsigned char mode = 0xff;
unsigned char size = 0xff;
unsigned char clock = 0;
int opts = 0;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-bin")) {
binfile = true;
opts++;
} else if (!strcmp(argv[i], "-lib")) {
libfile = true;
opts++;
} else if (!strcmp(argv[i], "-header")) {
headerfile = true;
opts++;
} else if (!strcmp(argv[i], "-quiet")) {
quieton = true;
} else if (!strcmp(argv[i], "-debug")) {
debugon = true;
} else if (!strcmp(argv[i], "-boot0")) {
bootver = 0;
} else if (!strcmp(argv[i], "-boot1")) {
bootver = 1;
} else if (!strcmp(argv[i], "-boot2")) {
bootver = 2;
} else if (!strcmp(argv[i], "-qio")) {
mode = 0;
} else if (!strcmp(argv[i], "-qout")) {
mode = 1;
} else if (!strcmp(argv[i], "-dio")) {
mode = 2;
} else if (!strcmp(argv[i], "-dout")) {
mode = 3;
} else if (!strcmp(argv[i], "-256")) {
size = 1;
} else if (!strcmp(argv[i], "-512")) {
size = 0;
} else if (!strcmp(argv[i], "-1024")) {
size = 2;
} else if (!strcmp(argv[i], "-2048")) {
size = 3;
} else if (!strcmp(argv[i], "-4096")) {
size = 4;
} else if (!strcmp(argv[i], "-20")) {
clock = 2;
} else if (!strcmp(argv[i], "-26.7")) {
clock = 1;
} else if (!strcmp(argv[i], "-40")) {
clock = 0;
} else if (!strcmp(argv[i], "-80")) {
clock = 15;
} else if (!strcmp(argv[i], "-iromchksum")) {
iromchksum = true;
} else if (!strcmp(argv[i], "--")) {
i++;
break;
} else if (argv[i][0] == '-') {
paramerror = true;
break;
} else {
break;
}
}
print("esptool2 v2.0.0 - (c) 2015 Richard A Burton <richardaburton@gmail.com>\r\n");
print("This program is licensed under the GPL v3.\r\n");
print("See the file LICENSE for details.\r\n\r\n");
if (paramerror) {
error("Error: Unrecognised option '%s'.\r\n", argv[i]);
return -1;
}
if (argc < 2) {
print("Usage:\r\n");
print("esptool2 -lib [options] <input_file> <output_file>\r\n");
print("esptool2 -bin [options] <input_file> <output_file> <elf_section>...\r\n");
print("esptool2 -header [options] <input_file> <output_file> <elf_section>...\r\n");
print("\r\n");
print(" -lib\r\n");
print(" Export the sdk library (.irom0.text), for a standalone app.\r\n");
print(" e.g. esptool2 -elf esp8266_iot.out out.bin\r\n");
print("\r\n");
print(" -header\r\n");
print(" Export elf sections as bytes to a C header file.\r\n");
print(" e.g. esptool2 -elf esp8266_iot.out out.h .text .data .rodata\r\n");
print("\r\n");
print(" -bin\r\n");
print(" Create binary program image, for standalone and bootloaded apps, with\r\n");
print(" specified elf sections. Includes sdk library for bootloaded apps.\r\n");
print(" e.g. esptool2 -bin esp8266_iot.out out.bin .text .data .rodata\r\n");
print(" Options:\r\n");
print(" bootloader: -boot0 -boot1 -boot2 (default -boot0)\r\n");
print(" -boot0 = standalone app, not built for bootloader use\r\n");
print(" -boot1 = built for bootloader v1.1\r\n");
print(" -boot2 = built for bootloader v1.2+ (use for rBoot roms)\r\n");
print(" (elf file must have been linked appropriately for chosen option)\r\n");
print(" spi size (kb): -256 -512 -1024 -2048 -4096 (default -512)\r\n");
print(" spi mode: -qio -qout -dio -dout (default -qio)\r\n");
print(" spi speed: -20 -26.7 -40 -80 (default -40)\r\n");
print(" include irom in checksum: -iromchksum (also needs enabling in rBoot)\r\n");
print("\r\n");
print("General options:\r\n");
print(" -quiet prints only error messages\r\n");
print(" -debug print extra debug information\r\n");
print(" -- no more options follow (needed if your elf file starts with a '-')\r\n");
print("\r\n");
print("Returns:\r\n");
print(" 0 on success\r\n");
print(" -1 on failure\r\n");
print("\r\n");
return -1;
}
if (opts != 1) {
error("Error: You must specify -bin OR -lib OR -header for build type.\r\n");
return -1;
}
if (quieton && debugon) {
error("Error: You cannot specify -quiet and -debug.\r\n");
return -1;
}
if ((libfile && i + 2 > argc) || ((binfile | headerfile) && i + 3 > argc)) {
error("Error: Not enough arguments supplied.\r\n");
return -1;
} else if (libfile && i + 2 < argc) {
error("Error: Too many arguments supplied.\r\n");
return -1;
}
infile = argv[i++];
outfile = argv[i++];
numstr = argc - i;
if (binfile) {
if (!CreateBinFile(infile, outfile, bootver, mode, clock, size, iromchksum, &argv[i], numstr)) {
remove(outfile);
return -1;
}
} else if (headerfile) {
if (!CreateHeaderFile(infile, outfile, &argv[i], numstr)) {
remove(outfile);
return -1;
}
} else {
if (!ExportElfSection(infile, outfile, ".irom0.text")) {
remove(outfile);
return -1;
}
}
print("Successfully created '%s'.\r\n", outfile);
return 0;
}