elf_object.cc revision 6392
15222Sksewell@umich.edu/*
25268Sksewell@umich.edu * Copyright (c) 2003-2005 The Regents of The University of Michigan
35254Sksewell@umich.edu * All rights reserved.
45222Sksewell@umich.edu *
55254Sksewell@umich.edu * Redistribution and use in source and binary forms, with or without
65254Sksewell@umich.edu * modification, are permitted provided that the following conditions are
75254Sksewell@umich.edu * met: redistributions of source code must retain the above copyright
85254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer;
95254Sksewell@umich.edu * redistributions in binary form must reproduce the above copyright
105254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer in the
115254Sksewell@umich.edu * documentation and/or other materials provided with the distribution;
125254Sksewell@umich.edu * neither the name of the copyright holders nor the names of its
135254Sksewell@umich.edu * contributors may be used to endorse or promote products derived from
145254Sksewell@umich.edu * this software without specific prior written permission.
155222Sksewell@umich.edu *
165254Sksewell@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
175254Sksewell@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
185254Sksewell@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
195254Sksewell@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
205254Sksewell@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
215254Sksewell@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
225254Sksewell@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
235254Sksewell@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245254Sksewell@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255254Sksewell@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
265254Sksewell@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
275222Sksewell@umich.edu *
285254Sksewell@umich.edu * Authors: Steve Reinhardt
295254Sksewell@umich.edu *          Ali Saidi
305254Sksewell@umich.edu */
315222Sksewell@umich.edu
325222Sksewell@umich.edu#include <cassert>
335222Sksewell@umich.edu#include <string>
345222Sksewell@umich.edu
355222Sksewell@umich.edu#include "gelf.h"
365222Sksewell@umich.edu
375222Sksewell@umich.edu#include "base/loader/elf_object.hh"
385222Sksewell@umich.edu#include "base/loader/symtab.hh"
395222Sksewell@umich.edu#include "base/misc.hh"
405222Sksewell@umich.edu#include "base/trace.hh"        // for DPRINTF
415222Sksewell@umich.edu#include "sim/byteswap.hh"
425222Sksewell@umich.edu
435222Sksewell@umich.eduusing namespace std;
448229Snate@binkert.org
455222Sksewell@umich.eduObjectFile *
468229Snate@binkert.orgElfObject::tryFile(const string &fname, int fd, size_t len, uint8_t *data)
475222Sksewell@umich.edu{
488229Snate@binkert.org    Elf *elf;
495222Sksewell@umich.edu    GElf_Ehdr ehdr;
505222Sksewell@umich.edu    Arch arch = UnknownArch;
518229Snate@binkert.org    OpSys opSys = UnknownOpSys;
525222Sksewell@umich.edu
535222Sksewell@umich.edu    // check that header matches library version
545222Sksewell@umich.edu    if (elf_version(EV_CURRENT) == EV_NONE)
555222Sksewell@umich.edu        panic("wrong elf version number!");
565222Sksewell@umich.edu
575222Sksewell@umich.edu    // get a pointer to elf structure
585222Sksewell@umich.edu    elf = elf_memory((char*)data,len);
595222Sksewell@umich.edu    // will only fail if fd is invalid
605222Sksewell@umich.edu    assert(elf != NULL);
615222Sksewell@umich.edu
625222Sksewell@umich.edu    // Check that we actually have a elf file
635222Sksewell@umich.edu    if (gelf_getehdr(elf, &ehdr) ==0) {
645222Sksewell@umich.edu        DPRINTFR(Loader, "Not ELF\n");
655222Sksewell@umich.edu        elf_end(elf);
665222Sksewell@umich.edu        return NULL;
675222Sksewell@umich.edu    } else {
685222Sksewell@umich.edu        //Detect the architecture
695222Sksewell@umich.edu        //Since we don't know how to check for alpha right now, we'll
705222Sksewell@umich.edu        //just assume if it wasn't something else and it's 64 bit, that's
715222Sksewell@umich.edu        //what it must be.
725222Sksewell@umich.edu        if (ehdr.e_machine == EM_SPARC64 ||
735222Sksewell@umich.edu                (ehdr.e_machine == EM_SPARC &&
745222Sksewell@umich.edu                 ehdr.e_ident[EI_CLASS] == ELFCLASS64)||
755222Sksewell@umich.edu                ehdr.e_machine == EM_SPARCV9) {
765222Sksewell@umich.edu            arch = ObjectFile::SPARC64;
775222Sksewell@umich.edu        } else if (ehdr.e_machine == EM_SPARC32PLUS ||
785222Sksewell@umich.edu                        (ehdr.e_machine == EM_SPARC &&
795222Sksewell@umich.edu                         ehdr.e_ident[EI_CLASS] == ELFCLASS32)) {
805222Sksewell@umich.edu            arch = ObjectFile::SPARC32;
815222Sksewell@umich.edu        } else if (ehdr.e_machine == EM_MIPS
825222Sksewell@umich.edu                && ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
835222Sksewell@umich.edu            if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) {
845222Sksewell@umich.edu                arch = ObjectFile::Mips;
855222Sksewell@umich.edu            } else {
865222Sksewell@umich.edu                fatal("The binary you're trying to load is compiled for big "
875222Sksewell@umich.edu                        "endian MIPS. M5\nonly supports little endian MIPS. "
887064Snate@binkert.org                        "Please recompile your binary.\n");
895222Sksewell@umich.edu            }
905222Sksewell@umich.edu        } else if (ehdr.e_machine == EM_X86_64 &&
915222Sksewell@umich.edu                ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
925222Sksewell@umich.edu            arch = ObjectFile::X86_64;
935222Sksewell@umich.edu        } else if (ehdr.e_machine == EM_386 &&
945222Sksewell@umich.edu                ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
955222Sksewell@umich.edu            arch = ObjectFile::I386;
965222Sksewell@umich.edu        } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
975222Sksewell@umich.edu            arch = ObjectFile::Alpha;
985222Sksewell@umich.edu        } else if (ehdr.e_machine == EM_ARM) {
995222Sksewell@umich.edu            arch = ObjectFile::Arm;
1005222Sksewell@umich.edu        } else {
1015222Sksewell@umich.edu            warn("Unknown architecture: %d\n", ehdr.e_machine);
1025222Sksewell@umich.edu            arch = ObjectFile::UnknownArch;
1035222Sksewell@umich.edu        }
1045222Sksewell@umich.edu
1055222Sksewell@umich.edu        //Detect the operating system
1065222Sksewell@umich.edu        switch (ehdr.e_ident[EI_OSABI])
1075222Sksewell@umich.edu        {
1085222Sksewell@umich.edu
1095222Sksewell@umich.edu          case ELFOSABI_LINUX:
1105222Sksewell@umich.edu            opSys = ObjectFile::Linux;
1115222Sksewell@umich.edu            break;
1125222Sksewell@umich.edu          case ELFOSABI_SOLARIS:
1135222Sksewell@umich.edu            opSys = ObjectFile::Solaris;
1145222Sksewell@umich.edu            break;
1155222Sksewell@umich.edu          case ELFOSABI_TRU64:
1165222Sksewell@umich.edu            opSys = ObjectFile::Tru64;
1175222Sksewell@umich.edu            break;
1185222Sksewell@umich.edu          case ELFOSABI_ARM:
1195222Sksewell@umich.edu            opSys = ObjectFile::LinuxArmOABI;
1205222Sksewell@umich.edu            break;
1215222Sksewell@umich.edu          default:
1225222Sksewell@umich.edu            opSys = ObjectFile::UnknownOpSys;
1235222Sksewell@umich.edu        }
1245222Sksewell@umich.edu
1255222Sksewell@umich.edu        //take a look at the .note.ABI section
1265222Sksewell@umich.edu        //It can let us know what's what.
1275222Sksewell@umich.edu        if (opSys == ObjectFile::UnknownOpSys) {
1285222Sksewell@umich.edu            Elf_Scn *section;
1295222Sksewell@umich.edu            GElf_Shdr shdr;
1305222Sksewell@umich.edu            Elf_Data *data;
1315222Sksewell@umich.edu            uint32_t osAbi;;
1325222Sksewell@umich.edu            int secIdx = 1;
1335222Sksewell@umich.edu
1345222Sksewell@umich.edu            // Get the first section
1355222Sksewell@umich.edu            section = elf_getscn(elf, secIdx);
1365222Sksewell@umich.edu
1375222Sksewell@umich.edu            // While there are no more sections
1385222Sksewell@umich.edu            while (section != NULL && opSys == ObjectFile::UnknownOpSys) {
1395222Sksewell@umich.edu                gelf_getshdr(section, &shdr);
1405222Sksewell@umich.edu                if (shdr.sh_type == SHT_NOTE && !strcmp(".note.ABI-tag",
1415222Sksewell@umich.edu                            elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name))) {
1425222Sksewell@umich.edu                    // we have found a ABI note section
1435222Sksewell@umich.edu                    // Check the 5th 32bit word for OS  0 == linux, 1 == hurd,
1445222Sksewell@umich.edu                    // 2 == solaris, 3 == freebsd
1455222Sksewell@umich.edu                    data = elf_rawdata(section, NULL);
1465222Sksewell@umich.edu                    assert(data->d_buf);
1475222Sksewell@umich.edu                    if(ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
1485222Sksewell@umich.edu                        osAbi = htole(((uint32_t*)data->d_buf)[4]);
1495222Sksewell@umich.edu                    else
1505222Sksewell@umich.edu                        osAbi = htobe(((uint32_t*)data->d_buf)[4]);
1515222Sksewell@umich.edu
1525222Sksewell@umich.edu                    switch(osAbi) {
1535222Sksewell@umich.edu                      case 0:
1545222Sksewell@umich.edu                        opSys = ObjectFile::Linux;
1555222Sksewell@umich.edu                        break;
1565222Sksewell@umich.edu                      case 2:
1575222Sksewell@umich.edu                        opSys = ObjectFile::Solaris;
1585222Sksewell@umich.edu                        break;
1595222Sksewell@umich.edu                    }
1605222Sksewell@umich.edu                } // if section found
1615222Sksewell@umich.edu                if (!strcmp(".SUNW_version", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)))
1625222Sksewell@umich.edu                        opSys = ObjectFile::Solaris;
1635222Sksewell@umich.edu                if (!strcmp(".stab.index", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name)))
1645222Sksewell@umich.edu                        opSys = ObjectFile::Solaris;
1655222Sksewell@umich.edu
1665222Sksewell@umich.edu            section = elf_getscn(elf, ++secIdx);
1675222Sksewell@umich.edu            } // while sections
1685222Sksewell@umich.edu        }
1695222Sksewell@umich.edu
1705222Sksewell@umich.edu        ElfObject * result = new ElfObject(fname, fd, len, data, arch, opSys);
1715222Sksewell@umich.edu
1725222Sksewell@umich.edu        //The number of headers in the file
1735222Sksewell@umich.edu        result->_programHeaderCount = ehdr.e_phnum;
1745222Sksewell@umich.edu        //Record the size of each entry
1755222Sksewell@umich.edu        result->_programHeaderSize = ehdr.e_phentsize;
1765222Sksewell@umich.edu        if(result->_programHeaderCount) //If there is a program header table
1775222Sksewell@umich.edu        {
1785222Sksewell@umich.edu            //Figure out the virtual address of the header table in the
1795222Sksewell@umich.edu            //final memory image. We use the program headers themselves
1805222Sksewell@umich.edu            //to translate from a file offset to the address in the image.
1815222Sksewell@umich.edu            GElf_Phdr phdr;
1825222Sksewell@umich.edu            uint64_t e_phoff = ehdr.e_phoff;
1835222Sksewell@umich.edu            result->_programHeaderTable = 0;
1845222Sksewell@umich.edu            for(int hdrnum = 0; hdrnum < result->_programHeaderCount; hdrnum++)
1855222Sksewell@umich.edu            {
1865222Sksewell@umich.edu                gelf_getphdr(elf, hdrnum, &phdr);
1875222Sksewell@umich.edu                //Check if we've found the segment with the headers in it
1885222Sksewell@umich.edu                if(phdr.p_offset <= e_phoff &&
1895222Sksewell@umich.edu                        phdr.p_offset + phdr.p_filesz > e_phoff)
190                {
191                    result->_programHeaderTable = phdr.p_paddr + e_phoff;
192                    break;
193                }
194            }
195        }
196        else
197            result->_programHeaderTable = 0;
198
199
200        elf_end(elf);
201        return result;
202    }
203}
204
205
206ElfObject::ElfObject(const string &_filename, int _fd,
207                     size_t _len, uint8_t *_data,
208                     Arch _arch, OpSys _opSys)
209    : ObjectFile(_filename, _fd, _len, _data, _arch, _opSys)
210
211{
212    Elf *elf;
213    GElf_Ehdr ehdr;
214
215    // check that header matches library version
216    if (elf_version(EV_CURRENT) == EV_NONE)
217        panic("wrong elf version number!");
218
219    // get a pointer to elf structure
220    elf = elf_memory((char*)fileData,len);
221    // will only fail if fd is invalid
222    assert(elf != NULL);
223
224    // Check that we actually have a elf file
225    if (gelf_getehdr(elf, &ehdr) ==0) {
226        panic("Not ELF, shouldn't be here");
227    }
228
229    entry = ehdr.e_entry;
230
231    // initialize segment sizes to 0 in case they're not present
232    text.size = data.size = bss.size = 0;
233
234    int secIdx = 1;
235    Elf_Scn *section;
236    GElf_Shdr shdr;
237
238    // The first address of some important sections.
239    Addr textSecStart = 0;
240    Addr dataSecStart = 0;
241    Addr bssSecStart = 0;
242
243    // Get the first section
244    section = elf_getscn(elf, secIdx);
245
246    // Find the beginning of the most interesting sections.
247    while (section != NULL) {
248        gelf_getshdr(section, &shdr);
249        char * secName = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
250
251        if (!strcmp(".text", secName)) {
252            textSecStart = shdr.sh_addr;
253        } else if (!strcmp(".data", secName)) {
254            dataSecStart = shdr.sh_addr;
255        } else if (!strcmp(".bss", secName)) {
256            bssSecStart = shdr.sh_addr;
257        }
258
259        section = elf_getscn(elf, ++secIdx);
260    }
261
262    // Go through all the segments in the program, record them, and scrape
263    // out information about the text, data, and bss areas needed by other
264    // code.
265    for (int i = 0; i < ehdr.e_phnum; ++i) {
266        GElf_Phdr phdr;
267        if (gelf_getphdr(elf, i, &phdr) == 0) {
268            panic("gelf_getphdr failed for segment %d.", i);
269        }
270
271        // for now we don't care about non-loadable segments
272        if (!(phdr.p_type & PT_LOAD))
273            continue;
274
275        // Check to see if this segment contains the bss section.
276        if (phdr.p_paddr <= bssSecStart &&
277                phdr.p_paddr + phdr.p_memsz > bssSecStart &&
278                phdr.p_memsz - phdr.p_filesz > 0) {
279            bss.baseAddr = phdr.p_paddr + phdr.p_filesz;
280            bss.size = phdr.p_memsz - phdr.p_filesz;
281            bss.fileImage = NULL;
282        }
283
284        // Check to see if this is the text or data segment
285        if (phdr.p_vaddr <= textSecStart &&
286                phdr.p_vaddr + phdr.p_filesz > textSecStart) {
287            text.baseAddr = phdr.p_paddr;
288            text.size = phdr.p_filesz;
289            text.fileImage = fileData + phdr.p_offset;
290        } else if (phdr.p_vaddr <= dataSecStart &&
291                phdr.p_vaddr + phdr.p_filesz > dataSecStart) {
292            data.baseAddr = phdr.p_paddr;
293            data.size = phdr.p_filesz;
294            data.fileImage = fileData + phdr.p_offset;
295        } else {
296            // If it's none of the above but is loadable,
297            // load the filesize worth of data
298            Segment extra;
299            extra.baseAddr = phdr.p_paddr;
300            extra.size = phdr.p_filesz;
301            extra.fileImage = fileData + phdr.p_offset;
302            extraSegments.push_back(extra);
303        }
304    }
305
306    // should have found at least one loadable segment
307    assert(text.size != 0);
308
309    DPRINTFR(Loader, "text: 0x%x %d\ndata: 0x%x %d\nbss: 0x%x %d\n",
310             text.baseAddr, text.size, data.baseAddr, data.size,
311             bss.baseAddr, bss.size);
312
313    elf_end(elf);
314
315    // We will actually read the sections when we need to load them
316}
317
318
319bool
320ElfObject::loadSomeSymbols(SymbolTable *symtab, int binding)
321{
322    Elf *elf;
323    int sec_idx = 1; // there is a 0 but it is nothing, go figure
324    Elf_Scn *section;
325    GElf_Shdr shdr;
326    Elf_Data *data;
327    int count, ii;
328    bool found = false;
329    GElf_Sym sym;
330
331    if (!symtab)
332        return false;
333
334    // check that header matches library version
335    if (elf_version(EV_CURRENT) == EV_NONE)
336        panic("wrong elf version number!");
337
338    // get a pointer to elf structure
339    elf = elf_memory((char*)fileData,len);
340
341    assert(elf != NULL);
342
343    // Get the first section
344    section = elf_getscn(elf, sec_idx);
345
346    // While there are no more sections
347    while (section != NULL) {
348        gelf_getshdr(section, &shdr);
349
350        if (shdr.sh_type == SHT_SYMTAB) {
351            found = true;
352            data = elf_getdata(section, NULL);
353            count = shdr.sh_size / shdr.sh_entsize;
354            DPRINTF(Loader, "Found Symbol Table, %d symbols present\n", count);
355
356            // loop through all the symbols, only loading global ones
357            for (ii = 0; ii < count; ++ii) {
358                gelf_getsym(data, ii, &sym);
359                if (GELF_ST_BIND(sym.st_info) == binding) {
360                   symtab->insert(sym.st_value,
361                                  elf_strptr(elf, shdr.sh_link, sym.st_name));
362                }
363            }
364        }
365        ++sec_idx;
366        section = elf_getscn(elf, sec_idx);
367    }
368
369    elf_end(elf);
370
371    return found;
372}
373
374bool
375ElfObject::loadGlobalSymbols(SymbolTable *symtab, Addr addrMask)
376{
377    return loadSomeSymbols(symtab, STB_GLOBAL);
378}
379
380bool
381ElfObject::loadLocalSymbols(SymbolTable *symtab, Addr addrMask)
382{
383    return loadSomeSymbols(symtab, STB_LOCAL);
384}
385
386bool
387ElfObject::loadSections(Port *memPort, Addr addrMask)
388{
389    if (!ObjectFile::loadSections(memPort, addrMask))
390        return false;
391
392    vector<Segment>::iterator extraIt;
393    for (extraIt = extraSegments.begin();
394            extraIt != extraSegments.end(); extraIt++) {
395        if (!loadSection(&(*extraIt), memPort, addrMask)) {
396            return false;
397        }
398    }
399    return true;
400}
401
402void
403ElfObject::getSections()
404{
405    Elf *elf;
406    int sec_idx = 1; // there is a 0 but it is nothing, go figure
407    Elf_Scn *section;
408    GElf_Shdr shdr;
409
410    GElf_Ehdr ehdr;
411
412    assert(!sectionNames.size());
413
414    // check that header matches library version
415    if (elf_version(EV_CURRENT) == EV_NONE)
416        panic("wrong elf version number!");
417
418    // get a pointer to elf structure
419    elf = elf_memory((char*)fileData,len);
420    assert(elf != NULL);
421
422    // Check that we actually have a elf file
423    if (gelf_getehdr(elf, &ehdr) ==0) {
424        panic("Not ELF, shouldn't be here");
425    }
426
427    // Get the first section
428    section = elf_getscn(elf, sec_idx);
429
430    // While there are no more sections
431    while (section != NULL) {
432        gelf_getshdr(section, &shdr);
433        sectionNames.insert(elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name));
434        section = elf_getscn(elf, ++sec_idx);
435    } // while sections
436}
437
438bool
439ElfObject::sectionExists(string sec)
440{
441    if (!sectionNames.size())
442        getSections();
443    return sectionNames.find(sec) != sectionNames.end();
444}
445
446
447