1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com> 4 */ 5 6 /* 7 * objtool: 8 * 9 * The 'check' subcmd analyzes every .o file and ensures the validity of its 10 * stack trace metadata. It enforces a set of rules on asm code and C inline 11 * assembly code so that stack traces can be reliable. 12 * 13 * For more information, see tools/objtool/Documentation/stack-validation.txt. 14 */ 15 16 #include <stdio.h> 17 #include <stdbool.h> 18 #include <string.h> 19 #include <stdlib.h> 20 #include <unistd.h> 21 #include <subcmd/exec-cmd.h> 22 #include <subcmd/pager.h> 23 #include <linux/kernel.h> 24 25 #include <objtool/builtin.h> 26 #include <objtool/objtool.h> 27 #include <objtool/warn.h> 28 29 struct cmd_struct { 30 const char *name; 31 int (*fn)(int, const char **); 32 const char *help; 33 }; 34 35 static const char objtool_usage_string[] = 36 "objtool COMMAND [ARGS]"; 37 38 static struct cmd_struct objtool_cmds[] = { 39 {"check", cmd_check, "Perform stack metadata validation on an object file" }, 40 {"orc", cmd_orc, "Generate in-place ORC unwind tables for an object file" }, 41 }; 42 43 bool help; 44 45 const char *objname; 46 static struct objtool_file file; 47 48 static bool objtool_create_backup(const char *_objname) 49 { 50 int len = strlen(_objname); 51 char *buf, *base, *name = malloc(len+6); 52 int s, d, l, t; 53 54 if (!name) { 55 perror("failed backup name malloc"); 56 return false; 57 } 58 59 strcpy(name, _objname); 60 strcpy(name + len, ".orig"); 61 62 d = open(name, O_CREAT|O_WRONLY|O_TRUNC, 0644); 63 if (d < 0) { 64 perror("failed to create backup file"); 65 return false; 66 } 67 68 s = open(_objname, O_RDONLY); 69 if (s < 0) { 70 perror("failed to open orig file"); 71 return false; 72 } 73 74 buf = malloc(4096); 75 if (!buf) { 76 perror("failed backup data malloc"); 77 return false; 78 } 79 80 while ((l = read(s, buf, 4096)) > 0) { 81 base = buf; 82 do { 83 t = write(d, base, l); 84 if (t < 0) { 85 perror("failed backup write"); 86 return false; 87 } 88 base += t; 89 l -= t; 90 } while (l); 91 } 92 93 if (l < 0) { 94 perror("failed backup read"); 95 return false; 96 } 97 98 free(name); 99 free(buf); 100 close(d); 101 close(s); 102 103 return true; 104 } 105 106 struct objtool_file *objtool_open_read(const char *_objname) 107 { 108 if (objname) { 109 if (strcmp(objname, _objname)) { 110 WARN("won't handle more than one file at a time"); 111 return NULL; 112 } 113 return &file; 114 } 115 objname = _objname; 116 117 file.elf = elf_open_read(objname, O_RDWR); 118 if (!file.elf) 119 return NULL; 120 121 if (backup && !objtool_create_backup(objname)) { 122 WARN("can't create backup file"); 123 return NULL; 124 } 125 126 INIT_LIST_HEAD(&file.insn_list); 127 hash_init(file.insn_hash); 128 INIT_LIST_HEAD(&file.retpoline_call_list); 129 INIT_LIST_HEAD(&file.static_call_list); 130 INIT_LIST_HEAD(&file.mcount_loc_list); 131 file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment"); 132 file.ignore_unreachables = no_unreachable; 133 file.hints = false; 134 135 return &file; 136 } 137 138 void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) 139 { 140 if (!noinstr) 141 return; 142 143 if (!f->pv_ops) { 144 WARN("paravirt confusion"); 145 return; 146 } 147 148 /* 149 * These functions will be patched into native code, 150 * see paravirt_patch(). 151 */ 152 if (!strcmp(func->name, "_paravirt_nop") || 153 !strcmp(func->name, "_paravirt_ident_64")) 154 return; 155 156 /* already added this function */ 157 if (!list_empty(&func->pv_target)) 158 return; 159 160 list_add(&func->pv_target, &f->pv_ops[idx].targets); 161 f->pv_ops[idx].clean = false; 162 } 163 164 static void cmd_usage(void) 165 { 166 unsigned int i, longest = 0; 167 168 printf("\n usage: %s\n\n", objtool_usage_string); 169 170 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 171 if (longest < strlen(objtool_cmds[i].name)) 172 longest = strlen(objtool_cmds[i].name); 173 } 174 175 puts(" Commands:"); 176 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 177 printf(" %-*s ", longest, objtool_cmds[i].name); 178 puts(objtool_cmds[i].help); 179 } 180 181 printf("\n"); 182 183 if (!help) 184 exit(129); 185 exit(0); 186 } 187 188 static void handle_options(int *argc, const char ***argv) 189 { 190 while (*argc > 0) { 191 const char *cmd = (*argv)[0]; 192 193 if (cmd[0] != '-') 194 break; 195 196 if (!strcmp(cmd, "--help") || !strcmp(cmd, "-h")) { 197 help = true; 198 break; 199 } else { 200 fprintf(stderr, "Unknown option: %s\n", cmd); 201 cmd_usage(); 202 } 203 204 (*argv)++; 205 (*argc)--; 206 } 207 } 208 209 static void handle_internal_command(int argc, const char **argv) 210 { 211 const char *cmd = argv[0]; 212 unsigned int i, ret; 213 214 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 215 struct cmd_struct *p = objtool_cmds+i; 216 217 if (strcmp(p->name, cmd)) 218 continue; 219 220 ret = p->fn(argc, argv); 221 222 exit(ret); 223 } 224 225 cmd_usage(); 226 } 227 228 int main(int argc, const char **argv) 229 { 230 static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED"; 231 232 /* libsubcmd init */ 233 exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED); 234 pager_init(UNUSED); 235 236 argv++; 237 argc--; 238 handle_options(&argc, &argv); 239 240 if (!argc || help) 241 cmd_usage(); 242 243 handle_internal_command(argc, argv); 244 245 return 0; 246 } 247