]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Implement --build-id command line parameter for ld.
authorUlrich Drepper <drepper@redhat.com>
Thu, 31 Jan 2008 19:44:12 +0000 (19:44 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 31 Jan 2008 19:44:12 +0000 (19:44 +0000)
lib/ChangeLog
lib/Makefile.am
libelf/ChangeLog
libelf/elf_strptr.c
src/ChangeLog
src/elf32-i386.script
src/ld.c
src/ld.h
src/ldgeneric.c
src/ldlex.l

index cc2850c3ad010ba1415d68c24dd26117c755e935..1697d00ec21ac6f8aa8edc2b5bd5b46746a25093 100644 (file)
@@ -1,3 +1,8 @@
+2008-01-31  Ulrich Drepper  <drepper@redhat.com>
+
+       * Makefile.am (libeu_a_SOURCES): Add md5.c.
+       (noinst_HEADERS): Add md5.h.
+
 2006-04-04  Ulrich Drepper  <drepper@redhat.com>
 
        * Makefile.am (libeu_a_SOURCES): We don't need xstrdup in the moment.
index 4218e9639aeb9214408ffd46272cd103a40f90be..7ee03463acd4d79192d539a7df59e0c2b9f70c77 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 1996-2001, 2002, 2004, 2005 Red Hat, Inc.
+## Copyright (C) 1996-2001, 2002, 2004, 2005, 2008 Red Hat, Inc.
 ## This file is part of Red Hat elfutils.
 ##
 ## Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -36,9 +36,9 @@ INCLUDES = -I$(srcdir)/../libelf -I..
 noinst_LIBRARIES = libeu.a
 
 libeu_a_SOURCES = xstrndup.c xmalloc.c next_prime.c \
-                 crc32.c crc32_file.c
+                 crc32.c crc32_file.c md5.c
 
-noinst_HEADERS = fixedsizehash.h system.h dynamicsizehash.h list.h
+noinst_HEADERS = fixedsizehash.h system.h dynamicsizehash.h list.h md5.h
 EXTRA_DIST = dynamicsizehash.c
 
 if !GPROF
index 0688cbdbed47c5ff1cfaa3cf35bf5b9469c5dd29..17633ba7d677914c81933ef6e0252cc8b4396dc4 100644 (file)
@@ -1,3 +1,8 @@
+2008-01-31  Ulrich Drepper  <drepper@redhat.com>
+
+       * elf_strptr.c (elf_strptr): Don't fail if the ELF file is currently
+       under construction and no raw data can be read from disk.
+
 2008-01-20  Roland McGrath  <roland@redhat.com>
 
        * elf_getaroff.c: Calculate from start_offset, instead of using
index 28042211967c84d38ffc603eca871a1ddbe60d86..c1ea60de5a6522cc39a50e22deada8084936148f 100644 (file)
@@ -1,5 +1,5 @@
 /* Return string pointer from string section.
-   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Red Hat, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2008 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 1998.
 
@@ -140,12 +140,30 @@ elf_strptr (elf, idx, offset)
        }
     }
 
-  if (strscn->rawdata_base == NULL
+  if (strscn->rawdata_base == NULL && ! strscn->data_read
       /* Read the section data.  */
       && __libelf_set_rawdata (strscn) != 0)
     goto out;
 
-  result = &strscn->rawdata_base[offset];
+  if (likely (strscn->rawdata_base != NULL))
+    result = &strscn->rawdata_base[offset];
+  else
+    {
+      /* This is a file which is currently created.  Use the list of
+        data blocks.  */
+      struct Elf_Data_List *dl = &strscn->data_list;
+      while (dl != NULL)
+       {
+         if (offset >= (size_t) dl->data.d.d_off
+             && offset < dl->data.d.d_off + dl->data.d.d_size)
+           {
+             result = (char *) dl->data.d.d_buf + (offset - dl->data.d.d_off);
+             break;
+           }
+
+         dl = dl->next;
+       }
+    }
 
  out:
   rwlock_unlock (elf->lock);
index bbc2708c3067f92beecd987f01372eeb39a1f59f..6f5f43377e943bec7c3b3bb24cca3ed990a638b3 100644 (file)
@@ -1,3 +1,12 @@
+2008-01-31  Ulrich Drepper  <drepper@redhat.com>
+
+       * elf32-i386.script: Add .note.ABI-tag and .note.gnu.build-id sections.
+       * ld.c: Recognize --build-id command line parameter.
+       * ld.h: Define scn_dot_note_gnu_build_id.
+       (struct ld_state): Add build_id and buildidscnidx elements.
+       * ldgeneric.c: Implement --build-id command line parameter.
+       * ldlex.l (ID): Recognize - as valid character after the first one.
+
 2008-01-29  Ulrich Drepper  <drepper@redhat.com>
 
        * ld.c (replace_args): New function.
