From 446eaa5f66db385d89357ba43aa5886f8b906ff0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 5 Dec 2011 23:18:21 +1030 Subject: [PATCH] autodata: stash pointers in a binary. This is a more portable variant of the ELF section trick. --- ccan/autodata/LICENSE | 1 + ccan/autodata/_info | 27 +++++++++ ccan/autodata/autodata.c | 80 +++++++++++++++++++++++++ ccan/autodata/autodata.h | 106 +++++++++++++++++++++++++++++++++ ccan/autodata/test/helper.c | 6 ++ ccan/autodata/test/run-fools.c | 71 ++++++++++++++++++++++ ccan/autodata/test/run.c | 41 +++++++++++++ 7 files changed, 332 insertions(+) create mode 120000 ccan/autodata/LICENSE create mode 100644 ccan/autodata/_info create mode 100644 ccan/autodata/autodata.c create mode 100644 ccan/autodata/autodata.h create mode 100644 ccan/autodata/test/helper.c create mode 100644 ccan/autodata/test/run-fools.c create mode 100644 ccan/autodata/test/run.c diff --git a/ccan/autodata/LICENSE b/ccan/autodata/LICENSE new file mode 120000 index 00000000..2354d129 --- /dev/null +++ b/ccan/autodata/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/autodata/_info b/ccan/autodata/_info new file mode 100644 index 00000000..b804bcdd --- /dev/null +++ b/ccan/autodata/_info @@ -0,0 +1,27 @@ +#include +#include "config.h" + +/** + * autodata - stash pointers in your binary for automatic registration + * + * This code allows declarations in your source which you can gather + * together at runtime to form tables. This is often used in place of + * having to patch a registration function call out, or a central + * table. + * + * License: BSD-MIT + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/compiler\n"); + printf("ccan/ptr_valid\n"); + return 0; + } + + return 1; +} diff --git a/ccan/autodata/autodata.c b/ccan/autodata/autodata.c new file mode 100644 index 00000000..d8ea88fe --- /dev/null +++ b/ccan/autodata/autodata.c @@ -0,0 +1,80 @@ +// Licensed under BSD-MIT: See LICENSE. +#include "autodata.h" +#include +#include +#include + +#if HAVE_SECTION_START_STOP +void *autodata_get_section(void *start, void *stop, size_t *nump) +{ + *nump = (void **)(stop) - (void **)(start); + return start; +} + +void autodata_free(void *table) +{ +} +#else +#include + +void *autodata_make_table(const void *example, const char *name, size_t *nump) +{ + const char *start, *end, *tag; + struct ptr_valid_batch batch; + const void *const magic = (void *)AUTODATA_MAGIC; + void **table = NULL; + char first_magic; + + if (!ptr_valid_batch_start(&batch)) + return NULL; + + /* Get range to search. */ + for (start = (char *)((intptr_t)example & ~(getpagesize() - 1)); + ptr_valid_batch(&batch, start-getpagesize(), 1, sizeof(void *), + false); + start -= getpagesize()); + + for (end = (char *)((intptr_t)example & ~(getpagesize() - 1)); + ptr_valid_batch(&batch, end, 1, sizeof(void *), false); + end += getpagesize()); + + *nump = 0; + first_magic = *(char *)&magic; + for (tag = memchr(start, first_magic, end - start); + tag; + tag = memchr(tag+1, first_magic, end - (tag + 1))) { + void *adata[4]; + + /* We can read 4 void *'s here? */ + if (tag + sizeof(adata) > end) + continue; + + memcpy(adata, tag, sizeof(adata)); + + /* False match? */ + if (adata[0] != (void *)AUTODATA_MAGIC || adata[1] != tag) + continue; + + /* OK, check name. */ + if (!ptr_valid_batch_string(&batch, adata[3]) + || strcmp(name, adata[3]) != 0) + continue; + + if (!ptr_valid_batch_read(&batch, (char *)adata[2])) + continue; + + table = realloc(table, sizeof(void *) * (*nump + 1)); + if (!table) + break; + table[*nump] = adata[2]; + (*nump)++; + } + ptr_valid_batch_end(&batch); + return table; +} + +void autodata_free(void *table) +{ + free(table); +} +#endif diff --git a/ccan/autodata/autodata.h b/ccan/autodata/autodata.h new file mode 100644 index 00000000..d16c6a5c --- /dev/null +++ b/ccan/autodata/autodata.h @@ -0,0 +1,106 @@ +// Licensed under BSD-MIT: See LICENSE. +#ifndef CCAN_AUTODATA_H +#define CCAN_AUTODATA_H +#include "config.h" +#include +#include + +#if HAVE_SECTION_START_STOP + +/** + * AUTODATA_TYPE - declare the type for a given autodata name. + * @name: the name for this set of autodata + * @type: the type this autodata points to + * + * This macro is usually placed in a header: it must preceed any + * autodata functions in the file. + * + * Example: + * // My set of char pointers. + * AUTODATA_TYPE(names, char); + */ +#define AUTODATA_TYPE(name, type) \ + typedef type autodata_##name##_; \ + extern type *__start_autodata_##name[], *__stop_autodata_##name[] + +/** + * AUTODATA - add a pointer to this autodata set + * @name: the name of the set of autodata + * @ptr: the compile-time-known pointer + * + * This embeds @ptr into the binary, with the tag corresponding to + * @name (which must look like a valid identifier, no punctuation!). + * The type of @ptr must match that given by AUTODATA_TYPE. It is + * usually a file-level declaration. + * + * Example: + * // Put two char pointers into the names AUTODATA set. + * AUTODATA(names, "Arabella"); + * AUTODATA(names, "Alex"); + */ +#define AUTODATA(name, ptr) \ + static const autodata_##name##_ *NEEDED \ + __attribute__((section("autodata_" #name))) \ + AUTODATA_VAR_(name, __LINE__) = (ptr); + +/** + * autodata_get - get an autodata set + * @name: the name of the set of autodata + * @nump: the number of items in the set. + * + * This extract the embedded pointers matching @name. It may fail + * if malloc() fails, or if there is no AUTODATA at all. + * + * The return will be a pointer to an array of @type pointers (from + * AUTODATA_TYPE). + * + * Example: + * static void print_embedded_names(void) + * { + * unsigned int i, num; + * char **n = autodata_get(names, &num); + * + * for (i = 0; i < num; i++) + * printf("%s\n", n[i]); + * } + */ +#define autodata_get(name, nump) \ + ((autodata_##name##_ **) \ + autodata_get_section(__start_autodata_##name, \ + __stop_autodata_##name, (nump))) +#endif /* HAVE_SECTION_START_STOP */ + +/** + * autodata_free - free the table returned by autodata_get() + * @p: the table. + */ +void autodata_free(void *p); + +/* Internal functions. */ +#define AUTODATA_VAR__(name, line) autodata_##name##_##line +#define AUTODATA_VAR_(name, line) AUTODATA_VAR__(name, line) + +#if HAVE_SECTION_START_STOP +void *autodata_get_section(void *start, void *stop, size_t *nump); +#else +#define AUTODATA_TYPE(name, type) \ + typedef type autodata_##name##_; \ + static const void *autodata_##name##_ex = &autodata_##name##_ex + +#define AUTODATA_MAGIC ((long)0xFEEDA10DA7AF00D5ULL) +#define AUTODATA(name, ptr) \ + static const autodata_##name##_ *NEEDED \ + AUTODATA_VAR_(name, __LINE__)[4] = \ + { (void *)AUTODATA_MAGIC, \ + (void *)&AUTODATA_VAR_(name, __LINE__), \ + (ptr), \ + (void *)#name } + +#define autodata_get(name, nump) \ + ((autodata_##name##_ **) \ + autodata_make_table(&autodata_##name##_ex, #name, (nump))) + +void *autodata_make_table(const void *example, const char *name, size_t *nump); +#endif + +#endif /* CCAN_AUTODATA_H */ diff --git a/ccan/autodata/test/helper.c b/ccan/autodata/test/helper.c new file mode 100644 index 00000000..5655914d --- /dev/null +++ b/ccan/autodata/test/helper.c @@ -0,0 +1,6 @@ +/* Check that linking together works. */ +#include + +AUTODATA_TYPE(autostrings, char); + +AUTODATA(autostrings, "helper"); diff --git a/ccan/autodata/test/run-fools.c b/ccan/autodata/test/run-fools.c new file mode 100644 index 00000000..47a3b067 --- /dev/null +++ b/ccan/autodata/test/run-fools.c @@ -0,0 +1,71 @@ +#include +/* Include the C files directly. */ +#include +#include + +AUTODATA_TYPE(autostrings, char); + +AUTODATA(autostrings, "genuine"); + +#if !HAVE_SECTION_START_STOP +/* These are all fake, to test the various failure paths. */ +/* Hopefully fake_alpha or fake_omega will test run-past-end. */ +static const void *NEEDED fake_alpha[] = { (void *)AUTODATA_MAGIC }; + +/* Wrong magic in the middle. */ +static const void *NEEDED fake1[] = { (void *)(AUTODATA_MAGIC ^ 0x10000), + (void *)&fake1, + "fake1", + (void *)"autostrings" }; + +/* Wrong self pointer. */ +static const void *NEEDED fake2[] = { (void *)AUTODATA_MAGIC, + (void *)&fake1, + "fake2", + (void *)"autostrings" }; + +/* Wrong name. */ +static const void *NEEDED fake3[] = { (void *)AUTODATA_MAGIC, + (void *)&fake3, + "fake3", + (void *)"autostrings2" }; + +/* Invalid self-pointer. */ +static const void *NEEDED fake4[] = { (void *)AUTODATA_MAGIC, + (void *)1UL, + "fake4", + (void *)"autostrings" }; + +/* Invalid name pointer */ +static const void *NEEDED fake5[] = { (void *)AUTODATA_MAGIC, + (void *)&fake5, + "fake5", + (void *)1UL }; + +/* Invalid contents pointer */ +static const void *NEEDED fake6[] = { (void *)AUTODATA_MAGIC, + (void *)&fake6, + (char *)1UL, + (void *)"autostrings" }; + +static const void *NEEDED fake_omega[] = { (void *)AUTODATA_MAGIC }; +#endif + +int main(void) +{ + char **table; + size_t num; + + /* This is how many tests you plan to run */ + plan_tests(2); + + table = autodata_get(autostrings, &num); + ok1(num == 2); + ok1((!strcmp(table[0], "genuine") && !strcmp(table[1], "helper")) + || (!strcmp(table[1], "genuine") && !strcmp(table[0], "helper"))); + + autodata_free(table); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/autodata/test/run.c b/ccan/autodata/test/run.c new file mode 100644 index 00000000..d70faaa2 --- /dev/null +++ b/ccan/autodata/test/run.c @@ -0,0 +1,41 @@ +#include +/* Include the C files directly. */ +#include +#include + +AUTODATA_TYPE(autostrings, char); + +AUTODATA(autostrings, "hello"); +AUTODATA(autostrings, "world"); + +int main(void) +{ + char **table; + size_t num; + int i, hello = -1, world = -1, helper = -1; + + /* This is how many tests you plan to run */ + plan_tests(4); + + table = autodata_get(autostrings, &num); + ok1(num == 3); + + for (i = 0; i < num; i++) { + if (strcmp(table[i], "hello") == 0) + hello = i; + else if (strcmp(table[i], "world") == 0) + world = i; + else if (strcmp(table[i], "helper") == 0) + helper = i; + else + fail("Unknown entry %s", table[i]); + } + ok1(hello != -1); + ok1(world != -1); + ok1(helper != -1); + + autodata_free(table); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} -- 2.39.2