Linking in AIX

Q: What linker-option -blibpath is good for?
A: It specifies a list of directories where run-time dependencies could be resolved from. Example:
gcc -o executable -Wl,-blibpath:/usr/local/lib64:/usr/lib
You can check the result with dump(1)
$ dump -H -X32_64 executable
...
INDEX  PATH                          BASE                MEMBER
0      /usr/local/lib64:/usr/lib
...
Q: What linker-option -bipath is good for?
A: It tells the linker to store depencies with file-path — but only if they had been specified with file-path in the command line. Example:
gcc -o executable -Wl,-brtl,-bipath -la /usr/local/lib/libb.so.2
Check:
$ dump -H -X32_64 executable
...
INDEX  PATH                          BASE                MEMBER
...
1                                    liba.so
2      /usr/local/lib                libb.so.2
Q: In the previous example, why isn't the major version number of liba.so stored in the executable?
A: Because AIX doesn't have the concept of version numbers. External components might have version numbers, but they don't matter for AIX-linker.
Q: What about option -brtl?
A: Without that, the linker would ignore 'standalone' shared objects (*.so files), when resolving libraries.
Q: What does libgcc_s.a contain, and where does it come from?
A: It contains common subroutines that gcc-compiled programs might call. It's part of gcc; if installed from RPM (package libgcc), you have files like these:
/opt/freeware/lib/gcc/powerpc-ibm-aix6.1.0.0/4.8.3/libgcc_s.a
/opt/freeware/lib/gcc/powerpc-ibm-aix6.1.0.0/4.8.3/ppc64/libgcc_s.a
/opt/freeware/lib/gcc/powerpc-ibm-aix6.1.0.0/4.8.3/pthread/libgcc_s.a
/opt/freeware/lib/gcc/powerpc-ibm-aix6.1.0.0/4.8.3/pthread/ppc64/libgcc_s.a
Q: That's more than enough, if you ask me. Which one should I actually use?
A: I suggest .../pthread/libgcc_s.a for 32-bit, and .../pthread/ppc64/libgcc_s.a for 64-bit. They are meant to be THREAD_SAFE.
Q: Pardon? What do you mean by "THREAD_SAFE"?
A: I mean objects/libraries compiled with -D_THREAD_SAFE aren't compatible with those that were compiled without this symbol defined (check your /usr/include/errno.h file for errno and _Errno). I suggest to compile everything with -D_THREAD_SAFE.
Q: Well, back to libgcc_s.a: aren't these locations a bit random? How about binary-program-portability?
A: You can query the relevant directory with a simple and intuitive command, e.g.:
$ dirname $(gcc -pthread -maix64 -print-file-name=libgcc_s.a)
/opt/freeware/lib/gcc/powerpc-ibm-aix6.1.1.0/5.4.0/pthread/ppc64
Nontheless I suggest you link/copy your latest/only libgcc_s.a file into directory /usr/local/lib[64] and use that. Here as an example, my libiconv.so file(s):
$ ls -l /usr/local/lib64/libiconv.so*
lrwxrwxrwx 1 root system      17 Dec 29 /usr/local/lib64/libiconv.so   -> libiconv.so.2.5.1
lrwxrwxrwx 1 root system      17 Dec 29 /usr/local/lib64/libiconv.so.2 -> libiconv.so.2.5.1
-rw-r--r-- 1 root system 1139080 Dec 29 /usr/local/lib64/libiconv.so.2.5.1

$ dump -X32_64 -H /usr/local/lib64/libiconv.so.2
INDEX  PATH                          BASE                MEMBER
0      /usr/local/lib64:/usr/lib:/lib
1      /usr/local/lib64              libgcc_s.a          shr.o
2      /usr/local/lib64              libcpotlas.so.1
3      /usr/lib                      libc.a              shr_64.o
Q: What about libgomp.a?
A: It is part of the gcc/OpenMP, less commonly used than libgcc_s.a (for example msgmerge is a program that can use it). I suggest the same treatment for it: link/copy the latest/only version of it into /usr/local/lib[64] (the 'threaded' variant).
Q: Other suggestions regarding compilation?
A: Forget 32-bit mode, it is 2023 now. But if you absolutely have to compile in 32-bit mode, remember that objects/libraries compiled with largefile-support aren't compatible with those that were compiler without it. I suggest compiling everything with largefile-support.
Q: Are you sure largefile-support cannot raise problems?
A: Of course it can raise problems, especially because it is implemented via defines like this:
$ grep fopen64 /usr/include/stdio.h
#define fopen fopen64
extern FILE *fopen64(const char *, const char *);
Because of this define, if you compile PHP from source, you might end up with a PHP-program that doesn't have a fopen built-in function, only a 'fopen64'.
Q: Any solution for this problem?
A: Well, you can do some dark magic with gcc, but it doesn't worth it, 64-bit is the sound solution.
Q: Collision of 32-bit and 64-bit versions of a program can cause troubles?
A: Yes, it can produce hard-to-debug problems. OpenSSL is an example: header-files (opensslconf.h to be precise) are meant either for 32-bit or 64-bit, but not for both.
Note: Though you can fix it, if you edit the said file:
    181  /* Only one for the following should be defined */
    182  #undef SIXTY_FOUR_BIT_LONG
    183  #undef SIXTY_FOUR_BIT
    184  #undef THIRTY_TWO_BIT
    185  #if defined(__64BIT__)
    186  # define SIXTY_FOUR_BIT_LONG
    187  #else
    188  # define THIRTY_TWO_BIT
    189  #endif
