From 500ba9fcc59b7f27aec5e4b9122754fe035f0628 Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Thu, 15 Oct 2020 17:07:03 -0400 Subject: vlib: add cgo-friendly plugin registration support Allows us to declare plugin registrations in a non-disgusting way: var plugin_reg = vpp.PluginRegistration{ Description: "The CGO plugin", Version: "My Version", Overrides: "sample_plugin.so", } It turns out that the specific compiler setup generates (.data section offset, length) pairs in the .vlib_plugin_r2 section: Contents of section .vlib_plugin_r2: 1ba9d0 00000000 00000000 50a81800 00000000 ........P....... 1ba9e0 0a000000 00000000 00000000 00000000 ................ 1ba9f0 00000000 00000000 00000000 00000000 ................ 1baa00 00000000 00000000 00000000 00000000 ................ 1baa10 00000000 00000000 70a81800 00000000 ........p....... 1baa20 0e000000 00000000 ........ Contents of section .data: 18a800 00a81800 00000000 00000000 00000000 ................ 18a810 00000000 00000000 00000000 00000000 ................ 18a820 00000000 00000000 00000000 00000000 ................ 18a830 00000000 00000000 00000000 00000000 ................ 18a840 00000000 00000000 14000000 00000000 ................ 18a850 4d792056 65727369 6f6e0000 00000000 My Version...... 18a860 00000000 00000000 14000000 00000000 ................ 18a870 54686520 45474f20 706c7567 696e0000 The CGO plugin.. 18a880 00000000 00000000 0c000000 00000000 ................ Unfortunately, it seems impossible to torture clang / gcc into producing anything like this. This patch fabricates a plausible vlib_plugin_registration_t from the so-called vlib_plugin_r2_t. Type: improvement Signed-off-by: Dave Barach Change-Id: I8c0c5a24f3b7bfea07d5181a7250b3d9685e8446 --- src/vlib/unix/plugin.c | 110 +++++++++++++++++++++++++++++++++++++++++++++---- src/vlib/unix/plugin.h | 27 ++++++++++++ 2 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/vlib/unix/plugin.c b/src/vlib/unix/plugin.c index 0837ecdb13e..a714c7c29ce 100644 --- a/src/vlib/unix/plugin.c +++ b/src/vlib/unix/plugin.c @@ -81,22 +81,120 @@ extract (u8 * sp, u8 * ep) return rv; } +/* + * If a plugin .so contains a ".vlib_plugin_r2" section, + * this function converts the vlib_plugin_r2_t to + * a vlib_plugin_registration_t. + */ + +static clib_error_t * +r2_to_reg (elf_main_t * em, vlib_plugin_r2_t * r2, + vlib_plugin_registration_t * reg) +{ + clib_error_t *error; + elf_section_t *section; + uword data_segment_offset; + u8 *data; + + /* It turns out that the strings land in the ".data" section */ + error = elf_get_section_by_name (em, ".data", §ion); + if (error) + return error; + data = elf_get_section_contents (em, section->index, 1); + + /* + * Offsets in the ".vlib_plugin_r2" section + * need to have the data section base subtracted from them. + * The offset is in the first 8 bytes of the ".data" section + */ + + data_segment_offset = *((uword *) data); + + /* Relocate pointers, subtract data_segment_offset */ +#define _(a) r2->a.data_segment_offset -= data_segment_offset; + foreach_r2_string_field; +#undef _ + + if (r2->version.length >= ARRAY_LEN (reg->version) - 1) + return clib_error_return (0, "Version string too long"); + + if (r2->version_required.length >= ARRAY_LEN (reg->version_required) - 1) + return clib_error_return (0, "Version-required string too long"); + + if (r2->overrides.length >= ARRAY_LEN (reg->overrides) - 1) + return clib_error_return (0, "Override string too long"); + + /* Compatibility with C-initializer */ + memcpy ((void *) reg->version, data + r2->version.data_segment_offset, + r2->version.length); + memcpy ((void *) reg->version_required, + data + r2->version_required.data_segment_offset, + r2->version_required.length); + memcpy ((void *) reg->overrides, data + r2->overrides.data_segment_offset, + r2->overrides.length); + + if (r2->early_init.length > 0) + { + u8 *ei = 0; + vec_validate (ei, r2->early_init.length + 1); + memcpy (ei, data + r2->early_init.data_segment_offset, + r2->early_init.length); + reg->early_init = (void *) ei; + } + + if (r2->description.length > 0) + { + u8 *desc = 0; + vec_validate (desc, r2->description.length + 1); + memcpy (desc, data + r2->description.data_segment_offset, + r2->description.length); + reg->description = (void *) desc; + } + vec_free (data); + return 0; +} + + static int load_one_plugin (plugin_main_t * pm, plugin_info_t * pi, int from_early_init) { void *handle; + int reread_reg = 1; clib_error_t *error; elf_main_t em = { 0 }; elf_section_t *section; u8 *data; char *version_required; vlib_plugin_registration_t *reg; + vlib_plugin_r2_t *r2; plugin_config_t *pc = 0; uword *p; if (elf_read_file (&em, (char *) pi->filename)) return -1; + /* New / improved (well, not really) registration structure? */ + error = elf_get_section_by_name (&em, ".vlib_plugin_r2", §ion); + if (error == 0) + { + data = elf_get_section_contents (&em, section->index, 1); + r2 = (vlib_plugin_r2_t *) data; + reg = clib_mem_alloc (sizeof (*reg)); + memset (reg, 0, sizeof (*reg)); + + reg->default_disabled = r2->default_disabled != 0; + error = r2_to_reg (&em, r2, reg); + if (error) + { + PLUGIN_LOG_ERR ("Bad r2 registration: %s\n", (char *) pi->name); + return -1; + } + if (pm->plugins_default_disable) + reg->default_disabled = 1; + reread_reg = 0; + goto process_reg; + } + error = elf_get_section_by_name (&em, ".vlib_plugin_registration", §ion); if (error) @@ -118,6 +216,7 @@ load_one_plugin (plugin_main_t * pm, plugin_info_t * pi, int from_early_init) if (pm->plugins_default_disable) reg->default_disabled = 1; +process_reg: p = hash_get_mem (pm->config_index_by_name, pi->name); if (p) { @@ -216,15 +315,8 @@ load_one_plugin (plugin_main_t * pm, plugin_info_t * pi, int from_early_init) pi->handle = handle; - reg = dlsym (pi->handle, "vlib_plugin_registration"); - - if (reg == 0) - { - /* This should never happen unless somebody changes registration macro */ - PLUGIN_LOG_ERR ("Missing plugin registration in plugin '%s'", pi->name); - dlclose (pi->handle); - goto error; - } + if (reread_reg) + reg = dlsym (pi->handle, "vlib_plugin_registration"); pi->reg = reg; pi->version = str_array_to_vec ((char *) ®->version, diff --git a/src/vlib/unix/plugin.h b/src/vlib/unix/plugin.h index c9a33ccd96a..4beae43d504 100644 --- a/src/vlib/unix/plugin.h +++ b/src/vlib/unix/plugin.h @@ -67,6 +67,33 @@ typedef CLIB_PACKED(struct { }) vlib_plugin_registration_t; /* *INDENT-ON* */ +/* + * Plugins may also use this registration format, which is + * easy enough to emit from e.g. a golang compiler. + */ +typedef struct +{ + uword data_segment_offset; + uword length; +} vlib_r2_string_t; + +typedef struct +{ + int default_disabled; + vlib_r2_string_t version; + vlib_r2_string_t version_required; + vlib_r2_string_t overrides; + vlib_r2_string_t early_init; + vlib_r2_string_t description; +} vlib_plugin_r2_t; + +#define foreach_r2_string_field \ +_(version) \ +_(version_required) \ +_(overrides) \ +_(early_init) \ +_(description) + typedef struct { u8 *name; -- cgit 1.2.3-korg