Skip to content

Commit

Permalink
Add support for executing the FreeBSD 1/i386 a.out binaries on amd64.
Browse files Browse the repository at this point in the history
In particular:
- implement compat shims for old stat(2) variants and ogetdirentries(2);
- implement delivery of signals with ancient stack frame layout and
  corresponding sigreturn(2);
- implement old getpagesize(2);
- provide a user-mode trampoline and LDT call gate for lcall $7,$0;
- port a.out image activator and connect it to the build as a module
  on amd64.

The changes are hidden under COMPAT_43.

MFC after:   1 month
  • Loading branch information
kostikbel committed Apr 1, 2011
1 parent 6d6007b commit 7c2eaa2
Show file tree
Hide file tree
Showing 16 changed files with 504 additions and 40 deletions.
11 changes: 11 additions & 0 deletions sys/amd64/ia32/ia32_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,14 @@ freebsd32_sysarch(struct thread *td, struct freebsd32_sysarch_args *uap)
return (sysarch(td, &uap1));
}
}

#ifdef COMPAT_43
int
ofreebsd32_getpagesize(struct thread *td,
struct ofreebsd32_getpagesize_args *uap)
{

td->td_retval[0] = IA32_PAGE_SIZE;
return (0);
}
#endif
170 changes: 170 additions & 0 deletions sys/amd64/ia32/ia32_signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,109 @@ freebsd32_swapcontext(struct thread *td, struct freebsd32_swapcontext_args *uap)
* frame pointer, it returns to the user
* specified pc, psl.
*/

#ifdef COMPAT_43
static void
ia32_osendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
{
struct ia32_sigframe3 sf, *fp;
struct proc *p;
struct thread *td;
struct sigacts *psp;
struct trapframe *regs;
int sig;
int oonstack;

td = curthread;
p = td->td_proc;
PROC_LOCK_ASSERT(p, MA_OWNED);
sig = ksi->ksi_signo;
psp = p->p_sigacts;
mtx_assert(&psp->ps_mtx, MA_OWNED);
regs = td->td_frame;
oonstack = sigonstack(regs->tf_rsp);

/* Allocate space for the signal handler context. */
if ((td->td_pflags & TDP_ALTSTACK) && !oonstack &&
SIGISMEMBER(psp->ps_sigonstack, sig)) {
fp = (struct ia32_sigframe3 *)(td->td_sigstk.ss_sp +
td->td_sigstk.ss_size - sizeof(sf));
td->td_sigstk.ss_flags |= SS_ONSTACK;
} else
fp = (struct ia32_sigframe3 *)regs->tf_rsp - 1;

/* Translate the signal if appropriate. */
if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize)
sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)];

/* Build the argument list for the signal handler. */
sf.sf_signum = sig;
sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc;
if (SIGISMEMBER(psp->ps_siginfo, sig)) {
/* Signal handler installed with SA_SIGINFO. */
sf.sf_arg2 = (register_t)&fp->sf_siginfo;
sf.sf_siginfo.si_signo = sig;
sf.sf_siginfo.si_code = ksi->ksi_code;
sf.sf_ah = (uintptr_t)catcher;
} else {
/* Old FreeBSD-style arguments. */
sf.sf_arg2 = ksi->ksi_code;
sf.sf_addr = (register_t)ksi->ksi_addr;
sf.sf_ah = (uintptr_t)catcher;
}
mtx_unlock(&psp->ps_mtx);
PROC_UNLOCK(p);

/* Save most if not all of trap frame. */
sf.sf_siginfo.si_sc.sc_eax = regs->tf_rax;
sf.sf_siginfo.si_sc.sc_ebx = regs->tf_rbx;
sf.sf_siginfo.si_sc.sc_ecx = regs->tf_rcx;
sf.sf_siginfo.si_sc.sc_edx = regs->tf_rdx;
sf.sf_siginfo.si_sc.sc_esi = regs->tf_rsi;
sf.sf_siginfo.si_sc.sc_edi = regs->tf_rdi;
sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs;
sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds;
sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss;
sf.sf_siginfo.si_sc.sc_es = regs->tf_es;
sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs;
sf.sf_siginfo.si_sc.sc_gs = regs->tf_gs;
sf.sf_siginfo.si_sc.sc_isp = regs->tf_rsp;

