xref: /openbmc/linux/tools/objtool/objtool.c (revision 35267cea)
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 static void cmd_usage(void)
139 {
140 	unsigned int i, longest = 0;
141 
142 	printf("\n usage: %s\n\n", objtool_usage_string);
143 
144 	for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) {
145 		if (longest < strlen(objtool_cmds[i].name))
146 			longest = strlen(objtool_cmds[i].name);
147 	}
148 
149 	puts(" Commands:");
150 	for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) {
151 		printf("   %-*s   ", longest, objtool_cmds[i].name);
152 		puts(objtool_cmds[i].help);
153 	}
154 
155 	printf("\n");
156 
157 	if (!help)
158 		exit(129);
159 	exit(0);
160 }
161 
162 static void handle_options(int *argc, const char ***argv)
163 {
164 	while (*argc > 0) {
165 		const char *cmd = (*argv)[0];
166 
167 		if (cmd[0] != '-')
168 			break;
169 
170 		if (!strcmp(cmd, "--help") || !strcmp(cmd, "-h")) {
171 			help = true;
172 			break;
173 		} else {
174 			fprintf(stderr, "Unknown option: %s\n", cmd);
175 			cmd_usage();
176 		}
177 
178 		(*argv)++;
179 		(*argc)--;
180 	}
181 }
182 
183 static void handle_internal_command(int argc, const char **argv)
184 {
185 	const char *cmd = argv[0];
186 	unsigned int i, ret;
187 
188 	for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) {
189 		struct cmd_struct *p = objtool_cmds+i;
190 
191 		if (strcmp(p->name, cmd))
192 			continue;
193 
194 		ret = p->fn(argc, argv);
195 
196 		exit(ret);
197 	}
198 
199 	cmd_usage();
200 }
201 
202 int main(int argc, const char **argv)
203 {
204 	static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED";
205 
206 	/* libsubcmd init */
207 	exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED);
208 	pager_init(UNUSED);
209 
210 	argv++;
211 	argc--;
212 	handle_options(&argc, &argv);
213 
214 	if (!argc || help)
215 		cmd_usage();
216 
217 	handle_internal_command(argc, argv);
218 
219 	return 0;
220 }
221