Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions target/i386/latx/context/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ my_file = files(
'wrappedlibxcbutil.c',
'wrappedlibxcbxinerama.c',
'wrappedlibxcbxinput.c',
'wrappedlib-preflight.c',
'wrappedcairo-preflight.c',
'wrappedfont-preflight.c',
'wrappedcairo.c',
'wrappedlibx11xcb.c',
'wrappedxkbcommon.c',
Expand All @@ -36,6 +38,8 @@ my_file = files(
'wrappedlibxcomposite.c',
'wrappedlibxrender.c',
'wrappedlibxft.c',
'wrappedfontconfig.c',
'wrappedfreetype.c',
'wrappedlibxtst.c',
'wrappedlibxt.c',
'wrappedlibxss.c',
Expand Down
223 changes: 16 additions & 207 deletions target/i386/latx/context/wrappedcairo-preflight.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@
* SPDX-License-Identifier: GPL-2.0-only
*/

#include <dlfcn.h>
#include <elf.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "qemu/osdep.h"

#include <string.h>

#include "wrappedcairo-preflight.h"
#include "wrappedlib-preflight.h"

#define GO(name, signature) #name,
#define GOM(name, signature) #name,
Expand All @@ -27,6 +24,11 @@
static const char *const cairo_supported_symbols[] = {
#include "wrappedcairo_private.h"
};
static const char *const cairo_required_host_symbols[] = {
"cairo_font_face_destroy",
"cairo_font_face_get_user_data",
"cairo_font_face_status",
};
#undef GO
#undef GOM
#undef GOW
Expand All @@ -38,212 +40,19 @@ static const char *const cairo_supported_symbols[] = {
#undef DATAB
#undef DATAM

static bool cairo_symbol_is_supported(const char *name)
{
size_t count = sizeof(cairo_supported_symbols) /
sizeof(cairo_supported_symbols[0]);

for (size_t i = 0; i < count; i++) {
if (!strcmp(name, cairo_supported_symbols[i])) {
return true;
}
}
return false;
}

static bool read_exact(FILE *file, void *buffer, size_t size, long offset)
static bool cairo_symbol_filter(const char *name)
{
return fseek(file, offset, SEEK_SET) == 0 &&
fread(buffer, 1, size, file) == size;
}

static bool range_is_valid(uint64_t offset, uint64_t size,
uint64_t file_size)
{
return offset <= file_size && size <= file_size - offset;
}

static bool reject(char *reason, size_t reason_size, const char *format,
const char *detail)
{
snprintf(reason, reason_size, format, detail ? detail : "");
return false;
return !strncmp(name, "cairo_", 6);
}

bool latx_cairo_preflight_guest(const char *guest_path,
const char *host_soname,
char *reason, size_t reason_size)
{
Elf64_Ehdr ehdr;
Elf64_Shdr *sections = NULL;
FILE *file = NULL;
void *host = NULL;
bool found_dynsym = false;
bool found_cairo_symbol = false;
bool ok = false;
long file_size;

if (!reason || !reason_size) {
return false;
}
reason[0] = '\0';
if (!guest_path || !host_soname) {
return reject(reason, reason_size,
"guest or host Cairo path is unavailable%s", NULL);
}

file = fopen(guest_path, "rb");
if (!file) {
snprintf(reason, reason_size, "cannot open guest ELF '%s': %s",
guest_path, strerror(errno));
goto out;
}
if (fseek(file, 0, SEEK_END) != 0 ||
(file_size = ftell(file)) < 0 ||
fseek(file, 0, SEEK_SET) != 0) {
reject(reason, reason_size,
"cannot determine guest Cairo ELF size%s", NULL);
goto out;
}
if (fread(&ehdr, 1, sizeof(ehdr), file) != sizeof(ehdr) ||
memcmp(ehdr.e_ident, ELFMAG, SELFMAG) ||
ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
ehdr.e_ident[EI_DATA] != ELFDATA2LSB ||
ehdr.e_type != ET_DYN ||
ehdr.e_machine != EM_X86_64 ||
ehdr.e_shentsize != sizeof(Elf64_Shdr) ||
!ehdr.e_shoff || !ehdr.e_shnum ||
!range_is_valid(ehdr.e_shoff,
(uint64_t)ehdr.e_shnum * sizeof(Elf64_Shdr),
file_size)) {
reject(reason, reason_size,
"guest Cairo is not a supported ELF64 little-endian shared object%s",
NULL);
goto out;
}
sections = calloc(ehdr.e_shnum, sizeof(*sections));
if (!sections ||
!read_exact(file, sections, ehdr.e_shnum * sizeof(*sections),
(long)ehdr.e_shoff)) {
reject(reason, reason_size,
"cannot read guest Cairo section table%s", NULL);
goto out;
}

host = dlopen(host_soname, RTLD_LAZY | RTLD_LOCAL);
if (!host) {
reject(reason, reason_size, "cannot load host Cairo: %s", dlerror());
goto out;
}

for (size_t section_index = 0; section_index < ehdr.e_shnum;
section_index++) {
const Elf64_Shdr *dynsym = &sections[section_index];
const Elf64_Shdr *strtab;
Elf64_Sym *symbols = NULL;
char *strings = NULL;
size_t symbol_count;

if (dynsym->sh_type != SHT_DYNSYM) {
continue;
}
found_dynsym = true;
if (dynsym->sh_link >= ehdr.e_shnum ||
dynsym->sh_entsize != sizeof(Elf64_Sym) ||
dynsym->sh_size % sizeof(Elf64_Sym) ||
!range_is_valid(dynsym->sh_offset, dynsym->sh_size,
file_size)) {
reject(reason, reason_size,
"guest Cairo has an invalid dynamic symbol table%s", NULL);
goto out;
}
strtab = &sections[dynsym->sh_link];
if (strtab->sh_type != SHT_STRTAB || !strtab->sh_size ||
!range_is_valid(strtab->sh_offset, strtab->sh_size,
file_size)) {
reject(reason, reason_size,
"guest Cairo has an invalid dynamic string table%s",
NULL);
goto out;
}
symbols = malloc(dynsym->sh_size);
strings = malloc(strtab->sh_size);
if (!symbols || !strings ||
!read_exact(file, symbols, dynsym->sh_size,
(long)dynsym->sh_offset) ||
!read_exact(file, strings, strtab->sh_size,
(long)strtab->sh_offset)) {
free(symbols);
free(strings);
reject(reason, reason_size,
"cannot read guest Cairo dynamic symbols%s", NULL);
goto out;
}

symbol_count = dynsym->sh_size / sizeof(Elf64_Sym);
for (size_t i = 0; i < symbol_count; i++) {
const Elf64_Sym *symbol = &symbols[i];
const char *name;
unsigned type = ELF64_ST_TYPE(symbol->st_info);
unsigned binding = ELF64_ST_BIND(symbol->st_info);

if (symbol->st_shndx == SHN_UNDEF ||
(type != STT_FUNC && type != STT_GNU_IFUNC) ||
(binding != STB_GLOBAL && binding != STB_WEAK) ||
symbol->st_name >= strtab->sh_size) {
continue;
}
name = strings + symbol->st_name;
if (!memchr(name, '\0', strtab->sh_size - symbol->st_name)) {
free(symbols);
free(strings);
reject(reason, reason_size,
"guest Cairo has an unterminated dynamic symbol%s",
NULL);
goto out;
}
if (strncmp(name, "cairo_", 6)) {
continue;
}
found_cairo_symbol = true;
if (!cairo_symbol_is_supported(name)) {
snprintf(reason, reason_size,
"guest symbol '%s' has no safe wrapper", name);
free(symbols);
free(strings);
goto out;
}
if (!dlsym(host, name)) {
snprintf(reason, reason_size,
"host Cairo is missing guest symbol '%s'", name);
free(symbols);
free(strings);
goto out;
}
}
free(symbols);
free(strings);
}

if (!found_dynsym) {
reject(reason, reason_size,
"guest Cairo has no inspectable dynamic symbol table%s", NULL);
goto out;
}
if (!found_cairo_symbol) {
reject(reason, reason_size,
"guest Cairo exports no Cairo functions%s", NULL);
goto out;
}
ok = true;

out:
if (host) {
dlclose(host);
}
free(sections);
if (file) {
fclose(file);
}
return ok;
return latx_wrappedlib_preflight_guest(
guest_path, host_soname, "Cairo", cairo_symbol_filter,
cairo_supported_symbols, ARRAY_SIZE(cairo_supported_symbols),
cairo_required_host_symbols,
ARRAY_SIZE(cairo_required_host_symbols),
reason, reason_size);
}
Loading