From 89759c76ee1e3231ee89d4aafed3a88772ce2245 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 31 Jan 2008 19:44:12 +0000 Subject: Implement --build-id command line parameter for ld. --- lib/ChangeLog | 5 ++ lib/Makefile.am | 6 +- libelf/ChangeLog | 5 ++ libelf/elf_strptr.c | 24 ++++++- src/ChangeLog | 9 +++ src/elf32-i386.script | 2 + src/ld.c | 40 ++++++++++- src/ld.h | 7 +- src/ldgeneric.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++- src/ldlex.l | 4 +- 10 files changed, 274 insertions(+), 11 deletions(-) diff --git a/lib/ChangeLog b/lib/ChangeLog index cc2850c3..1697d00e 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,8 @@ +2008-01-31 Ulrich Drepper + + * Makefile.am (libeu_a_SOURCES): Add md5.c. + (noinst_HEADERS): Add md5.h. + 2006-04-04 Ulrich Drepper * Makefile.am (libeu_a_SOURCES): We don't need xstrdup in the moment. diff --git a/lib/Makefile.am b/lib/Makefile.am index 4218e963..7ee03463 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 diff --git a/libelf/ChangeLog b/libelf/ChangeLog index 0688cbdb..17633ba7 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,8 @@ +2008-01-31 Ulrich Drepper + + * 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 * elf_getaroff.c: Calculate from start_offset, instead of using diff --git a/libelf/elf_strptr.c b/libelf/elf_strptr.c index 28042211..c1ea60de 100644 --- a/libelf/elf_strptr.c +++ b/libelf/elf_strptr.c @@ -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 , 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); diff --git a/src/ChangeLog b/src/ChangeLog index bbc2708c..6f5f4337 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2008-01-31 Ulrich Drepper + + * 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 * ld.c (replace_args): New function. diff --git a/src/elf32-i386.script b/src/elf32-i386.script index e3f6906c..1c46b690 100644 --- a/src/elf32-i386.script +++ b/src/elf32-i386.script @@ -18,6 +18,8 @@ SEGMENT [RX] #endif .interp; + .note.ABI-tag; + .note.gnu.build-id; .hash; .gnu.hash; .dynsym; diff --git a/src/ld.c b/src/ld.c index 6f8048f5..3b422bc6 100644 --- 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) { diff --git a/src/ld.h b/src/ld.h index d4ad8f9d..860bcdb4 100644 --- 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; diff --git a/src/ldgeneric.c b/src/ldgeneric.c index a1076a0d..6674887b 100644 --- a/src/ldgeneric.c +++ b/src/ldgeneric.c @@ -28,6 +28,7 @@ #endif #include +#include #include #include #include @@ -44,9 +45,11 @@ #include #include -#include +#include #include "ld.h" #include "list.h" +#include +#include /* 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); diff --git a/src/ldlex.l b/src/ldlex.l index 17d5be78..eb15c7be 100644 --- a/src/ldlex.l +++ b/src/ldlex.l @@ -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 , 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]? -- cgit v1.2.1