1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <elf_parser.h>
17 #include "utilities.h"
18
19 using namespace OHOS::Developtools::HiPerf::ELF;
20 namespace OHOS {
21 namespace Developtools {
22 namespace HiPerf {
ElfFile(const std::string & filename)23 ElfFile::ElfFile(const std::string &filename)
24 {
25 #if is_mingw
26 std::string resolvedPath = CanonicalizeSpecPath(filename.c_str());
27 fd_ = open(resolvedPath.c_str(), O_RDONLY | O_BINARY);
28 #else
29 std::string resolvedPath = CanonicalizeSpecPath(filename.c_str());
30 fd_ = open(resolvedPath.c_str(), O_RDONLY);
31 #endif
32 if (fd_ != -1) {
33 struct stat sb;
34 if (fstat(fd_, &sb) == -1) {
35 HLOGE("unable to check the file size");
36 } else {
37 HLOGD("file stat size %" PRIu64 "", sb.st_size);
38 mmap_ = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd_, 0);
39 if (mmap_ == MMAP_FAILED) {
40 HLOGE("unable to map the file size %" PRIu64 " ", sb.st_size);
41 mmapSize_ = 0;
42 } else {
43 mmapSize_ = sb.st_size;
44 HLOGD("mmap build with size %" PRIu64 " ", mmapSize_);
45 }
46 }
47 }
48 }
49
~ElfFile()50 ElfFile::~ElfFile()
51 {
52 if (mmap_ != MMAP_FAILED) {
53 munmap(mmap_, mmapSize_);
54 }
55
56 if (fd_ != -1) {
57 close(fd_);
58 fd_ = -1;
59 }
60 }
61
MakeUnique(const std::string & filename)62 std::unique_ptr<ElfFile> ElfFile::MakeUnique(const std::string &filename)
63 {
64 std::unique_ptr<ElfFile> file {new (std::nothrow) ElfFile(filename)};
65 if (file == nullptr) {
66 HLOGE("Error in ElfFile::MakeUnique(): ElfFile::ElfFile() failed");
67 return nullptr;
68 }
69 if (!file->IsOpened()) {
70 HLOGE("Error in ElfFile::MakeUnique(): elf file not opended");
71 return nullptr;
72 }
73 if (!file->ParseFile()) {
74 HLOGE("parse elf file failed");
75 return nullptr;
76 }
77 return file;
78 }
79
ParseFile()80 bool ElfFile::ParseFile()
81 {
82 if (!ParseElfHeader()) {
83 HLOGE("Error in ElfFile::MakeUnique(): ElfFile::ParseElfHeader() failed");
84 return false;
85 }
86 if (!ParsePrgHeaders()) {
87 HLOGE("Error in ElfFile::MakeUnique(): ElfFile::ParsePrgHeaders() failed");
88 return false;
89 }
90 if (!ParseSecNamesStr()) {
91 HLOGE("Error in ElfFile::MakeUnique(): ElfFile::ParseSecNamesStr() failed");
92 return false;
93 }
94 if (!ParseSecHeaders()) {
95 HLOGE("Error in ElfFile::MakeUnique(): ElfFile::ParseSecHeaders() failed");
96 return false;
97 }
98 return true;
99 }
100
ParseElfHeader()101 bool ElfFile::ParseElfHeader()
102 {
103 ssize_t ret = lseek(fd_, 0, SEEK_SET);
104 if (ret != 0) {
105 HLOGW("lseek ret %zu", ret);
106 return false;
107 }
108 HLOG_ASSERT(ret == 0);
109 unsigned char ehdrBuf[ehdr64Size] {0};
110 size_t readsize = ReadFile(ehdrBuf, ehdr64Size);
111 if (readsize < ehdr64Size) {
112 HLOGW("file size not enough, try read %zu, only have %zu", ehdr64Size, readsize);
113 return false;
114 }
115 HLOG_ASSERT(readsize > 0);
116 ehdr_ = ElfHeader::MakeUnique(ehdrBuf, readsize);
117 return !(ehdr_ == nullptr);
118 }
119
ParsePrgHeaders()120 bool ElfFile::ParsePrgHeaders()
121 {
122 size_t phdrSize = ehdr_->phdrEntSize_;
123 size_t numPhdrs = ehdr_->phdrNumEnts_;
124 uint64_t phdrOffset = ehdr_->phdrOffset_;
125 int64_t ret = lseek(fd_, phdrOffset, SEEK_SET);
126 HLOG_ASSERT(ret == static_cast<int64_t>(phdrOffset));
127 char *phdrsBuf = new (std::nothrow) char[phdrSize * numPhdrs];
128 if (phdrsBuf == nullptr) {
129 HLOGE("Error in ELF::ElfFile::ParsePrgHeaders(): new failed");
130 return false;
131 }
132 (void)memset_s(phdrsBuf, phdrSize * numPhdrs, 0, phdrSize * numPhdrs);
133 ret = ReadFile(phdrsBuf, phdrSize * numPhdrs);
134 if (ret != static_cast<int64_t>(phdrSize * numPhdrs)) {
135 delete[] phdrsBuf;
136 phdrsBuf = nullptr;
137 return false;
138 }
139 char *phdrBuf = phdrsBuf;
140 for (size_t count = 0; count < numPhdrs; ++count) {
141 std::unique_ptr<ProgramHeader> phdr = ProgramHeader::MakeUnique(phdrBuf, phdrSize);
142 if (phdr == nullptr) {
143 delete[] phdrsBuf;
144 phdrsBuf = nullptr;
145 HLOGE("Error in Elf::ParsePrgHeaders(): ProgramHeader::MakeUnique() failed");
146 return false;
147 }
148 phdrs_.push_back(std::move(phdr));
149 phdrBuf += phdrSize;
150 }
151 delete[] phdrsBuf;
152 phdrsBuf = nullptr;
153 return true;
154 }
155
ParseSecNamesStr()156 bool ElfFile::ParseSecNamesStr()
157 {
158 // get string table section header
159 size_t shdrSize = ehdr_->shdrEntSize_;
160 size_t shdrIndex = ehdr_->shdrStrTabIdx_;
161 uint64_t shdrOffset = ehdr_->shdrOffset_ + ((uint64_t)shdrIndex) * shdrSize;
162 int64_t ret = lseek(fd_, shdrOffset, SEEK_SET);
163 HLOG_ASSERT(ret == static_cast<int64_t>(shdrOffset));
164 char *shdrBuf = new (std::nothrow) char[shdrSize];
165 if (shdrBuf == nullptr) {
166 HLOGE("Error in ElfFile::ParseSecNamesStr(): new failed");
167 return false;
168 }
169 (void)memset_s(shdrBuf, shdrSize, 0, shdrSize);
170 ret = ReadFile(shdrBuf, shdrSize);
171 HLOG_ASSERT(ret == static_cast<int64_t>(shdrSize));
172 const std::string secName {".shstrtab"};
173 shdrs_[secName] = SectionHeader::MakeUnique(shdrBuf, shdrSize, shdrIndex);
174 if (shdrs_[secName] == nullptr) {
175 HLOGE("Error in ElfFile::ParseSecNamesStr(): SectionHeader::MakeUnique() failed");
176 delete[] shdrBuf;
177 shdrBuf = nullptr;
178 return false;
179 }
180 delete[] shdrBuf;
181 shdrBuf = nullptr;
182 // get content of string section table
183 uint64_t secOffset = shdrs_[secName]->fileOffset_;
184 uint64_t secSize = shdrs_[secName]->secSize_;
185 if (secSize > mmapSize_ || mmapSize_ == 0) {
186 HLOGE("secSize is too large secSize: %" PRIu64 " mmapSize_: %" PRIu64 "", secSize, mmapSize_);
187 return false;
188 }
189 ret = lseek(fd_, secOffset, SEEK_SET);
190 HLOG_ASSERT(ret == static_cast<int64_t>(secOffset));
191 char *secNamesBuf = new (std::nothrow) char[secSize];
192 if (secNamesBuf == nullptr) {
193 HLOGE("Error in ElfFile::ParseSecNamesStr(): new secNamesBuf failed");
194 return false;
195 }
196 (void)memset_s(secNamesBuf, secSize, '\0', secSize);
197 ret = ReadFile(secNamesBuf, secSize);
198 if (ret != static_cast<int64_t>(secSize)) {
199 delete[] secNamesBuf;
200 secNamesBuf = nullptr;
201 return false;
202 }
203 secNamesStr_ = std::string(secNamesBuf, secNamesBuf + secSize);
204 delete[] secNamesBuf;
205 secNamesBuf = nullptr;
206 return true;
207 }
208
ParseSecHeaders()209 bool ElfFile::ParseSecHeaders()
210 {
211 size_t shdrSize = ehdr_->shdrEntSize_;
212 size_t numShdrs = ehdr_->shdrNumEnts_;
213 uint64_t shdrOffset = ehdr_->shdrOffset_;
214 int64_t ret = lseek(fd_, shdrOffset, SEEK_SET);
215 HLOG_ASSERT(ret == static_cast<int64_t>(shdrOffset));
216 char *shdrsBuf = new (std::nothrow) char[shdrSize * numShdrs];
217 if (shdrsBuf == nullptr) {
218 HLOGE("Error in ELF::ElfFile::ParseSecHeaders(): new failed");
219 return false;
220 }
221 (void)memset_s(shdrsBuf, shdrSize * numShdrs, '\0', shdrSize * numShdrs);
222 ret = ReadFile(shdrsBuf, shdrSize * numShdrs);
223 HLOG_ASSERT(ret == static_cast<int64_t>(shdrSize * numShdrs));
224 char *shdrBuf = shdrsBuf;
225 for (size_t count = 0; count < numShdrs; ++count) {
226 if (count == ehdr_->shdrStrTabIdx_) {
227 shdrBuf += shdrSize;
228 continue;
229 }
230 std::unique_ptr<SectionHeader> shdr = SectionHeader::MakeUnique(shdrBuf, shdrSize, count);
231 if (shdr == nullptr) {
232 delete[] shdrsBuf;
233 shdrsBuf = nullptr;
234 return false;
235 }
236 std::string secName = GetSectionName(shdr->nameIndex_);
237 shdrs_[secName] = std::move(shdr);
238 shdr.reset(nullptr);
239 shdrBuf += shdrSize;
240 }
241 delete[] shdrsBuf;
242 shdrsBuf = nullptr;
243 return true;
244 }
245
ParseSymTable(const std::string secName)246 bool ElfFile::ParseSymTable(const std::string secName)
247 {
248 if (shdrs_.find(secName) == shdrs_.end()) {
249 HLOGE("Error in ELF::ElfFile::ParseSymTable(): section %s does not exist", secName.c_str());
250 return false;
251 } else {
252 return ParseSymTable(shdrs_[secName].get());
253 }
254 }
255
ParseSymTable(const SectionHeader * shdr)256 bool ElfFile::ParseSymTable(const SectionHeader *shdr)
257 {
258 if (shdr == nullptr) {
259 return false;
260 }
261 uint64_t secOffset = shdr->fileOffset_;
262 int64_t ret = lseek(fd_, secOffset, SEEK_SET);
263 HLOG_ASSERT(ret == static_cast<int64_t>(secOffset));
264 uint64_t secSize = shdr->secSize_;
265 uint64_t entrySize = shdr->secEntrySize_;
266 if (entrySize > mmapSize_ || secSize > mmapSize_ || mmapSize_ == 0) {
267 HLOGE("entrySize or secSize is too large secSize: %" PRIu64 " entrySize: %" PRIu64 " mmapSize_: %" PRIu64 "",
268 secSize, entrySize, mmapSize_);
269 return false;
270 }
271 char *secBuf = new (std::nothrow) char[secSize];
272 if (secBuf == nullptr) {
273 HLOGE("Error in EFL::ElfFile::ParseSymTable(): new failed");
274 return false;
275 }
276 ret = ReadFile(secBuf, secSize);
277 HLOG_ASSERT(ret == static_cast<int64_t>(secSize));
278 symTable_ = SymbolTable::MakeUnique(symNamesStr_, secBuf, secSize, entrySize);
279 if (symTable_ == nullptr) {
280 delete[] secBuf;
281 secBuf = nullptr;
282 return false;
283 }
284 delete[] secBuf;
285 secBuf = nullptr;
286 return true;
287 }
288
ParseSymNamesStr()289 bool ElfFile::ParseSymNamesStr()
290 {
291 const std::string secName {".strtab"};
292 if (shdrs_.find(secName) == shdrs_.end()) {
293 HLOGE("Error in ElfFile::ParseSymNamesStr(): section %s does not exist", secName.c_str());
294 return false;
295 }
296 const auto &shdr = shdrs_[secName];
297 uint64_t secOffset = shdr->fileOffset_;
298 uint64_t secSize = shdr->secSize_;
299 if (secSize > mmapSize_ || mmapSize_ == 0) {
300 HLOGE("secSize is too large secSize: %" PRIu64 " mmapSize_: %" PRIu64 "", secSize, mmapSize_);
301 return false;
302 }
303 int64_t ret = lseek(fd_, secOffset, SEEK_SET);
304 HLOG_ASSERT(ret >= 0);
305 char *secBuf = new (std::nothrow) char[secSize];
306 if (secBuf == nullptr) {
307 HLOGE("Error in ElfFile::ParsesymNamesStr(): new failed");
308 return false;
309 }
310 (void)memset_s(secBuf, secSize, '\0', secSize);
311 ret = ReadFile(secBuf, secSize);
312 HLOG_ASSERT(ret == static_cast<int64_t>(secSize));
313 symNamesStr_ = std::string(secBuf, secSize);
314 if (symNamesStr_ == "") {
315 delete[] secBuf;
316 secBuf = nullptr;
317 return false;
318 }
319 delete[] secBuf;
320 secBuf = nullptr;
321 return true;
322 }
323
ParseDynSymTable()324 bool ElfFile::ParseDynSymTable()
325 {
326 const std::string secName {".dynsym"};
327 if (shdrs_.find(secName) == shdrs_.end()) {
328 HLOGE("Error in ELF::ElfFile::ParseSymTable(): section %s does not exist", secName.c_str());
329 return false;
330 }
331 const auto &shdr = shdrs_[secName];
332 uint64_t secOffset = shdr->fileOffset_;
333 int64_t ret = lseek(fd_, secOffset, SEEK_SET);
334 HLOG_ASSERT(ret == static_cast<int64_t>(secOffset));
335 uint64_t secSize = shdr->secSize_;
336 uint64_t entrySize = shdr->secEntrySize_;
337 if (entrySize > mmapSize_ || secSize > mmapSize_ || mmapSize_ == 0) {
338 HLOGE("entrySize or secSize is too large secSize: %" PRIu64 " entrySize: %" PRIu64 " mmapSize_: %" PRIu64 "",
339 secSize, entrySize, mmapSize_);
340 return false;
341 }
342 char *secBuf = new (std::nothrow) char[secSize];
343 if (secBuf == nullptr) {
344 HLOGE("Error in EFL::ElfFile::ParseDynSymTable(): new failed");
345 return false;
346 }
347 ret = ReadFile(secBuf, secSize);
348 HLOG_ASSERT(ret == static_cast<int64_t>(secSize));
349 dynSymTable_ = SymbolTable::MakeUnique(symNamesStr_, secBuf, secSize, entrySize);
350 if (dynSymTable_ == nullptr) {
351 delete[] secBuf;
352 secBuf = nullptr;
353 return false;
354 }
355 delete[] secBuf;
356 secBuf = nullptr;
357 return true;
358 }
359
GetSectionName(const uint32_t startIndex)360 std::string ElfFile::GetSectionName(const uint32_t startIndex)
361 {
362 if (startIndex >= secNamesStr_.size()) {
363 HLOGF("out_of_range %s ,endIndex %d ", secNamesStr_.c_str(), startIndex);
364 return "";
365 }
366 size_t endIndex {startIndex};
367 for (; endIndex < secNamesStr_.size(); ++endIndex) {
368 if (secNamesStr_[endIndex] == '\0') {
369 break;
370 }
371 }
372 return secNamesStr_.substr(startIndex, endIndex - startIndex);
373 }
374 } // namespace HiPerf
375 } // namespace Developtools
376 } // namespace OHOS
377