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 list_add(&func->pv_target, &f->pv_ops[idx].targets); 157 f->pv_ops[idx].clean = false; 158 } 159 160 static void cmd_usage(void) 161 { 162 unsigned int i, longest = 0; 163 164 printf("\n usage: %s\n\n", objtool_usage_string); 165 166 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 167 if (longest < strlen(objtool_cmds[i].name)) 168 longest = strlen(objtool_cmds[i].name); 169 } 170 171 puts(" Commands:"); 172 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 173 printf(" %-*s ", longest, objtool_cmds[i].name); 174 puts(objtool_cmds[i].help); 175 } 176 177 printf("\n"); 178 179 if (!help) 180 exit(129); 181 exit(0); 182 } 183 184 static void handle_options(int *argc, const char ***argv) 185 { 186 while (*argc > 0) { 187 const char *cmd = (*argv)[0]; 188 189 if (cmd[0] != '-') 190 break; 191 192 if (!strcmp(cmd, "--help") || !strcmp(cmd, "-h")) { 193 help = true; 194 break; 195 } else { 196 fprintf(stderr, "Unknown option: %s\n", cmd); 197 cmd_usage(); 198 } 199 200 (*argv)++; 201 (*argc)--; 202 } 203 } 204 205 static void handle_internal_command(int argc, const char **argv) 206 { 207 const char *cmd = argv[0]; 208 unsigned int i, ret; 209 210 for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) { 211 struct cmd_struct *p = objtool_cmds+i; 212 213 if (strcmp(p->name, cmd)) 214 continue; 215 216 ret = p->fn(argc, argv); 217 218 exit(ret); 219 } 220 221 cmd_usage(); 222 } 223 224 int main(int argc, const char **argv) 225 { 226 static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED"; 227 228 /* libsubcmd init */ 229 exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED); 230 pager_init(UNUSED); 231 232 argv++; 233 argc--; 234 handle_options(&argc, &argv); 235 236 if (!argc || help) 237 cmd_usage(); 238 239 handle_internal_command(argc, argv); 240 241 return 0; 242 } 243