18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO
48c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 Andrew Lutomirski
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This tests __kernel_vsyscall's unwind info.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define _GNU_SOURCE
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <features.h>
128c2ecf20Sopenharmony_ci#include <stdio.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "helpers.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciint main()
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	/* We need getauxval(). */
218c2ecf20Sopenharmony_ci	printf("[SKIP]\tGLIBC before 2.16 cannot compile this test\n");
228c2ecf20Sopenharmony_ci	return 0;
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#else
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <sys/time.h>
288c2ecf20Sopenharmony_ci#include <stdlib.h>
298c2ecf20Sopenharmony_ci#include <syscall.h>
308c2ecf20Sopenharmony_ci#include <unistd.h>
318c2ecf20Sopenharmony_ci#include <string.h>
328c2ecf20Sopenharmony_ci#include <inttypes.h>
338c2ecf20Sopenharmony_ci#include <sys/mman.h>
348c2ecf20Sopenharmony_ci#include <signal.h>
358c2ecf20Sopenharmony_ci#include <sys/ucontext.h>
368c2ecf20Sopenharmony_ci#include <err.h>
378c2ecf20Sopenharmony_ci#include <stddef.h>
388c2ecf20Sopenharmony_ci#include <stdbool.h>
398c2ecf20Sopenharmony_ci#include <sys/ptrace.h>
408c2ecf20Sopenharmony_ci#include <sys/user.h>
418c2ecf20Sopenharmony_ci#include <link.h>
428c2ecf20Sopenharmony_ci#include <sys/auxv.h>
438c2ecf20Sopenharmony_ci#include <dlfcn.h>
448c2ecf20Sopenharmony_ci#include <unwind.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
478c2ecf20Sopenharmony_ci		       int flags)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct sigaction sa;
508c2ecf20Sopenharmony_ci	memset(&sa, 0, sizeof(sa));
518c2ecf20Sopenharmony_ci	sa.sa_sigaction = handler;
528c2ecf20Sopenharmony_ci	sa.sa_flags = SA_SIGINFO | flags;
538c2ecf20Sopenharmony_ci	sigemptyset(&sa.sa_mask);
548c2ecf20Sopenharmony_ci	if (sigaction(sig, &sa, 0))
558c2ecf20Sopenharmony_ci		err(1, "sigaction");
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic volatile sig_atomic_t nerrs;
598c2ecf20Sopenharmony_cistatic unsigned long sysinfo;
608c2ecf20Sopenharmony_cistatic bool got_sysinfo = false;
618c2ecf20Sopenharmony_cistatic unsigned long return_address;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct unwind_state {
648c2ecf20Sopenharmony_ci	unsigned long ip;	/* trap source */
658c2ecf20Sopenharmony_ci	int depth;		/* -1 until we hit the trap source */
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci_Unwind_Reason_Code trace_fn(struct _Unwind_Context * ctx, void *opaque)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct unwind_state *state = opaque;
718c2ecf20Sopenharmony_ci	unsigned long ip = _Unwind_GetIP(ctx);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (state->depth == -1) {
748c2ecf20Sopenharmony_ci		if (ip == state->ip)
758c2ecf20Sopenharmony_ci			state->depth = 0;
768c2ecf20Sopenharmony_ci		else
778c2ecf20Sopenharmony_ci			return _URC_NO_REASON;	/* Not there yet */
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci	printf("\t  0x%lx\n", ip);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (ip == return_address) {
828c2ecf20Sopenharmony_ci		/* Here we are. */
838c2ecf20Sopenharmony_ci		unsigned long eax = _Unwind_GetGR(ctx, 0);
848c2ecf20Sopenharmony_ci		unsigned long ecx = _Unwind_GetGR(ctx, 1);
858c2ecf20Sopenharmony_ci		unsigned long edx = _Unwind_GetGR(ctx, 2);
868c2ecf20Sopenharmony_ci		unsigned long ebx = _Unwind_GetGR(ctx, 3);
878c2ecf20Sopenharmony_ci		unsigned long ebp = _Unwind_GetGR(ctx, 5);
888c2ecf20Sopenharmony_ci		unsigned long esi = _Unwind_GetGR(ctx, 6);
898c2ecf20Sopenharmony_ci		unsigned long edi = _Unwind_GetGR(ctx, 7);
908c2ecf20Sopenharmony_ci		bool ok = (eax == SYS_getpid || eax == getpid()) &&
918c2ecf20Sopenharmony_ci			ebx == 1 && ecx == 2 && edx == 3 &&
928c2ecf20Sopenharmony_ci			esi == 4 && edi == 5 && ebp == 6;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		if (!ok)
958c2ecf20Sopenharmony_ci			nerrs++;
968c2ecf20Sopenharmony_ci		printf("[%s]\t  NR = %ld, args = %ld, %ld, %ld, %ld, %ld, %ld\n",
978c2ecf20Sopenharmony_ci		       (ok ? "OK" : "FAIL"),
988c2ecf20Sopenharmony_ci		       eax, ebx, ecx, edx, esi, edi, ebp);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		return _URC_NORMAL_STOP;
1018c2ecf20Sopenharmony_ci	} else {
1028c2ecf20Sopenharmony_ci		state->depth++;
1038c2ecf20Sopenharmony_ci		return _URC_NO_REASON;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic void sigtrap(int sig, siginfo_t *info, void *ctx_void)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	ucontext_t *ctx = (ucontext_t *)ctx_void;
1108c2ecf20Sopenharmony_ci	struct unwind_state state;
1118c2ecf20Sopenharmony_ci	unsigned long ip = ctx->uc_mcontext.gregs[REG_EIP];
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (!got_sysinfo && ip == sysinfo) {
1148c2ecf20Sopenharmony_ci		got_sysinfo = true;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		/* Find the return address. */
1178c2ecf20Sopenharmony_ci		return_address = *(unsigned long *)(unsigned long)ctx->uc_mcontext.gregs[REG_ESP];
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		printf("\tIn vsyscall at 0x%lx, returning to 0x%lx\n",
1208c2ecf20Sopenharmony_ci		       ip, return_address);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!got_sysinfo)
1248c2ecf20Sopenharmony_ci		return;		/* Not there yet */
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (ip == return_address) {
1278c2ecf20Sopenharmony_ci		ctx->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
1288c2ecf20Sopenharmony_ci		printf("\tVsyscall is done\n");
1298c2ecf20Sopenharmony_ci		return;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	printf("\tSIGTRAP at 0x%lx\n", ip);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	state.ip = ip;
1358c2ecf20Sopenharmony_ci	state.depth = -1;
1368c2ecf20Sopenharmony_ci	_Unwind_Backtrace(trace_fn, &state);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciint main()
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	sysinfo = getauxval(AT_SYSINFO);
1428c2ecf20Sopenharmony_ci	printf("\tAT_SYSINFO is 0x%lx\n", sysinfo);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	Dl_info info;
1458c2ecf20Sopenharmony_ci	if (!dladdr((void *)sysinfo, &info)) {
1468c2ecf20Sopenharmony_ci		printf("[WARN]\tdladdr failed on AT_SYSINFO\n");
1478c2ecf20Sopenharmony_ci	} else {
1488c2ecf20Sopenharmony_ci		printf("[OK]\tAT_SYSINFO maps to %s, loaded at 0x%p\n",
1498c2ecf20Sopenharmony_ci		       info.dli_fname, info.dli_fbase);
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	sethandler(SIGTRAP, sigtrap, 0);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	syscall(SYS_getpid);  /* Force symbol binding without TF set. */
1558c2ecf20Sopenharmony_ci	printf("[RUN]\tSet TF and check a fast syscall\n");
1568c2ecf20Sopenharmony_ci	set_eflags(get_eflags() | X86_EFLAGS_TF);
1578c2ecf20Sopenharmony_ci	syscall(SYS_getpid, 1, 2, 3, 4, 5, 6);
1588c2ecf20Sopenharmony_ci	if (!got_sysinfo) {
1598c2ecf20Sopenharmony_ci		set_eflags(get_eflags() & ~X86_EFLAGS_TF);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		/*
1628c2ecf20Sopenharmony_ci		 * The most likely cause of this is that you're on Debian or
1638c2ecf20Sopenharmony_ci		 * a Debian-based distro, you're missing libc6-i686, and you're
1648c2ecf20Sopenharmony_ci		 * affected by libc/19006 (https://sourceware.org/PR19006).
1658c2ecf20Sopenharmony_ci		 */
1668c2ecf20Sopenharmony_ci		printf("[WARN]\tsyscall(2) didn't enter AT_SYSINFO\n");
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (get_eflags() & X86_EFLAGS_TF) {
1708c2ecf20Sopenharmony_ci		printf("[FAIL]\tTF is still set\n");
1718c2ecf20Sopenharmony_ci		nerrs++;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (nerrs) {
1758c2ecf20Sopenharmony_ci		printf("[FAIL]\tThere were errors\n");
1768c2ecf20Sopenharmony_ci		return 1;
1778c2ecf20Sopenharmony_ci	} else {
1788c2ecf20Sopenharmony_ci		printf("[OK]\tAll is well\n");
1798c2ecf20Sopenharmony_ci		return 0;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci#endif	/* New enough libc */
184