/* Build the signal context to be used by osigreturn(). */
sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0;
SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask);
sf.sf_siginfo.si_sc.sc_esp = regs->tf_rsp;
sf.sf_siginfo.si_sc.sc_ebp = regs->tf_rbp;
sf.sf_siginfo.si_sc.sc_eip = regs->tf_rip;
sf.sf_siginfo.si_sc.sc_eflags = regs->tf_rflags;
sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno;
sf.sf_siginfo.si_sc.sc_err = regs->tf_err;

/*
* Copy the sigframe out to the user's stack.
*/
if (copyout(&sf, fp, sizeof(*fp)) != 0) {
#ifdef DEBUG
printf("process %ld has trashed its stack\n", (long)p->p_pid);
#endif
PROC_LOCK(p);
sigexit(td, SIGILL);
}

regs->tf_rsp = (uintptr_t)fp;
regs->tf_rip = p->p_sysent->sv_psstrings - sz_ia32_osigcode;
regs->tf_rflags &= ~(PSL_T | PSL_D);
regs->tf_cs = _ucode32sel;
regs->tf_ds = _udatasel;
regs->tf_es = _udatasel;
regs->tf_fs = _udatasel;
regs->tf_ss = _udatasel;
set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
PROC_LOCK(p);
mtx_lock(&psp->ps_mtx);
}
#endif

#ifdef COMPAT_FREEBSD4
static void
freebsd4_ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
Expand Down Expand Up @@ -440,6 +543,12 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
freebsd4_ia32_sendsig(catcher, ksi, mask);
return;
}
#endif
#ifdef COMPAT_43
if (SIGISMEMBER(psp->ps_osigset, sig)) {
ia32_osendsig(catcher, ksi, mask);
return;
}
#endif
mtx_assert(&psp->ps_mtx, MA_OWNED);
regs = td->td_frame;
Expand Down Expand Up @@ -547,6 +656,64 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
* make sure that the user has not modified the
* state to gain improper privileges.
*/

#ifdef COMPAT_43
int
ofreebsd32_sigreturn(struct thread *td, struct ofreebsd32_sigreturn_args *uap)
{
struct ia32_sigcontext3 sc, *scp;
struct trapframe *regs;
int eflags, error;
ksiginfo_t ksi;

regs = td->td_frame;
error = copyin(uap->sigcntxp, &sc, sizeof(sc));
if (error != 0)
return (error);
scp = &sc;
eflags = scp->sc_eflags;
if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_rflags & ~PSL_RF)) {
return (EINVAL);
}
if (!CS_SECURE(scp->sc_cs)) {
ksiginfo_init_trap(&ksi);
ksi.ksi_signo = SIGBUS;
ksi.ksi_code = BUS_OBJERR;
ksi.ksi_trapno = T_PROTFLT;
ksi.ksi_addr = (void *)regs->tf_rip;
trapsignal(td, &ksi);
return (EINVAL);
}
regs->tf_ds = scp->sc_ds;
regs->tf_es = scp->sc_es;
regs->tf_fs = scp->sc_fs;
regs->tf_gs = scp->sc_gs;

regs->tf_rax = scp->sc_eax;
regs->tf_rbx = scp->sc_ebx;
regs->tf_rcx = scp->sc_ecx;
regs->tf_rdx = scp->sc_edx;
regs->tf_rsi = scp->sc_esi;
regs->tf_rdi = scp->sc_edi;
regs->tf_cs = scp->sc_cs;
regs->tf_ss = scp->sc_ss;
regs->tf_rbp = scp->sc_ebp;
regs->tf_rsp = scp->sc_esp;
regs->tf_rip = scp->sc_eip;
regs->tf_rflags = eflags;

if (scp->sc_onstack & 1)
td->td_sigstk.ss_flags |= SS_ONSTACK;
else
td->td_sigstk.ss_flags &= ~SS_ONSTACK;

kern_sigprocmask(td, SIG_SETMASK, (sigset_t *)&scp->sc_mask, NULL,
SIGPROCMASK_OLD);
set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
return (EJUSTRETURN);
}
#endif

