/* * Copyright (c) 2010-2014 Wind River Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * @brief generate offset definition header file * * genOffsetHeader -i -o * * This Zephyr development host utility will process an ELF object module that * consists of a series of absolute symbols representing the byte offset of a * structure member and the size of the structure. Each absolute symbol will * be translated into a C preprocessor '#define' directive. For example, * assuming that the module offsets.o contains the following absolute symbols: * * $ nm offsets.o * 00000010 A __tNANO_common_isp_OFFSET * 00000008 A __tNANO_current_OFFSET * 0000000c A __tNANO_nested_OFFSET * 00000000 A __tNANO_fiber_OFFSET * 00000004 A __tNANO_task_OFFSET * * ... the following C preprocessor code will be generated: * * #define __tNANO_common_isp_OFFSET 0x10 * #define __tNANO_current_OFFSET 0x8 * #define __tNANO_nested_OFFSET 0xC * #define __tNANO_fiber_OFFSET 0x0 * #define __tNANO_task_OFFSET 0x4 */ /* includes */ #include #include #include #include #include #include "elf.h" #include /* for malloc()/free()/exit() */ #include /* for strstr() */ #include #include /* defines */ #undef DEBUG /* the symbol name suffix used to denote structure member offsets */ #define STRUCT_OFF_SUFFIX "_OFFSET" #define STRUCT_SIZ_SUFFIX "_SIZEOF" #ifdef DEBUG #define DBG_PRINT(args...) printf(args) #else #define DBG_PRINT(args...) #endif /* byte swapping macros */ #define SWAB_Elf32_Half(x) ((x >> 8) | (x << 8)) #define SWAB_Elf32_Word(x) (((x >> 24) & 0xff) | \ ((x << 8) & 0xff0000) | \ ((x >> 8) & 0xff00) | \ ((x << 24) & 0xff000000)) #define SWAB_Elf32_Addr SWAB_Elf32_Word #define SWAB_Elf32_Off SWAB_Elf32_Word #define SWAB_Elf32_Sword SWAB_Elf32_Word #if defined(_WIN32) || defined(__CYGWIN32__) || defined(__WIN32__) #define OPEN_FLAGS (O_RDONLY|O_BINARY) #else #define OPEN_FLAGS (O_RDONLY) #endif /* locals */ /* global indicating whether ELF structures need to be byte swapped */ static char swabRequired; /* usage information */ static char usage[] = "usage: %s -i -o \n"; /* output header file preamble */ static char preamble1[] = "\ /* %s - structure member offsets definition header */\n\ \n\ /*\n\ * Copyright (c) 2010-2014 Wind River Systems, Inc.\n\ *\n\ * Licensed under the Apache License, Version 2.0 (the \"License\");\n\ * you may not use this file except in compliance with the License.\n\ * You may obtain a copy of the License at\n\ *\n\ * http://www.apache.org/licenses/LICENSE-2.0\n\ *\n\ * Unless required by applicable law or agreed to in writing, software\n\ * distributed under the License is distributed on an \"AS IS\" BASIS,\n\ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\ * See the License for the specific language governing permissions and\n\ * limitations under the License.\n\ */\n\ \n\ /* THIS FILE IS AUTO GENERATED. PLEASE DO NOT EDIT */\n\ \n\ /*\n\ * This header file provides macros for the offsets of various structure\n\ * members. These offset macros are primarily intended to be used in\n\ * assembly code.\n\ */\n\n"; static char preamble2[] = "\ /*\n\ * Auto-generated header guard.\n\ */\n\ #ifndef %s\n\ #define %s\n\ \n\ #ifdef __cplusplus\n\ extern \"C\" {\n\ #endif\n\ \n\ /* defines */\n\n"; /* output header file postscript */ static char postscript[] = "\ \n\ #ifdef __cplusplus\n\ }\n\ #endif\n\ \n\ #endif /* _HGUARD_ */\n"; static Elf32_Ehdr ehdr; /* ELF header */ static Elf32_Shdr * shdr; /* pointer to array ELF section headers */ /** * @brief byte swap the Elf32_Ehdr structure * * @returns N/A */ static void swabElfHdr(Elf32_Ehdr *pHdrToSwab) { if (swabRequired == 0) { return; /* do nothing */ } pHdrToSwab->e_type = SWAB_Elf32_Half(pHdrToSwab->e_type); pHdrToSwab->e_machine = SWAB_Elf32_Half(pHdrToSwab->e_machine); pHdrToSwab->e_version = SWAB_Elf32_Word(pHdrToSwab->e_version); pHdrToSwab->e_entry = SWAB_Elf32_Addr(pHdrToSwab->e_entry); pHdrToSwab->e_phoff = SWAB_Elf32_Off(pHdrToSwab->e_phoff); pHdrToSwab->e_shoff = SWAB_Elf32_Off(pHdrToSwab->e_shoff); pHdrToSwab->e_flags = SWAB_Elf32_Word(pHdrToSwab->e_flags); pHdrToSwab->e_ehsize = SWAB_Elf32_Half(pHdrToSwab->e_ehsize); pHdrToSwab->e_phentsize = SWAB_Elf32_Half(pHdrToSwab->e_phentsize); pHdrToSwab->e_phnum = SWAB_Elf32_Half(pHdrToSwab->e_phnum); pHdrToSwab->e_shentsize = SWAB_Elf32_Half(pHdrToSwab->e_shentsize); pHdrToSwab->e_shnum = SWAB_Elf32_Half(pHdrToSwab->e_shnum); pHdrToSwab->e_shstrndx = SWAB_Elf32_Half(pHdrToSwab->e_shstrndx); } /** * @brief byte swap the Elf32_Shdr structure * * @returns N/A */ static void swabElfSectionHdr(Elf32_Shdr *pHdrToSwab) { if (swabRequired == 0) { return; /* do nothing */ } pHdrToSwab->sh_name = SWAB_Elf32_Word(pHdrToSwab->sh_name); pHdrToSwab->sh_type = SWAB_Elf32_Word(pHdrToSwab->sh_type); pHdrToSwab->sh_flags = SWAB_Elf32_Word(pHdrToSwab->sh_flags); pHdrToSwab->sh_addr = SWAB_Elf32_Addr(pHdrToSwab->sh_addr); pHdrToSwab->sh_offset = SWAB_Elf32_Off(pHdrToSwab->sh_offset); pHdrToSwab->sh_size = SWAB_Elf32_Word(pHdrToSwab->sh_size); pHdrToSwab->sh_link = SWAB_Elf32_Word(pHdrToSwab->sh_link); pHdrToSwab->sh_info = SWAB_Elf32_Word(pHdrToSwab->sh_info); pHdrToSwab->sh_addralign = SWAB_Elf32_Word(pHdrToSwab->sh_addralign); pHdrToSwab->sh_addralign = SWAB_Elf32_Word(pHdrToSwab->sh_addralign); } /** * * @brief byte swap the Elf32_Sym structure * * @returns N/A */ static void swabElfSym(Elf32_Sym *pHdrToSwab) { if (swabRequired == 0) { return; /* do nothing */ } pHdrToSwab->st_name = SWAB_Elf32_Word(pHdrToSwab->st_name); pHdrToSwab->st_value = SWAB_Elf32_Addr(pHdrToSwab->st_value); pHdrToSwab->st_size = SWAB_Elf32_Word(pHdrToSwab->st_size); pHdrToSwab->st_shndx = SWAB_Elf32_Half(pHdrToSwab->st_shndx); } /** * @brief load the ELF header * * @param fd file descriptor of file from which to read * @returns 0 on success, -1 on failure */ static int ehdrLoad(int fd) { unsigned ix = 0x12345678; /* used to auto-detect endian-ness */ size_t nBytes; /* number of bytes read from file */ if (lseek(fd, 0, SEEK_SET) == -1) { fprintf(stderr, "Unable to seek\n"); return -1; } nBytes = read(fd, &ehdr, sizeof(ehdr)); if (nBytes != sizeof(ehdr)) { fprintf(stderr, "Failed to read ELF header\n"); return -1; } /* perform some rudimentary ELF file validation */ if (strncmp((char *)ehdr.e_ident, ELFMAG, 4) != 0) { fprintf(stderr, "Input object module not ELF format\n"); return -1; } /* 64-bit ELF module not supported (for now) */ if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) { fprintf(stderr, "ELF64 class not supported\n"); return -1; } /* * Dynamically determine the endianess of the host (in the absence of * a compile time macro ala _BYTE_ORDER). The ELF structures will require * byte swapping if the host and target have different byte ordering. */ if (((*(char*)&ix == 0x78) && (ehdr.e_ident[EI_DATA] == ELFDATA2MSB)) || ((*(char*)&ix == 0x12) && (ehdr.e_ident[EI_DATA] == ELFDATA2LSB))) { swabRequired = 1; DBG_PRINT("Swab required\n"); } swabElfHdr(&ehdr); /* swap bytes (if required) */ /* debugging: dump some important ELF header fields */ DBG_PRINT("Elf header Magic = %s\n", ehdr.e_ident); DBG_PRINT("Elf header e_type = %d\n", ehdr.e_type); DBG_PRINT("Elf header e_shstrndx = %d\n", ehdr.e_shstrndx); DBG_PRINT("Elf header e_shnum = %d\n", ehdr.e_shnum); return 0; } /** * @brief load the section headers * @param fd file descriptor of file from which to read * * @returns 0 on success, -1 on failure */ static int shdrsLoad(int fd) { size_t nBytes; /* number of bytes read from file */ unsigned ix; /* loop index */ shdr = malloc(ehdr.e_shnum * sizeof(Elf32_Shdr)); if (shdr == NULL) { fprintf(stderr, "No memory for section headers!\n"); return -1; } /* Seek to the start of the table of section headers */ if (lseek(fd, ehdr.e_shoff, SEEK_SET) == -1) { fprintf(stderr, "Unable to seek\n"); return -1; } for (ix = 0; ix < ehdr.e_shnum; ix++) { nBytes = read(fd, &shdr[ix], sizeof(Elf32_Shdr)); if (nBytes != sizeof(Elf32_Shdr)) { fprintf(stderr, "Unable to read entire section header (#%d)\n", ix); return -1; } swabElfSectionHdr(&shdr[ix]); /* swap bytes (if required) */ } return 0; } /** * * symTblFind - search the section headers for the symbol table * * This routine searches the section headers for the symbol table. There is * expected to be only one symbol table in the section headers. * * @param pSymTblOffset ptr to symbol table offset * @param pSymTblSize ptr to symbol table size * @returns 0 if found, -1 if not */ static int symTblFind(unsigned *pSymTblOffset, unsigned *pSymTblSize) { unsigned ix; /* loop index */ for (ix = 0; ix < ehdr.e_shnum; ++ix) { if (shdr[ix].sh_type == SHT_SYMTAB) { *pSymTblOffset = shdr[ix].sh_offset; *pSymTblSize = shdr[ix].sh_size; return 0; } } fprintf(stderr, "Object module missing symbol table!\n"); return -1; } /** * * @brief search the section headers for the string table * * This routine searches the section headers for the string table associated * with the symbol names. * * Typically, there are two string tables defined in the section headers. These * are ".shstrtbl" and ".strtbl" for section header names and symbol names * respectively. It has been observed with the DIAB compiler (but not with * either the GCC nor ICC compilers) that the two tables may be mashed together * into one. Consequently, the following algorithm is used to select the * appropriate string table. * * 1. Assume that the first found string table is valid. * 2. If another string table is found, use that only if its section header * index does not match the index for ".shstrtbl" stored in the ELF header. * * @param pStrTblIx ptr to string table's index * @returns 0 if found, -1 if not */ static int strTblFind(unsigned *pStrTblIx) { unsigned strTblIx = 0xffffffff; unsigned ix; for (ix = 0; ix < ehdr.e_shnum; ++ix) { if (shdr[ix].sh_type == SHT_STRTAB) { if ((strTblIx == 0xffffffff) || (ix != ehdr.e_shstrndx)) { strTblIx = ix; } } } if (strTblIx == 0xffffffff) { fprintf(stderr, "Object module missing string table!\n"); return -1; } *pStrTblIx = strTblIx; return 0; } /** * @brief load the string table * * @param fd file descriptor of file from which to read * @param strTblIx string table's index * @param ppStringTable ptr to ptr to string table * @returns 0 on success, -1 on failure */ static int strTblLoad(int fd, unsigned strTblIx, char **ppStringTable) { char * pTable; int nBytes; DBG_PRINT("Allocating %d bytes for string table\n", shdr[strTblIx].sh_size); pTable = malloc(shdr[strTblIx].sh_size); if (pTable == NULL) { fprintf(stderr, "No memory for string table!"); return -1; } if (lseek(fd, shdr[strTblIx].sh_offset, SEEK_SET) == -1) { fprintf(stderr, "Unable to seek\n"); return -1; } nBytes = read(fd, pTable, shdr[strTblIx].sh_size); if (nBytes != shdr[strTblIx].sh_size) { free(pTable); fprintf(stderr, "Unable to read entire string table!\n"); return -1; } *ppStringTable = pTable; return 0; } /** * * @brief dump the header preamble to the header file * * @param fd file pointer to which to write * @parama filename name of the output file * @returns N/A */ static void headerPreambleDump(FILE *fp, char *filename) { unsigned hash = 5381; /* hash value */ size_t ix; /* loop counter */ char fileNameHash[20]; /* '_HGUARD_' + 8 character hash + '\0' */ /* dump header file preamble1[] */ fprintf(fp, preamble1, filename, filename, filename); /* * Dump header file preamble2[]. Hash file name into something that * is small enough to be a C macro name and does not have invalid * characters for a macro name to use as a header guard. The result * of the hash should be unique enough for our purposes. */ for (ix = 0; ix < sizeof(filename); ++ix) { hash = (hash * 33) + (unsigned int) filename[ix]; } sprintf(fileNameHash, "_HGUARD_%08x", hash); fprintf(fp, preamble2, fileNameHash, fileNameHash); } /** * @brief dump the absolute symbols to the header file * * @param fd file descriptor of file from which to read * @param fp file pointer to which to write * @param symTblOffset symbol table offset * @param symTblSize size of the symbol table * @param pStringTable ptr to the string table * @returns N/A */ static void headerAbsoluteSymbolsDump(int fd, FILE *fp, Elf32_Off symTblOffset, Elf32_Word symTblSize, char *pStringTable) { Elf32_Sym aSym; /* absolute symbol */ unsigned ix; /* loop counter */ unsigned numSyms; /* number of symbols in the symbol table */ size_t nBytes; /* context the symbol table: pick out absolute syms */ numSyms = symTblSize / sizeof(Elf32_Sym); if (lseek(fd, symTblOffset, SEEK_SET) == -1) { fprintf(stderr, "Unable to seek\n"); return; } for (ix = 0; ix < numSyms; ++ix) { /* read in a single symbol structure */ nBytes = read(fd, &aSym, sizeof(Elf32_Sym)); if (nBytes) { swabElfSym(&aSym); /* swap bytes (if required) */ } /* * Only generate definitions for global absolute symbols * of the form *_OFFSET */ if ((aSym.st_shndx == SHN_ABS) && (ELF_ST_BIND(aSym.st_info) == STB_GLOBAL)) { if ((strstr(&pStringTable[aSym.st_name], STRUCT_OFF_SUFFIX) != NULL) || (strstr(&pStringTable[aSym.st_name], STRUCT_SIZ_SUFFIX) != NULL)) { fprintf(fp, "#define\t%s\t0x%X\n", &pStringTable[aSym.st_name], aSym.st_value); } } } } /** * * @brief dump the header postscript to the header file * @param fp file pointer to which to write * @returns N/A */ static void headerPostscriptDump(FILE *fp) { fputs(postscript, fp); } /** * @brief entry point for the genOffsetHeader utility * * usage: $ genOffsetHeader -i -o * * @returns 0 on success, 1 on failure */ int main(int argc, char *argv[]) { Elf32_Off symTblOffset = 0; Elf32_Word symTblSize; /* in bytes */ char * pStringTable = NULL; char * inFileName = NULL; char * outFileName = NULL; int option; int inFd = -1; FILE * outFile = NULL; unsigned strTblIx; /* argument parsing */ if (argc != 5) { fprintf(stderr, usage, argv[0]); goto errorReturn; } while ((option = getopt(argc, argv, "i:o:")) != -1) { switch (option) { case 'i': inFileName = optarg; break; case 'o': outFileName = optarg; break; default: fprintf(stderr, usage, argv[0]); goto errorReturn; } } /* open input object ELF module and output header file */ inFd = open(inFileName, OPEN_FLAGS); if (inFd == -1) { fprintf(stderr, "Cannot open input object module"); goto errorReturn; } outFile = fopen(outFileName, "w"); if (outFile == NULL) { fprintf(stderr, "Cannot open output header file"); goto errorReturn; } /* * In the following order, attempt to ... * 1. Load the ELF header * 2. Load the section headers * 3. Find the symbol table * 4. Find the string table * 5. Load the string table * Bail if any of those steps fail. */ if ((ehdrLoad(inFd) != 0) || (shdrsLoad(inFd) != 0) || (symTblFind(&symTblOffset, &symTblSize) != 0) || (strTblFind(&strTblIx) != 0) || (strTblLoad(inFd, strTblIx, &pStringTable) != 0)) { goto errorReturn; } /* * Dump the following to the header file ... * 1. Header file preamble * 2. Absolute symbols * 3. Header file postscript */ headerPreambleDump(outFile, outFileName); headerAbsoluteSymbolsDump(inFd, outFile, symTblOffset, symTblSize, pStringTable); headerPostscriptDump(outFile); /* done: cleanup */ close(inFd); fclose(outFile); free(shdr); free(pStringTable); return 0; errorReturn: if (inFd != -1) { close(inFd); } if (outFile != NULL) { fclose(outFile); } if (shdr != NULL) { free(shdr); } if (pStringTable != NULL) { free(pStringTable); } return 1; }