/root/doris/be/src/common/elf.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // This file is copied from |
18 | | // https://github.com/ClickHouse/ClickHouse/blob/master/src/Common/Elf.cpp |
19 | | // and modified by Doris |
20 | | |
21 | | #if defined(__ELF__) && !defined(__FreeBSD__) |
22 | | |
23 | | #include <common/elf.h> |
24 | | #include <fcntl.h> |
25 | | #include <fmt/format.h> |
26 | | #include <gutil/macros.h> |
27 | | #include <sys/mman.h> |
28 | | #include <unistd.h> |
29 | | #include <vec/common/unaligned.h> |
30 | | |
31 | | #include <cstring> |
32 | | #include <system_error> |
33 | | |
34 | | #include "common/logging.h" |
35 | | |
36 | | namespace doris { |
37 | | |
38 | 14 | Elf::Elf(const std::string& path) { |
39 | 14 | _file = path; |
40 | 14 | std::error_code ec; |
41 | 14 | elf_size = std::filesystem::file_size(_file, ec); |
42 | 14 | if (ec) { |
43 | 0 | LOG(FATAL) << fmt::format("failed to get file size {}: ({}), {}", _file.native(), |
44 | 0 | ec.value(), ec.message()); |
45 | 0 | } |
46 | | /// Check if it's an elf. |
47 | 14 | if (elf_size < sizeof(ElfEhdr)) { |
48 | 0 | LOG(FATAL) << fmt::format("The size of supposedly ELF file '{}' is too small", path); |
49 | 0 | } |
50 | 14 | RETRY_ON_EINTR(_fd, open(_file.c_str(), O_RDONLY)); |
51 | 14 | if (_fd < 0) { |
52 | 0 | LOG(FATAL) << fmt::format("failed to open {}", _file.native()); |
53 | 0 | } |
54 | 14 | mapped = static_cast<char*>(mmap(nullptr, elf_size, PROT_READ, MAP_SHARED, _fd, 0)); |
55 | 14 | if (MAP_FAILED == mapped) { |
56 | 0 | LOG(FATAL) << fmt::format("MMappedFileDescriptor: Cannot mmap {}, read from {}.", elf_size, |
57 | 0 | path); |
58 | 0 | } |
59 | | |
60 | 14 | header = reinterpret_cast<const ElfEhdr*>(mapped); |
61 | | |
62 | 14 | if (memcmp(header->e_ident, |
63 | 14 | "\x7F" |
64 | 14 | "ELF", |
65 | 14 | 4) != 0) { |
66 | 0 | LOG(FATAL) << fmt::format("The file '{}' is not ELF according to magic", path); |
67 | 0 | } |
68 | | |
69 | | /// Get section header. |
70 | 14 | ElfOff section_header_offset = header->e_shoff; |
71 | 14 | uint16_t section_header_num_entries = header->e_shnum; |
72 | | |
73 | 14 | if (!section_header_offset || !section_header_num_entries || |
74 | 14 | section_header_offset + section_header_num_entries * sizeof(ElfShdr) > elf_size) { |
75 | 0 | LOG(FATAL) << fmt::format( |
76 | 0 | "The ELF '{}' is truncated (section header points after end of file)", path); |
77 | 0 | } |
78 | | |
79 | 14 | section_headers = reinterpret_cast<const ElfShdr*>(mapped + section_header_offset); |
80 | | |
81 | | /// The string table with section names. |
82 | 572 | auto section_names_strtab = findSection([&](const Section& section, size_t idx) { |
83 | 572 | return section.header.sh_type == SHT_STRTAB && header->e_shstrndx == idx; |
84 | 572 | }); |
85 | | |
86 | 14 | if (!section_names_strtab) { |
87 | 0 | LOG(FATAL) << fmt::format("The ELF '{}' doesn't have string table with section names", |
88 | 0 | path); |
89 | 0 | } |
90 | | |
91 | 14 | ElfOff section_names_offset = section_names_strtab->header.sh_offset; |
92 | 14 | if (section_names_offset >= elf_size) { |
93 | 0 | LOG(FATAL) << fmt::format( |
94 | 0 | "The ELF '{}' is truncated (section names string table points after end of file)", |
95 | 0 | path); |
96 | 0 | } |
97 | 14 | section_names = reinterpret_cast<const char*>(mapped + section_names_offset); |
98 | | |
99 | | /// Get program headers |
100 | | |
101 | 14 | ElfOff program_header_offset = header->e_phoff; |
102 | 14 | uint16_t program_header_num_entries = header->e_phnum; |
103 | | |
104 | 14 | if (!program_header_offset || !program_header_num_entries || |
105 | 14 | program_header_offset + program_header_num_entries * sizeof(ElfPhdr) > elf_size) { |
106 | 0 | LOG(FATAL) << fmt::format( |
107 | 0 | "The ELF '{}' is truncated (program header points after end of file)", path); |
108 | 0 | } |
109 | 14 | program_headers = reinterpret_cast<const ElfPhdr*>(mapped + program_header_offset); |
110 | 14 | } |
111 | | |
112 | 14 | Elf::~Elf() { |
113 | 14 | if (mapped) { |
114 | 14 | munmap(static_cast<void*>(mapped), elf_size); |
115 | 14 | } |
116 | 14 | if (_fd > 0) { |
117 | 14 | int res = ::close(_fd); |
118 | 14 | if (-1 == res) { |
119 | 0 | LOG(WARNING) << fmt::format("failed to close {}", _file.native()); |
120 | 0 | } |
121 | 14 | _fd = -1; |
122 | 14 | } |
123 | 14 | } |
124 | | |
125 | 26.3k | Elf::Section::Section(const ElfShdr& header_, const Elf& elf_) : header(header_), elf(elf_) {} |
126 | | |
127 | 458 | bool Elf::iterateSections(std::function<bool(const Section& section, size_t idx)>&& pred) const { |
128 | 26.5k | for (size_t idx = 0; idx < header->e_shnum; ++idx) { |
129 | 26.3k | Section section(section_headers[idx], *this); |
130 | | |
131 | | /// Sections spans after end of file. |
132 | 26.3k | if (section.header.sh_offset + section.header.sh_size > elf_size) { |
133 | 2 | continue; |
134 | 2 | } |
135 | | |
136 | 26.3k | if (pred(section, idx)) { |
137 | 247 | return true; |
138 | 247 | } |
139 | 26.3k | } |
140 | 211 | return false; |
141 | 458 | } |
142 | | |
143 | | std::optional<Elf::Section> Elf::findSection( |
144 | 432 | std::function<bool(const Section& section, size_t idx)>&& pred) const { |
145 | 432 | std::optional<Elf::Section> result; |
146 | | |
147 | 25.7k | iterateSections([&](const Section& section, size_t idx) { |
148 | 25.7k | if (pred(section, idx)) { |
149 | 223 | result.emplace(section); |
150 | 223 | return true; |
151 | 223 | } |
152 | 25.5k | return false; |
153 | 25.7k | }); |
154 | | |
155 | 432 | return result; |
156 | 432 | } |
157 | | |
158 | 418 | std::optional<Elf::Section> Elf::findSectionByName(const char* name) const { |
159 | 418 | return findSection( |
160 | 25.2k | [&](const Section& section, size_t) { return 0 == strcmp(name, section.name()); }); |
161 | 418 | } |
162 | | |
163 | 14 | std::string Elf::getBuildID() const { |
164 | | /// Section headers are the first choice for a debuginfo file |
165 | 64 | if (std::string build_id; iterateSections([&build_id](const Section& section, size_t) { |
166 | 64 | if (section.header.sh_type == SHT_NOTE) { |
167 | 17 | build_id = Elf::getBuildID(section.begin(), section.size()); |
168 | 17 | if (!build_id.empty()) { |
169 | 14 | return true; |
170 | 14 | } |
171 | 17 | } |
172 | 50 | return false; |
173 | 64 | })) { |
174 | 14 | return build_id; |
175 | 14 | } |
176 | | |
177 | | /// fallback to PHDR |
178 | 0 | for (size_t idx = 0; idx < header->e_phnum; ++idx) { |
179 | 0 | const ElfPhdr& phdr = program_headers[idx]; |
180 | |
|
181 | 0 | if (phdr.p_type == PT_NOTE) { |
182 | 0 | return getBuildID(mapped + phdr.p_offset, phdr.p_filesz); |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | 0 | return {}; |
187 | 0 | } |
188 | | |
189 | | #if defined(OS_SUNOS) |
190 | | std::string Elf::getBuildID(const char* nhdr_pos, size_t size) { |
191 | | return {}; |
192 | | } |
193 | | #else |
194 | 29 | std::string Elf::getBuildID(const char* nhdr_pos, size_t size) { |
195 | 29 | const char* nhdr_end = nhdr_pos + size; |
196 | | |
197 | 35 | while (nhdr_pos < nhdr_end) { |
198 | 31 | ElfNhdr nhdr = unaligned_load<ElfNhdr>(nhdr_pos); |
199 | | |
200 | 31 | nhdr_pos += sizeof(ElfNhdr) + nhdr.n_namesz; |
201 | 31 | if (nhdr.n_type == NT_GNU_BUILD_ID) { |
202 | 25 | const char* build_id = nhdr_pos; |
203 | 25 | return {build_id, nhdr.n_descsz}; |
204 | 25 | } |
205 | 6 | nhdr_pos += nhdr.n_descsz; |
206 | 6 | } |
207 | | |
208 | 4 | return {}; |
209 | 29 | } |
210 | | #endif // OS_SUNOS |
211 | | |
212 | 0 | std::string Elf::getStoredBinaryHash() const { |
213 | 0 | if (auto section = findSectionByName(".clickhouse.hash")) { |
214 | 0 | return {section->begin(), section->end()}; |
215 | 0 | } else { |
216 | 0 | return {}; |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | 25.2k | const char* Elf::Section::name() const { |
221 | 25.2k | if (!elf.section_names) { |
222 | 0 | LOG(FATAL) << fmt::format("Section names are not initialized"); |
223 | 0 | } |
224 | | |
225 | | /// TODO buffer overflow is possible, we may need to check strlen. |
226 | 25.2k | return elf.section_names + header.sh_name; |
227 | 25.2k | } |
228 | | |
229 | 256 | const char* Elf::Section::begin() const { |
230 | 256 | return elf.mapped + header.sh_offset; |
231 | 256 | } |
232 | | |
233 | 10 | const char* Elf::Section::end() const { |
234 | 10 | return begin() + size(); |
235 | 10 | } |
236 | | |
237 | 236 | size_t Elf::Section::size() const { |
238 | 236 | return header.sh_size; |
239 | 236 | } |
240 | | |
241 | | } // namespace doris |
242 | | |
243 | | #endif |