diff --git a/app/test/commands.c b/app/test/commands.c
index 8d5a03a95..06fc33ee5 100644
--- a/app/test/commands.c
+++ b/app/test/commands.c
@@ -50,12 +50,22 @@
 
 /****************/
 
+static uint8_t test_abi_version = TEST_DPDK_ABI_VERSION_DEFAULT;
+
+static struct test_abi_version_list abi_version_list =
+	TAILQ_HEAD_INITIALIZER(abi_version_list);
+
 static struct test_commands_list commands_list =
 	TAILQ_HEAD_INITIALIZER(commands_list);
 
-void
-add_test_command(struct test_command *t)
+void add_abi_version(struct test_abi_version *av)
+{
+	TAILQ_INSERT_TAIL(&abi_version_list, av, next);
+}
+
+void add_test_command(struct test_command *t, uint8_t abi_version)
 {
+	t->abi_version = abi_version;
 	TAILQ_INSERT_TAIL(&commands_list, t, next);
 }
 
@@ -63,6 +73,12 @@ struct cmd_autotest_result {
 	cmdline_fixed_string_t autotest;
 };
 
+cmdline_parse_token_string_t
+cmd_autotest_autotest[TEST_DPDK_ABI_VERSION_MAX] = {
+	[0 ... TEST_DPDK_ABI_VERSION_MAX-1] =
+	TOKEN_STRING_INITIALIZER(struct cmd_autotest_result, autotest, "")
+};
+
 static void cmd_autotest_parsed(void *parsed_result,
 				__attribute__((unused)) struct cmdline *cl,
 				__attribute__((unused)) void *data)