Q: Any other problems regarding 64-bit compilation?
A: For example macros major and minor from <sys/sysmacros.h> work incorretly when compiling for 64-bit, use macros major64 and minor64 (mind you, they don't exist on other platforms).
Quote from an exampl program:
    printf ("major=%llx minor=%llx\n",
#if defined(_AIX) && __SIZEOF_POINTER__ == 8
        (long long)major64(st.st_dev), (long long)minor64(st.st_dev)
#else
        (long long)major(st.st_dev), (long long)minor(st.st_dev)
#endif
    );
Q:Why do I get this error message:
0509-136 Symbol __pthread (number 35) is not exported from
         dependent module /usr/lib/libpthreads.a[shr_xpg5_64.o].
A: You have a program that was created under a newer version of AIX then moved back to version 5.x Mind you, this would work in 32-bit; in that variant this __pthread symbol is exported from /usr/lib/libpthreads.a[shr_xpg5.o]
Q: Speaking of debugging, how can I get complete core files so that I can debug the problem?
A: Execute these command as root-user:
chdev -l sys0 -a fullcore=true # enabling
lsattr -l sys0 -a fullcore -E  # verifying
Q: How about C++?
A: Avoid it, if you can. It is very problematic even on popular platforms like MS Windows and linux/x86_64, let alone on exotic platforms like AIX.
Q: And if I have to use it?
A: Then you have to deal with library libstdc++.a or libstdc++.so, which have the same issues I mentioned at libgcc_s.a. Here is an example for libstdc-using program:
$ dump -X32_64 -H /usr/local/bin/gengetopt
INDEX  PATH                          BASE                MEMBER
0      /usr/local/lib64:/usr/lib:/lib
1      /usr/local/lib64              libgcc_s.a          shr.o
2      /usr/local/lib64              libstdc++.so.6
3      /usr/local/lib64              libcpotlas.so
4      /usr/lib                      libpthreads.a       shr_xpg5_64.o
5      /usr/lib                      libc.a              shr_64.o
Here file /usr/local/lib64/libstdc++.so.6 has come from /opt/freeware/lib/gcc/powerpc-ibm-aix6.1.0.0/4.8.3/pthread/ppc64/libstdc++.a Note: I got this name with the following command:
g++ -maix64 -pthread -print-file-name=libstdc++.a
Q: When I install something from source, should I create a a little script that bundles configure, make and make install?
A: Absolutely. These scripts might save your life. (Also the log-files you should keep.) A script like this will evolve and grow longer; a simple start is something like this:
#!/usr/local/bin/bash
# bash is important as it has option 'pipefail'

export PATH=/usr/local/bin:/opt/freeware/bin:$PATH
export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig:$PKG_CONFIG_PATH

make distclean || make clean

set -e
set -o pipefail

export CFLAGS='-maix64'
export CXXFLAGS='-maix64'
export CPPFLAGS='-I/usr/local/include'
export LDFLAGS='-maix64 -L/usr/local/lib64 -L/opt/freeware/lib64
	-Wl,-brtl,-bipath,-blibpath:/usr/local/lib64:/opt/freeware/lib64:/usr/lib'

./configure \
	--prefix=/usr/local \
	--libdir=/usr/local/lib64 \
	CFLAGS="$CFLAGS" \
	CXXFLAGS="$CXXFLAGS" \
	CPPFLAGS="$CPPFLAGS" \
	LDFLAGS="$LDFLAGS" \
	AR='/usr/bin/ar -X64' \
	NM='/usr/bin/nm -X64' \
	SIZE='/usr/bin/size -X64' \
	2>&1 | tee log.configure

make all 2>&1 | tee log.make.all
make install 2>&1 | tee log.make.install

echo 'Ok(?)'
Q: Cool, but it still doesn't work for me; I think I should hack some files so they would be compatible with my system
A: Don't worry, that's very typical on AIX. The first thing you should know AIX!sed doesn't support option -i, so I suggest using a script called sed_repl installed into /usr/local/bin
#!/bin/sh

if test $# -ne 2; then
    echo "usage: $0 script file"
    exit 1
fi

TMPFILE=/tmp/sed_repl.$$

set -e

sed -e "$1" <"$2" >"$TMPFILE"
cp "$TMPFILE" "$2"
rm "$TMPFILE"
Q: And how do I use this in my install-scripts?
A: Here's some examples:
# common problem #1
# configure forces '/usr/local/lib' instead of /usr/local/lib64
# insert this before 'configure'

find . -name configure -o -name 'config.*' | while read C; do
    sed_repl 's;/usr/local/lib;/usr/local/lib64;g
	      s;/usr/local/lib6464;/usr/local/lib64;g
	      s;/usr/local/lib64exec;/usr/local/libexec;g
	     ' "$C"
done


# common problem #2
# configure does funny things with some "hardcoding" that breaks compilation

find . -name configure -o -name config.rpath |\
while read C; do
    sed_repl 's|hardcode_direct=.*$|hardcode_direct=no|
	      s|hardcode_libdir_flag_spec=.*$|hardcode_libdir_flag_spec=|
	     ' "$C"
done


# common problem #3
# configure ingnores "$AR", so -X32_64 option isn't passed to /usr/bin/ar
# insert this between 'configure' and 'make'

find . -name Makefile | while read M; do
    sed_repl 's/^AR = .*$/AR = /usr/bin/ar -X32_64
	     ' "$M"
done


# common problem #4
# Makefiles are full with @at-signs so that you couldn't trace problems
# insert this between 'configure' and 'make'

find . -name Makefile | while read M; do
    sed_repl "$(printf 's;^\t@;\t;')" "$M"
done

# common problem #5
# 'configure' checks a static library for an exported symbol,
# but it checks for a function while it is actually a variable
#
# Example: 'configure' of 'php' checks for 'rl_pending_input'
# from library 'readline' as if it were a 'function'
#
# Note: This problem doesn't affect shared libraries;
# the reason is realated to this question:
# statically linking a foobar function would require the .foobar
# symbol, but variables don't have such .dotted versions.

sed_repl 's/rl_pending_input ()/rl_pending_input/' configure
Q: Some programs ask for pkgconfig. Should I install it?
A: Yes, but you might experience problems as pkgconfig and glib mutually depend on each other. I could workaround this problem by first installing an older version of pkgconfig (e.g. 0.23), that didn't require glib.
Q: What the .dot means in the following list:
$ dump -X32_64 -H libphp5.so
...
INDEX  PATH                          BASE                MEMBER
0      /opt/lib64:/usr/local/lib64:/usr/lib
...
23     /usr/lib                      libc.a              shr_64.o
24                                   .
A: This module has external symbols that are meant to be resolved from the main program. You can see these symbols with the following command:
$ dump -X32_64 -Tv libphp5.so | grep 'EXTref  '
[1224]  0x00000000    undef      IMP     DS EXTref               . ap_mpm_query
[1225]  0x00000000    undef      IMP     DS EXTref               . ap_server_root_relative
...
The main program is (supposed to be) apache!httpd which exports these symbols:
$ dump -X32_64 -Tv /usr/local/sbin/httpd | grep ap_mpm_query
[2261]  0x20007130    .data      EXP     DS   Ldef        [noIMid] ap_mpm_query
Q: What about the double dots:
$ dump -X32_64 -H my_php_plugin.so
...
INDEX  PATH                          BASE                MEMBER
...
5      /usr/lib                      libc.a              shr_64.o
6                                    ..
A: This module has external symbols that are meant to be resolved loading time, and we cannot tell yet where they will come from. Their list:
$ dump -X32_64 -Tv my_php_plugin.so | fgrep '..'
[18]    0x00000000    undef      IMP     DS EXTref              .. php_info_print_table_start
[19]    0x00000000    undef      IMP     DS EXTref              .. php_info_print_table_header
[20]    0x00000000    undef      IMP     DS EXTref              .. php_info_print_table_end
[21]    0x00000000    undef      IMP     DS EXTref              .. zend_parse_parameters
[22]    0x00000000    undef      IMP     DS EXTref              .. _estrndup
[23]    0x00000000    undef      IMP     DS EXTref              .. zend_register_long_constant
These type of unresolved externs is not welcome in AIX, you should try and avoid them. If you cannot, remember using option -brtlib when linking the main executable (note: in 32-bit mode, /usr/lib/librtl.a will be linked into the program).
Q: So why are these symbols unresolved in this PHP-plugin?
A: It is a special case, because these symbols will be resolved either from /usr/local/bin/php or from /usr/local/libexec64/libphp.so (when the main program in apache!httpd).
Q: Should I install my locally compiled components into random locations such as /extra/teddy/stuff/whatever-1020?
A: Absolutely not. Install locally compiled components into /usr/local
Q: And what is /opt good for?
A: For components that aren't part of the standard distribution and come in 'binary only' format, such as Oracle, Java, Firefox.
Q: What about /opt/freeware?
A: That's where GNU/other free products traditionally go on AIX, if you install them from RPM-packages (e.g. from Perzl.org).
Q: Speaking of rpm, I got an error message about missing package AIX-rpm. Where does it come from?
A: It is a virtual package, which is based on your OS. (The rpm command comes from actual package rpm.rte.) There is an updtvpkg command that updates the virtual package:
# lslpp -l rpm.rte
  Fileset                      Level  State      Description
  ----------------------------------------------------------------------------
Path: /usr/lib/objrepos
  rpm.rte                   4.15.1.1  COMMITTED  RPM Package Manager

# updtvpkg
Please wait...

# rpm -qi AIX-rpm
Name        : AIX-rpm
Version     : 7.2.5.0
...
Description :
This package contains no files.  It is a package which contains
information about shared libraries and shell interpreters installed
on the operating system.
Q: What's the deal with libiconv.a?
A: Well, it is a bit problematic, as you might have more than one instances of this file with different contents, as different programs use it for different purposes.
GNU iconv might keep static object modules in it (this is not problematic, also it is safe to compile libiconv with options --disable-static) e.g.:
$ ar -X64 -tv /usr/local/lib64/libiconv.a
rw-r--r--     0/0    1114345 Jul 22 17:12 2015 iconv.o
rw-r--r--     0/0       3346 Jul 22 17:12 2015 localcharset.o
rw-r--r--     0/0       2564 Jul 22 17:12 2015 relocatable.o
Also AIX iconv stores it's shared modules in (another) libiconv.a:
$ ar -X32_64 -tv /usr/lib/libiconv.a
r--r--r--     2/2     117276 Sep 15 21:15 2013 shr4.o
r--r--r--     2/2     117526 Sep 15 21:15 2013 shr.o
r--r--r--     2/2     159410 Sep 15 21:15 2013 shr4_64.o
Also some versions on GNU iconv keep shared objects in (yet another) libiconv.a:
$ ar -X32_64 -tv /opt/freeware/lib/libiconv.a # filtered output
rwxr-xr-x   203/1     1100139 Feb 03 08:00 2017 libiconv.so.2 # for 32-bit
rwxr-xr-x   203/1     1128141 Feb 03 07:57 2017 libiconv.so.2 # for 64-bit
Another possible source of problems that /usr/lib/libi18n.a(shr*so) depends on libiconv.a(shr*.so) (without path), so if the loading finds a libiconv.a without these shared objects, the loading fails. (It won't search for another libiconv.a archive.)
The best option would be merging all these elements (at least the shared objects) together, and make sure that the loading process finds the merged version. (With option -blibpath or with enviroment variable LIBPATH).
If you have an /opt/freeware/lib/libiconv.a archive, it might already contain every shared object:
$ ar -X32_64 -tv /opt/freeware/lib/libiconv.a # complete output
rwxr-xr-x   203/1     1100139 Feb 03 08:00 2017 libiconv.so.2 # GNU iconv, 32-bit
rwxr-xr-x   203/1     1128141 Feb 03 07:57 2017 libiconv.so.2 # GNU iconv, 64-bit
r--r--r--     0/0      117162 Jul 19 13:04 2017 shr4.o        # AIX iconv, 32-bit
r--r--r--     0/0      117412 Jul 19 13:04 2017 shr.o         # AIX iconv, 32-bit
r--r--r--     0/0      159296 Jul 19 13:04 2017 shr4_64.o     # AIX iconv, 64-bit
If you don't have such file, you can use ar to create something like that; as you have seen, 32- and 64-bit components can be merged in one archive (even with the same name); also the two iconv implementations (GNU and AIX) can be put into the same file without problems.
Q: I heard that fontconfig (albeit not part of the AIX-distribution) can be used by JVM if I install it and make it available for dynamic linking (dlopen). How could I do that?
A: Well some JVM versions search for libfontconfig.so.1 others for libfontconfig.a(libfontconfig.so.1).
Here's a version that works with both, if your locally-compiled (64-bit) libfontconfig.so.1 is installed in /usr/local/lib64:
ar -X64 rcs /usr/local/lib64/libfontconfig.soa /usr/local/lib64/libfontconfig.so.1
ln -sf /usr/local/lib64/libfontconfig.soa  /usr/lib/libfontconfig.a
ln -sf /usr/local/lib64/libfontconfig.so.1 /usr/lib/libfontconfig.so.1
ls -l /usr/lib/libfontconfig*
/usr/lib/libfontconfig.a    -> /usr/local/lib64/libfontconfig.soa
/usr/lib/libfontconfig.so.1 -> /usr/local/lib64/libfontconfig.so.1
Q: Please name some utilities that have something to do with shared libraries.
A: Here's some:
slibclean		  remove unused shared libraries from memory
genld -ld		  list of processes and the shared libs they use
genkld -d		  list of shared libs loaded into memory
procldd <pid>		  shared libs used by a process
dump -X32_64 -H <module>  shared libs that the <module> (directly) depends on
dump -X32_64 -Tv <module> list of external symbols in <module>
nm -Pg <obj>		  list of external symbols in <obj>
ldd <module>		  list of shared libs <module> (directly or indirectly) depends on

module:	executable or shared lib
obj:	object module, executable or shared lib
Bonus:
explist_so <prog>	list exported symbols (this script uses 'dump -T')
Q: What is the function descriptor?
A: A data area containing three pointers related to the function in question:
code	address of the first executable statement (in the text-segment)
data	address of the TOC (the data-area of the module)
context	(unused in C)
The function descriptor exists in the data-segment. The TOC is a section in the object module that contains the function's data.
At function call (bl, bctrl or similar), the first value is loaded into the program counter, the second value is loaded into register r2.
Q: You mean every object-module has a separate TOC?
A: Well, when the linker creates a module (an executable or a shared object), it merges these sections, so every function of the module share the same TOC.
Q: But the term TOC doesn't actually refer to the whole data segment!
A: That's right, the TOC is just the most important part of the data segment, I could say it's like a table of content in a book.
Q: So if I have a non-static function (say 'foo'), why do I get two external symbols 'foo' and '.foo'?
A: Well, .foo is the code, foo is the descriptor. Function calls use bl .foo; in-module context it is a direct call, otherwise it goes through a glue-module (see below).
Example Assembly-list of an object module:
	.globl myhello			'myhello' is a public function
	.globl .myhello
	.csect myhello[DS]		DS = function descriptor
myhello:
	.long .myhello, TOC[tc0], 0	code, toc, context(unused)
	.csect .text[PR]		PR = code
.myhello:
	mflr 0				actual code of 'myhello' starts here

	...

	.lglobl .smyhello		'smyhello' is a static function
	.csect smyhello[DS]		DS = function descriptor
smyhello:
	.long .smyhello, TOC[tc0], 0	code, toc, context(unused)
	.csect .text[PR]		PR = code
.smyhello:
	mflr 0				actual code of 'smyhello' starts here

	...

	bl .myhello			'main' calls 'myhello'
part of "nm hello":
$ nm hello
.myhello             T   268436440  92		# public text
.smyhello            t   268436532  92		# local text
part of "nm -g /usr/lib/libc.a"
/usr/lib/libc.a[shr.o]:
.puts                T     2522688		# code
puts                 D      478040          12	# function destructor
Q: How could I check these?
A: When compiling, ask for Assembly listing: -Wa,-lfoo.lst, when linking ask for map-file: -Wl,-bmap:foo.map
Q: Which one is used when I call a function?
A: Example function call (fwrite):
.text  00000164  8062000c       lwz 3,LC..7(2)	; par1
.text  00000168  38800001       li 4,1		; par2
.text  0000016c  38a00027       li 5,39		; par3
.text  00000170  80c20004       lwz 6,LC..8(2)	; par4
.text  00000174  4bfffe8d       bl .fwrite	; the actual call
.text  00000178  60000000       nop		; (don't ask)
When you call a function via function-pointer (functor), the pointer contains the address of the descriptor:
.text  0000020c  813f0038       lwz 9,56(31)	; r9 := descriptor of the callée
.text  00000210  90410014       stw 2,20(1)	; save r2 (our TOC)
.text  00000214  81490000       lwz 10,0(9)	; address of the callée
.text  00000218  81690008       lwz 11,8(9)	; context of the callée
.text  0000021c  7d4903a6       mtctr 10	; ctr := r10
.text  00000220  80490004       lwz 2,4(9)	; TOC of the callée
.text  00000224  4e800421       bctrl		; the actual call
.text  00000228  80410014       lwz 2,20(1)	; restore r2 (our TOC)
Q: So at indirect call, there is a proper context switch (or TOC-and-context-switch, to be precise). What about normal calls?
A: Well, if the caller and the callée exist in the same module (executable or shared lib), they share the same TOC-and-context, so no problem here.
At cross-module calls the linker adds a piece of code to the caller (known as glue code); it is something like this:
<caller>
0xd1247334 <+56>:    bl      0xd124757c	; call the 'glue code' (and set the link register)
0xd1247338 <+60>:    lwz     r2,20(r1)  ; restore r2 (our TOC)

<glue code>
0xd124757c <+0>:     lwz     r12,36(r2)	; r12 := descriptor of the callée
0xd1247580 <+4>:     stw     r2,20(r1)  ; save r2 (our TOC)
0xd1247584 <+8>:     lwz     r0,0(r12)  ; address of the callée
0xd1247588 <+12>:    lwz     r2,4(r12)  ; TOC of the callée
0xd124758c <+16>:    mtctr   r0		; address to the callée
0xd1247590 <+20>:    bctr		; jump into the callée
Such adapter-code is generated for every called function. The code comes form /usr/lib/glink*o
Q: What was the 'don't ask' part above?
A: Well, the compiler generates a nop after every bl, but if the linker decides it is a 'far call' to another module (implemented via glue code), it replaces nop with lwz r2,20(r1) or ld r2,40(r1) (32-/64-bit) to restore r2, which is the TOC-pointer.
Q: Off: Are there similar concepts in MS Windows?
A: Yes, but there are some differences, too, e.g.:
* Windows 16 and Windows 32 don't use position-independent code (Windows 64 does) in DLL, they relocate the DLL's code when it is loaded (unless it is lucky enough to get loaded into its own preferred address). Note: Win16 uses Single Virtual Storage, Win32 and Win64 uses Multiple Virtual Storage (like AIX))
* Windows can refer to exported symbols via numbers (so called ordinals) instead of names – it is very dangerous
* it's common practice (but not mandatory) to explicitly tell the compiler which functions are DLL-exported and/or imported, instead of leaving it to the linker to deal with this (see function attributes like __declspec(dllexport) and __declspec(dllimport))
* some linkers cannot directly link from DLLs, they need a matching import library for every used DLL (the import library can be programmatically generated though)
* different compilers might use different name-decorating methods, so names like FooBar@32 and _FOOBAR might or might not match when linking.
* the main executable cannot export symbols (note: I might be wrong here; dubious area)
Q: Some info about map-files?
A: This table shows some Class/Types codes:
CL 					TY
-- 					--
PR   code (also known as 'text')	ER   external (to be resolved)
RO   read only data			SD   section (also known as 'csect')
RW   data (writable)			LD   label (also known as 'entry')
BS   data (uninitialized, writable)	CM   common
DS   function descriptor
GL   glue code				IE
T0   header of the TOC			--
TC   address in TOC			I  imported symbol
TD   other data in TOC			E  exported symbol
UA   "unclassified"
XO   "extended operations code"
The output consists of two parts: code and data; the TOC resides at the end of the data.
Symbols between <angles> are hidden symbols.
Q: How do you decide which symbols are to be exported when creating a shared library?
A: You can choose from linker-options -bautoexp, -bexpall or -bexpfull; or you can use some script to collect the symbols to be exported (I suggest the latter). From the output of nm(1) you can grep these:
B	Global bss symbol
D	Global data symbol
T	Global text symbol
We exclude those that begin with .dot, as they are code-pointers that aren't meant to be directly called from other modules.
Q: You cannot really expect me to do this manually, can you?
A: I suggest using libtool and let it deal with these details. Or you can use a script like this:
#!/bin/sh
nm -X32_64 -Ppg -- "$@" |\
awk '{ if ((($2 == "T") || ($2 == "D") || ($2 == "B")) && (substr($1,1,1) != ".")) 
     { print $1 } }' |\
sort -u
Q: What should I do if I have (poorly written) Assembly sources with .name entries, but without name entries (i.e. without function descriptors). (older versions of OpenSSL had such files, for example.) Can I still export them?
A: Yes, if you tell the linker to export these symbols, it will create function descriptors for them. In this case, the above script could be modified like this:
#!/bin/sh
nm -X32_64 -Ppg -- "$@" |\
awk '{ if (($2 == "T") || ($2 == "D") || ($2 == "B")) {
      if (substr($1,1,1) == ".") print substr($1,2); else print $1; } }' |\
sort -u
Q: Using option -bexpall might cause problems?
A: Sure. For example, OpenSSL before version 1.1.1 used this option, which ended in libcrypto.so exporting symbols it (statically) linked from libc.a. Examples:
$ nm -gP libcrypto.so.1.0.2 | egrep '^(str|mem)'| grep ' D '
memmove              D    2001aa48           8
strcat               D    200238f8           8
strcmp               D    200177a0           8
strcpy               D    2001e574           8
strncpy              D    2001b794           8
Another example is Oracle client 12 — it exports tons of symbols it shouldn't, like:
$ dump -Tv /opt/lib32/libclntsh.so.12 |\
grep ' EXP ' |\
egrep ' (BIO_|ERR_|SSLv23_|SSLv3_|TLSv1_|ssl23_|ssl3_|ssl_|tls1_|BZ2_|inflate|deflate)' |\
wc -l
210
Q: Why do I get warnings about duplicated symbol like __init_aix_libgcc_cxa_atexit?
A: You have two or more shared objects that were created without export-list (with option -bautoexp or similiar).
Q: What about symbols p_xargc, p_xargv, text, etext, data, edata etc?
A: They come from one of the /usr/lib/crt*.o objects. The compiler automagically adds the necessary crt*.o object into your executable.
Q: But I got linker-warnings about they being duplicated symbols.
A: One or more of your shared objects have been built without export-list, so they export symbols they shouldn't.
Q: What about symbols like _GLOBAL__F_xxx?
A: They might have something to do with exceptions: either it is C++, or you used option -fexceptions compile-time.
Q: What is the optimal setting of environment variable LIBPATH?
A: Unsetting it. If the linkage has been performed correctly and the shared libraries are in the same place, LIBPATH is not needed.
Should you have broken executables that don't run without LIBPATH, you can create launching scripts, eg:
cd /somepath/bin
mv someexe someexe.bin
cat >someexe <<DONE
#!/bin/sh
export LIBPATH=/somepath/lib:/usr/lib
exec /somepath/bin/someexe.bin "$@"
DONE
chmod 0755 someexe
Q: Shared libraries placed into *.a archives might cause problems during program-loading?
A: Sure. If the loader finds an archive, but it doesn't contain the required element, the loader won't search for another archive, it will stop the loading with an error message.
Example:
$ dump -H -X32 /opt/freeware/bin/rpm
INDEX  PATH                          BASE                MEMBER
0      /opt/freeware/lib:/usr/lib:/lib
1                                    librpmbuild.so
2                                    libintl.a           libintl.so.1
3                                    librpm.so
4                                    libpopt.so
5                                    libc.a              shr.o
6                                    librtl.a            shr.o
$ /opt/freeware/bin/rpm # it works
$ LIBPATH=/usr/local/lib64 /opt/freeware/bin/rpm # it doesn't work
Could not load program /opt/freeware/bin/rpm:
        Dependent module /usr/local/lib64/libintl.a(libintl.so.1) could not be loaded.
        Member libintl.so.1 is not found in archive 
