Commit | Line | Data |
---|---|---|
1ccea77e | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
442f04c3 | 2 | /* |
dcc914f4 | 3 | * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> |
442f04c3 JP |
4 | */ |
5 | ||
442f04c3 | 6 | #include <subcmd/parse-options.h> |
c4a33939 | 7 | #include <string.h> |
900b4df3 | 8 | #include <stdlib.h> |
7786032e VG |
9 | #include <objtool/builtin.h> |
10 | #include <objtool/objtool.h> | |
442f04c3 | 11 | |
753da417 JP |
12 | #define ERROR(format, ...) \ |
13 | fprintf(stderr, \ | |
14 | "error: objtool: " format "\n", \ | |
15 | ##__VA_ARGS__) | |
16 | ||
2daf7fab | 17 | struct opts opts; |
442f04c3 | 18 | |
dcc914f4 | 19 | static const char * const check_usage[] = { |
b51277eb | 20 | "objtool <actions> [<options>] file.o", |
442f04c3 JP |
21 | NULL, |
22 | }; | |
23 | ||
900b4df3 PZ |
24 | static const char * const env_usage[] = { |
25 | "OBJTOOL_ARGS=\"<options>\"", | |
26 | NULL, | |
27 | }; | |
28 | ||
b51277eb JP |
29 | static int parse_dump(const struct option *opt, const char *str, int unset) |
30 | { | |
31 | if (!str || !strcmp(str, "orc")) { | |
32 | opts.dump_orc = true; | |
33 | return 0; | |
34 | } | |
35 | ||
36 | return -1; | |
37 | } | |
38 | ||
4ab7674f JP |
39 | static int parse_hacks(const struct option *opt, const char *str, int unset) |
40 | { | |
41 | bool found = false; | |
42 | ||
43 | /* | |
44 | * Use strstr() as a lazy method of checking for comma-separated | |
45 | * options. | |
46 | * | |
47 | * No string provided == enable all options. | |
48 | */ | |
49 | ||
50 | if (!str || strstr(str, "jump_label")) { | |
51 | opts.hack_jump_label = true; | |
52 | found = true; | |
53 | } | |
54 | ||
22102f45 JP |
55 | if (!str || strstr(str, "noinstr")) { |
56 | opts.hack_noinstr = true; | |
57 | found = true; | |
58 | } | |
59 | ||
0c0a6d89 PZ |
60 | if (!str || strstr(str, "skylake")) { |
61 | opts.hack_skylake = true; | |
62 | found = true; | |
63 | } | |
64 | ||
4ab7674f JP |
65 | return found ? 0 : -1; |
66 | } | |
67 | ||
cfd66e81 | 68 | static const struct option check_options[] = { |
2daf7fab | 69 | OPT_GROUP("Actions:"), |
0c0a6d89 | 70 | OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks), |
2daf7fab JP |
71 | OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"), |
72 | OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"), | |
73 | OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"), | |
b51277eb | 74 | OPT_BOOLEAN('o', "orc", &opts.orc, "generate ORC metadata"), |
2daf7fab | 75 | OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"), |
f43b9876 | 76 | OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"), |
a09a6e23 | 77 | OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"), |
9f2899fe | 78 | OPT_INTEGER(0, "prefix", &opts.prefix, "generate prefix symbols"), |
2daf7fab | 79 | OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"), |
72064474 | 80 | OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"), |
26e17689 | 81 | OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"), |
2daf7fab | 82 | OPT_BOOLEAN('u', "uaccess", &opts.uaccess, "validate uaccess rules for SMAP"), |
9a479f76 | 83 | OPT_BOOLEAN(0 , "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"), |
b51277eb | 84 | OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump), |
2daf7fab JP |
85 | |
86 | OPT_GROUP("Options:"), | |
87 | OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"), | |
88 | OPT_BOOLEAN(0, "backup", &opts.backup, "create .orig files before modification"), | |
89 | OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"), | |
753da417 | 90 | OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"), |
2daf7fab | 91 | OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"), |
280981d6 | 92 | OPT_BOOLEAN(0, "mnop", &opts.mnop, "nop out mcount call sites"), |
2daf7fab | 93 | OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"), |
99c0beb5 | 94 | OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"), |
2daf7fab | 95 | OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"), |
ca653464 | 96 | OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"), |
2daf7fab | 97 | |
dcc914f4 JP |
98 | OPT_END(), |
99 | }; | |
100 | ||
a2f605f9 PZ |
101 | int cmd_parse_options(int argc, const char **argv, const char * const usage[]) |
102 | { | |
900b4df3 PZ |
103 | const char *envv[16] = { }; |
104 | char *env; | |
105 | int envc; | |
106 | ||
107 | env = getenv("OBJTOOL_ARGS"); | |
108 | if (env) { | |
109 | envv[0] = "OBJTOOL_ARGS"; | |
110 | for (envc = 1; envc < ARRAY_SIZE(envv); ) { | |
111 | envv[envc++] = env; | |
112 | env = strchr(env, ' '); | |
113 | if (!env) | |
114 | break; | |
115 | *env = '\0'; | |
116 | env++; | |
117 | } | |
118 | ||
119 | parse_options(envc, envv, check_options, env_usage, 0); | |
120 | } | |
121 | ||
ca653464 JP |
122 | env = getenv("OBJTOOL_VERBOSE"); |
123 | if (env && !strcmp(env, "1")) | |
124 | opts.verbose = true; | |
125 | ||
a2f605f9 PZ |
126 | argc = parse_options(argc, argv, check_options, usage, 0); |
127 | if (argc != 1) | |
128 | usage_with_options(usage, check_options); | |
129 | return argc; | |
130 | } | |
131 | ||
b51277eb JP |
132 | static bool opts_valid(void) |
133 | { | |
4ab7674f | 134 | if (opts.hack_jump_label || |
22102f45 | 135 | opts.hack_noinstr || |
4ab7674f JP |
136 | opts.ibt || |
137 | opts.mcount || | |
138 | opts.noinstr || | |
139 | opts.orc || | |
140 | opts.retpoline || | |
f43b9876 | 141 | opts.rethunk || |
4ab7674f JP |
142 | opts.sls || |
143 | opts.stackval || | |
144 | opts.static_call || | |
b51277eb JP |
145 | opts.uaccess) { |
146 | if (opts.dump_orc) { | |
753da417 | 147 | ERROR("--dump can't be combined with other options"); |
b51277eb JP |
148 | return false; |
149 | } | |
150 | ||
151 | return true; | |
152 | } | |
153 | ||
f43b9876 PZ |
154 | if (opts.unret && !opts.rethunk) { |
155 | ERROR("--unret requires --rethunk"); | |
156 | return false; | |
157 | } | |
158 | ||
b51277eb JP |
159 | if (opts.dump_orc) |
160 | return true; | |
161 | ||
753da417 | 162 | ERROR("At least one command required"); |
b51277eb JP |
163 | return false; |
164 | } | |
165 | ||
280981d6 SV |
166 | static bool mnop_opts_valid(void) |
167 | { | |
168 | if (opts.mnop && !opts.mcount) { | |
169 | ERROR("--mnop requires --mcount"); | |
170 | return false; | |
171 | } | |
172 | ||
173 | return true; | |
174 | } | |
175 | ||
753da417 JP |
176 | static bool link_opts_valid(struct objtool_file *file) |
177 | { | |
178 | if (opts.link) | |
179 | return true; | |
180 | ||
181 | if (has_multiple_files(file->elf)) { | |
182 | ERROR("Linked object detected, forcing --link"); | |
183 | opts.link = true; | |
184 | return true; | |
185 | } | |
186 | ||
187 | if (opts.noinstr) { | |
188 | ERROR("--noinstr requires --link"); | |
189 | return false; | |
190 | } | |
191 | ||
192 | if (opts.ibt) { | |
193 | ERROR("--ibt requires --link"); | |
194 | return false; | |
195 | } | |
196 | ||
a09a6e23 PZ |
197 | if (opts.unret) { |
198 | ERROR("--unret requires --link"); | |
199 | return false; | |
200 | } | |
201 | ||
753da417 JP |
202 | return true; |
203 | } | |
204 | ||
b51277eb | 205 | int objtool_run(int argc, const char **argv) |
442f04c3 | 206 | { |
0e731dbc | 207 | const char *objname; |
6545eb03 | 208 | struct objtool_file *file; |
d44becb9 | 209 | int ret; |
442f04c3 | 210 | |
a2f605f9 | 211 | argc = cmd_parse_options(argc, argv, check_usage); |
442f04c3 JP |
212 | objname = argv[0]; |
213 | ||
b51277eb JP |
214 | if (!opts_valid()) |
215 | return 1; | |
216 | ||
217 | if (opts.dump_orc) | |
218 | return orc_dump(objname); | |
219 | ||
6545eb03 JT |
220 | file = objtool_open_read(objname); |
221 | if (!file) | |
222 | return 1; | |
223 | ||
280981d6 SV |
224 | if (!mnop_opts_valid()) |
225 | return 1; | |
226 | ||
753da417 JP |
227 | if (!link_opts_valid(file)) |
228 | return 1; | |
229 | ||
d44becb9 JT |
230 | ret = check(file); |
231 | if (ret) | |
232 | return ret; | |
233 | ||
234 | if (file->elf->changed) | |
235 | return elf_write(file->elf); | |
236 | ||
237 | return 0; | |
442f04c3 | 238 | } |