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 *
check_file_to_type(GElf_Sym * symp,const char * file,const char * name)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
check_global(ctf_file_t * fp,GElf_Sym * symp,int symid,const char * file,const char * name)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
check_mumble(ctf_file_t * fp,GElf_Sym * symp,int symid,const char * file,const char * name)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
check_merge_static(const char * file,ctf_file_t * fp,Elf * elf)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
main(int argc,char * argv[])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