From: Jon TURNEY Date: Tue, 8 Apr 2014 09:59:43 +0000 (+0100) Subject: Add support for generating and inserting build IDs into COFF binaries. X-Git-Url: http://git.efficios.com/?a=commitdiff_plain;h=61e2488cd8497d158303a78563ad40f51f5c3f8e;hp=ae1d276159c3cfb29caacdf567aea01f433f78b0;p=deliverable%2Fbinutils-gdb.git Add support for generating and inserting build IDs into COFF binaries. * peXXigen.c (pe_print_debugdata): New function: Displays the contents of the debug directory and decodes codeview entries. (_bfd_XXi_swap_debugdir_in, _bfd_XXi_swap_debugdir_out) (_bfd_XXi_slurp_codeview_record, _bfd_XXi_write_codeview_record): Add functions for reading and writing debugdir and codeview records. * libpei.h (_bfd_XXi_swap_debugdir_in, _bfd_XXi_swap_debugdir_out) (_bfd_XXi_write_codeview_record): Add prototypes and macros. * libcoff-in.h (pe_tdata): Add build-id data. * libcoff.h: Regenerate. * coffcode.h (coff_write_object_contents): Run build_id after_write_object_contents hook. * pe.h (external_IMAGE_DEBUG_DIRECTORY, _CV_INFO_PDB70) (_CV_INFO_PDB20): Add structures and constants for debug directory and codeview records. * internal.h (internal_IMAGE_DEBUG_DIRECTORY, CODEVIEW_INFO): Add structures and constants for internal representation of debug directory and codeview records. * emultempl/elf32.em (id_note_section_size, read_hex, write_build_id): Move code for parsing build-id option and calculating the build-id to... * ldbuildid.c: New file. * ldbuildid.h: New file. * Makefile.am (CFILES, HFILES, OFILES, ld_new_SOURCES): Add new files. * Makefile.in: Regenerate. * ld.texinfo: Update --build-id description to mention COFF support. * NEWS: Mention support for COFF build ids. * emultempl/pe.em (gld${EMULATION_NAME}_handle_option): (pecoff_checksum_contents, write_build_id, setup_build_id) (gld_${EMULATION_NAME}_after_open): Handle and implement build-id option. * emultempl/pep.em: Likewise. --- diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 6ad175fd04..70d23c50b1 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,18 @@ +2014-04-08 Jon TURNEY + + * peXXigen.c (pe_print_debugdata): New function: Displays the + contents of the debug directory and decodes codeview entries. + (_bfd_XXi_swap_debugdir_in, _bfd_XXi_swap_debugdir_out) + (_bfd_XXi_slurp_codeview_record, _bfd_XXi_write_codeview_record): + Add functions for reading and writing debugdir and codeview + records. + * libpei.h (_bfd_XXi_swap_debugdir_in, _bfd_XXi_swap_debugdir_out) + (_bfd_XXi_write_codeview_record): Add prototypes and macros. + * libcoff-in.h (pe_tdata): Add build-id data. + * libcoff.h: Regenerate. + * coffcode.h (coff_write_object_contents): Run build_id + after_write_object_contents hook. + 2014-04-05 Alan Modra * elflink.c (_bfd_elf_add_default_symbol): Pass poldbfd when diff --git a/bfd/coffcode.h b/bfd/coffcode.h index d6fe39f157..4994fb3afe 100644 --- a/bfd/coffcode.h +++ b/bfd/coffcode.h @@ -4329,7 +4329,18 @@ coff_write_object_contents (bfd * abfd) } #endif - /* Now write them. */ +#ifdef COFF_WITH_PE + { + /* After object contents are finalized so we can compute a reasonable hash, + but before header is written so we can update it to point to debug directory. */ + struct pe_tdata *pe = pe_data (abfd); + + if (pe->build_id.after_write_object_contents != NULL) + (*pe->build_id.after_write_object_contents) (abfd); + } +#endif + + /* Now write header. */ if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0) return FALSE; diff --git a/bfd/libcoff-in.h b/bfd/libcoff-in.h index a556edcfce..cc1e52b04f 100644 --- a/bfd/libcoff-in.h +++ b/bfd/libcoff-in.h @@ -119,6 +119,14 @@ typedef struct pe_tdata bfd_boolean insert_timestamp; bfd_boolean (*in_reloc_p) (bfd *, reloc_howto_type *); flagword real_flags; + + /* Build-id info. */ + struct + { + bfd_boolean (*after_write_object_contents) (bfd *); + const char *style; + asection *sec; + } build_id; } pe_data_type; #define pe_data(bfd) ((bfd)->tdata.pe_obj_data) diff --git a/bfd/libcoff.h b/bfd/libcoff.h index 36c9829bde..6cb387ceb8 100644 --- a/bfd/libcoff.h +++ b/bfd/libcoff.h @@ -123,6 +123,14 @@ typedef struct pe_tdata bfd_boolean insert_timestamp; bfd_boolean (*in_reloc_p) (bfd *, reloc_howto_type *); flagword real_flags; + + /* build-id info. */ + struct + { + bfd_boolean (*after_write_object_contents) (bfd *); + const char *style; + asection *sec; + } build_id; } pe_data_type; #define pe_data(bfd) ((bfd)->tdata.pe_obj_data) diff --git a/bfd/libpei.h b/bfd/libpei.h index 0fdafb0a13..ffcafded78 100644 --- a/bfd/libpei.h +++ b/bfd/libpei.h @@ -235,6 +235,9 @@ #define _bfd_XXi_swap_scnhdr_out _bfd_pex64i_swap_scnhdr_out #define _bfd_XXi_swap_sym_in _bfd_pex64i_swap_sym_in #define _bfd_XXi_swap_sym_out _bfd_pex64i_swap_sym_out +#define _bfd_XXi_swap_debugdir_in _bfd_pex64i_swap_debugdir_in +#define _bfd_XXi_swap_debugdir_out _bfd_pex64i_swap_debugdir_out +#define _bfd_XXi_write_codeview_record _bfd_pex64i_write_codeview_record #elif defined COFF_WITH_pep @@ -266,6 +269,9 @@ #define _bfd_XXi_swap_scnhdr_out _bfd_pepi_swap_scnhdr_out #define _bfd_XXi_swap_sym_in _bfd_pepi_swap_sym_in #define _bfd_XXi_swap_sym_out _bfd_pepi_swap_sym_out +#define _bfd_XXi_swap_debugdir_in _bfd_pepi_swap_debugdir_in +#define _bfd_XXi_swap_debugdir_out _bfd_pepi_swap_debugdir_out +#define _bfd_XXi_write_codeview_record _bfd_pepi_write_codeview_record #else /* !COFF_WITH_pep */ @@ -297,6 +303,9 @@ #define _bfd_XXi_swap_scnhdr_out _bfd_pei_swap_scnhdr_out #define _bfd_XXi_swap_sym_in _bfd_pei_swap_sym_in #define _bfd_XXi_swap_sym_out _bfd_pei_swap_sym_out +#define _bfd_XXi_swap_debugdir_in _bfd_pei_swap_debugdir_in +#define _bfd_XXi_swap_debugdir_out _bfd_pei_swap_debugdir_out +#define _bfd_XXi_write_codeview_record _bfd_pei_write_codeview_record #endif /* !COFF_WITH_pep */ @@ -339,6 +348,9 @@ bfd_boolean _bfd_XX_print_private_bfd_data_common (bfd *, void *); bfd_boolean _bfd_XX_bfd_copy_private_bfd_data_common (bfd *, bfd *); void _bfd_XX_get_symbol_info (bfd *, asymbol *, symbol_info *); bfd_boolean _bfd_XXi_final_link_postscript (bfd *, struct coff_final_link_info *); +void _bfd_XXi_swap_debugdir_in (bfd *, void *, void *); +unsigned _bfd_XXi_swap_debugdir_out (bfd *, void *, void *); +unsigned _bfd_XXi_write_codeview_record (bfd *, file_ptr, CODEVIEW_INFO *); /* The following are needed only for ONE of pe or pei, but don't otherwise vary; peicode.h fixes up ifdefs but we provide the diff --git a/bfd/peXXigen.c b/bfd/peXXigen.c index e78edaacd1..ea7846f496 100644 --- a/bfd/peXXigen.c +++ b/bfd/peXXigen.c @@ -802,7 +802,7 @@ _bfd_XXi_only_swap_filehdr_out (bfd * abfd, void * in, void * out) /* Only use a real timestamp if the option was chosen. */ if ((pe_data (abfd)->insert_timestamp)) - H_PUT_32 (abfd, time(0), filehdr_out->f_timdat); + H_PUT_32 (abfd, time (0), filehdr_out->f_timdat); PUT_FILEHDR_SYMPTR (abfd, filehdr_in->f_symptr, filehdr_out->f_symptr); @@ -1031,6 +1031,106 @@ _bfd_XXi_swap_scnhdr_out (bfd * abfd, void * in, void * out) return ret; } +void +_bfd_XXi_swap_debugdir_in (bfd * abfd, void * ext1, void * in1) +{ + struct external_IMAGE_DEBUG_DIRECTORY *ext = (struct external_IMAGE_DEBUG_DIRECTORY *) ext1; + struct internal_IMAGE_DEBUG_DIRECTORY *in = (struct internal_IMAGE_DEBUG_DIRECTORY *) in1; + + in->Characteristics = H_GET_32(abfd, ext->Characteristics); + in->TimeDateStamp = H_GET_32(abfd, ext->TimeDateStamp); + in->MajorVersion = H_GET_16(abfd, ext->MajorVersion); + in->MinorVersion = H_GET_16(abfd, ext->MinorVersion); + in->Type = H_GET_32(abfd, ext->Type); + in->SizeOfData = H_GET_32(abfd, ext->SizeOfData); + in->AddressOfRawData = H_GET_32(abfd, ext->AddressOfRawData); + in->PointerToRawData = H_GET_32(abfd, ext->PointerToRawData); +} + +unsigned int +_bfd_XXi_swap_debugdir_out (bfd * abfd, void * inp, void * extp) +{ + struct external_IMAGE_DEBUG_DIRECTORY *ext = (struct external_IMAGE_DEBUG_DIRECTORY *) extp; + struct internal_IMAGE_DEBUG_DIRECTORY *in = (struct internal_IMAGE_DEBUG_DIRECTORY *) inp; + + H_PUT_32(abfd, in->Characteristics, ext->Characteristics); + H_PUT_32(abfd, in->TimeDateStamp, ext->TimeDateStamp); + H_PUT_16(abfd, in->MajorVersion, ext->MajorVersion); + H_PUT_16(abfd, in->MinorVersion, ext->MinorVersion); + H_PUT_32(abfd, in->Type, ext->Type); + H_PUT_32(abfd, in->SizeOfData, ext->SizeOfData); + H_PUT_32(abfd, in->AddressOfRawData, ext->AddressOfRawData); + H_PUT_32(abfd, in->PointerToRawData, ext->PointerToRawData); + + return sizeof (struct external_IMAGE_DEBUG_DIRECTORY); +} + +static CODEVIEW_INFO * +_bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo) +{ + char buffer[256+1]; + + if (bfd_seek (abfd, where, SEEK_SET) != 0) + return NULL; + + if (bfd_bread (buffer, 256, abfd) < 4) + return NULL; + + /* ensure null termination of filename */ + buffer[256] = '\0'; + + cvinfo->CVSignature = H_GET_32(abfd, buffer); + cvinfo->Age = 0; + + if ((cvinfo->CVSignature == CVINFO_PDB70_CVSIGNATURE) + && (length > sizeof (CV_INFO_PDB70))) + { + CV_INFO_PDB70 *cvinfo70 = (CV_INFO_PDB70 *)(buffer); + + cvinfo->Age = H_GET_32(abfd, cvinfo70->Age); + memcpy (cvinfo->Signature, cvinfo70->Signature, CV_INFO_SIGNATURE_LENGTH); + cvinfo->SignatureLength = CV_INFO_SIGNATURE_LENGTH; + // cvinfo->PdbFileName = cvinfo70->PdbFileName; + + return cvinfo; + } + else if ((cvinfo->CVSignature == CVINFO_PDB20_CVSIGNATURE) + && (length > sizeof (CV_INFO_PDB20))) + { + CV_INFO_PDB20 *cvinfo20 = (CV_INFO_PDB20 *)(buffer); + cvinfo->Age = H_GET_32(abfd, cvinfo20->Age); + memcpy (cvinfo->Signature, cvinfo20->Signature, 4); + cvinfo->SignatureLength = 4; + // cvinfo->PdbFileName = cvinfo20->PdbFileName; + + return cvinfo; + } + + return NULL; +} + +unsigned int +_bfd_XXi_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinfo) +{ + unsigned int size = sizeof (CV_INFO_PDB70) + 1; + CV_INFO_PDB70 *cvinfo70; + char buffer[size]; + + if (bfd_seek (abfd, where, SEEK_SET) != 0) + return 0; + + cvinfo70 = (CV_INFO_PDB70 *) buffer; + H_PUT_32 (abfd, CVINFO_PDB70_CVSIGNATURE, cvinfo70->CvSignature); + memcpy (&(cvinfo70->Signature), cvinfo->Signature, CV_INFO_SIGNATURE_LENGTH); + H_PUT_32 (abfd, cvinfo->Age, cvinfo70->Age); + cvinfo70->PdbFileName[0] = '\0'; + + if (bfd_bwrite (buffer, size, abfd) != size) + return 0; + + return size; +} + static char * dir_names[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] = { N_("Export Directory [.edata (or where ever we found it)]"), @@ -2240,6 +2340,117 @@ rsrc_print_section (bfd * abfd, void * vfile) return TRUE; } +#define IMAGE_NUMBEROF_DEBUG_TYPES 12 + +static char * debug_type_names[IMAGE_NUMBEROF_DEBUG_TYPES] = +{ + "Unknown", + "COFF", + "CodeView", + "FPO", + "Misc", + "Exception", + "Fixup", + "OMAP-to-SRC", + "OMAP-from-SRC", + "Borland", + "Reserved", + "CLSID", +}; + +static bfd_boolean +pe_print_debugdata (bfd * abfd, void * vfile) +{ + FILE *file = (FILE *) vfile; + pe_data_type *pe = pe_data (abfd); + struct internal_extra_pe_aouthdr *extra = &pe->pe_opthdr; + asection *section; + bfd_byte *data = 0; + bfd_size_type dataoff; + unsigned int i; + + bfd_vma addr = extra->DataDirectory[PE_DEBUG_DATA].VirtualAddress; + bfd_size_type size = extra->DataDirectory[PE_DEBUG_DATA].Size; + + if (size == 0) + return TRUE; + + addr += extra->ImageBase; + for (section = abfd->sections; section != NULL; section = section->next) + { + if ((addr >= section->vma) && (addr < (section->vma + section->size))) + break; + } + + if (section == NULL) + { + fprintf (file, + _("\nThere is a debug directory, but the section containing it could not be found\n")); + return TRUE; + } + + fprintf (file, _("\nThere is a debug directory in %s at 0x%lx\n\n"), + section->name, (unsigned long) addr); + + dataoff = addr - section->vma; + + fprintf (file, + _("Type Size Rva Offset\n")); + + /* Read the whole section. */ + if (!bfd_malloc_and_get_section (abfd, section, &data)) + { + if (data != NULL) + free (data); + return FALSE; + } + + for (i = 0; i < size / sizeof (struct external_IMAGE_DEBUG_DIRECTORY); i++) + { + const char *type_name; + struct external_IMAGE_DEBUG_DIRECTORY *ext + = &((struct external_IMAGE_DEBUG_DIRECTORY *)(data + dataoff))[i]; + struct internal_IMAGE_DEBUG_DIRECTORY idd; + + _bfd_XXi_swap_debugdir_in (abfd, ext, &idd); + + if ((idd.Type) > IMAGE_NUMBEROF_DEBUG_TYPES) + type_name = debug_type_names[0]; + else + type_name = debug_type_names[idd.Type]; + + fprintf (file, " %2ld %14s %08lx %08lx %08lx\n", + idd.Type, type_name, idd.SizeOfData, + idd.AddressOfRawData, idd.PointerToRawData); + + if (idd.Type == PE_IMAGE_DEBUG_TYPE_CODEVIEW) + { + char signature[CV_INFO_SIGNATURE_LENGTH * 2 + 1]; + char buffer[256 + 1]; + CODEVIEW_INFO *cvinfo = (CODEVIEW_INFO *) buffer; + + /* The debug entry doesn't have to have to be in a section, + in which case AddressOfRawData is 0, so always use PointerToRawData. */ + if (!_bfd_XXi_slurp_codeview_record (abfd, (file_ptr) idd.PointerToRawData, + idd.SizeOfData, cvinfo)) + continue; + + for (i = 0; i < cvinfo->SignatureLength; i++) + sprintf (&signature[i*2], "%02x", cvinfo->Signature[i] & 0xff); + + fprintf (file, "(format %c%c%c%c signature %s age %ld)\n", + buffer[0], buffer[1], buffer[2], buffer[3], + signature, cvinfo->Age); + } + } + + if (size % sizeof (struct external_IMAGE_DEBUG_DIRECTORY) != 0) + fprintf (file, + _("The debug directory size is not a multiple of the debug directory entry size\n")); + + return TRUE; +} + /* Print out the program headers. */ bfd_boolean @@ -2413,6 +2624,7 @@ _bfd_XX_print_private_bfd_data_common (bfd * abfd, void * vfile) else pe_print_pdata (abfd, vfile); pe_print_reloc (abfd, vfile); + pe_print_debugdata (abfd, file); rsrc_print_section (abfd, vfile); @@ -3576,7 +3788,7 @@ rsrc_process_section (bfd * abfd, if (num_input_rsrc < 2) goto end; - + /* Step one: Walk the section, computing the size of the tables, leaves and data and decide if we need to do anything. */ dataend = data + size; @@ -3841,7 +4053,7 @@ _bfd_XXi_final_link_postscript (bfd * abfd, struct coff_final_link_info *pfinfo) } h1 = coff_link_hash_lookup (coff_hash_table (info), - (bfd_get_symbol_leading_char(abfd) != 0 + (bfd_get_symbol_leading_char (abfd) != 0 ? "__tls_used" : "_tls_used"), FALSE, FALSE, TRUE); if (h1 != NULL) diff --git a/include/coff/ChangeLog b/include/coff/ChangeLog index 2ee6361f82..769e9ead7e 100644 --- a/include/coff/ChangeLog +++ b/include/coff/ChangeLog @@ -1,3 +1,12 @@ +2014-04-08 Jon TURNEY + + * pe.h (external_IMAGE_DEBUG_DIRECTORY, _CV_INFO_PDB70) + (_CV_INFO_PDB20): Add structures and constants for debug directory + and codeview records. + * internal.h (internal_IMAGE_DEBUG_DIRECTORY, CODEVIEW_INFO): + Add structures and constants for internal representation of debug + directory and codeview records. + 2014-03-13 Tristan Gingold * pe.h (struct external_ANON_OBJECT_HEADER_BIGOBJ): Declare. diff --git a/include/coff/internal.h b/include/coff/internal.h index b34e5c6c34..47e85d9fec 100644 --- a/include/coff/internal.h +++ b/include/coff/internal.h @@ -1,18 +1,18 @@ /* Internal format of COFF object file data structures, for GNU BFD. This file is part of BFD, the Binary File Descriptor library. - + Copyright (C) 1999-2014 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, @@ -55,7 +55,7 @@ struct internal_extra_pe_filehdr unsigned short e_res2[10]; /* Reserved words, all 0x0 */ bfd_vma e_lfanew; /* File address of new exe header, 0x80 */ unsigned long dos_message[16]; /* text which always follows dos header */ - bfd_vma nt_signature; /* required NT signature, 0x4550 */ + bfd_vma nt_signature; /* required NT signature, 0x4550 */ }; #define GO32_STUBSIZE 2048 @@ -66,7 +66,7 @@ struct internal_filehdr /* coff-stgo32 EXE stub header before BFD tdata has been allocated. Its data is kept in INTERNAL_FILEHDR.GO32STUB afterwards. - + F_GO32STUB is set iff go32stub contains a valid data. Artifical headers created in BFD have no pre-set go32stub. */ char go32stub[GO32_STUBSIZE]; @@ -109,7 +109,7 @@ struct internal_filehdr #define F_GO32STUB (0x4000) /* Extra structure which is used in the optional header. */ -typedef struct _IMAGE_DATA_DIRECTORY +typedef struct _IMAGE_DATA_DIRECTORY { bfd_vma VirtualAddress; long Size; @@ -132,6 +132,44 @@ typedef struct _IMAGE_DATA_DIRECTORY /* DataDirectory[15] is currently reserved, so no define. */ #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 +/* Extra structure used in debug directory. */ +struct internal_IMAGE_DEBUG_DIRECTORY +{ + unsigned long Characteristics; + unsigned long TimeDateStamp; + unsigned short MajorVersion; + unsigned short MinorVersion; + unsigned long Type; + unsigned long SizeOfData; + unsigned long AddressOfRawData; + unsigned long PointerToRawData; +}; + +#define PE_IMAGE_DEBUG_TYPE_UNKNOWN 0 +#define PE_IMAGE_DEBUG_TYPE_COFF 1 +#define PE_IMAGE_DEBUG_TYPE_CODEVIEW 2 +#define PE_IMAGE_DEBUG_TYPE_FPO 3 +#define PE_IMAGE_DEBUG_TYPE_MISC 4 +#define PE_IMAGE_DEBUG_TYPE_EXCEPTION 5 +#define PE_IMAGE_DEBUG_TYPE_FIXUP 6 +#define PE_IMAGE_DEBUG_TYPE_OMAP_TO_SRC 7 +#define PE_IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8 +#define PE_IMAGE_DEBUG_TYPE_BORLAND 9 +#define PE_IMAGE_DEBUG_TYPE_RESERVED10 10 +#define PE_IMAGE_DEBUG_TYPE_CLSID 11 + +/* Extra structure for a codeview debug record */ +#define CV_INFO_SIGNATURE_LENGTH 16 + +typedef struct _CODEVIEW_INFO +{ + unsigned long CVSignature; + char Signature[CV_INFO_SIGNATURE_LENGTH]; + unsigned int SignatureLength; + unsigned long Age; + // char PdbFileName[]; +} CODEVIEW_INFO; + /* Default image base for NT. */ #define NT_EXE_IMAGE_BASE 0x400000 #define NT_DLL_IMAGE_BASE 0x10000000 @@ -147,22 +185,22 @@ typedef struct _IMAGE_DATA_DIRECTORY # define PE_DEF_FILE_ALIGNMENT 0x200 #endif -struct internal_extra_pe_aouthdr +struct internal_extra_pe_aouthdr { /* FIXME: The following entries are in AOUTHDR. But they aren't available internally in bfd. We add them here so that objdump can dump them. */ - /* The state of the image file */ + /* The state of the image file. */ short Magic; - /* Linker major version number */ + /* Linker major version number. */ char MajorLinkerVersion; - /* Linker minor version number */ + /* Linker minor version number. */ char MinorLinkerVersion; - /* Total size of all code sections */ + /* Total size of all code sections. */ long SizeOfCode; - /* Total size of all initialized data sections */ + /* Total size of all initialized data sections. */ long SizeOfInitializedData; - /* Total size of all uninitialized data sections */ + /* Total size of all uninitialized data sections. */ long SizeOfUninitializedData; /* Address of entry point relative to image base. */ bfd_vma AddressOfEntryPoint; @@ -170,40 +208,40 @@ struct internal_extra_pe_aouthdr bfd_vma BaseOfCode; /* Address of the first data section relative to image base. */ bfd_vma BaseOfData; - + /* PE stuff */ - bfd_vma ImageBase; /* address of specific location in memory that - file is located, NT default 0x10000 */ - - bfd_vma SectionAlignment; /* section alignment default 0x1000 */ - bfd_vma FileAlignment; /* file alignment default 0x200 */ - short MajorOperatingSystemVersion; /* minimum version of the operating */ - short MinorOperatingSystemVersion; /* system req'd for exe, default to 1*/ - short MajorImageVersion; /* user defineable field to store version of */ - short MinorImageVersion; /* exe or dll being created, default to 0 */ - short MajorSubsystemVersion; /* minimum subsystem version required to */ - short MinorSubsystemVersion; /* run exe; default to 3.1 */ - long Reserved1; /* seems to be 0 */ - long SizeOfImage; /* size of memory to allocate for prog */ - long SizeOfHeaders; /* size of PE header and section table */ - long CheckSum; /* set to 0 */ + bfd_vma ImageBase; /* Address of specific location in memory that + file is located, NT default 0x10000. */ + + bfd_vma SectionAlignment; /* Section alignment default 0x1000. */ + bfd_vma FileAlignment; /* File alignment default 0x200. */ + short MajorOperatingSystemVersion; /* Minimum version of the operating. */ + short MinorOperatingSystemVersion; /* System req'd for exe, default to 1. */ + short MajorImageVersion; /* User defineable field to store version of */ + short MinorImageVersion; /* exe or dll being created, default to 0. */ + short MajorSubsystemVersion; /* Minimum subsystem version required to */ + short MinorSubsystemVersion; /* run exe; default to 3.1. */ + long Reserved1; /* Seems to be 0. */ + long SizeOfImage; /* Size of memory to allocate for prog. */ + long SizeOfHeaders; /* Size of PE header and section table. */ + long CheckSum; /* Set to 0. */ short Subsystem; - /* type of subsystem exe uses for user interface, + /* Type of subsystem exe uses for user interface, possible values: 1 - NATIVE Doesn't require a subsystem 2 - WINDOWS_GUI runs in Windows GUI subsystem 3 - WINDOWS_CUI runs in Windows char sub. (console app) 5 - OS2_CUI runs in OS/2 character subsystem - 7 - POSIX_CUI runs in Posix character subsystem */ - unsigned short DllCharacteristics; /* flags for DLL init */ - bfd_vma SizeOfStackReserve; /* amount of memory to reserve */ - bfd_vma SizeOfStackCommit; /* amount of memory initially committed for - initial thread's stack, default is 0x1000 */ - bfd_vma SizeOfHeapReserve; /* amount of virtual memory to reserve and */ - bfd_vma SizeOfHeapCommit; /* commit, don't know what to defaut it to */ - long LoaderFlags; /* can probably set to 0 */ - long NumberOfRvaAndSizes; /* number of entries in next entry, 16 */ + 7 - POSIX_CUI runs in Posix character subsystem. */ + unsigned short DllCharacteristics; /* flags for DLL init. */ + bfd_vma SizeOfStackReserve; /* Amount of memory to reserve. */ + bfd_vma SizeOfStackCommit; /* Amount of memory initially committed for + initial thread's stack, default is 0x1000. */ + bfd_vma SizeOfHeapReserve; /* Amount of virtual memory to reserve and */ + bfd_vma SizeOfHeapCommit; /* commit, don't know what to defaut it to. */ + long LoaderFlags; /* Can probably set to 0. */ + long NumberOfRvaAndSizes; /* Number of entries in next entry, 16. */ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; }; diff --git a/include/coff/pe.h b/include/coff/pe.h index 3166531097..0ed9dde3b2 100644 --- a/include/coff/pe.h +++ b/include/coff/pe.h @@ -1,4 +1,4 @@ -/* pe.h - PE COFF header information +/* pe.h - PE COFF header information Copyright (C) 1999-2014 Free Software Foundation, Inc. @@ -163,9 +163,9 @@ #define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 #define IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER 13 #define IMAGE_SUBSYSTEM_XBOX 14 - + /* Magic values that are true for all dos/nt implementations. */ -#define DOSMAGIC 0x5a4d +#define DOSMAGIC 0x5a4d #define NT_SIGNATURE 0x00004550 /* NT allows long filenames, we want to accommodate this. @@ -264,7 +264,7 @@ struct external_PEI_filehdr /* 32-bit PE a.out header: */ -typedef struct +typedef struct { AOUTHDR standard; @@ -300,7 +300,7 @@ typedef struct /* Like PEAOUTHDR, except that the "standard" member has no BaseOfData (aka data_start) member and that some of the members are 8 instead of just 4 bytes long. */ -typedef struct +typedef struct { #ifdef AOUTHDRSZ64 AOUTHDR64 standard; @@ -338,7 +338,7 @@ typedef struct #else #define PEPAOUTSZ 240 #endif - + #undef E_FILNMLEN #define E_FILNMLEN 18 /* # characters in a file name. */ @@ -584,4 +584,41 @@ struct external_pex64_scope_entry (PEX64_OFFSET_TO_SCOPE_COUNT(COUNTOFUNWINDCODES) + \ PEX64_SCOPE_ENTRY_SIZE * (IDX)) +/* Extra structure used in debug directory */ +struct external_IMAGE_DEBUG_DIRECTORY { + char Characteristics[4]; + char TimeDateStamp[4]; + char MajorVersion[2]; + char MinorVersion[2]; + char Type[4]; + char SizeOfData[4]; + char AddressOfRawData[4]; + char PointerToRawData[4]; +}; + +/* Extra structures used in codeview debug record */ +/* This is not part of the PE specification */ + +#define CVINFO_PDB70_CVSIGNATURE 0x53445352 // "RSDS" +#define CVINFO_PDB20_CVSIGNATURE 0x3031424e // "NB10" +#define CVINFO_CV50_CVSIGNATURE 0x3131424e // "NB11" +#define CVINFO_CV41_CVSIGNATURE 0x3930424e // âNB09" + +typedef struct _CV_INFO_PDB70 +{ + char CvSignature[4]; + char Signature[16]; + char Age[4]; + char PdbFileName[]; +} CV_INFO_PDB70; + +typedef struct _CV_INFO_PDB20 +{ + char CvHeader[4]; + char Offset[4]; + char Signature[4]; + char Age[4]; + char PdbFileName[]; +} CV_INFO_PDB20; + #endif /* _PE_H */ diff --git a/ld/ChangeLog b/ld/ChangeLog index 0a02f900a7..cdba250b17 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,21 @@ +2014-04-08 Jon TURNEY + + * emultempl/elf32.em (id_note_section_size, read_hex, write_build_id): + Move code for parsing build-id option and calculating the build-id to... + * ldbuildid.c: New file. + * ldbuildid.h: New file. + * Makefile.am (CFILES, HFILES, OFILES, ld_new_SOURCES): Add new + files. + * Makefile.in: Regenerate. + * ld.texinfo: Update --build-id description to mention COFF + support. + * NEWS: Mention support for COFF build ids. + * emultempl/pe.em (gld${EMULATION_NAME}_handle_option): + (pecoff_checksum_contents, write_build_id, setup_build_id) + (gld_${EMULATION_NAME}_after_open): Handle and implement + build-id option. + * emultempl/pep.em: Likewise. + 2014-04-04 Cary Coutant PR gold/16804 diff --git a/ld/Makefile.am b/ld/Makefile.am index 795663f740..3e2dc1af64 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -496,12 +496,12 @@ ALL_EMUL_EXTRA_BINARIES = CFILES = ldctor.c ldemul.c ldexp.c ldfile.c ldlang.c \ ldmain.c ldmisc.c ldver.c ldwrite.c lexsup.c \ mri.c ldcref.c pe-dll.c pep-dll.c ldlex-wrapper.c \ - $(PLUGIN_C) + $(PLUGIN_C) ldbuildid.c HFILES = ld.h ldctor.h ldemul.h ldexp.h ldfile.h \ ldlang.h ldlex.h ldmain.h ldmisc.h ldver.h \ ldwrite.h mri.h deffile.h pe-dll.h pep-dll.h \ - elf-hints-local.h $(PLUGIN_H) + elf-hints-local.h $(PLUGIN_H) ldbuildid.h GENERATED_CFILES = ldgram.c ldlex.c deffilep.c GENERATED_HFILES = ldgram.h ldemul-list.h deffilep.h @@ -513,7 +513,8 @@ BUILT_SOURCES = $(GENERATED_HFILES) OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@ \ mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ $(PLUGIN_OBJECT) \ ldwrite.@OBJEXT@ ldexp.@OBJEXT@ ldemul.@OBJEXT@ ldver.@OBJEXT@ ldmisc.@OBJEXT@ \ - ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} + ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} \ + ldbuildid.@OBJEXT@ STAGESTUFF = *.@OBJEXT@ ldscripts/* e*.c @@ -1935,7 +1936,8 @@ EXTRA_ld_new_SOURCES = deffilep.y ldlex.l EXTRA_ld_new_SOURCES += pep-dll.c pe-dll.c ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \ - ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c $(PLUGIN_C) + ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c $(PLUGIN_C) \ + ldbuildid.c ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(EMUL_EXTRA_BINARIES) \ $(BFDLIB) $(LIBIBERTY) $(LIBINTL_DEP) ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) diff --git a/ld/Makefile.in b/ld/Makefile.in index 3c9f8f4b1d..4f54c26cdc 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -109,7 +109,7 @@ am_ld_new_OBJECTS = ldgram.$(OBJEXT) ldlex-wrapper.$(OBJEXT) \ ldctor.$(OBJEXT) ldmain.$(OBJEXT) ldwrite.$(OBJEXT) \ ldexp.$(OBJEXT) ldemul.$(OBJEXT) ldver.$(OBJEXT) \ ldmisc.$(OBJEXT) ldfile.$(OBJEXT) ldcref.$(OBJEXT) \ - $(am__objects_1) + $(am__objects_1) ldbuildid.$(OBJEXT) ld_new_OBJECTS = $(am_ld_new_OBJECTS) am__DEPENDENCIES_1 = DEFAULT_INCLUDES = -I.@am__isrc@ @@ -800,12 +800,12 @@ ALL_EMUL_EXTRA_BINARIES = CFILES = ldctor.c ldemul.c ldexp.c ldfile.c ldlang.c \ ldmain.c ldmisc.c ldver.c ldwrite.c lexsup.c \ mri.c ldcref.c pe-dll.c pep-dll.c ldlex-wrapper.c \ - $(PLUGIN_C) + $(PLUGIN_C) ldbuildid.c HFILES = ld.h ldctor.h ldemul.h ldexp.h ldfile.h \ ldlang.h ldlex.h ldmain.h ldmisc.h ldver.h \ ldwrite.h mri.h deffile.h pe-dll.h pep-dll.h \ - elf-hints-local.h $(PLUGIN_H) + elf-hints-local.h $(PLUGIN_H) ldbuildid.h GENERATED_CFILES = ldgram.c ldlex.c deffilep.c GENERATED_HFILES = ldgram.h ldemul-list.h deffilep.h @@ -816,7 +816,8 @@ BUILT_SOURCES = $(GENERATED_HFILES) OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@ \ mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ $(PLUGIN_OBJECT) \ ldwrite.@OBJEXT@ ldexp.@OBJEXT@ ldemul.@OBJEXT@ ldver.@OBJEXT@ ldmisc.@OBJEXT@ \ - ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} + ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} \ + ldbuildid.@OBJEXT@ STAGESTUFF = *.@OBJEXT@ ldscripts/* e*.c @@ -838,7 +839,8 @@ ELF_GEN_DEPS = $(srcdir)/emultempl/generic.em $(srcdir)/emultempl/elf-generic.em EXTRA_ld_new_SOURCES = deffilep.y ldlex.l pep-dll.c pe-dll.c \ $(ALL_EMULATION_SOURCES) $(ALL_64_EMULATION_SOURCES) ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \ - ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c $(PLUGIN_C) + ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c $(PLUGIN_C) \ + ldbuildid.c ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(EMUL_EXTRA_BINARIES) \ $(BFDLIB) $(LIBIBERTY) $(LIBINTL_DEP) @@ -1365,6 +1367,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ez80.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ez8001.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ez8002.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldbuildid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldcref.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldctor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldemul.Po@am__quote@ diff --git a/ld/NEWS b/ld/NEWS index 94d086e93c..a9124b6d14 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -1,5 +1,9 @@ -*- text -*- +* Add support for the --build-id command line option to COFF based targets. + +* x86/x86_64 pe-coff now supports the --build-id option. + * Add support for the Andes NDS32. Changes in 2.24: diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em index de460a274f..0173b66d7b 100644 --- a/ld/emultempl/elf32.em +++ b/ld/emultempl/elf32.em @@ -39,10 +39,7 @@ fragment < #include "bfdlink.h" @@ -54,6 +51,7 @@ fragment < #include "elf/common.h" #include "elf-bfd.h" @@ -895,53 +893,20 @@ id_note_section_size (bfd *abfd ATTRIBUTE_UNUSED) { const char *style = emit_note_gnu_build_id; bfd_size_type size; + bfd_size_type build_id_size; size = offsetof (Elf_External_Note, name[sizeof "GNU"]); size = (size + 3) & -(bfd_size_type) 4; - if (!strcmp (style, "md5") || !strcmp (style, "uuid")) - size += 128 / 8; - else if (!strcmp (style, "sha1")) - size += 160 / 8; - else if (!strncmp (style, "0x", 2)) - { - /* ID is in string form (hex). Convert to bits. */ - const char *id = style + 2; - do - { - if (ISXDIGIT (id[0]) && ISXDIGIT (id[1])) - { - ++size; - id += 2; - } - else if (*id == '-' || *id == ':') - ++id; - else - { - size = 0; - break; - } - } while (*id != '\0'); - } + build_id_size = compute_build_id_size (style); + if (build_id_size) + size += build_id_size; else size = 0; return size; } -static unsigned char -read_hex (const char xdigit) -{ - if (ISDIGIT (xdigit)) - return xdigit - '0'; - if (ISUPPER (xdigit)) - return xdigit - 'A' + 0xa; - if (ISLOWER (xdigit)) - return xdigit - 'a' + 0xa; - abort (); - return 0; -} - static bfd_boolean write_build_id (bfd *abfd) { @@ -954,7 +919,6 @@ write_build_id (bfd *abfd) bfd_size_type size; file_ptr position; Elf_External_Note *e_note; - typedef void (*sum_fn) (const void *, size_t, void *); style = t->o->build_id.style; asec = t->o->build_id.sec; @@ -986,55 +950,7 @@ write_build_id (bfd *abfd) bfd_h_put_32 (abfd, NT_GNU_BUILD_ID, &e_note->type); memcpy (e_note->name, "GNU", sizeof "GNU"); - if (strcmp (style, "md5") == 0) - { - struct md5_ctx ctx; - - md5_init_ctx (&ctx); - if (!bed->s->checksum_contents (abfd, (sum_fn) &md5_process_bytes, &ctx)) - return FALSE; - md5_finish_ctx (&ctx, id_bits); - } - else if (strcmp (style, "sha1") == 0) - { - struct sha1_ctx ctx; - - sha1_init_ctx (&ctx); - if (!bed->s->checksum_contents (abfd, (sum_fn) &sha1_process_bytes, &ctx)) - return FALSE; - sha1_finish_ctx (&ctx, id_bits); - } - else if (strcmp (style, "uuid") == 0) - { - int n; - int fd = open ("/dev/urandom", O_RDONLY); - if (fd < 0) - return FALSE; - n = read (fd, id_bits, size); - close (fd); - if (n < (int) size) - return FALSE; - } - else if (strncmp (style, "0x", 2) == 0) - { - /* ID is in string form (hex). Convert to bits. */ - const char *id = style + 2; - size_t n = 0; - do - { - if (ISXDIGIT (id[0]) && ISXDIGIT (id[1])) - { - id_bits[n] = read_hex (*id++) << 4; - id_bits[n++] |= read_hex (*id++); - } - else if (*id == '-' || *id == ':') - ++id; - else - abort (); /* Should have been validated earlier. */ - } while (*id != '\0'); - } - else - abort (); /* Should have been validated earlier. */ + generate_build_id (abfd, style, bed->s->checksum_contents, id_bits, size); position = i_shdr->sh_offset + asec->output_offset; size = asec->size; diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em index c18cb266be..ba51cc0488 100644 --- a/ld/emultempl/pe.em +++ b/ld/emultempl/pe.em @@ -66,6 +66,7 @@ fragment <sections; asec != NULL; asec = asec->next) + { + struct bfd_link_order *l = NULL; + for (l = asec->map_head.link_order; l != NULL; l = l->next) + { + if ((l->type == bfd_indirect_link_order)) + { + if (l->u.indirect.section == t->build_id.sec) + { + link_order = l; + break; + } + } + } + + if (link_order) + break; + } + + if (!link_order) + { + einfo (_("%P: warning: .build-id section discarded," + " --build-id ignored.\n")); + return TRUE; + } + + if (t->build_id.sec->contents == NULL) + t->build_id.sec->contents = (unsigned char *) xmalloc (t->build_id.sec->size); + contents = t->build_id.sec->contents; + size = t->build_id.sec->size; + + build_id_size = compute_build_id_size (t->build_id.style); + build_id = xmalloc (build_id_size); + generate_build_id (abfd, t->build_id.style, pecoff_checksum_contents, build_id, build_id_size); + + bfd_vma ib = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase; + + /* Construct a debug directory entry which points to an immediately following CodeView record. */ + struct internal_IMAGE_DEBUG_DIRECTORY idd; + idd.Characteristics = 0; + idd.TimeDateStamp = 0; + idd.MajorVersion = 0; + idd.MinorVersion = 0; + idd.Type = PE_IMAGE_DEBUG_TYPE_CODEVIEW; + idd.SizeOfData = sizeof (CV_INFO_PDB70) + 1; + idd.AddressOfRawData = asec->vma - ib + link_order->offset + + sizeof (struct external_IMAGE_DEBUG_DIRECTORY); + idd.PointerToRawData = asec->filepos + link_order->offset + + sizeof (struct external_IMAGE_DEBUG_DIRECTORY); + + struct external_IMAGE_DEBUG_DIRECTORY *ext = (struct external_IMAGE_DEBUG_DIRECTORY *)contents; + _bfd_XXi_swap_debugdir_out (abfd, &idd, ext); + + /* Write the debug directory entry. */ + if (bfd_seek (abfd, asec->filepos + link_order->offset, SEEK_SET) != 0) + return 0; + + if ((bfd_bwrite (contents, size, abfd) != size)) + return 0; + + /* Construct the CodeView record. */ + CODEVIEW_INFO cvinfo; + cvinfo.CVSignature = CVINFO_PDB70_CVSIGNATURE; + cvinfo.Age = 1; + + /* Zero pad or truncate the generated build_id to fit in the CodeView record. */ + memset (&(cvinfo.Signature), 0, CV_INFO_SIGNATURE_LENGTH); + memcpy (&(cvinfo.Signature), build_id, (build_id_size > CV_INFO_SIGNATURE_LENGTH) + ? CV_INFO_SIGNATURE_LENGTH : build_id_size); + + free (build_id); + + /* Write the codeview record. */ + if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo) == 0) + return 0; + + /* Record the location of the debug directory in the data directory. */ + pe_data (link_info.output_bfd)->pe_opthdr.DataDirectory[PE_DEBUG_DATA].VirtualAddress + = asec->vma - ib + link_order->offset; + pe_data (link_info.output_bfd)->pe_opthdr.DataDirectory[PE_DEBUG_DATA].Size + = sizeof (struct external_IMAGE_DEBUG_DIRECTORY); + + return TRUE; +} + +/* Make .build-id section, and set up coff_tdata->build_id. */ +static bfd_boolean +setup_build_id (bfd *ibfd) +{ + asection *s; + flagword flags; + + if (!validate_build_id_style (emit_build_id)) + { + einfo ("%P: warning: unrecognized --build-id style ignored.\n"); + return FALSE; + } + + flags = (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_IN_MEMORY + | SEC_LINKER_CREATED | SEC_READONLY | SEC_DATA); + s = bfd_make_section_anyway_with_flags (ibfd, ".build-id", flags); + if (s != NULL) + { + struct pe_tdata *t = pe_data (link_info.output_bfd); + t->build_id.after_write_object_contents = &write_build_id; + t->build_id.style = emit_build_id; + t->build_id.sec = s; + + /* Section is a fixed size: + One IMAGE_DEBUG_DIRECTORY entry, of type IMAGE_DEBUG_TYPE_CODEVIEW, + pointing at a CV_INFO_PDB70 record containing the build-id, with a + null byte for PdbFileName. */ + s->size = sizeof (struct external_IMAGE_DEBUG_DIRECTORY) + + sizeof (CV_INFO_PDB70) + 1; + + return TRUE; + } + + einfo ("%P: warning: Cannot create .build-id section," + " --build-id ignored.\n"); + return FALSE; +} + static void gld_${EMULATION_NAME}_after_open (void) { @@ -1257,6 +1438,26 @@ gld_${EMULATION_NAME}_after_open (void) } #endif + if (emit_build_id != NULL) + { + bfd *abfd; + + /* Find a COFF input. */ + for (abfd = link_info.input_bfds; + abfd != (bfd *) NULL; abfd = abfd->link_next) + if (bfd_get_flavour (abfd) == bfd_target_coff_flavour) + break; + + /* If there are no COFF input files do not try to + add a build-id section. */ + if (abfd == NULL + || !setup_build_id (abfd)) + { + free ((char *) emit_build_id); + emit_build_id = NULL; + } + } + /* Pass the wacky PE command line options into the output bfd. FIXME: This should be done via a function, rather than by including an internal BFD header. */ @@ -1279,17 +1480,23 @@ gld_${EMULATION_NAME}_after_open (void) find it, so enable it in that case. */ if (pe_use_coff_long_section_names < 0 && link_info.strip == strip_none) { - /* Iterate over all sections of all input BFDs, checking - for any that begin 'debug_' and are long names. */ - LANG_FOR_EACH_INPUT_STATEMENT (is) + if (link_info.relocatable) + pe_use_coff_long_section_names = 1; + else { - int found_debug = 0; - bfd_map_over_sections (is->the_bfd, debug_section_p, &found_debug); - if (found_debug) - { - pe_use_coff_long_section_names = 1; - break; - } + /* Iterate over all sections of all input BFDs, checking + for any that begin 'debug_' and are long names. */ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + int found_debug = 0; + + bfd_map_over_sections (is->the_bfd, debug_section_p, &found_debug); + if (found_debug) + { + pe_use_coff_long_section_names = 1; + break; + } + } } } diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em index dca36cc341..d1575e2673 100644 --- a/ld/emultempl/pep.em +++ b/ld/emultempl/pep.em @@ -64,6 +64,7 @@ fragment <name, sizeof (".debug_") - 1) == 0) *found = 1; } +static bfd_boolean +pecoff_checksum_contents (bfd *abfd, + void (*process) (const void *, size_t, void *), + void *arg) +{ + file_ptr filepos = (file_ptr) 0; + + while (1) + { + unsigned char b; + int status; + + if (bfd_seek (abfd, filepos, SEEK_SET) != 0) + return 0; + + status = bfd_bread (&b, (bfd_size_type) 1, abfd); + if (status < 1) + { + break; + } + + (*process) (&b, 1, arg); + filepos += 1; + } + + return TRUE; +} + +static bfd_boolean +write_build_id (bfd *abfd) +{ + struct pe_tdata *t = pe_data (abfd); + asection *asec; + struct bfd_link_order *link_order = NULL; + unsigned char *contents; + bfd_size_type size; + bfd_size_type build_id_size; + unsigned char *build_id; + + /* Find the section the .build-id output section has been merged info. */ + for (asec = abfd->sections; asec != NULL; asec = asec->next) + { + struct bfd_link_order *l = NULL; + for (l = asec->map_head.link_order; l != NULL; l = l->next) + { + if ((l->type == bfd_indirect_link_order)) + { + if (l->u.indirect.section == t->build_id.sec) + { + link_order = l; + break; + } + } + } + + if (link_order) + break; + } + + if (!link_order) + { + einfo (_("%P: warning: .build-id section discarded," + " --build-id ignored.\n")); + return TRUE; + } + + if (t->build_id.sec->contents == NULL) + t->build_id.sec->contents = (unsigned char *) xmalloc (t->build_id.sec->size); + contents = t->build_id.sec->contents; + size = t->build_id.sec->size; + + build_id_size = compute_build_id_size (t->build_id.style); + build_id = xmalloc (build_id_size); + generate_build_id (abfd, t->build_id.style, pecoff_checksum_contents, build_id, build_id_size); + + bfd_vma ib = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase; + + /* Construct a debug directory entry which points to an immediately following CodeView record. */ + struct internal_IMAGE_DEBUG_DIRECTORY idd; + idd.Characteristics = 0; + idd.TimeDateStamp = 0; + idd.MajorVersion = 0; + idd.MinorVersion = 0; + idd.Type = PE_IMAGE_DEBUG_TYPE_CODEVIEW; + idd.SizeOfData = sizeof (CV_INFO_PDB70) + 1; + idd.AddressOfRawData = asec->vma - ib + link_order->offset + + sizeof (struct external_IMAGE_DEBUG_DIRECTORY); + idd.PointerToRawData = asec->filepos + link_order->offset + + sizeof (struct external_IMAGE_DEBUG_DIRECTORY); + + struct external_IMAGE_DEBUG_DIRECTORY *ext = (struct external_IMAGE_DEBUG_DIRECTORY *)contents; + _bfd_XXi_swap_debugdir_out (abfd, &idd, ext); + + /* Write the debug directory enttry */ + if (bfd_seek (abfd, asec->filepos + link_order->offset, SEEK_SET) != 0) + return 0; + + if ((bfd_bwrite (contents, size, abfd) != size)) + return 0; + + /* Construct the CodeView record. */ + CODEVIEW_INFO cvinfo; + cvinfo.CVSignature = CVINFO_PDB70_CVSIGNATURE; + cvinfo.Age = 1; + + /* Zero pad or truncate the generated build_id to fit in the CodeView record. */ + memset (&(cvinfo.Signature), 0, CV_INFO_SIGNATURE_LENGTH); + memcpy (&(cvinfo.Signature), build_id, (build_id_size > CV_INFO_SIGNATURE_LENGTH) + ? CV_INFO_SIGNATURE_LENGTH : build_id_size); + + free (build_id); + + /* Write the codeview record. */ + if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo) == 0) + return 0; + + /* Record the location of the debug directory in the data directory. */ + pe_data (link_info.output_bfd)->pe_opthdr.DataDirectory[PE_DEBUG_DATA].VirtualAddress + = asec->vma - ib + link_order->offset; + pe_data (link_info.output_bfd)->pe_opthdr.DataDirectory[PE_DEBUG_DATA].Size + = sizeof (struct external_IMAGE_DEBUG_DIRECTORY); + + return TRUE; +} + +/* Make .build-id section, and set up coff_tdata->build_id. */ +static bfd_boolean +setup_build_id (bfd *ibfd) +{ + asection *s; + flagword flags; + + if (!validate_build_id_style (emit_build_id)) + { + einfo ("%P: warning: unrecognized --build-id style ignored.\n"); + return FALSE; + } + + flags = (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_IN_MEMORY + | SEC_LINKER_CREATED | SEC_READONLY | SEC_DATA); + s = bfd_make_section_anyway_with_flags (ibfd, ".build-id", flags); + if (s != NULL) + { + struct pe_tdata *t = pe_data (link_info.output_bfd); + t->build_id.after_write_object_contents = &write_build_id; + t->build_id.style = emit_build_id; + t->build_id.sec = s; + + /* Section is a fixed size: + One IMAGE_DEBUG_DIRECTORY entry, of type IMAGE_DEBUG_TYPE_CODEVIEW, + pointing at a CV_INFO_PDB70 record containing the build-id, with a + null byte for PdbFileName. */ + s->size = sizeof (struct external_IMAGE_DEBUG_DIRECTORY) + + sizeof (CV_INFO_PDB70) + 1; + + return TRUE; + } + + einfo ("%P: warning: Cannot create .build-id section," + " --build-id ignored.\n"); + return FALSE; +} + static void gld_${EMULATION_NAME}_after_open (void) { @@ -1214,6 +1396,26 @@ gld_${EMULATION_NAME}_after_open (void) } #endif + if (emit_build_id != NULL) + { + bfd *abfd; + + /* Find a COFF input. */ + for (abfd = link_info.input_bfds; + abfd != (bfd *) NULL; abfd = abfd->link_next) + if (bfd_get_flavour (abfd) == bfd_target_coff_flavour) + break; + + /* If there are no COFF input files do not try to + add a build-id section. */ + if (abfd == NULL + || !setup_build_id (abfd)) + { + free ((char *) emit_build_id); + emit_build_id = NULL; + } + } + /* Pass the wacky PE command line options into the output bfd. FIXME: This should be done via a function, rather than by including an internal BFD header. */ @@ -1236,17 +1438,23 @@ gld_${EMULATION_NAME}_after_open (void) find it, so enable it in that case. */ if (pep_use_coff_long_section_names < 0 && link_info.strip == strip_none) { - /* Iterate over all sections of all input BFDs, checking - for any that begin 'debug_' and are long names. */ - LANG_FOR_EACH_INPUT_STATEMENT (is) + if (link_info.relocatable) + pep_use_coff_long_section_names = 1; + else { - int found_debug = 0; - bfd_map_over_sections (is->the_bfd, debug_section_p, &found_debug); - if (found_debug) - { - pep_use_coff_long_section_names = 1; - break; - } + /* Iterate over all sections of all input BFDs, checking + for any that begin 'debug_' and are long names. */ + LANG_FOR_EACH_INPUT_STATEMENT (is) + { + int found_debug = 0; + + bfd_map_over_sections (is->the_bfd, debug_section_p, &found_debug); + if (found_debug) + { + pep_use_coff_long_section_names = 1; + break; + } + } } } diff --git a/ld/ld.texinfo b/ld/ld.texinfo index 62d8aa7563..7d2de3b0eb 100644 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@ -2160,16 +2160,16 @@ enable other tradeoffs in future versions of the linker. @kindex --build-id=@var{style} @item --build-id @itemx --build-id=@var{style} -Request creation of @code{.note.gnu.build-id} ELF note section. -The contents of the note are unique bits identifying this linked -file. @var{style} can be @code{uuid} to use 128 random bits, -@code{sha1} to use a 160-bit @sc{SHA1} hash on the normative -parts of the output contents, @code{md5} to use a 128-bit -@sc{MD5} hash on the normative parts of the output contents, or -@code{0x@var{hexstring}} to use a chosen bit string specified as -an even number of hexadecimal digits (@code{-} and @code{:} -characters between digit pairs are ignored). If @var{style} is -omitted, @code{sha1} is used. +Request the creation of a @code{.note.gnu.build-id} ELF note section +or a @code{.build-id} COFF section. The contents of the note are +unique bits identifying this linked file. @var{style} can be +@code{uuid} to use 128 random bits, @code{sha1} to use a 160-bit +@sc{SHA1} hash on the normative parts of the output contents, +@code{md5} to use a 128-bit @sc{MD5} hash on the normative parts of +the output contents, or @code{0x@var{hexstring}} to use a chosen bit +string specified as an even number of hexadecimal digits (@code{-} and +@code{:} characters between digit pairs are ignored). If @var{style} +is omitted, @code{sha1} is used. The @code{md5} and @code{sha1} styles produces an identifier that is always the same in an identical output file, but will be diff --git a/ld/ldbuildid.c b/ld/ldbuildid.c new file mode 100644 index 0000000000..1214789671 --- /dev/null +++ b/ld/ldbuildid.c @@ -0,0 +1,158 @@ +/* ldbuildid.c - Build Id support routines + Copyright 2013, 2014 Free Software Foundation, Inc. + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "sysdep.h" +#include "bfd.h" +#include "safe-ctype.h" +#include "md5.h" +#include "sha1.h" +#include "ldbuildid.h" + +#define streq(a,b) strcmp ((a), (b)) == 0 +#define strneq(a,b,n) strncmp ((a), (b), (n)) == 0 + +bfd_boolean +validate_build_id_style (const char *style) +{ + if ((streq (style, "md5")) || (streq (style, "sha1")) +#ifndef __MINGW32__ + || (streq (style, "uuid")) +#endif + || (strneq (style, "0x", 2))) + return TRUE; + + return FALSE; +} + +bfd_size_type +compute_build_id_size (const char *style) +{ + if (streq (style, "md5") || streq (style, "uuid")) + return 128 / 8; + + if (streq (style, "sha1")) + return 160 / 8; + + if (strneq (style, "0x", 2)) + { + bfd_size_type size = 0; + /* ID is in string form (hex). Count the bytes. */ + const char *id = style + 2; + + do + { + if (ISXDIGIT (id[0]) && ISXDIGIT (id[1])) + { + ++size; + id += 2; + } + else if (*id == '-' || *id == ':') + ++id; + else + { + size = 0; + break; + } + } while (*id != '\0'); + return size; + } + + return 0; +} + +static unsigned char +read_hex (const char xdigit) +{ + if (ISDIGIT (xdigit)) + return xdigit - '0'; + + if (ISUPPER (xdigit)) + return xdigit - 'A' + 0xa; + + if (ISLOWER (xdigit)) + return xdigit - 'a' + 0xa; + + abort (); + return 0; +} + +bfd_boolean +generate_build_id (bfd *abfd, + const char *style, + checksum_fn checksum_contents, + unsigned char *id_bits, + int size) +{ + if (streq (style, "md5")) + { + struct md5_ctx ctx; + + md5_init_ctx (&ctx); + if (!(*checksum_contents) (abfd, (sum_fn) &md5_process_bytes, &ctx)) + return FALSE; + md5_finish_ctx (&ctx, id_bits); + } + else if (streq (style, "sha1")) + { + struct sha1_ctx ctx; + + sha1_init_ctx (&ctx); + if (!(*checksum_contents) (abfd, (sum_fn) &sha1_process_bytes, &ctx)) + return FALSE; + sha1_finish_ctx (&ctx, id_bits); + } +#ifndef __MINGW32__ + else if (streq (style, "uuid")) + { + int n; + int fd = open ("/dev/urandom", O_RDONLY); + + if (fd < 0) + return FALSE; + n = read (fd, id_bits, size); + close (fd); + if (n < size) + return FALSE; + } +#endif + else if (strneq (style, "0x", 2)) + { + /* ID is in string form (hex). Convert to bits. */ + const char *id = style + 2; + size_t n = 0; + + do + { + if (ISXDIGIT (id[0]) && ISXDIGIT (id[1])) + { + id_bits[n] = read_hex (*id++) << 4; + id_bits[n++] |= read_hex (*id++); + } + else if (*id == '-' || *id == ':') + ++id; + else + abort (); /* Should have been validated earlier. */ + } while (*id != '\0'); + } + else + abort (); /* Should have been validated earlier. */ + + return TRUE; +} diff --git a/ld/ldbuildid.h b/ld/ldbuildid.h new file mode 100644 index 0000000000..a91ac1a1b8 --- /dev/null +++ b/ld/ldbuildid.h @@ -0,0 +1,39 @@ +/* ldbuildid.h - + Copyright 2013, 2014 Free Software Foundation, Inc. + + This file is part of the GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#ifndef LDBUILDID_H +#define LDBUILDID_H + +extern bfd_boolean +validate_build_id_style (const char *); + +extern bfd_size_type +compute_build_id_size (const char *); + +typedef void (*sum_fn) (const void *, size_t, void *); + +typedef bfd_boolean (*checksum_fn) (bfd *, + void (*) (const void *, size_t, void *), + void *); + +extern bfd_boolean +generate_build_id (bfd *, const char *, checksum_fn, unsigned char *, int); + +#endif /* LDBUILDID_H */ diff --git a/ld/ldmain.c b/ld/ldmain.c index 14253a6f0c..b132dae62d 100644 --- a/ld/ldmain.c +++ b/ld/ldmain.c @@ -378,6 +378,13 @@ main (int argc, char **argv) lang_final (); + /* If the only command line argument has been -v or --version or --verbose + then ignore any input files provided by linker scripts and exit now. + We do not want to create an output file when the linker is just invoked + to provide version information. */ + if (argc == 2 && version_printed) + xexit (0); + if (!lang_has_input_file) { if (version_printed || command_line.print_output_format) diff --git a/ld/testsuite/ld-pe/longsecn-3.d b/ld/testsuite/ld-pe/longsecn-3.d index 0317be3084..c86a828db9 100644 --- a/ld/testsuite/ld-pe/longsecn-3.d +++ b/ld/testsuite/ld-pe/longsecn-3.d @@ -37,5 +37,4 @@ Idx Name Size VMA +LMA +File off Algn CONTENTS, ALLOC, LOAD, DATA 14 \.rodata\.very\.long\.section\$1234 [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ 2\*\*[0-9] CONTENTS, ALLOC, LOAD, DATA - 15 \.(bss |text) [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ 2\*\*[0-9] - (ALLOC|CONTENTS, ALLOC, LOAD, (READONLY, )?CODE) +#... diff --git a/ld/testsuite/ld-pe/longsecn-4.d b/ld/testsuite/ld-pe/longsecn-4.d index 565ef38100..e326d98e80 100644 --- a/ld/testsuite/ld-pe/longsecn-4.d +++ b/ld/testsuite/ld-pe/longsecn-4.d @@ -37,5 +37,4 @@ Idx Name Size VMA +LMA +File off Algn CONTENTS, ALLOC, LOAD, DATA 14 \.rodata\. [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ 2\*\*[0-9] CONTENTS, ALLOC, LOAD, DATA - 15 \.(bss |text) [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ 2\*\*[0-9] - (ALLOC|CONTENTS, ALLOC, LOAD, (READONLY, )?CODE) +#... diff --git a/ld/testsuite/ld-pe/longsecn-5.d b/ld/testsuite/ld-pe/longsecn-5.d index 82d94b8ab2..f3ef22bd9a 100644 --- a/ld/testsuite/ld-pe/longsecn-5.d +++ b/ld/testsuite/ld-pe/longsecn-5.d @@ -37,5 +37,4 @@ Idx Name Size VMA +LMA +File off Algn CONTENTS, ALLOC, LOAD, DATA 14 \.rodata\.very\.long\.section\$1234 [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ 2\*\*[0-9] CONTENTS, ALLOC, LOAD, DATA - 15 \.(bss |text) [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ [0-9a-fA-F]+ 2\*\*[0-9] - (ALLOC|CONTENTS, ALLOC, LOAD, (READONLY, )?CODE) +#... diff --git a/ld/testsuite/ld-pe/non-c-lang-syms.s b/ld/testsuite/ld-pe/non-c-lang-syms.s index e849d9e168..28006a1bf6 100644 --- a/ld/testsuite/ld-pe/non-c-lang-syms.s +++ b/ld/testsuite/ld-pe/non-c-lang-syms.s @@ -1,4 +1,3 @@ - main: _main: nop diff --git a/ld/testsuite/ld-pe/orphana_nu.s b/ld/testsuite/ld-pe/orphana_nu.s index d3c564f3f4..618789c8dd 100644 --- a/ld/testsuite/ld-pe/orphana_nu.s +++ b/ld/testsuite/ld-pe/orphana_nu.s @@ -1,6 +1,8 @@ + .globl _mainCRTStartup .globl mainCRTStartup .globl start .text +_mainCRTStartup: mainCRTStartup: start: