1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30/*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
31/*	  All Rights Reserved	*/
32
33#pragma ident	"@(#)checksum.c	1.7	08/05/31 SMI"
34
35#include <errno.h>
36#include <libelf.h>
37#include "decl.h"
38#include "msg.h"
39
40/*
41 * Routines for generating a checksum for an elf image. Typically used to create
42 * a DT_CHECKSUM entry.  This checksum is intended to remain constant after
43 * operations such as strip(1)/mcs(1), thus only allocatable sections are
44 * processed, and of those, any that might be modified by these external
45 * commands are skipped.
46 */
47#define	MSW(l)	(((l) >> 16) & 0x0000ffffL)
48#define	LSW(l)	((l) & 0x0000ffffL)
49
50
51/*
52 * update and epilogue sum functions (stolen from libcmd)
53 */
54static long
55sumupd(long sum, char *cp, unsigned long cnt)
56{
57	if ((cp == 0) || (cnt == 0))
58		return (sum);
59
60	while (cnt--)
61		sum += *cp++ & 0x00ff;
62
63	return (sum);
64}
65
66static long
67sumepi(long sum)
68{
69	long	_sum;
70
71	_sum = LSW(sum) + MSW(sum);
72	return ((ushort_t)(LSW(_sum) + MSW(_sum)));
73}
74
75long
76elf32_checksum(Elf * elf)
77{
78	long		sum = 0;
79	Elf32_Ehdr *	ehdr;
80	Elf32_Shdr *	shdr;
81	Elf_Scn *	scn;
82	Elf_Data *	data, * (* getdata)(Elf_Scn *, Elf_Data *);
83	size_t		shnum;
84
85	if ((ehdr = elf32_getehdr(elf)) == 0)
86		return (0);
87
88	/*
89	 * Determine the data information to retrieve.  When called from ld()
90	 * we're processing an ELF_C_IMAGE (memory) image and thus need to use
91	 * elf_getdata(), as there is not yet a file image (or raw data) backing
92	 * this.  When called from utilities like elfdump(1) we're processing a
93	 * file image and thus using the elf_rawdata() allows the same byte
94	 * stream to be processed from different architectures - presently this
95	 * is irrelevant, as the checksum simply sums the data bytes, their
96	 * order doesn't matter.  But being uncooked is slightly less overhead.
97	 *
98	 * If the file is writable, the raw data will not reflect any
99	 * changes made in the process, so the uncooked version is only
100	 * for readonly files.
101	 */
102	if ((elf->ed_myflags & (EDF_MEMORY | EDF_WRITE)) != 0)
103		getdata = elf_getdata;
104	else
105		getdata = elf_rawdata;
106
107	for (shnum = 1; shnum < ehdr->e_shnum; shnum++) {
108		if ((scn = elf_getscn(elf, shnum)) == 0)
109			return (0);
110		if ((shdr = elf32_getshdr(scn)) == 0)
111			return (0);
112
113		if (!(shdr->sh_flags & SHF_ALLOC))
114			continue;
115
116		if ((shdr->sh_type == SHT_DYNSYM) ||
117		    (shdr->sh_type == SHT_DYNAMIC))
118			continue;
119
120		data = 0;
121		while ((data = (*getdata)(scn, data)) != 0)
122			sum = sumupd(sum, data->d_buf, data->d_size);
123
124	}
125	return (sumepi(sum));
126}
127
128long
129elf64_checksum(Elf * elf)
130{
131	long		sum = 0;
132	Elf64_Ehdr *	ehdr;
133	Elf64_Shdr *	shdr;
134	Elf_Scn *	scn;
135	Elf_Data *	data;
136	size_t		shnum;
137
138	if ((ehdr = elf64_getehdr(elf)) == 0)
139		return (0);
140
141	for (shnum = 1; shnum < ehdr->e_shnum; shnum++) {
142		if ((scn = elf_getscn(elf, shnum)) == 0)
143			return (0);
144		if ((shdr = elf64_getshdr(scn)) == 0)
145			return (0);
146
147		/* Exclude strippable sections */
148		if (!(shdr->sh_flags & SHF_ALLOC))
149			continue;
150
151		/*
152		 * Exclude allocable sections that can change:
153		 *	- The .dynsym section can contain section symbols
154		 *		that strip might remove.
155		 *	- The .dynamic section is modified by the setting of
156		 *		this checksum value.
157		 */
158		if ((shdr->sh_type == SHT_DYNSYM) ||
159		    (shdr->sh_type == SHT_DYNAMIC))
160			continue;
161
162		data = 0;
163		while ((data = elf_getdata(scn, data)) != 0)
164			sum = sumupd(sum, data->d_buf, data->d_size);
165
166	}
167	return (sumepi(sum));
168}
169