from netbsd; Add capability to read archives (from Thomas Eberhardt; PR#497)
authorderaadt <deraadt@openbsd.org>
Mon, 15 Jan 1996 20:12:13 +0000 (20:12 +0000)
committerderaadt <deraadt@openbsd.org>
Mon, 15 Jan 1996 20:12:13 +0000 (20:12 +0000)
usr.bin/size/size.1
usr.bin/size/size.c

index 3e70fe5..cad9d12 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $NetBSD: size.1,v 1.5 1994/12/21 08:07:20 jtc Exp $
+.\"    $NetBSD: size.1,v 1.6 1996/01/14 23:07:11 pk Exp $
 .\"
 .\" Copyright (c) 1990, 1993, 1994
 .\"    The Regents of the University of California.  All rights reserved.
 .Nd display object file segment sizes (text, data and bss)
 .Sh SYNOPSIS
 .Nm size
-.Op Ar object_file ...
+.Op Fl w
+.Ar
 .Sh DESCRIPTION
 .Nm Size
 displays the text, data and bss segment sizes of the specified
-.Ar object_file
+.Ar file(s)
 in bytes (in decimal), and the sum of the three segments (in
 decimal and hexadecimal).
+If a library (archive) is given,
+.Nm
+displays the segment sizes for each object archive member.
 If no
-.Ar object_file
+.Ar file
 is specified
 .Nm
 attempts to report on the file
 .Pa a.out .
+.Bl -tag -width flag
+.It Fl w
+Warn about non-object archive members.
+Normally,
+.Nm
+will silently ignore all archive members which are not
+object files.
+.El
 .Sh SEE ALSO
 .Xr a.out 5
 .Sh HISTORY
index 77c5c8b..676e705 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: size.c,v 1.6 1994/12/21 08:07:21 jtc Exp $     */
+/*     $NetBSD: size.c,v 1.7 1996/01/14 23:07:12 pk Exp $      */
 
 /*
  * Copyright (c) 1988, 1993
@@ -43,18 +43,26 @@ static char copyright[] =
 #if 0
 static char sccsid[] = "@(#)size.c     8.2 (Berkeley) 12/9/93";
 #endif
-static char rcsid[] = "$NetBSD: size.c,v 1.6 1994/12/21 08:07:21 jtc Exp $";
+static char rcsid[] = "$NetBSD: size.c,v 1.7 1996/01/14 23:07:12 pk Exp $";
 #endif /* not lint */
 
 #include <sys/param.h>
 #include <sys/file.h>
 #include <a.out.h>
+#include <ar.h>
+#include <ranlib.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <err.h>
 
-int    show __P((int, char *));
+int ignore_bad_archive_entries = 1;
+
+int    process_file __P((int, char *));
+int    show_archive __P((int, char *, FILE *));
+int    show_object __P((int, char *, FILE *));
+void   *emalloc __P((size_t));
+void   *erealloc __P((void *, size_t));
 void   usage __P((void));
 
 int
@@ -64,8 +72,11 @@ main(argc, argv)
 {
        int ch, eval;
 
-       while ((ch = getopt(argc, argv, "")) != EOF)
+       while ((ch = getopt(argc, argv, "w")) != EOF)
                switch(ch) {
+               case 'w':
+                       ignore_bad_archive_entries = 0;
+                       break;
                case '?':
                default:
                        usage();
@@ -76,33 +87,191 @@ main(argc, argv)
        eval = 0;
        if (*argv)
                do {
-                       eval |= show(argc, *argv);
+                       eval |= process_file(argc, *argv);
                } while (*++argv);
        else
-               eval |= show(1, "a.out");
+               eval |= process_file(1, "a.out");
        exit(eval);
 }
 
+/*
+ * process_file()
+ *     show symbols in the file given as an argument.  Accepts archive and
+ *     object files as input.
+ */
+int
+process_file(count, fname)
+       int count;
+       char *fname;
+{
+       struct exec exec_head;
+       FILE *fp;
+       int retval;
+       char magic[SARMAG];
+    
+       if (!(fp = fopen(fname, "r"))) {
+               warnx("cannot read %s", fname);
+               return(1);
+       }
+
+       /*
+        * first check whether this is an object file - read a object
+        * header, and skip back to the beginning
+        */
+       if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) {
+               warnx("%s: bad format", fname);
+               (void)fclose(fp);
+               return(1);
+       }
+       rewind(fp);
+
+       /* this could be an archive */
+       if (N_BADMAG(exec_head)) {
+               if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 ||
+                   strncmp(magic, ARMAG, SARMAG)) {
+                       warnx("%s: not object file or archive", fname);
+                       (void)fclose(fp);
+                       return(1);
+               }
+               retval = show_archive(count, fname, fp);
+       } else
+               retval = show_objfile(count, fname, fp);
+       (void)fclose(fp);
+       return(retval);
+}
+
+/*
+ * show_archive()
+ *     show symbols in the given archive file
+ */
+int
+show_archive(count, fname, fp)
+       int count;
+       char *fname;
+       FILE *fp;
+{
+       struct ar_hdr ar_head;
+       struct exec exec_head;
+       int i, rval;
+       long last_ar_off;
+       char *p, *name;
+       int baselen, namelen;
+
+       baselen = strlen(fname) + 3;
+       namelen = sizeof(ar_head.ar_name);
+       name = emalloc(baselen + namelen);
+
+       rval = 0;
+
+       /* while there are more entries in the archive */
+       while (fread((char *)&ar_head, sizeof(ar_head), (size_t)1, fp) == 1) {
+               /* bad archive entry - stop processing this archive */
+               if (strncmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
+                       warnx("%s: bad format archive header", fname);
+                       (void)free(name);
+                       return(1);
+               }
+
+               /* remember start position of current archive object */
+               last_ar_off = ftell(fp);
+
+               /* skip ranlib entries */
+               if (!strncmp(ar_head.ar_name, RANLIBMAG, sizeof(RANLIBMAG) - 1))
+                       goto skip;
+
+               /*
+                * construct a name of the form "archive.a:obj.o:" for the
+                * current archive entry if the object name is to be printed
+                * on each output line
+                */
+               p = name;
+               if (count > 1)
+                       p += sprintf(p, "%s:", fname);
+#ifdef AR_EFMT1
+               /*
+                * BSD 4.4 extended AR format: #1/<namelen>, with name as the
+                * first <namelen> bytes of the file
+                */
+               if (            (ar_head.ar_name[0] == '#') &&
+                               (ar_head.ar_name[1] == '1') &&
+                               (ar_head.ar_name[2] == '/') && 
+                               (isdigit(ar_head.ar_name[3]))) {
+
+                       int len = atoi(&ar_head.ar_name[3]);
+                       if (len > namelen) {
+                               p -= (long)name;
+                               name = (char *)erealloc(name, baselen+len);
+                               namelen = len;
+                               p += (long)name;
+                       }
+                       if (fread(p, len, 1, fp) != 1) {
+                               (void)fprintf(stderr,
+                                   "nm: %s: premature EOF.\n", name);
+                               (void)free(name);
+                               return 1;
+                       }
+                       p += len;
+               } else
+#endif
+               for (i = 0; i < sizeof(ar_head.ar_name); ++i)
+                       if (ar_head.ar_name[i] && ar_head.ar_name[i] != ' ')
+                               *p++ = ar_head.ar_name[i];
+               *p++ = '\0';
+
+               /* get and check current object's header */
+               if (fread((char *)&exec_head, sizeof(exec_head),
+                   (size_t)1, fp) != 1) {
+                       warnx("%s: premature EOF", name);
+                       (void)free(name);
+                       return(1);
+               }
+
+               if (N_BADMAG(exec_head)) {
+                       if (!ignore_bad_archive_entries) {
+                               warnx("%s: bad format", name);
+                               rval = 1;
+                       }
+               } else {
+                       (void)fseek(fp, (long)-sizeof(exec_head),
+                           SEEK_CUR);
+                       rval |= show_objfile(2, name, fp);
+               }
+
+               /*
+                * skip to next archive object - it starts at the next
+                * even byte boundary
+                */
+#define even(x) (((x) + 1) & ~1)
+skip:          if (fseek(fp, last_ar_off + even(atol(ar_head.ar_size)),
+                   SEEK_SET)) {
+                       warn("%s", fname);
+                       (void)free(name);
+                       return(1);
+               }
+       }
+       (void)free(name);
+       return(rval);
+}
+
 int
