[comp.unix.aux] How do you read symbols in /dev/kmem when nlist won't work

name@portiaStanford.EDU (tony cooper) (06/06/90)

nlist is declared in A/UX starting as:

struct  nlist { /* symbol table entry */
	char    n_name[8];      /* symbol name */

Well n_name is restricted to 8 bytes so names must be 7 bytes long or less.
So how do you read sysmbols that are longer than 7 characters? There are
plenty of symbols that are longer. Most other UNIXs declare char *n_name so
they don't have this problem.

The only source code example I have is xload. But it reads the symbol avenrun
which is 7 characters. Other A/UX programs can read longer sysmbols eg netstat
reads symbols longer than 7 such as icmpstat and rthashsize. (But most
symbol reading programs read short symbols only).

Any ideas?

Tony Cooper
tony@popserver.stanford.edu

liam@cs.qmw.ac.uk (William Roberts) (06/08/90)

In <1990Jun6.003947.27323@portia.Stanford.EDU> name@portiaStanford.EDU (tony cooper) writes:

>nlist is declared in A/UX starting as:

>struct  nlist { /* symbol table entry */
>       char    n_name[8];      /* symbol name */

>Well n_name is restricted to 8 bytes so names must be 7 bytes long or less.
>So how do you read sysmbols that are longer than 7 characters?

Close but no cigar. nlist is actually defined as

        #define nlist syment

in both A/UX 1.1.1 and A/UX 2.0b10, so what you are getting is
a struct syment. In order to allow backwards compatibility with
the Stone Age, the first 8 bytes of the structure are defined
in <syms.h> as

        union
        {
                char            _n_name[SYMNMLEN];      /* old COFF version */
                struct
                {
                        long    _n_zeroes;      /* new == 0 */
                        long    _n_offset;      /* offset into string table */
                } _n_n;
                char            *_n_nptr[2];    /* allows for overlaying */
        } _n;

Symbols of up to 8 bytes are placed in _n_name by old-style
compilers. New style compilers (e.g. the A/UX ones) set _n_zeroes
to zero as a marker, and then put an offset into the string
table in _n_offset. Naturally there are #defines to hide the
_n union and the _n_n structure. Old and new styles don't mix
(I don't think) and the syment() library routine returns the
value associated with the symbol (normally an address) in n_value.

>There are
>plenty of symbols that are longer. Most other UNIXs declare char *n_name so
>they don't have this problem.
>The only source code example I have is xload. But it reads the symbol avenrun
>which is 7 characters. Other A/UX programs can read longer sysmbols eg netstat
>reads symbols longer than 7 such as icmpstat and rthashsize. (But most
>symbol reading programs read short symbols only).

**** WARNING: I am now speculating - see confession below ****

As explained above, you can still do this, but you need 4 zero
bytes first to show that this is what you are doing. This is a
snag for the standard BSD cliche, namely

    struct nlist Nl[] = {
        { "icmpstat" },
    #define NL_ICMPSTAT         0
        { "rthashsize" },
    #define NL_RTHASHSIZE       1
        0
    };

Why? Because you can't declare a static initialiser for a union.
I've been think about this today (and running adb on the
nlist.o file from libc.a, which is where I gleaned the above
details) and my considered solution is as follows:

Replace the existing nlist.h with the one from the shar file
given below.

Edit your code so that it converts anything of the form shown
above into the the following form

    struct nlist_syment Nl[] = {
        NLIST_ENTRY("icmpstat"),
    #define NL_ICMPSTAT         0
        NLIST_ENTRY("rthashsize"),
    #define NL_RTHASHSIZE       1
        NLIST_ENTRY(0)
    };

You haven't got real source code compatibility anyway, and I
think that the above change is small enough to be OK for the
simple things: obviously anything which uses fields other than
n_name and n_value are going to need extensive rewriting.

In the longer term, perhaps Apple could simply abandon the
defunct 8 character limit on symbol names, though this still
won't help make BSD and SysV completely interchangeable.

Here's my replacement for nlist.h and a little demo program.
I have to confess that the test program seems unable to read
quite a variety of symbols which definitely DO exist - anyone
know what I haven't got quite right?

# cc -o nlist_test nlist_test.c; nlist_test
symbol icmpstat has value 0x0 (type 0)
symbol rthashsize has value 0x110087c0 (type 0)
symbol icmp_input has value 0x1004990e (type 0)
symbol avenrun has value 0x0 (type 0)
symbol icmproto has value 0x0 (type 0)
# nm -x /unix | egrep "icmpstat|rthashsize|icmp_input|avenrun|icmproto"
icmproto            |0x11008d20|static|                 |      |     |.data
avenrun             |0x12011440|extern|                 |      |     |.bss
rthashsize          |0x110087c0|extern|                 |      |     |.data
icmp_input          |0x1004990e|extern|                 |      |     |.text
icmpstat            |0x12017c34|extern|                 |      |     |.bss
#

Just as well I decided to test it :-(

#         This is a shar archive.
#         Remove everything above and including the cut line.
#         Then run the rest of the file through sh.
#--------cut--------cut--------cut--------cut--------cut--------
#! /bin/sh
#  shar:  Shell Archiver
#         Run the following with /bin/sh to create:
#             nlist.h
#             nlist_test.c
# This archive created: Thu Jun  7 19:47:30 WET DST 1990
echo shar: extracting "nlist.h" '('1498 chars')'
if test -f nlist.h
then
    echo shar: will not overwrite existing file "nlist.h"
else
cat << \SHAR_EOF > nlist.h
/* Imitation nlist.h file for rough and ready compatibility between
 * the BSD style of doing things and SysV
 *
 * Use this file in preference to a.out.h for BSD files, though
 * you might want to convert over entirely to the SysV scheme.
 *
 * William Roberts      QMW, University of London       7th June 1990
 */

#ifndef __NLIST__
#define __NLIST__

struct nlist_syment
{
        long                    n_zeroes;       /*  must be 0 */
        char                    *n_name;        /* offset into string table */
        long                    n_value;        /* value of symbol */
        short                   n_scnum;        /* section number */
        unsigned short          n_type;         /* type and derived type */
        char                    n_sclass;       /* storage class */
        char                    n_numaux;       /* number of aux. entries */
};

#define nlist syment

#define NLIST_ENTRY(s)  {0, (s)}

/* The tradition BSD cliche of
 *
 *     struct nlist nl[] = {
 *          { "name1" },
 *          { "name2" },
 *          { "" }
 *     }
 *
 * should be converted to
 *
 *     struct nlist_syment nl[] = {
 *          NLIST_ENTRY("name1"),
 *          NLIST_ENTRY("name2"),
 *          NLIST_ENTRY(0)
 *     };
 *
 * Note that you probably won't get away with using an empty string as the
 * end marker rather than NULL.
 *
 * You need the struct nlist_syment as defined above otherwise you will get
 * the struct syment which includes a union and so can't be given a
 * static initialiser.
 */

#endif
SHAR_EOF
if test 1498 -ne `wc -c < nlist.h`
then
    echo shar: error transmitting "nlist.h" '('should be 1498 chars')'
else
    echo nlist.h
fi
fi
echo shar: extracting "nlist_test.c" '('494 chars')'
if test -f nlist_test.c
then
    echo shar: will not overwrite existing file "nlist_test.c"
else
cat << \SHAR_EOF > nlist_test.c
#include <stdio.h>
#include "nlist.h"

struct nlist_syment Nl[] = {
      NLIST_ENTRY("icmpstat"),
      NLIST_ENTRY("rthashsize"),
      NLIST_ENTRY("icmp_input"),
      NLIST_ENTRY("avenrun"),
      NLIST_ENTRY("icmproto"),
      NLIST_ENTRY(0)
};

main()
{
    int i;

    i = nlist("/unix", Nl);
    if (i < 0) {
        perror("nlist");
    } else {
        for (i=0; Nl[i].n_name != 0; i++) {
            printf("symbol %s has value 0x%x (type %d)\n",
                Nl[i].n_name, Nl[i].n_value, Nl[i].n_type);
        }
    }
}
SHAR_EOF
if test 494 -ne `wc -c < nlist_test.c`
then
    echo shar: error transmitting "nlist_test.c" '('should be 494 chars')'
else
    echo nlist_test.c
fi
fi
#         End of shar archive
exit 0
-- 

William Roberts                 ARPA: liam@cs.qmw.ac.uk
Queen Mary & Westfield College  UUCP: liam@qmw-cs.UUCP
Mile End Road                   AppleLink: UK0087
LONDON, E1 4NS, UK              Tel:  071-975 5250 (Fax: 081-980 6533)

liam@cs.qmw.ac.uk (William Roberts) (06/08/90)

Got it! The common factor between the things that didn't work
with my version of nlist.h (some of which have worked for me in
other A/UX programs after I had converted to dynamic
initialisation into the n_name array) is

        THEY ARE LESS THAN 9 CHARACTERS LONG

You guessed it - the compiler puts the symbol directly into the
entry if it is small enough to fit, only using the string table
if the name is too long. Damn!

Apple, please can you fix nlist.c (aka syment()) so that it
checks the length of new-style labels passed in as a parameter
and is prepared to be flexible about matching oldstyle with
newstyle.

I've reworked my nlist.h file but it is now terribly ugly. The
revised nlist.h and nlist_test.c files are appended to this
message, and the compilation and test run go as follows:

# cc -o nlist_test nlist_test.c
"nlist_test.c", line 25: warning: non-null byte ignored in string initializer
"nlist_test.c", line 26: warning: non-null byte ignored in string initializer
# nlist_test
First, the long names...
symbol rthashsize has value 0x110087c0 (type 0)
symbol icmp_input has value 0x1004990e (type 0)
symbol icmproto has value 0x0 (type 0)
symbol avenrun has value 0x0 (type 0)
symbol clstat has value 0x0 (type 0)
symbol icmpstat has value 0x0 (type 0)

Then the short names...
symbol icmproto has value 0x11008d20 (type 0)
symbol avenrun has value 0x12011440 (type 0)
symbol clstat has value 0x1201c444 (type 0)
symbol icmpstat has value 0x12017c34 (type 0)
symbol rthashsi has value 0x0 (type 0)
symbol icmp_inp has value 0x0 (type 0)
#

You guessed, it doesn't try to do anything clever about
matching just the first 8 characters either (probably a good
thing).

Hope this helps you fix your problems with xload...

#         This is a shar archive.
#         Remove everything above and including the cut line.
#         Then run the rest of the file through sh.
#--------cut--------cut--------cut--------cut--------cut--------
#! /bin/sh
#  shar:  Shell Archiver
#         Run the following with /bin/sh to create:
#             nlist.h
#             nlist_test.c
# This archive created: Thu Jun  7 20:42:47 WET DST 1990
echo shar: extracting "nlist.h" '('2365 chars')'
if test -f nlist.h
then
    echo shar: will not overwrite existing file "nlist.h"
else
cat << \SHAR_EOF > nlist.h
/* Imitation nlist.h file for rough and ready compatibility between
 * the BSD style of doing things and SysV
 *
 * Use this file in preference to a.out.h for BSD files, though
 * you might want to convert over entirely to the SysV scheme.
 *
 * William Roberts      QMW, University of London       7th June 1990
 */

#ifndef __NLIST__
#define __NLIST__

struct nlist_syment_long
{
        long                n_zeroes;           /*  must be 0 */
        char                *n_name;            /* offset into string table */
        long                n_value;            /* value of symbol */
        short               n_scnum;            /* section number */
        unsigned short      n_type;             /* type and derived type */
        char                n_sclass;           /* storage class */
        char                n_numaux;           /* number of aux. entries */
};

#define  SYMNMLEN       8
struct nlist_syment_short
{
        char                n_name[SYMNMLEN];   /* the symbol name */
        long                n_value;            /* value of symbol */
        short               n_scnum;            /* section number */
        unsigned short      n_type;             /* type and derived type */
        char                n_sclass;           /* storage class */
        char                n_numaux;           /* number of aux. entries */
};

#define nlist syment

#define NLIST_LONG_ENTRY(s)  {0, (s)}
#define NLIST_SHORT_ENTRY(s) {(s)}

/* The tradition BSD cliche of
 *
 *     struct nlist nl[] = {
 *          { "longname1" },
 *          { "name2" },
 *          { 0 }
 *     }
 *
 * should be converted to
 *
 *     struct nlist_syment_long nl_long[] = {
 *          NLIST_LONG_ENTRY("longname1"),
 *          NLIST_LONG_ENTRY(0)
 *     };
 *
 *     struct nlist_syment_short nl_short[] = {
 *          NLIST_SHORT_ENTRY("name2"),
 *          NLIST_SHORT_ENTRY(0)
 *     };
 *
 * The reason for having two different structures is that the brain-damaged
 * linker insists on saving a few bytes for short symbols by using the
 * other variant, but the syment routine (equivalent to nlist) will
 * only compare like with like.
 *
 * Note that you probably won't get away with using an empty string as the
 * end marker rather than NULL.
 *
 * You need the struct nlist_syments as defined above otherwise you will get
 * the struct syment which includes a union and so can't be given a
 * static initialiser.
 */

#endif
SHAR_EOF
if test 2365 -ne `wc -c < nlist.h`
then
    echo shar: error transmitting "nlist.h" '('should be 2365 chars')'
else
    echo nlist.h
fi
fi
echo shar: extracting "nlist_test.c" '('1683 chars')'
if test -f nlist_test.c
then
    echo shar: will not overwrite existing file "nlist_test.c"
else
cat << \SHAR_EOF > nlist_test.c
#include <stdio.h>
#include "nlist.h"

struct nlist_syment_long Nl_long[] = {
      NLIST_LONG_ENTRY("rthashsize"),
      NLIST_LONG_ENTRY("icmp_input"),
/* the rest won't work because the COFF file will have them as
 * short form entries...
 */
      NLIST_LONG_ENTRY("icmproto"),
      NLIST_LONG_ENTRY("avenrun"),
      NLIST_LONG_ENTRY("clstat"),
      NLIST_LONG_ENTRY("icmpstat"),
      NLIST_LONG_ENTRY(0)
};
struct nlist_syment_short Nl_short[] = {
      NLIST_SHORT_ENTRY("icmproto"),
      NLIST_SHORT_ENTRY("avenrun"),
      NLIST_SHORT_ENTRY("clstat"),
      NLIST_SHORT_ENTRY("icmpstat"),
/* These are long entries - the compiler will issue a warning when
 * it truncates the initialiser and then they won't match with the
 * long form entries in the COFF file.
 */
      NLIST_SHORT_ENTRY("rthashsize"),
      NLIST_SHORT_ENTRY("icmp_input"),
      NLIST_SHORT_ENTRY(0)
};

main()
{
    int i;

    printf("First, the long names...\n");
    i = nlist("/unix", Nl_long);
    if (i < 0) {
        perror("nlist");
    } else {
        for (i=0; Nl_long[i].n_name != 0; i++) {
            printf("symbol %s has value 0x%x (type %d)\n",
                Nl_long[i].n_name, Nl_long[i].n_value, Nl_long[i].n_type);
        }
    }

    /* Note that the "run off end" test for Nl_short is
     * different, and that we need to restrict the string length
     * to a maximum of 8 characters when printing it out
     */
    printf("\nThen the short names...\n");
    i = nlist("/unix", Nl_short);
    if (i < 0) {
        perror("nlist");
    } else {
        for (i=0; Nl_short[i].n_name[0] != 0; i++) {
            printf("symbol %.8s has value 0x%x (type %d)\n",
                Nl_short[i].n_name, Nl_short[i].n_value, Nl_short[i].n_type);
        }
    }
}
SHAR_EOF
if test 1683 -ne `wc -c < nlist_test.c`
then
    echo shar: error transmitting "nlist_test.c" '('should be 1683 chars')'
else
    echo nlist_test.c
fi
fi
#         End of shar archive
exit 0
-- 

William Roberts                 ARPA: liam@cs.qmw.ac.uk
Queen Mary & Westfield College  UUCP: liam@qmw-cs.UUCP
Mile End Road                   AppleLink: UK0087
LONDON, E1 4NS, UK              Tel:  071-975 5250 (Fax: 081-980 6533)