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