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 #include <iostream>
22
23 namespace android {
24 namespace elf64 {
25
Elf64Parser(const std::string & fileName,Elf64Binary & elfBinary)26 Elf64Parser::Elf64Parser(const std::string& fileName, Elf64Binary& elfBinary)
27 : elf64stream(fileName) {
28 if (!elf64stream) {
29 std::cerr << "Failed to open the file: " << fileName << std::endl;
30 }
31
32 elfBinaryPtr = &elfBinary;
33 }
34
35 // Parse the executable header.
36 //
37 // Note: The command below can be used to print the executable header:
38 //
39 // $ readelf -h ../shared_lib.so
ParseExecutableHeader()40 bool Elf64Parser::ParseExecutableHeader() {
41 // Move the cursor position to the very beginning.
42 elf64stream.seekg(0);
43 elf64stream.read((char*)&elfBinaryPtr->ehdr, sizeof(elfBinaryPtr->ehdr));
44
45 return elf64stream.good();
46 }
47
48 // Parse the Program or Segment Headers.
49 //
50 // Note: The command below can be used to print the program headers:
51 //
52 // $ readelf --program-headers ./shared_lib.so
53 // $ readelf -l ./shared_lib.so
ParseProgramHeaders()54 bool Elf64Parser::ParseProgramHeaders() {
55 uint64_t phOffset = elfBinaryPtr->ehdr.e_phoff;
56 uint16_t phNum = elfBinaryPtr->ehdr.e_phnum;
57
58 // Move the cursor position to the program header offset.
59 elf64stream.seekg(phOffset);
60
61 for (int i = 0; i < phNum; i++) {
62 Elf64_Phdr phdr;
63
64 elf64stream.read((char*)&phdr, sizeof(phdr));
65 if (!elf64stream) return false;
66
67 elfBinaryPtr->phdrs.push_back(phdr);
68 }
69
70 return true;
71 }
72
ParseSections()73 bool Elf64Parser::ParseSections() {
74 Elf64_Sc sStrTblPtr;
75
76 // Parse sections after reading all the section headers.
77 for (int i = 0; i < elfBinaryPtr->shdrs.size(); i++) {
78 uint64_t sOffset = elfBinaryPtr->shdrs[i].sh_offset;
79 uint64_t sSize = elfBinaryPtr->shdrs[i].sh_size;
80
81 Elf64_Sc section;
82
83 // Skip .bss section.
84 if (elfBinaryPtr->shdrs[i].sh_type != SHT_NOBITS) {
85 section.data.resize(sSize);
86
87 // Move the cursor position to the section offset.
88 elf64stream.seekg(sOffset);
89 elf64stream.read(section.data.data(), sSize);
90 if (!elf64stream) return false;
91 }
92
93 section.size = sSize;
94 section.index = i;
95
96 // The index of the string table is in the executable header.
97 if (elfBinaryPtr->ehdr.e_shstrndx == i) {
98 sStrTblPtr = section;
99 }
100
101 elfBinaryPtr->sections.push_back(section);
102 }
103
104 // Set the data section name.
105 // This is done after reading the data section with index e_shstrndx.
106 for (int i = 0; i < elfBinaryPtr->sections.size(); i++) {
107 uint32_t nameIdx = elfBinaryPtr->shdrs[i].sh_name;
108 char* st = sStrTblPtr.data.data();
109
110 if (nameIdx < sStrTblPtr.size) {
111 CHECK_NE(nullptr, memchr(&st[nameIdx], 0, sStrTblPtr.size - nameIdx));
112 elfBinaryPtr->sections[i].name = &st[nameIdx];
113 }
114 }
115
116 return true;
117 }
118
119 // Parse the Section Headers.
120 //
121 // Note: The command below can be used to print the section headers:
122 //
123 // $ readelf --sections ./shared_lib.so
124 // $ readelf -S ./shared_lib.so
ParseSectionHeaders()125 bool Elf64Parser::ParseSectionHeaders() {
126 uint64_t shOffset = elfBinaryPtr->ehdr.e_shoff;
127 uint16_t shNum = elfBinaryPtr->ehdr.e_shnum;
128
129 // Move the cursor position to the section headers offset.
130 elf64stream.seekg(shOffset);
131
132 for (int i = 0; i < shNum; i++) {
133 Elf64_Shdr shdr;
134
135 elf64stream.read((char*)&shdr, sizeof(shdr));
136 if (!elf64stream) return false;
137
138 elfBinaryPtr->shdrs.push_back(shdr);
139 }
140
141 return true;
142 }
143
144 // Parse the elf file and populate the elfBinary object.
ParseElfFile(const std::string & fileName,Elf64Binary & elf64Binary)145 bool Elf64Parser::ParseElfFile(const std::string& fileName, Elf64Binary& elf64Binary) {
146 Elf64Parser elf64Parser(fileName, elf64Binary);
147 if (elf64Parser.elf64stream && elf64Parser.ParseExecutableHeader() && elf64Binary.IsElf64() &&
148 elf64Parser.ParseProgramHeaders() && elf64Parser.ParseSectionHeaders() &&
149 elf64Parser.ParseSections()) {
150 elf64Binary.path = fileName;
151 return true;
152 }
153
154 return false;
155 }
156
IsElf64(const std::string & fileName)157 bool Elf64Parser::IsElf64(const std::string& fileName) {
158 Elf64Binary elf64Binary;
159
160 Elf64Parser elf64Parser(fileName, elf64Binary);
161 if (elf64Parser.elf64stream && elf64Parser.ParseExecutableHeader() && elf64Binary.IsElf64()) {
162 return true;
163 }
164
165 return false;
166 }
167
168 } // namespace elf64
169 } // namespace android
170
171