From 0ecf87244dea6530abed0d6f5172397e24fda922 Mon Sep 17 00:00:00 2001
From: Sriram RK <sriram.rk@in.ibm.com>
Date: Mon, 20 May 2024 01:38:59 -0500
Subject: [PATCH] AIX support, revert the changes from 0b16bb8776bb8. Trim
 AIX/XLC changes to support gcc only.

Review comments from Heikki.
---
 Makefile                                      |   2 -
 configure                                     |  37 +++---
 configure.ac                                  |  28 ++---
 doc/src/sgml/dfunc.sgml                       |  19 +++
 doc/src/sgml/installation.sgml                | 118 +++++++++++++++++-
 doc/src/sgml/runtime.sgml                     |  23 ++++
 meson.build                                   |  57 +++++----
 src/Makefile.shlib                            |  29 +++++
 src/backend/Makefile                          |  20 +++
 src/backend/meson.build                       |  15 +++
 src/backend/port/aix/mkldexport.sh            |  61 +++++++++
 src/backend/utils/error/elog.c                |   2 +
 src/backend/utils/misc/ps_status.c            |   4 +-
 src/bin/pg_basebackup/t/010_pg_basebackup.pl  |   3 +-
 src/bin/pg_verifybackup/t/008_untar.pl        |   3 +-
 src/bin/pg_verifybackup/t/010_client_untar.pl |   3 +-
 src/include/c.h                               |   4 +-
 src/include/port/aix.h                        |   6 +
 src/include/storage/s_lock.h                  |  25 +++-
 src/interfaces/libpq/Makefile                 |   2 +-
 src/interfaces/libpq/meson.build              |   5 +-
 src/makefiles/Makefile.aix                    |  39 ++++++
 src/port/README                               |   2 +-
 src/port/strerror.c                           |   2 +
 src/template/aix                              |  14 +++
 src/test/regress/Makefile                     |   5 +
 src/test/regress/expected/sanity_check.out    |  29 +++++
 src/test/regress/expected/test_setup.out      |   4 +
 src/test/regress/regress.c                    |  40 ++++++
 src/test/regress/sql/sanity_check.sql         |  26 ++++
 src/test/regress/sql/test_setup.sql           |   5 +
 src/tools/gen_export.pl                       |  11 +-
 src/tools/pginclude/headerscheck              |   1 +
 33 files changed, 555 insertions(+), 89 deletions(-)
 create mode 100755 src/backend/port/aix/mkldexport.sh
 create mode 100644 src/include/port/aix.h
 create mode 100644 src/makefiles/Makefile.aix
 create mode 100644 src/template/aix

diff --git a/Makefile b/Makefile
index 8a2ec9396b..9bc1a4ec17 100644
--- a/Makefile
+++ b/Makefile
@@ -13,8 +13,6 @@
 
 # AIX make defaults to building *every* target of the first rule.  Start with
 # a single-target, empty rule to make the other targets non-default.
-# (We don't support AIX anymore, but if someone tries to build on AIX anyway,
-# at least they'll get the instructions to run 'configure' first.)
 all:
 
 all check install installdirs installcheck installcheck-parallel uninstall clean distclean maintainer-clean dist distcheck world check-world install-world installcheck-world:
diff --git a/configure b/configure
index 89644f2249..f218b2e040 100755
--- a/configure
+++ b/configure
@@ -2988,6 +2988,7 @@ else
 # --with-template not given
 
 case $host_os in
+     aix*) template=aix ;;
   cygwin*|msys*) template=cygwin ;;
   darwin*) template=darwin ;;
 dragonfly*) template=netbsd ;;
@@ -6848,7 +6849,6 @@ if test x"$pgac_cv_prog_CXX_cxxflags__fno_strict_aliasing" = x"yes"; then
   CXXFLAGS="${CXXFLAGS} -fno-strict-aliasing"
 fi
 
-
 fi
 
 # If the compiler knows how to hide symbols, add the switch needed for that to
@@ -12860,7 +12860,8 @@ fi
 
 fi
 
-# Note: We can test for libldap_r only after we know PTHREAD_LIBS
+# Note: We can test for libldap_r only after we know PTHREAD_LIBS;
+# also, on AIX, we may need to have openssl in LIBS for this step.
 if test "$with_ldap" = yes ; then
   _LIBS="$LIBS"
   if test "$PORTNAME" != "win32"; then
@@ -16951,28 +16952,18 @@ _ACEOF
 
 
 # Compute maximum alignment of any basic type.
-#
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
-
-MAX_ALIGNOF=$ac_cv_alignof_double
+# We assume long's alignment is at least as strong as char, short, or int;
+# but we must check long long (if it is being used for int64) and double.
+# Note that we intentionally do not consider any types wider than 64 bits,
+# as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much of a penalty
+# for disk and memory space.
 
-if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'long' is greater than the alignment of 'double'" "$LINENO" 5
+MAX_ALIGNOF=$ac_cv_alignof_long
+if test $MAX_ALIGNOF -lt $ac_cv_alignof_double ; then
+  MAX_ALIGNOF=$ac_cv_alignof_double
 fi
-if test x"$HAVE_LONG_LONG_INT_64" = xyes && test $ac_cv_alignof_long_long_int -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'long long int' is greater than the alignment of 'double'" "$LINENO" 5
+if test x"$HAVE_LONG_LONG_INT_64" = xyes && test $MAX_ALIGNOF -lt $ac_cv_alignof_long_long_int ; then
+  MAX_ALIGNOF="$ac_cv_alignof_long_long_int"
 fi
 
 cat >>confdefs.h <<_ACEOF
@@ -17090,7 +17081,7 @@ else
 /* end confdefs.h.  */
 
 /* This must match the corresponding code in c.h: */
-#if defined(__GNUC__) || defined(__SUNPRO_C)
+#if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
 #define pg_attribute_aligned(a) __attribute__((aligned(a)))
 #elif defined(_MSC_VER)
 #define pg_attribute_aligned(a) __declspec(align(a))