Q: What's the host-type (or machine-triplet) of my AIX-computer?
A: It's a machine-vendor-os triplet:
machine: rs6000 or powerpc -- output of /usr/bin/uname -p
vendor:  ibm
os:      aixV.V.V.V -- aix$(/usr/bin/oslevel)

example: powerpc-ibm-aix6.1.0.0
Q: Are you sure in the last part?
A: Well, often the version of the bos.rte.libc package used instead; try command lslpp -l bos.rte.libc.
Q: Can I re-link a shared library?
A: Sometimes you can, but it is not trivial (except for the trivial cases). Here is an example: you wish to relink mylib.so which depends on two other libraries and libc:
explist_so mylib.so >mylib.exp
ld -r -o mylib.o -bnso mylib.so
ld -G -bnoentry -bernotok -bE:mylib.exp -o mylib_relinked.so mylib.o foo.so foocommon.so -lc
Q: How could I debug heap corruption bugs?
A: There is a debug feature in malloc, which you can activate if you set a special environment variable before starting your program:
$ MALLOCDEBUG=catch_overflow ./myexe
You can also try using Electric Fence (next question).
Q: How could I use Electric Fence to find heap corruption bugs?
A: It is not as easy as it is in Linux (LD_PRELOAD=libefence.so myprogram), but it still can be useful. Here is a QuickStart:
Download the source, and compile it into a shared/static library:
# 64 bit shared:
gcc -shared -maix64 -o libefence_sh.a efence.c page.c print.c -pthread

