elf_object.cc revision 4484
12817Sksewell@umich.edu/*
28733Sgeoffrey.blake@arm.com * Copyright (c) 2003-2005 The Regents of The University of Michigan
38733Sgeoffrey.blake@arm.com * All rights reserved.
48733Sgeoffrey.blake@arm.com *
58733Sgeoffrey.blake@arm.com * Redistribution and use in source and binary forms, with or without
68733Sgeoffrey.blake@arm.com * modification, are permitted provided that the following conditions are
78733Sgeoffrey.blake@arm.com * met: redistributions of source code must retain the above copyright
88733Sgeoffrey.blake@arm.com * notice, this list of conditions and the following disclaimer;
98733Sgeoffrey.blake@arm.com * redistributions in binary form must reproduce the above copyright
108733Sgeoffrey.blake@arm.com * notice, this list of conditions and the following disclaimer in the
118733Sgeoffrey.blake@arm.com * documentation and/or other materials provided with the distribution;
128733Sgeoffrey.blake@arm.com * neither the name of the copyright holders nor the names of its
138733Sgeoffrey.blake@arm.com * contributors may be used to endorse or promote products derived from
142817Sksewell@umich.edu * this software without specific prior written permission.
152817Sksewell@umich.edu *
162817Sksewell@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
172817Sksewell@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
182817Sksewell@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
192817Sksewell@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
202817Sksewell@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
212817Sksewell@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
222817Sksewell@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232817Sksewell@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242817Sksewell@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252817Sksewell@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
262817Sksewell@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272817Sksewell@umich.edu *
282817Sksewell@umich.edu * Authors: Steve Reinhardt
292817Sksewell@umich.edu *          Ali Saidi
302817Sksewell@umich.edu */
312817Sksewell@umich.edu
322817Sksewell@umich.edu#include <string>
332817Sksewell@umich.edu
342817Sksewell@umich.edu#include "gelf.h"
352817Sksewell@umich.edu
362817Sksewell@umich.edu#include "base/loader/elf_object.hh"
372817Sksewell@umich.edu#include "base/loader/symtab.hh"
382817Sksewell@umich.edu#include "base/misc.hh"
392817Sksewell@umich.edu#include "base/trace.hh"	// for DPRINTF
402817Sksewell@umich.edu#include "sim/byteswap.hh"
412817Sksewell@umich.edu
422817Sksewell@umich.eduusing namespace std;
432817Sksewell@umich.edu
442817Sksewell@umich.eduObjectFile *
452817Sksewell@umich.eduElfObject::tryFile(const string &fname, int fd, size_t len, uint8_t *data)
466658Snate@binkert.org{
478229Snate@binkert.org    Elf *elf;
482935Sksewell@umich.edu    GElf_Ehdr ehdr;
492817Sksewell@umich.edu    Arch arch = UnknownArch;
502834Sksewell@umich.edu    OpSys opSys = UnknownOpSys;
512834Sksewell@umich.edu
522834Sksewell@umich.edu    // check that header matches library version
538902Sandreas.hansson@arm.com    if (elf_version(EV_CURRENT) == EV_NONE)
542834Sksewell@umich.edu        panic("wrong elf version number!");
552817Sksewell@umich.edu
562817Sksewell@umich.edu    // get a pointer to elf structure
572817Sksewell@umich.edu    elf = elf_memory((char*)data,len);
582817Sksewell@umich.edu    // will only fail if fd is invalid
592817Sksewell@umich.edu    assert(elf != NULL);
602817Sksewell@umich.edu
612817Sksewell@umich.edu    // Check that we actually have a elf file
622817Sksewell@umich.edu    if (gelf_getehdr(elf, &ehdr) ==0) {
632817Sksewell@umich.edu        DPRINTFR(Loader, "Not ELF\n");
642817Sksewell@umich.edu        elf_end(elf);
652817Sksewell@umich.edu        return NULL;
662817Sksewell@umich.edu    } else {
672817Sksewell@umich.edu        //Detect the architecture
682817Sksewell@umich.edu        //Since we don't know how to check for alpha right now, we'll
692817Sksewell@umich.edu        //just assume if it wasn't something else and it's 64 bit, that's
702817Sksewell@umich.edu        //what it must be.
712817Sksewell@umich.edu        if (ehdr.e_machine == EM_SPARC64 ||
722817Sksewell@umich.edu                (ehdr.e_machine == EM_SPARC &&
732817Sksewell@umich.edu                 ehdr.e_ident[EI_CLASS] == ELFCLASS64)||
742817Sksewell@umich.edu                ehdr.e_machine == EM_SPARCV9) {
752817Sksewell@umich.edu            arch = ObjectFile::SPARC64;
762817Sksewell@umich.edu        } else if (ehdr.e_machine == EM_SPARC32PLUS ||
772817Sksewell@umich.edu                        (ehdr.e_machine == EM_SPARC &&
782817Sksewell@umich.edu                         ehdr.e_ident[EI_CLASS] == ELFCLASS32)) {
792817Sksewell@umich.edu            arch = ObjectFile::SPARC32;
803784Sgblack@eecs.umich.edu        } else if (ehdr.e_machine == EM_MIPS
816022Sgblack@eecs.umich.edu                && ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
823784Sgblack@eecs.umich.edu            arch = ObjectFile::Mips;
833784Sgblack@eecs.umich.edu        } else if (ehdr.e_machine == EM_X86_64 &&
846022Sgblack@eecs.umich.edu                ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
853784Sgblack@eecs.umich.edu            //In the future, we might want to differentiate between 32 bit
868887Sgeoffrey.blake@arm.com            //and 64 bit x86 processes in case there are differences in their
878733Sgeoffrey.blake@arm.com            //initial stack frame.
889023Sgblack@eecs.umich.edu            arch = ObjectFile::X86;
899023Sgblack@eecs.umich.edu        } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
909023Sgblack@eecs.umich.edu            arch = ObjectFile::Alpha;
919023Sgblack@eecs.umich.edu        } else {
929023Sgblack@eecs.umich.edu            warn("Unknown architecture: %d\n", ehdr.e_machine);
938541Sgblack@eecs.umich.edu            arch = ObjectFile::UnknownArch;
942817Sksewell@umich.edu        }
952817Sksewell@umich.edu
962817Sksewell@umich.edu        //Detect the operating system
972817Sksewell@umich.edu        switch (ehdr.e_ident[EI_OSABI])
985712Shsul@eecs.umich.edu        {
992817Sksewell@umich.edu
1005714Shsul@eecs.umich.edu          case ELFOSABI_LINUX:
1015714Shsul@eecs.umich.edu            opSys = ObjectFile::Linux;
1025714Shsul@eecs.umich.edu            break;
1035714Shsul@eecs.umich.edu          case ELFOSABI_SOLARIS:
1045715Shsul@eecs.umich.edu            opSys = ObjectFile::Solaris;
1055715Shsul@eecs.umich.edu            break;
1065715Shsul@eecs.umich.edu          case ELFOSABI_TRU64:
1075715Shsul@eecs.umich.edu            opSys = ObjectFile::Tru64;
1082817Sksewell@umich.edu            break;
1092817Sksewell@umich.edu          default:
1102817Sksewell@umich.edu            opSys = ObjectFile::UnknownOpSys;
1112817Sksewell@umich.edu        }
1123548Sgblack@eecs.umich.edu
1132817Sksewell@umich.edu        //take a look at the .note.ABI section
1142817Sksewell@umich.edu        //It can let us know what's what.
1158541Sgblack@eecs.umich.edu        if (opSys == ObjectFile::UnknownOpSys) {
1168541Sgblack@eecs.umich.edu            Elf_Scn *section;
1178754Sgblack@eecs.umich.edu            GElf_Shdr shdr;
1188852Sandreas.hansson@arm.com            Elf_Data *data;
1192817Sksewell@umich.edu            uint32_t osAbi;;
1208852Sandreas.hansson@arm.com            int secIdx = 1;
1213675Sktlim@umich.edu
1228706Sandreas.hansson@arm.com            // Get the first section
1238706Sandreas.hansson@arm.com            section = elf_getscn(elf, secIdx);
1248799Sgblack@eecs.umich.edu
1258852Sandreas.hansson@arm.com            // While there are no more sections
1268706Sandreas.hansson@arm.com            while (section != NULL && opSys == ObjectFile::UnknownOpSys) {
1272817Sksewell@umich.edu                gelf_getshdr(section, &shdr);
1282817Sksewell@umich.edu                if (shdr.sh_type == SHT_NOTE && !strcmp(".note.ABI-tag",
1292817Sksewell@umich.edu                            elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name))) {
1302817Sksewell@umich.edu                    // we have found a ABI note section
1312817Sksewell@umich.edu                    // Check the 5th 32bit word for OS  0 == linux, 1 == hurd,
1322817Sksewell@umich.edu                    // 2 == solaris, 3 == freebsd
1332817Sksewell@umich.edu                    data = elf_rawdata(section, NULL);
1342817Sksewell@umich.edu                    assert(data->d_buf);
1352817Sksewell@umich.edu                    if(ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
1362817Sksewell@umich.edu                        osAbi = htole(((uint32_t*)data->d_buf)[4]);
1379180Sandreas.hansson@arm.com                    else
1382817Sksewell@umich.edu                        osAbi = htobe(((uint32_t*)data->d_buf)[4]);
1392817Sksewell@umich.edu
1409180Sandreas.hansson@arm.com                    switch(osAbi) {
1412817Sksewell@umich.edu                      case 0:
1422817Sksewell@umich.edu                        opSys = ObjectFile::Linux;
1439180Sandreas.hansson@arm.com                        break;
1442817Sksewell@umich.edu                      case 2:
1452817Sksewell@umich.edu                        opSys = ObjectFile::Solaris;
1462817Sksewell@umich.edu                        break;
1472817Sksewell@umich.edu                    }
1482817Sksewell@umich.edu                } // if section found
1498777Sgblack@eecs.umich.edu                if (!strcmp(".SUNW_version", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)))
1502817Sksewell@umich.edu                        opSys = ObjectFile::Solaris;
1512817Sksewell@umich.edu                if (!strcmp(".stab.index", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)))
1522817Sksewell@umich.edu                        opSys = ObjectFile::Solaris;
1532817Sksewell@umich.edu
1542817Sksewell@umich.edu            section = elf_getscn(elf, ++secIdx);
1552817Sksewell@umich.edu            } // while sections
1562817Sksewell@umich.edu        }
1572817Sksewell@umich.edu
1582817Sksewell@umich.edu        ElfObject * result = new ElfObject(fname, fd, len, data, arch, opSys);
1592817Sksewell@umich.edu
1602817Sksewell@umich.edu        //The number of headers in the file
1612817Sksewell@umich.edu        result->_programHeaderCount = ehdr.e_phnum;
1622817Sksewell@umich.edu        //Record the size of each entry
1632817Sksewell@umich.edu        result->_programHeaderSize = ehdr.e_phentsize;
1642817Sksewell@umich.edu        if(result->_programHeaderCount) //If there is a program header table
1652817Sksewell@umich.edu        {
1662817Sksewell@umich.edu            //Figure out the virtual address of the header table in the
1672817Sksewell@umich.edu            //final memory image. We use the program headers themselves
1682817Sksewell@umich.edu            //to translate from a file offset to the address in the image.
1692817Sksewell@umich.edu            GElf_Phdr phdr;
1702817Sksewell@umich.edu            uint64_t e_phoff = ehdr.e_phoff;
1712817Sksewell@umich.edu            result->_programHeaderTable = 0;
1722817Sksewell@umich.edu            for(int hdrnum = 0; hdrnum < result->_programHeaderCount; hdrnum++)
1732817Sksewell@umich.edu            {
1742817Sksewell@umich.edu                gelf_getphdr(elf, hdrnum, &phdr);
1752817Sksewell@umich.edu                //Check if we've found the segment with the headers in it
1762817Sksewell@umich.edu                if(phdr.p_offset <= e_phoff &&
1772817Sksewell@umich.edu                        phdr.p_offset + phdr.p_filesz > e_phoff)
1782817Sksewell@umich.edu                {
1792817Sksewell@umich.edu                    result->_programHeaderTable = phdr.p_vaddr + e_phoff;
1802817Sksewell@umich.edu                    break;
1812817Sksewell@umich.edu                }
1822817Sksewell@umich.edu            }
1832817Sksewell@umich.edu        }
1842817Sksewell@umich.edu        else
1852817Sksewell@umich.edu            result->_programHeaderTable = 0;
1862817Sksewell@umich.edu
1872817Sksewell@umich.edu
1882817Sksewell@umich.edu        elf_end(elf);
1892817Sksewell@umich.edu        return result;
1902817Sksewell@umich.edu    }
1917720Sgblack@eecs.umich.edu}
1927720Sgblack@eecs.umich.edu
1937720Sgblack@eecs.umich.edu
1947720Sgblack@eecs.umich.eduElfObject::ElfObject(const string &_filename, int _fd,
1957720Sgblack@eecs.umich.edu                     size_t _len, uint8_t *_data,
1967720Sgblack@eecs.umich.edu                     Arch _arch, OpSys _opSys)
1977720Sgblack@eecs.umich.edu    : ObjectFile(_filename, _fd, _len, _data, _arch, _opSys)
1988733Sgeoffrey.blake@arm.com
1998733Sgeoffrey.blake@arm.com{
2002817Sksewell@umich.edu    Elf *elf;
2017720Sgblack@eecs.umich.edu    GElf_Ehdr ehdr;
2027720Sgblack@eecs.umich.edu
2032817Sksewell@umich.edu    // check that header matches library version
2042817Sksewell@umich.edu    if (elf_version(EV_CURRENT) == EV_NONE)
2057720Sgblack@eecs.umich.edu        panic("wrong elf version number!");
2067720Sgblack@eecs.umich.edu
2072817Sksewell@umich.edu    // get a pointer to elf structure
2087720Sgblack@eecs.umich.edu    elf = elf_memory((char*)fileData,len);
2097720Sgblack@eecs.umich.edu    // will only fail if fd is invalid
2107720Sgblack@eecs.umich.edu    assert(elf != NULL);
2115259Sksewell@umich.edu
2122817Sksewell@umich.edu    // Check that we actually have a elf file
2134172Ssaidi@eecs.umich.edu    if (gelf_getehdr(elf, &ehdr) ==0) {
2145715Shsul@eecs.umich.edu        panic("Not ELF, shouldn't be here");
2154172Ssaidi@eecs.umich.edu    }
2164172Ssaidi@eecs.umich.edu
2174172Ssaidi@eecs.umich.edu    entry = ehdr.e_entry;
2182817Sksewell@umich.edu
2195715Shsul@eecs.umich.edu
2202817Sksewell@umich.edu    // initialize segment sizes to 0 in case they're not present
2212817Sksewell@umich.edu    text.size = data.size = bss.size = 0;
2224172Ssaidi@eecs.umich.edu
2232817Sksewell@umich.edu    for (int i = 0; i < ehdr.e_phnum; ++i) {
2242817Sksewell@umich.edu        GElf_Phdr phdr;
2252817Sksewell@umich.edu        if (gelf_getphdr(elf, i, &phdr) == 0) {
2264172Ssaidi@eecs.umich.edu            panic("gelf_getphdr failed for section %d", i);
2272817Sksewell@umich.edu        }
2286313Sgblack@eecs.umich.edu
2296313Sgblack@eecs.umich.edu        // for now we don't care about non-loadable segments
2306313Sgblack@eecs.umich.edu        if (!(phdr.p_type & PT_LOAD))
2312817Sksewell@umich.edu            continue;
2322817Sksewell@umich.edu
2332817Sksewell@umich.edu        // the headers don't explicitly distinguish text from data,
2342817Sksewell@umich.edu        // but empirically the text segment comes first.
2352817Sksewell@umich.edu        if (text.size == 0) {  // haven't seen text segment yet
2362817Sksewell@umich.edu            text.baseAddr = phdr.p_vaddr;
2372817Sksewell@umich.edu            text.size = phdr.p_filesz;
2382817Sksewell@umich.edu            text.fileImage = fileData + phdr.p_offset;
2392817Sksewell@umich.edu            // if there's any padding at the end that's not in the
2402817Sksewell@umich.edu            // file, call it the bss.  This happens in the "text"
2412817Sksewell@umich.edu            // segment if there's only one loadable segment (as for
2422817Sksewell@umich.edu            // kernel images).
2432817Sksewell@umich.edu            bss.size = phdr.p_memsz - phdr.p_filesz;
2442817Sksewell@umich.edu            bss.baseAddr = phdr.p_vaddr + phdr.p_filesz;
2452817Sksewell@umich.edu            bss.fileImage = NULL;
2462817Sksewell@umich.edu        } else if (data.size == 0) { // have text, this must be data
2472817Sksewell@umich.edu            data.baseAddr = phdr.p_vaddr;
2482817Sksewell@umich.edu            data.size = phdr.p_filesz;
2492817Sksewell@umich.edu            data.fileImage = fileData + phdr.p_offset;
2505715Shsul@eecs.umich.edu            // if there's any padding at the end that's not in the
2512817Sksewell@umich.edu            // file, call it the bss.  Warn if this happens for both
2522817Sksewell@umich.edu            // the text & data segments (should only have one bss).
2532817Sksewell@umich.edu            if (phdr.p_memsz - phdr.p_filesz > 0 && bss.size != 0) {
2548777Sgblack@eecs.umich.edu                warn("Two implied bss segments in file!\n");
2555595Sgblack@eecs.umich.edu            }
2565595Sgblack@eecs.umich.edu            bss.size = phdr.p_memsz - phdr.p_filesz;
2575595Sgblack@eecs.umich.edu            bss.baseAddr = phdr.p_vaddr + phdr.p_filesz;
2585595Sgblack@eecs.umich.edu            bss.fileImage = NULL;
2595595Sgblack@eecs.umich.edu        } else {
2605595Sgblack@eecs.umich.edu            warn("More than two loadable segments in ELF object.");
2612817Sksewell@umich.edu            warn("Ignoring segment @ 0x%x length 0x%x.",
2622817Sksewell@umich.edu                 phdr.p_vaddr, phdr.p_filesz);
2632817Sksewell@umich.edu        }
264    }
265
266    // should have found at least one loadable segment
267    assert(text.size != 0);
268
269    DPRINTFR(Loader, "text: 0x%x %d\ndata: 0x%x %d\nbss: 0x%x %d\n",
270             text.baseAddr, text.size, data.baseAddr, data.size,
271             bss.baseAddr, bss.size);
272
273    elf_end(elf);
274
275    // We will actually read the sections when we need to load them
276}
277
278
279bool
280ElfObject::loadSomeSymbols(SymbolTable *symtab, int binding)
281{
282    Elf *elf;
283    int sec_idx = 1; // there is a 0 but it is nothing, go figure
284    Elf_Scn *section;
285    GElf_Shdr shdr;
286    Elf_Data *data;
287    int count, ii;
288    bool found = false;
289    GElf_Sym sym;
290
291    if (!symtab)
292        return false;
293
294    // check that header matches library version
295    if (elf_version(EV_CURRENT) == EV_NONE)
296        panic("wrong elf version number!");
297
298    // get a pointer to elf structure
299    elf = elf_memory((char*)fileData,len);
300
301    assert(elf != NULL);
302
303    // Get the first section
304    section = elf_getscn(elf, sec_idx);
305
306    // While there are no more sections
307    while (section != NULL) {
308        gelf_getshdr(section, &shdr);
309
310        if (shdr.sh_type == SHT_SYMTAB) {
311            found = true;
312            data = elf_getdata(section, NULL);
313            count = shdr.sh_size / shdr.sh_entsize;
314            DPRINTF(Loader, "Found Symbol Table, %d symbols present\n", count);
315
316            // loop through all the symbols, only loading global ones
317            for (ii = 0; ii < count; ++ii) {
318                gelf_getsym(data, ii, &sym);
319                if (GELF_ST_BIND(sym.st_info) == binding) {
320                   symtab->insert(sym.st_value,
321                                  elf_strptr(elf, shdr.sh_link, sym.st_name));
322                }
323            }
324        }
325        ++sec_idx;
326        section = elf_getscn(elf, sec_idx);
327    }
328
329    elf_end(elf);
330
331    return found;
332}
333
334bool
335ElfObject::loadGlobalSymbols(SymbolTable *symtab, Addr addrMask)
336{
337    return loadSomeSymbols(symtab, STB_GLOBAL);
338}
339
340bool
341ElfObject::loadLocalSymbols(SymbolTable *symtab, Addr addrMask)
342{
343    return loadSomeSymbols(symtab, STB_LOCAL);
344}
345
346bool
347ElfObject::isDynamic()
348{
349    Elf *elf;
350    int sec_idx = 1; // there is a 0 but it is nothing, go figure
351    Elf_Scn *section;
352    GElf_Shdr shdr;
353
354    GElf_Ehdr ehdr;
355
356    // check that header matches library version
357    if (elf_version(EV_CURRENT) == EV_NONE)
358        panic("wrong elf version number!");
359
360    // get a pointer to elf structure
361    elf = elf_memory((char*)fileData,len);
362    assert(elf != NULL);
363
364    // Check that we actually have a elf file
365    if (gelf_getehdr(elf, &ehdr) ==0) {
366        panic("Not ELF, shouldn't be here");
367    }
368
369    // Get the first section
370    section = elf_getscn(elf, sec_idx);
371
372    // While there are no more sections
373    while (section != NULL) {
374        gelf_getshdr(section, &shdr);
375        if (!strcmp(".interp", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)))
376            return true;
377        section = elf_getscn(elf, ++sec_idx);
378    } // while sections
379    return false;
380}
381
382
383