forked from google/LLpatch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fixup_command.cc
406 lines (360 loc) · 12.1 KB
/
fixup_command.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Yonghyun Hwang <[email protected]>
*/
#include "fixup_command.h"
#include <argp.h>
#include <fcntl.h>
#include <gelf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include <iterator>
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "elf_error.h"
#include "elf_bin.h"
#include "elf_rela.h"
#include "elf_symbol.h"
#include "symbol_map.h"
#include "thin_archive.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace
{
struct FixupArgs {
char *klp_patch_filename = nullptr;
char *mod_filename = nullptr;
char *symbol_map = nullptr;
char *thin_archive = nullptr;
bool create_klp_rela = false;
bool quiet_mode = false;
};
const char kFixupArgsDoc[] = "<klp_patch.o>";
const char kFixupPrgDoc[] = "common fixup options:\n";
const struct argp_option kFixupOptions[] = {
// name, key, arg, flags, doc,
{ "mod", 'm', "MOD", 0,
"Path to kernel module. For vmlinux, don't specify" },
{ "symbol_map", 's', "SYMBOL_MAP", 0,
"Symbol map file for LLpatch symbols in livepatch wrapper" },
{ "thin_archive", 't', "THIN_ARCHIVE", 0,
"Thin archive file for kernel module or vmlinux" },
{ "rela", 'r', nullptr, 0, "Create relocation section for KLP" },
{ "quiet", 'q', nullptr, 0, "Don't print out any messages on fixup" },
{ nullptr }
};
constexpr std::string_view kKlpPrefix = ".klp.sym.";
constexpr std::string_view kKlpRelaPrefix = ".klp.rela.";
constexpr std::string_view kObjVmlinux = "vmlinux.";
error_t ParseFixupOpt(int key, char *arg, struct argp_state *state)
{
FixupArgs *args = static_cast<FixupArgs *>(state->input);
switch (key) {
case 'm':
args->mod_filename = arg;
break;
case 'r':
args->create_klp_rela = true;
break;
case 'q':
args->quiet_mode = true;
break;
case 's':
args->symbol_map = arg;
break;
case 't':
args->thin_archive = arg;
break;
case ARGP_KEY_ARG:
if (!args->klp_patch_filename) {
args->klp_patch_filename = arg;
} else {
argp_usage(state);
}
break;
case ARGP_KEY_END:
if (!args->klp_patch_filename) {
argp_usage(state);
}
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
} // namespace
// Assumption: there is 1-to-1 correspondence between a relocation section
// and the section that the relocation section relocates. The following is
// the example.
//
// [Nr] Name Type Address Off Size ES Flg Lk Inf Al
// [ 1] .text PROGBITS 0000000000000000 000040 0014ca 00 AX 0 0 16
// [ 2] .rela.text RELA 0000000000000000 001510 002268 18 I 18 1 8
//
// ".rela.text" should be only one relocation section for ".text".
std::error_code FixupCommand::CreateKlpRela(ElfBin *elf_bin)
{
ElfRela::KlpRelaEntryMap klp_rela_entry_map;
ElfRela::RelaEntryMap rela_entry_map;
std::unordered_map<size_t, size_t> symtab_map;
for (ElfRela *i : elf_bin->Relas()) {
std::string sym_name(i->Name());
if (sym_name.find(kKlpPrefix) != 0) {
// Store rela entry for non-livepatched symbols.
rela_entry_map[i->SectionId()].emplace_back(
*(i->Entry()));
continue;
}
i->SetSectionIndex(ElfSymbol::SectionIndex::LIVEPATCH);
size_t mod_name_start = kKlpPrefix.length();
size_t mod_name_end =
sym_name.find('.', mod_name_start) - mod_name_start;
const std::string &mod_name =
sym_name.substr(mod_name_start, mod_name_end);
if (!quiet_mode_) {
out_ << "klp symbol[" << mod_name << "] :: ";
i->PrintCurrentEntry();
}
// Collect all relocation entries for livepatched symbols.
klp_rela_entry_map[std::make_pair(mod_name, i->SectionId())]
.emplace_back(*(i->Entry()));
symtab_map[i->SectionId()] = i->SymTabId();
}
// Update existing rela sections to avoid duplication with KLP rela
// sections.
for (auto &[section_id, rela_vector] : rela_entry_map) {
// Update rela section that has livepatched symbols.
elf_bin->UpdateRela(section_id, &rela_vector);
}
// Update RELA sections before adding new KLP RELA sections.
elf_bin->ElfUpdate();
// Create new relocation section for KLP.
std::unique_ptr<std::vector<char> > str_section =
elf_bin->GetSection(elf_bin->GetStringSectionIndex());
for (auto &[entry_key, rela_vector] : klp_rela_entry_map) {
elf_bin->CreateKlpRela(std::get</*section_id*/ 1>(entry_key),
symtab_map[std::get<1>(entry_key)],
str_section->size(), &rela_vector);
// Format for The name of a livepatch relocation section:
//
// .klp.rela.objname.section_name
// ^ ^^ ^ ^ ^
// |________||_____| |__________|
// [A] [B] [C]
// [A]: prefix
// [B]: vmlinux or module name that the symbol belongs.
// [C]: section name to which this relocation section applies. should be "text"
const std::string kKlpRelaName =
std::string(kKlpRelaPrefix) +
std::get</*mod_name*/ 0>(entry_key) + "." +
std::string(
elf_bin->SectionName(std::get<1>(entry_key)));
llvm::outs() << "KLP rela section::" << kKlpRelaName << "\n";
std::copy(kKlpRelaName.begin(), kKlpRelaName.end(),
std::back_insert_iterator<std::vector<char> >(
*str_section));
str_section->push_back('\0');
}
elf_bin->UpdateSection(elf_bin->GetStringSectionIndex(),
str_section->data(), str_section->size());
elf_bin->ElfUpdate();
return Command::ErrorCode::NO_ERROR;
}
std::error_code FixupCommand::RenameKlpSymbols(ElfBin *elf_bin,
std::string_view mod_filename,
std::string_view symbol_map,
std::string_view thin_archive)
{
// Load names for all "defined" symbols in kernel module if specified.
std::unordered_set<std::string> mod_symbol_set;
std::string mod_name(kObjVmlinux);
if (!mod_filename.empty()) {
ElfBin mod_bin(mod_filename);
for (ElfSymbol *i : mod_bin.Symbols()) {
if (i->HasSectionIndex(
ElfSymbol::SectionIndex::UNDEF)) {
continue;
}
mod_symbol_set.insert(std::string(i->Name()));
}
mod_name = mod_bin.ModName() + ".";
}
// Elf binary always starts w/ dummy undefined symbol. iterator from
// ElfSymbol skips the first dummy symbol. Hence, default memory buffer
// for string section has '\0' by default.
std::vector<char> sym_name_buf{ '\0' };
ElfSymbol elf_symbols = elf_bin->Symbols();
size_t sym_name_offset = 0;
auto RenameSymbol = [&sym_name_buf, &sym_name_offset](
ElfSymbol *i,
const std::string_view new_name) {
std::copy(new_name.begin(), new_name.end(),
std::back_insert_iterator<std::vector<char> >(
sym_name_buf));
sym_name_buf.push_back('\0');
i->Rename(sym_name_offset);
sym_name_offset = sym_name_buf.size();
};
// This loop iterates through all symbols in the ELF binary and renames
// symbol if it's undefined. While renaming the symbol, it also builds
// up a memory buffer for symbol names. The buffer is used to update
// string section in ELF binary after this loop.
std::unique_ptr<ThinArchive> tar =
ThinArchive::Create(std::string(thin_archive));
std::unique_ptr<SymbolMap> sym_map =
SymbolMap::Create(std::string(symbol_map));
for (ElfSymbol *i : elf_symbols) {
// __fentry__ is for kernel's ftrace. don't touch even though it's UND.
if (!i->HasSectionIndex(ElfSymbol::SectionIndex::UNDEF) ||
i->Name() == "__fentry__") {
RenameSymbol(i, i->Name());
continue;
}
StringRef RealSymName = i->Name();
StringRef SrcFile;
std::string SymName = std::string(i->Name());
std::string RealSymNameStr = RealSymName.str();
if (sym_map) {
if (i->IsLLpatchSymbol()) {
auto alias = i->GetLLpatchSymbolAlias();
const auto &sym_entry =
sym_map->QueryAlias(std::string(alias));
RealSymName =
sym_entry[SymbolMap::ElemIndex::SYMBOL];
SrcFile = sym_entry[SymbolMap::ElemIndex::PATH];
mod_name =
sym_entry[SymbolMap::ElemIndex::MOD_NAME] +
".";
RealSymNameStr.assign(RealSymName);
} else {
// with symbol map given, only llpatch symbol should be KLP
// symbol.
RenameSymbol(i, RealSymNameStr);
continue;
}
} else {
if (i->IsKLPLocalSymbol()) {
auto SplitName = RealSymName.split(':');
RealSymName = SplitName.second.split(':').first;
SrcFile = SplitName.second.split(':').second;
RealSymNameStr.assign(RealSymName);
}
if (mod_name != kObjVmlinux &&
mod_symbol_set.find(RealSymNameStr) ==
mod_symbol_set.end()) {
// given kernel module doesn't have symbol name, which
// implies EXPORTed symbol. So, do not mark this as
// livepatched symbol.
RenameSymbol(i, RealSymNameStr);
continue;
}
}
i->SetSectionIndex(ElfSymbol::SectionIndex::LIVEPATCH);
// Rename the symbol for livepatching. The following is the format.
//
// .klp.sym.objname.symbol_name,sympos
// ^ ^^ ^ ^ ^ ^
// |_______||_____| |_________| |
// [A] [B] [C] [D]
//
// [A]: Prefix
// [B]: vmlinux or module name that the symbol belongs.
// [C]: Actual name of the symbol.
// [D]: The position of the symbol in the object (as according
// to kallsyms) This is used to differentiate
// duplicate symbols within the same object. The
// symbol position is expressed numerically (0, 1,
// 2, ...). The symbol position of a unique symbol
// is 0.
int pos = 0;
if (tar) {
const auto &symbol = RealSymName.str();
const auto &filename =
SrcFile.rsplit('.').first.str() + ".o";
pos = tar->QuerySymbol(symbol, filename);
if (pos < 0) {
errs() << "Symbol: " << symbol
<< ", Filename: " << filename << "\n"
<< "Fail to find the symbol in thin archive\n";
return Command::ErrorCode::SYM_FIND_FAILED;
}
}
const std::string kKlpSymName = std::string(kKlpPrefix) +
mod_name + RealSymNameStr +
"," + std::to_string(pos);
out_ << "KLP Symbols::" << RealSymNameStr << " --> "
<< kKlpSymName << "\n";
RenameSymbol(i, kKlpSymName);
}
// A new memory buffer for symbol name is built up in
// sym_name_buf. need to replace old buffer w/ the new one for string
// section before calling Elfbin::ElfUpdate()
elf_bin->UpdateSection(elf_symbols.GetStringSectionIndex(),
sym_name_buf.data(), sym_name_buf.size());
elf_bin->ElfUpdate();
return Command::ErrorCode::NO_ERROR;
}
std::unique_ptr<FixupCommand> FixupCommand::Create(int argc, char **argv)
noexcept(false)
{
if (argc < 1) {
throw std::error_code{ ErrorCode::NOT_ENOUGH_ARGS };
}
FixupArgs arguments;
struct argp argp = { kFixupOptions, ParseFixupOpt, kFixupArgsDoc,
kFixupPrgDoc };
// First argument is a command, 'fixup' and it's already consumed. So,
// argv[0] = argv[0] + argv[1] to let others used for options.
std::string command = std::string(argv[0]) + " " + argv[1];
--argc;
++argv;
argv[0] = const_cast<char *>(command.c_str());
argp_parse(&argp, argc, argv, /*flags=*/0,
/*arg_index=*/nullptr, /*input=*/&arguments);
auto *cmd = new FixupCommand(arguments.quiet_mode ? llvm::nulls() :
llvm::outs());
cmd->quiet_mode_ = arguments.quiet_mode;
cmd->klp_patch_filename_ = arguments.klp_patch_filename;
if (arguments.mod_filename) {
cmd->mod_filename_ = arguments.mod_filename;
}
cmd->create_klp_rela_ = arguments.create_klp_rela;
if (arguments.thin_archive) {
cmd->thin_archive_ = arguments.thin_archive;
}
if (arguments.symbol_map) {
cmd->symbol_map_ = arguments.symbol_map;
}
return std::unique_ptr<FixupCommand>(cmd);
}
std::error_code FixupCommand::Run()
{
std::error_code ec;
ElfBin elf_bin(klp_patch_filename_);
if (create_klp_rela_) {
ec = CreateKlpRela(&elf_bin);
} else {
ec = RenameKlpSymbols(&elf_bin, mod_filename_, symbol_map_,
thin_archive_);
}
return ec;
}