# 64 bit static:
for i in efence page print; do
    gcc -maix64 -o $i.o -c $i.c  -pthread
done
ar -X64 rcs libefence.a efence.o page.o print.o
Whichever you created, add it into the linkage, and run your program. You should see this message at startup:
Electric Fence 2.1 Copyright (C) 1987-1998 Bruce Perens.
Q: And exactly what good will this do for me?
A: It might detect heap corruption problems earlier, making debugging easier. Here is an example program:
$ cat efence_test.c
/* efence_test.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (void)
{
    char *p= malloc (22);
    
    memcpy (p, "I've corrupted the heap!!", 25);
    return 0;
}
$ gcc -g -maix64 efence_test.c -o efence_test libefence.a -pthread
$ gdb ./efence_test
(gdb) run
Thread 2 received signal SIGSEGV, Segmentation fault.
(gdb) bt
#0  0x000000000000f470 in ?? ()
#1  0x0000000100000614 in main () at efence_test.c:11
(gdb) l 11
11          memcpy (p, "I've corrupted the heap!!", 25);
Q: How can I use loadquery and execerror to get detailed information about failed dlopen calls?
A: Here is an example program:
/* dltest.c */

/* gcc -maix32 -o dltest32 dltest.c */
/* gcc -maix64 -o dltest64 dltest.c */

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ldr.h>
#include <unistd.h>

