xref: /openbmc/linux/tools/objtool/objtool.c (revision 4b33b5ff)
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.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