rtld: Shrink by eliminating stdio
authorJohn Marino <draco@marino.st>
Fri, 16 Mar 2012 19:46:52 +0000 (20:46 +0100)
committerJohn Marino <draco@marino.st>
Fri, 16 Mar 2012 21:34:01 +0000 (22:34 +0100)
Eliminate stdio for parsing libmap.conf, and instead parse it from direct
mapping.  Also stop using strerror(3) in rtld which sucks in msgcat and
stdio.  Instead directly access sys_errlist array of error messages with
private rtld_strerror() function.

Results on x86_64 (includes debug symbols):

> size /usr/libexec/ld-elf.so.2*
   text    data     bss     dec     hex filename
  95294    1560    7680  104534   19856 /usr/libexec/ld-elf.so.2
 108830    3128   18216  130174   1fc7e /usr/libexec/ld-elf.so.2.old

> ls -al /usr/libexec/ld-elf.so.2*
-r-xr-xr-x  1 root  wheel  305763 Mar 16 20:33 /usr/libexec/ld-elf.so.2
-r-xr-xr-x  1 root  wheel  370392 Mar 14 00:02 /usr/libexec/ld-elf.so.2.old

Taken from:
FreeBSD SVN 232862 (2012-03-12)
FreeBSD SVN 232974 (2012-03-14)

libexec/rtld-elf/libmap.c
libexec/rtld-elf/map_object.c
libexec/rtld-elf/rtld.c
libexec/rtld-elf/rtld.h

index c8b60f6..2851a11 100644 (file)
@@ -2,12 +2,14 @@
  * $FreeBSD$
  */
 
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/queue.h>
 #include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include "debug.h"
 #include "rtld.h"
@@ -21,7 +23,6 @@ TAILQ_HEAD(lm_list, lm);
 struct lm {
        char *f;
        char *t;
-
        TAILQ_ENTRY(lm) lm_link;
 };
 
@@ -33,76 +34,112 @@ struct lmp {
        TAILQ_ENTRY(lmp) lmp_link;
 };
 
-static int     lm_count;
+static int lm_count;
 
-static void            lmc_parse       (FILE *);
-static void            lm_add          (const char *, const char *, const char *);
-static void            lm_free         (struct lm_list *);
-static char *          lml_find        (struct lm_list *, const char *);
-static struct lm_list *        lmp_find        (const char *);
-static struct lm_list *        lmp_init        (char *);
-static const char * quickbasename      (const char *);
-static int     readstrfn       (void * cookie, char *buf, int len);
-static int     closestrfn      (void * cookie);
+static void lmc_parse(char *, size_t);
+static void lm_add(const char *, const char *, const char *);
+static void lm_free(struct lm_list *);
+static char *lml_find(struct lm_list *, const char *);
+static struct lm_list *lmp_find(const char *);
+static struct lm_list *lmp_init(char *);
+static const char *quickbasename(const char *);
 
 #define        iseol(c)        (((c) == '#') || ((c) == '\0') || \
                         ((c) == '\n') || ((c) == '\r'))
 
+/*
+ * Do not use ctype.h macros, which rely on working TLS.  It is
+ * too early to have thread-local variables functional.
+ */
+#define        rtld_isspace(c) ((c) == ' ' || (c) == '\t')
+
 int
-lm_init (char *libmap_override)
+lm_init(char *libmap_override)
 {
-       FILE    *fp;
-
-       dbg("%s(\"%s\")", __func__, libmap_override);
+       struct stat st;
+       char *lm_map, *p;
+       int fd;
 
+       dbg("lm_init(\"%s\")", libmap_override);
        TAILQ_INIT(&lmp_head);
 
-       fp = fopen(_PATH_LIBMAP_CONF, "r");
-       if (fp) {
-               lmc_parse(fp);
-               fclose(fp);
+       fd = open(_PATH_LIBMAP_CONF, O_RDONLY);
+       if (fd == -1) {
+               dbg("lm_init: open(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
+                   rtld_strerror(errno));
+               goto override;
+       }
+       if (fstat(fd, &st) == -1) {
+               close(fd);
+               dbg("lm_init: fstat(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
+                   rtld_strerror(errno));
+               goto override;
+       }
+       lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (lm_map == (const char *)MAP_FAILED) {
+               close(fd);
+               dbg("lm_init: mmap(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
+                   rtld_strerror(errno));
+               goto override;
        }
+       close(fd);
+       lmc_parse(lm_map, st.st_size);
+       munmap(lm_map, st.st_size);
 
+override:
        if (libmap_override) {
-               char    *p;
-               /* do some character replacement to make $LIBMAP look like a
-                  text file, then "open" it with funopen */
+               /*
+                * Do some character replacement to make $LIBMAP look
+                * like a text file, then parse it.
+                */
                libmap_override = xstrdup(libmap_override);
-
                for (p = libmap_override; *p; p++) {
                        switch (*p) {
-                               case '=':
-                                       *p = ' '; break;
-                               case ',':
-                                       *p = '\n'; break;
+                       case '=':
+                               *p = ' ';
+                               break;
+                       case ',':
+                               *p = '\n';
+                               break;
                        }
                }
-               fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn);
-               if (fp) {
-                       lmc_parse(fp);
-                       fclose(fp);
-               }
+               lmc_parse(p, strlen(p));
+               free(p);
        }
 
        return (lm_count == 0);
 }
 
 static void
