xref: /illumos-gate/usr/src/test/util-tests/tests/ctf/check-merge-static.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2019, Joyent, Inc.
14  */
15 
16 /*
17  * Verify that various type information for static symbols is accurate for the
18  * file in question. To run this test, there's a global and static version of a
19  * symbol and function that exists on a per-file basis. These will all have been
20  * reproduced in the output file. As such, we need to iterate the symbol table
21  * to know which version should be which and use that to drive things.
22  */
23 
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <libelf.h>
28 #include <gelf.h>
29 #include <limits.h>
30 #include <strings.h>
31 
32 #include "check-common.h"
33 
34 typedef struct check_map {
35 	const char *map_file;
36 	const char *map_type;
37 } check_map_t;
38 
39 static const char *global_type = "int";
40 static check_map_t map[] = {
41 	{ "test-a.c", "uint8_t" },
42 	{ "test-b.c", "uint16_t" },
43 	{ "test-c.c", "uint32_t" },
44 	{ "test-d.c", "uint64_t" },
45 	{ NULL }
46 };
47 
48 static const char *
49 check_file_to_type(GElf_Sym *symp, const char *file, const char *name)
50 {
51 	uint_t i;
52 
53 	if (ELF32_ST_BIND(symp->st_info) == STB_GLOBAL) {
54 		return (global_type);
55 	}
56 
57 	if (file == NULL) {
58 		warnx("encountered non-global symbol without STT_FILE info: %s",
59 		    name);
60 		return (NULL);
61 	}
62 
63 	for (i = 0; map[i].map_file != NULL; i++) {
64 		if (strcmp(map[i].map_file, file) == 0) {
65 			return (map[i].map_type);
66 		}
67 	}
68 
69 	warnx("failed to find type mapping for symbol %s, file %s", name, file);
70 	return (NULL);
71 }
72 
73 static int
74 check_global(ctf_file_t *fp, GElf_Sym *symp, int symid, const char *file,
75     const char *name)
76 {
77 	const char *type;
78 	ctf_id_t tid;
79 	char buf[2048];
80 
81 	if ((type = check_file_to_type(symp, file, name)) == NULL) {
82 		return (EXIT_FAILURE);
83 	}
84 
85 	if ((tid = ctf_lookup_by_symbol(fp, symid)) == CTF_ERR) {
86 		warnx("failed to get type for symbol %s (%d): %s", name, symid,
87 		    ctf_errmsg(ctf_errno(fp)));
88 		return (EXIT_FAILURE);
89 	}
90 
91 	if (ctf_type_name(fp, tid, buf, sizeof (buf)) == NULL) {
92 		warnx("failed to get type name for symbol %s (%d): %s",
93 		    name, symid, ctf_errmsg(ctf_errno(fp)));
94 		return (EXIT_FAILURE);
95 	}
96 
97 	if (strcmp(buf, type) != 0) {
98 		warnx("type mismatch for symbol %s (%d): found %s, expected %s",
99 		    name, symid, buf, type);
100 		return (EXIT_FAILURE);
101 	}
102 
103 	return (0);
104 }
105 
106 static int
107 check_mumble(ctf_file_t *fp, GElf_Sym *symp, int symid, const char *file,
108     const char *name)
109 {
110 	const char *type;
111 	ctf_funcinfo_t fi;
112 	ctf_id_t id, args;
113 
114 	if ((type = check_file_to_type(symp, file, name)) == NULL) {
115 		return (EXIT_FAILURE);
116 	}
117 
118 	if ((id = ctf_lookup_by_name(fp, type)) == CTF_ERR) {
119 		warnx("failed to lookup type id for %s: %s", type,
120 		    ctf_errmsg(ctf_errno(fp)));
121 		return (EXIT_FAILURE);
122 	}
123 
124 	if (ctf_func_info(fp, symid, &fi) != 0) {
125 		warnx("failed to get function information for %s (%d): %s",
126 		    name, symid, ctf_errmsg(ctf_errno(fp)));
127 		return (EXIT_FAILURE);
128 	}
129 
130 	if (fi.ctc_argc != 1) {
131 		warnx("argument count mismatch for symbol %s (%d): found %u, "
132 		    "expected %d", name, symid, fi.ctc_argc, 1);
133 		return (EXIT_FAILURE);
134 	}
135 
136 	if (fi.ctc_flags != 0) {
137 		warnx("function flags mismatch for symbol %s (%d): found %u, "
138 		    "expected %d", name, symid, fi.ctc_flags, 0);
139 		return (EXIT_FAILURE);
140 	}
141 
142 	if (fi.ctc_return != id) {
143 		warnx("return value mismatch for symbol %s (%d): found %ld, "
144 		    "expected %ld", name, symid, fi.ctc_return, id);
145 		return (EXIT_FAILURE);
146 	}
147 
148 	if (ctf_func_args(fp, symid, 1, &args) != 0) {
149 		warnx("failed to get function arguments for symbol %s (%d): %s",
150 		    name, symid, ctf_errmsg(ctf_errno(fp)));
151 		return (EXIT_FAILURE);
152 	}
153 
154 	if (args != id) {
155 		warnx("argument mismatch for symbol %s (%d): found %ld, "
156 		    "expected %ld", name, symid, args, id);
157 		return (EXIT_FAILURE);
158 	}
159 
160 	return (0);
161 }
162 
163 static int
164 check_merge_static(const char *file, ctf_file_t *fp, Elf *elf)
165 {
166 	Elf_Scn *scn = NULL, *symscn = NULL;
167 	Elf_Data *symdata = NULL;
168 	GElf_Shdr symhdr;
169 	ulong_t nsyms;
170 	int i;
171 	const char *curfile = NULL;
172 	int ret = 0;
173 
174 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
175 		if (gelf_getshdr(scn, &symhdr) == NULL) {
176 			warnx("failed to get section header: %s",
177 			    elf_errmsg(elf_errno()));
178 			return (EXIT_FAILURE);
179 		}
180 
181 		if (symhdr.sh_type == SHT_SYMTAB) {
182 			symscn = scn;
183 			break;
184 		}
185 	}
186 
187 	if (symscn == NULL) {
188 		warnx("failed to find symbol table for %s", file);
189 		return (EXIT_FAILURE);
190 	}
191 
192 	if ((symdata = elf_getdata(symscn, NULL)) == NULL) {
193 		warnx("failed to get data for symbol table %s: %s", file,
194 		    elf_errmsg(elf_errno()));
195 		return (EXIT_FAILURE);
196 	}
197 
198 	if (symhdr.sh_link == SHN_XINDEX) {
199 		warnx("test does not support extended ELF sections!");
200 		return (EXIT_FAILURE);
201 	}
202 	nsyms = symhdr.sh_size / symhdr.sh_entsize;
203 	if (nsyms > INT_MAX) {
204 		warnx("file contains more symbols than libelf can iterate");
205 		return (EXIT_FAILURE);
206 	}
207 
208 	for (i = 1; i < (int)nsyms; i++) {
209 		GElf_Sym sym;
210 		const char *name;
211 		uint_t type;
212 
213 		if (gelf_getsym(symdata, i, &sym) == NULL) {
214 			warnx("failed to get data about symbol %d", i);
215 			return (EXIT_FAILURE);
216 		}
217 
218 		if ((name = elf_strptr(elf, symhdr.sh_link, sym.st_name)) ==
219 		    NULL) {
220 			warnx("failed to get name for symbol %d", i);
221 			return (EXIT_FAILURE);
222 		}
223 
224 		type = GELF_ST_TYPE(sym.st_info);
225 		if (type == STT_FILE) {
226 			curfile = name;
227 			continue;
228 		}
229 
230 		if (strcmp(name, "global") == 0) {
231 			ret |= check_global(fp, &sym, i, curfile, name);
232 		} else if (strcmp(name, "mumble") == 0) {
233 			ret |= check_mumble(fp, &sym, i, curfile, name);
234 		}
235 	}
236 
237 	return (ret);
238 }
239 
240 int
241 main(int argc, char *argv[])
242 {
243 	int i, ret = 0;
244 
245 	if (argc < 2) {
246 		errx(EXIT_FAILURE, "missing test files");
247 	}
248 
249 	if (elf_version(EV_CURRENT) == EV_NONE) {
250 		errx(EXIT_FAILURE, "failed to initialize libelf");
251 	}
252 
253 	for (i = 1; i < argc; i++) {
254 		int fd;
255 		ctf_file_t *fp;
256 		Elf *elf;
257 
258 		if ((fd = open(argv[i], O_RDONLY)) < 0) {
259 			warn("failed to open %s", argv[i]);
260 			ret = EXIT_FAILURE;
261 			continue;
262 		}
263 
264 		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
265 			warnx("failed to open libelf handle to %s", argv[i]);
266 			ret = EXIT_FAILURE;
267 			(void) close(fd);
268 			continue;
269 		}
270 
271 		if ((fp = ctf_open(argv[i], &ret)) == NULL) {
272 			warnx("failed to open %s: %s", argv[i],
273 			    ctf_errmsg(ret));
274 			ret = EXIT_FAILURE;
275 			(void) close(fd);
276 			(void) elf_end(elf);
277 			continue;
278 		}
279 
280 		if (check_merge_static(argv[i], fp, elf) != 0) {
281 			ret = EXIT_FAILURE;
282 		}
283 
284 		ctf_close(fp);
285 		(void) close(fd);
286 		(void) elf_end(elf);
287 	}
288 
289 	return (ret);
290 }
291