index e3f6906c676ef81fdf2d82cac98213c9593094c6..1c46b690d1fd268b5bb7c7a975c5ded88354c7b1 100644 (file)
@@ -18,6 +18,8 @@ SEGMENT [RX]
 #endif
 
   .interp;
+  .note.ABI-tag;
+  .note.gnu.build-id;
   .hash;
   .gnu.hash;
   .dynsym;
index 6f8048f547faa5ac25760f0f72032e15b546976f..3b422bc649b524cabb336803695ed5762fe33e0d 100644 (file)
--- a/src/ld.c
+++ b/src/ld.c
@@ -74,6 +74,7 @@ enum
     ARGP_no_as_needed,
     ARGP_eh_frame_hdr,
     ARGP_hash_style,
+    ARGP_build_id,
 #if YYDEBUG
     ARGP_yydebug,
 #endif
@@ -164,12 +165,14 @@ Default rules of extracting from archive; weak references are not enough."),
   { "dynamic-linker", 'I', "NAME", 0, N_("Set the dynamic linker name."), 0 },
   { NULL, 'Q', "YN", OPTION_HIDDEN, NULL, 0 },
   { "-Q y | n", 'Q', NULL, OPTION_DOC,
-    N_("Add/suppress addition indentifying link-editor to .comment section"),
+    N_("Add/suppress addition indentifying link-editor to .comment section."),
     0 },
   { "eh-frame-hdr", ARGP_eh_frame_hdr, NULL, 0,
     N_("Create .eh_frame_hdr section"), 0 },
   { "hash-style", ARGP_hash_style, "STYLE", 0,
     N_("Set hash style to sysv, gnu or both."), 0 },
+  { "build-id", ARGP_build_id, "STYLE", OPTION_ARG_OPTIONAL,
+    N_("Generate build ID note (md5 (default), uuid)."), 0 },
 
   { NULL, 0, NULL, 0, N_("Linker Operation Control:"), 0 },
   { "verbose", 'v', NULL, 0, N_("Verbose messages."), 0 },
@@ -519,6 +522,30 @@ replace_args (int argc, char *argv[])
 }
 
 
