Merge r6102/6103:
A new module ("Initial Image"), whose purpose is to set up the
client's initial memory and register state before running it. On
Linux this does all the stack/auxv/envp stuff which was previously
done in m_main. On AIX5 the kernel prepares the process' initial
image, so there's nothing to be done there. But LD_PRELOAD doesn't
work on AIX5, so m_initimg sets up the client so as to start by
running a short bit of code which gets the kernel to load in the core
and tool preloads and then start the client.
As a result of this, m_main gets a lot shorter and cleaner.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6251 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_initimg/initimg-aix5.c b/coregrind/m_initimg/initimg-aix5.c
new file mode 100644
index 0000000..a1d3b16
--- /dev/null
+++ b/coregrind/m_initimg/initimg-aix5.c
@@ -0,0 +1,519 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Startup: create initial process image on AIX5 ---*/
+/*--- initimg-aix5.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2006-2006 OpenWorks LLP
+ info@open-works.co.uk
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcfile.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_clientstate.h"
+#include "pub_core_aspacemgr.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_machine.h"
+#include "pub_core_ume.h"
+#include "pub_core_options.h"
+#include "pub_core_threadstate.h" /* ThreadArchState */
+#include "pub_core_tooliface.h" /* VG_TRACK */
+#include "pub_core_trampoline.h" /* VG_(ppc32_aix5_do_preloads_then_start_client) */
+#include "pub_core_syscall.h" // VG_(do_syscall1)
+#include "pub_core_initimg.h" /* self */
+
+#include "simple_huffman.c"
+
+#if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5)
+#error "This should only be compiled on AIX"
+#endif
+
+
+static void diagnose_load_failure ( void );
+
+/* --- Create the client's initial memory image. --- */
+
+ClientInitImgInfo
+ VG_(setup_client_initial_image)(
+ /*IN*/ HChar** argv,
+ /*IN*/ HChar** envp,
+ /*IN*/ HChar* toolname,
+ /*IN*/ Addr clstack_top,
+ /*IN*/ SizeT clstack_max_size
+ )
+{
+ ClientInitImgInfo ciii;
+
+ /* Set up an AIX5PreloadPage structure with the names of
+
+ $VALGRIND_LIB/PLATFORM/vgpreload_core.so
+ $VALGRIND_LIB/PLATFORM/vgpreload_TOOL.so, if it exists
+ xxx in "LD_PRELOAD=xxx", if it exists
+
+ The client is started by running (on the simulator, of course)
+ VG_(ppc{32,64}_aix5_do_preloads_then_start_client), which uses
+ __loadx/_kload to load these .so's. When the preloading is
+ done, various guest registers are restored to what they are
+ really supposed to be at client startup, so these values too are
+ stored in the AIX5PreloadPage. Finally, we jump to the client's
+ entry point address.
+ */
+ const HChar* _so = ".so";
+ const HChar* vgpreload_ = "vgpreload_";
+ const HChar* vgpreload_core_so = "vgpreload_core.so";
+ const HChar* errmsg_str
+ = "valgrind: FATAL: core/tool/LD_PRELOAD= "
+ "preload failed.\n";
+ Int plcore_len, pltool_len, ld_pre_len, errmsg_len;
+ HChar *plcore_str, *pltool_str, *ld_pre_str;
+ Bool have_tool_so, have_ld_pre;
+
+ AIX5PreloadPage* pp;
+ UChar* pc;
+ Int szB, szPG;
+ SysRes sres;
+
+ vg_assert( toolname );
+ pltool_len = VG_(strlen)( VG_(libdir) )
+ + 1 /*slash*/
+ + VG_(strlen)(VG_PLATFORM)
+ + 1 /*slash*/
+ + VG_(strlen)( vgpreload_ )
+ + VG_(strlen)( toolname )
+ + VG_(strlen)( _so )
+ + 1 /*NUL*/;
+ vg_assert(pltool_len > 0);
+ pltool_str = VG_(malloc)( pltool_len );
+ pltool_str[0] = 0;
+ VG_(strcat)( pltool_str, VG_(libdir) );
+ VG_(strcat)( pltool_str, "/" );
+ VG_(strcat)( pltool_str, VG_PLATFORM );
+ VG_(strcat)( pltool_str, "/" );
+ VG_(strcat)( pltool_str, vgpreload_ );
+ VG_(strcat)( pltool_str, toolname );
+ VG_(strcat)( pltool_str, _so );
+ vg_assert( pltool_str[pltool_len-1] == 0);
+ vg_assert( VG_(strlen)(pltool_str) == pltool_len-1 );
+
+ plcore_len = VG_(strlen)( VG_(libdir) )
+ + 1 /*slash*/
+ + VG_(strlen)(VG_PLATFORM)
+ + 1 /*slash*/
+ + VG_(strlen)( vgpreload_core_so )
+ + 1 /*NUL*/;
+ vg_assert(plcore_len > 0);
+ plcore_str = VG_(malloc)( plcore_len );
+ plcore_str[0] = 0;
+ VG_(strcat)( plcore_str, VG_(libdir) );
+ VG_(strcat)( plcore_str, "/" );
+ VG_(strcat)( plcore_str, VG_PLATFORM );
+ VG_(strcat)( plcore_str, "/" );
+ VG_(strcat)( plcore_str, vgpreload_core_so );
+ vg_assert( plcore_str[plcore_len-1] == 0 );
+ vg_assert( VG_(strlen)(plcore_str) == plcore_len-1 );
+
+ errmsg_len = VG_(strlen)( errmsg_str )
+ + 1 /*NUL*/;
+
+ ld_pre_str = VG_(getenv)("LD_PRELOAD");
+ if (ld_pre_str && VG_(strlen)(ld_pre_str) > 0) {
+ have_ld_pre = True;
+ ld_pre_len = VG_(strlen)(ld_pre_str) + 1/*NUL*/;
+ ld_pre_str = VG_(malloc)( ld_pre_len );
+ ld_pre_str[0] = 0;
+ VG_(strcat)( ld_pre_str, VG_(getenv)("LD_PRELOAD") );
+ vg_assert( ld_pre_str[ld_pre_len-1] == 0);
+ vg_assert( VG_(strlen)( ld_pre_str ) == ld_pre_len - 1 );
+ } else {
+ have_ld_pre = False;
+ ld_pre_len = 0;
+ ld_pre_str = NULL;
+ }
+
+ VG_(debugLog)(1, "initimg", "plcore_str = '%s'\n", plcore_str );
+ VG_(debugLog)(1, "initimg", "pltool_str = '%s'\n", pltool_str );
+ VG_(debugLog)(1, "initimg", "ld_pre_str = '%s'\n", ld_pre_str );
+
+ if (0 != VG_(access)(plcore_str, True,False,True))
+ VG_(err_config_error)("Can't find core preload "
+ "(vgpreload_core.so)");
+
+ have_tool_so = 0 == VG_(access)(pltool_str, True,False,True);
+
+ /* Figure out how much space is needed for an AIX5PreloadInfo
+ followed by the three preload strings. */
+
+ vg_assert((sizeof(AIX5PreloadPage) % 4) == 0); /* paranoia */
+
+ szB = sizeof(AIX5PreloadPage) + plcore_len
+ + (have_tool_so ? pltool_len : 0)
+ + (have_ld_pre ? ld_pre_len : 0)
+ + errmsg_len;
+ szPG = VG_PGROUNDUP(szB+1) / VKI_PAGE_SIZE;
+ VG_(debugLog)(2, "initimg", "preload page size: %d bytes, %d pages\n", szB, szPG);
+
+ vg_assert(szB > 0);
+ vg_assert(szB < szPG * VKI_PAGE_SIZE);
+
+ /* We'll need szPG pages of anonymous, rw-, client space (needs w
+ so we can write it here) */
+ sres = VG_(am_mmap_anon_float_client)
+ ( szPG * VKI_PAGE_SIZE, VKI_PROT_READ|VKI_PROT_WRITE);
+ if (sres.isError)
+ VG_(err_config_error)("Can't allocate client page(s) "
+ "for preload info");
+ pp = (AIX5PreloadPage*)sres.res;
+
+ VG_(debugLog)(2, "initimg", "preload page allocation succeeded at %p\n", pp);
+
+ /* Zero out the initial structure. */
+ VG_(memset)(pp, 0, sizeof(AIX5PreloadPage));
+
+ pc = (UChar*)pp;
+ pc += sizeof(AIX5PreloadPage);
+ VG_(memcpy)(pc, plcore_str, plcore_len);
+ pp->off_preloadcorename = pc - (UChar*)pp;
+ pc += plcore_len;
+ if (have_tool_so) {
+ VG_(memcpy)(pc, pltool_str, pltool_len);
+ pp->off_preloadtoolname = pc - (UChar*)pp;
+ pc += pltool_len;
+ }
+ if (have_ld_pre) {
+ VG_(memcpy)(pc, ld_pre_str, ld_pre_len);
+ pp->off_ld_preloadname = pc - (UChar*)pp;
+ pc += ld_pre_len;
+ }
+ VG_(memcpy)(pc, errmsg_str, errmsg_len);
+ pp->off_errmsg = pc - (UChar*)pp;
+ pp->len_errmsg = errmsg_len - 1; /* -1: skip terminating NUL */
+
+ vg_assert(pc <= ((UChar*)pp) - 1 + szPG * VKI_PAGE_SIZE);
+
+ VG_(free)(plcore_str);
+ VG_(free)(pltool_str);
+
+ /* Fill in all the other preload page fields that we can right
+ now. */
+# if defined(VGP_ppc32_aix5)
+ vg_assert(__NR_AIX5___loadx != __NR_AIX5_UNKNOWN);
+ pp->nr_load = __NR_AIX5___loadx;
+# else /* defined(VGP_ppc64_aix5) */
+ vg_assert(__NR_AIX5_kload != __NR_AIX5_UNKNOWN);
+ pp->nr_load = __NR_AIX5_kload;
+# endif
+
+ vg_assert(__NR_AIX5_kwrite != __NR_AIX5_UNKNOWN);
+ pp->nr_kwrite = __NR_AIX5_kwrite; /* kwrite */
+
+ vg_assert(__NR_AIX5__exit != __NR_AIX5_UNKNOWN);
+ pp->nr__exit = __NR_AIX5__exit; /* _exit */
+
+ pp->p_diagnose_load_failure = &diagnose_load_failure;
+
+ ciii.preloadpage = pp;
+ ciii.intregs37 = 0; /* filled in in m_main.c */
+ return ciii;
+}
+
+
+/* --- Finalise the initial image and register state. --- */
+
+static UChar unz_page[VKI_PAGE_SIZE];
+
+static UInt compute_adler32 ( void* addr, UWord len )
+{
+ UInt s1 = 1;
+ UInt s2 = 0;
+ UChar* buf = (UChar*)addr;
+ while (len > 0) {
+ s1 += buf[0];
+ s2 += s1;
+ s1 %= 65521;
+ s2 %= 65521;
+ len--;
+ buf++;
+ }
+ return (s2 << 16) + s1;
+}
+
+void VG_(finalise_thread1state)( /*MOD*/ThreadArchState* arch,
+ ClientInitImgInfo ciii )
+{
+ UInt adler32_act;
+ SysRes sres;
+ /* On AIX we get a block of 37 words telling us the initial state
+ for (GPR0 .. GPR31, PC, CR, LR, CTR, XER), and we start with all
+ the other registers zeroed. */
+
+# if defined(VGP_ppc32_aix5)
+
+ vg_assert(0 == sizeof(VexGuestPPC32State) % 8);
+
+ /* Zero out the initial state, and set up the simulated FPU in a
+ sane way. */
+ LibVEX_GuestPPC32_initialise(&arch->vex);
+
+ /* Zero out the shadow area. */
+ VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestPPC32State));
+
+# else /* defined(VGP_ppc64_aix5) */
+
+ vg_assert(0 == sizeof(VexGuestPPC64State) % 8);
+
+ /* Zero out the initial state, and set up the simulated FPU in a
+ sane way. */
+ LibVEX_GuestPPC64_initialise(&arch->vex);
+
+ /* Zero out the shadow area. */
+ VG_(memset)(&arch->vex_shadow, 0, sizeof(VexGuestPPC64State));
+
+# endif
+
+ /* ciii.intregs37 contains the integer register state as it needs
+ to be at client startup. These values are supplied by the
+ launcher. The 37 regs are:initial values from launcher for:
+ GPR0 .. GPR31, PC, CR, LR, CTR, XER. */
+
+ /* Put essential stuff into the new state. */
+ arch->vex.guest_GPR0 = (UWord)ciii.intregs37[0];
+ arch->vex.guest_GPR1 = (UWord)ciii.intregs37[1];
+ arch->vex.guest_GPR2 = (UWord)ciii.intregs37[2];
+ arch->vex.guest_GPR3 = (UWord)ciii.intregs37[3];
+ arch->vex.guest_GPR4 = (UWord)ciii.intregs37[4];
+ arch->vex.guest_GPR5 = (UWord)ciii.intregs37[5];
+ arch->vex.guest_GPR6 = (UWord)ciii.intregs37[6];
+ arch->vex.guest_GPR7 = (UWord)ciii.intregs37[7];
+ arch->vex.guest_GPR8 = (UWord)ciii.intregs37[8];
+ arch->vex.guest_GPR9 = (UWord)ciii.intregs37[9];
+ arch->vex.guest_GPR10 = (UWord)ciii.intregs37[10];
+ arch->vex.guest_GPR11 = (UWord)ciii.intregs37[11];
+ arch->vex.guest_GPR12 = (UWord)ciii.intregs37[12];
+ arch->vex.guest_GPR13 = (UWord)ciii.intregs37[13];
+ arch->vex.guest_GPR14 = (UWord)ciii.intregs37[14];
+ arch->vex.guest_GPR15 = (UWord)ciii.intregs37[15];
+ arch->vex.guest_GPR16 = (UWord)ciii.intregs37[16];
+ arch->vex.guest_GPR17 = (UWord)ciii.intregs37[17];
+ arch->vex.guest_GPR18 = (UWord)ciii.intregs37[18];
+ arch->vex.guest_GPR19 = (UWord)ciii.intregs37[19];
+ arch->vex.guest_GPR20 = (UWord)ciii.intregs37[20];
+ arch->vex.guest_GPR21 = (UWord)ciii.intregs37[21];
+ arch->vex.guest_GPR22 = (UWord)ciii.intregs37[22];
+ arch->vex.guest_GPR23 = (UWord)ciii.intregs37[23];
+ arch->vex.guest_GPR24 = (UWord)ciii.intregs37[24];
+ arch->vex.guest_GPR25 = (UWord)ciii.intregs37[25];
+ arch->vex.guest_GPR26 = (UWord)ciii.intregs37[26];
+ arch->vex.guest_GPR27 = (UWord)ciii.intregs37[27];
+ arch->vex.guest_GPR28 = (UWord)ciii.intregs37[28];
+ arch->vex.guest_GPR29 = (UWord)ciii.intregs37[29];
+ arch->vex.guest_GPR30 = (UWord)ciii.intregs37[30];
+ arch->vex.guest_GPR31 = (UWord)ciii.intregs37[31];
+
+ arch->vex.guest_CIA = (UWord)ciii.intregs37[32+0];
+ arch->vex.guest_LR = (UWord)ciii.intregs37[32+2];
+ arch->vex.guest_CTR = (UWord)ciii.intregs37[32+3];
+
+# if defined(VGP_ppc32_aix5)
+
+ LibVEX_GuestPPC32_put_CR( (UWord)ciii.intregs37[32+1], &arch->vex );
+ LibVEX_GuestPPC32_put_XER( (UWord)ciii.intregs37[32+4], &arch->vex );
+
+ /* Set the cache line size (KLUDGE) */
+ VG_(machine_ppc32_set_clszB)( 128 );
+
+# else /* defined(VGP_ppc64_aix5) */
+
+ LibVEX_GuestPPC64_put_CR( (UWord)ciii.intregs37[32+1], &arch->vex );
+ LibVEX_GuestPPC64_put_XER( (UWord)ciii.intregs37[32+4], &arch->vex );
+
+ /* Set the cache line size (KLUDGE) */
+ VG_(machine_ppc64_set_clszB)( 128 );
+
+# endif
+
+ /* Fix up the client's command line. Its argc/v/envp is in r3/4/5
+ (32-bit AIX) or r14/15/16 (64-bit AIX). but that is for the
+ Valgrind invokation as a whole. Hence we need to decrement argc
+ and advance argv to step over the args for Valgrind, and the
+ name of the Valgrind tool exe bogusly inserted by the launcher
+ (hence the "+1"). */
+
+# if defined(VGP_ppc32_aix5)
+
+ vg_assert(arch->vex.guest_GPR3 >= 1 + VG_(args_for_valgrind).used);
+ arch->vex.guest_GPR3 -= (1 + VG_(args_for_valgrind).used);
+ arch->vex.guest_GPR4 += sizeof(UWord) * (1 + VG_(args_for_valgrind).used);
+
+# else /* defined(VGP_ppc64_aix5) */
+
+ vg_assert(arch->vex.guest_GPR14 >= 1 + VG_(args_for_valgrind).used);
+ arch->vex.guest_GPR14 -= (1 + VG_(args_for_valgrind).used);
+ arch->vex.guest_GPR15 += sizeof(UWord) * (1 + VG_(args_for_valgrind).used);
+
+# endif
+
+ /* At this point the guest register state is correct for client
+ startup. However, that's not where we want to start; in fact we
+ want to start at VG_(ppc{3,64}2_aix5_do_preloads_then_start_client),
+ passing it ciii.preloadpage in r3. This will load the core/tool
+ preload .so's, then restore r2-r10 from what's stashed in the
+ preloadpage, and then start the client really. Hence: */
+
+ /* Save r2-r10 and the client start point in preloadpage */
+ ciii.preloadpage->r2 = (ULong)arch->vex.guest_GPR2;
+ ciii.preloadpage->r3 = (ULong)arch->vex.guest_GPR3;
+ ciii.preloadpage->r4 = (ULong)arch->vex.guest_GPR4;
+ ciii.preloadpage->r5 = (ULong)arch->vex.guest_GPR5;
+ ciii.preloadpage->r6 = (ULong)arch->vex.guest_GPR6;
+ ciii.preloadpage->r7 = (ULong)arch->vex.guest_GPR7;
+ ciii.preloadpage->r8 = (ULong)arch->vex.guest_GPR8;
+ ciii.preloadpage->r9 = (ULong)arch->vex.guest_GPR9;
+ ciii.preloadpage->r10 = (ULong)arch->vex.guest_GPR10;
+ ciii.preloadpage->client_start = (ULong)arch->vex.guest_CIA;
+
+
+# if defined(VGP_ppc32_aix5)
+
+ /* Set up to start at VG_(ppc32_aix5_do_preloads_then_start_client) */
+ arch->vex.guest_CIA = (UWord)&VG_(ppc32_aix5_do_preloads_then_start_client);
+
+# else /* defined(VGP_ppc64_aix5) */
+
+ /* Set up to start at VG_(ppc64_aix5_do_preloads_then_start_client) */
+ arch->vex.guest_CIA = (UWord)&VG_(ppc64_aix5_do_preloads_then_start_client);
+
+# endif
+
+ arch->vex.guest_GPR3 = (UWord)ciii.preloadpage;
+
+ /* The rest of the preloadpage fields will already have been filled
+ in by VG_(setup_client_initial_image). So we're done. */
+
+ /* Finally, decompress the page compressed by the launcher. We
+ can't do this any earlier, because the page is (effectively)
+ decompressed in place, which trashes ciii.intregs37. So we have
+ to wait till this point, at which we're done with ciii.intregs37
+ (to be precise, with what it points at). */
+ VG_(debugLog)(1, "initimg", "decompressing page at %p\n",
+ (void*)ciii.compressed_page);
+ vg_assert(VG_IS_PAGE_ALIGNED(ciii.compressed_page));
+
+ Huffman_Uncompress( (void*)ciii.compressed_page, unz_page,
+ VKI_PAGE_SIZE, VKI_PAGE_SIZE );
+ adler32_act = compute_adler32(unz_page, VKI_PAGE_SIZE);
+
+ VG_(debugLog)(1, "initimg",
+ "decompress done, adler32s: act 0x%x, exp 0x%x\n",
+ adler32_act, ciii.adler32_exp );
+
+ VG_(memcpy)((void*)ciii.compressed_page, unz_page, VKI_PAGE_SIZE);
+
+ VG_(debugLog)(1, "initimg", "copy back done\n");
+
+ /* Tell the tool that we just wrote to the registers. */
+ VG_TRACK( post_reg_write, Vg_CoreStartup, /*tid*/1, /*offset*/0,
+ sizeof(VexGuestArchState));
+
+ /* Determine the brk limit. */
+ VG_(debugLog)(1, "initimg", "establishing current brk ..\n");
+ vg_assert(__NR_AIX5_sbrk != __NR_AIX5_UNKNOWN);
+ sres = VG_(do_syscall1)(__NR_AIX5_sbrk, 0);
+ vg_assert(sres.err == 0); /* assert no error */
+ VG_(brk_base) = VG_(brk_limit) = sres.res;
+ VG_(debugLog)(1, "initimg", ".. brk = %p\n", (void*)VG_(brk_base));
+}
+
+
+/* --- Diagnose preload failures. --- */
+
+/* This is a nasty but effective kludge. The address of the following
+ function is put into the preload page. So, if a preload failure
+ happens, we call here to get helpful info printed out (the call
+ site is in m_trampoline.S). This is a dirty hack (1) because
+ diagnose_load_failure runs on the simulated CPU, not the real one
+ and (2) because it induces a libc dependency. Oh well. */
+
+/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#include <stdlib.h>
+#include <sys/ldr.h>
+/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+
+static void diagnose_load_failure ( void )
+{
+# define NBUF 1024
+ UChar buf[NBUF];
+ VG_(debugLog)(0, "initimg", "Diagnosing load failure\n");
+ if (sizeof(void*) == 8) {
+ VG_(debugLog)(0, "initimg", "Can't safely do loadquery() "
+ "in 64-bit mode. Sorry.\n");
+ /* because this requires dynamic linking to be working (IIRC)
+ and it isn't; the tool file's dynamic linking was never done,
+ because it was loaded by the bootstrap stub, which simply did
+ sys_kload() but didn't make usla do the relevant
+ relocations. */
+ } else {
+ UChar** p;
+ Int r = loadquery(L_GETMESSAGES, buf, NBUF);
+ VG_(debugLog)(0, "initimg", "loadquery returned %d (0 = success)\n", r);
+ p = (UChar**)(&buf[0]);
+ for (; *p; p++)
+ VG_(debugLog)(0, "initimg", "\"%s\"\n", *p);
+ VG_(debugLog)(0, "initimg", "Use /usr/sbin/execerror to make "
+ "sense of above string(s)\n");
+ VG_(debugLog)(0, "initimg", "See also comments at the bottom of\n");
+ VG_(debugLog)(0, "initimg", "coregrind/m_initimg/"
+ "initimg-aix5.c (in Valgrind sources)\n");
+ }
+# undef NBUF
+}
+
+/* Take the strings that this prints out and feed them
+ to /usr/sbin/execerror. For example, it might print
+
+ (ld 3 1 __libc_freeres /foo/bar/ppc32-aix5/vgpreload_core.so
+
+ in which case
+
+ $ execerror xyzzy \
+ "(ld 3 1 __libc_freeres /foo/bar/ppc32-aix5/vgpreload_core.so"
+
+ gets you
+
+ Could not load program xyzzy:
+ rtld: 0712-001 Symbol __libc_freeres was referenced
+ from module /foo/bar/ppc32-aix5/vgpreload_core.so(),
+ but a runtime definition
+ of the symbol was not found.
+*/
+
+/*--------------------------------------------------------------------*/
+/*--- initimg-aix5.c ---*/
+/*--------------------------------------------------------------------*/