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