#ifdef COMPAT_FREEBSD4
/*
* MPSAFE
Expand Down Expand Up @@ -734,6 +901,9 @@ ia32_setregs(struct thread *td, struct image_params *imgp, u_long stack)
user_ldt_free(td);
else
mtx_unlock(&dt_lock);
#ifdef COMPAT_43
setup_lcall_gate();
#endif

pcb->pcb_fsbase = 0;
pcb->pcb_gsbase = 0;
Expand Down
37 changes: 37 additions & 0 deletions sys/amd64/ia32/ia32_sigtramp.S
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,35 @@ freebsd4_ia32_sigcode:
jmp 1b
#endif

#ifdef COMPAT_43
ALIGN_TEXT
ia32_osigcode:
calll *IA32_SIGF_HANDLER(%esp)/* call signal handler */
leal IA32_SIGF_SC(%esp),%eax /* get sigcontext */
pushl %eax
movl $103,%eax /* 3.x SYS_sigreturn */
pushl %eax /* junk to fake return addr. */
int $0x80 /* enter kernel with args */
1:
jmp 1b


ALIGN_TEXT
lcall_tramp:
pushl %ebp
movl %esp,%ebp
pushl 0x24(%ebp) /* arg 6 */
pushl 0x20(%ebp)
pushl 0x1c(%ebp)
pushl 0x18(%ebp)
pushl 0x14(%ebp)
pushl 0x10(%ebp) /* arg 1 */
pushl 0xc(%ebp) /* gap */
int $0x80
leave
lretl
#endif

ALIGN_TEXT
esigcode:

Expand All @@ -78,3 +107,11 @@ sz_ia32_sigcode:
sz_freebsd4_ia32_sigcode:
.long esigcode-freebsd4_ia32_sigcode
#endif
#ifdef COMPAT_43
.globl sz_ia32_osigcode
sz_ia32_osigcode:
.long esigcode-ia32_osigcode
.globl sz_lcall_tramp
sz_lcall_tramp:
.long esigcode-lcall_tramp
#endif
53 changes: 53 additions & 0 deletions sys/amd64/ia32/ia32_syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
*/

#include "opt_clock.h"
#include "opt_compat.h"
#include "opt_cpu.h"
#include "opt_isa.h"

Expand Down Expand Up @@ -78,7 +79,17 @@ __FBSDID("$FreeBSD$");
#include <machine/intr_machdep.h>
#include <machine/md_var.h>

#include <compat/freebsd32/freebsd32_signal.h>
#include <compat/freebsd32/freebsd32_util.h>
#include <compat/ia32/ia32_signal.h>
#include <machine/psl.h>
#include <machine/segments.h>
#include <machine/specialreg.h>
#include <machine/sysarch.h>
#include <machine/frame.h>
#include <machine/md_var.h>
#include <machine/pcb.h>
#include <machine/cpufunc.h>

#define IDTVEC(name) __CONCAT(X,name)

Expand Down Expand Up @@ -198,3 +209,45 @@ ia32_syscall_disable(void *dummy)

SYSINIT(ia32_syscall, SI_SUB_EXEC, SI_ORDER_ANY, ia32_syscall_enable, NULL);
SYSUNINIT(ia32_syscall, SI_SUB_EXEC, SI_ORDER_ANY, ia32_syscall_disable, NULL);

#ifdef COMPAT_43
int
setup_lcall_gate(void)
{
struct i386_ldt_args uap;
struct user_segment_descriptor descs[2];
struct gate_descriptor *ssd;
uint32_t lcall_addr;
int error;

bzero(&uap, sizeof(uap));
uap.start = 0;
uap.num = 2;

/*
* This is the easiest way to cut the space for system
* descriptor in ldt. Manually adjust the descriptor type to
* the call gate later.
*/
bzero(&descs[0], sizeof(descs));
descs[0].sd_type = SDT_SYSNULL;
descs[1].sd_type = SDT_SYSNULL;
error = amd64_set_ldt(curthread, &uap, descs);
if (error != 0)
return (error);

lcall_addr = curproc->p_sysent->sv_psstrings - sz_lcall_tramp;
mtx_lock(&dt_lock);
ssd = (struct gate_descriptor *)(curproc->p_md.md_ldt->ldt_base);
bzero(ssd, sizeof(*ssd));
ssd->gd_looffset = lcall_addr;
ssd->gd_hioffset = lcall_addr >> 16;
ssd->gd_selector = _ucode32sel;
ssd->gd_type = SDT_SYSCGT;
ssd->gd_dpl = SEL_UPL;
ssd->gd_p = 1;
mtx_unlock(&dt_lock);

return (0);
}
#endif
Loading

0 comments on commit 7c2eaa2

Please sign in to comment.