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