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