+static int
+valid_hexarg (const char *arg)
+{
+  if (strncasecmp (arg, "0x", 2) != 0)
+    return 0;
+
+  arg += 2;
+  do
+    {
+      if (isxdigit (arg[0]) && isxdigit (arg[1]))
+       {
+         arg += 2;
+         if (arg[0] == '-' || arg[0] == ':')
+           ++arg;
+       }
+      else
+       return 0;
+    }
+  while (*arg != '\0');
+
+  return 1;
+}
+
+
 /* Quick scan of the parameter list for options with global effect.  */
 static error_t
 parse_opt_1st (int key, char *arg,
@@ -659,6 +686,17 @@ parse_opt_1st (int key, char *arg,
        error (EXIT_FAILURE, 0, gettext ("invalid hash style '%s'"), arg);
       break;
 
+    case ARGP_build_id:
+      if (arg == NULL)
+       ld_state.build_id = "md5";
+      else if (strcmp (arg, "uuid") != 0
+              && strcmp (arg, "md5") != 0
+              && !valid_hexarg (arg))
+       error (EXIT_FAILURE, 0, gettext ("invalid build-ID style '%s'"), arg);
+      else
+       ld_state.build_id = arg;
+      break;
+
     case 's':
       if (arg == NULL)
        {
index d4ad8f9dc1b543609e9717a9775ce6203fd7ea6e..860bcdb4e9cff80d18203873b3e7010e370a835c 100644 (file)
--- a/src/ld.h
+++ b/src/ld.h
@@ -689,7 +689,8 @@ struct scnhead
       scn_dot_plt,             /* Generated .plt section.  */
       scn_dot_pltrel,          /* Generated .rel.plt section.  */
       scn_dot_version,         /* Generated .gnu.version section.  */
-      scn_dot_version_r                /* Generated .gnu.version_r section.  */
+      scn_dot_version_r,       /* Generated .gnu.version_r section.  */
+      scn_dot_note_gnu_build_id        /* Generated .note.gnu.build-id section.  */
     } kind;
 
   /* True is the section is used in the output.  */
@@ -1044,6 +1045,10 @@ struct ld_state
      the dynamic symbol table.  */
   bool export_all_dynamic;
 
+  /* Build-ID style.  NULL is none.  */
+  const char *build_id;
+  Elf32_Word buildidscnidx;
+
   /* If DSO is generated, this is the SONAME.  */
   const char *soname;
 
index a1076a0de97be646dbc82ab8805c96cf4c79380a..6674887b0e72fcbe08375ec5d5fd34e6493fdf8f 100644 (file)
@@ -28,6 +28,7 @@
 #endif
 
 #include <assert.h>
+#include <ctype.h>
 #include <dlfcn.h>
 #include <errno.h>
 #include <error.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 
-#include <system.h>
+#include <elf-knowledge.h>
 #include "ld.h"
 #include "list.h"
+#include <md5.h>
+#include <system.h>
 
 
 /* Header of .eh_frame_hdr section.  */
@@ -2432,6 +2435,11 @@ ld_generic_generate_sections (struct ld_state *statep)
   /* The relocation section type.  */
   int rel_type = REL_TYPE (&ld_state) == DT_REL ? SHT_REL : SHT_RELA;
 
+  /* When requested, every output file will have a build ID section.  */
+  if (statep->build_id != NULL)
+    new_generated_scn (scn_dot_note_gnu_build_id, ".note.gnu.build-id",
+                      SHT_NOTE, SHF_ALLOC, 0, 4);
+
   /* When building dynamically linked object we have to include a
      section containing a string describing the interpreter.  This
      should be at the very beginning of the file together with the
@@ -4089,6 +4097,163 @@ cannot create hash table section for output file: %s"),
 }
 
 
+static void
+create_build_id_section (Elf_Scn *scn)
+{
+  /* We know how large the section will be so we can create it now.  */
+  Elf_Data *d = elf_newdata (scn);
+  if (d == NULL)
+    error (EXIT_FAILURE, 0, gettext ("cannot create build ID section: %s"),
+          elf_errmsg (-1));
+
+  d->d_type = ELF_T_BYTE;
+  d->d_version = EV_CURRENT;
+
+  /* The note section header.  */
+  assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
+  d->d_size = sizeof (GElf_Nhdr);
+  /* The string is four bytes long.  */
+  d->d_size += sizeof (ELF_NOTE_GNU);
+  assert (d->d_size % 4 == 0);
+
+  if (strcmp (ld_state.build_id, "md5") == 0
+      || strcmp (ld_state.build_id, "uuid") == 0)
+    d->d_size += 16;
+  else
+    {
+      assert (ld_state.build_id[0] == '0' && ld_state.build_id[1] == 'x');
+      /* Use an upper limit of the possible number of bytes generated
+        from the string.  */
+      d->d_size += strlen (ld_state.build_id) / 2;
+    }
+
+  d->d_buf = xcalloc (d->d_size, 1);
+  d->d_off = 0;
+  d->d_align = 0;
+}
+
+
+/* Iterate over the sections */
+static void
+compute_build_id (void)
+{
+  Elf_Data *d = elf_getdata (elf_getscn (ld_state.outelf,
+                                        ld_state.buildidscnidx), NULL);
+  assert (d != NULL);
+
+  GElf_Nhdr *hdr = d->d_buf;
+  hdr->n_namesz = sizeof (ELF_NOTE_GNU);
+  hdr->n_type = NT_GNU_BUILD_ID;
+  char *dp = mempcpy (hdr + 1, ELF_NOTE_GNU, sizeof (ELF_NOTE_GNU));
+
+  if (strcmp (ld_state.build_id, "md5") == 0)
+    {
+      /* Compute the MD5 sum of various parts of the generated file.
+        We compute the hash sum over the external representation.  */
+      struct md5_ctx ctx;
+      md5_init_ctx (&ctx);
+
+      /* The call cannot fail.  */
+      size_t shstrndx;
+      (void) elf_getshstrndx (ld_state.outelf, &shstrndx);
+
+      const char *ident = elf_getident (ld_state.outelf, NULL);
+      bool same_byte_order = ((ident[EI_DATA] == ELFDATA2LSB
+                              && __BYTE_ORDER == __LITTLE_ENDIAN)
+                             || (ident[EI_DATA] == ELFDATA2MSB
+                                 && __BYTE_ORDER == __BIG_ENDIAN));
+
+      /* Iterate over all sections to find those which are not strippable.  */
+      Elf_Scn *scn = NULL;
+      while ((scn = elf_nextscn (ld_state.outelf, scn)) != NULL)
+       {
+         /* Get the section header.  */
+         GElf_Shdr shdr_mem;
+         GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+         assert (shdr != NULL);
+
+         if (SECTION_STRIP_P (shdr,
+                              elf_strptr (ld_state.outelf, shstrndx,
+                                          shdr->sh_name), true))
+           /* The section can be stripped.  Don't use it.  */
+           continue;
+
+         /* Do not look at NOBITS sections.  */
+         if (shdr->sh_type == SHT_NOBITS)
+           continue;
+
+         /* Iterate through the list of data blocks.  */
+         Elf_Data *data = NULL;
+         while ((data = INTUSE(elf_getdata) (scn, data)) != NULL)
+           /* If the file byte order is the same as the host byte order
+              process the buffer directly.  If the data is just a stream
+              of bytes which the library will not convert we can use it
+              as well.  */
+           if (likely (same_byte_order) || data->d_type == ELF_T_BYTE)
+             md5_process_bytes (data->d_buf, data->d_size, &ctx);
+           else
+             {
+               /* Convert the data to file byte order.  */
+               if (gelf_xlatetof (ld_state.outelf, data, data, ident[EI_DATA])
+                   == NULL)
+                 error (EXIT_FAILURE, 0, gettext ("\
+cannot convert section data to file format: %s"),
+                        elf_errmsg (-1));
+
+               md5_process_bytes (data->d_buf, data->d_size, &ctx);
+
+               /* And convert it back.  */
+               if (gelf_xlatetom (ld_state.outelf, data, data, ident[EI_DATA])
+                   == NULL)
+                 error (EXIT_FAILURE, 0, gettext ("\
+cannot convert section data to memory format: %s"),
+                        elf_errmsg (-1));
+             }
+
+         /* We are done computing the checksum.  */
+         (void) md5_finish_ctx (&ctx, dp);
+
+         hdr->n_descsz = 16;
+       }
+    }
+  else if (strcmp (ld_state.build_id, "uuid") == 0)
+    {
+      int fd = open ("/dev/urandom", O_RDONLY);
+      if (fd == -1)
+       error (EXIT_FAILURE, errno, gettext ("cannot open '%s'"),
+              "/dev/urandom");
+
+      if (TEMP_FAILURE_RETRY (read (fd, dp, 16)) != 16)
+       error (EXIT_FAILURE, 0, gettext ("cannot read enough data for UUID"));
+
+      close (fd);
+
+      hdr->n_descsz = 16;
+    }
+  else
+    {
+      const char *cp = ld_state.build_id + 2;
+
+      /* The form of the string has been verified before so here we can
+        simplify the scanning.  */
+      do
+       {
+         if (isxdigit (cp[0]))
+           {
+             char ch1 = tolower (cp[0]);
+             char ch2 = tolower (cp[1]);
+
+             *dp++ = (((isdigit (ch1) ? ch1 - '0' : ch1 - 'a' + 10) << 4)
+                      | (isdigit (ch2) ? ch2 - '0' : ch2 - 'a' + 10));
+           }
+         else
+           ++cp;
+       }
+      while (*cp != '\0');
+    }
+}
+
+
 /* Create the output file.
 
    For relocatable files what basically has to happen is that all
@@ -4485,6 +4650,16 @@ ld_generic_create_outfile (struct ld_state *statep)
          continue;
        }
 
+      if (unlikely (head->kind == scn_dot_note_gnu_build_id))
+       {
+         /* Remember the index of this section.  */
+         ld_state.buildidscnidx = elf_ndxscn (scn);
+
+         create_build_id_section (scn);
+
+         continue;
+       }
+
       /* If we come here we must be handling a normal section.  */
       assert (head->kind == scn_normal);
 