diff --git a/configure.ac b/configure.ac
index c7322e292c..a059509cd9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,6 +62,7 @@ PGAC_ARG_REQ(with, template, [NAME], [override operating system template],
 # --with-template not given
 
 case $host_os in
+     aix*) template=aix ;;
   cygwin*|msys*) template=cygwin ;;
   darwin*) template=darwin ;;
 dragonfly*) template=netbsd ;;
@@ -1385,7 +1386,8 @@ if test "$with_zstd" = yes ; then
   AC_CHECK_LIB(zstd, ZSTD_compress, [], [AC_MSG_ERROR([library 'zstd' is required for ZSTD support])])
 fi
 
-# Note: We can test for libldap_r only after we know PTHREAD_LIBS
+# Note: We can test for libldap_r only after we know PTHREAD_LIBS;
+# also, on AIX, we may need to have openssl in LIBS for this step.
 if test "$with_ldap" = yes ; then
   _LIBS="$LIBS"
   if test "$PORTNAME" != "win32"; then
@@ -1643,8 +1645,9 @@ PGAC_TYPE_LOCALE_T
 # spelling it understands, because it conflicts with
 # __declspec(restrict). Therefore we define pg_restrict to the
 # appropriate definition, which presumably won't conflict.
+#
 AC_C_RESTRICT
-if test "$ac_cv_c_restrict" = "no"; then
+if test "$ac_cv_c_restrict" = "no" ; then
   pg_restrict=""
 else
   pg_restrict="$ac_cv_c_restrict"
@@ -1996,22 +1999,11 @@ fi
 AC_CHECK_ALIGNOF(double)
 
 # Compute maximum alignment of any basic type.
-#
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
-
-MAX_ALIGNOF=$ac_cv_alignof_double
+# We assume long's alignment is at least as strong as char, short, or int;
+# but we must check long long (if it is being used for int64) and double.
+# Note that we intentionally do not consider any types wider than 64 bits,
+# as allowing MAXIMUM_ALIGNOF to exceed 8 would be too much of a penalty
+# for disk and memory space.
 
 if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
   AC_MSG_ERROR([alignment of 'long' is greater than the alignment of 'double'])
diff --git a/doc/src/sgml/dfunc.sgml b/doc/src/sgml/dfunc.sgml
index b94aefcd0c..554f9fac4c 100644
--- a/doc/src/sgml/dfunc.sgml
+++ b/doc/src/sgml/dfunc.sgml
@@ -202,4 +202,23 @@ gcc -G -o foo.so foo.o
   server expects to find the shared library files.
  </para>
 
