1/*
2 * Copyright (c) 1999 Global Technology Associates, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
19 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#define	_KERNEL
30#include <sys/param.h>
31#undef _KERNEL
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/wait.h>
35
36#include <err.h>
37#include <fcntl.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <unistd.h>
41
42#include <a.out.h>
43
44#include "aouthdr.h"
45#include "elfhdr.h"
46#include "kgzip.h"
47
48static void mk_data(const struct iodesc *i, const struct iodesc *,
49		    struct kgz_hdr *, size_t);
50static int ld_elf(const struct iodesc *, const struct iodesc *,
51		  struct kgz_hdr *, const Elf32_Ehdr *);
52static int ld_aout(const struct iodesc *, const struct iodesc *,
53		   struct kgz_hdr *, const struct exec *);
54
55/*
56 * Compress executable and output it in relocatable object format.
57 */
58void
59kgzcmp(struct kgz_hdr *kh, const char *f1, const char *f2)
60{
61    struct iodesc idi, ido;
62    struct kgz_hdr khle;
63
64    if ((idi.fd = open(idi.fname = f1, O_RDONLY)) == -1)
65	err(1, "%s", idi.fname);
66    if ((ido.fd = open(ido.fname = f2, O_CREAT | O_TRUNC | O_WRONLY,
67		       0666)) == -1)
68	err(1, "%s", ido.fname);
69    kh->ident[0] = KGZ_ID0;
70    kh->ident[1] = KGZ_ID1;
71    kh->ident[2] = KGZ_ID2;
72    kh->ident[3] = KGZ_ID3;
73    mk_data(&idi, &ido, kh,
74	    (format == F_AOUT ? sizeof(struct kgz_aouthdr0) :
75				sizeof(struct kgz_elfhdr)) +
76	     sizeof(struct kgz_hdr));
77    kh->dload &= 0xffffff;
78    kh->entry &= 0xffffff;
79    if (format == F_AOUT) {
80	struct kgz_aouthdr0 ahdr0 = aouthdr0;
81	struct kgz_aouthdr1 ahdr1 = aouthdr1;
82	unsigned x = (sizeof(struct kgz_hdr) + kh->nsize) & (16 - 1);
83	if (x) {
84	    x = 16 - x;
85	    xzero(&ido, x);
86	}
87	xwrite(&ido, &ahdr1, sizeof(ahdr1));
88	ahdr0.a.a_data += kh->nsize + x;
89	xseek(&ido, 0);
90	xwrite(&ido, &ahdr0, sizeof(ahdr0));
91    } else {
92	struct kgz_elfhdr ehdr = elfhdr;
93	ehdr.st[KGZ_ST_KGZ_NDATA].st_size = htole32(kh->nsize);
94	ehdr.sh[KGZ_SH_DATA].sh_size =
95	    htole32(le32toh(ehdr.sh[KGZ_SH_DATA].sh_size) + kh->nsize);
96	xseek(&ido, 0);
97	xwrite(&ido, &ehdr, sizeof(ehdr));
98    }
99    khle = *kh;
100    khle.dload = htole32(khle.dload);
101    khle.dsize = htole32(khle.dsize);
102    khle.isize = htole32(khle.isize);
103    khle.entry = htole32(khle.entry);
104    khle.nsize = htole32(khle.nsize);
105    xwrite(&ido, &khle, sizeof(khle));
106    xclose(&ido);
107    xclose(&idi);
108}
109
110/*
111 * Make encoded (compressed) data.
112 */
113static void
114mk_data(const struct iodesc * idi, const struct iodesc * ido,
115	struct kgz_hdr * kh, size_t off)
116{
117    union {
118	struct exec ex;
119	Elf32_Ehdr ee;
120    } hdr;
121    struct stat sb;
122    struct iodesc idp;
123    int fd[2];
124    pid_t pid;
125    size_t n;
126    int fmt, status, e;
127
128    n = xread(idi, &hdr, sizeof(hdr), 0);
129    fmt = 0;
130    if (n >= sizeof(hdr.ee) && IS_ELF(hdr.ee))
131	fmt = F_ELF;
132    else if (n >= sizeof(hdr.ex) && N_GETMAGIC(hdr.ex) == ZMAGIC)
133	fmt = F_AOUT;
134    if (!fmt)
135	errx(1, "%s: Format not supported", idi->fname);
136    xseek(ido, off);
137    if (pipe(fd))
138	err(1, NULL);
139    switch (pid = fork()) {
140    case -1:
141	err(1, NULL);
142    case 0:
143	close(fd[1]);
144	dup2(fd[0], STDIN_FILENO);
145	close(fd[0]);
146	close(idi->fd);
147	dup2(ido->fd, STDOUT_FILENO);
148	close(ido->fd);
149	execlp("gzip", "gzip", "-9n", (char *)NULL);
150	warn(NULL);
151	_exit(1);
152    default:
153	close(fd[0]);
154	idp.fname = "(pipe)";
155	idp.fd = fd[1];
156	e = fmt == F_ELF  ? ld_elf(idi, &idp, kh, &hdr.ee) :
157	    fmt == F_AOUT ? ld_aout(idi, &idp, kh, &hdr.ex) : -1;
158	close(fd[1]);
159	if ((pid = waitpid(pid, &status, 0)) == -1)
160	    err(1, NULL);
161	if (WIFSIGNALED(status) || WEXITSTATUS(status))
162	    exit(1);
163    }
164    if (e)
165	errx(1, "%s: Invalid format", idi->fname);
166    if (fstat(ido->fd, &sb))
167	err(1, "%s", ido->fname);
168    kh->nsize = sb.st_size - off;
169}
170
171/*
172 * "Load" an ELF-format executable.
173 */
174static int
175ld_elf(const struct iodesc * idi, const struct iodesc * ido,
176       struct kgz_hdr * kh, const Elf32_Ehdr * e)
177{
178    Elf32_Phdr p;
179    size_t load, addr, n;
180    unsigned x, i;
181
182    load = addr = n = 0;
183    for (x = i = 0; i < e->e_phnum; i++) {
184	if (xread(idi, &p, sizeof(p),
185		  e->e_phoff + i * e->e_phentsize) != e->e_phentsize)
186	    return -1;
187	if (p.p_type != PT_LOAD)
188	    continue;
189	if (!x)
190	    load = addr = p.p_vaddr;
191	else {
192	    if (p.p_vaddr < addr)
193		return -1;
194	    n = p.p_vaddr - addr;
195	    if (n) {
196		xzero(ido, n);
197		addr += n;
198	    }
199	}
200	if (p.p_memsz < p.p_filesz)
201	    return -1;
202	n = p.p_memsz - p.p_filesz;
203	xcopy(idi, ido, p.p_filesz, p.p_offset);
204	addr += p.p_filesz;
205	x++;
206    }
207    if (!x)
208	return -1;
209    kh->dload = load;
210    kh->dsize = addr - load;
211    kh->isize = kh->dsize + n;
212    kh->entry = e->e_entry;
213    return 0;
214}
215
216/*
217 * "Load" an a.out-format executable.
218 */
219static int
220ld_aout(const struct iodesc * idi, const struct iodesc * ido,
221	struct kgz_hdr * kh, const struct exec * a)
222{
223    size_t load, addr;
224
225    load = addr = N_TXTADDR(*a);
226    xcopy(idi, ido, le32toh(a->a_text), N_TXTOFF(*a));
227    addr += le32toh(a->a_text);
228    if (N_DATADDR(*a) != addr)
229	return -1;
230    xcopy(idi, ido, le32toh(a->a_data), N_DATOFF(*a));
231    addr += le32toh(a->a_data);
232    kh->dload = load;
233    kh->dsize = addr - load;
234    kh->isize = kh->dsize + le32toh(a->a_bss);
235    kh->entry = le32toh(a->a_entry);
236    return 0;
237}
238