[dpdk-dev,v3,5/9] pmdinfogen: move to drivers subdirectory

Message ID 78a1d2393d08ec3152f01be35d29401447ce616f.1497999952.git.gaetan.rivet@6wind.com (mailing list archive)
State Rejected, archived
Headers

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/Intel-compilation fail apply patch file failure

Commit Message

Gaëtan Rivet June 20, 2017, 11:36 p.m. UTC
  pmdinfogen has a dependency on the PCI bus. The latter must be built
first.

Signed-off-by: Gaetan Rivet <gaetan.rivet@6wind.com>
---
 GNUmakefile                        |   2 +-
 MAINTAINERS                        |   2 +-
 buildtools/Makefile                |  36 ----
 buildtools/pmdinfogen/Makefile     |  47 -----
 buildtools/pmdinfogen/pmdinfogen.c | 422 -------------------------------------
 buildtools/pmdinfogen/pmdinfogen.h | 125 -----------
 drivers/Makefile                   |   4 +-
 drivers/pmdinfogen/Makefile        |  47 +++++
 drivers/pmdinfogen/pmdinfogen.c    | 422 +++++++++++++++++++++++++++++++++++++
 drivers/pmdinfogen/pmdinfogen.h    | 125 +++++++++++
 10 files changed, 599 insertions(+), 633 deletions(-)
 delete mode 100644 buildtools/Makefile
 delete mode 100644 buildtools/pmdinfogen/Makefile
 delete mode 100644 buildtools/pmdinfogen/pmdinfogen.c
 delete mode 100644 buildtools/pmdinfogen/pmdinfogen.h
 create mode 100644 drivers/pmdinfogen/Makefile
 create mode 100644 drivers/pmdinfogen/pmdinfogen.c
 create mode 100644 drivers/pmdinfogen/pmdinfogen.h
  

Comments

Thomas Monjalon June 21, 2017, 7:57 a.m. UTC | #1
21/06/2017 01:36, Gaetan Rivet:
> pmdinfogen has a dependency on the PCI bus. The latter must be built
> first.

I think it should not be moved outside of buildtools.

The build order can be fixed by installing headers before any compilation
like proposed in this RFC: http://dpdk.org/patch/25463

Another (probably better) solution is to keep basic definitions
and helpers in EAL:
	- rte_pci.h keeps only some PCI definitions and helpers
like rte_pci_addr and eal_parse_pci_BDF() in EAL
	- bus management is done in the PCI driver

For pmdinfogen, we just need struct rte_pci_id.
Other tools or applications will probably need this kind of basic
struct and functions available in EAL.
  
Gaëtan Rivet June 21, 2017, 9:40 a.m. UTC | #2
On Wed, Jun 21, 2017 at 09:57:18AM +0200, Thomas Monjalon wrote:
> 21/06/2017 01:36, Gaetan Rivet:
> > pmdinfogen has a dependency on the PCI bus. The latter must be built
> > first.
> 
> I think it should not be moved outside of buildtools.
> 

Right, I agree. I took the simplest path here, but if there is a better
solution to be found I'd prefer to use it.

> The build order can be fixed by installing headers before any compilation
> like proposed in this RFC: http://dpdk.org/patch/25463
> 

This RFC should be discussed on its own merit, I will chime in the
relevant thread.

> Another (probably better) solution is to keep basic definitions
> and helpers in EAL:
> 	- rte_pci.h keeps only some PCI definitions and helpers
> like rte_pci_addr and eal_parse_pci_BDF() in EAL
> 	- bus management is done in the PCI driver
> 
> For pmdinfogen, we just need struct rte_pci_id.
> Other tools or applications will probably need this kind of basic
> struct and functions available in EAL.

I mostly agree, this proposal should be kept to a minimum at first for
this release and carefully expanded afterward.

If that's ok, I will propose a new version of this patchset with a new
librte_pci, that might fix both pmdinfogen and librte_kni.
  
Thomas Monjalon June 21, 2017, 10 a.m. UTC | #3
21/06/2017 11:40, Gaëtan Rivet:
> On Wed, Jun 21, 2017 at 09:57:18AM +0200, Thomas Monjalon wrote:
> > Another (probably better) solution is to keep basic definitions
> > and helpers in EAL:
> > 	- rte_pci.h keeps only some PCI definitions and helpers
> > like rte_pci_addr and eal_parse_pci_BDF() in EAL
> > 	- bus management is done in the PCI driver
> > 
> > For pmdinfogen, we just need struct rte_pci_id.
> > Other tools or applications will probably need this kind of basic
> > struct and functions available in EAL.
> 
> I mostly agree, this proposal should be kept to a minimum at first for
> this release and carefully expanded afterward.
> 
> If that's ok, I will propose a new version of this patchset with a new
> librte_pci, that might fix both pmdinfogen and librte_kni.

Why creating a new librte_pci instead of just keeping it in EAL?
  
Gaëtan Rivet June 21, 2017, 11:39 a.m. UTC | #4
On Wed, Jun 21, 2017 at 12:00:24PM +0200, Thomas Monjalon wrote:
> 21/06/2017 11:40, Gaëtan Rivet:
> > On Wed, Jun 21, 2017 at 09:57:18AM +0200, Thomas Monjalon wrote:
> > > Another (probably better) solution is to keep basic definitions
> > > and helpers in EAL:
> > > 	- rte_pci.h keeps only some PCI definitions and helpers
> > > like rte_pci_addr and eal_parse_pci_BDF() in EAL
> > > 	- bus management is done in the PCI driver
> > > 
> > > For pmdinfogen, we just need struct rte_pci_id.
> > > Other tools or applications will probably need this kind of basic
> > > struct and functions available in EAL.
> > 
> > I mostly agree, this proposal should be kept to a minimum at first for
> > this release and carefully expanded afterward.
> > 
> > If that's ok, I will propose a new version of this patchset with a new
> > librte_pci, that might fix both pmdinfogen and librte_kni.
> 
> Why creating a new librte_pci instead of just keeping it in EAL?

While I agree that it makes sense to have PCI helpers shared among
several subsystems, I do not see a reason for the EAL to rely on it.

The EAL is the bedrock of the whole system. Having those helpers within
would mean that one expects them to be used to build this bedrock. It
would be misleading.

In the context of a framework, aimed at being used by others, an okay
architecture is one that works. A good architecture is one that
intrinsically convey meaning and explains its goal to developers relying
on it. I think that having this PCI lib within EAL just because nothing
prevents us from doing so is mistaken, in this regard.

