0001
0002
0003
0004
0005
0006 #include <subcmd/parse-options.h>
0007 #include <string.h>
0008 #include <stdlib.h>
0009 #include <objtool/builtin.h>
0010 #include <objtool/objtool.h>
0011
0012 #define ERROR(format, ...) \
0013 fprintf(stderr, \
0014 "error: objtool: " format "\n", \
0015 ##__VA_ARGS__)
0016
0017 struct opts opts;
0018
0019 static const char * const check_usage[] = {
0020 "objtool <actions> [<options>] file.o",
0021 NULL,
0022 };
0023
0024 static const char * const env_usage[] = {
0025 "OBJTOOL_ARGS=\"<options>\"",
0026 NULL,
0027 };
0028
0029 static int parse_dump(const struct option *opt, const char *str, int unset)
0030 {
0031 if (!str || !strcmp(str, "orc")) {
0032 opts.dump_orc = true;
0033 return 0;
0034 }
0035
0036 return -1;
0037 }
0038
0039 static int parse_hacks(const struct option *opt, const char *str, int unset)
0040 {
0041 bool found = false;
0042
0043
0044
0045
0046
0047
0048
0049
0050 if (!str || strstr(str, "jump_label")) {
0051 opts.hack_jump_label = true;
0052 found = true;
0053 }
0054
0055 if (!str || strstr(str, "noinstr")) {
0056 opts.hack_noinstr = true;
0057 found = true;
0058 }
0059
0060 return found ? 0 : -1;
0061 }
0062
0063 const struct option check_options[] = {
0064 OPT_GROUP("Actions:"),
0065 OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr", "patch toolchain bugs/limitations", parse_hacks),
0066 OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
0067 OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
0068 OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
0069 OPT_BOOLEAN('o', "orc", &opts.orc, "generate ORC metadata"),
0070 OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
0071 OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
0072 OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),
0073 OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
0074 OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
0075 OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
0076 OPT_BOOLEAN('u', "uaccess", &opts.uaccess, "validate uaccess rules for SMAP"),
0077 OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
0078
0079 OPT_GROUP("Options:"),
0080 OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
0081 OPT_BOOLEAN(0, "backup", &opts.backup, "create .orig files before modification"),
0082 OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
0083 OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"),
0084 OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
0085 OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
0086 OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"),
0087 OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
0088
0089 OPT_END(),
0090 };
0091
0092 int cmd_parse_options(int argc, const char **argv, const char * const usage[])
0093 {
0094 const char *envv[16] = { };
0095 char *env;
0096 int envc;
0097
0098 env = getenv("OBJTOOL_ARGS");
0099 if (env) {
0100 envv[0] = "OBJTOOL_ARGS";
0101 for (envc = 1; envc < ARRAY_SIZE(envv); ) {
0102 envv[envc++] = env;
0103 env = strchr(env, ' ');
0104 if (!env)
0105 break;
0106 *env = '\0';
0107 env++;
0108 }
0109
0110 parse_options(envc, envv, check_options, env_usage, 0);
0111 }
0112
0113 argc = parse_options(argc, argv, check_options, usage, 0);
0114 if (argc != 1)
0115 usage_with_options(usage, check_options);
0116 return argc;
0117 }
0118
0119 static bool opts_valid(void)
0120 {
0121 if (opts.hack_jump_label ||
0122 opts.hack_noinstr ||
0123 opts.ibt ||
0124 opts.mcount ||
0125 opts.noinstr ||
0126 opts.orc ||
0127 opts.retpoline ||
0128 opts.rethunk ||
0129 opts.sls ||
0130 opts.stackval ||
0131 opts.static_call ||
0132 opts.uaccess) {
0133 if (opts.dump_orc) {
0134 ERROR("--dump can't be combined with other options");
0135 return false;
0136 }
0137
0138 return true;
0139 }
0140
0141 if (opts.unret && !opts.rethunk) {
0142 ERROR("--unret requires --rethunk");
0143 return false;
0144 }
0145
0146 if (opts.dump_orc)
0147 return true;
0148
0149 ERROR("At least one command required");
0150 return false;
0151 }
0152
0153 static bool link_opts_valid(struct objtool_file *file)
0154 {
0155 if (opts.link)
0156 return true;
0157
0158 if (has_multiple_files(file->elf)) {
0159 ERROR("Linked object detected, forcing --link");
0160 opts.link = true;
0161 return true;
0162 }
0163
0164 if (opts.noinstr) {
0165 ERROR("--noinstr requires --link");
0166 return false;
0167 }
0168
0169 if (opts.ibt) {
0170 ERROR("--ibt requires --link");
0171 return false;
0172 }
0173
0174 if (opts.unret) {
0175 ERROR("--unret requires --link");
0176 return false;
0177 }
0178
0179 return true;
0180 }
0181
0182 int objtool_run(int argc, const char **argv)
0183 {
0184 const char *objname;
0185 struct objtool_file *file;
0186 int ret;
0187
0188 argc = cmd_parse_options(argc, argv, check_usage);
0189 objname = argv[0];
0190
0191 if (!opts_valid())
0192 return 1;
0193
0194 if (opts.dump_orc)
0195 return orc_dump(objname);
0196
0197 file = objtool_open_read(objname);
0198 if (!file)
0199 return 1;
0200
0201 if (!link_opts_valid(file))
0202 return 1;
0203
0204 ret = check(file);
0205 if (ret)
0206 return ret;
0207
0208 if (file->elf->changed)
0209 return elf_write(file->elf);
0210
0211 return 0;
0212 }