From c66d280c37e5a65a1bddd161d90ddf040bb0ca26 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 14 Feb 2026 18:19:56 -0500
Subject: [PATCH v6 2/7] Revert removal of AIX support.

This commit is as nearly as possible a straight revert of commits
0b16bb877 and e6bb491bf, except that:
* I did not put back anything to do with supporting XLC.
* I did some minimal updating of the documentation to reflect that.
* The changes in MAXALIGN calculation are no longer needed.
* s_lock.h changes are no longer needed either.

More work on the docs is needed, in particular we'll likely
rip out the discussion of how to make it work in 32 bits.

This is mainly to create a reference point for comparing what
the IBM crew has done.
---
 Makefile                           |   2 -
 configure                          |   4 +-
 configure.ac                       |   4 +-
 doc/src/sgml/installation.sgml     | 121 ++++++++++++++++++++++++++++-
 doc/src/sgml/runtime.sgml          |  23 ++++++
 meson.build                        |  21 ++++-
 src/Makefile.shlib                 |  29 +++++++
 src/backend/Makefile               |  38 ++++++++-
 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/include/port/aix.h             |   3 +
 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                   |   9 +++
 src/test/regress/Makefile          |   5 ++
 src/tools/gen_export.pl            |  11 ++-
 src/tools/pginclude/headerscheck   |   1 +
 22 files changed, 385 insertions(+), 18 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 8a2ec9396b6..9bc1a4ec17b 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 ef6ca1422a5..215f56bb227 100755
--- a/configure
+++ b/configure
@@ -3022,6 +3022,7 @@ else
 # --with-template not given
 
 case $host_os in
+     aix*) template=aix ;;
   cygwin*|msys*) template=cygwin ;;
   darwin*) template=darwin ;;
 dragonfly*) template=netbsd ;;
@@ -13378,7 +13379,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
diff --git a/configure.ac b/configure.ac
index 5edcccc0a15..e5255d55587 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 ;;
@@ -1469,7 +1470,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
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index c903ccff988..554d1bde7e0 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -3439,7 +3439,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
@@ -3461,7 +3461,7 @@ export MANPATH
    Historical versions of <productname>PostgreSQL</productname> or POSTGRES
    also ran on CPU architectures including Alpha, Itanium, M32R, M68K,
    M88K, NS32K, PA-RISC, SuperH, and VAX,
-   and operating systems including 4.3BSD, AIX, BEOS,
+   and operating systems including 4.3BSD, BEOS,
    BSD/OS, DG/UX, Dynix, HP-UX, IRIX, NeXTSTEP, QNX, SCO, SINIX, Sprite, SunOS,
    Tru64 UNIX, and ULTRIX.
   </para>
@@ -3484,6 +3484,123 @@ 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 must use GCC
+    to build <productname>PostgreSQL</productname>
+    on <productname>AIX</productname>.
+    The native IBM compiler <command>xlc</command> is not supported.
+   </para>
+
+   <para>
+    <productname>AIX</productname> versions before 7.2 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 0c60bafac63..b4914faff1c 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -901,6 +901,29 @@ $ <userinput>postgres -D $PGDATA -C num_os_semaphores</userinput>
 
 
     <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 4b3bd5c9a7e..77a122b43c8 100644
--- a/meson.build
+++ b/meson.build
@@ -202,7 +202,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'
diff --git a/src/Makefile.shlib b/src/Makefile.shlib
index 3825af5b228..65870d629c2 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 05642dc02e3..fe717001951 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -81,16 +81,18 @@ override LDFLAGS := $(LDFLAGS) $(LDFLAGS_EX) $(LDFLAGS_EX_BE)
 
 ##########################################################################
 
-all: submake-libpgport submake-catalog-headers submake-utils-headers postgres
+all: submake-libpgport submake-catalog-headers submake-utils-headers postgres $(POSTGRES_IMP)
 
 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)
 
@@ -117,6 +119,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
 
 
@@ -210,8 +230,12 @@ install-postgres-bitcode: $(OBJS) all
 	$(call install_llvm_module,postgres,$(call expand_subsys, $(filter-out $(top_builddir)/src/timezone/objfiles.txt, $(SUBDIROBJS))))
 endif
 
-install-bin: postgres installdirs
+install-bin: postgres $(POSTGRES_IMP) installdirs
 	$(INSTALL_PROGRAM) postgres$(X) '$(DESTDIR)$(bindir)/postgres$(X)'
