summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Momjian2006-05-30 14:09:32 +0000
committerBruce Momjian2006-05-30 14:09:32 +0000
commit4a3b50c59c800e101faeebf219c177dbe315d14c (patch)
treee0a86a2d9bfd9a6ff83594e75856d366f0eebf86
parentdbce76c8cf08d8fd2b9ccfac54391be8067d16de (diff)
Add pgmagic header block to store compile-time constants:
It now only checks four things: Major version number (7.4 or 8.1 for example) NAMEDATALEN FUNC_MAX_ARGS INDEX_MAX_KEYS The three constants were chosen because: 1. We document them in the config page in the docs 2. We mark them as changable in pg_config_manual.h 3. Changing any of these will break some of the more popular modules: FUNC_MAX_ARGS changes fmgr interface, every module uses this NAMEDATALEN changes syscache interface, every PL as well as tsearch uses this INDEX_MAX_KEYS breaks tsearch and anything using GiST. Martijn van Oosterhout
-rw-r--r--doc/src/sgml/xfunc.sgml27
-rw-r--r--src/backend/utils/fmgr/dfmgr.c45
-rw-r--r--src/include/pgmagic.h73
-rw-r--r--src/test/regress/regress.c3
4 files changed, 146 insertions, 2 deletions
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 3ba7aaeb34..5fc4172598 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1149,6 +1149,13 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision
</para>
<para>
+ After the module has been found, PostgreSQL looks for its magic block.
+ This block contains information about the environment a module was
+ compiled in. The server uses this to verify the module was compiled
+ under the same assumptions and environment as the backend.
+ </para>
+
+ <para>
The user ID the <productname>PostgreSQL</productname> server runs
as must be able to traverse the path to the file you intend to
load. Making the file or a higher-level directory not readable
@@ -1953,6 +1960,26 @@ concat_text(PG_FUNCTION_ARGS)
<listitem>
<para>
+ To ensure your module is not loaded into an incompatible backend, it
+ is recommended to include a magic block. To do this you must include
+ the header <filename>pgmagic.h</filename> and declare the block as
+ follows:
+ </para>
+
+<programlisting>
+#include "pgmagic.h"
+
+PG_MODULE_MAGIC;
+</programlisting>
+
+ <para>
+ If the module consists of multiple source files, this only needs to
+ be done in one of them.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
Symbol names defined within object files must not conflict
with each other or with symbols defined in the
<productname>PostgreSQL</productname> server executable. You
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index 86094a9ec1..f9566a9221 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -20,7 +20,7 @@
#include "dynloader.h"
#include "miscadmin.h"
#include "utils/dynamic_loader.h"
-
+#include "pgmagic.h"
/*
* List of dynamically loaded files (kept in malloc'd memory).
@@ -60,6 +60,9 @@ static char *find_in_dynamic_libpath(const char *basename);
static char *expand_dynamic_library_name(const char *name);
static char *substitute_libpath_macro(const char *name);
+/* Magic structure that module needs to match to be accepted */
+static Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
+
/*
* Load the specified dynamic-link library file, and look for a function
* named funcname in it. (funcname can be NULL to just load the file.)
@@ -116,6 +119,7 @@ load_external_function(char *filename, char *funcname,
if (file_scanner == NULL)
{
+ PGModuleMagicFunction magic_func;
/*
* File not loaded yet.
*/
@@ -146,6 +150,45 @@ load_external_function(char *filename, char *funcname,
fullname, load_error)));
}
+ /* Check the magic function to determine compatability */
+ magic_func = pg_dlsym( file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING );
+ if( magic_func )
+ {
+ Pg_magic_struct *module_magic_data = magic_func();
+ if( module_magic_data->len != magic_data.len ||
+ memcmp( module_magic_data, &magic_data, magic_data.len ) != 0 )
+ {
+ pg_dlclose( file_scanner->handle );
+
+ if( module_magic_data->len != magic_data.len )
+ ereport(ERROR,
+ (errmsg("incompatible library \"%s\": Magic block length mismatch",
+ fullname)));
+ if( module_magic_data->version != magic_data.version )
+ ereport(ERROR,
+ (errmsg("incompatible library \"%s\": Version mismatch",
+ fullname),
+ errdetail("Expected %d.%d, got %d.%d",
+ magic_data.version/100, magic_data.version % 100,
+ module_magic_data->version/100, module_magic_data->version % 100)));
+
+ if( module_magic_data->magic != magic_data.magic )
+ ereport(ERROR,
+ (errmsg("incompatible library \"%s\": Magic constant mismatch",
+ fullname),
+ errdetail("Expected 0x%08X, got 0x%08X",
+ magic_data.magic, magic_data.magic)));
+ /* Should never get here */
+ ereport(ERROR,(errmsg("incompatible library \"%s\": Reason unknown",
+ fullname)));
+ }
+ }
+ else
+ /* Currently we do not penalize modules for not having a
+ magic block, it would break every external module in
+ existance. At some point though... */
+ ereport(LOG, (errmsg("external library \"%s\" did not have magic block", fullname )));
+
/* OK to link it into list */
if (file_list == NULL)
file_list = file_scanner;
diff --git a/src/include/pgmagic.h b/src/include/pgmagic.h
new file mode 100644
index 0000000000..eb8194fcf6
--- /dev/null
+++ b/src/include/pgmagic.h
@@ -0,0 +1,73 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgmagic.h
+ * Defines a magic block that can mark a module in a way so show that
+ * it is compatible with the server it is being loaded into.
+ *
+ * This file is intended to be included into modules that wish to load
+ * themselves into the backend. All they need to do is include this header
+ * into one of the source files and include the line:
+ *
+ * PG_MODULE_MAGIC;
+ *
+ * The trailing semi-colon is optional. To work with versions of PostgreSQL
+ * that do not support this, you may put an #ifdef/endif block around it.
+ *
+ * Note, there is space available, particularly in the bitfield part. If it
+ * turns out that a change has happened within a major release that would
+ * require all modules to be recompiled, just setting one unused bit there
+ * will do the trick.
+ *
+ * Originally written by Martijn van Oosterhout <[email protected]>
+ *
+ * $PostgreSQL: $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PGMAGIC_H
+#define PGMAGIC_H
+
+#include "c.h"
+
+/* The main structure in which the magic is stored. the length field is used
+ * to detect major changes */
+
+typedef struct {
+ int len;
+ int version;
+ int magic;
+} Pg_magic_struct;
+
+/* Declare the module magic function. It needs to be a function as the dlsym
+ * in the backend is only guarenteed to work on functions, not data */
+
+typedef Pg_magic_struct *(*PGModuleMagicFunction) (void);
+
+#define PG_MAGIC_FUNCTION_NAME Pg_magic_func
+#define PG_MAGIC_FUNCTION_NAME_STRING "Pg_magic_func"
+
+#define PG_MODULE_MAGIC \
+extern DLLIMPORT Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \
+Pg_magic_struct * \
+PG_MAGIC_FUNCTION_NAME(void) \
+{ \
+ static Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \
+ return &Pg_magic_data; \
+}
+
+ /* Common user adjustable constants */
+#define PG_MODULE_MAGIC_CONST \
+ ((INDEX_MAX_KEYS << 0) + \
+ (FUNC_MAX_ARGS << 8) + \
+ (NAMEDATALEN << 16))
+
+/* Finally, the actual data block */
+#define PG_MODULE_MAGIC_DATA \
+{ \
+ sizeof(Pg_magic_struct), \
+ PG_VERSION_NUM / 100, /* Major version of postgres */ \
+ PG_MODULE_MAGIC_CONST, /* Constants users can configure */ \
+}
+
+#endif /* PGMAGIC_H */
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 26eaa06b69..c78c41ab9c 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -9,6 +9,7 @@
#include "utils/geo_decls.h" /* includes <math.h> */
#include "executor/executor.h" /* For GetAttributeByName */
#include "commands/sequence.h" /* for nextval() */
+#include "pgmagic.h"
#define P_MAXDIG 12
#define LDELIM '('
@@ -27,7 +28,7 @@ extern int oldstyle_length(int n, text *t);
extern Datum int44in(PG_FUNCTION_ARGS);
extern Datum int44out(PG_FUNCTION_ARGS);
-
+PG_MODULE_MAGIC;
/*
* Distance from a point to a path
*/