/* ************************************************************************************************************************ * * Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. * ***********************************************************************************************************************/ // // Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved. // #include "elf.hpp" #include #include #include #if defined(__linux__) #include #endif #include "os/os.hpp" #include "_libelf.h" namespace amd { using namespace oclelfutils; #if !defined(ELFMAG) #define ELFMAG "\177ELF" #define SELFMAG 4 #endif /* Opague data type definition. */ struct symbol_handle { union { Elf64_Sym sym64; Elf32_Sym sym32; } u; }; typedef struct { OclElf::oclElfSections id; const char *name; Elf_Type d_type; uint64_t d_align; // section alignment in bytes Elf32_Word sh_type; // section type Elf32_Word sh_flags; // section flags const char *desc; } OclElfSectionsDesc; namespace { // Objects that are visible only within this module const OclElfSectionsDesc oclElfSecDesc[] = { { OclElf::LLVMIR, ".llvmir", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "ASIC-independent LLVM IR" }, { OclElf::SOURCE, ".source", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "OpenCL source" }, { OclElf::ILTEXT, ".amdil", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "AMD IL text" }, { OclElf::ASTEXT, ".astext", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "X86 assembly text" }, { OclElf::CAL, ".text", ELF_T_BYTE, 1, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, "AMD CalImage" }, { OclElf::DLL, ".text", ELF_T_BYTE, 1, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, "x86 dll" }, { OclElf::STRTAB, ".strtab", ELF_T_BYTE, 1, SHT_STRTAB, SHF_STRINGS, "String table" }, { OclElf::SYMTAB, ".symtab", ELF_T_SYM, sizeof(Elf64_Xword), SHT_SYMTAB, 0, "Symbol table" }, { OclElf::RODATA, ".rodata", ELF_T_BYTE, 1, SHT_PROGBITS, SHF_ALLOC, "Read-only data" }, { OclElf::SHSTRTAB, ".shstrtab", ELF_T_BYTE, 1, SHT_STRTAB, SHF_STRINGS, "Section names" }, { OclElf::NOTES, ".note", ELF_T_NOTE, 1, SHT_NOTE, 0, "used by loader for notes" }, { OclElf::COMMENT, ".comment", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Version string" }, { OclElf::ILDEBUG, ".debugil", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "AMD Debug IL" }, { OclElf::DEBUG_INFO, ".debug_info", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug info" }, { OclElf::DEBUG_ABBREV, ".debug_abbrev", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug abbrev" }, { OclElf::DEBUG_LINE, ".debug_line", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug line" }, { OclElf::DEBUG_PUBNAMES, ".debug_pubnames", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug pubnames" }, { OclElf::DEBUG_PUBTYPES, ".debug_pubtypes", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug pubtypes" }, { OclElf::DEBUG_LOC, ".debug_loc", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug loc" }, { OclElf::DEBUG_ARANGES, ".debug_aranges", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug aranges" }, { OclElf::DEBUG_RANGES, ".debug_ranges", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug ranges" }, { OclElf::DEBUG_MACINFO, ".debug_macinfo", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug macinfo" }, { OclElf::DEBUG_STR, ".debug_str", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug str" }, { OclElf::DEBUG_FRAME, ".debug_frame", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Dwarf debug frame" }, { OclElf::JITBINARY, ".text", ELF_T_BYTE, 1, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, "x86 JIT Binary" }, { OclElf::CODEGEN, ".cg", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Target dependent IL" }, { OclElf::TEXT, ".text", ELF_T_BYTE, 1, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, "Device specific ISA" }, { OclElf::INTERNAL, ".internal", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Internal usage" }, { OclElf::SPIR, ".spir", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "Vendor/Device-independent LLVM IR" }, { OclElf::SPIRV, ".spirv", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "SPIR-V Binary" }, { OclElf::RUNTIME_METADATA,".AMDGPU.runtime_metadata", ELF_T_BYTE, 1, SHT_PROGBITS, 0, "AMDGPU runtime metadata" }, }; // index 0 is reserved and must be there (NULL section) const char shstrtab[] = { /* index 0 */ '\0', /* index 1 */ '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', '\0', /* index 11 */ '.', 's', 't', 'r', 't', 'a', 'b', '\0' }; #define SHSTRTAB_NAME_NDX 1 #define STRTAB_NAME_NDX 11 // index 0 is reserved and must be there (NULL name) const char strtab[] = { /* index 0 */ '\0' }; } bool isElfMagic(const char* p) { if (p==NULL || strncmp(p, ELFMAG, SELFMAG) != 0) { return false; } return true; } // bool isElfHeader(const char* p, signed char ec) { if (!isElfMagic(p)) { return false; } signed char libVersion = elf_version(EV_CURRENT); signed char fileVersion = p[EI_VERSION]; signed char elfClass = p[EI_CLASS]; if( fileVersion > libVersion) { return false; } // class check: if ( elfClass != ec) { return false; } return true; } bool isCALTarget(const char* p, signed char ec) { if (!isElfMagic(p)) { return false; } Elf64_Half machine; if (ec == ELFCLASS32) { machine = ((Elf32_Ehdr*)p)->e_machine; } else { machine = ((Elf64_Ehdr*)p)->e_machine; } #if !defined(WITH_LIGHTNING_COMPILER) if ( (machine >= OclElf::CAL_FIRST) && (machine <= OclElf::CAL_LAST) ) { return true; } #endif // !defined(WITH_LIGHTNING_COMPILER) return false; } /////////////////////////////////////////////////////////////// ////////////////////// elf initializers /////////////////////// /////////////////////////////////////////////////////////////// OclElf::OclElf ( unsigned char eclass, const char* rawElfBytes, uint64_t rawElfSize, const char* elfFileName, Elf_Cmd elfcmd ) : _fd (-1), _fname (elfFileName), _e (0), _err (), _eclass (eclass), _rawElfBytes (rawElfBytes), _rawElfSize (rawElfSize), _elfCmd (elfcmd), _elfMemory(), _shstrtab_ndx (0), _strtab_ndx (0) { if (rawElfBytes != NULL) { /* In general, 'eclass' should be the same as rawElfBytes's. 'eclass' is what the runtime will use for generating an ELF, and therefore it expects the input ELF to have this 'eclass'. However, GPU needs to accept both 32-bit and 64-bit ELF for compatibility (we used to generate 64-bit ELF, which is the bad design in the first place). Here we just uses eclass from rawElfBytes, and overrides the input 'eclass'. */ _eclass = (unsigned char)rawElfBytes[EI_CLASS]; } (void)Init(); } OclElf::~OclElf() { #if 0 Elf_Cmd c = (_errCmd == ELF_C_READ) ? ELF_C_NULL : _errCmd; if (elf_update(_e, c < 0) { _err.xfail("OclElf::Fini() : elf_update() failed: %s", elf_errmsg(-1); return; } #endif _err.Fini(); elf_end(_e); _e = 0; if (_fd != -1) { xclose(_err, _fname, _fd); char* tname= const_cast(_fname); if (tname) { unlink(tname); free(tname); } _fd = -1; _fname = NULL; } elfMemoryRelease(); } bool OclElf::Clear() { if (_e) { elf_end(_e); _e = NULL; } if (_fd != -1) { if (xclose(_err, _fname, _fd) < 0) { return false; } _fd = -1; } elfMemoryRelease(); _err.Fini(); // Re-initialize the object Init(); return !hasError(); } /* Initialize OclElf object */ bool OclElf::Init() { _err.Init(); // Create a temporary file if it is needed if (_elfCmd != ELF_C_READ) { if (_fname != NULL) { size_t sz = strlen(_fname) + 1; char* tname = (char*)xmalloc(_err, sz); if (tname == 0) { _err.xfail("OclElf::Init() failed to malloc()"); return false; } strcpy(tname, _fname); _fname = static_cast(tname); } } if (elf_version(EV_CURRENT) == EV_NONE) { _err.xfail("OclElf::Init(): Application expects CURRENT elf version"); return false; } int oflag, pmode; #if defined(_MSC_VER) if (_elfCmd == ELF_C_READ) { oflag = _O_RDONLY | _O_BINARY; } else { oflag = _O_CREAT | _O_RDWR | _O_TRUNC | _O_BINARY; } pmode = _S_IREAD | _S_IWRITE; #else if (_elfCmd == ELF_C_READ) { oflag = O_RDONLY; } else { oflag = O_CREAT | O_RDWR | O_TRUNC; } pmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644 #endif if ((_fd == -1) && (_rawElfBytes == NULL)) { // case 1: elf object is in file '_fname' _fd = xopen(_err, _fname, oflag, pmode); if (_fd == -1) { _err.xfail("OclElf::Init(): Cannot Open File %s!", _fname); return false; } _e = elf_begin(_fd, _elfCmd, NULL, NULL); if (_e == NULL) { _err.xfail ("OclElf::Init(): elf_begin failed"); return false; } } else if (_fd == -1) { // case 2: elf object is in memory if (_elfCmd == ELF_C_READ) { assert ((_fname == NULL) && "ELF file name should not be provided for a read only elf."); } else { _fd = xopen(_err, _fname, oflag, pmode); if (_fd == -1) { _err.xfail("OclElf::Init(): Cannot Open File %s!", _fname); return false; } } // const_cast is safe _e = elf_memory(const_cast(_rawElfBytes), _rawElfSize, NULL); if ( _e == NULL) { _err.xfail("OclElf::Init(): elf_memory failed: %s", elf_errmsg(-1)); return false; } // If _fd != -1, then we are a read/write and not just a read, so change accordingly. if (_fd != -1) { _e->e_fd = _fd; _e->e_cmd = _elfCmd; } } else { // _fd != -1 // case 3: elf object is in a file with file descriptor '_fd' _e = elf_begin(_fd, _elfCmd, NULL, NULL); if (_e == NULL) { _err.xfail ("OclElf::Init(): elf_begin failed: %s", elf_errmsg(-1)); return false; } } if (!InitElf()) { return false; } // Success return true; } /* Return true: if InitElf() is successful Return false: if InitElf() failed. */ bool OclElf::InitElf () { assert (_e && "libelf object should have been created already"); if (_elfCmd != ELF_C_WRITE) { // Set up _shstrtab_ndx and _strtab_ndx GElf_Ehdr gehdr; if (gelf_getehdr(_e, &gehdr) == NULL) { _err.xfail("OclElf::InitElf() failed in gelf_getehdr()- %s", elf_errmsg(-1)); return false; } _shstrtab_ndx = gehdr.e_shstrndx; Elf_Scn* scn; if (!getSectionDesc(scn, STRTAB)) { _err.xfail("OclElf::InitElf() failed in getSectionDesc(STRTAB)"); return false; } // Sanity check. Each ELF binary should have STRTAB ! if (scn != NULL) { _strtab_ndx = elf_ndxscn(scn); } return true; } /*********************************/ /******** ELF_C_WRITE ************/ /*********************************/ // // 1. Create ELF header // if (_eclass == ELFCLASS32) { Elf32_Ehdr* ehdr32 = elf32_newehdr(_e); if (ehdr32 == NULL) { _err.xfail("OclElf::InitElf() failed in elf32_newehdr: %s.", elf_errmsg(-1)); return false; } } else { Elf64_Ehdr* ehdr64 = elf64_newehdr(_e); if (ehdr64 == NULL) { _err.xfail("OclElf::InitElf() failed in elf32_newehdr : %s.", elf_errmsg(-1)); return false; } } #if 0 if (elf_update(_e, ELF_C_NULL) < 0) { _err.xfail("elf_update() failed"); return -1; } #endif // // 2. Create ELF shstrtab // Elf_Scn* scn_shstrtab = elf_newscn(_e); if (scn_shstrtab == NULL) { _err.xfail("Elf::InitElf() failed in elf_newscn : %s", elf_errmsg(-1)); return false; } /* addng ELF_Data descriptor associated with section scn */ Elf_Data* data_shstrtab = createElfData(scn_shstrtab, SHSTRTAB, const_cast(shstrtab), (uint64_t)sizeof(shstrtab), false); if (data_shstrtab == NULL) { return false; } if (!createShdr(SHSTRTAB, scn_shstrtab, SHSTRTAB_NAME_NDX)) { return false; } // Save shstrtab section index _shstrtab_ndx = elf_ndxscn(scn_shstrtab); #if defined(BSD_LIBELF) elf_setshstrndx(_e, _shstrtab_ndx); #else elfx_update_shstrndx(_e, _shstrtab_ndx); #endif #if 0 if (elf_update(_e, ELF_C_NULL) < 0) { _err.xfail("elf_update() failed"); return -1; } #endif // // 3. Create .strtab section // Elf_Scn* scn_strtab = elf_newscn(_e); if (scn_strtab == NULL) { _err.xfail("Elf::InitElf() failed in elf_newscn : %s", elf_errmsg(-1)); return false; } /* addng ELF_Data descriptor associated with section scn */ Elf_Data* data_strtab = createElfData(scn_strtab, STRTAB, const_cast(strtab), (uint64_t)sizeof(strtab), false); if (data_strtab == NULL) { return false; } if (!createShdr(STRTAB, scn_strtab, STRTAB_NAME_NDX)) { return false; } // Save strtab section index _strtab_ndx = elf_ndxscn(scn_strtab); // Need to update section header if (elf_update(_e, ELF_C_NULL) < 0) { _err.xfail("elf_update() failed"); return false; } // // 4. Create the symbol table // // Create the first reserved symbol (undefined symbol) size_t sym_sz = (_eclass == ELFCLASS32) ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym); void* sym = oclelf_calloc(sym_sz); if (sym == NULL) { _err.xfail("OclElf::InitElf() failed to alloc memory"); return false; } Elf_Scn* scn_symtab = newSection(SYMTAB, sym, sym_sz, false); if (scn_symtab == NULL) { // Use newSection()'s error message. return false; } return true; } Elf_Data* OclElf::createElfData( Elf_Scn*& scn, oclElfSections id, void* d_buf, uint64_t d_size, bool do_copy ) { /* addng Elf_Data descriptor associated with section scn */ Elf_Data* data = elf_newdata(scn); if (data == NULL) { _err.xfail("OclElf::createElfData() failed in elf_newdata() - %s", elf_errmsg(-1)); return NULL; } void* newbuf; if (do_copy) { newbuf = oclelf_allocAndCopy((void*)d_buf, d_size); } else { newbuf = d_buf; } data->d_align = oclElfSecDesc[id].d_align; data->d_off = 0LL; data->d_buf = newbuf; data->d_type = oclElfSecDesc[id].d_type; data->d_size = d_size; data->d_version = EV_CURRENT ; if (elf_update(_e, ELF_C_NULL) < 0) { _err.xfail("elf_update() failed"); return NULL; } return data; } bool OclElf::createShdr ( oclElfSections id, Elf_Scn*& scn, Elf64_Word shname, Elf64_Word shlink ) { if (_eclass == ELFCLASS32) { Elf32_Shdr* shdr32 = elf32_getshdr(scn); if (shdr32 == NULL) { _err.xfail("Elf::createShdr() failed in elf32_getshdr(): %s.", elf_errmsg(-1)); return false; } shdr32->sh_name = (Elf32_Word)shname; shdr32->sh_type = (Elf32_Word)oclElfSecDesc[id].sh_type; shdr32->sh_flags = (Elf32_Word)oclElfSecDesc[id].sh_flags; shdr32->sh_link = (Elf32_Word)shlink; } else { Elf64_Shdr* shdr64 = elf64_getshdr(scn); if (shdr64 == NULL) { _err.xfail("Elf::InitElf() failed in elf64_getshdr(): %s.", elf_errmsg(-1)); return false; } shdr64->sh_name = (Elf64_Word)shname; shdr64->sh_type = (Elf64_Word)oclElfSecDesc[id].sh_type; shdr64->sh_flags = (Elf64_Xword)oclElfSecDesc[id].sh_flags; shdr64->sh_link = (Elf64_Word)shlink; } return true; } bool OclElf::getTarget(uint16_t& machine, oclElfPlatform& platform) { assert(_e != 0); GElf_Ehdr ehdrO; GElf_Ehdr *ehdr = gelf_getehdr(_e, &ehdrO); if (ehdr == NULL) { return false; } Elf64_Half mach = ehdr->e_machine; if ((mach >= CPU_FIRST) && (mach <= CPU_LAST)) { platform = CPU_PLATFORM; machine = mach - CPU_BASE; } #if !defined(WITH_LIGHTNING_COMPILER) else if ( (mach >= CAL_FIRST) && (mach <= CAL_LAST)) { platform = CAL_PLATFORM; machine = mach - CAL_BASE; } #endif // !defined(WITH_LIGHTNING_COMPILER) else if (mach == EM_386 || mach == EM_HSAIL || mach == EM_HSAIL_64 || mach == EM_AMDIL || mach == EM_AMDIL_64 || mach == EM_X86_64) { platform = COMPLIB_PLATFORM; machine = mach; } else { // Invalid machine return false; } return true; } bool OclElf::setTarget(uint16_t machine, oclElfPlatform platform) { assert(_e != 0); Elf64_Half mach; if (platform == CPU_PLATFORM) mach = machine + CPU_BASE; else if (platform == CAL_PLATFORM) mach = machine + CAL_BASE; else mach = machine; if (_eclass == ELFCLASS32) { Elf32_Ehdr* ehdr32 = elf32_getehdr(_e); if (ehdr32 == NULL) { _err.xfail("setTarget() : failed in elf32_getehdr()- %s.", elf_errmsg(-1)); return false; } ehdr32->e_ident[EI_DATA] = ELFDATA2LSB; ehdr32->e_type = ET_NONE; ehdr32->e_machine = (Elf32_Half)mach; } else { Elf64_Ehdr* ehdr64 = elf64_getehdr(_e); if (ehdr64 == NULL) { _err.xfail("setTarget() : failed in elf64_getehdr()- %s.", elf_errmsg(-1)); return false; } ehdr64->e_ident[EI_DATA] = ELFDATA2LSB; ehdr64->e_type = ET_NONE; ehdr64->e_machine = mach; } return true; } bool OclElf::getType(uint16_t &type) { assert(_e != 0); if (_eclass == ELFCLASS32) { Elf32_Ehdr* ehdr32 = elf32_getehdr(_e); if (ehdr32 == NULL) { _err.xfail("setTarget() : failed in elf32_getehdr()- %s.", elf_errmsg(-1)); return false; } type = ehdr32->e_type; } else { Elf64_Ehdr* ehdr64 = elf64_getehdr(_e); if (ehdr64 == NULL) { _err.xfail("setTarget() : failed in elf64_getehdr()- %s.", elf_errmsg(-1)); return false; } type = ehdr64->e_type; } return true; } bool OclElf::setType(uint16_t type) { assert(_e != 0); if (_eclass == ELFCLASS32) { Elf32_Ehdr* ehdr32 = elf32_getehdr(_e); if (ehdr32 == NULL) { _err.xfail("setTarget() : failed in elf32_getehdr()- %s.", elf_errmsg(-1)); return false; } ehdr32->e_type = type; } else { Elf64_Ehdr* ehdr64 = elf64_getehdr(_e); if (ehdr64 == NULL) { _err.xfail("setTarget() : failed in elf64_getehdr()- %s.", elf_errmsg(-1)); return false; } ehdr64->e_type = type; } return true; } bool OclElf::getFlags(uint32_t &flag) { assert(_e != 0); if (_eclass == ELFCLASS32) { Elf32_Ehdr* ehdr32 = elf32_getehdr(_e); if (ehdr32 == NULL) { _err.xfail("setTarget() : failed in elf32_getehdr()- %s.", elf_errmsg(-1)); return false; } flag = ehdr32->e_flags; } else { Elf64_Ehdr* ehdr64 = elf64_getehdr(_e); if (ehdr64 == NULL) { _err.xfail("setTarget() : failed in elf64_getehdr()- %s.", elf_errmsg(-1)); return false; } flag = ehdr64->e_flags; } return true; } bool OclElf::setFlags(uint32_t flag) { assert(_e != 0); if (_eclass == ELFCLASS32) { Elf32_Ehdr* ehdr32 = elf32_getehdr(_e); if (ehdr32 == NULL) { _err.xfail("setTarget() : failed in elf32_getehdr()- %s.", elf_errmsg(-1)); return false; } ehdr32->e_flags = flag; } else { Elf64_Ehdr* ehdr64 = elf64_getehdr(_e); if (ehdr64 == NULL) { _err.xfail("setTarget() : failed in elf64_getehdr()- %s.", elf_errmsg(-1)); return false; } ehdr64->e_flags = flag; } return true; } /* returns true if success; return false if fail. scn will return scn for 'id'. */ bool OclElf::getSectionDesc(Elf_Scn*& scn, OclElf::oclElfSections id) const { if ( ((id == SHSTRTAB) && (_shstrtab_ndx != 0)) || ((id == STRTAB) && (_strtab_ndx != 0)) ) { // Special (efficient) processing of SHSTRTAB/STRTAB size_t idx = (id == SHSTRTAB) ? _shstrtab_ndx : _strtab_ndx; if ((scn = elf_getscn(_e, idx)) == NULL) { _err.xfail("OclElf::addSectionDesc(): elf_getscn() failed - %s", elf_errmsg(-1)); return false; } } else { /* Search sections */ const char* sname = oclElfSecDesc[id].name; for (scn = elf_nextscn(_e, 0); scn != NULL; scn = elf_nextscn(_e, scn)) { size_t idx = elf_ndxscn(scn); if ( ((idx == _shstrtab_ndx) && (_shstrtab_ndx != 0)) || ((idx == _strtab_ndx) && (_strtab_ndx != 0)) ) { continue; } GElf_Shdr shdr; if (gelf_getshdr(scn, &shdr) != &shdr) { _err.xfail("OclElf::getSectionDesc() : failed in gelf_getshdr()- %s.", elf_errmsg(-1)); return false; } /* Convert an index (to the shdr string table) to a char pointer */ char *nm = elf_strptr(_e, _shstrtab_ndx, shdr.sh_name); if (strcmp(sname, nm ? nm : "") == 0) { // Found ! break; } } } return true; } /* Return true if success; return false if fail. data will return Elf_Data. */ bool OclElf::getSectionData(Elf_Data*& data, OclElf::oclElfSections id) const { assert(_e != 0); data = NULL; Elf_Scn* scn; if (!getSectionDesc(scn, id)) { return false; } if (scn != NULL) { // There is only one data descriptor (we are reading!) data = elf_getdata(scn, 0); } return true; } /* Get the whole section, assuming that there is only one data descriptor */ bool OclElf::getSection(OclElf::oclElfSections id, char** dst, size_t* sz) const { assert((oclElfSecDesc[id].id == id) && "oclElfSecDesc[] should be in the same order as enum oclElfSections"); Elf_Data* data; if (!getSectionData(data, id)) { _err.xfail("OclElf::getSection() failed in getSectionData()"); return false; } if (data == NULL) { *dst = NULL; *sz = 0; } else { *sz = (size_t)data->d_size; *dst = (char*)data->d_buf; } return true; } /* API routines for manipulating symbols */ Sym_Handle OclElf::nextSymbol(Sym_Handle symHandle) const { size_t sz; char* beg, *end; if (!getSection(SYMTAB, &beg, &sz)) { _err.xfail("OclElf::nextSymbol() failed in getSection()"); return NULL; } if ( (beg == 0) || (sz == 0) ) { return NULL; } end = beg + sz; if (_eclass == ELFCLASS64) { // Skip the first dummy symbol (STT_NOTYPE) beg += sizeof(Elf64_Sym); if (beg == end) { // No valid symbols in the table return NULL; } if (symHandle == NULL) { // Return the first symbol return reinterpret_cast(beg); } // Return the next symbol Elf64_Sym* sym64 = reinterpret_cast(symHandle); sym64++; if (reinterpret_cast(sym64) == end) { return NULL; } return reinterpret_cast(sym64); } else { // Skip the first dummy symbol (STT_NOTYPE) beg += sizeof(Elf32_Sym); if (beg == end) { // No valid symbols in the table return NULL; } if (symHandle == NULL) { // Return the first symbol return reinterpret_cast(beg); } Elf32_Sym* sym32 = reinterpret_cast(symHandle); sym32++; if (reinterpret_cast(sym32) == end) { return NULL; } return reinterpret_cast(sym32); } // UNREACHABLE return NULL; } /* Given a symbol handle, return info for this symbol Fails with symbols which have special section indexes (like absolute symbols). It is impossible to return valid SymbolInfo for such symbols because correct section names are unknown (unspecified in ELF). */ bool OclElf::getSymbolInfo(Sym_Handle symHandle, SymbolInfo* symInfo) const { assert(_e != 0); Elf_Scn *scn; char* sym_name; Elf64_Addr st_value; /* visibility */ Elf64_Xword st_size; /* index of related section */ if (_eclass == ELFCLASS64) { Elf64_Sym* sym64 = reinterpret_cast(symHandle); if (sym64->st_shndx >= SHN_LORESERVE && sym64->st_shndx <= SHN_HIRESERVE) { return false; } sym_name = elf_strptr(_e, _strtab_ndx, sym64->st_name); st_value = (Elf64_Addr)(sym64->st_value); st_size = (Elf64_Xword)(sym64->st_size); // get section scn = elf_getscn(_e, sym64->st_shndx); } else { Elf32_Sym* sym32 = reinterpret_cast(symHandle); if (sym32->st_shndx >= SHN_LORESERVE && sym32->st_shndx <= SHN_HIRESERVE) { return false; } sym_name = elf_strptr(_e, _strtab_ndx, sym32->st_name); st_value = (Elf64_Addr)(sym32->st_value); st_size = (Elf64_Xword)(sym32->st_size); // get section scn = elf_getscn(_e, sym32->st_shndx); } GElf_Shdr gshdr; if (gelf_getshdr(scn, &gshdr) == NULL) { _err.xfail("OclElf::getSymbolInfo() failed in gelf_getshdr() - %s.", elf_errmsg(-1)); return false; } char* sec_name = elf_strptr(_e, _shstrtab_ndx, gshdr.sh_name); // Assume there is only one Elf_Data. For reading, it's always true Elf_Data* data = elf_getdata(scn, 0); if (data == NULL) { symInfo->sec_addr = (char*)NULL; symInfo->sec_size = 0; symInfo->address = (char*)NULL; symInfo->size = (uint64_t)0; } else { symInfo->sec_addr = (char*)data->d_buf; symInfo->sec_size = data->d_size; symInfo->address = symInfo->sec_addr + (size_t)st_value; symInfo->size = (uint64_t)st_size; } symInfo->sec_name = sec_name; symInfo->sym_name = sym_name; return true; } /* AddSectionData() will add data into a section. Return the offset of the data in this section if success; return -1 if fail. */ bool OclElf::addSectionData ( Elf64_Xword& outOffset, oclElfSections id, const void* buffer, size_t size, bool do_copy // true if buffer needs to be copied ) { outOffset = 0; const char* secName = oclElfSecDesc[id].name; GElf_Shdr shdr; Elf_Scn* scn; if (!getSectionDesc(scn, id)) { return false; } assert (scn && "Elf_Scn should have been created already"); if (gelf_getshdr(scn, &shdr) != &shdr) { _err.xfail("OclElf::addSectionData(): gelf_getshdr() failed - %s", elf_errmsg(-1)); return false; } outOffset = (Elf64_Xword)shdr.sh_size; /* addng Elf_Data descriptor associated with section scn */ Elf_Data* data = createElfData(scn, id, const_cast(buffer), (uint64_t)size, do_copy); if (data == NULL) { return false; } return true; } /* getShdrNdx() returns an index to the .shstrtab in 'outNdx' for "name" if it is in .shstrtab (outNdx == 0 means it is not in .shstrtab). It return true if it is successful; return false if en error occured. */ bool OclElf::getShstrtabNdx(Elf64_Word& outNdx, const char* name) { outNdx = 0; // .shstrtab must be created already Elf_Scn* scn = elf_getscn(_e, _shstrtab_ndx); if (scn == NULL) { _err.xfail("OclElf::getShdrNdx() failed in elf_getscn for section .shstrtab - %s", elf_errmsg(-1)); return false; } Elf_Data* data = elf_getdata(scn, NULL); if (data == NULL) { _err.xfail("Elf::getShdrNdx() failed in elf_getdata for section .shstrtab - %s", elf_errmsg(-1)); return false; } size_t name_sz = strlen(name); uint64_t data_offset = 0; do { if (data->d_size > name_sz) { char* base = (char*)data->d_buf; char* end = base + (size_t)data->d_size; char* b = base; char* e; while ( b != end) { e = b; // find the next 0 char while ( (e != end) && (*e != 0) ) { e++; } if ((e != end) && ((size_t)(e - b) == name_sz) && (strcmp(b, name) == 0)) { outNdx = (Elf64_Word)((b - base) + data_offset); return true; } b = e+1; } } data_offset += data->d_size; } while ((data = elf_getdata(scn, data)) != NULL); return true; } /* newSection() assumes that .shstrtab and .strtab have been created already. Return the pointer to the new section if success; return 0 if fail. */ Elf_Scn* OclElf::newSection ( OclElf::oclElfSections id, const void* d_buf, size_t d_size, bool do_copy ) { Elf64_Word sh_name; if (!getShstrtabNdx(sh_name, oclElfSecDesc[id].name)) { _err.xfail("OclElf::newSection() failed in getShstrtabNdx() for section %s", oclElfSecDesc[id].name); return NULL; } if (sh_name == 0) { // Need to create a new entry for this section name Elf64_Xword offset; if (!addSectionData(offset, SHSTRTAB, oclElfSecDesc[id].name, strlen(oclElfSecDesc[id].name) + 1, false)) { _err.xfail("OclElf::newSection() failed in getSectionData() for section %s", oclElfSecDesc[id].name); return NULL; } sh_name = (Elf64_Word)offset; } // Create a new section Elf_Scn* scn = elf_newscn(_e); if (scn == NULL) { _err.xfail("OclElf::newSection() failed in elf_newscn() - %s.", elf_errmsg(-1)); return NULL; } // If there is no data, skip creating Elf_Data if ((d_buf != NULL) && (d_size != 0)) { Elf_Data* data = createElfData(scn, id, const_cast(d_buf), (uint64_t)d_size, do_copy); if (data == NULL) { return NULL; } } if (!createShdr(id, scn, sh_name, (id == SYMTAB) ? _strtab_ndx : 0)) { return NULL; } if (elf_update(_e, ELF_C_NULL) < 0) { _err.xfail("OclElf::newSection(): elf_update() failed"); return NULL; } return scn; } /* Return true: success false: fail */ bool OclElf::addSection ( oclElfSections id, const void* d_buf, size_t d_size, bool do_copy ) { assert(oclElfSecDesc[id].id == id && "struct oclElfSecDesc should be ordered by id same as enum Elf::oclElfSections"); /* If section is already in elf object, simply return its address */ Elf_Scn* scn; if (!getSectionDesc(scn, id)) { // Failed return false; } if (scn != NULL) { Elf64_Xword sec_offset; if (!addSectionData(sec_offset, id, d_buf, d_size, do_copy)) { _err.xfail("OclElf::addSection() failed in addSectionData() for section name %s.", oclElfSecDesc[id].name); return false; } } else { scn = newSection(id, d_buf, d_size, do_copy); if (scn == NULL) { _err.xfail("OclElf::addSection() failed in newSection() for section name %s.", oclElfSecDesc[id].name); return false; } } return true; } bool OclElf::addSymbol( oclElfSections id, const char* symbolName, const void* buffer, size_t size, bool do_copy ) { assert(oclElfSecDesc[id].id == id && "The order of oclElfSecDesc[] and Elf::oclElfSections mismatches."); const char* sectionName = oclElfSecDesc[id].name; bool isFunction = ((id == OclElf::CAL) || (id == OclElf::DLL) || (id == OclElf::JITBINARY)) ? true : false; // Get section index Elf_Scn* scn; if (!getSectionDesc(scn, id)) { _err.xfail("OclElf::addSymbol() failed in getSectionDesc"); return false; } if (scn == NULL) { // Create a new section. if ((scn = newSection(id, NULL, 0, false)) == NULL) { _err.xfail("OclElf::addSymbol() failed in newSection"); return false; } } size_t sec_ndx = elf_ndxscn(scn); if (sec_ndx == SHN_UNDEF) { _err.xfail("OclElf::addSymbol() failed in elf_ndxscn() - %s.", elf_errmsg(-1)); return false; } // Put symbolName into .strtab section Elf64_Xword strtab_offset; if (!addSectionData(strtab_offset, STRTAB, (void*)symbolName, strlen(symbolName)+1, true)) { _err.xfail("OclElf::addSymbol() failed in addSectionData(.strtab)"); return false; } // Put buffer into section Elf64_Xword sec_offset = 0; if ( (buffer != NULL) && (size != 0) ) { if (!addSectionData(sec_offset, id, buffer, size, do_copy)) { _err.xfail("OclElf::addSymbol() failed in addSectionData(%s)", sectionName); return false; } } bool retvalue; Elf64_Xword symtab_offset; if (_eclass == ELFCLASS64) { Elf64_Sym* sym64 = (Elf64_Sym*)oclelf_calloc(sizeof(Elf64_Sym)); sym64->st_name = (Elf64_Word)strtab_offset; sym64->st_value = (Elf64_Addr)sec_offset; sym64->st_size = (Elf64_Xword)size; sym64->st_info = (isFunction)? STT_FUNC : STT_OBJECT; sym64->st_shndx = (Elf64_Section)sec_ndx; retvalue = addSectionData(symtab_offset, SYMTAB, sym64, sizeof(Elf64_Sym), false); } else { // _eclass == ELFCLASS32 Elf32_Sym* sym32 = (Elf32_Sym*)oclelf_calloc(sizeof(Elf32_Sym)); sym32->st_name = (Elf32_Word)strtab_offset; sym32->st_value = (Elf32_Addr)sec_offset; sym32->st_size = (Elf32_Word)size; sym32->st_info = (isFunction)? STT_FUNC : STT_OBJECT; sym32->st_shndx = (Elf32_Section)sec_ndx; retvalue = addSectionData(symtab_offset, SYMTAB, sym32, sizeof(Elf32_Sym), false); } if (!retvalue) { _err.xfail("OclElf::addSymbol() failed in addSectionData(.symtab)"); return false; } if (elf_update(_e, ELF_C_NULL) < 0) { _err.xfail("OclElf::addSymbol() : elf_update() failed"); return false; } return true; } bool OclElf::getSymbol( oclElfSections id, const char* symbolName, char** buffer, size_t* size ) const { assert(oclElfSecDesc[id].id == id && "The order of oclElfSecDesc[] and Elf::oclElfSections mismatches."); if (!size || !buffer || !symbolName) { return false; } // Initialize the size and buffer to invalid data points. (*size) = 0; (*buffer) = NULL; for (amd::Sym_Handle s = nextSymbol(NULL); s; s = nextSymbol(s)) { amd::OclElf::SymbolInfo si; // Continue if symbol information is not retrieved. if (!getSymbolInfo(s, &si)) { continue; } // Continue if the symbol is in the wrong section. if (strcmp(oclElfSecDesc[id].name, si.sec_name)) { continue; } // Continue if the symbol name doesn't match. if (strcmp(symbolName, si.sym_name)) { continue; } // Set the size and the address and return true. (*size) = si.size; (*buffer) = si.address; return true; } return false; } bool OclElf::addNote( const char* noteName, const char* noteDesc, size_t nameSize, size_t descSize ) { if ((nameSize == 0 && descSize == 0) || (nameSize != 0 && noteName == NULL) || (descSize != 0 && noteDesc == NULL)) { _err.xfail("OclElf::addNote() empty note"); return false; } const oclElfSections sid = NOTES; assert(oclElfSecDesc[sid].id == sid && "The order of oclElfSecDesc[] and Elf::oclElfSections mismatches."); // Get section Elf_Scn* scn; if (!getSectionDesc(scn, sid)) { _err.xfail("OclElf::addNote() failed in getSectionDesc"); return false; } if (scn == NULL) { // Create a new section. if ((scn = newSection(sid, NULL, 0, false)) == NULL) { _err.xfail("OclElf::addNote() failed in newSection"); return false; } } // Put note into section Elf64_Xword sec_offset = 0; size_t bufsize = sizeof(Elf_Note) + nameSize + descSize; char* buffer = (char*)oclelf_calloc(bufsize); if (buffer == NULL) return false; Elf_Note* en = reinterpret_cast(buffer); en->n_namesz = nameSize; en->n_descsz = descSize; en->n_type = 0; memcpy(buffer+sizeof(Elf_Note), noteName, nameSize); memcpy(buffer+sizeof(Elf_Note)+nameSize, noteDesc, descSize); if (!addSectionData(sec_offset, sid, buffer, bufsize, false/*not copy*/)) { const char* sectionName = oclElfSecDesc[sid].name; _err.xfail("OclElf::addNote() failed in addSectionData(%s)", sectionName); return false; } if (elf_update(_e, ELF_C_NULL) < 0) { _err.xfail("OclElf::addNote() : elf_update() failed"); return false; } return true; } bool OclElf::getNote( const char* noteName, char** noteDesc, size_t *descSize ) { if (!descSize || !noteDesc || !noteName) { return false; } const oclElfSections sid = NOTES; assert(oclElfSecDesc[sid].id == sid && "The order of oclElfSecDesc[] and Elf::oclElfSections mismatches."); // Get section Elf_Scn* scn; if (!getSectionDesc(scn, sid)) { _err.xfail("OclElf::getNote() failed in getSectionDesc"); return false; } if (scn == NULL) { _err.xfail("OclElf::getNote() failed: .note section not found"); return false; } // read the whole .note section Elf_Data* data = elf_getdata(scn, 0); // Initialize the size and buffer to invalid data points. *descSize = 0; *noteDesc = NULL; // look for the specified note char* ptr = (char*)data->d_buf; while (ptr < (char*)data->d_buf + data->d_size) { Elf_Note* note = reinterpret_cast(ptr); // Continue if the note name doesn't match. if (strlen(noteName) != note->n_namesz || strncmp(noteName, ptr+sizeof(Elf_Note), note->n_namesz) != 0) { ptr += sizeof(Elf_Note) + note->n_namesz + note->n_descsz; continue; } // Set the size and the address and return true. *descSize = note->n_descsz; *noteDesc = ptr + sizeof(Elf_Note) + note->n_namesz; return true; } return false; } bool OclElf::dumpImage(char** buff, size_t* len) { if (buff == NULL || len == NULL ) { return false; } assert ((_fd != -1) && "_fd in Elf::dumpImage should be defined"); // Now, write the ELF into the file if (elf_update(_e, ELF_C_WRITE) < 0) { _err.xfail("OclElf::dumpImage() : elf_update() failed - %s", elf_errmsg(-1)); return false; } int buff_sz = xlseek(_err, _fname, _fd, 0, SEEK_END); if (buff_sz == -1) { return false; } /* The memory is owned by caller, and caller assumes that the memory is new'ed. So, use new instead of malloc */ *buff = new char[buff_sz]; if (*buff == NULL) { _err.xfail("OclElf::dumpImage() : new char[sz] failed"); return false; } if (xlseek(_err, _fname, _fd, 0, SEEK_SET) == -1) { _err.xfail("OclElf::dumpImage() failed in xlseek()"); delete [] *buff; return false; } if (xread(_err, _fname, _fd, *buff, buff_sz) != buff_sz) { _err.xfail("OclElf::dumpImage() failed in xread()"); delete [] *buff; *buff = 0; return false; } *len = buff_sz; return true; } void* OclElf::oclelf_allocAndCopy(void* p, size_t sz) { if (p == 0 || sz == 0) return p; void* buf = xmalloc(_err, sz); if (buf == 0) { _err.xfail("OclElf::oclelf_allocAndCopy() failed"); return 0; } memcpy(buf, p, sz); _elfMemory.insert( std::make_pair(buf, sz)); return buf; } void* OclElf::oclelf_calloc(size_t sz) { void* buf = xmalloc(_err, sz); if (buf == 0) { _err.xfail("OclElf::oclelf_calloc() failed"); return 0; } _elfMemory.insert( std::make_pair(buf, sz)); return buf; } void OclElf::elfMemoryRelease() { for(EMemory::iterator it = _elfMemory.begin(); it != _elfMemory.end(); it++) { free(it->first); } _elfMemory.clear(); } } // namespace amd