Conversely, the argument about being conservative in the changes,
especially to an essential part such as the EAL, is obsolete for this
release and this subsystem, as deep changes are necessary anyway,
and the design should be right from the get go to allow further stability.

Finally, the PCI lib and bus is also an example for other developers. I
do not think that all other hardware buses should be allowed in adding
their own specific helpers to the EAL. In this regard, there is no
reason to have an exception made just for the PCI lib.
  
Thomas Monjalon June 21, 2017, 12:14 p.m. UTC | #5
21/06/2017 13:39, Gaëtan Rivet:
> On Wed, Jun 21, 2017 at 12:00:24PM +0200, Thomas Monjalon wrote:
> > 21/06/2017 11:40, Gaëtan Rivet:
> > > On Wed, Jun 21, 2017 at 09:57:18AM +0200, Thomas Monjalon wrote:
> > > > Another (probably better) solution is to keep basic definitions
> > > > and helpers in EAL:
> > > > 	- rte_pci.h keeps only some PCI definitions and helpers
> > > > like rte_pci_addr and eal_parse_pci_BDF() in EAL
> > > > 	- bus management is done in the PCI driver
> > > > 
> > > > For pmdinfogen, we just need struct rte_pci_id.
> > > > Other tools or applications will probably need this kind of basic
> > > > struct and functions available in EAL.
> > > 
> > > I mostly agree, this proposal should be kept to a minimum at first for
> > > this release and carefully expanded afterward.
> > > 
> > > If that's ok, I will propose a new version of this patchset with a new
> > > librte_pci, that might fix both pmdinfogen and librte_kni.
> > 
> > Why creating a new librte_pci instead of just keeping it in EAL?
> 
> While I agree that it makes sense to have PCI helpers shared among
> several subsystems, I do not see a reason for the EAL to rely on it.
> 
> The EAL is the bedrock of the whole system. Having those helpers within
> would mean that one expects them to be used to build this bedrock. It
> would be misleading.
> 
> In the context of a framework, aimed at being used by others, an okay
> architecture is one that works. A good architecture is one that
> intrinsically convey meaning and explains its goal to developers relying
> on it. I think that having this PCI lib within EAL just because nothing
> prevents us from doing so is mistaken, in this regard.
> 
> Conversely, the argument about being conservative in the changes,
> especially to an essential part such as the EAL, is obsolete for this
> release and this subsystem, as deep changes are necessary anyway,
> and the design should be right from the get go to allow further stability.
> 
> Finally, the PCI lib and bus is also an example for other developers. I
> do not think that all other hardware buses should be allowed in adding
> their own specific helpers to the EAL. In this regard, there is no
> reason to have an exception made just for the PCI lib.

You get a point :)
  

Patch

diff --git a/GNUmakefile b/GNUmakefile
index 45b7fbb..c292646 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -40,7 +40,7 @@  export RTE_SDK
 # directory list
 #
 
-ROOTDIRS-y := buildtools lib drivers app
+ROOTDIRS-y := lib drivers app
 ROOTDIRS-  := test
 
 include $(RTE_SDK)/mk/rte.sdkroot.mk
diff --git a/MAINTAINERS b/MAINTAINERS
index f6095ef..c8c57cb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -72,7 +72,7 @@  F: doc/guides/rel_notes/deprecation.rst
 F: devtools/validate-abi.sh
 
 Driver information
-F: buildtools/pmdinfogen/
+F: drivers/pmdinfogen/
 F: usertools/dpdk-pmdinfo.py
 F: doc/guides/tools/pmdinfo.rst
 
