mirror of
https://github.com/qemu/qemu.git
synced 2025-07-22 18:27:05 +00:00

In gen-vdso we load in a file and assume it's a valid ELF file. In particular we assume it's big enough to be able to read the ELF information in e_ident in the ELF header. Add a check that the total file length is at least big enough for all the e_ident bytes, which is good enough for the code in gen-vdso.c. This will catch the most obvious possible bad input file (truncated) and allow us to run the sanity checks like "not actually an ELF file" without potentially crashing. The code in elf32_process() and elf64_process() still makes assumptions about the file being well-formed, but this is OK because we only run it on the vdso binaries that we create ourselves in the build process by running the compiler. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-ID: <20250710170707.1299926-3-peter.maydell@linaro.org>
241 lines
6.0 KiB
C
241 lines
6.0 KiB
C
/*
|
|
* Post-process a vdso elf image for inclusion into qemu.
|
|
*
|
|
* Copyright 2023 Linaro, Ltd.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <endian.h>
|
|
#include <unistd.h>
|
|
#include "elf.h"
|
|
|
|
|
|
#define bswap_(p) _Generic(*(p), \
|
|
uint16_t: __builtin_bswap16, \
|
|
uint32_t: __builtin_bswap32, \
|
|
uint64_t: __builtin_bswap64, \
|
|
int16_t: __builtin_bswap16, \
|
|
int32_t: __builtin_bswap32, \
|
|
int64_t: __builtin_bswap64)
|
|
#define bswaps(p) (*(p) = bswap_(p)(*(p)))
|
|
|
|
static void output_reloc(FILE *outf, void *buf, void *loc)
|
|
{
|
|
fprintf(outf, " 0x%08tx,\n", loc - buf);
|
|
}
|
|
|
|
static const char *sigreturn_sym;
|
|
static const char *rt_sigreturn_sym;
|
|
|
|
static unsigned sigreturn_addr;
|
|
static unsigned rt_sigreturn_addr;
|
|
|
|
#define N 32
|
|
#define elfN(x) elf32_##x
|
|
#define ElfN(x) Elf32_##x
|
|
#include "gen-vdso-elfn.c.inc"
|
|
#undef N
|
|
#undef elfN
|
|
#undef ElfN
|
|
|
|
#define N 64
|
|
#define elfN(x) elf64_##x
|
|
#define ElfN(x) Elf64_##x
|
|
#include "gen-vdso-elfn.c.inc"
|
|
#undef N
|
|
#undef elfN
|
|
#undef ElfN
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
FILE *inf = NULL, *outf = NULL;
|
|
long total_len;
|
|
const char *prefix = "vdso";
|
|
const char *inf_name;
|
|
const char *outf_name = NULL;
|
|
unsigned char *buf = NULL;
|
|
bool need_bswap;
|
|
int ret = EXIT_FAILURE;
|
|
|
|
while (1) {
|
|
int opt = getopt(argc, argv, "o:p:r:s:");
|
|
if (opt < 0) {
|
|
break;
|
|
}
|
|
switch (opt) {
|
|
case 'o':
|
|
outf_name = optarg;
|
|
break;
|
|
case 'p':
|
|
prefix = optarg;
|
|
break;
|
|
case 'r':
|
|
rt_sigreturn_sym = optarg;
|
|
break;
|
|
case 's':
|
|
sigreturn_sym = optarg;
|
|
break;
|
|
default:
|
|
usage:
|
|
fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] "
|
|
"[-s sigreturn-name] -o output-file input-file\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (optind >= argc || outf_name == NULL) {
|
|
goto usage;
|
|
}
|
|
inf_name = argv[optind];
|
|
|
|
/*
|
|
* Open the input and output files.
|
|
*/
|
|
inf = fopen(inf_name, "rb");
|
|
if (inf == NULL) {
|
|
goto perror_inf;
|
|
}
|
|
outf = fopen(outf_name, "w");
|
|
if (outf == NULL) {
|
|
goto perror_outf;
|
|
}
|
|
|
|
/*
|
|
* Read the input file into a buffer.
|
|
* We expect the vdso to be small, on the order of one page,
|
|
* therefore we do not expect a partial read.
|
|
*/
|
|
if (fseek(inf, 0, SEEK_END) < 0) {
|
|
goto perror_inf;
|
|
}
|
|
total_len = ftell(inf);
|
|
if (total_len < 0) {
|
|
goto perror_inf;
|
|
}
|
|
if (fseek(inf, 0, SEEK_SET) < 0) {
|
|
goto perror_inf;
|
|
}
|
|
|
|
if (total_len < EI_NIDENT) {
|
|
fprintf(stderr, "%s: file too small (truncated?)\n", inf_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
buf = malloc(total_len);
|
|
if (buf == NULL) {
|
|
goto perror_inf;
|
|
}
|
|
|
|
errno = 0;
|
|
if (fread(buf, 1, total_len, inf) != total_len) {
|
|
if (errno) {
|
|
goto perror_inf;
|
|
}
|
|
fprintf(stderr, "%s: incomplete read\n", inf_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Identify which elf flavor we're processing.
|
|
* The first 16 bytes of the file are e_ident.
|
|
*/
|
|
|
|
if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 ||
|
|
buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) {
|
|
fprintf(stderr, "%s: not an elf file\n", inf_name);
|
|
return EXIT_FAILURE;
|
|
}
|
|
switch (buf[EI_DATA]) {
|
|
case ELFDATA2LSB:
|
|
need_bswap = BYTE_ORDER != LITTLE_ENDIAN;
|
|
break;
|
|
case ELFDATA2MSB:
|
|
need_bswap = BYTE_ORDER != BIG_ENDIAN;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n",
|
|
inf_name, buf[EI_DATA]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* We need to relocate the VDSO image. The one built into the kernel
|
|
* is built for a fixed address. The one we built for QEMU is not,
|
|
* since that requires close control of the guest address space.
|
|
*
|
|
* Output relocation addresses as we go.
|
|
*/
|
|
|
|
fprintf(outf,
|
|
"/* Automatically generated by linux-user/gen-vdso.c. */\n"
|
|
"\n"
|
|
"static const unsigned %s_relocs[] = {\n", prefix);
|
|
|
|
switch (buf[EI_CLASS]) {
|
|
case ELFCLASS32:
|
|
elf32_process(outf, buf, total_len, need_bswap);
|
|
break;
|
|
case ELFCLASS64:
|
|
elf64_process(outf, buf, total_len, need_bswap);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n",
|
|
inf_name, buf[EI_CLASS]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
fprintf(outf, "};\n\n"); /* end vdso_relocs. */
|
|
|
|
/*
|
|
* Write out the vdso image now, after we made local changes.
|
|
*/
|
|
fprintf(outf,
|
|
"static const uint8_t %s_image[] = {",
|
|
prefix);
|
|
for (long i = 0; i < total_len; ++i) {
|
|
if (i % 12 == 0) {
|
|
fputs("\n ", outf);
|
|
}
|
|
fprintf(outf, " 0x%02x,", buf[i]);
|
|
}
|
|
fprintf(outf, "\n};\n\n");
|
|
|
|
fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix);
|
|
fprintf(outf, " .image = %s_image,\n", prefix);
|
|
fprintf(outf, " .relocs = %s_relocs,\n", prefix);
|
|
fprintf(outf, " .image_size = sizeof(%s_image),\n", prefix);
|
|
fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix);
|
|
fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr);
|
|
fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr);
|
|
fprintf(outf, "};\n");
|
|
|
|
ret = EXIT_SUCCESS;
|
|
|
|
cleanup:
|
|
free(buf);
|
|
|
|
if (outf && fclose(outf) != 0) {
|
|
ret = EXIT_FAILURE;
|
|
}
|
|
if (inf && fclose(inf) != 0) {
|
|
ret = EXIT_FAILURE;
|
|
}
|
|
return ret;
|
|
|
|
perror_inf:
|
|
perror(inf_name);
|
|
goto cleanup;
|
|
|
|
perror_outf:
|
|
perror(outf_name);
|
|
goto cleanup;
|
|
}
|