@@ -72,7 +88,8 @@ static void cmd_autotest_parsed(void *parsed_result,
 	int ret = 0;
 
 	TAILQ_FOREACH(t, &commands_list, next) {
-		if (!strcmp(res->autotest, t->command))
+		if (!strcmp(res->autotest, t->command)
+				&& t->abi_version == test_abi_version)
 			ret = t->callback();
 	}
 
@@ -86,10 +103,6 @@ static void cmd_autotest_parsed(void *parsed_result,
 	fflush(stdout);
 }
 
-cmdline_parse_token_string_t cmd_autotest_autotest =
-	TOKEN_STRING_INITIALIZER(struct cmd_autotest_result, autotest,
-				 "");
-
 cmdline_parse_inst_t cmd_autotest = {
 	.f = cmd_autotest_parsed,  /* function to call */
 	.data = NULL,      /* 2nd arg of func */
@@ -244,6 +257,53 @@ cmdline_parse_inst_t cmd_quit = {
 
 /****************/
 
+struct cmd_set_abi_version_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t abi_version_name;
+};
+
+static void cmd_set_abi_version_parsed(
+				void *parsed_result,
+				__attribute__((unused)) struct cmdline *cl,
+				__attribute__((unused)) void *data)
+{
+	struct test_abi_version *av;
+	struct cmd_set_abi_version_result *res = parsed_result;
+
+	TAILQ_FOREACH(av, &abi_version_list, next) {
+		if (!strcmp(res->abi_version_name, av->version_name)) {
+
+			printf("abi version set to %s\n", av->version_name);
+			test_abi_version = av->version_id;
+			cmd_autotest.tokens[0] =
+				(void *)&cmd_autotest_autotest[av->version_id];
+		}
+	}
+
+	fflush(stdout);
+}
+
+cmdline_parse_token_string_t cmd_set_abi_version_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_abi_version_result, set,
+				"set_abi_version");
+
+cmdline_parse_token_string_t cmd_set_abi_version_abi_version =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_abi_version_result,
+				abi_version_name, NULL);
+
+cmdline_parse_inst_t cmd_set_abi_version = {
+	.f = cmd_set_abi_version_parsed,  /* function to call */
+	.data = NULL,      /* 2nd arg of func */
+	.help_str = "set abi version: ",
+	.tokens = {        /* token list, NULL terminated */
+		(void *)&cmd_set_abi_version_set,
+		(void *)&cmd_set_abi_version_abi_version,
+		NULL,
+	},
+};
+
+/****************/
+
 struct cmd_set_rxtx_result {
 	cmdline_fixed_string_t set;
 	cmdline_fixed_string_t mode;
@@ -259,7 +319,7 @@ static void cmd_set_rxtx_parsed(void *parsed_result, struct cmdline *cl,
 
 cmdline_parse_token_string_t cmd_set_rxtx_set =
 	TOKEN_STRING_INITIALIZER(struct cmd_set_rxtx_result, set,
-				 "set_rxtx_mode");
+				"set_rxtx_mode");
 
 cmdline_parse_token_string_t cmd_set_rxtx_mode =
 	TOKEN_STRING_INITIALIZER(struct cmd_set_rxtx_result, mode, NULL);
@@ -360,29 +420,66 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_set_rxtx,
 	(cmdline_parse_inst_t *)&cmd_set_rxtx_anchor,
 	(cmdline_parse_inst_t *)&cmd_set_rxtx_sc,
+	(cmdline_parse_inst_t *)&cmd_set_abi_version,
 	NULL,
 };
 
 int commands_init(void)
 {
+	struct test_abi_version *av;
 	struct test_command *t;
-	char *commands;
-	int commands_len = 0;
+	char *commands[TEST_DPDK_ABI_VERSION_MAX];
+	char *help;
+
+	int commands_len[TEST_DPDK_ABI_VERSION_MAX] = {
+		[0 ... TEST_DPDK_ABI_VERSION_MAX-1] = 0
+	};
+	int help_len = strlen(cmd_set_abi_version.help_str);
+	int abi_version;
+
+	/* set the set_abi_version command help string */
+	TAILQ_FOREACH(av, &abi_version_list, next) {
+		help_len += strlen(av->version_name) + 1;
+	}
+
+	help = (char *)calloc(help_len, sizeof(char));
+	if (!help)
+		return -1;
+
+	strlcat(help, cmd_set_abi_version.help_str, help_len);
+	TAILQ_FOREACH(av, &abi_version_list, next) {
+		strlcat(help, av->version_name, help_len);
+		if (TAILQ_NEXT(av, next) != NULL)
+			strlcat(help, "|", help_len);
+	}
+
+	cmd_set_abi_version.help_str = help;
 
+	/* set the parse strings for the command lists */
 	TAILQ_FOREACH(t, &commands_list, next) {
-		commands_len += strlen(t->command) + 1;
+		commands_len[t->abi_version] += strlen(t->command) + 1;
 	}
 
-	commands = (char *)calloc(commands_len, sizeof(char));
-	if (!commands)
-		return -1;
+	for (abi_version = 0; abi_version < TEST_DPDK_ABI_VERSION_MAX;
+		abi_version++) {
+		commands[abi_version] =
+			(char *)calloc(commands_len[abi_version], sizeof(char));
+		if (!commands[abi_version])
+			return -1;
+	}
 
 	TAILQ_FOREACH(t, &commands_list, next) {
-		strlcat(commands, t->command, commands_len);
+		strlcat(commands[t->abi_version],
+			t->command, commands_len[t->abi_version]);
 		if (TAILQ_NEXT(t, next) != NULL)
-			strlcat(commands, "#", commands_len);
+			strlcat(commands[t->abi_version],
+				"#", commands_len[t->abi_version]);
 	}
 
-	cmd_autotest_autotest.string_data.str = commands;
+	for (abi_version = 0; abi_version < TEST_DPDK_ABI_VERSION_MAX;
+		abi_version++)
+		cmd_autotest_autotest[abi_version].string_data.str =
+			commands[abi_version];
+
 	return 0;
 }
diff --git a/app/test/test.c b/app/test/test.c
index cd7aaf645..67179d4af 100644
--- a/app/test/test.c
+++ b/app/test/test.c
@@ -307,3 +307,5 @@ unit_test_suite_runner(struct unit_test_suite *suite)
 		return TEST_SKIPPED;
 	return TEST_SUCCESS;
 }
+
+REGISTER_TEST_ABI_VERSION(default, TEST_DPDK_ABI_VERSION_DEFAULT)
diff --git a/app/test/test.h b/app/test/test.h
index ac0c50616..5ec3728d0 100644
--- a/app/test/test.h
+++ b/app/test/test.h
@@ -162,25 +162,59 @@ int test_set_rxtx_conf(cmdline_fixed_string_t mode);
 int test_set_rxtx_anchor(cmdline_fixed_string_t type);
 int test_set_rxtx_sc(cmdline_fixed_string_t type);
 
