1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> |
4 | */ |
5 | |
6 | #include <subcmd/parse-options.h> |
7 | #include <string.h> |
8 | #include <stdlib.h> |
9 | #include <objtool/builtin.h> |
10 | #include <objtool/objtool.h> |
11 | |
12 | #define ERROR(format, ...) \ |
13 | fprintf(stderr, \ |
14 | "error: objtool: " format "\n", \ |
15 | ##__VA_ARGS__) |
16 | |
17 | struct opts opts; |
18 | |
19 | static const char * const check_usage[] = { |
20 | "objtool <actions> [<options>] file.o" , |
21 | NULL, |
22 | }; |
23 | |
24 | static const char * const env_usage[] = { |
25 | "OBJTOOL_ARGS=\"<options>\"" , |
26 | NULL, |
27 | }; |
28 | |
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 | |
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 | |
55 | if (!str || strstr(str, "noinstr" )) { |
56 | opts.hack_noinstr = true; |
57 | found = true; |
58 | } |
59 | |
60 | if (!str || strstr(str, "skylake" )) { |
61 | opts.hack_skylake = true; |
62 | found = true; |
63 | } |
64 | |
65 | return found ? 0 : -1; |
66 | } |
67 | |
68 | static const struct option check_options[] = { |
69 | OPT_GROUP("Actions:" ), |
70 | OPT_CALLBACK_OPTARG('h', "hacks" , NULL, NULL, "jump_label,noinstr,skylake" , "patch toolchain bugs/limitations" , parse_hacks), |
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" ), |
74 | OPT_BOOLEAN('o', "orc" , &opts.orc, "generate ORC metadata" ), |
75 | OPT_BOOLEAN('r', "retpoline" , &opts.retpoline, "validate and annotate retpoline usage" ), |
76 | OPT_BOOLEAN(0, "rethunk" , &opts.rethunk, "validate and annotate rethunk usage" ), |
77 | OPT_BOOLEAN(0, "unret" , &opts.unret, "validate entry unret placement" ), |
78 | OPT_INTEGER(0, "prefix" , &opts.prefix, "generate prefix symbols" ), |
79 | OPT_BOOLEAN('l', "sls" , &opts.sls, "validate straight-line-speculation mitigations" ), |
80 | OPT_BOOLEAN('s', "stackval" , &opts.stackval, "validate frame pointer rules" ), |
81 | OPT_BOOLEAN('t', "static-call" , &opts.static_call, "annotate static calls" ), |
82 | OPT_BOOLEAN('u', "uaccess" , &opts.uaccess, "validate uaccess rules for SMAP" ), |
83 | OPT_BOOLEAN(0 , "cfi" , &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles" ), |
84 | OPT_CALLBACK_OPTARG(0, "dump" , NULL, NULL, "orc" , "dump metadata" , parse_dump), |
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" ), |
90 | OPT_BOOLEAN(0, "link" , &opts.link, "object is a linked object" ), |
91 | OPT_BOOLEAN(0, "module" , &opts.module, "object is part of a kernel module" ), |
92 | OPT_BOOLEAN(0, "mnop" , &opts.mnop, "nop out mcount call sites" ), |
93 | OPT_BOOLEAN(0, "no-unreachable" , &opts.no_unreachable, "skip 'unreachable instruction' warnings" ), |
94 | OPT_BOOLEAN(0, "sec-address" , &opts.sec_address, "print section addresses in warnings" ), |
95 | OPT_BOOLEAN(0, "stats" , &opts.stats, "print statistics" ), |
96 | OPT_BOOLEAN('v', "verbose" , &opts.verbose, "verbose warnings" ), |
97 | |
98 | OPT_END(), |
99 | }; |
100 | |
101 | int cmd_parse_options(int argc, const char **argv, const char * const usage[]) |
102 | { |
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 | |
122 | env = getenv("OBJTOOL_VERBOSE" ); |
123 | if (env && !strcmp(env, "1" )) |
124 | opts.verbose = true; |
125 | |
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 | |
132 | static bool opts_valid(void) |
133 | { |
134 | if (opts.hack_jump_label || |
135 | opts.hack_noinstr || |
136 | opts.ibt || |
137 | opts.mcount || |
138 | opts.noinstr || |
139 | opts.orc || |
140 | opts.retpoline || |
141 | opts.rethunk || |
142 | opts.sls || |
143 | opts.stackval || |
144 | opts.static_call || |
145 | opts.uaccess) { |
146 | if (opts.dump_orc) { |
147 | ERROR("--dump can't be combined with other options" ); |
148 | return false; |
149 | } |
150 | |
151 | return true; |
152 | } |
153 | |
154 | if (opts.unret && !opts.rethunk) { |
155 | ERROR("--unret requires --rethunk" ); |
156 | return false; |
157 | } |
158 | |
159 | if (opts.dump_orc) |
160 | return true; |
161 | |
162 | ERROR("At least one command required" ); |
163 | return false; |
164 | } |
165 | |
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 | |
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 | |
197 | if (opts.unret) { |
198 | ERROR("--unret requires --link" ); |
199 | return false; |
200 | } |
201 | |
202 | return true; |
203 | } |
204 | |
205 | int objtool_run(int argc, const char **argv) |
206 | { |
207 | const char *objname; |
208 | struct objtool_file *file; |
209 | int ret; |
210 | |
211 | argc = cmd_parse_options(argc, argv, usage: check_usage); |
212 | objname = argv[0]; |
213 | |
214 | if (!opts_valid()) |
215 | return 1; |
216 | |
217 | if (opts.dump_orc) |
218 | return orc_dump(objname); |
219 | |
220 | file = objtool_open_read(objname); |
221 | if (!file) |
222 | return 1; |
223 | |
224 | if (!mnop_opts_valid()) |
225 | return 1; |
226 | |
227 | if (!link_opts_valid(file)) |
228 | return 1; |
229 | |
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; |
238 | } |
239 | |