+ifeq ($(MAKE_EXPORTS), true)
+	$(INSTALL_DATA) $(POSTGRES_IMP) '$(DESTDIR)$(pkglibdir)/$(POSTGRES_IMP)'
+	$(INSTALL_PROGRAM) $(MKLDEXPORT) '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)/mkldexport.sh'
+endif
 
 .PHONY: install-bin
 
@@ -227,12 +251,20 @@ ifeq ($(MAKE_DLL), true)
 	$(MKDIR_P) '$(DESTDIR)$(libdir)'
 endif
 endif
+ifeq ($(MAKE_EXPORTS), true)
+	$(MKDIR_P) '$(DESTDIR)$(pkglibdir)'
+	$(MKDIR_P) '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)'
+endif
 
 
 ##########################################################################
 
 uninstall:
 	rm -f '$(DESTDIR)$(bindir)/postgres$(X)'
+ifeq ($(MAKE_EXPORTS), true)
+	rm -f '$(DESTDIR)$(pkglibdir)/$(POSTGRES_IMP)'
+	rm -f '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)/mkldexport.sh'
+endif
 ifeq ($(PORTNAME), cygwin)
 ifeq ($(MAKE_DLL), true)
 	rm -f '$(DESTDIR)$(libdir)/libpostgres.a'
@@ -257,7 +289,7 @@ endif
 ##########################################################################
 
 clean:
-	rm -f $(LOCALOBJS) postgres$(X)
+	rm -f $(LOCALOBJS) postgres$(X) $(POSTGRES_IMP)
 ifeq ($(PORTNAME), cygwin)
 	rm -f postgres.dll libpostgres.a
 endif
diff --git a/src/backend/meson.build b/src/backend/meson.build
index 712a857cdb4..ba273df24b5 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 00000000000..adf3793e868
--- /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 59315e94e3e..b4fb828dffd 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -915,7 +915,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 9e55be244f7..51dce24947a 100644
--- a/src/backend/utils/misc/ps_status.c
+++ b/src/backend/utils/misc/ps_status.c
@@ -52,7 +52,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__) || defined(__GNU__)
+#elif defined(__linux__) || defined(_AIX) || defined(__sun) || defined(__darwin__) || defined(__GNU__)
 #define PS_USE_CLOBBER_ARGV
 #elif defined(WIN32)
 #define PS_USE_WIN32
@@ -62,7 +62,7 @@ bool		update_process_title = DEFAULT_UPDATE_PROCESS_TITLE;
 
 
 /* Different systems want the buffer padded differently */
-#if defined(__linux__) || defined(__darwin__) || defined(__GNU__)
+#if defined(_AIX) || defined(__linux__) || defined(__darwin__) || defined(__GNU__)
 #define PS_PADDING '\0'
 #else
 #define PS_PADDING ' '
diff --git a/src/include/port/aix.h b/src/include/port/aix.h
new file mode 100644
index 00000000000..c86983a4452
--- /dev/null
+++ b/src/include/port/aix.h
@@ -0,0 +1,3 @@
+/*
+ * src/include/port/aix.h
+ */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index bf4baa92917..0963995eed4 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -136,7 +136,9 @@ $(stlib): $(OBJS_STATIC)
 # expect them to honor this coding rule.
 libpq-refs-stamp: $(shlib)
 ifneq ($(enable_coverage), yes)
+ifeq (,$(filter aix,$(PORTNAME)))
 	$(PERL) $(srcdir)/libpq_check.pl --input_file $< --nm='$(NM)'
+endif
 endif
 	touch $@
 
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index c5ecd9c3a87..3333f6d00c8 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -55,8 +55,9 @@ libpq_so_c_args = ['-DUSE_DYNAMIC_OAUTH']
 #    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 00000000000..dd16a7a0378
--- /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 ed5c54a72fa..97f18a62338 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 76af67ec2d0..d70734196b2 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 00000000000..f39c680a314
--- /dev/null
+++ b/src/template/aix
@@ -0,0 +1,9 @@
+# src/template/aix
+
+# 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 6409a485e84..7c665ff892d 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/tools/gen_export.pl b/src/tools/gen_export.pl
index 515f6d4a7a6..36f3b79bea7 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 7a6755991bb..3d3e371a8e1 100755
--- a/src/tools/pginclude/headerscheck
+++ b/src/tools/pginclude/headerscheck
@@ -89,6 +89,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.43.7

