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