/src/elfutils/libdwelf/dwelf_elf_gnu_build_id.c
Line | Count | Source |
1 | | /* Returns the build id if found in a NT_GNU_BUILD_ID note. |
2 | | Copyright (C) 2014 Red Hat, Inc. |
3 | | This file is part of elfutils. |
4 | | |
5 | | This file is free software; you can redistribute it and/or modify |
6 | | it under the terms of either |
7 | | |
8 | | * the GNU Lesser General Public License as published by the Free |
9 | | Software Foundation; either version 3 of the License, or (at |
10 | | your option) any later version |
11 | | |
12 | | or |
13 | | |
14 | | * the GNU General Public License as published by the Free |
15 | | Software Foundation; either version 2 of the License, or (at |
16 | | your option) any later version |
17 | | |
18 | | or both in parallel, as here. |
19 | | |
20 | | elfutils is distributed in the hope that it will be useful, but |
21 | | WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
23 | | General Public License for more details. |
24 | | |
25 | | You should have received copies of the GNU General Public License and |
26 | | the GNU Lesser General Public License along with this program. If |
27 | | not, see <http://www.gnu.org/licenses/>. */ |
28 | | |
29 | | #ifdef HAVE_CONFIG_H |
30 | | # include <config.h> |
31 | | #endif |
32 | | |
33 | | #include "libdwelfP.h" |
34 | | #include "libdwflP.h" |
35 | | |
36 | 8.96k | #define NO_VADDR ((GElf_Addr) -1l) |
37 | | |
38 | | static int |
39 | | check_notes (Elf_Data *data, GElf_Addr data_elfaddr, |
40 | | const void **build_id_bits, GElf_Addr *build_id_elfaddr, |
41 | | int *build_id_len) |
42 | 70.1k | { |
43 | 70.1k | size_t pos = 0; |
44 | 70.1k | GElf_Nhdr nhdr; |
45 | 70.1k | size_t name_pos; |
46 | 70.1k | size_t desc_pos; |
47 | 68.7M | while ((pos = gelf_getnote (data, pos, &nhdr, &name_pos, &desc_pos)) > 0) |
48 | 68.6M | if (nhdr.n_type == NT_GNU_BUILD_ID |
49 | 8.11k | && nhdr.n_namesz == sizeof "GNU" |
50 | 7.69k | && !memcmp (data->d_buf + name_pos, "GNU", sizeof "GNU")) |
51 | 7.18k | { |
52 | 7.18k | *build_id_bits = data->d_buf + desc_pos; |
53 | 7.18k | *build_id_elfaddr = (data_elfaddr == NO_VADDR |
54 | 7.18k | ? 0 : data_elfaddr + desc_pos); |
55 | 7.18k | *build_id_len = nhdr.n_descsz; |
56 | 7.18k | return 1; |
57 | 7.18k | } |
58 | 62.9k | return 0; |
59 | 70.1k | } |
60 | | |
61 | | /* Defined here for reuse. The dwelf interface doesn't care about the |
62 | | address of the note, but libdwfl does. */ |
63 | | static int |
64 | | find_elf_build_id (Dwfl_Module *mod, int e_type, Elf *elf, |
65 | | const void **build_id_bits, GElf_Addr *build_id_elfaddr, |
66 | | int *build_id_len) |
67 | 10.8k | { |
68 | 10.8k | size_t shstrndx = SHN_UNDEF; |
69 | 10.8k | int result = 0; |
70 | | |
71 | 10.8k | Elf_Scn *scn = elf_nextscn (elf, NULL); |
72 | | |
73 | 10.8k | if (scn == NULL) |
74 | 1.59k | { |
75 | | /* No sections, have to look for phdrs. */ |
76 | 1.59k | size_t phnum; |
77 | 1.59k | if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) |
78 | 0 | { |
79 | 0 | if (mod != NULL) |
80 | 0 | __libdwfl_seterrno (DWFL_E_LIBELF); |
81 | 0 | return -1; |
82 | 0 | } |
83 | 3.32M | for (size_t i = 0; result == 0 && i < phnum; ++i) |
84 | 3.32M | { |
85 | 3.32M | GElf_Phdr phdr_mem; |
86 | 3.32M | GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); |
87 | 3.32M | if (likely (phdr != NULL) && phdr->p_type == PT_NOTE) |
88 | 52.5k | result = check_notes (elf_getdata_rawchunk (elf, |
89 | 52.5k | phdr->p_offset, |
90 | 52.5k | phdr->p_filesz, |
91 | 52.5k | (phdr->p_align == 8 |
92 | 52.5k | ? ELF_T_NHDR8 |
93 | 52.5k | : ELF_T_NHDR)), |
94 | 52.5k | phdr->p_vaddr, |
95 | 52.5k | build_id_bits, |
96 | 52.5k | build_id_elfaddr, |
97 | 52.5k | build_id_len); |
98 | 3.32M | } |
99 | 1.59k | } |
100 | 9.27k | else |
101 | 9.27k | do |
102 | 314k | { |
103 | 314k | GElf_Shdr shdr_mem; |
104 | 314k | GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); |
105 | 314k | if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE) |
106 | 17.5k | { |
107 | | /* Determine the right sh_addr in this module. */ |
108 | 17.5k | GElf_Addr vaddr = 0; |
109 | 17.5k | if (!(shdr->sh_flags & SHF_ALLOC)) |
110 | 1.78k | vaddr = NO_VADDR; |
111 | 15.7k | else if (mod == NULL || e_type != ET_REL) |
112 | 15.7k | vaddr = shdr->sh_addr; |
113 | 0 | else if (__libdwfl_relocate_value (mod, elf, &shstrndx, |
114 | 0 | elf_ndxscn (scn), &vaddr)) |
115 | 0 | vaddr = NO_VADDR; |
116 | 17.5k | result = check_notes (elf_getdata (scn, NULL), vaddr, |
117 | 17.5k | build_id_bits, |
118 | 17.5k | build_id_elfaddr, |
119 | 17.5k | build_id_len); |
120 | 17.5k | } |
121 | 314k | } |
122 | 314k | while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL); |
123 | | |
124 | 10.8k | return result; |
125 | 10.8k | } |
126 | | |
127 | | int |
128 | | internal_function |
129 | | __libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf, |
130 | | const void **build_id_bits, |
131 | | GElf_Addr *build_id_elfaddr, int *build_id_len) |
132 | 8.02k | { |
133 | 8.02k | GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); |
134 | 8.02k | if (unlikely (ehdr == NULL)) |
135 | 0 | { |
136 | 0 | __libdwfl_seterrno (DWFL_E_LIBELF); |
137 | 0 | return -1; |
138 | 0 | } |
139 | | // MOD->E_TYPE is zero here. |
140 | 8.02k | assert (ehdr->e_type != ET_REL || mod != NULL); |
141 | | |
142 | 8.02k | return find_elf_build_id (mod, ehdr->e_type, elf, |
143 | 8.02k | build_id_bits, build_id_elfaddr, build_id_len); |
144 | 8.02k | } |
145 | | |
146 | | ssize_t |
147 | | dwelf_elf_gnu_build_id (Elf *elf, const void **build_idp) |
148 | 2.85k | { |
149 | 2.85k | GElf_Addr build_id_elfaddr; |
150 | 2.85k | int build_id_len; |
151 | 2.85k | int result = find_elf_build_id (NULL, ET_NONE, elf, build_idp, |
152 | 2.85k | &build_id_elfaddr, &build_id_len); |
153 | 2.85k | if (result > 0) |
154 | 2.85k | return build_id_len; |
155 | | |
156 | 0 | return result; |
157 | 2.85k | } |
158 | | INTDEF(dwelf_elf_gnu_build_id) |