1 /*
2 checksum.c - implementation of the elf{32,64}_checksum(3) functions.
3 Copyright (C) 1995 - 2001 Michael Riepe
4 
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9 
10 This library 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 GNU
13 Library General Public License for more details.
14 
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19 
20 #include <private.h>
21 
22 #ifndef lint
23 static const char rcsid[] = "@(#) $Id: checksum.c,v 1.7 2008/05/23 08:15:34 michael Exp $";
24 #endif /* lint */
25 
26 /*
27  * Compatibility note:
28  *
29  * The algorithm used in {elf32,elf64,gelf}_checksum() does not seem to
30  * be documented.  I hope I got it right.  My implementation does the
31  * following:
32  *
33  *   - skip sections that do not have the SHF_ALLOC flag set
34  *   - skip sections of type SHT_NULL, SHT_NOBITS, SHT_DYNSYM and
35  *     SHT_DYNAMIC
36  *   - add all data bytes from the remaining sections, modulo 2**32
37  *   - add upper and lower half of the result
38  *   - subtract 0xffff if the result is > 0xffff
39  *   - if any error occurs, return 0L
40  */
41 
42 static int
skip_section(Elf_Scn * scn,unsigned cls)43 skip_section(Elf_Scn *scn, unsigned cls) {
44     if (cls == ELFCLASS32) {
45 	Elf32_Shdr *shdr = &scn->s_shdr32;
46 
47 	if (!(shdr->sh_flags & SHF_ALLOC)) {
48 	    return 1;
49 	}
50 	switch (shdr->sh_type) {
51 	    case SHT_NULL:
52 	    case SHT_NOBITS:
53 	    /* Solaris seems to ignore these, too */
54 	    case SHT_DYNSYM:
55 	    case SHT_DYNAMIC:
56 		return 1;
57 	}
58     }
59 #if __LIBELF64
60     else if (cls == ELFCLASS64) {
61 	Elf64_Shdr *shdr = &scn->s_shdr64;
62 
63 	if (!(shdr->sh_flags & SHF_ALLOC)) {
64 	    return 1;
65 	}
66 	switch (shdr->sh_type) {
67 	    case SHT_NULL:
68 	    case SHT_NOBITS:
69 	    /* Solaris seems to ignore these, too */
70 	    case SHT_DYNSYM:
71 	    case SHT_DYNAMIC:
72 		return 1;
73 	}
74     }
75 #endif /* __LIBELF64 */
76     else {
77 	seterr(ERROR_UNIMPLEMENTED);
78     }
79     return 0;
80 }
81 
82 static long
add_bytes(unsigned char * ptr,size_t len)83 add_bytes(unsigned char *ptr, size_t len) {
84     long csum = 0;
85 
86     while (len--) {
87 	csum += *ptr++;
88     }
89     return csum;
90 }
91 
92 static long
_elf_csum(Elf * elf)93 _elf_csum(Elf *elf) {
94     long csum = 0;
95     Elf_Data *data;
96     Elf_Scn *scn;
97 
98     if (!elf->e_ehdr && !_elf_cook(elf)) {
99 	/* propagate errors from _elf_cook */
100 	return 0L;
101     }
102     seterr(0);
103     for (scn = elf->e_scn_1; scn; scn = scn->s_link) {
104 	if (scn->s_index == SHN_UNDEF || skip_section(scn, elf->e_class)) {
105 	    continue;
106 	}
107 	data = NULL;
108 	while ((data = elf_getdata(scn, data))) {
109 	    if (data->d_size) {
110 		if (data->d_buf == NULL) {
111 		    seterr(ERROR_NULLBUF);
112 		    return 0L;
113 		}
114 		csum += add_bytes(data->d_buf, data->d_size);
115 	    }
116 	}
117     }
118     if (_elf_errno) {
119 	return 0L;
120     }
121     csum = (csum & 0xffff) + ((csum >> 16) & 0xffff);
122     if (csum > 0xffff) {
123 	csum -= 0xffff;
124     }
125     return csum;
126 }
127 
128 long
elf32_checksum(Elf * elf)129 elf32_checksum(Elf *elf) {
130     if (elf) {
131 	if (elf->e_kind != ELF_K_ELF) {
132 	    seterr(ERROR_NOTELF);
133 	}
134 	else if (elf->e_class != ELFCLASS32) {
135 	    seterr(ERROR_CLASSMISMATCH);
136 	}
137 	else {
138 	    return _elf_csum(elf);
139 	}
140     }
141     return 0L;
142 }
143 
144 #if __LIBELF64
145 
146 long
elf64_checksum(Elf * elf)147 elf64_checksum(Elf *elf) {
148     if (elf) {
149 	if (elf->e_kind != ELF_K_ELF) {
150 	    seterr(ERROR_NOTELF);
151 	}
152 	else if (elf->e_class != ELFCLASS64) {
153 	    seterr(ERROR_CLASSMISMATCH);
154 	}
155 	else {
156 	    return _elf_csum(elf);
157 	}
158     }
159     return 0L;
160 }
161 
162 long
gelf_checksum(Elf * elf)163 gelf_checksum(Elf *elf) {
164     if (elf) {
165 	if (elf->e_kind != ELF_K_ELF) {
166 	    seterr(ERROR_NOTELF);
167 	}
168 	else if (!valid_class(elf->e_class)) {
169 	    seterr(ERROR_UNKNOWN_CLASS);
170 	}
171 	else {
172 	    return _elf_csum(elf);
173 	}
174     }
175     return 0L;
176 }
177 
178 #endif /* __LIBELF64 */
179