162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vdso2c - A vdso image preparation tool
462306a36Sopenharmony_ci * Copyright (c) 2014 Andy Lutomirski and others
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * vdso2c requires stripped and unstripped input.  It would be trivial
762306a36Sopenharmony_ci * to fully strip the input in here, but, for reasons described below,
862306a36Sopenharmony_ci * we need to write a section table.  Doing this is more or less
962306a36Sopenharmony_ci * equivalent to dropping all non-allocatable sections, but it's
1062306a36Sopenharmony_ci * easier to let objcopy handle that instead of doing it ourselves.
1162306a36Sopenharmony_ci * If we ever need to do something fancier than what objcopy provides,
1262306a36Sopenharmony_ci * it would be straightforward to add here.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * We keep a section table for a few reasons:
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Binutils has issues debugging the vDSO: it reads the section table to
1762306a36Sopenharmony_ci * find SHT_NOTE; it won't look at PT_NOTE for the in-memory vDSO, which
1862306a36Sopenharmony_ci * would break build-id if we removed the section table.  Binutils
1962306a36Sopenharmony_ci * also requires that shstrndx != 0.  See:
2062306a36Sopenharmony_ci * https://sourceware.org/bugzilla/show_bug.cgi?id=17064
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * elfutils might not look for PT_NOTE if there is a section table at
2362306a36Sopenharmony_ci * all.  I don't know whether this matters for any practical purpose.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * For simplicity, rather than hacking up a partial section table, we
2662306a36Sopenharmony_ci * just write a mostly complete one.  We omit non-dynamic symbols,
2762306a36Sopenharmony_ci * though, since they're rather large.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Once binutils gets fixed, we might be able to drop this for all but
3062306a36Sopenharmony_ci * the 64-bit vdso, since build-id only works in kernel RPMs, and
3162306a36Sopenharmony_ci * systems that update to new enough kernel RPMs will likely update
3262306a36Sopenharmony_ci * binutils in sync.  build-id has never worked for home-built kernel
3362306a36Sopenharmony_ci * RPMs without manual symlinking, and I suspect that no one ever does
3462306a36Sopenharmony_ci * that.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <inttypes.h>
4262306a36Sopenharmony_ci#include <stdint.h>
4362306a36Sopenharmony_ci#include <unistd.h>
4462306a36Sopenharmony_ci#include <stdarg.h>
4562306a36Sopenharmony_ci#include <stdlib.h>
4662306a36Sopenharmony_ci#include <stdio.h>
4762306a36Sopenharmony_ci#include <string.h>
4862306a36Sopenharmony_ci#include <fcntl.h>
4962306a36Sopenharmony_ci#include <err.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include <sys/mman.h>
5262306a36Sopenharmony_ci#include <sys/types.h>
5362306a36Sopenharmony_ci#include <tools/be_byteshift.h>
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#include <linux/elf.h>
5662306a36Sopenharmony_ci#include <linux/types.h>
5762306a36Sopenharmony_ci#include <linux/kernel.h>
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciconst char *outfilename;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Symbols that we need in vdso2c. */
6262306a36Sopenharmony_cienum {
6362306a36Sopenharmony_ci	sym_vvar_start,
6462306a36Sopenharmony_ci	sym_VDSO_FAKE_SECTION_TABLE_START,
6562306a36Sopenharmony_ci	sym_VDSO_FAKE_SECTION_TABLE_END,
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct vdso_sym {
6962306a36Sopenharmony_ci	const char *name;
7062306a36Sopenharmony_ci	int export;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct vdso_sym required_syms[] = {
7462306a36Sopenharmony_ci	[sym_vvar_start] = {"vvar_start", 1},
7562306a36Sopenharmony_ci	[sym_VDSO_FAKE_SECTION_TABLE_START] = {
7662306a36Sopenharmony_ci		"VDSO_FAKE_SECTION_TABLE_START", 0
7762306a36Sopenharmony_ci	},
7862306a36Sopenharmony_ci	[sym_VDSO_FAKE_SECTION_TABLE_END] = {
7962306a36Sopenharmony_ci		"VDSO_FAKE_SECTION_TABLE_END", 0
8062306a36Sopenharmony_ci	},
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci__attribute__((format(printf, 1, 2))) __attribute__((noreturn))
8462306a36Sopenharmony_cistatic void fail(const char *format, ...)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	va_list ap;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	va_start(ap, format);
8962306a36Sopenharmony_ci	fprintf(stderr, "Error: ");
9062306a36Sopenharmony_ci	vfprintf(stderr, format, ap);
9162306a36Sopenharmony_ci	if (outfilename)
9262306a36Sopenharmony_ci		unlink(outfilename);
9362306a36Sopenharmony_ci	exit(1);
9462306a36Sopenharmony_ci	va_end(ap);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * Evil macros for big-endian reads and writes
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_ci#define GBE(x, bits, ifnot)						\
10162306a36Sopenharmony_ci	__builtin_choose_expr(						\
10262306a36Sopenharmony_ci		(sizeof(*(x)) == bits/8),				\
10362306a36Sopenharmony_ci		(__typeof__(*(x)))get_unaligned_be##bits(x), ifnot)
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define LAST_GBE(x)							\
10662306a36Sopenharmony_ci	__builtin_choose_expr(sizeof(*(x)) == 1, *(x), (void)(0))
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define GET_BE(x)							\
10962306a36Sopenharmony_ci	GBE(x, 64, GBE(x, 32, GBE(x, 16, LAST_GBE(x))))
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define PBE(x, val, bits, ifnot)					\
11262306a36Sopenharmony_ci	__builtin_choose_expr(						\
11362306a36Sopenharmony_ci		(sizeof(*(x)) == bits/8),				\
11462306a36Sopenharmony_ci		put_unaligned_be##bits((val), (x)), ifnot)
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define LAST_PBE(x, val)						\
11762306a36Sopenharmony_ci	__builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), (void)(0))
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define PUT_BE(x, val)					\
12062306a36Sopenharmony_ci	PBE(x, val, 64, PBE(x, val, 32, PBE(x, val, 16, LAST_PBE(x, val))))
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define NSYMS ARRAY_SIZE(required_syms)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define BITSFUNC3(name, bits, suffix) name##bits##suffix
12562306a36Sopenharmony_ci#define BITSFUNC2(name, bits, suffix) BITSFUNC3(name, bits, suffix)
12662306a36Sopenharmony_ci#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS, )
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#define INT_BITS BITSFUNC2(int, ELF_BITS, _t)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
13162306a36Sopenharmony_ci#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
13262306a36Sopenharmony_ci#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#define ELF_BITS 64
13562306a36Sopenharmony_ci#include "vdso2c.h"
13662306a36Sopenharmony_ci#undef ELF_BITS
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#define ELF_BITS 32
13962306a36Sopenharmony_ci#include "vdso2c.h"
14062306a36Sopenharmony_ci#undef ELF_BITS
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void go(void *raw_addr, size_t raw_len,
14362306a36Sopenharmony_ci	       void *stripped_addr, size_t stripped_len,
14462306a36Sopenharmony_ci	       FILE *outfile, const char *name)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	Elf64_Ehdr *hdr = (Elf64_Ehdr *)raw_addr;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (hdr->e_ident[EI_CLASS] == ELFCLASS64) {
14962306a36Sopenharmony_ci		go64(raw_addr, raw_len, stripped_addr, stripped_len,
15062306a36Sopenharmony_ci		     outfile, name);
15162306a36Sopenharmony_ci	} else if (hdr->e_ident[EI_CLASS] == ELFCLASS32) {
15262306a36Sopenharmony_ci		go32(raw_addr, raw_len, stripped_addr, stripped_len,
15362306a36Sopenharmony_ci		     outfile, name);
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		fail("unknown ELF class\n");
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void map_input(const char *name, void **addr, size_t *len, int prot)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	off_t tmp_len;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	int fd = open(name, O_RDONLY);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (fd == -1)
16662306a36Sopenharmony_ci		err(1, "%s", name);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	tmp_len = lseek(fd, 0, SEEK_END);
16962306a36Sopenharmony_ci	if (tmp_len == (off_t)-1)
17062306a36Sopenharmony_ci		err(1, "lseek");
17162306a36Sopenharmony_ci	*len = (size_t)tmp_len;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	*addr = mmap(NULL, tmp_len, prot, MAP_PRIVATE, fd, 0);
17462306a36Sopenharmony_ci	if (*addr == MAP_FAILED)
17562306a36Sopenharmony_ci		err(1, "mmap");
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	close(fd);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ciint main(int argc, char **argv)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	size_t raw_len, stripped_len;
18362306a36Sopenharmony_ci	void *raw_addr, *stripped_addr;
18462306a36Sopenharmony_ci	FILE *outfile;
18562306a36Sopenharmony_ci	char *name, *tmp;
18662306a36Sopenharmony_ci	int namelen;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (argc != 4) {
18962306a36Sopenharmony_ci		printf("Usage: vdso2c RAW_INPUT STRIPPED_INPUT OUTPUT\n");
19062306a36Sopenharmony_ci		return 1;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	 * Figure out the struct name.  If we're writing to a .so file,
19562306a36Sopenharmony_ci	 * generate raw output insted.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	name = strdup(argv[3]);
19862306a36Sopenharmony_ci	namelen = strlen(name);
19962306a36Sopenharmony_ci	if (namelen >= 3 && !strcmp(name + namelen - 3, ".so")) {
20062306a36Sopenharmony_ci		name = NULL;
20162306a36Sopenharmony_ci	} else {
20262306a36Sopenharmony_ci		tmp = strrchr(name, '/');
20362306a36Sopenharmony_ci		if (tmp)
20462306a36Sopenharmony_ci			name = tmp + 1;
20562306a36Sopenharmony_ci		tmp = strchr(name, '.');
20662306a36Sopenharmony_ci		if (tmp)
20762306a36Sopenharmony_ci			*tmp = '\0';
20862306a36Sopenharmony_ci		for (tmp = name; *tmp; tmp++)
20962306a36Sopenharmony_ci			if (*tmp == '-')
21062306a36Sopenharmony_ci				*tmp = '_';
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	map_input(argv[1], &raw_addr, &raw_len, PROT_READ);
21462306a36Sopenharmony_ci	map_input(argv[2], &stripped_addr, &stripped_len, PROT_READ);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	outfilename = argv[3];
21762306a36Sopenharmony_ci	outfile = fopen(outfilename, "w");
21862306a36Sopenharmony_ci	if (!outfile)
21962306a36Sopenharmony_ci		err(1, "%s", argv[2]);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	munmap(raw_addr, raw_len);
22462306a36Sopenharmony_ci	munmap(stripped_addr, stripped_len);
22562306a36Sopenharmony_ci	fclose(outfile);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
229