/* * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include "Image.h" #include #include #include #include #include #include #include #include #include using namespace BPrivate::Debug; // #pragma mark - Image Image::Image() { } Image::~Image() { } status_t Image::GetSymbol(const char* name, int32 symbolType, void** _symbolLocation, size_t* _symbolSize, int32* _symbolType) const { // TODO: At least for ImageFile we could do hash lookups! int32 iterator = 0; const char* foundName; size_t foundNameLen; addr_t foundAddress; size_t foundSize; int32 foundType; while (NextSymbol(iterator, &foundName, &foundNameLen, &foundAddress, &foundSize, &foundType) == B_OK) { if ((symbolType == B_SYMBOL_TYPE_ANY || symbolType == foundType) && strcmp(name, foundName) == 0) { if (_symbolLocation != NULL) *_symbolLocation = (void*)foundAddress; if (_symbolSize != NULL) *_symbolSize = foundSize; if (_symbolType != NULL) *_symbolType = foundType; return B_OK; } } return B_ENTRY_NOT_FOUND; } // #pragma mark - SymbolTableBasedImage SymbolTableBasedImage::SymbolTableBasedImage() : fLoadDelta(0), fSymbolTable(NULL), fStringTable(NULL), fSymbolCount(0), fStringTableSize(0) { } SymbolTableBasedImage::~SymbolTableBasedImage() { } const elf_sym* SymbolTableBasedImage::LookupSymbol(addr_t address, addr_t* _baseAddress, const char** _symbolName, size_t *_symbolNameLen, bool *_exactMatch) const { const elf_sym* symbolFound = NULL; const char* symbolName = NULL; bool exactMatch = false; addr_t deltaFound = ~(addr_t)0; for (int32 i = 0; i < fSymbolCount; i++) { const elf_sym* symbol = &fSymbolTable[i]; if (symbol->st_value == 0 || symbol->st_size >= (size_t)fInfo.text_size + fInfo.data_size) { continue; } addr_t symbolAddress = symbol->st_value + fLoadDelta; if (symbolAddress > address) continue; addr_t symbolDelta = address - symbolAddress; if (symbolDelta >= 0 && symbolDelta < symbol->st_size) exactMatch = true; if (exactMatch || symbolDelta < deltaFound) { deltaFound = symbolDelta; symbolFound = symbol; symbolName = fStringTable + symbol->st_name; if (exactMatch) break; } } if (symbolFound != NULL) { if (_baseAddress != NULL) *_baseAddress = symbolFound->st_value + fLoadDelta; if (_symbolName != NULL) *_symbolName = symbolName; if (_exactMatch != NULL) *_exactMatch = exactMatch; if (_symbolNameLen != NULL) *_symbolNameLen = _SymbolNameLen(symbolName); } return symbolFound; } status_t SymbolTableBasedImage::NextSymbol(int32& iterator, const char** _symbolName, size_t* _symbolNameLen, addr_t* _symbolAddress, size_t* _symbolSize, int32* _symbolType) const { while (true) { if (++iterator >= fSymbolCount) return B_ENTRY_NOT_FOUND; const elf_sym* symbol = &fSymbolTable[iterator]; if ((symbol->Type() != STT_FUNC && symbol->Type() != STT_OBJECT) || symbol->st_value == 0) { continue; } *_symbolName = fStringTable + symbol->st_name; *_symbolNameLen = _SymbolNameLen(*_symbolName); *_symbolAddress = symbol->st_value + fLoadDelta; *_symbolSize = symbol->st_size; *_symbolType = symbol->Type() == STT_FUNC ? B_SYMBOL_TYPE_TEXT : B_SYMBOL_TYPE_DATA; return B_OK; } } size_t SymbolTableBasedImage::_SymbolNameLen(const char* symbolName) const { if (symbolName == NULL || (addr_t)symbolName < (addr_t)fStringTable || (addr_t)symbolName >= (addr_t)fStringTable + fStringTableSize) { return 0; } return strnlen(symbolName, (addr_t)fStringTable + fStringTableSize - (addr_t)symbolName); } // #pragma mark - ImageFile ImageFile::ImageFile() : fFD(-1), fFileSize(0), fMappedFile((uint8*)MAP_FAILED) { } ImageFile::~ImageFile() { if (fMappedFile != MAP_FAILED) munmap(fMappedFile, fFileSize); if (fFD >= 0) close(fFD); } status_t ImageFile::Init(const image_info& info) { // just copy the image info fInfo = info; // load the file addr_t textAddress; size_t textSize; addr_t dataAddress; size_t dataSize; status_t error = _LoadFile(info.name, &textAddress, &textSize, &dataAddress, &dataSize); if (error != B_OK) return error; // compute the load delta fLoadDelta = (addr_t)fInfo.text - textAddress; return B_OK; } status_t ImageFile::Init(const char* path) { // load the file addr_t textAddress; size_t textSize; addr_t dataAddress; size_t dataSize; status_t error = _LoadFile(path, &textAddress, &textSize, &dataAddress, &dataSize); if (error != B_OK) return error; // init the image info fInfo.id = -1; fInfo.type = B_LIBRARY_IMAGE; fInfo.sequence = 0; fInfo.init_order = 0; fInfo.init_routine = 0; fInfo.term_routine = 0; fInfo.device = -1; fInfo.node = -1; strlcpy(fInfo.name, path, sizeof(fInfo.name)); fInfo.text = (void*)textAddress; fInfo.data = (void*)dataAddress; fInfo.text_size = textSize; fInfo.data_size = dataSize; // the image isn't loaded, so no delta fLoadDelta = 0; return B_OK; } status_t ImageFile::_LoadFile(const char* path, addr_t* _textAddress, size_t* _textSize, addr_t* _dataAddress, size_t* _dataSize) { // open and stat() the file fFD = open(path, O_RDONLY); if (fFD < 0) return errno; struct stat st; if (fstat(fFD, &st) < 0) return errno; fFileSize = st.st_size; if (fFileSize < (off_t)sizeof(elf_ehdr)) return B_NOT_AN_EXECUTABLE; // map it fMappedFile = (uint8*)mmap(NULL, fFileSize, PROT_READ, MAP_PRIVATE, fFD, 0); if (fMappedFile == MAP_FAILED) return errno; // examine the elf header elf_ehdr* elfHeader = (elf_ehdr*)fMappedFile; if (memcmp(elfHeader->e_ident, ELFMAG, 4) != 0) return B_NOT_AN_EXECUTABLE; if (elfHeader->e_ident[4] != ELF_CLASS) return B_NOT_AN_EXECUTABLE; // verify the location of the program headers int32 programHeaderCount = elfHeader->e_phnum; if (elfHeader->e_phoff < sizeof(elf_ehdr) || elfHeader->e_phentsize < sizeof(elf_phdr) || (off_t)(elfHeader->e_phoff + programHeaderCount * elfHeader->e_phentsize) > fFileSize) { return B_NOT_AN_EXECUTABLE; } elf_phdr* programHeaders = (elf_phdr*)(fMappedFile + elfHeader->e_phoff); // verify the location of the section headers int32 sectionCount = elfHeader->e_shnum; if (elfHeader->e_shoff < sizeof(elf_ehdr) || elfHeader->e_shentsize < sizeof(elf_shdr) || (off_t)(elfHeader->e_shoff + sectionCount * elfHeader->e_shentsize) > fFileSize) { return B_NOT_AN_EXECUTABLE; } // find the text and data segment -- we need load address and size *_textAddress = 0; *_textSize = 0; *_dataAddress = 0; *_dataSize = 0; for (int32 i = 0; i < programHeaderCount; i++) { elf_phdr* header = (elf_phdr*) ((uint8*)programHeaders + i * elfHeader->e_phentsize); if (header->p_type == PT_LOAD) { if ((header->p_flags & PF_WRITE) == 0) { *_textAddress = header->p_vaddr; *_textSize = header->p_memsz; } else { *_dataAddress = header->p_vaddr; *_dataSize = header->p_memsz; break; } } } status_t error = _FindTableInSection(elfHeader, SHT_SYMTAB); if (error != B_OK) error = _FindTableInSection(elfHeader, SHT_DYNSYM); return error; } status_t ImageFile::_FindTableInSection(elf_ehdr* elfHeader, uint16 sectionType) { elf_shdr* sectionHeaders = (elf_shdr*)(fMappedFile + elfHeader->e_shoff); // find the symbol table for (int32 i = 0; i < elfHeader->e_shnum; i++) { elf_shdr* sectionHeader = (elf_shdr*) ((uint8*)sectionHeaders + i * elfHeader->e_shentsize); if (sectionHeader->sh_type == sectionType) { elf_shdr& stringHeader = *(elf_shdr*) ((uint8*)sectionHeaders + sectionHeader->sh_link * elfHeader->e_shentsize); if (stringHeader.sh_type != SHT_STRTAB) return B_BAD_DATA; if ((off_t)(sectionHeader->sh_offset + sectionHeader->sh_size) > fFileSize || (off_t)(stringHeader.sh_offset + stringHeader.sh_size) > fFileSize) { return B_BAD_DATA; } fSymbolTable = (elf_sym*)(fMappedFile + sectionHeader->sh_offset); fStringTable = (char*)(fMappedFile + stringHeader.sh_offset); fSymbolCount = sectionHeader->sh_size / sizeof(elf_sym); fStringTableSize = stringHeader.sh_size; return B_OK; } } return B_BAD_DATA; } // #pragma mark - KernelImage KernelImage::KernelImage() { } KernelImage::~KernelImage() { delete[] fSymbolTable; delete[] fStringTable; } status_t KernelImage::Init(const image_info& info) { fInfo = info; // get the table sizes fSymbolCount = 0; fStringTableSize = 0; status_t error = _kern_read_kernel_image_symbols(fInfo.id, NULL, &fSymbolCount, NULL, &fStringTableSize, NULL); if (error != B_OK) return error; // allocate the tables fSymbolTable = new(std::nothrow) elf_sym[fSymbolCount]; fStringTable = new(std::nothrow) char[fStringTableSize]; if (fSymbolTable == NULL || fStringTable == NULL) return B_NO_MEMORY; // get the info return _kern_read_kernel_image_symbols(fInfo.id, fSymbolTable, &fSymbolCount, fStringTable, &fStringTableSize, &fLoadDelta); } CommPageImage::CommPageImage() { } CommPageImage::~CommPageImage() { delete[] fSymbolTable; delete[] fStringTable; } status_t CommPageImage::Init(const image_info& info) { // find kernel image for commpage image_id commPageID = -1; image_info commPageInfo; int32 cookie = 0; while (_kern_get_next_image_info(B_SYSTEM_TEAM, &cookie, &commPageInfo, sizeof(image_info)) == B_OK) { if (!strcmp("commpage", commPageInfo.name)) { commPageID = commPageInfo.id; break; } } if (commPageID < 0) return B_ENTRY_NOT_FOUND; fInfo = commPageInfo; fInfo.text = info.text; // get the table sizes fSymbolCount = 0; fStringTableSize = 0; status_t error = _kern_read_kernel_image_symbols(commPageID, NULL, &fSymbolCount, NULL, &fStringTableSize, NULL); if (error != B_OK) return error; // allocate the tables fSymbolTable = new(std::nothrow) elf_sym[fSymbolCount]; fStringTable = new(std::nothrow) char[fStringTableSize]; if (fSymbolTable == NULL || fStringTable == NULL) return B_NO_MEMORY; // get the info error = _kern_read_kernel_image_symbols(commPageID, fSymbolTable, &fSymbolCount, fStringTable, &fStringTableSize, NULL); if (error != B_OK) { delete[] fSymbolTable; delete[] fStringTable; return error; } fLoadDelta = (addr_t)info.text; return B_OK; }