diff --git a/buildtools/Makefile b/buildtools/Makefile
deleted file mode 100644
index 35a42ff..0000000
--- a/buildtools/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@ 
-#   BSD LICENSE
-#
-#   Copyright(c) 2016 Neil Horman. All rights reserved.
-#   All rights reserved.
-#
-#   Redistribution and use in source and binary forms, with or without
-#   modification, are permitted provided that the following conditions
-#   are met:
-#
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above copyright
-#       notice, this list of conditions and the following disclaimer in
-#       the documentation and/or other materials provided with the
-#       distribution.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may be used to endorse or promote products derived
-#       from this software without specific prior written permission.
-#
-#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-DIRS-y += pmdinfogen
-
-include $(RTE_SDK)/mk/rte.subdir.mk
diff --git a/buildtools/pmdinfogen/Makefile b/buildtools/pmdinfogen/Makefile
deleted file mode 100644
index bf07b6f..0000000
--- a/buildtools/pmdinfogen/Makefile
+++ /dev/null
@@ -1,47 +0,0 @@ 
-#   BSD LICENSE
-#
-#   Copyright(c) 2016 Neil Horman. All rights reserved.
-#   All rights reserved.
-#
-#   Redistribution and use in source and binary forms, with or without
-#   modification, are permitted provided that the following conditions
-#   are met:
-#
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above copyright
-#       notice, this list of conditions and the following disclaimer in
-#       the documentation and/or other materials provided with the
-#       distribution.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may be used to endorse or promote products derived
-#       from this software without specific prior written permission.
-#
-#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-#
-# library name
-#
-HOSTAPP = dpdk-pmdinfogen
-
-#
-# all sources are stored in SRCS-y
-#
-SRCS-y += pmdinfogen.c
-
-HOST_CFLAGS += $(WERROR_FLAGS) -g
-HOST_CFLAGS += -I$(RTE_OUTPUT)/include
-
-include $(RTE_SDK)/mk/rte.hostapp.mk
diff --git a/buildtools/pmdinfogen/pmdinfogen.c b/buildtools/pmdinfogen/pmdinfogen.c
deleted file mode 100644
index ba1a12e..0000000
--- a/buildtools/pmdinfogen/pmdinfogen.c
+++ /dev/null
@@ -1,422 +0,0 @@ 
-/* Postprocess pmd object files to export hw support
- *
- * Copyright 2016 Neil Horman <nhorman@tuxdriver.com>
- * Based in part on modpost.c from the linux kernel
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License V2, incorporated herein by reference.
- *
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <libgen.h>
-
-#include <rte_common.h>
-#include "pmdinfogen.h"
-
-#ifdef RTE_ARCH_64
-#define ADDR_SIZE 64
-#else
-#define ADDR_SIZE 32
-#endif
-
-
-static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
-{
-	if (sym)
-		return elf->strtab + sym->st_name;
-	else
-		return "(unknown)";
-}
-
-static void *grab_file(const char *filename, unsigned long *size)
-{
-	struct stat st;
-	void *map = MAP_FAILED;
-	int fd;
-
-	fd = open(filename, O_RDONLY);
-	if (fd < 0)
-		return NULL;
-	if (fstat(fd, &st))
-		goto failed;
-
-	*size = st.st_size;
-	map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
-
-failed:
-	close(fd);
-	if (map == MAP_FAILED)
-		return NULL;
-	return map;
-}
-
-/**
-  * Return a copy of the next line in a mmap'ed file.
-  * spaces in the beginning of the line is trimmed away.
-  * Return a pointer to a static buffer.
-  **/
-static void release_file(void *file, unsigned long size)
-{
-	munmap(file, size);
-}
-
-
-static void *get_sym_value(struct elf_info *info, const Elf_Sym *sym)
-{
-	return RTE_PTR_ADD(info->hdr,
-		info->sechdrs[sym->st_shndx].sh_offset + sym->st_value);
-}
-
-static Elf_Sym *find_sym_in_symtab(struct elf_info *info,
-				   const char *name, Elf_Sym *last)
-{
-	Elf_Sym *idx;
-	if (last)
-		idx = last+1;
-	else
-		idx = info->symtab_start;
-
-	for (; idx < info->symtab_stop; idx++) {
-		const char *n = sym_name(info, idx);
-		if (!strncmp(n, name, strlen(name)))
-			return idx;
-	}
-	return NULL;
-}
-
-static int parse_elf(struct elf_info *info, const char *filename)
-{
-	unsigned int i;
-	Elf_Ehdr *hdr;
-	Elf_Shdr *sechdrs;
-	Elf_Sym  *sym;
-	int endian;
-	unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U;
-
-	hdr = grab_file(filename, &info->size);
-	if (!hdr) {
-		perror(filename);
-		exit(1);
-	}
-	info->hdr = hdr;
-	if (info->size < sizeof(*hdr)) {
-		/* file too small, assume this is an empty .o file */
-		return 0;
-	}
-	/* Is this a valid ELF file? */
-	if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
-	    (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
-	    (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
-	    (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
-		/* Not an ELF file - silently ignore it */
-		return 0;
-	}
-
-	if (!hdr->e_ident[EI_DATA]) {
-		/* Unknown endian */
-		return 0;
-	}
-
-	endian = hdr->e_ident[EI_DATA];
-
-	/* Fix endianness in ELF header */
-	hdr->e_type      = TO_NATIVE(endian, 16, hdr->e_type);
-	hdr->e_machine   = TO_NATIVE(endian, 16, hdr->e_machine);
-	hdr->e_version   = TO_NATIVE(endian, 32, hdr->e_version);
-	hdr->e_entry     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_entry);
-	hdr->e_phoff     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_phoff);
-	hdr->e_shoff     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_shoff);
-	hdr->e_flags     = TO_NATIVE(endian, 32, hdr->e_flags);
-	hdr->e_ehsize    = TO_NATIVE(endian, 16, hdr->e_ehsize);
-	hdr->e_phentsize = TO_NATIVE(endian, 16, hdr->e_phentsize);
-	hdr->e_phnum     = TO_NATIVE(endian, 16, hdr->e_phnum);
-	hdr->e_shentsize = TO_NATIVE(endian, 16, hdr->e_shentsize);
-	hdr->e_shnum     = TO_NATIVE(endian, 16, hdr->e_shnum);
-	hdr->e_shstrndx  = TO_NATIVE(endian, 16, hdr->e_shstrndx);
-
-	sechdrs = RTE_PTR_ADD(hdr, hdr->e_shoff);
-	info->sechdrs = sechdrs;
-
-	/* Check if file offset is correct */
-	if (hdr->e_shoff > info->size) {
-		fprintf(stderr, "section header offset=%lu in file '%s' "
-		      "is bigger than filesize=%lu\n",
-		      (unsigned long)hdr->e_shoff,
-		      filename, info->size);
-		return 0;
-	}
-
-	if (hdr->e_shnum == SHN_UNDEF) {
-		/*
-		 * There are more than 64k sections,
-		 * read count from .sh_size.
-		 */
-		info->num_sections = TO_NATIVE(endian, 32, sechdrs[0].sh_size);
-	} else {
-		info->num_sections = hdr->e_shnum;
-	}
-	if (hdr->e_shstrndx == SHN_XINDEX)
-		info->secindex_strings =
-			TO_NATIVE(endian, 32, sechdrs[0].sh_link);
-	else
-		info->secindex_strings = hdr->e_shstrndx;
-
-	/* Fix endianness in section headers */
-	for (i = 0; i < info->num_sections; i++) {
-		sechdrs[i].sh_name      =
-			TO_NATIVE(endian, 32, sechdrs[i].sh_name);
-		sechdrs[i].sh_type      =
-			TO_NATIVE(endian, 32, sechdrs[i].sh_type);
-		sechdrs[i].sh_flags     =
-			TO_NATIVE(endian, 32, sechdrs[i].sh_flags);
-		sechdrs[i].sh_addr      =
-			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_addr);
-		sechdrs[i].sh_offset    =
-			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_offset);
-		sechdrs[i].sh_size      =
-			TO_NATIVE(endian, 32, sechdrs[i].sh_size);
-		sechdrs[i].sh_link      =
-			TO_NATIVE(endian, 32, sechdrs[i].sh_link);
-		sechdrs[i].sh_info      =
-			TO_NATIVE(endian, 32, sechdrs[i].sh_info);
-		sechdrs[i].sh_addralign =
-			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_addralign);
-		sechdrs[i].sh_entsize   =
-			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_entsize);
-	}
-	/* Find symbol table. */
-	for (i = 1; i < info->num_sections; i++) {
-		int nobits = sechdrs[i].sh_type == SHT_NOBITS;
-
-		if (!nobits && sechdrs[i].sh_offset > info->size) {
-			fprintf(stderr, "%s is truncated. "
-			      "sechdrs[i].sh_offset=%lu > sizeof(*hrd)=%zu\n",
-			      filename, (unsigned long)sechdrs[i].sh_offset,
-			      sizeof(*hdr));
-			return 0;
-		}
-
-		if (sechdrs[i].sh_type == SHT_SYMTAB) {
-			unsigned int sh_link_idx;
-			symtab_idx = i;
-			info->symtab_start = RTE_PTR_ADD(hdr,
-				sechdrs[i].sh_offset);
-			info->symtab_stop  = RTE_PTR_ADD(hdr,
-				sechdrs[i].sh_offset + sechdrs[i].sh_size);
-			sh_link_idx = sechdrs[i].sh_link;
-			info->strtab       = RTE_PTR_ADD(hdr,
-				sechdrs[sh_link_idx].sh_offset);
-		}
-
-		/* 32bit section no. table? ("more than 64k sections") */
-		if (sechdrs[i].sh_type == SHT_SYMTAB_SHNDX) {
-			symtab_shndx_idx = i;
-			info->symtab_shndx_start = RTE_PTR_ADD(hdr,
-				sechdrs[i].sh_offset);
-			info->symtab_shndx_stop  = RTE_PTR_ADD(hdr,
-				sechdrs[i].sh_offset + sechdrs[i].sh_size);
-		}
-	}
-	if (!info->symtab_start)
-		fprintf(stderr, "%s has no symtab?\n", filename);
-	else {
-		/* Fix endianness in symbols */
-		for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
-			sym->st_shndx = TO_NATIVE(endian, 16, sym->st_shndx);
-			sym->st_name  = TO_NATIVE(endian, 32, sym->st_name);
-			sym->st_value = TO_NATIVE(endian, ADDR_SIZE, sym->st_value);
-			sym->st_size  = TO_NATIVE(endian, ADDR_SIZE, sym->st_size);
-		}
-	}
-
-	if (symtab_shndx_idx != ~0U) {
-		Elf32_Word *p;
-		if (symtab_idx != sechdrs[symtab_shndx_idx].sh_link)
-			fprintf(stderr,
-			      "%s: SYMTAB_SHNDX has bad sh_link: %u!=%u\n",
-			      filename, sechdrs[symtab_shndx_idx].sh_link,
-			      symtab_idx);
-		/* Fix endianness */
-		for (p = info->symtab_shndx_start; p < info->symtab_shndx_stop;
-		     p++)
-			*p = TO_NATIVE(endian, 32, *p);
-	}
-
-	return 1;
-}
-
-static void parse_elf_finish(struct elf_info *info)
-{
-	struct pmd_driver *tmp, *idx = info->drivers;
-	release_file(info->hdr, info->size);
-	while (idx) {
-		tmp = idx->next;
-		free(idx);
-		idx = tmp;
-	}
-}
-
-struct opt_tag {
-	const char *suffix;
-	const char *json_id;
-};
-
-static const struct opt_tag opt_tags[] = {
-	{"_param_string_export", "params"},
-	{"_kmod_dep_export", "kmod"},
-};
-
-static int complete_pmd_entry(struct elf_info *info, struct pmd_driver *drv)
-{
-	const char *tname;
-	int i;
-	char tmpsymname[128];
-	Elf_Sym *tmpsym;
-
-	drv->name = get_sym_value(info, drv->name_sym);
-
-	for (i = 0; i < PMD_OPT_MAX; i++) {
-		memset(tmpsymname, 0, 128);
-		sprintf(tmpsymname, "__%s%s", drv->name, opt_tags[i].suffix);
-		tmpsym = find_sym_in_symtab(info, tmpsymname, NULL);
-		if (!tmpsym)
-			continue;
-		drv->opt_vals[i] = get_sym_value(info, tmpsym);
-	}
-
-	memset(tmpsymname, 0, 128);
-	sprintf(tmpsymname, "__%s_pci_tbl_export", drv->name);
-
-	tmpsym = find_sym_in_symtab(info, tmpsymname, NULL);
-
-
-	/*
-	 * If this returns NULL, then this is a PMD_VDEV, because
-	 * it has no pci table reference
-	 */
-	if (!tmpsym) {
-		drv->pci_tbl = NULL;
-		return 0;
-	}
-
-	tname = get_sym_value(info, tmpsym);
-	tmpsym = find_sym_in_symtab(info, tname, NULL);
-	if (!tmpsym)
-		return -ENOENT;
-
-	drv->pci_tbl = (struct rte_pci_id *)get_sym_value(info, tmpsym);
-	if (!drv->pci_tbl)
-		return -ENOENT;
-
-	return 0;
-}
-
-static int locate_pmd_entries(struct elf_info *info)
-{
-	Elf_Sym *last = NULL;
-	struct pmd_driver *new;
-
-	info->drivers = NULL;
-
-	do {
-		new = calloc(sizeof(struct pmd_driver), 1);
-		new->name_sym = find_sym_in_symtab(info, "this_pmd_name", last);
-		last = new->name_sym;
-		if (!new->name_sym)
-			free(new);
-		else {
-			if (complete_pmd_entry(info, new)) {
-				fprintf(stderr,
-					"Failed to complete pmd entry\n");
-				free(new);
-			} else {
-				new->next = info->drivers;
-				info->drivers = new;
-			}
-		}
-	} while (last);
-
-	return 0;
-}
-
-static void output_pmd_info_string(struct elf_info *info, char *outfile)
-{
-	FILE *ofd;
-	struct pmd_driver *drv;
-	struct rte_pci_id *pci_ids;
-	int idx = 0;
-
-	ofd = fopen(outfile, "w+");
-	if (!ofd) {
-		fprintf(stderr, "Unable to open output file\n");
-		return;
-	}
-
-	drv = info->drivers;
-
-	while (drv) {
-		fprintf(ofd, "const char %s_pmd_info[] __attribute__((used)) = "
-			"\"PMD_INFO_STRING= {",
-			drv->name);
-		fprintf(ofd, "\\\"name\\\" : \\\"%s\\\", ", drv->name);
-
-		for (idx = 0; idx < PMD_OPT_MAX; idx++) {
-			if (drv->opt_vals[idx])
-				fprintf(ofd, "\\\"%s\\\" : \\\"%s\\\", ",
-					opt_tags[idx].json_id,
-					drv->opt_vals[idx]);
-		}
-
-		pci_ids = drv->pci_tbl;
-		fprintf(ofd, "\\\"pci_ids\\\" : [");
-
-		while (pci_ids && pci_ids->device_id) {
-			fprintf(ofd, "[%d, %d, %d, %d]",
-				pci_ids->vendor_id, pci_ids->device_id,
-				pci_ids->subsystem_vendor_id,
-				pci_ids->subsystem_device_id);
-			pci_ids++;
-			if (pci_ids->device_id)
-				fprintf(ofd, ",");
-			else
-				fprintf(ofd, " ");
-		}
-		fprintf(ofd, "]}\";\n");
-		drv = drv->next;
-	}
-
-	fclose(ofd);
-}
-
-int main(int argc, char **argv)
-{
-	struct elf_info info;
-	int rc = 1;
-
-	if (argc < 3) {
-		fprintf(stderr,
-			"usage: %s <object file> <c output file>\n",
-			basename(argv[0]));
-		exit(127);
-	}
-	parse_elf(&info, argv[1]);
-
-	locate_pmd_entries(&info);
-
-	if (info.drivers) {
-		output_pmd_info_string(&info, argv[2]);
-		rc = 0;
-	} else {
-		fprintf(stderr, "No drivers registered\n");
-	}
-
-	parse_elf_finish(&info);
-	exit(rc);
-}
diff --git a/buildtools/pmdinfogen/pmdinfogen.h b/buildtools/pmdinfogen/pmdinfogen.h
deleted file mode 100644
index 27bab30..0000000
--- a/buildtools/pmdinfogen/pmdinfogen.h
+++ /dev/null
@@ -1,125 +0,0 @@ 
-
-/* Postprocess pmd object files to export hw support
- *
- * Copyright 2016 Neil Horman <nhorman@tuxdriver.com>
- * Based in part on modpost.c from the linux kernel
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License V2, incorporated herein by reference.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#ifdef __linux__
-#include <endian.h>
-#else
-#include <sys/endian.h>
-#endif
-#include <fcntl.h>
-#include <unistd.h>
-#include <elf.h>
-#include <rte_config.h>
-#include <rte_pci.h>
-
-/* On BSD-alike OSes elf.h defines these according to host's word size */
-#undef ELF_ST_BIND
-#undef ELF_ST_TYPE
-#undef ELF_R_SYM
-#undef ELF_R_TYPE
-
-/*
- * Define ELF64_* to ELF_*, the latter being defined in both 32 and 64 bit
- * flavors in elf.h.  This makes our code a bit more generic between arches
- * and allows us to support 32 bit code in the future should we ever want to
- */
-#ifdef RTE_ARCH_64
-#define Elf_Ehdr    Elf64_Ehdr
-#define Elf_Shdr    Elf64_Shdr
-#define Elf_Sym     Elf64_Sym
-#define Elf_Addr    Elf64_Addr
-#define Elf_Sword   Elf64_Sxword
-#define Elf_Section Elf64_Half
-#define ELF_ST_BIND ELF64_ST_BIND
-#define ELF_ST_TYPE ELF64_ST_TYPE
-
-#define Elf_Rel     Elf64_Rel
-#define Elf_Rela    Elf64_Rela
-#define ELF_R_SYM   ELF64_R_SYM
-#define ELF_R_TYPE  ELF64_R_TYPE
-#else
-#define Elf_Ehdr    Elf32_Ehdr
-#define Elf_Shdr    Elf32_Shdr
-#define Elf_Sym     Elf32_Sym
-#define Elf_Addr    Elf32_Addr
-#define Elf_Sword   Elf32_Sxword
-#define Elf_Section Elf32_Half
-#define ELF_ST_BIND ELF32_ST_BIND
-#define ELF_ST_TYPE ELF32_ST_TYPE
-
-#define Elf_Rel     Elf32_Rel
-#define Elf_Rela    Elf32_Rela
-#define ELF_R_SYM   ELF32_R_SYM
-#define ELF_R_TYPE  ELF32_R_TYPE
-#endif
-
-
-/*
- * Note, it seems odd that we have both a CONVERT_NATIVE and a TO_NATIVE macro
- * below.  We do this because the values passed to TO_NATIVE may themselves be
- * macros and need both macros here to get expanded.  Specifically its the width
- * variable we are concerned with, because it needs to get expanded prior to
- * string concatenation
- */
-#define CONVERT_NATIVE(fend, width, x) ({ \
-typeof(x) ___x; \
-if ((fend) == ELFDATA2LSB) \
-	___x = le##width##toh(x); \
-else \
-	___x = be##width##toh(x); \
-	___x; \
-})
-
-#define TO_NATIVE(fend, width, x) CONVERT_NATIVE(fend, width, x)
-
-enum opt_params {
-	PMD_PARAM_STRING = 0,
-	PMD_KMOD_DEP,
-	PMD_OPT_MAX
-};
-
-struct pmd_driver {
-	Elf_Sym *name_sym;
-	const char *name;
-	struct rte_pci_id *pci_tbl;
-	struct pmd_driver *next;
-
-	const char *opt_vals[PMD_OPT_MAX];
-};
-
-struct elf_info {
-	unsigned long size;
-	Elf_Ehdr     *hdr;
-	Elf_Shdr     *sechdrs;
-	Elf_Sym      *symtab_start;
-	Elf_Sym      *symtab_stop;
-	char         *strtab;
-
-	/* support for 32bit section numbers */
-
-	unsigned int num_sections; /* max_secindex + 1 */
-	unsigned int secindex_strings;
-	/* if Nth symbol table entry has .st_shndx = SHN_XINDEX,
-	 * take shndx from symtab_shndx_start[N] instead
-	 */
-	Elf32_Word   *symtab_shndx_start;
-	Elf32_Word   *symtab_shndx_stop;
-
-	struct pmd_driver *drivers;
-};
-
diff --git a/drivers/Makefile b/drivers/Makefile
index a04a01f..f3f9417 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -32,10 +32,12 @@ 
 include $(RTE_SDK)/mk/rte.vars.mk
 
 DIRS-y += bus