int main (int argc, char **argv)
{
    void *dh;
    char *args[1024];

    if (argc < 2) {
        fprintf (stderr, "usage: %s sharedobject\n", argv[0]);
        return 0;
    }
    dh= dlopen (argv[1], RTLD_LAZY | RTLD_MEMBER);
    if (dh) {
        fprintf (stderr, "dlopen(\"%s\") returned %p\n", argv[1], dh);
        return 0;
    }
    args[0]= "execerror";
    args[1]= "";

    loadquery (L_GETMESSAGES, &args[2], (sizeof args) - 2*(sizeof args[0]));
    execvp ("/usr/sbin/execerror", args);

    fprintf (stderr, "couldn't execute execerror");
    return 0;
}
– Glossary –
Function descriptor
A data area consisting of three pointers; to call a function of an external module you have to have the address of its function descriptor.
Function-pointer; functor
A pointer that points to a function; in AIX, it contains the address of the function descriptor.
Module
Executable or shared object/library; result of linkage
Object library
An archive that contains object modules; *.a file (*.lib on some platforms); it can be used in linkage; on AIX, you often find shared objects in *.a files (e.g. /usr/lib/libc.a[shr.o])
Object module
Result of compilation; *.o file (*.obj on some platfoms)
Reentrant code
(In this context) Code that can be used in multithreaded programs, typically it means non-using of static variables (eg: localtime_r(3) vs localtime(3))
Shared code
Code that can be used by different processes in the same time, (usually) it has to be position-independent (meaning: not required to be placed to a fixed memory-address, also doesn't need to be 'relocated' after loading from file).
Shared object/library
A module that is not a stand-alone executable but can be dynamically used by other modules; a *.so file (*.dll or *dylib on some platforms); on AIX, you often find shared objects in object libraries (e.g. /usr/lib/libc.a[shr.o])
Table Of Content (TOC)
The root data-area of a module, it contains the most important data and pointers to other data, also the function-descriptors of exported functions.
– Useful links –

IBM: Understanding and programming the toc
IBM: Understanding map files
IBM: AIX® Linking and Loading Mechanisms
gcc: IBM RS/6000 and PowerPC Options
AIX Open Source Packages - Perzl.org
IBM: AIX Toolbox for Open Source Software
– Sample linking –

linktest
– Unrelated –

timegm(3) implementation for AIX