Coverage Report

Created: 2025-04-25 12:06

/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
48
Elf::Elf(const std::string& path) {
39
48
    _file = path;
40
48
    std::error_code ec;
41
48
    elf_size = std::filesystem::file_size(_file, ec);
42
48
    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
48
    if (elf_size < sizeof(ElfEhdr)) {
48
0
        LOG(FATAL) << fmt::format("The size of supposedly ELF file '{}' is too small", path);
49
0
    }
50
48
    RETRY_ON_EINTR(_fd, open(_file.c_str(), O_RDONLY));
51
48
    if (_fd < 0) {
52
0
        LOG(FATAL) << fmt::format("failed to open {}", _file.native());
53
0
    }
54
48
    mapped = static_cast<char*>(mmap(nullptr, elf_size, PROT_READ, MAP_SHARED, _fd, 0));
55
48
    if (MAP_FAILED == mapped) {
56
0
        LOG(FATAL) << fmt::format("MMappedFileDescriptor: Cannot mmap {}, read from {}.", elf_size,
57
0
                                  path);
58
0
    }
59
60
48
    header = reinterpret_cast<const ElfEhdr*>(mapped);
61
62
48
    if (memcmp(header->e_ident,
63
48
               "\x7F"
64
48
               "ELF",
65
48
               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
48
    ElfOff section_header_offset = header->e_shoff;
71
48
    uint16_t section_header_num_entries = header->e_shnum;
72
73
48
    if (!section_header_offset || !section_header_num_entries ||
74
48
        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
48
    section_headers = reinterpret_cast<const ElfShdr*>(mapped + section_header_offset);
80
81
    /// The string table with section names.
82
1.60k
    auto section_names_strtab = findSection([&](const Section& section, size_t idx) {
83
1.60k
        return section.header.sh_type == SHT_STRTAB && header->e_shstrndx == idx;
84
1.60k
    });
85
86
48
    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
48
    ElfOff section_names_offset = section_names_strtab->header.sh_offset;
92
48
    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
48
    section_names = reinterpret_cast<const char*>(mapped + section_names_offset);
98
99
    /// Get program headers
100
101
48
    ElfOff program_header_offset = header->e_phoff;
102
48
    uint16_t program_header_num_entries = header->e_phnum;
103
104
48
    if (!program_header_offset || !program_header_num_entries ||
105
48
        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
48
    program_headers = reinterpret_cast<const ElfPhdr*>(mapped + program_header_offset);
110
48
}
111
112
44
Elf::~Elf() {
113
44
    if (mapped) {
114
44
        munmap(static_cast<void*>(mapped), elf_size);
115
44
    }
116
44
    if (_fd > 0) {
117
44
        int res = ::close(_fd);
118
44
        if (-1 == res) {
119
0
            LOG(WARNING) << fmt::format("failed to close {}", _file.native());
120
0
        }
121
44
        _fd = -1;
122
44
    }
123
44
}
124
125
165k
Elf::Section::Section(const ElfShdr& header_, const Elf& elf_) : header(header_), elf(elf_) {}
126
127
3.64k
bool Elf::iterateSections(std::function<bool(const Section& section, size_t idx)>&& pred) const {
128
165k
    for (size_t idx = 0; idx < header->e_shnum; ++idx) {
129
165k
        Section section(section_headers[idx], *this);
130
131
        /// Sections spans after end of file.
132
165k
        if (section.header.sh_offset + section.header.sh_size > elf_size) {
133
8
            continue;
134
8
        }
135
136
165k
        if (pred(section, idx)) {
137
3.64k
            return true;
138
3.64k
        }
139
165k
    }
140
0
    return false;
141
3.64k
}
142
143
std::optional<Elf::Section> Elf::findSection(
144
3.59k
        std::function<bool(const Section& section, size_t idx)>&& pred) const {
145
3.59k
    std::optional<Elf::Section> result;
146
147
165k
    iterateSections([&](const Section& section, size_t idx) {
148
165k
        if (pred(section, idx)) {
149
3.59k
            result.emplace(section);
150
3.59k
            return true;
151
3.59k
        }
152
161k
        return false;
153
165k
    });
154
155
3.59k
    return result;
156
3.59k
}
157
158
3.54k
std::optional<Elf::Section> Elf::findSectionByName(const char* name) const {
159
3.54k
    return findSection(
160
163k
            [&](const Section& section, size_t) { return 0 == strcmp(name, section.name()); });
161
3.54k
}
162
163
48
std::string Elf::getBuildID() const {
164
    /// Section headers are the first choice for a debuginfo file
165
140
    if (std::string build_id; iterateSections([&build_id](const Section& section, size_t) {
166
140
            if (section.header.sh_type == SHT_NOTE) {
167
90
                build_id = Elf::getBuildID(section.begin(), section.size());
168
90
                if (!build_id.empty()) {
169
48
                    return true;
170
48
                }
171
90
            }
172
92
            return false;
173
140
        })) {
174
48
        return build_id;
175
48
    }
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
140
std::string Elf::getBuildID(const char* nhdr_pos, size_t size) {
195
140
    const char* nhdr_end = nhdr_pos + size;
196
197
228
    while (nhdr_pos < nhdr_end) {
198
144
        ElfNhdr nhdr = unaligned_load<ElfNhdr>(nhdr_pos);
199
200
144
        nhdr_pos += sizeof(ElfNhdr) + nhdr.n_namesz;
201
144
        if (nhdr.n_type == NT_GNU_BUILD_ID) {
202
56
            const char* build_id = nhdr_pos;
203
56
            return {build_id, nhdr.n_descsz};
204
56
        }
205
88
        nhdr_pos += nhdr.n_descsz;
206
88
    }
207
208
84
    return {};
209
140
}
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
163k
const char* Elf::Section::name() const {
221
163k
    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
163k
    return elf.section_names + header.sh_name;
227
163k
}
228
229
3.65k
const char* Elf::Section::begin() const {
230
3.65k
    return elf.mapped + header.sh_offset;
231
3.65k
}
232
233
8
const char* Elf::Section::end() const {
234
8
    return begin() + size();
235
8
}
236
237
3.64k
size_t Elf::Section::size() const {
238
3.64k
    return header.sh_size;
239
3.64k
}
240
241
} // namespace doris
242
243
#endif