Coverage Report

Created: 2026-03-12 05:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/be/src/common/elf.cpp
Line
Count
Source
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
25
#include <fcntl.h>
26
#include <fmt/format.h>
27
#include <sys/mman.h>
28
#include <unistd.h>
29
30
#include <cstring>
31
#include <system_error>
32
33
#include "common/logging.h"
34
#include "common/macros.h"
35
#include "util/unaligned.h"
36
37
namespace doris {
38
39
182
Elf::Elf(const std::string& path) {
40
182
    _file = path;
41
182
    std::error_code ec;
42
182
    elf_size = std::filesystem::file_size(_file, ec);
43
182
    if (ec) {
44
0
        LOG(FATAL) << fmt::format("failed to get file size {}: ({}), {}", _file.native(),
45
0
                                  ec.value(), ec.message());
46
0
    }
47
    /// Check if it's an elf.
48
182
    if (elf_size < sizeof(ElfEhdr)) {
49
0
        LOG(FATAL) << fmt::format("The size of supposedly ELF file '{}' is too small", path);
50
0
    }
51
182
    RETRY_ON_EINTR(_fd, open(_file.c_str(), O_RDONLY));
52
182
    if (_fd < 0) {
53
0
        LOG(FATAL) << fmt::format("failed to open {}", _file.native());
54
0
    }
55
182
    mapped = static_cast<char*>(mmap(nullptr, elf_size, PROT_READ, MAP_SHARED, _fd, 0));
56
182
    if (MAP_FAILED == mapped) {
57
0
        LOG(FATAL) << fmt::format("MMappedFileDescriptor: Cannot mmap {}, read from {}.", elf_size,
58
0
                                  path);
59
0
    }
60
61
182
    header = reinterpret_cast<const ElfEhdr*>(mapped);
62
63
182
    if (memcmp(header->e_ident,
64
182
               "\x7F"
65
182
               "ELF",
66
182
               4) != 0) {
67
0
        LOG(FATAL) << fmt::format("The file '{}' is not ELF according to magic", path);
68
0
    }
69
70
    /// Get section header.
71
182
    ElfOff section_header_offset = header->e_shoff;
72
182
    uint16_t section_header_num_entries = header->e_shnum;
73
74
182
    if (!section_header_offset || !section_header_num_entries ||
75
182
        section_header_offset + section_header_num_entries * sizeof(ElfShdr) > elf_size) {
76
0
        LOG(FATAL) << fmt::format(
77
0
                "The ELF '{}' is truncated (section header points after end of file)", path);
78
0
    }
79
80
182
    section_headers = reinterpret_cast<const ElfShdr*>(mapped + section_header_offset);
81
82
    /// The string table with section names.
83
6.35k
    auto section_names_strtab = findSection([&](const Section& section, size_t idx) {
84
6.35k
        return section.header.sh_type == SHT_STRTAB && header->e_shstrndx == idx;
85
6.35k
    });
86
87
182
    if (!section_names_strtab) {
88
0
        LOG(FATAL) << fmt::format("The ELF '{}' doesn't have string table with section names",
89
0
                                  path);
90
0
    }
91
92
182
    ElfOff section_names_offset = section_names_strtab->header.sh_offset;
93
182
    if (section_names_offset >= elf_size) {
94
0
        LOG(FATAL) << fmt::format(
95
0
                "The ELF '{}' is truncated (section names string table points after end of file)",
96
0
                path);
97
0
    }
98
182
    section_names = reinterpret_cast<const char*>(mapped + section_names_offset);
99
100
    /// Get program headers
101
102
182
    ElfOff program_header_offset = header->e_phoff;
103
182
    uint16_t program_header_num_entries = header->e_phnum;
104
105
182
    if (!program_header_offset || !program_header_num_entries ||
106
182
        program_header_offset + program_header_num_entries * sizeof(ElfPhdr) > elf_size) {
107
0
        LOG(FATAL) << fmt::format(
108
0
                "The ELF '{}' is truncated (program header points after end of file)", path);
109
0
    }
110
182
    program_headers = reinterpret_cast<const ElfPhdr*>(mapped + program_header_offset);
111
182
}
112
113
166
Elf::~Elf() {
114
166
    if (mapped) {
115
166
        munmap(static_cast<void*>(mapped), elf_size);
116
166
    }
117
166
    if (_fd > 0) {
118
166
        int res = ::close(_fd);
119
166
        if (-1 == res) {
120
0
            LOG(WARNING) << fmt::format("failed to close {}", _file.native());
121
0
        }
122
166
        _fd = -1;
123
166
    }
124
166
}
125
126
63.3M
Elf::Section::Section(const ElfShdr& header_, const Elf& elf_) : header(header_), elf(elf_) {}
127
128
1.33M
bool Elf::iterateSections(std::function<bool(const Section& section, size_t idx)>&& pred) const {
129
63.3M
    for (size_t idx = 0; idx < header->e_shnum; ++idx) {
130
63.3M
        Section section(section_headers[idx], *this);
131
132
        /// Sections spans after end of file.
133
63.3M
        if (section.header.sh_offset + section.header.sh_size > elf_size) {
134
30
            continue;
135
30
        }
136
137
63.3M
        if (pred(section, idx)) {
138
1.33M
            return true;
139
1.33M
        }
140
63.3M
    }
141
2.96k
    return false;
142
1.33M
}
143
144
std::optional<Elf::Section> Elf::findSection(
145
1.33M
        std::function<bool(const Section& section, size_t idx)>&& pred) const {
146
1.33M
    std::optional<Elf::Section> result;
147
148
63.3M
    iterateSections([&](const Section& section, size_t idx) {
149
63.3M
        if (pred(section, idx)) {
150
1.33M
            result.emplace(section);
151
1.33M
            return true;
152
1.33M
        }
153
62.0M
        return false;
154
63.3M
    });
155
156
1.33M
    return result;
157
1.33M
}
158
159
1.33M
std::optional<Elf::Section> Elf::findSectionByName(const char* name) const {
160
1.33M
    return findSection(
161
63.3M
            [&](const Section& section, size_t) { return 0 == strcmp(name, section.name()); });
162
1.33M
}
163
164
182
std::string Elf::getBuildID() const {
165
    /// Section headers are the first choice for a debuginfo file
166
1.53k
    if (std::string build_id; iterateSections([&build_id](const Section& section, size_t) {
167
1.53k
            if (section.header.sh_type == SHT_NOTE) {
168
315
                build_id = Elf::getBuildID(section.begin(), section.size());
169
315
                if (!build_id.empty()) {
170
164
                    return true;
171
164
                }
172
315
            }
173
1.37k
            return false;
174
1.53k
        })) {
175
164
        return build_id;
176
164
    }
177
178
    /// fallback to PHDR
179
216
    for (size_t idx = 0; idx < header->e_phnum; ++idx) {
180
216
        const ElfPhdr& phdr = program_headers[idx];
181
182
216
        if (phdr.p_type == PT_NOTE) {
183
18
            return getBuildID(mapped + phdr.p_offset, phdr.p_filesz);
184
18
        }
185
216
    }
186
187
0
    return {};
188
18
}
189
190
#if defined(OS_SUNOS)
191
std::string Elf::getBuildID(const char* nhdr_pos, size_t size) {
192
    return {};
193
}
194
#else
195
512
std::string Elf::getBuildID(const char* nhdr_pos, size_t size) {
196
512
    const char* nhdr_end = nhdr_pos + size;
197
198
838
    while (nhdr_pos < nhdr_end) {
199
520
        ElfNhdr nhdr = unaligned_load<ElfNhdr>(nhdr_pos);
200
201
520
        nhdr_pos += sizeof(ElfNhdr) + nhdr.n_namesz;
202
520
        if (nhdr.n_type == NT_GNU_BUILD_ID) {
203
194
            const char* build_id = nhdr_pos;
204
194
            return {build_id, nhdr.n_descsz};
205
194
        }
206
326
        nhdr_pos += nhdr.n_descsz;
207
326
    }
208
209
318
    return {};
210
512
}
211
#endif // OS_SUNOS
212
213
0
std::string Elf::getStoredBinaryHash() const {
214
0
    if (auto section = findSectionByName(".clickhouse.hash")) {
215
0
        return {section->begin(), section->end()};
216
0
    } else {
217
0
        return {};
218
0
    }
219
0
}
220
221
63.3M
const char* Elf::Section::name() const {
222
63.3M
    if (!elf.section_names) {
223
0
        LOG(FATAL) << fmt::format("Section names are not initialized");
224
0
    }
225
226
    /// TODO buffer overflow is possible, we may need to check strlen.
227
63.3M
    return elf.section_names + header.sh_name;
228
63.3M
}
229
230
1.33M
const char* Elf::Section::begin() const {
231
1.33M
    return elf.mapped + header.sh_offset;
232
1.33M
}
233
234
31
const char* Elf::Section::end() const {
235
31
    return begin() + size();
236
31
}
237
238
1.33M
size_t Elf::Section::size() const {
239
1.33M
    return header.sh_size;
240
1.33M
}
241
242
} // namespace doris
243
244
#endif