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