+<!--
+Under AIX, object files are compiled normally but building the shared
+library requires a couple of steps.  First, create the object file:
+.nf
+cc <other flags> -c foo.c
+.fi
+You must then create a symbol \*(lqexports\*(rq file for the object
+file:
+.nf
+mkldexport foo.o `pwd` &gt; foo.exp
+.fi
+Finally, you can create the shared library:
+.nf
+ld <other flags> -H512 -T512 -o foo.so -e _nostart \e
+   -bI:.../lib/postgres.exp -bE:foo.exp foo.o \e
+   -lm -lc 2>/dev/null
+.fi
+  -->
+
 </sect2>
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 1b32d5ca62..60e9a34a03 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -3401,7 +3401,7 @@ export MANPATH
   <para>
    <productname>PostgreSQL</productname> can be expected to work on current
    versions of these operating systems: Linux, Windows,
-   FreeBSD, OpenBSD, NetBSD, DragonFlyBSD, macOS, Solaris, and illumos.
+   FreeBSD, OpenBSD, NetBSD, DragonFlyBSD, macOS, AIX, Solaris, and illumos.
    Other Unix-like systems may also work but are not currently
    being tested.  In most cases, all CPU architectures supported by
    a given operating system will work.  Look in
@@ -3445,6 +3445,122 @@ export MANPATH
    installation issues.
   </para>
 
+  <sect2 id="installation-notes-aix">
+   <title>AIX</title>
+
+   <indexterm zone="installation-notes-aix">
+    <primary>AIX</primary>
+    <secondary>installation on</secondary>
+   </indexterm>
+
+   <para>
+    You can use GCC to build <productname>PostgreSQL</productname>
+    on <productname>AIX</productname>.
+   </para>
+
+   <para>
+    <productname>AIX</productname> versions before 7.1 are no longer
+    tested nor supported by the <productname>PostgreSQL</productname>
+    community.
+   </para>
+
+   <sect3 id="installation-notes-aix-mem-management">
+    <title>Memory Management</title>
+    <!-- https://archives.postgresql.org/message-id/603bgqmpl9.fsf@dba2.int.libertyrms.com -->
+
+    <para>
+     AIX can be somewhat peculiar with regards to the way it does
+     memory management.  You can have a server with many multiples of
+     gigabytes of RAM free, but still get out of memory or address
+     space errors when running applications.  One example
+     is loading of extensions failing with unusual errors.
+     For example, running as the owner of the PostgreSQL installation:
+<screen>
+=# CREATE EXTENSION plperl;
+ERROR:  could not load library "/opt/dbs/pgsql/lib/plperl.so": A memory address is not in the address space for the process.
+</screen>
+    Running as a non-owner in the group possessing the PostgreSQL
+    installation:
+<screen>
+=# CREATE EXTENSION plperl;
+ERROR:  could not load library "/opt/dbs/pgsql/lib/plperl.so": Bad address
+</screen>
+     Another example is out of memory errors in the PostgreSQL server
+     logs, with every memory allocation near or greater than 256 MB
+     failing.
+    </para>
+
+    <para>
+     The overall cause of all these problems is the default bittedness
+     and memory model used by the server process.  By default, all
+     binaries built on AIX are 32-bit.  This does not depend upon
+     hardware type or kernel in use.  These 32-bit processes are
+     limited to 4 GB of memory laid out in 256 MB segments using one
+     of a few models.  The default allows for less than 256 MB in the
+     heap as it shares a single segment with the stack.
+    </para>
+
+    <para>
+     In the case of the <literal>plperl</literal> example, above,
+     check your umask and the permissions of the binaries in your
+     PostgreSQL installation.  The binaries involved in that example
+     were 32-bit and installed as mode 750 instead of 755.  Due to the
+     permissions being set in this fashion, only the owner or a member
+     of the possessing group can load the library.  Since it isn't
+     world-readable, the loader places the object into the process'
+     heap instead of the shared library segments where it would
+     otherwise be placed.
+    </para>
+
+    <para>
+     The <quote>ideal</quote> solution for this is to use a 64-bit
+     build of PostgreSQL, but that is not always practical, because
+     systems with 32-bit processors can build, but not run, 64-bit
+     binaries.
+    </para>
+
+    <para>
+     If a 32-bit binary is desired, set <symbol>LDR_CNTRL</symbol> to
+     <literal>MAXDATA=0x<replaceable>n</replaceable>0000000</literal>,
+     where 1 &lt;= n &lt;= 8, before starting the PostgreSQL server,
+     and try different values and <filename>postgresql.conf</filename>
+     settings to find a configuration that works satisfactorily.  This
+     use of <symbol>LDR_CNTRL</symbol> tells AIX that you want the
+     server to have <symbol>MAXDATA</symbol> bytes set aside for the
+     heap, allocated in 256 MB segments.  When you find a workable
+     configuration,
+     <command>ldedit</command> can be used to modify the binaries so
+     that they default to using the desired heap size.  PostgreSQL can
+     also be rebuilt, passing <literal>configure
+     LDFLAGS="-Wl,-bmaxdata:0x<replaceable>n</replaceable>0000000"</literal>
+     to achieve the same effect.
+    </para>
+
+    <para>
+     For a 64-bit build, set <envar>OBJECT_MODE</envar> to 64 and
+     pass <literal>CC="gcc -maix64"</literal>
+     and <literal>LDFLAGS="-Wl,-bbigtoc"</literal>
+     to <command>configure</command>. 
+     If you omit the export of
+    <envar>OBJECT_MODE</envar>, your build may fail with linker errors.  When
+    <envar>OBJECT_MODE</envar> is set, it tells AIX's build utilities
+    such as <command>ar</command>, <command>as</command>, and <command>ld</command> what
+    type of objects to default to handling.
+    </para>
+
+    <para>
+     By default, overcommit of paging space can happen.  While we have
+     not seen this occur, AIX will kill processes when it runs out of
+     memory and the overcommit is accessed.  The closest to this that
+     we have seen is fork failing because the system decided that
+     there was not enough memory for another process.  Like many other
+     parts of AIX, the paging space allocation method and
+     out-of-memory kill is configurable on a system- or process-wide
+     basis if this becomes a problem.
+    </para>
+   </sect3>
+  </sect2>
+
   <sect2 id="installation-notes-cygwin">
    <title>Cygwin</title>
 
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 6047b8171d..64753d9c01 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -891,6 +891,29 @@ psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such
 
 
     <variablelist>
+     <varlistentry>
+      <term><systemitem class="osname">AIX</systemitem>
+      <indexterm><primary>AIX</primary><secondary>IPC configuration</secondary></indexterm>
+      </term>
+      <listitem>
+       <para>
+        It should not be necessary to do
+        any special configuration for such parameters as
+        <varname>SHMMAX</varname>, as it appears this is configured to
+        allow all memory to be used as shared memory.  That is the
+        sort of configuration commonly used for other databases such
+        as <application>DB/2</application>.</para>
+
+       <para> It might, however, be necessary to modify the global
+       <command>ulimit</command> information in
+       <filename>/etc/security/limits</filename>, as the default hard
+       limits for file sizes (<varname>fsize</varname>) and numbers of
+       files (<varname>nofiles</varname>) might be too low.
+       </para>
+      </listitem>
+     </varlistentry>
+
+
      <varlistentry>
       <term><systemitem class="osname">FreeBSD</systemitem>
       <indexterm><primary>FreeBSD</primary><secondary>IPC configuration</secondary></indexterm>
diff --git a/meson.build b/meson.build
index 1c0579d5a6..f1ac154cdc 100644
--- a/meson.build
+++ b/meson.build
@@ -196,7 +196,26 @@ endif
 # that purpose.
 portname = host_system
 
-if host_system == 'cygwin'
+if host_system == 'aix'
+  library_path_var = 'LIBPATH'
+
+  export_file_format = 'aix'
+  export_fmt = '-Wl,-bE:@0@'
+  mod_link_args_fmt = ['-Wl,-bI:@0@']
+  mod_link_with_dir = 'libdir'
+  mod_link_with_name = '@0@.imp'
+
+  # M:SRE sets a flag indicating that an object is a shared library. Seems to
+  # work in some circumstances without, but required in others.
+  ldflags_sl += '-Wl,-bM:SRE'
+  ldflags_be += '-Wl,-brtllib'
+
+  # Native memset() is faster, tested on:
+  # - AIX 5.1 and 5.2, XLC 6.0 (IBM's cc)
+  # - AIX 5.3 ML3, gcc 4.0.1
+  memset_loop_limit = 0
+
+elif host_system == 'cygwin'
   sema_kind = 'unnamed_posix'
   cppflags += '-D_GNU_SOURCE'
   dlsuffix = '.dll'
@@ -1490,11 +1509,11 @@ sizeof_long = cc.sizeof('long', args: test_c_args)
 cdata.set('SIZEOF_LONG', sizeof_long)
 if sizeof_long == 8
   cdata.set('HAVE_LONG_INT_64', 1)
-  pg_int64_type = 'long int'
+  cdata.set('PG_INT64_TYPE', 'long int')
   cdata.set_quoted('INT64_MODIFIER', 'l')
 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
   cdata.set('HAVE_LONG_LONG_INT_64', 1)
-  pg_int64_type = 'long long int'
+  cdata.set('PG_INT64_TYPE', 'long long int')
   cdata.set_quoted('INT64_MODIFIER', 'll')
 else
   error('do not know how to get a 64bit int')
@@ -1508,31 +1527,15 @@ endif
 # Determine memory alignment requirements for the basic C data types.
 
 alignof_types = ['short', 'int', 'long', 'double']
+maxalign = 0
 foreach t : alignof_types
   align = cc.alignment(t, args: test_c_args)
+  if maxalign < align
+    maxalign = align
+  endif
   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
 endforeach
-
-# Compute maximum alignment of any basic type.
-#
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that int64's alignment is at least as strong
-# as long, char, short, or int.  Note that we intentionally do not consider
-# any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
-# would be too much of a penalty for disk and memory space.
-alignof_double = cdata.get('ALIGNOF_DOUBLE')
-if cc.alignment(pg_int64_type, args: test_c_args) > alignof_double
-  error('alignment of int64 is greater than the alignment of double')
-endif
-cdata.set('MAXIMUM_ALIGNOF', alignof_double)
+cdata.set('MAXIMUM_ALIGNOF', maxalign)
 
 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
@@ -1581,7 +1584,7 @@ if cc.links('''
   if not meson.is_cross_build()
     r = cc.run('''
     /* This must match the corresponding code in c.h: */
-    #if defined(__GNUC__) || defined(__SUNPRO_C)
+    #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
     #elif defined(_MSC_VER)
     #define pg_attribute_aligned(a) __declspec(align(a))
@@ -2476,6 +2479,10 @@ endif
 # conflict.
 #
 # We assume C99 support, so we don't need to make this conditional.
+#
+# XXX: Historically we allowed platforms to disable restrict in template
+# files, but that was only added for AIX when building with XLC, which we
+# don't support yet.
 cdata.set('pg_restrict', '__restrict')
 
 
diff --git a/src/Makefile.shlib b/src/Makefile.shlib
index fa81f6ffdd..8ca51ca03f 100644
--- a/src/Makefile.shlib
+++ b/src/Makefile.shlib
@@ -106,6 +106,20 @@ ifdef SO_MAJOR_VERSION
 override CPPFLAGS += -DSO_MAJOR_VERSION=$(SO_MAJOR_VERSION)
 endif
 
+ifeq ($(PORTNAME), aix)
+  LINK.shared		= $(COMPILER)
+  ifdef SO_MAJOR_VERSION
+    shlib		= lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION)
+  endif
+  haslibarule   = yes
+  # $(exports_file) is also usable as an import file
+  exports_file		= lib$(NAME).exp
+  BUILD.exports		= ( echo '\#! $(shlib)'; $(AWK) '/^[^\#]/ {printf "%s\n",$$1}' $< ) > $@
+  ifneq (,$(SHLIB_EXPORTS))
+    LINK.shared		+= -Wl,-bE:$(exports_file)
+  endif
+endif
+
 ifeq ($(PORTNAME), darwin)
   ifdef soname
     # linkable library
@@ -254,6 +268,14 @@ $(stlib): $(OBJS) | $(SHLIB_PREREQS)
 	touch $@
 endif #haslibarule
 
+# AIX wraps shared libraries inside a static library, can be used both
+# for static and shared linking
+ifeq ($(PORTNAME), aix)
+$(stlib): $(shlib)
+	rm -f $(stlib)
+	$(AR) $(AROPT) $(stlib) $(shlib)
+endif # aix
+
 ifeq (,$(filter cygwin win32,$(PORTNAME)))
 
 # Normal case
@@ -267,8 +289,11 @@ ifneq ($(shlib), $(shlib_major))
 endif
 # Make sure we have a link to a name without any version numbers
 ifneq ($(shlib), $(shlib_bare))
+# except on AIX, where that's not a thing
+ifneq ($(PORTNAME), aix)
 	rm -f $(shlib_bare)
 	$(LN_S) $(shlib) $(shlib_bare)
+endif # aix
 endif # shlib_bare
 endif # shlib_major
 
@@ -376,6 +401,9 @@ install-lib-static: $(stlib) installdirs-lib
 
 install-lib-shared: $(shlib) installdirs-lib
 ifdef soname
+# we don't install $(shlib) on AIX
+# (see http://archives.postgresql.org/message-id/52EF20B2E3209443BC37736D00C3C1380A6E79FE@EXADV1.host.magwien.gv.at)
+ifneq ($(PORTNAME), aix)
 	$(INSTALL_SHLIB) $< '$(DESTDIR)$(libdir)/$(shlib)'
 ifneq ($(PORTNAME), cygwin)
 ifneq ($(PORTNAME), win32)
@@ -391,6 +419,7 @@ ifneq ($(shlib), $(shlib_bare))
 endif
 endif # not win32
 endif # not cygwin
+endif # not aix
 ifneq (,$(findstring $(PORTNAME),win32 cygwin))
 	$(INSTALL_SHLIB) $< '$(DESTDIR)$(bindir)/$(shlib)'
 endif
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 6700aec039..954b14962f 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -62,12 +62,14 @@ all: submake-libpgport submake-catalog-headers submake-utils-headers postgres $(
 
 ifneq ($(PORTNAME), cygwin)
 ifneq ($(PORTNAME), win32)
+ifneq ($(PORTNAME), aix)
 
 postgres: $(OBJS)
 	$(CC) $(CFLAGS) $(call expand_subsys,$^) $(LDFLAGS) $(LIBS) -o $@
 
 endif
 endif
+endif
 
 ifeq ($(PORTNAME), cygwin)
 
@@ -94,6 +96,24 @@ libpostgres.a: postgres
 
 endif # win32
 
+ifeq ($(PORTNAME), aix)
+
+postgres: $(POSTGRES_IMP)
+	$(CC) $(CFLAGS) $(call expand_subsys,$(OBJS)) $(LDFLAGS) -Wl,-bE:$(top_builddir)/src/backend/$(POSTGRES_IMP) $(LIBS) -Wl,-brtllib -o $@
+
+# Linking to a single .o with -r is a lot faster than building a .a or passing
+# all objects to MKLDEXPORT.
+#
+# It looks alluring to use $(CC) -r instead of ld -r, but that doesn't
+# trivially work with gcc, due to gcc specific static libraries linked in with
+# -r.
+$(POSTGRES_IMP): $(OBJS)
+	ld -r -o SUBSYS.o $(call expand_subsys,$^)
+	$(MKLDEXPORT) SUBSYS.o . > $@
+	@rm -f SUBSYS.o
+
+endif # aix
+
 $(top_builddir)/src/port/libpgport_srv.a: | submake-libpgport
 
 
diff --git a/src/backend/meson.build b/src/backend/meson.build
index 436c04af08..8767aaba67 100644
--- a/src/backend/meson.build
+++ b/src/backend/meson.build
@@ -91,6 +91,21 @@ if cc.get_id() == 'msvc'
   # be restricted to b_pch=true.
   backend_link_with += postgres_lib
 
+elif host_system == 'aix'
+  # The '.' argument leads mkldexport.sh to emit "#! .", which refers to the
+  # main executable, allowing extension libraries to resolve their undefined
+  # symbols to symbols in the postgres binary.
+  postgres_imp = custom_target('postgres.imp',
+    command: [files('port/aix/mkldexport.sh'), '@INPUT@', '.'],
+    input: postgres_lib,
+    output: 'postgres.imp',
+    capture: true,
+    install: true,
+    install_dir: dir_lib,
+    build_by_default: false,
+  )
+  backend_link_args += '-Wl,-bE:@0@'.format(postgres_imp.full_path())
+  backend_link_depends += postgres_imp
 endif
 
 backend_input = []
diff --git a/src/backend/port/aix/mkldexport.sh b/src/backend/port/aix/mkldexport.sh
new file mode 100755
index 0000000000..adf3793e86
--- /dev/null
+++ b/src/backend/port/aix/mkldexport.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# mkldexport
+#	create an AIX exports file from an object file
+#
+# src/backend/port/aix/mkldexport.sh
+#
+# Usage:
+#	mkldexport objectfile [location]
+# where
+#	objectfile is the current location of the object file.
+#	location is the eventual (installed) location of the
+#		object file (if different from the current
+#		working directory).
+#
+# [This file comes from the Postgres 4.2 distribution. - ay 7/95]
+#
+# Header: /usr/local/devel/postgres/src/tools/mkldexport/RCS/mkldexport.sh,v 1.2 1994/03/13 04:59:12 aoki Exp
+#
+
+# setting this to nm -B might be better
+# ... due to changes in AIX 4.x ...
+# ... let us search in different directories - Gerhard Reithofer
+if [ -x /usr/ucb/nm ]
+then NM=/usr/ucb/nm
+elif [ -x /usr/bin/nm ]
+then NM=/usr/bin/nm
+elif [ -x /usr/ccs/bin/nm ]
+then NM=/usr/ccs/bin/nm
+elif [ -x /usr/usg/bin/nm ]
+then NM=/usr/usg/bin/nm
+else echo "Fatal error: cannot find `nm' ... please check your installation."
+     exit 1
+fi
+
+CMDNAME=`basename $0`
+if [ -z "$1" ]; then
+	echo "Usage: $CMDNAME object [location]"
+	exit 1
+fi
+OBJNAME=`basename $1`
+if [ "`basename $OBJNAME`" != "`basename $OBJNAME .o`" ]; then
+	OBJNAME=`basename $OBJNAME .o`.so
+fi
+if [ -z "$2" ]; then
+	echo '#!'
+else
+	if [ "$2" = "." ]; then
+		# for the base executable (AIX 4.2 and up)
+		echo '#! .'
+	else
+		echo '#!' $2
+	fi
+fi
+$NM -BCg $1 | \
+	egrep ' [TDB] ' | \
+	sed -e 's/.* //' | \
+	egrep -v '\$' | \
+	sed -e 's/^[.]//' | \
+	sort | \
+	uniq
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index d91a85cb2d..7b51e94f4a 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -908,7 +908,9 @@ errcode_for_file_access(void)
 			/* Wrong object type or state */
 		case ENOTDIR:			/* Not a directory */
 		case EISDIR:			/* Is a directory */
+#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
 		case ENOTEMPTY:			/* Directory not empty */
+#endif
 			edata->sqlerrcode = ERRCODE_WRONG_OBJECT_TYPE;
 			break;
 
diff --git a/src/backend/utils/misc/ps_status.c b/src/backend/utils/misc/ps_status.c
index 9da6377402..532eac31b4 100644
--- a/src/backend/utils/misc/ps_status.c
+++ b/src/backend/utils/misc/ps_status.c
@@ -50,7 +50,7 @@ bool		update_process_title = DEFAULT_UPDATE_PROCESS_TITLE;
 #define PS_USE_SETPROCTITLE_FAST
 #elif defined(HAVE_SETPROCTITLE)
 #define PS_USE_SETPROCTITLE
-#elif defined(__linux__) || defined(__sun) || defined(__darwin__)
+#elif defined(__linux__) || defined(_AIX) || defined(__sun) || defined(__darwin__)
 #define PS_USE_CLOBBER_ARGV
 #elif defined(WIN32)
 #define PS_USE_WIN32
@@ -60,7 +60,7 @@ bool		update_process_title = DEFAULT_UPDATE_PROCESS_TITLE;
 
 
 /* Different systems want the buffer padded differently */
-#if defined(__linux__) || defined(__darwin__)
+#if defined(_AIX) || defined(__linux__) || defined(__darwin__)
 #define PS_PADDING '\0'
 #else
 #define PS_PADDING ' '
diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
index 2d4b75a6fe..ce936959c1 100644
--- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl
+++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
@@ -401,7 +401,8 @@ SKIP:
 {
 	my $tar = $ENV{TAR};
 	# don't check for a working tar here, to accommodate various odd
-	# cases. If tar doesn't work the init_from_backup below will fail.
+	# cases such as AIX. If tar doesn't work the init_from_backup below
+	# will fail.
 	skip "no tar program available", 1
 	  if (!defined $tar || $tar eq '');
 
diff --git a/src/bin/pg_verifybackup/t/008_untar.pl b/src/bin/pg_verifybackup/t/008_untar.pl
index 7a09f3b75b..30d9f3f7f0 100644
--- a/src/bin/pg_verifybackup/t/008_untar.pl
+++ b/src/bin/pg_verifybackup/t/008_untar.pl
@@ -104,7 +104,8 @@ for my $tc (@test_configuration)
 		{
 			my $tar = $ENV{TAR};
 			# don't check for a working tar here, to accommodate various odd
-			# cases. If tar doesn't work the init_from_backup below will fail.
+			# cases such as AIX. If tar doesn't work the init_from_backup below
+			# will fail.
 			skip "no tar program available", 1
 			  if (!defined $tar || $tar eq '');
 
diff --git a/src/bin/pg_verifybackup/t/010_client_untar.pl b/src/bin/pg_verifybackup/t/010_client_untar.pl
index 8c076d46de..45010d79ac 100644
--- a/src/bin/pg_verifybackup/t/010_client_untar.pl
+++ b/src/bin/pg_verifybackup/t/010_client_untar.pl
@@ -134,7 +134,8 @@ for my $tc (@test_configuration)
 		{
 			my $tar = $ENV{TAR};
 			# don't check for a working tar here, to accommodate various odd
-			# cases. If tar doesn't work the init_from_backup below will fail.
+			# cases such as AIX. If tar doesn't work the init_from_backup below
+			# will fail.
 			skip "no tar program available", 1
 			  if (!defined $tar || $tar eq '');
 
diff --git a/src/include/c.h b/src/include/c.h
index dc1841346c..641cd9bdf1 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -182,7 +182,7 @@
 #define PG_USED_FOR_ASSERTS_ONLY pg_attribute_unused()
 #endif
 
-/* GCC supports format attributes */
+/* GCC support format attributes */
 #if defined(__GNUC__)
 #define pg_attribute_format_arg(a) __attribute__((format_arg(a)))
 #define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a)))
@@ -192,7 +192,7 @@
 #endif
 
 /* GCC and Sunpro support aligned, packed and noreturn */
-#if defined(__GNUC__) || defined(__SUNPRO_C)
+#if defined(__GNUC__) || defined(__SUNPRO_C) 
 #define pg_attribute_aligned(a) __attribute__((aligned(a)))
 #define pg_attribute_noreturn() __attribute__((noreturn))
 #define pg_attribute_packed() __attribute__((packed))
diff --git a/src/include/port/aix.h b/src/include/port/aix.h
new file mode 100644
index 0000000000..1c2d166791
--- /dev/null
+++ b/src/include/port/aix.h
@@ -0,0 +1,6 @@
+/*
+ * src/include/port/aix.h
+ */
+#define CLASS_CONFLICT
+#define DISABLE_XOPEN_NLS
+
diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h
index 29ac6cdcd9..2f6eb5df2a 100644
--- a/src/include/storage/s_lock.h
+++ b/src/include/storage/s_lock.h
@@ -424,17 +424,15 @@ tas(volatile slock_t *lock)
 	__asm__ __volatile__(
 "	lwarx   %0,0,%3,1	\n"
 "	cmpwi   %0,0		\n"
-"	bne     1f			\n"
+"	bne     $+16		\n"		/* branch to li %1,1 */
 "	addi    %0,%0,1		\n"
 "	stwcx.  %0,0,%3		\n"
-"	beq     2f			\n"
-"1: \n"
+"	beq     $+12		\n"		/* branch to lwsync */
 "	li      %1,1		\n"
-"	b       3f			\n"
-"2: \n"
+"	b       $+12		\n"		/* branch to end of asm sequence */
 "	lwsync				\n"
 "	li      %1,0		\n"
-"3: \n"
+
 :	"=&b"(_t), "=r"(_res), "+m"(*lock)
 :	"r"(lock)
 :	"memory", "cc");
@@ -662,6 +660,21 @@ tas(volatile slock_t *lock)
 
 #if !defined(HAS_TEST_AND_SET)	/* We didn't trigger above, let's try here */
 
+#if defined(_AIX)	/* AIX */
+/*
+ * AIX (POWER)
+ */
+#define HAS_TEST_AND_SET
+
+#include <sys/atomic_op.h>
+
+typedef int slock_t;
+
+#define TAS(lock)			_check_lock((slock_t *) (lock), 0, 1)
+#define S_UNLOCK(lock)		_clear_lock((slock_t *) (lock), 0)
+#endif	 /* _AIX */
+
+
 /* These are in sunstudio_(sparc|x86).s */
 
 #if defined(__SUNPRO_C) && (defined(__i386) || defined(__x86_64__) || defined(__sparc__) || defined(__sparc))
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index b36a765764..154429d4d1 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -114,7 +114,7 @@ backend_src = $(top_srcdir)/src/backend
 # coding rule.
 libpq-refs-stamp: $(shlib)
 ifneq ($(enable_coverage), yes)
-ifeq (,$(filter solaris,$(PORTNAME)))
+ifeq (,$(filter aix solaris,$(PORTNAME)))
 	@if nm -A -u $< 2>/dev/null | grep -v -e __cxa_atexit -e __tsan_func_exit | grep exit; then \
 		echo 'libpq must not be calling any function which invokes exit'; exit 1; \
 	fi
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index ed2a4048d1..7e2017daf0 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -54,8 +54,9 @@ libpq_c_args = ['-DSO_MAJOR_VERSION=5']
 #    libpq_st, and {pgport,common}_shlib for libpq_sh
 #
 # We could try to avoid building the source files twice, but it probably adds
-# more complexity than its worth (reusing object files requires also linking
-# to the library on windows or breaks precompiled headers).
+# more complexity than its worth (AIX doesn't support link_whole yet, reusing
+# object files requires also linking to the library on windows or breaks
+# precompiled headers).
 libpq_st = static_library('libpq',
   libpq_sources,
   include_directories: [libpq_inc],
diff --git a/src/makefiles/Makefile.aix b/src/makefiles/Makefile.aix
new file mode 100644
index 0000000000..dd16a7a037
--- /dev/null
+++ b/src/makefiles/Makefile.aix
@@ -0,0 +1,39 @@
+# MAKE_EXPORTS is required for svr4 loaders that want a file of
+# symbol names to tell them what to export/import.
+MAKE_EXPORTS= true
+
+# -blibpath must contain ALL directories where we should look for libraries
+libpath := $(shell echo $(subst -L,:,$(filter -L/%,$(LDFLAGS))) | sed -e's/ //g'):/usr/lib:/lib
+
+# when building with gcc, need to make sure that libgcc can be found
+ifeq ($(GCC), yes)
+libpath := $(libpath):$(dir $(shell gcc -print-libgcc-file-name))
+endif
+
+rpath = -Wl,-blibpath:'$(rpathdir)$(libpath)'
+
+LDFLAGS_SL += -Wl,-bnoentry -Wl,-H512 -Wl,-bM:SRE
+
+# gcc needs to know it's building a shared lib, otherwise it'll not emit
+# correct code / link to the right support libraries
+ifeq ($(GCC), yes)
+LDFLAGS_SL += -shared
+endif
+
+# env var name to use in place of LD_LIBRARY_PATH
+ld_library_path_var = LIBPATH
+
+
+POSTGRES_IMP= postgres.imp
+
+ifdef PGXS
+BE_DLLLIBS= -Wl,-bI:$(pkglibdir)/$(POSTGRES_IMP)
+else
+BE_DLLLIBS= -Wl,-bI:$(top_builddir)/src/backend/$(POSTGRES_IMP)
+endif
+
+MKLDEXPORT_DIR=src/backend/port/aix
+MKLDEXPORT=$(top_srcdir)/$(MKLDEXPORT_DIR)/mkldexport.sh
+
+%$(DLSUFFIX): %.o
+	$(CC) $(CFLAGS) $*.o $(LDFLAGS) $(LDFLAGS_SL) -o $@ $(BE_DLLLIBS)
diff --git a/src/port/README b/src/port/README
index ed5c54a72f..97f18a6233 100644
--- a/src/port/README
+++ b/src/port/README
@@ -28,5 +28,5 @@ applications.
 from libpgport are linked first.  This avoids having applications
 dependent on symbols that are _used_ by libpq, but not intended to be
 exported by libpq.  libpq's libpgport usage changes over time, so such a
-dependency is a problem.  Windows, Linux, and macOS use an export
+dependency is a problem.  Windows, Linux, AIX, and macOS use an export
 list to control the symbols exported by libpq.
diff --git a/src/port/strerror.c b/src/port/strerror.c
index 4918ba821c..1070a49802 100644
--- a/src/port/strerror.c
+++ b/src/port/strerror.c
@@ -214,8 +214,10 @@ get_errno_symbol(int errnum)
 			return "ENOTCONN";
 		case ENOTDIR:
 			return "ENOTDIR";
+#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
 		case ENOTEMPTY:
 			return "ENOTEMPTY";
+#endif
 		case ENOTSOCK:
 			return "ENOTSOCK";
 #ifdef ENOTSUP
diff --git a/src/template/aix b/src/template/aix
new file mode 100644
index 0000000000..0a788676c0
--- /dev/null
+++ b/src/template/aix
@@ -0,0 +1,14 @@
+# src/template/aix
+
+  # Due to a compiler bug, see 20171013023536.GA492146@rfd.leadboat.com for details,
+  # force restrict not to be used when compiling with xlc.
+#  FORCE_DISABLE_RESTRICT=yes
+#fi
+
+# Extra CFLAGS for code that will go into a shared library
+CFLAGS_SL=""
+
+# Native memset() is faster, tested on:
+# 	AIX 5.1 and 5.2, XLC 6.0 (IBM's cc)
+# 	AIX 5.3 ML3, gcc 4.0.1
+MEMSET_LOOP_LIMIT=0
diff --git a/src/test/regress/Makefile b/src/test/regress/Makefile
index 6409a485e8..7c665ff892 100644
--- a/src/test/regress/Makefile
+++ b/src/test/regress/Makefile
@@ -7,6 +7,11 @@
 # GNU make uses a make file named "GNUmakefile" in preference to "Makefile"
 # if it exists.  Postgres is shipped with a "GNUmakefile".
 
+
+# AIX make defaults to building *every* target of the first rule.  Start with
+# a single-target, empty rule to make the other targets non-default.
+all:
+
 all install clean check installcheck:
 	@echo "You must use GNU make to use Postgres.  It may be installed"
 	@echo "on your system with the name 'gmake'."
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 8370c1561c..c5c675b750 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -25,3 +25,32 @@ SELECT relname, relkind
 ---------+---------
 (0 rows)
 
+--
+-- When ALIGNOF_DOUBLE==4 (e.g. AIX), the C ABI may impose 8-byte alignment on
+-- some of the C types that correspond to TYPALIGN_DOUBLE SQL types.  To ensure
+-- catalog C struct layout matches catalog tuple layout, arrange for the tuple
+-- offset of each fixed-width, attalign='d' catalog column to be divisible by 8
+-- unconditionally.  Keep such columns before the first NameData column of the
+-- catalog, since packagers can override NAMEDATALEN to an odd number.
+--
+WITH check_columns AS (
+ SELECT relname, attname,
+  array(
+   SELECT t.oid
+    FROM pg_type t JOIN pg_attribute pa ON t.oid = pa.atttypid
+    WHERE pa.attrelid = a.attrelid AND
+          pa.attnum > 0 AND pa.attnum < a.attnum
+    ORDER BY pa.attnum) AS coltypes
+ FROM pg_attribute a JOIN pg_class c ON c.oid = attrelid
+  JOIN pg_namespace n ON c.relnamespace = n.oid
+ WHERE attalign = 'd' AND relkind = 'r' AND
+  attnotnull AND attlen <> -1 AND n.nspname = 'pg_catalog'
+)
+SELECT relname, attname, coltypes, get_columns_length(coltypes)
+ FROM check_columns
+ WHERE get_columns_length(coltypes) % 8 != 0 OR
+       'name'::regtype::oid = ANY(coltypes);
+ relname | attname | coltypes | get_columns_length 
+---------+---------+----------+--------------------
+(0 rows)
+
diff --git a/src/test/regress/expected/test_setup.out b/src/test/regress/expected/test_setup.out
index 3d0eeec996..5d9e6bf12b 100644
--- a/src/test/regress/expected/test_setup.out
+++ b/src/test/regress/expected/test_setup.out
@@ -209,6 +209,10 @@ CREATE FUNCTION ttdummy ()
     RETURNS trigger
     AS :'regresslib'
     LANGUAGE C;
+CREATE FUNCTION get_columns_length(oid[])
+    RETURNS int
+    AS :'regresslib'
+    LANGUAGE C STRICT STABLE PARALLEL SAFE;
 -- Use hand-rolled hash functions and operator classes to get predictable
 -- result on different machines.  The hash function for int4 simply returns
 -- the sum of the values passed to it and the one for text returns the length
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index deffaea578..cc633864d7 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -1221,3 +1221,43 @@ binary_coercible(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(IsBinaryCoercible(srctype, targettype));
 }
+
+/*
+ * Return the length of the portion of a tuple consisting of the given array
+ * of data types.  The input data types must be fixed-length data types.
+ */
+PG_FUNCTION_INFO_V1(get_columns_length);
+Datum
+get_columns_length(PG_FUNCTION_ARGS)
+{
+	ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
+	Oid		   *type_oids;
+	int			ntypes;
+	int			column_offset = 0;
+
+	if (ARR_HASNULL(ta) && array_contains_nulls(ta))
+		elog(ERROR, "argument must not contain nulls");
+
+	if (ARR_NDIM(ta) > 1)
+		elog(ERROR, "argument must be empty or one-dimensional array");
+
+	type_oids = (Oid *) ARR_DATA_PTR(ta);
+	ntypes = ArrayGetNItems(ARR_NDIM(ta), ARR_DIMS(ta));
+	for (int i = 0; i < ntypes; i++)
+	{
+		Oid			typeoid = type_oids[i];
+		int16		typlen;
+		bool		typbyval;
+		char		typalign;
+
+		get_typlenbyvalalign(typeoid, &typlen, &typbyval, &typalign);
+
+		/* the data type must be fixed-length */
+		if (typlen < 0)
+			elog(ERROR, "type %u is not fixed-length data type", typeoid);
+
+		column_offset = att_align_nominal(column_offset + typlen, typalign);
+	}
+
+	PG_RETURN_INT32(column_offset);
+}
diff --git a/src/test/regress/sql/sanity_check.sql b/src/test/regress/sql/sanity_check.sql
index 162e5324b5..7f338d191c 100644
--- a/src/test/regress/sql/sanity_check.sql
+++ b/src/test/regress/sql/sanity_check.sql
@@ -19,3 +19,29 @@ SELECT relname, relkind
   FROM pg_class
  WHERE relkind IN ('v', 'c', 'f', 'p', 'I')
        AND relfilenode <> 0;
+
+--
+-- When ALIGNOF_DOUBLE==4 (e.g. AIX), the C ABI may impose 8-byte alignment on
+-- some of the C types that correspond to TYPALIGN_DOUBLE SQL types.  To ensure
+-- catalog C struct layout matches catalog tuple layout, arrange for the tuple
+-- offset of each fixed-width, attalign='d' catalog column to be divisible by 8
+-- unconditionally.  Keep such columns before the first NameData column of the
+-- catalog, since packagers can override NAMEDATALEN to an odd number.
+--
+WITH check_columns AS (
+ SELECT relname, attname,
+  array(
+   SELECT t.oid
+    FROM pg_type t JOIN pg_attribute pa ON t.oid = pa.atttypid
+    WHERE pa.attrelid = a.attrelid AND
+          pa.attnum > 0 AND pa.attnum < a.attnum
+    ORDER BY pa.attnum) AS coltypes
+ FROM pg_attribute a JOIN pg_class c ON c.oid = attrelid
+  JOIN pg_namespace n ON c.relnamespace = n.oid
+ WHERE attalign = 'd' AND relkind = 'r' AND
+  attnotnull AND attlen <> -1 AND n.nspname = 'pg_catalog'
+)
+SELECT relname, attname, coltypes, get_columns_length(coltypes)
+ FROM check_columns
+ WHERE get_columns_length(coltypes) % 8 != 0 OR
+       'name'::regtype::oid = ANY(coltypes);
diff --git a/src/test/regress/sql/test_setup.sql b/src/test/regress/sql/test_setup.sql
index 06b0e2121f..1b2d434683 100644
--- a/src/test/regress/sql/test_setup.sql
+++ b/src/test/regress/sql/test_setup.sql
@@ -257,6 +257,11 @@ CREATE FUNCTION ttdummy ()
     AS :'regresslib'
     LANGUAGE C;
 
+CREATE FUNCTION get_columns_length(oid[])
+    RETURNS int
+    AS :'regresslib'
+    LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
 -- Use hand-rolled hash functions and operator classes to get predictable
 -- result on different machines.  The hash function for int4 simply returns
 -- the sum of the values passed to it and the one for text returns the length
diff --git a/src/tools/gen_export.pl b/src/tools/gen_export.pl
index d9fdaaaf6d..888c8a197a 100644
--- a/src/tools/gen_export.pl
+++ b/src/tools/gen_export.pl
@@ -16,11 +16,12 @@ GetOptions(
 	'input:s' => \$input,
 	'output:s' => \$output) or die "wrong arguments";
 
-if (not(   $format eq 'darwin'
+if (not(   $format eq 'aix'
+		or $format eq 'darwin'
 		or $format eq 'gnu'
 		or $format eq 'win'))
 {
-	die "$0: $format is not yet handled (only darwin, gnu, win are)\n";
+	die "$0: $format is not yet handled (only aix, darwin, gnu, win are)\n";
 }
 
 open(my $input_handle, '<', $input)
@@ -55,7 +56,11 @@ while (<$input_handle>)
 	}
 	elsif (/^(\S+)\s+(\S+)/)
 	{
-		if ($format eq 'darwin')
+		if ($format eq 'aix')
+		{
+			print $output_handle "$1\n";
+		}
+		elsif ($format eq 'darwin')
 		{
 			print $output_handle "_$1\n";
 		}
diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck
index 4a157d0a5f..cfd8c6896c 100755
--- a/src/tools/pginclude/headerscheck
+++ b/src/tools/pginclude/headerscheck
@@ -85,6 +85,7 @@ do
 
 	# These files are platform-specific, and c.h will include the
 	# one that's relevant for our current platform anyway.
+	test "$f" = src/include/port/aix.h && continue
 	test "$f" = src/include/port/cygwin.h && continue
 	test "$f" = src/include/port/darwin.h && continue
 	test "$f" = src/include/port/freebsd.h && continue
-- 
2.41.0