-lmc_parse (FILE *fp)
+lmc_parse(char *lm_p, size_t lm_len)
 {
-       char    *cp;
-       char    *f, *t, *c, *p;
-       char    prog[MAXPATHLEN];
-       char    line[MAXPATHLEN + 2];
-
-       dbg("%s(%p)", __func__, fp);
+       char *cp, *f, *t, *c, *p;
+       char prog[MAXPATHLEN];
+       char line[MAXPATHLEN + 2];
+       size_t cnt;
+       int i;
 
+       cnt = 0;
        p = NULL;
-       while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) {
+       while (cnt < lm_len) {
+               i = 0;
+               while (lm_p[cnt] != '\n' && cnt < lm_len &&
+                   i < sizeof(line) - 1) {
+                       line[i] = lm_p[cnt];
+                       cnt++;
+                       i++;
+               }
+               line[i] = '\0';
+               while (lm_p[cnt] != '\n' && cnt < lm_len)
+                       cnt++;
+               /* skip over nl */
+               cnt++;
+
+               cp = &line[0];
                t = f = c = NULL;
 
                /* Skip over leading space */
-               while (isspace(*cp)) cp++;
+               while (rtld_isspace(*cp)) cp++;
 
                /* Found a comment or EOL */
                if (iseol(*cp)) continue;
@@ -112,7 +149,7 @@ lmc_parse (FILE *fp)
                        cp++;
 
                        /* Skip leading space */
-                       while (isspace(*cp)) cp++;
+                       while (rtld_isspace(*cp)) cp++;
 
                        /* Found comment, EOL or end of selector */
                        if  (iseol(*cp) || *cp == ']')
@@ -120,11 +157,11 @@ lmc_parse (FILE *fp)
 
                        c = cp++;
                        /* Skip to end of word */
-                       while (!isspace(*cp) && !iseol(*cp) && *cp != ']')
+                       while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']')
                                cp++;
 
                        /* Skip and zero out trailing space */
-                       while (isspace(*cp)) *cp++ = '\0';
+                       while (rtld_isspace(*cp)) *cp++ = '\0';
 
                        /* Check if there is a closing brace */
                        if (*cp != ']') continue;
@@ -136,7 +173,7 @@ lmc_parse (FILE *fp)
                         * There should be nothing except whitespace or comment
                          from this point to the end of the line.
                         */
-                       while(isspace(*cp)) cp++;
+                       while(rtld_isspace(*cp)) cp++;
                        if (!iseol(*cp)) continue;
 
                        strcpy(prog, c);
@@ -146,20 +183,20 @@ lmc_parse (FILE *fp)
 
                /* Parse the 'from' candidate. */
                f = cp++;
-               while (!isspace(*cp) && !iseol(*cp)) cp++;
+               while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
 
                /* Skip and zero out the trailing whitespace */
-               while (isspace(*cp)) *cp++ = '\0';
+               while (rtld_isspace(*cp)) *cp++ = '\0';
 
                /* Found a comment or EOL */
                if (iseol(*cp)) continue;
 
                /* Parse 'to' mapping */
                t = cp++;
-               while (!isspace(*cp) && !iseol(*cp)) cp++;
+               while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
 
                /* Skip and zero out the trailing whitespace */
-               while (isspace(*cp)) *cp++ = '\0';
+               while (rtld_isspace(*cp)) *cp++ = '\0';
 
                /* Should be no extra tokens at this point */
                if (!iseol(*cp)) continue;
@@ -313,31 +350,3 @@ quickbasename (const char *path)
        }
        return (p);
 }
