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 INIT_LIST_HEAD(&file.endbr_list); 132 file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment"); 133 file.ignore_unreachables = no_unreachable; 134 file.hints = false; 135 136 return &file; 137 } 138 139 void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) 140 { 141 if (!noinstr) 142 return; 143 144 if (!f->pv_ops) { 145 WARN("paravirt confusion"); 146 return; 147 } 148 149 /* 150 * These functions will be patched into native code, 151 * see paravirt_patch(). 152 */ 153 if (!strcmp(func->name, "_paravirt_nop") || 154 !strcmp(func->name, "_paravirt_ident_64")) 155 return; 156 157 /* already added this function */ 158 if (!list_empty(&func->pv_target)) 159 return; 160 161 list_add(&func->pv_target, &f->pv_ops[idx].targets); 162 f->pv_ops[idx].clean = false; 163 } 164 165 static void cmd_usage(void) 166 { 167 unsigned int i, longest = 0; 168 169 printf("\n usage: %s\n\n", objtool_usage_string); 170 171 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 172 if (longest < strlen(objtool_cmds[i].name)) 173 longest = strlen(objtool_cmds[i].name); 174 } 175 176 puts(" Commands:"); 177 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 178 printf(" %-*s ", longest, objtool_cmds[i].name); 179 puts(objtool_cmds[i].help); 180 } 181 182 printf("\n"); 183 184 if (!help) 185 exit(129); 186 exit(0); 187 } 188 189 static void handle_options(int *argc, const char ***argv) 190 { 191 while (*argc > 0) { 192 const char *cmd = (*argv)[0]; 193 194 if (cmd[0] != '-') 195 break; 196 197 if (!strcmp(cmd, "--help") || !strcmp(cmd, "-h")) { 198 help = true; 199 break; 200 } else { 201 fprintf(stderr, "Unknown option: %s\n", cmd); 202 cmd_usage(); 203 } 204 205 (*argv)++; 206 (*argc)--; 207 } 208 } 209 210 static void handle_internal_command(int argc, const char **argv) 211 { 212 const char *cmd = argv[0]; 213 unsigned int i, ret; 214 215 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 216 struct cmd_struct *p = objtool_cmds+i; 217 218 if (strcmp(p->name, cmd)) 219 continue; 220 221 ret = p->fn(argc, argv); 222 223 exit(ret); 224 } 225 226 cmd_usage(); 227 } 228 229 int main(int argc, const char **argv) 230 { 231 static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED"; 232 233 /* libsubcmd init */ 234 exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED); 235 pager_init(UNUSED); 236 237 argv++; 238 argc--; 239 handle_options(&argc, &argv); 240 241 if (!argc || help) 242 cmd_usage(); 243 244 handle_internal_command(argc, argv); 245 246 return 0; 247 } 248