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