1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License. */
15 
16 #include <libelf64/parse.h>
17 
18 #include <elf.h>
19 
20 #include <fstream>
21 
22 using namespace std;
23 
24 namespace android {
25 namespace elf64 {
26 
OpenElfFile(const std::string & fileName,std::ifstream & elfFile)27 bool Elf64Parser::OpenElfFile(const std::string& fileName, std::ifstream& elfFile) {
28     elfFile.open(fileName.c_str(), std::ifstream::in);
29 
30     return elfFile.is_open();
31 }
32 
CloseElfFile(std::ifstream & elfFile)33 void Elf64Parser::CloseElfFile(std::ifstream& elfFile) {
34     if (!elfFile.is_open())
35         elfFile.close();
36 }
37 
38 // Parse the executable header.
39 //
40 // Note: The command below can be used to print the executable header:
41 //
42 //  $ readelf -h ../a.out
ParseExecutableHeader(std::ifstream & elfFile,Elf64Binary & elf64Binary)43 bool Elf64Parser::ParseExecutableHeader(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
44     // Move the cursor position to the very beginning.
45     elfFile.seekg(0);
46     elfFile.read((char*)&elf64Binary.ehdr, sizeof(elf64Binary.ehdr));
47 
48     return elfFile.good();
49 }
50 
IsElf64(Elf64Binary & elf64Binary)51 bool Elf64Parser::IsElf64(Elf64Binary& elf64Binary) {
52     return elf64Binary.ehdr.e_ident[EI_CLASS] == ELFCLASS64;
53 }
54 
55 // Parse the Program or Segment Headers.
56 //
57 // Note: The command below can be used to print the program headers:
58 //
59 //  $ readelf --program-headers ./example_4k
60 //  $ readelf -l ./example_4k
ParseProgramHeaders(std::ifstream & elfFile,Elf64Binary & elf64Binary)61 bool Elf64Parser::ParseProgramHeaders(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
62     uint64_t phOffset = elf64Binary.ehdr.e_phoff;
63     uint16_t phNum = elf64Binary.ehdr.e_phnum;
64 
65     // Move the cursor position to the program header offset.
66     elfFile.seekg(phOffset);
67 
68     for (int i = 0; i < phNum; i++) {
69         Elf64_Phdr phdr;
70 
71         elfFile.read((char*)&phdr, sizeof(phdr));
72         if (!elfFile.good())
73             return false;
74 
75         elf64Binary.phdrs.push_back(phdr);
76     }
77 
78     return true;
79 }
80 
ParseSections(std::ifstream & elfFile,Elf64Binary & elf64Binary)81 bool Elf64Parser::ParseSections(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
82     Elf64_Sc sStrTblPtr;
83 
84     // Parse sections after reading all the section headers.
85     for (int i = 0; i < elf64Binary.shdrs.size(); i++) {
86         Elf64_Shdr shdr = elf64Binary.shdrs[i];
87         uint64_t sOffset = shdr.sh_offset;
88         uint64_t sSize = shdr.sh_size;
89 
90         Elf64_Sc section;
91 
92         // Skip .bss section.
93         if (shdr.sh_type != SHT_NOBITS) {
94             section.data.resize(sSize);
95 
96             // Move the cursor position to the section offset.
97             elfFile.seekg(sOffset);
98             elfFile.read(section.data.data(), sSize);
99             if (!elfFile.good())
100                 return false;
101         }
102 
103         section.size = sSize;
104         section.index = i;
105 
106         // The index of the string table is in the executable header.
107         if (elf64Binary.ehdr.e_shstrndx == i) {
108             sStrTblPtr = section;
109         }
110 
111         elf64Binary.sections.push_back(section);
112     }
113 
114     // Set the data section name.
115     // This is done after reading the data section with index e_shstrndx.
116     for (int i = 0; i < elf64Binary.sections.size(); i++) {
117         Elf64_Sc section = elf64Binary.sections[i];
118         Elf64_Shdr shdr = elf64Binary.shdrs[i];
119         uint32_t nameIdx = shdr.sh_name;
120         char* st = sStrTblPtr.data.data();
121 
122         section.name = &st[nameIdx];
123     }
124 
125     return true;
126 }
127 
128 // Parse the Section Headers.
129 //
130 // Note: The command below can be used to print the section headers:
131 //
132 //   $ readelf --sections ./example_4k
133 //   $ readelf -S ./example_4k
ParseSectionHeaders(std::ifstream & elfFile,Elf64Binary & elf64Binary)134 bool Elf64Parser::ParseSectionHeaders(std::ifstream& elfFile, Elf64Binary& elf64Binary) {
135     uint64_t shOffset = elf64Binary.ehdr.e_shoff;
136     uint16_t shNum = elf64Binary.ehdr.e_shnum;
137 
138     // Move the cursor position to the section headers offset.
139     elfFile.seekg(shOffset);
140 
141     for (int i = 0; i < shNum; i++) {
142         Elf64_Shdr shdr;
143 
144         elfFile.read((char*)&shdr, sizeof(shdr));
145         if (!elfFile.good())
146             return false;
147 
148         elf64Binary.shdrs.push_back(shdr);
149     }
150 
151     return true;
152 }
153 
154 // Parse the elf file and populate the elfBinary object.
ParseElfFile(const std::string & fileName,Elf64Binary & elf64Binary)155 bool Elf64Parser::ParseElfFile(const std::string& fileName, Elf64Binary& elf64Binary) {
156     std::ifstream elfFile;
157     bool ret = false;
158 
159     if (OpenElfFile(fileName, elfFile) &&
160         ParseExecutableHeader(elfFile, elf64Binary) &&
161         IsElf64(elf64Binary) &&
162         ParseProgramHeaders(elfFile, elf64Binary) &&
163         ParseSectionHeaders(elfFile, elf64Binary) &&
164         ParseSections(elfFile, elf64Binary)) {
165         elf64Binary.path = fileName;
166         ret = true;
167     }
168 
169     CloseElfFile(elfFile);
170 
171     return ret;
172 }
173 
174 }  // namespace elf64
175 }  // namespace android
176 
177