18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * vdso2c - A vdso image preparation tool 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Andy Lutomirski and others 48c2ecf20Sopenharmony_ci * Licensed under the GPL v2 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * vdso2c requires stripped and unstripped input. It would be trivial 78c2ecf20Sopenharmony_ci * to fully strip the input in here, but, for reasons described below, 88c2ecf20Sopenharmony_ci * we need to write a section table. Doing this is more or less 98c2ecf20Sopenharmony_ci * equivalent to dropping all non-allocatable sections, but it's 108c2ecf20Sopenharmony_ci * easier to let objcopy handle that instead of doing it ourselves. 118c2ecf20Sopenharmony_ci * If we ever need to do something fancier than what objcopy provides, 128c2ecf20Sopenharmony_ci * it would be straightforward to add here. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * We keep a section table for a few reasons: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Binutils has issues debugging the vDSO: it reads the section table to 178c2ecf20Sopenharmony_ci * find SHT_NOTE; it won't look at PT_NOTE for the in-memory vDSO, which 188c2ecf20Sopenharmony_ci * would break build-id if we removed the section table. Binutils 198c2ecf20Sopenharmony_ci * also requires that shstrndx != 0. See: 208c2ecf20Sopenharmony_ci * https://sourceware.org/bugzilla/show_bug.cgi?id=17064 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * elfutils might not look for PT_NOTE if there is a section table at 238c2ecf20Sopenharmony_ci * all. I don't know whether this matters for any practical purpose. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * For simplicity, rather than hacking up a partial section table, we 268c2ecf20Sopenharmony_ci * just write a mostly complete one. We omit non-dynamic symbols, 278c2ecf20Sopenharmony_ci * though, since they're rather large. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Once binutils gets fixed, we might be able to drop this for all but 308c2ecf20Sopenharmony_ci * the 64-bit vdso, since build-id only works in kernel RPMs, and 318c2ecf20Sopenharmony_ci * systems that update to new enough kernel RPMs will likely update 328c2ecf20Sopenharmony_ci * binutils in sync. build-id has never worked for home-built kernel 338c2ecf20Sopenharmony_ci * RPMs without manual symlinking, and I suspect that no one ever does 348c2ecf20Sopenharmony_ci * that. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include <inttypes.h> 428c2ecf20Sopenharmony_ci#include <stdint.h> 438c2ecf20Sopenharmony_ci#include <unistd.h> 448c2ecf20Sopenharmony_ci#include <stdarg.h> 458c2ecf20Sopenharmony_ci#include <stdlib.h> 468c2ecf20Sopenharmony_ci#include <stdio.h> 478c2ecf20Sopenharmony_ci#include <string.h> 488c2ecf20Sopenharmony_ci#include <fcntl.h> 498c2ecf20Sopenharmony_ci#include <err.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <sys/mman.h> 528c2ecf20Sopenharmony_ci#include <sys/types.h> 538c2ecf20Sopenharmony_ci#include <tools/be_byteshift.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <linux/elf.h> 568c2ecf20Sopenharmony_ci#include <linux/types.h> 578c2ecf20Sopenharmony_ci#include <linux/kernel.h> 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciconst char *outfilename; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Symbols that we need in vdso2c. */ 628c2ecf20Sopenharmony_cienum { 638c2ecf20Sopenharmony_ci sym_vvar_start, 648c2ecf20Sopenharmony_ci sym_VDSO_FAKE_SECTION_TABLE_START, 658c2ecf20Sopenharmony_ci sym_VDSO_FAKE_SECTION_TABLE_END, 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct vdso_sym { 698c2ecf20Sopenharmony_ci const char *name; 708c2ecf20Sopenharmony_ci int export; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct vdso_sym required_syms[] = { 748c2ecf20Sopenharmony_ci [sym_vvar_start] = {"vvar_start", 1}, 758c2ecf20Sopenharmony_ci [sym_VDSO_FAKE_SECTION_TABLE_START] = { 768c2ecf20Sopenharmony_ci "VDSO_FAKE_SECTION_TABLE_START", 0 778c2ecf20Sopenharmony_ci }, 788c2ecf20Sopenharmony_ci [sym_VDSO_FAKE_SECTION_TABLE_END] = { 798c2ecf20Sopenharmony_ci "VDSO_FAKE_SECTION_TABLE_END", 0 808c2ecf20Sopenharmony_ci }, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci__attribute__((format(printf, 1, 2))) __attribute__((noreturn)) 848c2ecf20Sopenharmony_cistatic void fail(const char *format, ...) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci va_list ap; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci va_start(ap, format); 898c2ecf20Sopenharmony_ci fprintf(stderr, "Error: "); 908c2ecf20Sopenharmony_ci vfprintf(stderr, format, ap); 918c2ecf20Sopenharmony_ci if (outfilename) 928c2ecf20Sopenharmony_ci unlink(outfilename); 938c2ecf20Sopenharmony_ci exit(1); 948c2ecf20Sopenharmony_ci va_end(ap); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Evil macros for big-endian reads and writes 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci#define GBE(x, bits, ifnot) \ 1018c2ecf20Sopenharmony_ci __builtin_choose_expr( \ 1028c2ecf20Sopenharmony_ci (sizeof(*(x)) == bits/8), \ 1038c2ecf20Sopenharmony_ci (__typeof__(*(x)))get_unaligned_be##bits(x), ifnot) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define LAST_GBE(x) \ 1068c2ecf20Sopenharmony_ci __builtin_choose_expr(sizeof(*(x)) == 1, *(x), (void)(0)) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define GET_BE(x) \ 1098c2ecf20Sopenharmony_ci GBE(x, 64, GBE(x, 32, GBE(x, 16, LAST_GBE(x)))) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define PBE(x, val, bits, ifnot) \ 1128c2ecf20Sopenharmony_ci __builtin_choose_expr( \ 1138c2ecf20Sopenharmony_ci (sizeof(*(x)) == bits/8), \ 1148c2ecf20Sopenharmony_ci put_unaligned_be##bits((val), (x)), ifnot) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define LAST_PBE(x, val) \ 1178c2ecf20Sopenharmony_ci __builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), (void)(0)) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define PUT_BE(x, val) \ 1208c2ecf20Sopenharmony_ci PBE(x, val, 64, PBE(x, val, 32, PBE(x, val, 16, LAST_PBE(x, val)))) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define NSYMS ARRAY_SIZE(required_syms) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define BITSFUNC3(name, bits, suffix) name##bits##suffix 1258c2ecf20Sopenharmony_ci#define BITSFUNC2(name, bits, suffix) BITSFUNC3(name, bits, suffix) 1268c2ecf20Sopenharmony_ci#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS, ) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define INT_BITS BITSFUNC2(int, ELF_BITS, _t) 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x 1318c2ecf20Sopenharmony_ci#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) 1328c2ecf20Sopenharmony_ci#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define ELF_BITS 64 1358c2ecf20Sopenharmony_ci#include "vdso2c.h" 1368c2ecf20Sopenharmony_ci#undef ELF_BITS 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#define ELF_BITS 32 1398c2ecf20Sopenharmony_ci#include "vdso2c.h" 1408c2ecf20Sopenharmony_ci#undef ELF_BITS 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void go(void *raw_addr, size_t raw_len, 1438c2ecf20Sopenharmony_ci void *stripped_addr, size_t stripped_len, 1448c2ecf20Sopenharmony_ci FILE *outfile, const char *name) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci Elf64_Ehdr *hdr = (Elf64_Ehdr *)raw_addr; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (hdr->e_ident[EI_CLASS] == ELFCLASS64) { 1498c2ecf20Sopenharmony_ci go64(raw_addr, raw_len, stripped_addr, stripped_len, 1508c2ecf20Sopenharmony_ci outfile, name); 1518c2ecf20Sopenharmony_ci } else if (hdr->e_ident[EI_CLASS] == ELFCLASS32) { 1528c2ecf20Sopenharmony_ci go32(raw_addr, raw_len, stripped_addr, stripped_len, 1538c2ecf20Sopenharmony_ci outfile, name); 1548c2ecf20Sopenharmony_ci } else { 1558c2ecf20Sopenharmony_ci fail("unknown ELF class\n"); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void map_input(const char *name, void **addr, size_t *len, int prot) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci off_t tmp_len; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci int fd = open(name, O_RDONLY); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (fd == -1) 1668c2ecf20Sopenharmony_ci err(1, "%s", name); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci tmp_len = lseek(fd, 0, SEEK_END); 1698c2ecf20Sopenharmony_ci if (tmp_len == (off_t)-1) 1708c2ecf20Sopenharmony_ci err(1, "lseek"); 1718c2ecf20Sopenharmony_ci *len = (size_t)tmp_len; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci *addr = mmap(NULL, tmp_len, prot, MAP_PRIVATE, fd, 0); 1748c2ecf20Sopenharmony_ci if (*addr == MAP_FAILED) 1758c2ecf20Sopenharmony_ci err(1, "mmap"); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci close(fd); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci size_t raw_len, stripped_len; 1838c2ecf20Sopenharmony_ci void *raw_addr, *stripped_addr; 1848c2ecf20Sopenharmony_ci FILE *outfile; 1858c2ecf20Sopenharmony_ci char *name, *tmp; 1868c2ecf20Sopenharmony_ci int namelen; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (argc != 4) { 1898c2ecf20Sopenharmony_ci printf("Usage: vdso2c RAW_INPUT STRIPPED_INPUT OUTPUT\n"); 1908c2ecf20Sopenharmony_ci return 1; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * Figure out the struct name. If we're writing to a .so file, 1958c2ecf20Sopenharmony_ci * generate raw output insted. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci name = strdup(argv[3]); 1988c2ecf20Sopenharmony_ci namelen = strlen(name); 1998c2ecf20Sopenharmony_ci if (namelen >= 3 && !strcmp(name + namelen - 3, ".so")) { 2008c2ecf20Sopenharmony_ci name = NULL; 2018c2ecf20Sopenharmony_ci } else { 2028c2ecf20Sopenharmony_ci tmp = strrchr(name, '/'); 2038c2ecf20Sopenharmony_ci if (tmp) 2048c2ecf20Sopenharmony_ci name = tmp + 1; 2058c2ecf20Sopenharmony_ci tmp = strchr(name, '.'); 2068c2ecf20Sopenharmony_ci if (tmp) 2078c2ecf20Sopenharmony_ci *tmp = '\0'; 2088c2ecf20Sopenharmony_ci for (tmp = name; *tmp; tmp++) 2098c2ecf20Sopenharmony_ci if (*tmp == '-') 2108c2ecf20Sopenharmony_ci *tmp = '_'; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci map_input(argv[1], &raw_addr, &raw_len, PROT_READ); 2148c2ecf20Sopenharmony_ci map_input(argv[2], &stripped_addr, &stripped_len, PROT_READ); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci outfilename = argv[3]; 2178c2ecf20Sopenharmony_ci outfile = fopen(outfilename, "w"); 2188c2ecf20Sopenharmony_ci if (!outfile) 2198c2ecf20Sopenharmony_ci err(1, "%s", argv[2]); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci munmap(raw_addr, raw_len); 2248c2ecf20Sopenharmony_ci munmap(stripped_addr, stripped_len); 2258c2ecf20Sopenharmony_ci fclose(outfile); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 229