pmdinfogen: fix build warnings
[dpdk.git] / buildtools / pmdinfogen / pmdinfogen.c
1 /* Postprocess pmd object files to export hw support
2  *
3  * Copyright 2016 Neil Horman <nhorman@tuxdriver.com>
4  * Based in part on modpost.c from the linux kernel
5  *
6  * This software may be used and distributed according to the terms
7  * of the GNU General Public License V2, incorporated herein by reference.
8  *
9  */
10
11 #define _GNU_SOURCE
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <limits.h>
16 #include <stdbool.h>
17 #include <errno.h>
18 #include <rte_common.h>
19 #include "pmdinfogen.h"
20
21 #ifdef RTE_ARCH_64
22 #define ADDR_SIZE 64
23 #else
24 #define ADDR_SIZE 32
25 #endif
26
27
28 static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
29 {
30         if (sym)
31                 return elf->strtab + sym->st_name;
32         else
33                 return "(unknown)";
34 }
35
36 static void *grab_file(const char *filename, unsigned long *size)
37 {
38         struct stat st;
39         void *map = MAP_FAILED;
40         int fd;
41
42         fd = open(filename, O_RDONLY);
43         if (fd < 0)
44                 return NULL;
45         if (fstat(fd, &st))
46                 goto failed;
47
48         *size = st.st_size;
49         map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
50
51 failed:
52         close(fd);
53         if (map == MAP_FAILED)
54                 return NULL;
55         return map;
56 }
57
58 /**
59   * Return a copy of the next line in a mmap'ed file.
60   * spaces in the beginning of the line is trimmed away.
61   * Return a pointer to a static buffer.
62   **/
63 static void release_file(void *file, unsigned long size)
64 {
65         munmap(file, size);
66 }
67
68
69 static void *get_sym_value(struct elf_info *info, const Elf_Sym *sym)
70 {
71         return RTE_PTR_ADD(info->hdr,
72                 info->sechdrs[sym->st_shndx].sh_offset + sym->st_value);
73 }
74
75 static Elf_Sym *find_sym_in_symtab(struct elf_info *info,
76                                    const char *name, Elf_Sym *last)
77 {
78         Elf_Sym *idx;
79         if (last)
80                 idx = last+1;
81         else
82                 idx = info->symtab_start;
83
84         for (; idx < info->symtab_stop; idx++) {
85                 const char *n = sym_name(info, idx);
86                 if (!strncmp(n, name, strlen(name)))
87                         return idx;
88         }
89         return NULL;
90 }
91
92 static int parse_elf(struct elf_info *info, const char *filename)
93 {
94         unsigned int i;
95         Elf_Ehdr *hdr;
96         Elf_Shdr *sechdrs;
97         Elf_Sym  *sym;
98         int endian;
99         unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U;
100
101         hdr = grab_file(filename, &info->size);
102         if (!hdr) {
103                 perror(filename);
104                 exit(1);
105         }
106         info->hdr = hdr;
107         if (info->size < sizeof(*hdr)) {
108                 /* file too small, assume this is an empty .o file */
109                 return 0;
110         }
111         /* Is this a valid ELF file? */
112         if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
113             (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
114             (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
115             (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
116                 /* Not an ELF file - silently ignore it */
117                 return 0;
118         }
119
120         if (!hdr->e_ident[EI_DATA]) {
121                 /* Unknown endian */
122                 return 0;
123         }
124
125         endian = hdr->e_ident[EI_DATA];
126
127         /* Fix endianness in ELF header */
128         hdr->e_type      = TO_NATIVE(endian, 16, hdr->e_type);
129         hdr->e_machine   = TO_NATIVE(endian, 16, hdr->e_machine);
130         hdr->e_version   = TO_NATIVE(endian, 32, hdr->e_version);
131         hdr->e_entry     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_entry);
132         hdr->e_phoff     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_phoff);
133         hdr->e_shoff     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_shoff);
134         hdr->e_flags     = TO_NATIVE(endian, 32, hdr->e_flags);
135         hdr->e_ehsize    = TO_NATIVE(endian, 16, hdr->e_ehsize);
136         hdr->e_phentsize = TO_NATIVE(endian, 16, hdr->e_phentsize);
137         hdr->e_phnum     = TO_NATIVE(endian, 16, hdr->e_phnum);
138         hdr->e_shentsize = TO_NATIVE(endian, 16, hdr->e_shentsize);
139         hdr->e_shnum     = TO_NATIVE(endian, 16, hdr->e_shnum);
140         hdr->e_shstrndx  = TO_NATIVE(endian, 16, hdr->e_shstrndx);
141
142         sechdrs = RTE_PTR_ADD(hdr, hdr->e_shoff);
143         info->sechdrs = sechdrs;
144
145         /* Check if file offset is correct */
146         if (hdr->e_shoff > info->size) {
147                 fprintf(stderr, "section header offset=%lu in file '%s' "
148                       "is bigger than filesize=%lu\n",
149                       (unsigned long)hdr->e_shoff,
150                       filename, info->size);
151                 return 0;
152         }
153
154         if (hdr->e_shnum == SHN_UNDEF) {
155                 /*
156                  * There are more than 64k sections,
157                  * read count from .sh_size.
158                  */
159                 info->num_sections = TO_NATIVE(endian, 32, sechdrs[0].sh_size);
160         } else {
161                 info->num_sections = hdr->e_shnum;
162         }
163         if (hdr->e_shstrndx == SHN_XINDEX)
164                 info->secindex_strings =
165                         TO_NATIVE(endian, 32, sechdrs[0].sh_link);
166         else
167                 info->secindex_strings = hdr->e_shstrndx;
168
169         /* Fix endianness in section headers */
170         for (i = 0; i < info->num_sections; i++) {
171                 sechdrs[i].sh_name      =
172                         TO_NATIVE(endian, 32, sechdrs[i].sh_name);
173                 sechdrs[i].sh_type      =
174                         TO_NATIVE(endian, 32, sechdrs[i].sh_type);
175                 sechdrs[i].sh_flags     =
176                         TO_NATIVE(endian, 32, sechdrs[i].sh_flags);
177                 sechdrs[i].sh_addr      =
178                         TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_addr);
179                 sechdrs[i].sh_offset    =
180                         TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_offset);
181                 sechdrs[i].sh_size      =
182                         TO_NATIVE(endian, 32, sechdrs[i].sh_size);
183                 sechdrs[i].sh_link      =
184                         TO_NATIVE(endian, 32, sechdrs[i].sh_link);
185                 sechdrs[i].sh_info      =
186                         TO_NATIVE(endian, 32, sechdrs[i].sh_info);
187                 sechdrs[i].sh_addralign =
188                         TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_addralign);
189                 sechdrs[i].sh_entsize   =
190                         TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_entsize);
191         }
192         /* Find symbol table. */
193         for (i = 1; i < info->num_sections; i++) {
194                 int nobits = sechdrs[i].sh_type == SHT_NOBITS;
195
196                 if (!nobits && sechdrs[i].sh_offset > info->size) {
197                         fprintf(stderr, "%s is truncated. "
198                               "sechdrs[i].sh_offset=%lu > sizeof(*hrd)=%zu\n",
199                               filename, (unsigned long)sechdrs[i].sh_offset,
200                               sizeof(*hdr));
201                         return 0;
202                 }
203
204                 if (sechdrs[i].sh_type == SHT_SYMTAB) {
205                         unsigned int sh_link_idx;
206                         symtab_idx = i;
207                         info->symtab_start = RTE_PTR_ADD(hdr,
208                                 sechdrs[i].sh_offset);
209                         info->symtab_stop  = RTE_PTR_ADD(hdr,
210                                 sechdrs[i].sh_offset + sechdrs[i].sh_size);
211                         sh_link_idx = sechdrs[i].sh_link;
212                         info->strtab       = RTE_PTR_ADD(hdr,
213                                 sechdrs[sh_link_idx].sh_offset);
214                 }
215
216                 /* 32bit section no. table? ("more than 64k sections") */
217                 if (sechdrs[i].sh_type == SHT_SYMTAB_SHNDX) {
218                         symtab_shndx_idx = i;
219                         info->symtab_shndx_start = RTE_PTR_ADD(hdr,
220                                 sechdrs[i].sh_offset);
221                         info->symtab_shndx_stop  = RTE_PTR_ADD(hdr,
222                                 sechdrs[i].sh_offset + sechdrs[i].sh_size);
223                 }
224         }
225         if (!info->symtab_start)
226                 fprintf(stderr, "%s has no symtab?\n", filename);
227
228         /* Fix endianness in symbols */
229         for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
230                 sym->st_shndx = TO_NATIVE(endian, 16, sym->st_shndx);
231                 sym->st_name  = TO_NATIVE(endian, 32, sym->st_name);
232                 sym->st_value = TO_NATIVE(endian, ADDR_SIZE, sym->st_value);
233                 sym->st_size  = TO_NATIVE(endian, ADDR_SIZE, sym->st_size);
234         }
235
236         if (symtab_shndx_idx != ~0U) {
237                 Elf32_Word *p;
238                 if (symtab_idx != sechdrs[symtab_shndx_idx].sh_link)
239                         fprintf(stderr,
240                               "%s: SYMTAB_SHNDX has bad sh_link: %u!=%u\n",
241                               filename, sechdrs[symtab_shndx_idx].sh_link,
242                               symtab_idx);
243                 /* Fix endianness */
244                 for (p = info->symtab_shndx_start; p < info->symtab_shndx_stop;
245                      p++)
246                         *p = TO_NATIVE(endian, 32, *p);
247         }
248
249         return 1;
250 }
251
252 static void parse_elf_finish(struct elf_info *info)
253 {
254         struct pmd_driver *tmp, *idx = info->drivers;
255         release_file(info->hdr, info->size);
256         while (idx) {
257                 tmp = idx->next;
258                 free(idx);
259                 idx = tmp;
260         }
261 }
262
263 struct opt_tag {
264         const char *suffix;
265         const char *json_id;
266 };
267
268 static const struct opt_tag opt_tags[] = {
269         {"_param_string_export", "params"},
270 };
271
272 static int complete_pmd_entry(struct elf_info *info, struct pmd_driver *drv)
273 {
274         const char *tname;
275         int i;
276         char tmpsymname[128];
277         Elf_Sym *tmpsym;
278
279         drv->name = get_sym_value(info, drv->name_sym);
280
281         for (i = 0; i < PMD_OPT_MAX; i++) {
282                 memset(tmpsymname, 0, 128);
283                 sprintf(tmpsymname, "__%s%s", drv->name, opt_tags[i].suffix);
284                 tmpsym = find_sym_in_symtab(info, tmpsymname, NULL);
285                 if (!tmpsym)
286                         continue;
287                 drv->opt_vals[i] = get_sym_value(info, tmpsym);
288         }
289
290         memset(tmpsymname, 0, 128);
291         sprintf(tmpsymname, "__%s_pci_tbl_export", drv->name);
292
293         tmpsym = find_sym_in_symtab(info, tmpsymname, NULL);
294
295
296         /*
297          * If this returns NULL, then this is a PMD_VDEV, because
298          * it has no pci table reference
299          */
300         if (!tmpsym) {
301                 drv->pci_tbl = NULL;
302                 return 0;
303         }
304
305         tname = get_sym_value(info, tmpsym);
306         tmpsym = find_sym_in_symtab(info, tname, NULL);
307         if (!tmpsym)
308                 return -ENOENT;
309
310         drv->pci_tbl = (struct rte_pci_id *)get_sym_value(info, tmpsym);
311         if (!drv->pci_tbl)
312                 return -ENOENT;
313
314         return 0;
315 }
316
317 static int locate_pmd_entries(struct elf_info *info)
318 {
319         Elf_Sym *last = NULL;
320         struct pmd_driver *new;
321
322         info->drivers = NULL;
323
324         do {
325                 new = calloc(sizeof(struct pmd_driver), 1);
326                 new->name_sym = find_sym_in_symtab(info, "this_pmd_name", last);
327                 last = new->name_sym;
328                 if (!new->name_sym)
329                         free(new);
330                 else {
331                         if (complete_pmd_entry(info, new)) {
332                                 fprintf(stderr,
333                                         "Failed to complete pmd entry\n");
334                                 free(new);
335                         } else {
336                                 new->next = info->drivers;
337                                 info->drivers = new;
338                         }
339                 }
340         } while (last);
341
342         return 0;
343 }
344
345 static void output_pmd_info_string(struct elf_info *info, char *outfile)
346 {
347         FILE *ofd;
348         struct pmd_driver *drv;
349         struct rte_pci_id *pci_ids;
350         int idx = 0;
351
352         ofd = fopen(outfile, "w+");
353         if (!ofd) {
354                 fprintf(stderr, "Unable to open output file\n");
355                 return;
356         }
357
358         drv = info->drivers;
359
360         while (drv) {
361                 fprintf(ofd, "const char %s_pmd_info[] __attribute__((used)) = "
362                         "\"PMD_INFO_STRING= {",
363                         drv->name);
364                 fprintf(ofd, "\\\"name\\\" : \\\"%s\\\", ", drv->name);
365
366                 for (idx = 0; idx < PMD_OPT_MAX; idx++) {
367                         if (drv->opt_vals[idx])
368                                 fprintf(ofd, "\\\"%s\\\" : \\\"%s\\\", ",
369                                         opt_tags[idx].json_id,
370                                         drv->opt_vals[idx]);
371                 }
372
373                 pci_ids = drv->pci_tbl;
374                 fprintf(ofd, "\\\"pci_ids\\\" : [");
375
376                 while (pci_ids && pci_ids->device_id) {
377                         fprintf(ofd, "[%d, %d, %d, %d]",
378                                 pci_ids->vendor_id, pci_ids->device_id,
379                                 pci_ids->subsystem_vendor_id,
380                                 pci_ids->subsystem_device_id);
381                         pci_ids++;
382                         if (pci_ids->device_id)
383                                 fprintf(ofd, ",");
384                         else
385                                 fprintf(ofd, " ");
386                 }
387                 fprintf(ofd, "]}\";");
388                 drv = drv->next;
389         }
390
391         fclose(ofd);
392 }
393
394 int main(int argc, char **argv)
395 {
396         struct elf_info info;
397         int rc = 1;
398
399         if (argc < 3) {
400                 fprintf(stderr,
401                         "usage: pmdinfo <object file> <c output file>\n");
402                 exit(127);
403         }
404         parse_elf(&info, argv[1]);
405
406         locate_pmd_entries(&info);
407
408         if (info.drivers) {
409                 output_pmd_info_string(&info, argv[2]);
410                 rc = 0;
411         } else {
412                 fprintf(stderr, "No drivers registered\n");
413         }
414
415         parse_elf_finish(&info);
416         exit(rc);
417 }