@@ -6695,6 +6870,12 @@ internal error: nobits section follows nobits section"));
   /* Finalize the .plt section and what else belongs to it.  */
   FINALIZE_PLT (statep, nsym, nsym_local, ndxtosym);
 
+
+  /* Finally, if we have to compute the build ID.  */
+  if (ld_state.build_id != NULL)
+    compute_build_id ();
+
+
   /* We don't need the map from the symbol table index to the symbol
      structure anymore.  */
   free (ndxtosym);
index 17d5be789cdfced75e944546c96e91106a2d55cf..eb15c7bee82a658aec0832e44190d0afb62b1829 100644 (file)
@@ -1,5 +1,5 @@
 %{
-/* Copyright (C) 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
+/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2008 Red Hat, Inc.
    This file is part of Red Hat elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2001.
 
@@ -79,7 +79,7 @@ static int handle_ifdef (void);
 static void invalid_char (int ch);
 %}
 
-ID             [a-zA-Z0-9_.*?]+
+ID             [a-zA-Z0-9_.*?][a-zA-Z0-9_.*?-]*
 FILENAMECHAR1  [a-zA-Z0-9_/.\\~]
 FILENAMECHAR   [^][{}[:space:]():;]+
 HEX            0[xX][0-9a-fA-F]+[kKmM]?