-show(count, name)
+show_objfile(count, name, fp)
        int count;
        char *name;
+       FILE *fp;
 {
        static int first = 1;
        struct exec head;
        u_long total;
-       int fd;
 
-       if ((fd = open(name, O_RDONLY, 0)) < 0) {
-               warn("%s", name);
-               return (1);
+       if (fread((char *)&head, sizeof(head), (size_t)1, fp) != 1) {
+               warnx("%s: cannot read header", name);
+               return(1);
        }
-       if (read(fd, &head, sizeof(head)) != sizeof(head) || N_BADMAG(head)) {
-               (void)close(fd);
-               warnx("%s: not in a.out format", name);
-               return (1);
+
+       if (N_BADMAG(head)) {
+               warnx("%s: bad format", name);
+               return(1);
        }
-       (void)close(fd);
 
        if (first) {
                first = 0;
@@ -117,9 +286,32 @@ show(count, name)
        return (0);
 }
 
+void *
+emalloc(size)
+       size_t size;
+{
+       char *p;
+
+       /* NOSTRICT */
+       if (p = malloc(size))
+               return(p);
+       err(1, NULL);
+}
+
+void *
+erealloc(p, size)
+       void   *p;
+       size_t size;
+{
+       /* NOSTRICT */
+       if (p = realloc(p, size))
+               return(p);
+       err(1, NULL);
+}
+
 void
 usage()
 {
-       (void)fprintf(stderr, "usage: size [file ...]\n");
+       (void)fprintf(stderr, "usage: size [-w] [file ...]\n");
        exit(1);
 }