+#define MAP_ABI_SYMBOL_VERSION(name, abi_version)                             \
+	__asm(".symver "RTE_STR(name)","RTE_STR(name)"@"RTE_STR(abi_version))
+
+#define TEST_DPDK_ABI_VERSION_DEFAULT 0
+#define TEST_DPDK_ABI_VERSION_V1604   1
+#define TEST_DPDK_ABI_VERSION_V20     2
+#define TEST_DPDK_ABI_VERSION_MAX     3
+
+TAILQ_HEAD(test_abi_version_list, test_abi_version);
+struct test_abi_version {
+	TAILQ_ENTRY(test_abi_version) next;
+	const char *version_name;
+	uint8_t version_id;
+};
+
+void add_abi_version(struct test_abi_version *av);
+
+/* Register a test function with its command string */
+#define REGISTER_TEST_ABI_VERSION(name, id)                                   \
+	static struct test_abi_version test_struct_##name = {                 \
+		.version_name = RTE_STR(name),                                \
+		.version_id = id,                                             \
+	};                                                                    \
+	RTE_INIT(test_register_##name)                                        \
+	{                                                                     \
+		add_abi_version(&test_struct_##name);                         \
+	}
+
 typedef int (test_callback)(void);
 TAILQ_HEAD(test_commands_list, test_command);
 struct test_command {
 	TAILQ_ENTRY(test_command) next;
 	const char *command;
 	test_callback *callback;
+	uint8_t abi_version;
 };
 
-void add_test_command(struct test_command *t);
+void add_test_command(struct test_command *t, uint8_t abi_version);
+
+/* Register a test function with its command string and abi version */
+#define REGISTER_TEST_COMMAND_VERSION(cmd, func, abi_version)                 \
+	static struct test_command test_struct_##cmd = {                      \
+		.command = RTE_STR(cmd),                                      \
+		.callback = func,                                             \
+	};                                                                    \
+	RTE_INIT(test_register_##cmd)                                         \
+	{                                                                     \
+		add_test_command(&test_struct_##cmd, abi_version);            \
+	}
 
 /* Register a test function with its command string */
+
 #define REGISTER_TEST_COMMAND(cmd, func) \
-	static struct test_command test_struct_##cmd = { \
-		.command = RTE_STR(cmd), \
-		.callback = func, \
-	}; \
-	RTE_INIT(test_register_##cmd) \
-	{ \
-		add_test_command(&test_struct_##cmd); \
-	}
+	REGISTER_TEST_COMMAND_VERSION(cmd, func, TEST_DPDK_ABI_VERSION_DEFAULT)
 
 #endif
diff --git a/doc/guides/contributing/versioning.rst b/doc/guides/contributing/versioning.rst
index 3ab2c4346..63ef53ea3 100644
--- a/doc/guides/contributing/versioning.rst
+++ b/doc/guides/contributing/versioning.rst
@@ -221,6 +221,10 @@ The macros exported are:
   the linker to bind references to symbol ``b`` to the internal symbol
   ``b_e``.
 
+* ``BIND_VERSION_SYMBOL(b, n)``: Creates a symbol version entry instructing
+  the linker to bind references to symbol ``b`` to the external symbol
+  ``b@DPDK_n``
+
 * ``MAP_STATIC_SYMBOL(f, p)``: Declare the prototype ``f``, and map it to the
   fully qualified function ``p``, so that if a symbol becomes versioned, it
   can still be mapped back to the public symbol name.
diff --git a/lib/librte_eal/common/include/rte_compat.h b/lib/librte_eal/common/include/rte_compat.h
index 92ff28faf..be9724f4c 100644
--- a/lib/librte_eal/common/include/rte_compat.h
+++ b/lib/librte_eal/common/include/rte_compat.h
@@ -50,6 +50,13 @@
 #define BIND_DEFAULT_SYMBOL(b, e, n) __asm__(".symver " RTE_STR(b) RTE_STR(e) ", " RTE_STR(b) "@@DPDK_" RTE_STR(n))
 #define __vsym __attribute__((used))
 
+/*
+ * BIND_VERSION_SYMBOL
+ * Creates a symbol version entry instructing the linker to bind references to
+ * symbol <b> to the symbol version <b>@DPDK_<n>
+ */
+#define BIND_VERSION_SYMBOL(b, n) __asm__(".symver " RTE_STR(b) ", " RTE_STR(b) "@DPDK_" RTE_STR(n))
+
 /*
  * MAP_STATIC_SYMBOL
  * If a function has been bifurcated into multiple versions, none of which
