/*- * Copyright (c) 2002 - 2003 NetGroup, Politecnico di Torino (Italy) * Copyright (c) 2005 Jung-uk Kim * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Politecnico di Torino nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS intERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD: head/sys/i386/i386/bpf_jit_machdep.c 153151 2005-12-06 02:58:12Z jkim $"); #include "opt_bpf.h" #include #include #include #include #include #include #include #include #include #include bpf_filter_func bpf_jit_compile(struct bpf_insn *, u_int, int *); /* * emit routine to update the jump table */ static void emit_length(bpf_bin_stream *stream, u_int value, u_int len) { (stream->refs)[stream->bpf_pc] += len; stream->cur_ip += len; } /* * emit routine to output the actual binary code */ static void emit_code(bpf_bin_stream *stream, u_int value, u_int len) { switch (len) { case 1: stream->ibuf[stream->cur_ip] = (u_char)value; stream->cur_ip++; break; case 2: *((u_short *)(stream->ibuf + stream->cur_ip)) = (u_short)value; stream->cur_ip += 2; break; case 4: *((u_int *)(stream->ibuf + stream->cur_ip)) = value; stream->cur_ip += 4; break; } return; } /* * Function that does the real stuff */ bpf_filter_func bpf_jit_compile(struct bpf_insn *prog, u_int nins, int *mem) { struct bpf_insn *ins; u_int i, pass; bpf_bin_stream stream; /* * NOTE: do not modify the name of this variable, as it's used by * the macros to emit code. */ emit_func emitm; /* Allocate the reference table for the jumps */ stream.refs = (u_int *)malloc((nins + 1) * sizeof(u_int), M_BPFJIT, M_WAITOK); if (stream.refs == NULL) return NULL; /* Reset the reference table */ for (i = 0; i < nins + 1; i++) stream.refs[i] = 0; stream.cur_ip = 0; stream.bpf_pc = 0; /* * the first pass will emit the lengths of the instructions * to create the reference table */ emitm = emit_length; pass = 0; for (;;) { ins = prog; /* create the procedure header */ PUSH(EBP); MOVrd(EBP, ESP); PUSH(EDI); PUSH(ESI); PUSH(EBX); MOVodd(EBX, EBP, 8); for (i = 0; i < nins; i++) { stream.bpf_pc++; switch (ins->code) { default: return NULL; case BPF_RET|BPF_K: MOVid(EAX, ins->k); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); break; case BPF_RET|BPF_A: POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); break; case BPF_LD|BPF_W|BPF_ABS: MOVid(ECX, ins->k); MOVrd(ESI, ECX); ADDib(ECX, sizeof(int)); CMPodd(ECX, EBP, 0x10); JLEb(7); ZERO_EAX(); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVobd(EAX, EBX, ESI); BSWAP(EAX); break; case BPF_LD|BPF_H|BPF_ABS: ZERO_EAX(); MOVid(ECX, ins->k); MOVrd(ESI, ECX); ADDib(ECX, sizeof(short)); CMPodd(ECX, EBP, 0x10); JLEb(5); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVobw(AX, EBX, ESI); SWAP_AX(); break; case BPF_LD|BPF_B|BPF_ABS: ZERO_EAX(); MOVid(ECX, ins->k); CMPodd(ECX, EBP, 0x10); JLEb(5); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVobb(AL, EBX, ECX); break; case BPF_LD|BPF_W|BPF_LEN: MOVodd(EAX, EBP, 0xc); break; case BPF_LDX|BPF_W|BPF_LEN: MOVodd(EDX, EBP, 0xc); break; case BPF_LD|BPF_W|BPF_IND: MOVid(ECX, ins->k); ADDrd(ECX, EDX); MOVrd(ESI, ECX); ADDib(ECX, sizeof(int)); CMPodd(ECX, EBP, 0x10); JLEb(7); ZERO_EAX(); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVobd(EAX, EBX, ESI); BSWAP(EAX); break; case BPF_LD|BPF_H|BPF_IND: ZERO_EAX(); MOVid(ECX, ins->k); ADDrd(ECX, EDX); MOVrd(ESI, ECX); ADDib(ECX, sizeof(short)); CMPodd(ECX, EBP, 0x10); JLEb(5); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVobw(AX, EBX, ESI); SWAP_AX(); break; case BPF_LD|BPF_B|BPF_IND: ZERO_EAX(); MOVid(ECX, ins->k); ADDrd(ECX, EDX); CMPodd(ECX, EBP, 0x10); JLEb(5); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVobb(AL, EBX, ECX); break; case BPF_LDX|BPF_MSH|BPF_B: MOVid(ECX, ins->k); CMPodd(ECX, EBP, 0x10); JLEb(7); ZERO_EAX(); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVid(EDX, 0); MOVobb(DL, EBX, ECX); ANDib(DL, 0xf); SHLib(EDX, 2); break; case BPF_LD|BPF_IMM: MOVid(EAX, ins->k); break; case BPF_LDX|BPF_IMM: MOVid(EDX, ins->k); break; case BPF_LD|BPF_MEM: MOVid(ECX, (uintptr_t)mem); MOVid(ESI, ins->k * 4); MOVobd(EAX, ECX, ESI); break; case BPF_LDX|BPF_MEM: MOVid(ECX, (uintptr_t)mem); MOVid(ESI, ins->k * 4); MOVobd(EDX, ECX, ESI); break; case BPF_ST: /* * XXX this command and the following could * be optimized if the previous instruction * was already of this type */ MOVid(ECX, (uintptr_t)mem); MOVid(ESI, ins->k * 4); MOVomd(ECX, ESI, EAX); break; case BPF_STX: MOVid(ECX, (uintptr_t)mem); MOVid(ESI, ins->k * 4); MOVomd(ECX, ESI, EDX); break; case BPF_JMP|BPF_JA: JMP(stream.refs[stream.bpf_pc + ins->k] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JGT|BPF_K: CMPid(EAX, ins->k); /* 5 is the size of the following JMP */ JG(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc] + 5 ); JMP(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JGE|BPF_K: CMPid(EAX, ins->k); JGE(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc] + 5); JMP(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JEQ|BPF_K: CMPid(EAX, ins->k); JE(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc] + 5); JMP(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JSET|BPF_K: MOVrd(ECX, EAX); ANDid(ECX, ins->k); JE(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc] + 5); JMP(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JGT|BPF_X: CMPrd(EAX, EDX); JA(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc] + 5); JMP(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JGE|BPF_X: CMPrd(EAX, EDX); JAE(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc] + 5); JMP(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JEQ|BPF_X: CMPrd(EAX, EDX); JE(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc] + 5); JMP(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc]); break; case BPF_JMP|BPF_JSET|BPF_X: MOVrd(ECX, EAX); ANDrd(ECX, EDX); JE(stream.refs[stream.bpf_pc + ins->jf] - stream.refs[stream.bpf_pc] + 5); JMP(stream.refs[stream.bpf_pc + ins->jt] - stream.refs[stream.bpf_pc]); break; case BPF_ALU|BPF_ADD|BPF_X: ADDrd(EAX, EDX); break; case BPF_ALU|BPF_SUB|BPF_X: SUBrd(EAX, EDX); break; case BPF_ALU|BPF_MUL|BPF_X: MOVrd(ECX, EDX); MULrd(EDX); MOVrd(EDX, ECX); break; case BPF_ALU|BPF_DIV|BPF_X: CMPid(EDX, 0); JNEb(7); ZERO_EAX(); POP(EBX); POP(ESI); POP(EDI); LEAVE_RET(); MOVrd(ECX, EDX); MOVid(EDX, 0); DIVrd(ECX); MOVrd(EDX, ECX); break; case BPF_ALU|BPF_AND|BPF_X: ANDrd(EAX, EDX); break; case BPF_ALU|BPF_OR|BPF_X: ORrd(EAX, EDX); break; case BPF_ALU|BPF_LSH|BPF_X: MOVrd(ECX, EDX); SHL_CLrb(EAX); break; case BPF_ALU|BPF_RSH|BPF_X: MOVrd(ECX, EDX); SHR_CLrb(EAX); break; case BPF_ALU|BPF_ADD|BPF_K: ADD_EAXi(ins->k); break; case BPF_ALU|BPF_SUB|BPF_K: SUB_EAXi(ins->k); break; case BPF_ALU|BPF_MUL|BPF_K: MOVrd(ECX, EDX); MOVid(EDX, ins->k); MULrd(EDX); MOVrd(EDX, ECX); break; case BPF_ALU|BPF_DIV|BPF_K: MOVrd(ECX, EDX); MOVid(EDX, 0); MOVid(ESI, ins->k); DIVrd(ESI); MOVrd(EDX, ECX); break; case BPF_ALU|BPF_AND|BPF_K: ANDid(EAX, ins->k); break; case BPF_ALU|BPF_OR|BPF_K: ORid(EAX, ins->k); break; case BPF_ALU|BPF_LSH|BPF_K: SHLib(EAX, (ins->k) & 255); break; case BPF_ALU|BPF_RSH|BPF_K: SHRib(EAX, (ins->k) & 255); break; case BPF_ALU|BPF_NEG: NEGd(EAX); break; case BPF_MISC|BPF_TAX: MOVrd(EDX, EAX); break; case BPF_MISC|BPF_TXA: MOVrd(EAX, EDX); break; } ins++; } pass++; if (pass == 2) break; stream.ibuf = (char *)malloc(stream.cur_ip, M_BPFJIT, M_WAITOK); if (stream.ibuf == NULL) { free(stream.refs, M_BPFJIT); return NULL; } /* * modify the reference table to contain the offsets and * not the lengths of the instructions */ for (i = 1; i < nins + 1; i++) stream.refs[i] += stream.refs[i - 1]; /* Reset the counters */ stream.cur_ip = 0; stream.bpf_pc = 0; /* the second pass creates the actual code */ emitm = emit_code; } /* * the reference table is needed only during compilation, * now we can free it */ free(stream.refs, M_BPFJIT); return (bpf_filter_func)stream.ibuf; }