Files
DroidDeck/tools/proot/fastpath/svc-cache.h

91 lines
4.1 KiB
C

struct svc_cached_sites {
uint64_t magic;
uint32_t api, count;
uint64_t offset[256];
};
static unsigned svc_cache_hits, svc_cache_misses;
static int svc_segment(const struct dl_phdr_info *info, uint64_t at, size_t length, unsigned flags) {
for (int i = 0; i < info->dlpi_phnum; i++) {
const ElfW(Phdr) *p = &info->dlpi_phdr[i];
if (p->p_type == PT_LOAD && (p->p_flags & flags) == flags &&
at >= p->p_vaddr && at - p->p_vaddr <= p->p_memsz &&
length <= p->p_memsz - (at - p->p_vaddr)) return i + 1;
}
return 0;
}
static int svc_cache_name(const struct dl_phdr_info *info, char *path, size_t size) {
const char *enabled = getenv("PROOT_FP_SVC_CACHE");
if (enabled && strcmp(enabled, "1")) return 0;
for (int i = 0; i < info->dlpi_phnum; i++) {
const ElfW(Phdr) *p = &info->dlpi_phdr[i];
if (p->p_type != PT_NOTE || !svc_segment(info, p->p_vaddr, p->p_memsz, PF_R)) continue;
const unsigned char *note = (const unsigned char *)(info->dlpi_addr + p->p_vaddr);
for (size_t at = 0; p->p_memsz - at >= sizeof(ElfW(Nhdr));) {
ElfW(Nhdr) h;
memcpy(&h, note + at, sizeof h);
at += sizeof h;
size_t name = ((size_t)h.n_namesz + 3) & ~(size_t)3;
size_t desc = ((size_t)h.n_descsz + 3) & ~(size_t)3;
if (name > p->p_memsz - at || desc > p->p_memsz - at - name) break;
if (h.n_type == NT_GNU_BUILD_ID && h.n_namesz == 4 && !memcmp(note + at, "GNU", 4) &&
h.n_descsz && h.n_descsz <= 64) {
char id[129];
static const char hex[] = "0123456789abcdef";
for (size_t j = 0; j < h.n_descsz; j++) {
unsigned char b = note[at + name + j];
id[2 * j] = hex[b >> 4];
id[2 * j + 1] = hex[b & 15];
}
id[2 * h.n_descsz] = 0;
int n = snprintf(path, size, "/tmp/.droiddeck-svc-v3-%d-%s", svc_android_api, id);
return n > 0 && (size_t)n < size;
}
at += name + desc;
}
}
return 0;
}
static int svc_cache_read(const struct dl_phdr_info *info, const char *path) {
long fd = fp_open(AT_FDCWD, path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK, 0);
if (fd < 0) return 0;
struct stat st;
struct svc_cached_sites table;
int valid = sc(SYS_fstat, fd, (long)&st, 0, 0, 0) == 0 && S_ISREG(st.st_mode) &&
st.st_uid == (uid_t)sc(SYS_getuid, 0, 0, 0, 0, 0) && !(st.st_mode & 0077) &&
st.st_size == sizeof table && sc(SYS_read, fd, (long)&table, sizeof table, 0, 0) == sizeof table;
sc(SYS_close, fd, 0, 0, 0, 0);
if (!valid || table.magic != 0x324356534444ULL || table.api != (unsigned)svc_android_api ||
!table.count || table.count > 256 || table.count > 256 - svc_planned) return 0;
for (unsigned i = 0; i < table.count; i++) {
uint64_t offset = table.offset[i];
int segment = svc_segment(info, offset, 4, PF_R | PF_X);
if ((offset & 3) || (i && offset <= table.offset[i - 1]) || !segment) return 0;
const ElfW(Phdr) *p = &info->dlpi_phdr[segment - 1];
const uint32_t *text = (const uint32_t *)(info->dlpi_addr + p->p_vaddr);
size_t at = (offset - p->p_vaddr) / 4;
if (text[at] != 0xd4000001U || !svc_supported(svc_site_number(text, at))) return 0;
}
for (unsigned i = 0; i < table.count; i++)
svc_plan[svc_planned++].site = (uint32_t *)(info->dlpi_addr + table.offset[i]);
return 1;
}
static void svc_cache_write(const struct dl_phdr_info *info, const char *path, size_t first) {
if (svc_planned == first || svc_planned == 256) return;
struct svc_cached_sites table = {.magic = 0x324356534444ULL, .api = svc_android_api, .count = svc_planned - first};
for (unsigned i = 0; i < table.count; i++)
table.offset[i] = (uintptr_t)svc_plan[first + i].site - info->dlpi_addr;
char temporary[256];
int n = snprintf(temporary, sizeof temporary, "%s.part-%d", path, getpid());
if (n <= 0 || n >= (int)sizeof temporary) return;
long fd = fp_open(AT_FDCWD, temporary, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0600);
if (fd < 0) return;
long written = sc(SYS_write, fd, (long)&table, sizeof table, 0, 0);
sc(SYS_close, fd, 0, 0, 0, 0);
if (written == sizeof table) fp_rename(AT_FDCWD, temporary, AT_FDCWD, path, 0);
fp_unlink(AT_FDCWD, temporary, 0);
}