dcom.c revision 9900:1b86d65a4f9e
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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27/*
28 * dcom: Delete Comment
29 *
30 * This program demonstrates the use of libelf interface to
31 * copy the contents of one ELF file to create a new one.
32 * dcom creates a new ELF file using elf_begin(ELF_C_WRITE).
33 *
34 * In order to delete a section from an ELF file you must
35 * instead create a new ELF file and copy all but the 'selected'
36 * sections to the new ELF file.  This is because libelf is
37 * unable to delete any sections from an ELF file, it can
38 * only add them.
39 *
40 * NOTE: While this program works fine for simple ELF objects,
41 * as they get more complex it may not properly update all of the
42 * fields required.  This program is *only* an example of how
43 * to do this and not a complete program in itself.
44 */
45
46
47#include <stdio.h>
48#include <libelf.h>
49#include <gelf.h>
50#include <fcntl.h>
51#include <string.h>
52#include <stdlib.h>
53#include <unistd.h>
54#include <sys/types.h>
55#include <sys/stat.h>
56#include <sys/param.h>
57
58
59static const char *CommentStr = ".comment";
60
61/*
62 * Build a temporary file name that is in the
63 * same directory as the elf file being processed.
64 */
65static char *
66mkname(const char *bname)
67{
68	char	*ptr;
69	char	buffer[MAXPATHLEN];
70
71	ptr = strcpy(buffer, bname);
72	ptr += strlen(buffer);
73	while (ptr >= buffer) {
74		if (*ptr == '/') {
75			*(ptr + 1) = '\0';
76			break;
77		}
78		ptr--;
79	}
80	if (ptr < buffer) {
81		buffer[0] = '.';
82		buffer[1] = '\0';
83	}
84	return (tempnam(buffer, 0));
85}
86
87
88
89static void
90delete_comment(Elf *elf, int fd, const char *file)
91{
92	GElf_Ehdr	ehdr;
93	Elf_Scn		*scn = 0;
94	char		*tfile;
95	Elf		*telf;
96	int		tfd;
97	GElf_Ehdr	tehdr;
98	GElf_Phdr	phdr;
99	GElf_Phdr	tphdr;
100	size_t		shstrndx;
101	size_t		shnum;
102	size_t		phnum;
103	int		*shndx;
104	int		ndx = 1;
105	int		off = 0;
106	struct stat	sbuf;
107
108	if (gelf_getehdr(elf, &ehdr) == 0) {
109		(void) fprintf(stderr, "%s: elf_getehdr() failed: %s\n",
110		    file, elf_errmsg(0));
111		return;
112	}
113
114	if (elf_getshdrnum(elf, &shnum) == -1) {
115		(void) fprintf(stderr, "%s: elf_getshdrnum() failed: %s\n",
116		    file, elf_errmsg(0));
117		return;
118	}
119
120	if (elf_getshdrstrndx(elf, &shstrndx) == -1) {
121		(void) fprintf(stderr, "%s: elf_getshdrstrndx() failed: %s\n",
122		    file, elf_errmsg(0));
123		return;
124	}
125
126	if (elf_getphdrnum(elf, &phnum) == -1) {
127		(void) fprintf(stderr, "%s: elf_getphdrnum() failed: %s\n",
128		    file, elf_errmsg(0));
129		return;
130	}
131
132	/*
133	 * shndx is an array used to map the current section
134	 * indexes to the new section indexes.
135	 */
136	shndx = calloc(shnum, sizeof (int));
137
138	while ((scn = elf_nextscn(elf, scn)) != 0) {
139		GElf_Shdr	shdr;
140
141		/*
142		 * Do a string compare to examine each section header
143		 * to see if it is a ".comment" section.  If it is then
144		 * this is the section we want to process.
145		 */
146		if (gelf_getshdr(scn, &shdr) == 0) {
147			(void) fprintf(stderr, "%s: elf_getshdr() failed: %s\n",
148			    file, elf_errmsg(0));
149			free(shndx);
150			return;
151		}
152		if (strcmp(CommentStr, elf_strptr(elf, shstrndx,
153		    shdr.sh_name)) == 0) {
154			shndx[ndx] = -1;
155			off++;
156
157			/*
158			 * If the .comment section is part of a loadable
159			 * segment then it can not be delted from the
160			 * ELF file.
161			 */
162			if (shdr.sh_addr != 0) {
163				(void) printf("%s: .comment section is "
164				    "part of a loadable segment, it "
165				    "cannot be deleted.\n", file);
166				free(shndx);
167				return;
168			}
169		} else
170			shndx[ndx] = ndx - off;
171		ndx++;
172	}
173
174	/*
175	 * obtain a unique file name and open a file descriptor
176	 * pointing to that file.
177	 */
178	tfile = mkname(file);
179	if ((tfd = open(tfile, O_RDWR | O_CREAT, 0600)) == -1) {
180		perror("temp open");
181		return;
182	}
183
184	/*
185	 * Create a new ELF to duplicate the ELF file into.
186	 */
187	if ((telf = elf_begin(tfd, ELF_C_WRITE, 0)) == 0) {
188		(void) fprintf(stderr, "elf_begin(ELF_C_WRITE) failed: %s\n",
189		    elf_errmsg(0));
190		return;
191	}
192
193	if (gelf_newehdr(telf, gelf_getclass(elf)) == 0) {
194		(void) fprintf(stderr, "%s: elf_newehdr() failed: %s\n",
195		    file, elf_errmsg(0));
196		free(shndx);
197		return;
198	}
199	if (gelf_getehdr(telf, &tehdr) == 0) {
200		(void) fprintf(stderr, "%s: elf_getehdr() failed: %s\n",
201		    file, elf_errmsg(0));
202		free(shndx);
203		return;
204	}
205
206	scn = 0;
207	ndx = 1;
208	while ((scn = elf_nextscn(elf, scn)) != 0) {
209		Elf_Scn *	tscn;
210		Elf_Data *	data;
211		Elf_Data *	tdata;
212		GElf_Shdr	shdr;
213		GElf_Shdr	tshdr;
214
215		if (shndx[ndx] == -1) {
216			ndx++;
217			continue;
218		}
219
220		/*
221		 * Duplicate all but the .comment section in the
222		 * new file.
223		 */
224		if (gelf_getshdr(scn, &shdr) == 0) {
225			(void) fprintf(stderr, "%s: elf_getshdr() failed: %s\n",
226			    file, elf_errmsg(0));
227			free(shndx);
228			return;
229		}
230		if ((tscn = elf_newscn(telf)) == 0) {
231			(void) fprintf(stderr, "%s: elf_newscn() failed: %s\n",
232			    file, elf_errmsg(0));
233			free(shndx);
234			return;
235		}
236		if (gelf_getshdr(tscn, &tshdr) == 0) {
237			(void) fprintf(stderr, "%s: elf_getshdr() failed: %s\n",
238			    file, elf_errmsg(0));
239			free(shndx);
240			return;
241		}
242		tshdr = shdr;
243		tshdr.sh_link = shndx[shdr.sh_link];
244
245		/*
246		 * The relocation sections sh_info field also contains
247		 * a section index that needs to be adjusted.  This is
248		 * the only section who's sh_info field contains
249		 * a section index according to the ABI.
250		 *
251		 * If their are non-ABI sections who's sh_info field
252		 * contains section indexes they will not properly
253		 * be updated by this routine.
254		 */
255		if (shdr.sh_type == SHT_REL)
256			tshdr.sh_info = shndx[ndx];
257
258		/*
259		 * Flush the changes to the underlying elf32 or elf64
260		 * section header.
261		 */
262		gelf_update_shdr(tscn, &tshdr);
263
264		if ((data = elf_getdata(scn, 0)) == 0) {
265			(void) fprintf(stderr, "%s: elf_getdata() failed: %s\n",
266			    file, elf_errmsg(0));
267			free(shndx);
268			return;
269		}
270		if ((tdata = elf_newdata(tscn)) == 0) {
271			(void) fprintf(stderr, "%s: elf_newdata() failed: %s\n",
272			    file, elf_errmsg(0));
273			free(shndx);
274			return;
275		}
276		*tdata = *data;
277		ndx++;
278	}
279
280	tehdr = ehdr;
281	if (shndx[shstrndx] < SHN_LORESERVE)
282		tehdr.e_shstrndx = shndx[shstrndx];
283	else {
284		Elf_Scn		*_scn;
285		GElf_Shdr	shdr0;
286		/*
287		 * 'ELF Extended Sections' are enabled - we must
288		 * store the shstrndx in Shdr[0].sh_link
289		 */
290		if ((_scn = elf_getscn(telf, 0)) == 0) {
291			(void) fprintf(stderr, "%s: elf_getscn() failed: %s\n",
292			    file, elf_errmsg(0));
293			free(shndx);
294			return;
295		}
296		if (gelf_getshdr(_scn, &shdr0) == 0) {
297			(void) fprintf(stderr, "%s: elf_getshdr() failed: %s\n",
298			    file, elf_errmsg(0));
299			free(shndx);
300			return;
301		}
302		tehdr.e_shstrndx = SHN_XINDEX;
303		shdr0.sh_link = shndx[shstrndx];
304		gelf_update_shdr(_scn, &shdr0);
305	}
306	gelf_update_ehdr(telf, &tehdr);
307
308	free(shndx);
309
310	/*
311	 * Duplicate all program headers contained in the ELF file.
312	 */
313	if (phnum != 0) {
314		if (gelf_newphdr(telf, phnum) == 0) {
315			(void) fprintf(stderr, "%s: elf_newphdr() failed: %s\n",
316			    file, elf_errmsg(0));
317			return;
318		}
319		for (ndx = 0; ndx < (int)phnum; ndx++) {
320			if (gelf_getphdr(elf, ndx, &phdr) == 0 ||
321			    gelf_getphdr(telf, ndx, &tphdr) == 0) {
322				(void) fprintf(stderr,
323				    "%s: elf_getphdr() failed: %s\n",
324				    file, elf_errmsg(0));
325				return;
326			}
327			tphdr = phdr;
328			gelf_update_phdr(telf, ndx, &tphdr);
329		}
330	}
331
332	/*
333	 * The new Elf file has now been fully described to libelf.
334	 * elf_update() will construct the new Elf file and write
335	 * it out to disk.
336	 */
337	if (elf_update(telf, ELF_C_WRITE) == -1) {
338		(void) fprintf(stderr, "elf_update() failed: %s\n",
339		    elf_errmsg(0));
340		(void) elf_end(telf);
341		(void) close(tfd);
342		return;
343	}
344	(void) elf_end(telf);
345
346	/*
347	 * set new files permissions to the original files
348	 * permissions.
349	 */
350	(void) fstat(fd, &sbuf);
351	(void) fchmod(tfd, sbuf.st_mode);
352
353	(void) close(tfd);
354
355	/*
356	 * delete the original file and rename the new file
357	 * to the orignal file.
358	 */
359	(void) rename(tfile, file);
360}
361
362
363int
364main(int argc, char ** argv)
365{
366	int	i;
367
368	if (argc < 2) {
369		(void) printf("usage: %s elf_file ...\n", argv[0]);
370		return (1);
371	}
372
373	/*
374	 * Initialize the elf library, must be called before elf_begin()
375	 * can be called.
376	 */
377	if (elf_version(EV_CURRENT) == EV_NONE) {
378		(void) fprintf(stderr, "elf_version() failed: %s\n",
379		    elf_errmsg(0));
380		return (1);
381	}
382
383	for (i = 1; i < argc; i++) {
384		int	fd;
385		Elf	*elf;
386		char	*elf_fname;
387
388		elf_fname = argv[i];
389
390		if ((fd = open(elf_fname, O_RDONLY)) == -1) {
391			perror("open");
392			continue;
393		}
394
395		/*
396		 * Attempt to open an Elf descriptor Read/Write
397		 * for each file.
398		 */
399		if ((elf = elf_begin(fd, ELF_C_READ, 0)) == NULL) {
400			(void) fprintf(stderr, "elf_begin() failed: %s\n",
401			    elf_errmsg(0));
402			(void) close(fd);
403			continue;
404		}
405
406		/*
407		 * Determine what kind of elf file this is:
408		 */
409		if (elf_kind(elf) != ELF_K_ELF) {
410			/*
411			 * can only delete comment sections from
412			 * ELF files.
413			 */
414			(void) printf("%s not of type ELF_K_ELF.  "
415			    "elf_kind == %d\n", elf_fname, elf_kind(elf));
416		} else
417			delete_comment(elf, fd, elf_fname);
418
419		(void) elf_end(elf);
420		(void) close(fd);
421	}
422
423	return (0);
424}
425