blob: f58457d96452b07cc3403a3164fd48841d4d6c0e [file] [log] [blame] [edit]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* 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. */
#include <libelf64/parse.h>
#include <elf.h>
#include <fstream>
using namespace std;
namespace android {
namespace elf64 {
bool Elf64Parser::OpenElfFile(const std::string& fileName, std::ifstream& elfFile) {
elfFile.open(fileName.c_str(), std::ifstream::in);
return elfFile.is_open();
}
void Elf64Parser::CloseElfFile(std::ifstream& elfFile) {
if (!elfFile.is_open())
elfFile.close();
}
// Parse the executable header.
//
// Note: The command below can be used to print the executable header:
//
// $ readelf -h ../a.out
bool Elf64Parser::ParseExecutableHeader(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
// Move the cursor position to the very beginning.
elfFile.seekg(0);
elfFile.read((char*)&elf64Binary.ehdr, sizeof(elf64Binary.ehdr));
return elfFile.good();
}
bool Elf64Parser::IsElf64(Elf64Binary& elf64Binary) {
return elf64Binary.ehdr.e_ident[EI_CLASS] == ELFCLASS64;
}
// Parse the Program or Segment Headers.
//
// Note: The command below can be used to print the program headers:
//
// $ readelf --program-headers ./example_4k
// $ readelf -l ./example_4k
bool Elf64Parser::ParseProgramHeaders(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
uint64_t phOffset = elf64Binary.ehdr.e_phoff;
uint16_t phNum = elf64Binary.ehdr.e_phnum;
// Move the cursor position to the program header offset.
elfFile.seekg(phOffset);
for (int i = 0; i < phNum; i++) {
Elf64_Phdr phdr;
elfFile.read((char*)&phdr, sizeof(phdr));
if (!elfFile.good())
return false;
elf64Binary.phdrs.push_back(phdr);
}
return true;
}
bool Elf64Parser::ParseSections(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
Elf64_Sc sStrTblPtr;
// Parse sections after reading all the section headers.
for (int i = 0; i < elf64Binary.shdrs.size(); i++) {
Elf64_Shdr shdr = elf64Binary.shdrs[i];
uint64_t sOffset = shdr.sh_offset;
uint64_t sSize = shdr.sh_size;
Elf64_Sc section;
// Skip .bss section.
if (shdr.sh_type != SHT_NOBITS) {
section.data.resize(sSize);
// Move the cursor position to the section offset.
elfFile.seekg(sOffset);
elfFile.read(section.data.data(), sSize);
if (!elfFile.good())
return false;
}
section.size = sSize;
section.index = i;
// The index of the string table is in the executable header.
if (elf64Binary.ehdr.e_shstrndx == i) {
sStrTblPtr = section;
}
elf64Binary.sections.push_back(section);
}
// Set the data section name.
// This is done after reading the data section with index e_shstrndx.
for (int i = 0; i < elf64Binary.sections.size(); i++) {
Elf64_Sc section = elf64Binary.sections[i];
Elf64_Shdr shdr = elf64Binary.shdrs[i];
uint32_t nameIdx = shdr.sh_name;
char* st = sStrTblPtr.data.data();
section.name = &st[nameIdx];
}
return true;
}
// Parse the Section Headers.
//
// Note: The command below can be used to print the section headers:
//
// $ readelf --sections ./example_4k
// $ readelf -S ./example_4k
bool Elf64Parser::ParseSectionHeaders(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
uint64_t shOffset = elf64Binary.ehdr.e_shoff;
uint16_t shNum = elf64Binary.ehdr.e_shnum;
// Move the cursor position to the section headers offset.
elfFile.seekg(shOffset);
for (int i = 0; i < shNum; i++) {
Elf64_Shdr shdr;
elfFile.read((char*)&shdr, sizeof(shdr));
if (!elfFile.good())
return false;
elf64Binary.shdrs.push_back(shdr);
}
return true;
}
// Parse the elf file and populate the elfBinary object.
bool Elf64Parser::ParseElfFile(const std::string& fileName, Elf64Binary& elf64Binary) {
std::ifstream elfFile;
bool ret = false;
if (OpenElfFile(fileName, elfFile) &&
ParseExecutableHeader(elfFile, elf64Binary) &&
IsElf64(elf64Binary) &&
ParseProgramHeaders(elfFile, elf64Binary) &&
ParseSectionHeaders(elfFile, elf64Binary) &&
ParseSections(elfFile, elf64Binary)) {
elf64Binary.path = fileName;
ret = true;
}
CloseElfFile(elfFile);
return ret;
}
} // namespace elf64
} // namespace android