1330449Seadler/*-
2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3330449Seadler *
448905Srnordier * Copyright (c) 1999 Global Technology Associates, Inc.
548905Srnordier * All rights reserved.
648905Srnordier *
748905Srnordier * Redistribution and use in source and binary forms, with or without
848905Srnordier * modification, are permitted provided that the following conditions
948905Srnordier * are met:
1048905Srnordier * 1. Redistributions of source code must retain the above copyright
1148905Srnordier *    notice, this list of conditions and the following disclaimer.
1248905Srnordier * 2. Redistributions in binary form must reproduce the above copyright
1348905Srnordier *    notice, this list of conditions and the following disclaimer in the
1448905Srnordier *    documentation and/or other materials provided with the distribution.
1548905Srnordier *
1648905Srnordier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
1748905Srnordier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1848905Srnordier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1948905Srnordier * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
2048905Srnordier * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2148905Srnordier * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
2248905Srnordier * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2348905Srnordier * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2448905Srnordier * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
2548905Srnordier * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
2648905Srnordier * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2748905Srnordier *
2850479Speter * $FreeBSD: stable/11/usr.sbin/kgzip/kgzcmp.c 330449 2018-03-05 07:26:05Z eadler $
2948905Srnordier */
3048905Srnordier
31130927Sobrien#define	_KERNEL
32130927Sobrien#include <sys/param.h>
33130927Sobrien#undef _KERNEL
3448905Srnordier#include <sys/types.h>
3548905Srnordier#include <sys/stat.h>
3648905Srnordier#include <sys/wait.h>
3748905Srnordier
3848905Srnordier#include <err.h>
3948905Srnordier#include <fcntl.h>
4048905Srnordier#include <stdio.h>
4148905Srnordier#include <stdlib.h>
4248905Srnordier#include <unistd.h>
4348905Srnordier
44130927Sobrien#include <a.out.h>
4548905Srnordier
4668313Srnordier#include "aouthdr.h"
4748905Srnordier#include "elfhdr.h"
4848905Srnordier#include "kgzip.h"
4948905Srnordier
5048905Srnordierstatic void mk_data(const struct iodesc *i, const struct iodesc *,
5168313Srnordier		    struct kgz_hdr *, size_t);
5248905Srnordierstatic int ld_elf(const struct iodesc *, const struct iodesc *,
5348905Srnordier		  struct kgz_hdr *, const Elf32_Ehdr *);
5448905Srnordierstatic int ld_aout(const struct iodesc *, const struct iodesc *,
55130927Sobrien		   struct kgz_hdr *, const struct exec *);
5648905Srnordier
5748905Srnordier/*
5848905Srnordier * Compress executable and output it in relocatable object format.
5948905Srnordier */
6048905Srnordiervoid
6148905Srnordierkgzcmp(struct kgz_hdr *kh, const char *f1, const char *f2)
6248905Srnordier{
6348905Srnordier    struct iodesc idi, ido;
64112089Sru    struct kgz_hdr khle;
6548905Srnordier
6648905Srnordier    if ((idi.fd = open(idi.fname = f1, O_RDONLY)) == -1)
6748905Srnordier	err(1, "%s", idi.fname);
6848905Srnordier    if ((ido.fd = open(ido.fname = f2, O_CREAT | O_TRUNC | O_WRONLY,
6948905Srnordier		       0666)) == -1)
7048905Srnordier	err(1, "%s", ido.fname);
7148905Srnordier    kh->ident[0] = KGZ_ID0;
7248905Srnordier    kh->ident[1] = KGZ_ID1;
7348905Srnordier    kh->ident[2] = KGZ_ID2;
7448905Srnordier    kh->ident[3] = KGZ_ID3;
7568313Srnordier    mk_data(&idi, &ido, kh,
7668313Srnordier	    (format == F_AOUT ? sizeof(struct kgz_aouthdr0) :
7768313Srnordier				sizeof(struct kgz_elfhdr)) +
7868313Srnordier	     sizeof(struct kgz_hdr));
7948905Srnordier    kh->dload &= 0xffffff;
8048905Srnordier    kh->entry &= 0xffffff;
8168313Srnordier    if (format == F_AOUT) {
8268313Srnordier	struct kgz_aouthdr0 ahdr0 = aouthdr0;
8368313Srnordier	struct kgz_aouthdr1 ahdr1 = aouthdr1;
8468313Srnordier	unsigned x = (sizeof(struct kgz_hdr) + kh->nsize) & (16 - 1);
8568313Srnordier	if (x) {
8668313Srnordier	    x = 16 - x;
8768313Srnordier	    xzero(&ido, x);
8868313Srnordier	}
8968313Srnordier	xwrite(&ido, &ahdr1, sizeof(ahdr1));
9068313Srnordier	ahdr0.a.a_data += kh->nsize + x;
9168313Srnordier	xseek(&ido, 0);
9268313Srnordier	xwrite(&ido, &ahdr0, sizeof(ahdr0));
9368313Srnordier    } else {
9468313Srnordier	struct kgz_elfhdr ehdr = elfhdr;
95130927Sobrien	ehdr.st[KGZ_ST_KGZ_NDATA].st_size = htole32(kh->nsize);
96112089Sru	ehdr.sh[KGZ_SH_DATA].sh_size =
97130927Sobrien	    htole32(le32toh(ehdr.sh[KGZ_SH_DATA].sh_size) + kh->nsize);
9868313Srnordier	xseek(&ido, 0);
9968313Srnordier	xwrite(&ido, &ehdr, sizeof(ehdr));
10068313Srnordier    }
101112089Sru    khle = *kh;
102130927Sobrien    khle.dload = htole32(khle.dload);
103130927Sobrien    khle.dsize = htole32(khle.dsize);
104130927Sobrien    khle.isize = htole32(khle.isize);
105130927Sobrien    khle.entry = htole32(khle.entry);
106130927Sobrien    khle.nsize = htole32(khle.nsize);
107112089Sru    xwrite(&ido, &khle, sizeof(khle));
10848905Srnordier    xclose(&ido);
10948905Srnordier    xclose(&idi);
11048905Srnordier}
11148905Srnordier
11248905Srnordier/*
11348905Srnordier * Make encoded (compressed) data.
11448905Srnordier */
11548905Srnordierstatic void
11648905Srnordiermk_data(const struct iodesc * idi, const struct iodesc * ido,
11768313Srnordier	struct kgz_hdr * kh, size_t off)
11848905Srnordier{
11948905Srnordier    union {
120130927Sobrien	struct exec ex;
12148905Srnordier	Elf32_Ehdr ee;
12248905Srnordier    } hdr;
12348905Srnordier    struct stat sb;
12448905Srnordier    struct iodesc idp;
12548905Srnordier    int fd[2];
12648905Srnordier    pid_t pid;
12748905Srnordier    size_t n;
12848905Srnordier    int fmt, status, e;
12948905Srnordier
13048905Srnordier    n = xread(idi, &hdr, sizeof(hdr), 0);
13148905Srnordier    fmt = 0;
13248905Srnordier    if (n >= sizeof(hdr.ee) && IS_ELF(hdr.ee))
13348905Srnordier	fmt = F_ELF;
134130927Sobrien    else if (n >= sizeof(hdr.ex) && N_GETMAGIC(hdr.ex) == ZMAGIC)
13548905Srnordier	fmt = F_AOUT;
13648905Srnordier    if (!fmt)
13748905Srnordier	errx(1, "%s: Format not supported", idi->fname);
13868313Srnordier    xseek(ido, off);
13948905Srnordier    if (pipe(fd))
14048905Srnordier	err(1, NULL);
14148905Srnordier    switch (pid = fork()) {
14248905Srnordier    case -1:
14348905Srnordier	err(1, NULL);
14448905Srnordier    case 0:
14548905Srnordier	close(fd[1]);
14648905Srnordier	dup2(fd[0], STDIN_FILENO);
14748905Srnordier	close(fd[0]);
14848905Srnordier	close(idi->fd);
14948905Srnordier	dup2(ido->fd, STDOUT_FILENO);
15048905Srnordier	close(ido->fd);
151112047Sru	execlp("gzip", "gzip", "-9n", (char *)NULL);
15248905Srnordier	warn(NULL);
15348905Srnordier	_exit(1);
15448905Srnordier    default:
15548905Srnordier	close(fd[0]);
15648905Srnordier	idp.fname = "(pipe)";
15748905Srnordier	idp.fd = fd[1];
15848905Srnordier	e = fmt == F_ELF  ? ld_elf(idi, &idp, kh, &hdr.ee) :
15948905Srnordier	    fmt == F_AOUT ? ld_aout(idi, &idp, kh, &hdr.ex) : -1;
16048905Srnordier	close(fd[1]);
16148905Srnordier	if ((pid = waitpid(pid, &status, 0)) == -1)
16248905Srnordier	    err(1, NULL);
16348905Srnordier	if (WIFSIGNALED(status) || WEXITSTATUS(status))
16448905Srnordier	    exit(1);
16548905Srnordier    }
16648905Srnordier    if (e)
16748905Srnordier	errx(1, "%s: Invalid format", idi->fname);
16848905Srnordier    if (fstat(ido->fd, &sb))
16962986Skris	err(1, "%s", ido->fname);
17068313Srnordier    kh->nsize = sb.st_size - off;
17148905Srnordier}
17248905Srnordier
17348905Srnordier/*
17448905Srnordier * "Load" an ELF-format executable.
17548905Srnordier */
17648905Srnordierstatic int
17748905Srnordierld_elf(const struct iodesc * idi, const struct iodesc * ido,
17848905Srnordier       struct kgz_hdr * kh, const Elf32_Ehdr * e)
17948905Srnordier{
18048905Srnordier    Elf32_Phdr p;
18148905Srnordier    size_t load, addr, n;
18248905Srnordier    unsigned x, i;
18348905Srnordier
18448905Srnordier    load = addr = n = 0;
18548905Srnordier    for (x = i = 0; i < e->e_phnum; i++) {
18648905Srnordier	if (xread(idi, &p, sizeof(p),
18748905Srnordier		  e->e_phoff + i * e->e_phentsize) != e->e_phentsize)
18848905Srnordier	    return -1;
18948905Srnordier	if (p.p_type != PT_LOAD)
19048905Srnordier	    continue;
19148905Srnordier	if (!x)
19248905Srnordier	    load = addr = p.p_vaddr;
19348905Srnordier	else {
19448905Srnordier	    if (p.p_vaddr < addr)
19548905Srnordier		return -1;
19648905Srnordier	    n = p.p_vaddr - addr;
19748905Srnordier	    if (n) {
19848905Srnordier		xzero(ido, n);
19948905Srnordier		addr += n;
20048905Srnordier	    }
20148905Srnordier	}
20248905Srnordier	if (p.p_memsz < p.p_filesz)
20348905Srnordier	    return -1;
20448905Srnordier	n = p.p_memsz - p.p_filesz;
20548905Srnordier	xcopy(idi, ido, p.p_filesz, p.p_offset);
20648905Srnordier	addr += p.p_filesz;
20748905Srnordier	x++;
20848905Srnordier    }
20948905Srnordier    if (!x)
21048905Srnordier	return -1;
21148905Srnordier    kh->dload = load;
21248905Srnordier    kh->dsize = addr - load;
21348905Srnordier    kh->isize = kh->dsize + n;
21448905Srnordier    kh->entry = e->e_entry;
21548905Srnordier    return 0;
21648905Srnordier}
21748905Srnordier
21848905Srnordier/*
21948905Srnordier * "Load" an a.out-format executable.
22048905Srnordier */
22148905Srnordierstatic int
22248905Srnordierld_aout(const struct iodesc * idi, const struct iodesc * ido,
223130927Sobrien	struct kgz_hdr * kh, const struct exec * a)
22448905Srnordier{
22548905Srnordier    size_t load, addr;
22648905Srnordier
227130927Sobrien    load = addr = N_TXTADDR(*a);
228130927Sobrien    xcopy(idi, ido, le32toh(a->a_text), N_TXTOFF(*a));
229130927Sobrien    addr += le32toh(a->a_text);
230130927Sobrien    if (N_DATADDR(*a) != addr)
23148905Srnordier	return -1;
232130927Sobrien    xcopy(idi, ido, le32toh(a->a_data), N_DATOFF(*a));
233130927Sobrien    addr += le32toh(a->a_data);
23448905Srnordier    kh->dload = load;
23548905Srnordier    kh->dsize = addr - load;
236130927Sobrien    kh->isize = kh->dsize + le32toh(a->a_bss);
237130927Sobrien    kh->entry = le32toh(a->a_entry);
23848905Srnordier    return 0;
23948905Srnordier}
240