1 /* Copyright (C) 2001, 2002, 2003, 2005, 2007 Red Hat, Inc.
2 Written by Alexander Larsson <alexl@redhat.com>, 2002
3 Based on code by Jakub Jelinek <jakub@redhat.com>, 2001.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 /* Needed for libelf */
22 #define _FILE_OFFSET_BITS 64
34 #include <sys/types.h>
42 #include <rpm/rpmio.h>
43 #include <rpm/rpmpgp.h>
44 #include "tools/hashtab.h"
46 #define DW_TAG_partial_unit 0x3c
48 char *base_dir = NULL;
49 char *dest_dir = NULL;
50 char *list_file = NULL;
51 int list_file_fd = -1;
70 #define read_uleb128(ptr) ({ \
71 unsigned int ret = 0; \
77 ret |= (c & 0x7f) << shift; \
86 static uint16_t (*do_read_16) (unsigned char *ptr);
87 static uint32_t (*do_read_32) (unsigned char *ptr);
88 static void (*write_32) (unsigned char *ptr, GElf_Addr val);
92 static inline uint16_t
93 buf_read_ule16 (unsigned char *data)
95 return data[0] | (data[1] << 8);
98 static inline uint16_t
99 buf_read_ube16 (unsigned char *data)
101 return data[1] | (data[0] << 8);
104 static inline uint32_t
105 buf_read_ule32 (unsigned char *data)
107 return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
110 static inline uint32_t
111 buf_read_ube32 (unsigned char *data)
113 return data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
117 strptr (DSO *dso, int sec, off_t offset)
123 if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size)
126 while ((data = elf_rawdata (scn, data)) != NULL)
129 && offset >= data->d_off
130 && offset < data->d_off + data->d_size)
131 return (const char *) data->d_buf + (offset - data->d_off);
139 #define read_1(ptr) *ptr++
141 #define read_16(ptr) ({ \
142 uint16_t ret = do_read_16 (ptr); \
147 #define read_32(ptr) ({ \
148 uint32_t ret = do_read_32 (ptr); \
153 REL *relptr, *relend;
156 #define do_read_32_relocated(ptr) ({ \
157 uint32_t dret = do_read_32 (ptr); \
160 while (relptr < relend && relptr->ptr < ptr) \
162 if (relptr < relend && relptr->ptr == ptr) \
164 if (reltype == SHT_REL) \
165 dret += relptr->addend; \
167 dret = relptr->addend; \
173 #define read_32_relocated(ptr) ({ \
174 uint32_t ret = do_read_32_relocated (ptr); \
180 dwarf2_write_le32 (unsigned char *p, GElf_Addr val)
182 uint32_t v = (uint32_t) val;
192 dwarf2_write_be32 (unsigned char *p, GElf_Addr val)
194 uint32_t v = (uint32_t) val;
212 #define DEBUG_ABBREV 1
214 #define DEBUG_ARANGES 3
215 #define DEBUG_PUBNAMES 4
216 #define DEBUG_MACINFO 5
219 #define DEBUG_FRAME 8
220 #define DEBUG_RANGES 9
221 { ".debug_info", NULL, NULL, 0, 0, 0 },
222 { ".debug_abbrev", NULL, NULL, 0, 0, 0 },
223 { ".debug_line", NULL, NULL, 0, 0, 0 },
224 { ".debug_aranges", NULL, NULL, 0, 0, 0 },
225 { ".debug_pubnames", NULL, NULL, 0, 0, 0 },
226 { ".debug_macinfo", NULL, NULL, 0, 0, 0 },
227 { ".debug_loc", NULL, NULL, 0, 0, 0 },
228 { ".debug_str", NULL, NULL, 0, 0, 0 },
229 { ".debug_frame", NULL, NULL, 0, 0, 0 },
230 { ".debug_ranges", NULL, NULL, 0, 0, 0 },
231 { NULL, NULL, NULL, 0, 0, 0 }
245 struct abbrev_attr attr[0];
249 abbrev_hash (const void *p)
251 struct abbrev_tag *t = (struct abbrev_tag *)p;
257 abbrev_eq (const void *p, const void *q)
259 struct abbrev_tag *t1 = (struct abbrev_tag *)p;
260 struct abbrev_tag *t2 = (struct abbrev_tag *)q;
262 return t1->entry == t2->entry;
272 read_abbrev (DSO *dso, unsigned char *ptr)
274 htab_t h = htab_try_create (50, abbrev_hash, abbrev_eq, abbrev_del);
275 unsigned int attr, form;
276 struct abbrev_tag *t;
283 error (0, ENOMEM, "%s: Could not read .debug_abbrev", dso->filename);
289 while ((attr = read_uleb128 (ptr)) != 0)
292 t = malloc (sizeof (*t) + size * sizeof (struct abbrev_attr));
297 slot = htab_find_slot (h, t, INSERT);
305 error (0, 0, "%s: Duplicate DWARF-2 abbreviation %d", dso->filename,
311 t->tag = read_uleb128 (ptr);
312 ++ptr; /* skip children flag. */
313 while ((attr = read_uleb128 (ptr)) != 0)
315 if (t->nattr == size)
318 t = realloc (t, sizeof (*t) + size * sizeof (struct abbrev_attr));
322 form = read_uleb128 (ptr);
323 if (form == 2 || form > DW_FORM_indirect)
325 error (0, 0, "%s: Unknown DWARF-2 DW_FORM_%d", dso->filename, form);
330 t->attr[t->nattr].attr = attr;
331 t->attr[t->nattr++].form = form;
333 if (read_uleb128 (ptr) != 0)
335 error (0, 0, "%s: DWARF-2 abbreviation does not end with 2 zeros",
346 #define IS_DIR_SEPARATOR(c) ((c)=='/')
349 canonicalize_path (const char *s, char *d)
355 if (IS_DIR_SEPARATOR (*s))
358 if (IS_DIR_SEPARATOR (*s) && !IS_DIR_SEPARATOR (s[1]))
360 /* Special case for "//foo" meaning a Posix namespace
364 while (IS_DIR_SEPARATOR (*s))
372 /* At this point, we're always at the beginning of a path
375 if (s[0] == '.' && (s[1] == 0 || IS_DIR_SEPARATOR (s[1])))
379 while (IS_DIR_SEPARATOR (*s))
383 else if (s[0] == '.' && s[1] == '.'
384 && (s[2] == 0 || IS_DIR_SEPARATOR (s[2])))
386 char *pre = d - 1; /* includes slash */
387 while (droot < pre && IS_DIR_SEPARATOR (*pre))
389 if (droot <= pre && ! IS_DIR_SEPARATOR (*pre))
391 while (droot < pre && ! IS_DIR_SEPARATOR (*pre))
393 /* pre now points to the slash */
396 if (pre + 3 == d && pre[0] == '.' && pre[1] == '.')
406 while (IS_DIR_SEPARATOR (*s))
418 while (*s && ! IS_DIR_SEPARATOR (*s))
422 if (IS_DIR_SEPARATOR (*s))
425 while (IS_DIR_SEPARATOR (*s))
429 while (droot < d && IS_DIR_SEPARATOR (d[-1]))
439 has_prefix (const char *str,
445 str_len = strlen (str);
446 prefix_len = strlen (prefix);
448 if (str_len < prefix_len)
451 return strncmp (str, prefix, prefix_len) == 0;
455 edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase)
457 unsigned char *ptr = debug_sections[DEBUG_LINE].data, *dir;
458 unsigned char **dirt;
459 unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size;
460 unsigned char *endcu, *endprol;
461 unsigned char opcode_base;
462 uint32_t value, dirt_cnt;
463 size_t comp_dir_len = strlen (comp_dir);
464 size_t abs_file_cnt = 0, abs_dir_cnt = 0;
472 endcu += read_32 (ptr);
473 if (endcu == ptr + 0xffffffff)
475 error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
481 error (0, 0, "%s: .debug_line CU does not fit into section",
486 value = read_16 (ptr);
489 error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
495 endprol += read_32 (ptr);
498 error (0, 0, "%s: .debug_line CU prologue does not fit into CU",
503 opcode_base = ptr[4];
504 ptr = dir = ptr + 4 + opcode_base;
510 ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
514 dirt = (unsigned char **) alloca (value * sizeof (unsigned char *));
515 dirt[0] = (unsigned char *) ".";
520 dirt[dirt_cnt++] = ptr;
521 ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
529 size_t file_len, dir_len;
532 ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1;
533 value = read_uleb128 (ptr);
535 if (value >= dirt_cnt)
537 error (0, 0, "%s: Wrong directory table index %u",
538 dso->filename, value);
541 file_len = strlen (file);
542 dir_len = strlen ((char *)dirt[value]);
543 s = malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1);
546 error (0, ENOMEM, "%s: Reading file table", dso->filename);
551 memcpy (s, file, file_len + 1);
552 if (dest_dir && has_prefix (file, base_dir))
555 else if (*dirt[value] == '/')
557 memcpy (s, dirt[value], dir_len);
559 memcpy (s + dir_len + 1, file, file_len + 1);
564 if (comp_dir_len != 0)
566 memcpy (s, comp_dir, comp_dir_len);
567 s[comp_dir_len] = '/';
568 p += comp_dir_len + 1;
570 memcpy (p, dirt[value], dir_len);
572 memcpy (p + dir_len + 1, file, file_len + 1);
574 canonicalize_path (s, s);
575 if (list_file_fd != -1)
578 if (base_dir == NULL)
580 else if (has_prefix (s, base_dir))
581 p = s + strlen (base_dir);
582 else if (has_prefix (s, dest_dir))
583 p = s + strlen (dest_dir);
587 size_t size = strlen (p) + 1;
590 ssize_t ret = write (list_file_fd, p, size);
608 unsigned char *srcptr, *buf = NULL;
609 size_t base_len = strlen (base_dir);
610 size_t dest_len = strlen (dest_dir);
613 if (dest_len == base_len)
617 srcptr = buf = malloc (ptr - dir);
618 memcpy (srcptr, dir, ptr - dir);
625 size_t len = strlen ((char *)srcptr) + 1;
626 const unsigned char *readptr = srcptr;
628 if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir))
630 if (dest_len < base_len)
632 memcpy (ptr, dest_dir, dest_len);
638 shrank += srcptr - readptr;
639 canonicalize_path ((char *)readptr, (char *)ptr);
640 len = strlen ((char *)ptr) + 1;
644 elf_flagdata (debug_sections[DEBUG_STR].elf_data,
645 ELF_C_SET, ELF_F_DIRTY);
651 error (EXIT_FAILURE, 0,
652 "canonicalization unexpectedly shrank by one character");
655 memset (ptr, 'X', shrank);
661 if (abs_dir_cnt + abs_file_cnt != 0)
663 size_t len = (abs_dir_cnt + abs_file_cnt) * (base_len - dest_len);
666 error (EXIT_FAILURE, 0, "-b arg has to be either the same length as -d arg, or more than 1 char shorter");
667 memset (ptr, 'X', len - 1);
676 size_t len = strlen ((char *)srcptr) + 1;
678 if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir))
680 memcpy (ptr, dest_dir, dest_len);
681 if (dest_len < base_len)
683 memmove (ptr + dest_len, srcptr + base_len,
685 ptr += dest_len - base_len;
687 elf_flagdata (debug_sections[DEBUG_STR].elf_data,
688 ELF_C_SET, ELF_F_DIRTY);
690 else if (ptr != srcptr)
691 memmove (ptr, srcptr, len);
695 read_uleb128 (srcptr);
696 read_uleb128 (srcptr);
697 read_uleb128 (srcptr);
699 memmove (ptr, dir, srcptr - dir);
710 static unsigned char *
711 edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase)
721 for (i = 0; i < t->nattr; ++i)
723 uint32_t form = t->attr[i].form;
725 size_t base_len, dest_len;
730 if (t->attr[i].attr == DW_AT_stmt_list)
732 if (form == DW_FORM_data4)
734 list_offs = do_read_32_relocated (ptr);
739 if (t->attr[i].attr == DW_AT_comp_dir)
741 if ( form == DW_FORM_string )
744 comp_dir = strdup ((char *)ptr);
746 if (phase == 1 && dest_dir && has_prefix ((char *)ptr, base_dir))
748 base_len = strlen (base_dir);
749 dest_len = strlen (dest_dir);
751 memcpy (ptr, dest_dir, dest_len);
752 if (dest_len < base_len)
754 memset(ptr + dest_len, '/',
755 base_len - dest_len);
758 elf_flagdata (debug_sections[DEBUG_INFO].elf_data,
759 ELF_C_SET, ELF_F_DIRTY);
763 else if (form == DW_FORM_strp &&
764 debug_sections[DEBUG_STR].data)
768 dir = (char *) debug_sections[DEBUG_STR].data
769 + do_read_32_relocated (ptr);
772 comp_dir = strdup (dir);
774 if (phase == 1 && dest_dir && has_prefix (dir, base_dir))
776 base_len = strlen (base_dir);
777 dest_len = strlen (dest_dir);
779 memcpy (dir, dest_dir, dest_len);
780 if (dest_len < base_len)
782 memmove (dir + dest_len, dir + base_len,
783 strlen (dir + base_len) + 1);
785 elf_flagdata (debug_sections[DEBUG_STR].elf_data,
786 ELF_C_SET, ELF_F_DIRTY);
790 else if ((t->tag == DW_TAG_compile_unit
791 || t->tag == DW_TAG_partial_unit)
792 && t->attr[i].attr == DW_AT_name
793 && form == DW_FORM_strp
794 && debug_sections[DEBUG_STR].data)
798 name = (char *) debug_sections[DEBUG_STR].data
799 + do_read_32_relocated (ptr);
800 if (*name == '/' && comp_dir == NULL)
802 char *enddir = strrchr (name, '/');
806 comp_dir = malloc (enddir - name + 1);
807 memcpy (comp_dir, name, enddir - name);
808 comp_dir [enddir - name] = '\0';
811 comp_dir = strdup ("/");
814 if (phase == 1 && dest_dir && has_prefix (name, base_dir))
816 base_len = strlen (base_dir);
817 dest_len = strlen (dest_dir);
819 memcpy (name, dest_dir, dest_len);
820 if (dest_len < base_len)
822 memmove (name + dest_len, name + base_len,
823 strlen (name + base_len) + 1);
825 elf_flagdata (debug_sections[DEBUG_STR].elf_data,
826 ELF_C_SET, ELF_F_DIRTY);
832 case DW_FORM_ref_addr: /* ptr_size in DWARF 2, offset in DWARF 3 */
854 case DW_FORM_ref_udata:
862 ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1;
864 case DW_FORM_indirect:
865 form = read_uleb128 (ptr);
872 form = DW_FORM_block1;
876 form = DW_FORM_block1;
879 len = read_uleb128 (ptr);
880 form = DW_FORM_block1;
881 assert (len < UINT_MAX);
884 error (0, 0, "%s: Unknown DWARF-2 DW_FORM_%d", dso->filename,
889 if (form == DW_FORM_block1)
895 if (found_list_offs && comp_dir)
896 edit_dwarf2_line (dso, list_offs, comp_dir, phase);
904 rel_cmp (const void *a, const void *b)
906 REL *rela = (REL *) a, *relb = (REL *) b;
908 if (rela->ptr < relb->ptr)
911 if (rela->ptr > relb->ptr)
918 edit_dwarf2 (DSO *dso)
924 for (i = 0; debug_sections[i].name; ++i)
926 debug_sections[i].data = NULL;
927 debug_sections[i].size = 0;
928 debug_sections[i].sec = 0;
929 debug_sections[i].relsec = 0;
933 for (i = 1; i < dso->ehdr.e_shnum; ++i)
934 if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR))
935 && dso->shdr[i].sh_size)
937 const char *name = strptr (dso, dso->ehdr.e_shstrndx,
938 dso->shdr[i].sh_name);
940 if (strncmp (name, ".debug_", sizeof (".debug_") - 1) == 0)
942 for (j = 0; debug_sections[j].name; ++j)
943 if (strcmp (name, debug_sections[j].name) == 0)
945 if (debug_sections[j].data)
947 error (0, 0, "%s: Found two copies of %s section",
948 dso->filename, name);
953 data = elf_rawdata (scn, NULL);
954 assert (data != NULL && data->d_buf != NULL);
955 assert (elf_rawdata (scn, data) == NULL);
956 assert (data->d_off == 0);
957 assert (data->d_size == dso->shdr[i].sh_size);
958 debug_sections[j].data = data->d_buf;
959 debug_sections[j].elf_data = data;
960 debug_sections[j].size = data->d_size;
961 debug_sections[j].sec = i;
965 if (debug_sections[j].name == NULL)
967 error (0, 0, "%s: Unknown debugging section %s",
968 dso->filename, name);
971 else if (dso->ehdr.e_type == ET_REL
972 && ((dso->shdr[i].sh_type == SHT_REL
973 && strncmp (name, ".rel.debug_",
974 sizeof (".rel.debug_") - 1) == 0)
975 || (dso->shdr[i].sh_type == SHT_RELA
976 && strncmp (name, ".rela.debug_",
977 sizeof (".rela.debug_") - 1) == 0)))
979 for (j = 0; debug_sections[j].name; ++j)
980 if (strcmp (name + sizeof (".rel") - 1
981 + (dso->shdr[i].sh_type == SHT_RELA),
982 debug_sections[j].name) == 0)
984 debug_sections[j].relsec = i;
990 if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
992 do_read_16 = buf_read_ule16;
993 do_read_32 = buf_read_ule32;
994 write_32 = dwarf2_write_le32;
996 else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
998 do_read_16 = buf_read_ube16;
999 do_read_32 = buf_read_ube32;
1000 write_32 = dwarf2_write_be32;
1004 error (0, 0, "%s: Wrong ELF data enconding", dso->filename);
1008 if (debug_sections[DEBUG_INFO].data != NULL)
1010 unsigned char *ptr, *endcu, *endsec;
1013 struct abbrev_tag tag, *t;
1017 if (debug_sections[DEBUG_INFO].relsec)
1023 GElf_Addr base = dso->shdr[debug_sections[DEBUG_INFO].sec].sh_addr;
1024 Elf_Data *symdata = NULL;
1027 i = debug_sections[DEBUG_INFO].relsec;
1029 data = elf_getdata (scn, NULL);
1030 assert (data != NULL && data->d_buf != NULL);
1031 assert (elf_getdata (scn, data) == NULL);
1032 assert (data->d_off == 0);
1033 assert (data->d_size == dso->shdr[i].sh_size);
1034 maxndx = dso->shdr[i].sh_size / dso->shdr[i].sh_entsize;
1035 relbuf = malloc (maxndx * sizeof (REL));
1036 reltype = dso->shdr[i].sh_type;
1038 error (1, errno, "%s: Could not allocate memory", dso->filename);
1040 symdata = elf_getdata (dso->scn[dso->shdr[i].sh_link], NULL);
1041 assert (symdata != NULL && symdata->d_buf != NULL);
1042 assert (elf_getdata (dso->scn[dso->shdr[i].sh_link], symdata)
1044 assert (symdata->d_off == 0);
1045 assert (symdata->d_size
1046 == dso->shdr[dso->shdr[i].sh_link].sh_size);
1048 for (ndx = 0, relend = relbuf; ndx < maxndx; ++ndx)
1050 if (dso->shdr[i].sh_type == SHT_REL)
1052 gelf_getrel (data, ndx, &rel);
1053 rela.r_offset = rel.r_offset;
1054 rela.r_info = rel.r_info;
1058 gelf_getrela (data, ndx, &rela);
1059 gelf_getsym (symdata, ELF64_R_SYM (rela.r_info), &sym);
1060 /* Relocations against section symbols are uninteresting
1062 if (dso->shdr[i].sh_type == SHT_REL && sym.st_value == 0)
1064 /* Only consider relocations against .debug_str, .debug_line
1065 and .debug_abbrev. */
1066 if (sym.st_shndx != debug_sections[DEBUG_STR].sec
1067 && sym.st_shndx != debug_sections[DEBUG_LINE].sec
1068 && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec)
1070 rela.r_addend += sym.st_value;
1071 rtype = ELF64_R_TYPE (rela.r_info);
1072 switch (dso->ehdr.e_machine)
1075 case EM_SPARC32PLUS:
1077 if (rtype != R_SPARC_32 && rtype != R_SPARC_UA32)
1081 if (rtype != R_386_32)
1086 if (rtype != R_PPC_ADDR32 && rtype != R_PPC_UADDR32)
1090 if (rtype != R_390_32)
1094 if (rtype != R_IA64_SECREL32LSB)
1098 if (rtype != R_X86_64_32)
1102 if (rtype != R_ALPHA_REFLONG)
1107 error (1, 0, "%s: Unhandled relocation %d in .debug_info section",
1108 dso->filename, rtype);
1110 relend->ptr = debug_sections[DEBUG_INFO].data
1111 + (rela.r_offset - base);
1112 relend->addend = rela.r_addend;
1115 if (relbuf == relend)
1122 qsort (relbuf, relend - relbuf, sizeof (REL), rel_cmp);
1125 for (phase = 0; phase < 2; phase++)
1127 ptr = debug_sections[DEBUG_INFO].data;
1129 endsec = ptr + debug_sections[DEBUG_INFO].size;
1130 while (ptr < endsec)
1132 if (ptr + 11 > endsec)
1134 error (0, 0, "%s: .debug_info CU header too small",
1140 endcu += read_32 (ptr);
1141 if (endcu == ptr + 0xffffffff)
1143 error (0, 0, "%s: 64-bit DWARF not supported", dso->filename);
1149 error (0, 0, "%s: .debug_info too small", dso->filename);
1153 value = read_16 (ptr);
1156 error (0, 0, "%s: DWARF version %d unhandled", dso->filename,
1161 value = read_32_relocated (ptr);
1162 if (value >= debug_sections[DEBUG_ABBREV].size)
1164 if (debug_sections[DEBUG_ABBREV].data == NULL)
1165 error (0, 0, "%s: .debug_abbrev not present", dso->filename);
1167 error (0, 0, "%s: DWARF-2 CU abbrev offset too large",
1174 ptr_size = read_1 (ptr);
1175 if (ptr_size != 4 && ptr_size != 8)
1177 error (0, 0, "%s: Invalid DWARF-2 pointer size %d",
1178 dso->filename, ptr_size);
1182 else if (read_1 (ptr) != ptr_size)
1184 error (0, 0, "%s: DWARF-2 pointer size differs between CUs",
1189 abbrev = read_abbrev (dso,
1190 debug_sections[DEBUG_ABBREV].data + value);
1196 tag.entry = read_uleb128 (ptr);
1199 t = htab_find_with_hash (abbrev, &tag, tag.entry);
1202 error (0, 0, "%s: Could not find DWARF-2 abbreviation %d",
1203 dso->filename, tag.entry);
1204 htab_delete (abbrev);
1208 ptr = edit_attributes (dso, ptr, t, phase);
1213 htab_delete (abbrev);
1222 static struct poptOption optionsTable[] = {
1223 { "base-dir", 'b', POPT_ARG_STRING, &base_dir, 0,
1224 "base build directory of objects", NULL },
1225 { "dest-dir", 'd', POPT_ARG_STRING, &dest_dir, 0,
1226 "directory to rewrite base-dir into", NULL },
1227 { "list-file", 'l', POPT_ARG_STRING, &list_file, 0,
1228 "file where to put list of source and header file names", NULL },
1229 { "build-id", 'i', POPT_ARG_NONE, &do_build_id, 0,
1230 "recompute build ID note and print ID on stdout", NULL },
1232 { NULL, 0, 0, NULL, 0, NULL, NULL }
1236 fdopen_dso (int fd, const char *name)
1243 elf = elf_begin (fd, ELF_C_RDWR_MMAP, NULL);
1246 error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1));
1250 if (elf_kind (elf) != ELF_K_ELF)
1252 error (0, 0, "\"%s\" is not an ELF file", name);
1256 if (gelf_getehdr (elf, &ehdr) == NULL)
1258 error (0, 0, "cannot get the ELF header: %s",
1263 if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC && ehdr.e_type != ET_REL)
1265 error (0, 0, "\"%s\" is not a shared library", name);
1269 /* Allocate DSO structure. Leave place for additional 20 new section
1272 malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr)
1273 + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *));
1276 error (0, ENOMEM, "Could not open DSO");
1280 elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT);
1282 memset (dso, 0, sizeof(DSO));
1285 dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum + 20];
1287 for (i = 0; i < ehdr.e_shnum; ++i)
1289 dso->scn[i] = elf_getscn (elf, i);
1290 gelf_getshdr (dso->scn[i], dso->shdr + i);
1293 dso->filename = (const char *) strdup (name);
1299 free ((char *) dso->filename);
1309 static const pgpHashAlgo algorithms[] = { PGPHASHALGO_MD5,
1310 PGPHASHALGO_SHA1, PGPHASHALGO_SHA256, PGPHASHALGO_SHA384, PGPHASHALGO_SHA512 };
1312 /* Compute a fresh build ID bit-string from the editted file contents. */
1314 handle_build_id (DSO *dso, Elf_Data *build_id,
1315 size_t build_id_offset, size_t build_id_size)
1318 pgpHashAlgo algorithm;
1319 int i = sizeof(algorithms)/sizeof(algorithms[0]);
1320 void *digest = NULL;
1327 algorithm = algorithms[i];
1328 if (rpmDigestLength(algorithm) == build_id_size)
1333 fprintf (stderr, "Cannot handle %Zu-byte build ID\n", build_id_size);
1337 if (elf_update (dso->elf, ELF_C_NULL) < 0)
1339 fprintf (stderr, "Failed to update file: %s\n",
1340 elf_errmsg (elf_errno ()));
1344 /* Clear the old bits so they do not affect the new hash. */
1345 memset ((char *) build_id->d_buf + build_id_offset, 0, build_id_size);
1347 ctx = rpmDigestInit(algorithm, 0);
1349 /* Slurp the relevant header bits and section contents and feed them
1350 into the hash function. The only bits we ignore are the offset
1351 fields in ehdr and shdrs, since the semantically identical ELF file
1352 could be written differently if it doesn't change the phdr layout.
1353 We always use the GElf (i.e. Elf64) formats for the bits to hash
1354 since it is convenient. It doesn't matter whether this is an Elf32
1355 or Elf64 object, only that we are consistent in what bits feed the
1356 hash so it comes out the same for the same file contents. */
1364 Elf_Data x = { .d_version = EV_CURRENT, .d_buf = &u };
1366 x.d_type = ELF_T_EHDR;
1367 x.d_size = sizeof u.ehdr;
1369 u.ehdr.e_phoff = u.ehdr.e_shoff = 0;
1370 if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL)
1373 fprintf (stderr, "Failed to compute header checksum: %s\n",
1374 elf_errmsg (elf_errno ()));
1378 x.d_type = ELF_T_PHDR;
1379 x.d_size = sizeof u.phdr;
1380 for (i = 0; i < dso->ehdr.e_phnum; ++i)
1382 if (gelf_getphdr (dso->elf, i, &u.phdr) == NULL)
1384 if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL)
1386 rpmDigestUpdate(ctx, x.d_buf, x.d_size);
1389 x.d_type = ELF_T_SHDR;
1390 x.d_size = sizeof u.shdr;
1391 for (i = 0; i < dso->ehdr.e_shnum; ++i)
1392 if (dso->scn[i] != NULL)
1394 u.shdr = dso->shdr[i];
1395 u.shdr.sh_offset = 0;
1396 if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL)
1398 rpmDigestUpdate(ctx, x.d_buf, x.d_size);
1400 if (u.shdr.sh_type != SHT_NOBITS)
1402 Elf_Data *d = elf_rawdata (dso->scn[i], NULL);
1405 rpmDigestUpdate(ctx, d->d_buf, d->d_size);
1410 rpmDigestFinal(ctx, &digest, &len, 0);
1411 memcpy((unsigned char *)build_id->d_buf + build_id_offset, digest, build_id_size);
1414 elf_flagdata (build_id, ELF_C_SET, ELF_F_DIRTY);
1416 /* Now format the build ID bits in hex to print out. */
1418 const uint8_t * id = (uint8_t *)build_id->d_buf + build_id_offset;
1419 char *hex = pgpHexStr(id, build_id_size);
1426 main (int argc, char *argv[])
1431 poptContext optCon; /* context for parsing command-line options */
1434 struct stat stat_buf;
1436 Elf_Data *build_id = NULL;
1437 size_t build_id_offset = 0, build_id_size = 0;
1439 optCon = poptGetContext("debugedit", argc, (const char **)argv, optionsTable, 0);
1441 while ((nextopt = poptGetNextOpt (optCon)) > 0 || nextopt == POPT_ERROR_BADOPT)
1446 fprintf (stderr, "Error on option %s: %s.\nRun '%s --help' to see a full list of available command line options.\n",
1447 poptBadOption (optCon, 0),
1448 poptStrerror (nextopt),
1453 args = poptGetArgs (optCon);
1454 if (args == NULL || args[0] == NULL || args[1] != NULL)
1456 poptPrintHelp(optCon, stdout, 0);
1460 if (dest_dir != NULL)
1462 if (base_dir == NULL)
1464 fprintf (stderr, "You must specify a base dir if you specify a dest dir\n");
1467 if (strlen (dest_dir) > strlen (base_dir))
1469 fprintf (stderr, "Only dest dir longer than base dir not supported\n");
1474 /* Make sure there are trailing slashes in dirs */
1475 if (base_dir != NULL && base_dir[strlen (base_dir)-1] != '/')
1477 p = malloc (strlen (base_dir) + 2);
1478 strcpy (p, base_dir);
1483 if (dest_dir != NULL && dest_dir[strlen (dest_dir)-1] != '/')
1485 p = malloc (strlen (dest_dir) + 2);
1486 strcpy (p, dest_dir);
1492 if (list_file != NULL)
1494 list_file_fd = open (list_file, O_WRONLY|O_CREAT|O_APPEND, 0644);
1499 if (elf_version(EV_CURRENT) == EV_NONE)
1501 fprintf (stderr, "library out of date\n");
1505 if (stat(file, &stat_buf) < 0)
1507 fprintf (stderr, "Failed to open input file '%s': %s\n", file, strerror(errno));
1511 /* Make sure we can read and write */
1512 chmod (file, stat_buf.st_mode | S_IRUSR | S_IWUSR);
1514 fd = open (file, O_RDWR);
1517 fprintf (stderr, "Failed to open input file '%s': %s\n", file, strerror(errno));
1521 dso = fdopen_dso (fd, file);
1525 for (i = 1; i < dso->ehdr.e_shnum; i++)
1529 switch (dso->shdr[i].sh_type)
1532 name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name);
1533 /* TODO: Handle stabs */
1535 if (strcmp (name, ".stab") == 0)
1536 edit_stabs (dso, i);
1538 if (strcmp (name, ".debug_info") == 0)
1544 && build_id == NULL && (dso->shdr[i].sh_flags & SHF_ALLOC))
1546 /* Look for a build-ID note here. */
1547 Elf_Data *data = elf_rawdata (elf_getscn (dso->elf, i), NULL);
1551 .d_version = EV_CURRENT, .d_type = ELF_T_NHDR,
1552 .d_buf = &nh, .d_size = sizeof nh
1555 src.d_buf = data->d_buf;
1556 assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
1557 while ((char *) data->d_buf + data->d_size -
1558 (char *) src.d_buf > (int) sizeof nh
1559 && elf32_xlatetom (&dst, &src, dso->ehdr.e_ident[EI_DATA]))
1561 Elf32_Word len = sizeof nh + nh.n_namesz;
1562 len = (len + 3) & ~3;
1564 if (nh.n_namesz == sizeof "GNU" && nh.n_type == 3
1565 && !memcmp ((char *) src.d_buf + sizeof nh, "GNU", sizeof "GNU"))
1568 build_id_offset = (char *) src.d_buf + len -
1569 (char *) data->d_buf;
1570 build_id_size = nh.n_descsz;
1575 len = (len + 3) & ~3;
1576 src.d_buf = (char *) src.d_buf + len;
1585 if (do_build_id && build_id != NULL)
1586 handle_build_id (dso, build_id, build_id_offset, build_id_size);
1588 if (elf_update (dso->elf, ELF_C_WRITE) < 0)
1590 fprintf (stderr, "Failed to write file: %s\n", elf_errmsg (elf_errno()));
1593 if (elf_end (dso->elf) < 0)
1595 fprintf (stderr, "elf_end failed: %s\n", elf_errmsg (elf_errno()));
1600 /* Restore old access rights */
1601 chmod (file, stat_buf.st_mode);
1603 poptFreeContext (optCon);