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're keep a section table for a few reasons: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * The Go runtime had a couple of bugs: it would read the section 178c2ecf20Sopenharmony_ci * table to try to figure out how many dynamic symbols there were (it 188c2ecf20Sopenharmony_ci * shouldn't have looked at the section table at all) and, if there 198c2ecf20Sopenharmony_ci * were no SHT_SYNDYM section table entry, it would use an 208c2ecf20Sopenharmony_ci * uninitialized value for the number of symbols. An empty DYNSYM 218c2ecf20Sopenharmony_ci * table would work, but I see no reason not to write a valid one (and 228c2ecf20Sopenharmony_ci * keep full performance for old Go programs). This hack is only 238c2ecf20Sopenharmony_ci * needed on x86_64. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * The bug was introduced on 2012-08-31 by: 268c2ecf20Sopenharmony_ci * https://code.google.com/p/go/source/detail?r=56ea40aac72b 278c2ecf20Sopenharmony_ci * and was fixed on 2014-06-13 by: 288c2ecf20Sopenharmony_ci * https://code.google.com/p/go/source/detail?r=fc1cd5e12595 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Binutils has issues debugging the vDSO: it reads the section table to 318c2ecf20Sopenharmony_ci * find SHT_NOTE; it won't look at PT_NOTE for the in-memory vDSO, which 328c2ecf20Sopenharmony_ci * would break build-id if we removed the section table. Binutils 338c2ecf20Sopenharmony_ci * also requires that shstrndx != 0. See: 348c2ecf20Sopenharmony_ci * https://sourceware.org/bugzilla/show_bug.cgi?id=17064 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * elfutils might not look for PT_NOTE if there is a section table at 378c2ecf20Sopenharmony_ci * all. I don't know whether this matters for any practical purpose. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * For simplicity, rather than hacking up a partial section table, we 408c2ecf20Sopenharmony_ci * just write a mostly complete one. We omit non-dynamic symbols, 418c2ecf20Sopenharmony_ci * though, since they're rather large. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * Once binutils gets fixed, we might be able to drop this for all but 448c2ecf20Sopenharmony_ci * the 64-bit vdso, since build-id only works in kernel RPMs, and 458c2ecf20Sopenharmony_ci * systems that update to new enough kernel RPMs will likely update 468c2ecf20Sopenharmony_ci * binutils in sync. build-id has never worked for home-built kernel 478c2ecf20Sopenharmony_ci * RPMs without manual symlinking, and I suspect that no one ever does 488c2ecf20Sopenharmony_ci * that. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <inttypes.h> 528c2ecf20Sopenharmony_ci#include <stdint.h> 538c2ecf20Sopenharmony_ci#include <unistd.h> 548c2ecf20Sopenharmony_ci#include <stdarg.h> 558c2ecf20Sopenharmony_ci#include <stdlib.h> 568c2ecf20Sopenharmony_ci#include <stdio.h> 578c2ecf20Sopenharmony_ci#include <string.h> 588c2ecf20Sopenharmony_ci#include <fcntl.h> 598c2ecf20Sopenharmony_ci#include <err.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#include <sys/mman.h> 628c2ecf20Sopenharmony_ci#include <sys/types.h> 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include <tools/le_byteshift.h> 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#include <linux/elf.h> 678c2ecf20Sopenharmony_ci#include <linux/types.h> 688c2ecf20Sopenharmony_ci#include <linux/kernel.h> 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciconst char *outfilename; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Symbols that we need in vdso2c. */ 738c2ecf20Sopenharmony_cienum { 748c2ecf20Sopenharmony_ci sym_vvar_start, 758c2ecf20Sopenharmony_ci sym_vvar_page, 768c2ecf20Sopenharmony_ci sym_pvclock_page, 778c2ecf20Sopenharmony_ci sym_hvclock_page, 788c2ecf20Sopenharmony_ci sym_timens_page, 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciconst int special_pages[] = { 828c2ecf20Sopenharmony_ci sym_vvar_page, 838c2ecf20Sopenharmony_ci sym_pvclock_page, 848c2ecf20Sopenharmony_ci sym_hvclock_page, 858c2ecf20Sopenharmony_ci sym_timens_page, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistruct vdso_sym { 898c2ecf20Sopenharmony_ci const char *name; 908c2ecf20Sopenharmony_ci bool export; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct vdso_sym required_syms[] = { 948c2ecf20Sopenharmony_ci [sym_vvar_start] = {"vvar_start", true}, 958c2ecf20Sopenharmony_ci [sym_vvar_page] = {"vvar_page", true}, 968c2ecf20Sopenharmony_ci [sym_pvclock_page] = {"pvclock_page", true}, 978c2ecf20Sopenharmony_ci [sym_hvclock_page] = {"hvclock_page", true}, 988c2ecf20Sopenharmony_ci [sym_timens_page] = {"timens_page", true}, 998c2ecf20Sopenharmony_ci {"VDSO32_NOTE_MASK", true}, 1008c2ecf20Sopenharmony_ci {"__kernel_vsyscall", true}, 1018c2ecf20Sopenharmony_ci {"__kernel_sigreturn", true}, 1028c2ecf20Sopenharmony_ci {"__kernel_rt_sigreturn", true}, 1038c2ecf20Sopenharmony_ci {"int80_landing_pad", true}, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci__attribute__((format(printf, 1, 2))) __attribute__((noreturn)) 1078c2ecf20Sopenharmony_cistatic void fail(const char *format, ...) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci va_list ap; 1108c2ecf20Sopenharmony_ci va_start(ap, format); 1118c2ecf20Sopenharmony_ci fprintf(stderr, "Error: "); 1128c2ecf20Sopenharmony_ci vfprintf(stderr, format, ap); 1138c2ecf20Sopenharmony_ci if (outfilename) 1148c2ecf20Sopenharmony_ci unlink(outfilename); 1158c2ecf20Sopenharmony_ci exit(1); 1168c2ecf20Sopenharmony_ci va_end(ap); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * Evil macros for little-endian reads and writes 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci#define GLE(x, bits, ifnot) \ 1238c2ecf20Sopenharmony_ci __builtin_choose_expr( \ 1248c2ecf20Sopenharmony_ci (sizeof(*(x)) == bits/8), \ 1258c2ecf20Sopenharmony_ci (__typeof__(*(x)))get_unaligned_le##bits(x), ifnot) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ciextern void bad_get_le(void); 1288c2ecf20Sopenharmony_ci#define LAST_GLE(x) \ 1298c2ecf20Sopenharmony_ci __builtin_choose_expr(sizeof(*(x)) == 1, *(x), bad_get_le()) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define GET_LE(x) \ 1328c2ecf20Sopenharmony_ci GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_GLE(x)))) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define PLE(x, val, bits, ifnot) \ 1358c2ecf20Sopenharmony_ci __builtin_choose_expr( \ 1368c2ecf20Sopenharmony_ci (sizeof(*(x)) == bits/8), \ 1378c2ecf20Sopenharmony_ci put_unaligned_le##bits((val), (x)), ifnot) 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciextern void bad_put_le(void); 1408c2ecf20Sopenharmony_ci#define LAST_PLE(x, val) \ 1418c2ecf20Sopenharmony_ci __builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), bad_put_le()) 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define PUT_LE(x, val) \ 1448c2ecf20Sopenharmony_ci PLE(x, val, 64, PLE(x, val, 32, PLE(x, val, 16, LAST_PLE(x, val)))) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define NSYMS ARRAY_SIZE(required_syms) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define BITSFUNC3(name, bits, suffix) name##bits##suffix 1508c2ecf20Sopenharmony_ci#define BITSFUNC2(name, bits, suffix) BITSFUNC3(name, bits, suffix) 1518c2ecf20Sopenharmony_ci#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS, ) 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#define INT_BITS BITSFUNC2(int, ELF_BITS, _t) 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x 1568c2ecf20Sopenharmony_ci#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) 1578c2ecf20Sopenharmony_ci#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#define ELF_BITS 64 1608c2ecf20Sopenharmony_ci#include "vdso2c.h" 1618c2ecf20Sopenharmony_ci#undef ELF_BITS 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#define ELF_BITS 32 1648c2ecf20Sopenharmony_ci#include "vdso2c.h" 1658c2ecf20Sopenharmony_ci#undef ELF_BITS 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void go(void *raw_addr, size_t raw_len, 1688c2ecf20Sopenharmony_ci void *stripped_addr, size_t stripped_len, 1698c2ecf20Sopenharmony_ci FILE *outfile, const char *name) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci Elf64_Ehdr *hdr = (Elf64_Ehdr *)raw_addr; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (hdr->e_ident[EI_CLASS] == ELFCLASS64) { 1748c2ecf20Sopenharmony_ci go64(raw_addr, raw_len, stripped_addr, stripped_len, 1758c2ecf20Sopenharmony_ci outfile, name); 1768c2ecf20Sopenharmony_ci } else if (hdr->e_ident[EI_CLASS] == ELFCLASS32) { 1778c2ecf20Sopenharmony_ci go32(raw_addr, raw_len, stripped_addr, stripped_len, 1788c2ecf20Sopenharmony_ci outfile, name); 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci fail("unknown ELF class\n"); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void map_input(const char *name, void **addr, size_t *len, int prot) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci off_t tmp_len; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci int fd = open(name, O_RDONLY); 1898c2ecf20Sopenharmony_ci if (fd == -1) 1908c2ecf20Sopenharmony_ci err(1, "open(%s)", name); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci tmp_len = lseek(fd, 0, SEEK_END); 1938c2ecf20Sopenharmony_ci if (tmp_len == (off_t)-1) 1948c2ecf20Sopenharmony_ci err(1, "lseek"); 1958c2ecf20Sopenharmony_ci *len = (size_t)tmp_len; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci *addr = mmap(NULL, tmp_len, prot, MAP_PRIVATE, fd, 0); 1988c2ecf20Sopenharmony_ci if (*addr == MAP_FAILED) 1998c2ecf20Sopenharmony_ci err(1, "mmap"); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci close(fd); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciint main(int argc, char **argv) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci size_t raw_len, stripped_len; 2078c2ecf20Sopenharmony_ci void *raw_addr, *stripped_addr; 2088c2ecf20Sopenharmony_ci FILE *outfile; 2098c2ecf20Sopenharmony_ci char *name, *tmp; 2108c2ecf20Sopenharmony_ci int namelen; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (argc != 4) { 2138c2ecf20Sopenharmony_ci printf("Usage: vdso2c RAW_INPUT STRIPPED_INPUT OUTPUT\n"); 2148c2ecf20Sopenharmony_ci return 1; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * Figure out the struct name. If we're writing to a .so file, 2198c2ecf20Sopenharmony_ci * generate raw output insted. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci name = strdup(argv[3]); 2228c2ecf20Sopenharmony_ci namelen = strlen(name); 2238c2ecf20Sopenharmony_ci if (namelen >= 3 && !strcmp(name + namelen - 3, ".so")) { 2248c2ecf20Sopenharmony_ci name = NULL; 2258c2ecf20Sopenharmony_ci } else { 2268c2ecf20Sopenharmony_ci tmp = strrchr(name, '/'); 2278c2ecf20Sopenharmony_ci if (tmp) 2288c2ecf20Sopenharmony_ci name = tmp + 1; 2298c2ecf20Sopenharmony_ci tmp = strchr(name, '.'); 2308c2ecf20Sopenharmony_ci if (tmp) 2318c2ecf20Sopenharmony_ci *tmp = '\0'; 2328c2ecf20Sopenharmony_ci for (tmp = name; *tmp; tmp++) 2338c2ecf20Sopenharmony_ci if (*tmp == '-') 2348c2ecf20Sopenharmony_ci *tmp = '_'; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci map_input(argv[1], &raw_addr, &raw_len, PROT_READ); 2388c2ecf20Sopenharmony_ci map_input(argv[2], &stripped_addr, &stripped_len, PROT_READ); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci outfilename = argv[3]; 2418c2ecf20Sopenharmony_ci outfile = fopen(outfilename, "w"); 2428c2ecf20Sopenharmony_ci if (!outfile) 2438c2ecf20Sopenharmony_ci err(1, "fopen(%s)", outfilename); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci munmap(raw_addr, raw_len); 2488c2ecf20Sopenharmony_ci munmap(stripped_addr, stripped_len); 2498c2ecf20Sopenharmony_ci fclose(outfile); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 253