-
-static int
-readstrfn(void * cookie, char *buf, int len)
-{
-       static char     *current;
-       static int      left;
-       int     copied;
-
-       copied = 0;
-       if (!current) {
-               current = cookie;
-               left = strlen(cookie);
-       }
-       while (*current && left && len) {
-               *buf++ = *current++;
-               left--;
-               len--;
-               copied++;
-       }
-       return copied;
-}
-
-static int
-closestrfn(void * cookie)
-{
-       free(cookie);
-       return 0;
-}
index f33c839..a90a29e 100644 (file)
@@ -171,7 +171,7 @@ map_object(int fd, const char *path, const struct stat *sb)
       MAP_NOCORE, -1, 0);
     if (mapbase == (caddr_t) -1) {
        _rtld_error("%s: mmap of entire address space failed: %s",
-         path, strerror(errno));
+         path, rtld_strerror(errno));
        return NULL;
     }
     if (base_addr != NULL && mapbase != base_addr) {
@@ -191,7 +191,8 @@ map_object(int fd, const char *path, const struct stat *sb)
        data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED;
        if (mmap(data_addr, data_vlimit - data_vaddr, data_prot,
          data_flags, fd, data_offset) == (caddr_t) -1) {
-           _rtld_error("%s: mmap of data failed: %s", path, strerror(errno));
+           _rtld_error("%s: mmap of data failed: %s", path,
+               rtld_strerror(errno));
            return NULL;
        }
 
@@ -208,7 +209,7 @@ map_object(int fd, const char *path, const struct stat *sb)
                if ((data_prot & PROT_WRITE) == 0 && -1 ==
                     mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) {
                        _rtld_error("%s: mprotect failed: %s", path,
-                           strerror(errno));
+                           rtld_strerror(errno));
                        return NULL;
                }
 
@@ -232,7 +233,7 @@ map_object(int fd, const char *path, const struct stat *sb)
                if (mmap(bss_addr, bss_vlimit - bss_vaddr, data_prot,
                    data_flags | MAP_ANON, -1, 0) == (caddr_t)-1) {
                    _rtld_error("%s: mmap of bss failed: %s", path,
-                       strerror(errno));
+                       rtld_strerror(errno));
                    return NULL;
                }
            }
@@ -300,7 +301,7 @@ get_elf_header (int fd, const char *path)
     ssize_t nbytes;
 
     if ((nbytes = pread(fd, u.buf, PAGE_SIZE, 0)) == -1) {
-       _rtld_error("%s: read error: %s", path, strerror(errno));
+       _rtld_error("%s: read error: %s", path, rtld_strerror(errno));
        return NULL;
     }
 
index 17f6b06..9a7723c 100644 (file)
@@ -2265,7 +2265,7 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj,
            if (mprotect(obj->mapbase, obj->textsize,
              PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
                _rtld_error("%s: Cannot write-enable text segment: %s",
-                 obj->path, strerror(errno));
+                 obj->path, rtld_strerror(errno));
                return -1;
            }
        }
@@ -2292,7 +2292,7 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj,
            if (mprotect(obj->mapbase, obj->textsize,
              PROT_READ|PROT_EXEC) == -1) {
                _rtld_error("%s: Cannot write-protect text segment: %s",
-                 obj->path, strerror(errno));
+                 obj->path, rtld_strerror(errno));
                return -1;
            }
        }
@@ -2324,7 +2324,7 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj,
        if (obj->relro_size) {
            if (mprotect(obj->relro_page, obj->relro_size, PROT_READ) == -1) {
                _rtld_error("%s: Cannot enforce relro relocation: %s",
-                 obj->path, strerror(errno));
+                 obj->path, rtld_strerror(errno));
                return -1;
            }
        }
@@ -4405,3 +4405,12 @@ void
 __pthread_cxa_finalize(struct dl_phdr_info *a)
 {
 }
+
+const char *
+rtld_strerror(int errnum)
+{
+
+       if (errnum < 0 || errnum >= sys_nerr)
+               return ("Unknown error");
+       return (sys_errlist[errnum]);
+}
index d70d788..b58cea7 100644 (file)
@@ -306,6 +306,7 @@ typedef struct Struct_SymLook {
 } SymLook;
 
 void _rtld_error(const char *, ...) __printflike(1, 2);
+const char *rtld_strerror(int);
 Obj_Entry *map_object(int, const char *, const struct stat *);
 void *xcalloc(size_t);
 void *xmalloc(size_t);