+DIRS-y += pmdinfogen
+DEPDIRS-pmdinfogen := bus
 DIRS-y += mempool
 DEPDIRS-mempool := bus
 DIRS-y += net
-DEPDIRS-net := bus mempool
+DEPDIRS-net := bus pmdinfogen mempool
 DIRS-$(CONFIG_RTE_LIBRTE_CRYPTODEV) += crypto
 DEPDIRS-crypto := mempool
 DIRS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += event
diff --git a/drivers/pmdinfogen/Makefile b/drivers/pmdinfogen/Makefile
new file mode 100644
index 0000000..bf07b6f
--- /dev/null
+++ b/drivers/pmdinfogen/Makefile
@@ -0,0 +1,47 @@ 
+#   BSD LICENSE
+#
+#   Copyright(c) 2016 Neil Horman. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+#
+# library name
+#
+HOSTAPP = dpdk-pmdinfogen
+
+#
+# all sources are stored in SRCS-y
+#
+SRCS-y += pmdinfogen.c
+
+HOST_CFLAGS += $(WERROR_FLAGS) -g
+HOST_CFLAGS += -I$(RTE_OUTPUT)/include
+
+include $(RTE_SDK)/mk/rte.hostapp.mk
diff --git a/drivers/pmdinfogen/pmdinfogen.c b/drivers/pmdinfogen/pmdinfogen.c
new file mode 100644
index 0000000..ba1a12e
--- /dev/null
+++ b/drivers/pmdinfogen/pmdinfogen.c
@@ -0,0 +1,422 @@ 
+/* Postprocess pmd object files to export hw support
+ *
+ * Copyright 2016 Neil Horman <nhorman@tuxdriver.com>
+ * Based in part on modpost.c from the linux kernel
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License V2, incorporated herein by reference.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <libgen.h>
+
+#include <rte_common.h>
+#include "pmdinfogen.h"
+
+#ifdef RTE_ARCH_64
+#define ADDR_SIZE 64
+#else
+#define ADDR_SIZE 32
+#endif
+
+
+static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
+{
+	if (sym)
+		return elf->strtab + sym->st_name;
+	else
+		return "(unknown)";
+}
+
+static void *grab_file(const char *filename, unsigned long *size)
+{
+	struct stat st;
+	void *map = MAP_FAILED;
+	int fd;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+	if (fstat(fd, &st))
+		goto failed;
+
+	*size = st.st_size;
+	map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+failed:
+	close(fd);
+	if (map == MAP_FAILED)
+		return NULL;
+	return map;
+}
+
+/**
+  * Return a copy of the next line in a mmap'ed file.
+  * spaces in the beginning of the line is trimmed away.
+  * Return a pointer to a static buffer.
+  **/
+static void release_file(void *file, unsigned long size)
+{
+	munmap(file, size);
+}
+
+
+static void *get_sym_value(struct elf_info *info, const Elf_Sym *sym)
+{
+	return RTE_PTR_ADD(info->hdr,
+		info->sechdrs[sym->st_shndx].sh_offset + sym->st_value);
+}
+
+static Elf_Sym *find_sym_in_symtab(struct elf_info *info,
+				   const char *name, Elf_Sym *last)
+{
+	Elf_Sym *idx;
+	if (last)
+		idx = last+1;
+	else
+		idx = info->symtab_start;
+
+	for (; idx < info->symtab_stop; idx++) {
+		const char *n = sym_name(info, idx);
+		if (!strncmp(n, name, strlen(name)))
+			return idx;
+	}
+	return NULL;
+}
+
+static int parse_elf(struct elf_info *info, const char *filename)
+{
+	unsigned int i;
+	Elf_Ehdr *hdr;
+	Elf_Shdr *sechdrs;
+	Elf_Sym  *sym;
+	int endian;
+	unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U;
+
+	hdr = grab_file(filename, &info->size);
+	if (!hdr) {
+		perror(filename);
+		exit(1);
+	}
+	info->hdr = hdr;
+	if (info->size < sizeof(*hdr)) {
+		/* file too small, assume this is an empty .o file */
+		return 0;
+	}
+	/* Is this a valid ELF file? */
+	if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
+	    (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
+	    (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
+	    (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
+		/* Not an ELF file - silently ignore it */
+		return 0;
+	}
+
+	if (!hdr->e_ident[EI_DATA]) {
+		/* Unknown endian */
+		return 0;
+	}
+
+	endian = hdr->e_ident[EI_DATA];
+
+	/* Fix endianness in ELF header */
+	hdr->e_type      = TO_NATIVE(endian, 16, hdr->e_type);
+	hdr->e_machine   = TO_NATIVE(endian, 16, hdr->e_machine);
+	hdr->e_version   = TO_NATIVE(endian, 32, hdr->e_version);
+	hdr->e_entry     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_entry);
+	hdr->e_phoff     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_phoff);
+	hdr->e_shoff     = TO_NATIVE(endian, ADDR_SIZE, hdr->e_shoff);
+	hdr->e_flags     = TO_NATIVE(endian, 32, hdr->e_flags);
+	hdr->e_ehsize    = TO_NATIVE(endian, 16, hdr->e_ehsize);
+	hdr->e_phentsize = TO_NATIVE(endian, 16, hdr->e_phentsize);
+	hdr->e_phnum     = TO_NATIVE(endian, 16, hdr->e_phnum);
+	hdr->e_shentsize = TO_NATIVE(endian, 16, hdr->e_shentsize);
+	hdr->e_shnum     = TO_NATIVE(endian, 16, hdr->e_shnum);
+	hdr->e_shstrndx  = TO_NATIVE(endian, 16, hdr->e_shstrndx);
+
+	sechdrs = RTE_PTR_ADD(hdr, hdr->e_shoff);
+	info->sechdrs = sechdrs;
+
+	/* Check if file offset is correct */
+	if (hdr->e_shoff > info->size) {
+		fprintf(stderr, "section header offset=%lu in file '%s' "
+		      "is bigger than filesize=%lu\n",
+		      (unsigned long)hdr->e_shoff,
+		      filename, info->size);
+		return 0;
+	}
+
+	if (hdr->e_shnum == SHN_UNDEF) {
+		/*
+		 * There are more than 64k sections,
+		 * read count from .sh_size.
+		 */
+		info->num_sections = TO_NATIVE(endian, 32, sechdrs[0].sh_size);
+	} else {
+		info->num_sections = hdr->e_shnum;
+	}
+	if (hdr->e_shstrndx == SHN_XINDEX)
+		info->secindex_strings =
+			TO_NATIVE(endian, 32, sechdrs[0].sh_link);
+	else
+		info->secindex_strings = hdr->e_shstrndx;
+
+	/* Fix endianness in section headers */
+	for (i = 0; i < info->num_sections; i++) {
+		sechdrs[i].sh_name      =
+			TO_NATIVE(endian, 32, sechdrs[i].sh_name);
+		sechdrs[i].sh_type      =
+			TO_NATIVE(endian, 32, sechdrs[i].sh_type);
+		sechdrs[i].sh_flags     =
+			TO_NATIVE(endian, 32, sechdrs[i].sh_flags);
+		sechdrs[i].sh_addr      =
+			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_addr);
+		sechdrs[i].sh_offset    =
+			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_offset);
+		sechdrs[i].sh_size      =
+			TO_NATIVE(endian, 32, sechdrs[i].sh_size);
+		sechdrs[i].sh_link      =
+			TO_NATIVE(endian, 32, sechdrs[i].sh_link);
+		sechdrs[i].sh_info      =
+			TO_NATIVE(endian, 32, sechdrs[i].sh_info);
+		sechdrs[i].sh_addralign =
+			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_addralign);
+		sechdrs[i].sh_entsize   =
+			TO_NATIVE(endian, ADDR_SIZE, sechdrs[i].sh_entsize);
+	}
+	/* Find symbol table. */
+	for (i = 1; i < info->num_sections; i++) {
+		int nobits = sechdrs[i].sh_type == SHT_NOBITS;
+
+		if (!nobits && sechdrs[i].sh_offset > info->size) {
+			fprintf(stderr, "%s is truncated. "
+			      "sechdrs[i].sh_offset=%lu > sizeof(*hrd)=%zu\n",
+			      filename, (unsigned long)sechdrs[i].sh_offset,
+			      sizeof(*hdr));
+			return 0;
+		}
+
+		if (sechdrs[i].sh_type == SHT_SYMTAB) {
+			unsigned int sh_link_idx;
+			symtab_idx = i;
+			info->symtab_start = RTE_PTR_ADD(hdr,
+				sechdrs[i].sh_offset);
+			info->symtab_stop  = RTE_PTR_ADD(hdr,
+				sechdrs[i].sh_offset + sechdrs[i].sh_size);
+			sh_link_idx = sechdrs[i].sh_link;
+			info->strtab       = RTE_PTR_ADD(hdr,
+				sechdrs[sh_link_idx].sh_offset);
+		}
+
+		/* 32bit section no. table? ("more than 64k sections") */
+		if (sechdrs[i].sh_type == SHT_SYMTAB_SHNDX) {
+			symtab_shndx_idx = i;
+			info->symtab_shndx_start = RTE_PTR_ADD(hdr,
+				sechdrs[i].sh_offset);
+			info->symtab_shndx_stop  = RTE_PTR_ADD(hdr,
+				sechdrs[i].sh_offset + sechdrs[i].sh_size);
+		}
+	}
+	if (!info->symtab_start)
+		fprintf(stderr, "%s has no symtab?\n", filename);
+	else {
+		/* Fix endianness in symbols */
+		for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
+			sym->st_shndx = TO_NATIVE(endian, 16, sym->st_shndx);
+			sym->st_name  = TO_NATIVE(endian, 32, sym->st_name);
+			sym->st_value = TO_NATIVE(endian, ADDR_SIZE, sym->st_value);
+			sym->st_size  = TO_NATIVE(endian, ADDR_SIZE, sym->st_size);
+		}
+	}
+
+	if (symtab_shndx_idx != ~0U) {
+		Elf32_Word *p;
+		if (symtab_idx != sechdrs[symtab_shndx_idx].sh_link)
+			fprintf(stderr,
+			      "%s: SYMTAB_SHNDX has bad sh_link: %u!=%u\n",
+			      filename, sechdrs[symtab_shndx_idx].sh_link,
+			      symtab_idx);
+		/* Fix endianness */
+		for (p = info->symtab_shndx_start; p < info->symtab_shndx_stop;
+		     p++)
+			*p = TO_NATIVE(endian, 32, *p);
+	}
+
+	return 1;
+}
+
+static void parse_elf_finish(struct elf_info *info)
+{
+	struct pmd_driver *tmp, *idx = info->drivers;
+	release_file(info->hdr, info->size);
+	while (idx) {
+		tmp = idx->next;
+		free(idx);
+		idx = tmp;
+	}
+}
+
+struct opt_tag {
+	const char *suffix;
+	const char *json_id;
+};
+
+static const struct opt_tag opt_tags[] = {
+	{"_param_string_export", "params"},
+	{"_kmod_dep_export", "kmod"},
+};
+
+static int complete_pmd_entry(struct elf_info *info, struct pmd_driver *drv)
+{
+	const char *tname;
+	int i;
+	char tmpsymname[128];
+	Elf_Sym *tmpsym;
+
+	drv->name = get_sym_value(info, drv->name_sym);
+
+	for (i = 0; i < PMD_OPT_MAX; i++) {
+		memset(tmpsymname, 0, 128);
+		sprintf(tmpsymname, "__%s%s", drv->name, opt_tags[i].suffix);
+		tmpsym = find_sym_in_symtab(info, tmpsymname, NULL);
+		if (!tmpsym)
+			continue;
+		drv->opt_vals[i] = get_sym_value(info, tmpsym);
+	}
+
+	memset(tmpsymname, 0, 128);
+	sprintf(tmpsymname, "__%s_pci_tbl_export", drv->name);
+
+	tmpsym = find_sym_in_symtab(info, tmpsymname, NULL);
+
+
+	/*
+	 * If this returns NULL, then this is a PMD_VDEV, because
+	 * it has no pci table reference
+	 */
+	if (!tmpsym) {
+		drv->pci_tbl = NULL;
+		return 0;
+	}
+
+	tname = get_sym_value(info, tmpsym);
+	tmpsym = find_sym_in_symtab(info, tname, NULL);
+	if (!tmpsym)
+		return -ENOENT;
+
+	drv->pci_tbl = (struct rte_pci_id *)get_sym_value(info, tmpsym);
+	if (!drv->pci_tbl)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int locate_pmd_entries(struct elf_info *info)
+{
+	Elf_Sym *last = NULL;
+	struct pmd_driver *new;
+
+	info->drivers = NULL;
+
+	do {
+		new = calloc(sizeof(struct pmd_driver), 1);
+		new->name_sym = find_sym_in_symtab(info, "this_pmd_name", last);
+		last = new->name_sym;
+		if (!new->name_sym)
+			free(new);
+		else {
+			if (complete_pmd_entry(info, new)) {
+				fprintf(stderr,
+					"Failed to complete pmd entry\n");
+				free(new);
+			} else {
+				new->next = info->drivers;
+				info->drivers = new;
+			}
+		}
+	} while (last);
+
+	return 0;
+}
+
+static void output_pmd_info_string(struct elf_info *info, char *outfile)
+{
+	FILE *ofd;
+	struct pmd_driver *drv;
+	struct rte_pci_id *pci_ids;
+	int idx = 0;
+
+	ofd = fopen(outfile, "w+");
+	if (!ofd) {
+		fprintf(stderr, "Unable to open output file\n");
+		return;
+	}
+
+	drv = info->drivers;
+
+	while (drv) {
+		fprintf(ofd, "const char %s_pmd_info[] __attribute__((used)) = "
+			"\"PMD_INFO_STRING= {",
+			drv->name);
+		fprintf(ofd, "\\\"name\\\" : \\\"%s\\\", ", drv->name);
+
+		for (idx = 0; idx < PMD_OPT_MAX; idx++) {
+			if (drv->opt_vals[idx])
+				fprintf(ofd, "\\\"%s\\\" : \\\"%s\\\", ",
+					opt_tags[idx].json_id,
+					drv->opt_vals[idx]);
+		}
+
+		pci_ids = drv->pci_tbl;
+		fprintf(ofd, "\\\"pci_ids\\\" : [");
+
+		while (pci_ids && pci_ids->device_id) {
+			fprintf(ofd, "[%d, %d, %d, %d]",
+				pci_ids->vendor_id, pci_ids->device_id,
+				pci_ids->subsystem_vendor_id,
+				pci_ids->subsystem_device_id);
+			pci_ids++;
+			if (pci_ids->device_id)
+				fprintf(ofd, ",");
+			else
+				fprintf(ofd, " ");
+		}
+		fprintf(ofd, "]}\";\n");
+		drv = drv->next;
+	}
+
+	fclose(ofd);
+}
+
+int main(int argc, char **argv)
+{
+	struct elf_info info;
+	int rc = 1;
+
+	if (argc < 3) {
+		fprintf(stderr,
+			"usage: %s <object file> <c output file>\n",
+			basename(argv[0]));
+		exit(127);
+	}
+	parse_elf(&info, argv[1]);
+
+	locate_pmd_entries(&info);
+
+	if (info.drivers) {
+		output_pmd_info_string(&info, argv[2]);
+		rc = 0;
+	} else {
+		fprintf(stderr, "No drivers registered\n");
+	}
+
+	parse_elf_finish(&info);
+	exit(rc);
+}
diff --git a/drivers/pmdinfogen/pmdinfogen.h b/drivers/pmdinfogen/pmdinfogen.h
new file mode 100644
index 0000000..27bab30
--- /dev/null
+++ b/drivers/pmdinfogen/pmdinfogen.h
@@ -0,0 +1,125 @@ 
+
+/* Postprocess pmd object files to export hw support
+ *
+ * Copyright 2016 Neil Horman <nhorman@tuxdriver.com>
+ * Based in part on modpost.c from the linux kernel
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License V2, incorporated herein by reference.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#ifdef __linux__
+#include <endian.h>
+#else
+#include <sys/endian.h>
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+#include <elf.h>
+#include <rte_config.h>
+#include <rte_pci.h>
+
+/* On BSD-alike OSes elf.h defines these according to host's word size */
+#undef ELF_ST_BIND
+#undef ELF_ST_TYPE
+#undef ELF_R_SYM
+#undef ELF_R_TYPE
+
+/*
+ * Define ELF64_* to ELF_*, the latter being defined in both 32 and 64 bit
+ * flavors in elf.h.  This makes our code a bit more generic between arches
+ * and allows us to support 32 bit code in the future should we ever want to
+ */
+#ifdef RTE_ARCH_64
+#define Elf_Ehdr    Elf64_Ehdr
+#define Elf_Shdr    Elf64_Shdr
+#define Elf_Sym     Elf64_Sym
+#define Elf_Addr    Elf64_Addr
+#define Elf_Sword   Elf64_Sxword
+#define Elf_Section Elf64_Half
+#define ELF_ST_BIND ELF64_ST_BIND
+#define ELF_ST_TYPE ELF64_ST_TYPE
+
+#define Elf_Rel     Elf64_Rel
+#define Elf_Rela    Elf64_Rela
+#define ELF_R_SYM   ELF64_R_SYM
+#define ELF_R_TYPE  ELF64_R_TYPE
+#else
+#define Elf_Ehdr    Elf32_Ehdr
+#define Elf_Shdr    Elf32_Shdr
+#define Elf_Sym     Elf32_Sym
+#define Elf_Addr    Elf32_Addr
+#define Elf_Sword   Elf32_Sxword
+#define Elf_Section Elf32_Half
+#define ELF_ST_BIND ELF32_ST_BIND
+#define ELF_ST_TYPE ELF32_ST_TYPE
+
+#define Elf_Rel     Elf32_Rel
+#define Elf_Rela    Elf32_Rela
+#define ELF_R_SYM   ELF32_R_SYM
+#define ELF_R_TYPE  ELF32_R_TYPE
+#endif
+
+
+/*
+ * Note, it seems odd that we have both a CONVERT_NATIVE and a TO_NATIVE macro
+ * below.  We do this because the values passed to TO_NATIVE may themselves be
+ * macros and need both macros here to get expanded.  Specifically its the width
+ * variable we are concerned with, because it needs to get expanded prior to
+ * string concatenation
+ */
+#define CONVERT_NATIVE(fend, width, x) ({ \
+typeof(x) ___x; \
+if ((fend) == ELFDATA2LSB) \
+	___x = le##width##toh(x); \
+else \
+	___x = be##width##toh(x); \
+	___x; \
+})
+
+#define TO_NATIVE(fend, width, x) CONVERT_NATIVE(fend, width, x)
+
+enum opt_params {
+	PMD_PARAM_STRING = 0,
+	PMD_KMOD_DEP,
+	PMD_OPT_MAX
+};
+
+struct pmd_driver {
+	Elf_Sym *name_sym;
+	const char *name;
+	struct rte_pci_id *pci_tbl;
+	struct pmd_driver *next;
+
+	const char *opt_vals[PMD_OPT_MAX];
+};
+
+struct elf_info {
+	unsigned long size;
+	Elf_Ehdr     *hdr;
+	Elf_Shdr     *sechdrs;
+	Elf_Sym      *symtab_start;
+	Elf_Sym      *symtab_stop;
+	char         *strtab;
+
+	/* support for 32bit section numbers */
+
+	unsigned int num_sections; /* max_secindex + 1 */
+	unsigned int secindex_strings;
+	/* if Nth symbol table entry has .st_shndx = SHN_XINDEX,
+	 * take shndx from symtab_shndx_start[N] instead
+	 */
+	Elf32_Word   *symtab_shndx_start;
+	Elf32_Word   *symtab_shndx_stop;
+
+	struct pmd_driver *drivers;
+};
+