1*387f9dfdSAndroid Build Coastguard Worker--[[ 2*387f9dfdSAndroid Build Coastguard WorkerCopyright 2016 Marek Vavrusa <[email protected]> 3*387f9dfdSAndroid Build Coastguard Worker 4*387f9dfdSAndroid Build Coastguard WorkerLicensed under the Apache License, Version 2.0 (the "License"); 5*387f9dfdSAndroid Build Coastguard Workeryou may not use this file except in compliance with the License. 6*387f9dfdSAndroid Build Coastguard WorkerYou may obtain a copy of the License at 7*387f9dfdSAndroid Build Coastguard Worker 8*387f9dfdSAndroid Build Coastguard Workerhttp://www.apache.org/licenses/LICENSE-2.0 9*387f9dfdSAndroid Build Coastguard Worker 10*387f9dfdSAndroid Build Coastguard WorkerUnless required by applicable law or agreed to in writing, software 11*387f9dfdSAndroid Build Coastguard Workerdistributed under the License is distributed on an "AS IS" BASIS, 12*387f9dfdSAndroid Build Coastguard WorkerWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*387f9dfdSAndroid Build Coastguard WorkerSee the License for the specific language governing permissions and 14*387f9dfdSAndroid Build Coastguard Workerlimitations under the License. 15*387f9dfdSAndroid Build Coastguard Worker]] 16*387f9dfdSAndroid Build Coastguard Worker-- LuaJIT to BPF bytecode compiler. 17*387f9dfdSAndroid Build Coastguard Worker-- 18*387f9dfdSAndroid Build Coastguard Worker-- The code generation phase is currently one-pass and produces: 19*387f9dfdSAndroid Build Coastguard Worker-- * Compiled code in BPF bytecode format (https://www.kernel.org/doc/Documentation/networking/filter.txt) 20*387f9dfdSAndroid Build Coastguard Worker-- * Variables with liveness analysis and other meta (spill information, compile-time value) 21*387f9dfdSAndroid Build Coastguard Worker-- 22*387f9dfdSAndroid Build Coastguard Worker-- The code generator optimises as much as possible in single pass: 23*387f9dfdSAndroid Build Coastguard Worker-- * Fold compile-time expressions and constant propagation 24*387f9dfdSAndroid Build Coastguard Worker-- * Basic control flow analysis with dead code elimination (based on compile-time expressions) 25*387f9dfdSAndroid Build Coastguard Worker-- * Single-pass optimistic register allocation 26*387f9dfdSAndroid Build Coastguard Worker-- 27*387f9dfdSAndroid Build Coastguard Worker-- The first pass doesn't have variable lifetime visibility yet, so it relies on rewriter for further 28*387f9dfdSAndroid Build Coastguard Worker-- optimisations such as: 29*387f9dfdSAndroid Build Coastguard Worker-- * Dead store elimination (first-pass doesn't know if/when the variable is going to be used) 30*387f9dfdSAndroid Build Coastguard Worker-- * Common sub-expression elimination (relies on DCE and liveness analysis) 31*387f9dfdSAndroid Build Coastguard Worker-- * Orphan JMP elimination (removing this in first pass would break previous JMP targets) 32*387f9dfdSAndroid Build Coastguard Worker-- * Better register allocation (needs to be recomputed after optimisations) 33*387f9dfdSAndroid Build Coastguard Worker 34*387f9dfdSAndroid Build Coastguard Workerlocal ffi = require('ffi') 35*387f9dfdSAndroid Build Coastguard Workerlocal bit = require('bit') 36*387f9dfdSAndroid Build Coastguard Workerlocal S = require('syscall') 37*387f9dfdSAndroid Build Coastguard Workerlocal bytecode = require('bpf.ljbytecode') 38*387f9dfdSAndroid Build Coastguard Workerlocal cdef = require('bpf.cdef') 39*387f9dfdSAndroid Build Coastguard Workerlocal proto = require('bpf.proto') 40*387f9dfdSAndroid Build Coastguard Workerlocal builtins = require('bpf.builtins') 41*387f9dfdSAndroid Build Coastguard Worker 42*387f9dfdSAndroid Build Coastguard Worker-- Constants 43*387f9dfdSAndroid Build Coastguard Workerlocal ALWAYS, NEVER = -1, -2 44*387f9dfdSAndroid Build Coastguard Workerlocal BPF = ffi.typeof('struct bpf') 45*387f9dfdSAndroid Build Coastguard Workerlocal HELPER = ffi.typeof('struct bpf_func_id') 46*387f9dfdSAndroid Build Coastguard Worker 47*387f9dfdSAndroid Build Coastguard Worker-- Symbolic table of constant expressions over numbers 48*387f9dfdSAndroid Build Coastguard Workerlocal const_expr = { 49*387f9dfdSAndroid Build Coastguard Worker ADD = function (a, b) return a + b end, 50*387f9dfdSAndroid Build Coastguard Worker SUB = function (a, b) return a - b end, 51*387f9dfdSAndroid Build Coastguard Worker DIV = function (a, b) return a / b end, 52*387f9dfdSAndroid Build Coastguard Worker MOD = function (a, b) return a % b end, 53*387f9dfdSAndroid Build Coastguard Worker JEQ = function (a, b) return a == b end, 54*387f9dfdSAndroid Build Coastguard Worker JNE = function (a, b) return a ~= b end, 55*387f9dfdSAndroid Build Coastguard Worker JGE = function (a, b) return a >= b end, 56*387f9dfdSAndroid Build Coastguard Worker JGT = function (a, b) return a > b end, 57*387f9dfdSAndroid Build Coastguard Worker} 58*387f9dfdSAndroid Build Coastguard Worker 59*387f9dfdSAndroid Build Coastguard Workerlocal const_width = { 60*387f9dfdSAndroid Build Coastguard Worker [1] = BPF.B, [2] = BPF.H, [4] = BPF.W, [8] = BPF.DW, 61*387f9dfdSAndroid Build Coastguard Worker} 62*387f9dfdSAndroid Build Coastguard Worker 63*387f9dfdSAndroid Build Coastguard Worker-- Built-ins that are strict only (never compile-time expandable) 64*387f9dfdSAndroid Build Coastguard Workerlocal builtins_strict = { 65*387f9dfdSAndroid Build Coastguard Worker [ffi.new] = true, 66*387f9dfdSAndroid Build Coastguard Worker [print] = true, 67*387f9dfdSAndroid Build Coastguard Worker} 68*387f9dfdSAndroid Build Coastguard Worker 69*387f9dfdSAndroid Build Coastguard Worker-- Deep copy a table 70*387f9dfdSAndroid Build Coastguard Workerlocal function table_copy(t) 71*387f9dfdSAndroid Build Coastguard Worker local copy = {} 72*387f9dfdSAndroid Build Coastguard Worker for n,v in pairs(t) do 73*387f9dfdSAndroid Build Coastguard Worker if type(v) == 'table' then 74*387f9dfdSAndroid Build Coastguard Worker v = table_copy(v) 75*387f9dfdSAndroid Build Coastguard Worker end 76*387f9dfdSAndroid Build Coastguard Worker copy[n] = v 77*387f9dfdSAndroid Build Coastguard Worker end 78*387f9dfdSAndroid Build Coastguard Worker return copy 79*387f9dfdSAndroid Build Coastguard Workerend 80*387f9dfdSAndroid Build Coastguard Worker 81*387f9dfdSAndroid Build Coastguard Worker-- Return true if the constant part is a proxy 82*387f9dfdSAndroid Build Coastguard Workerlocal function is_proxy(x) 83*387f9dfdSAndroid Build Coastguard Worker return type(x) == 'table' and (x.__dissector or x.__map or x.__base) 84*387f9dfdSAndroid Build Coastguard Workerend 85*387f9dfdSAndroid Build Coastguard Worker 86*387f9dfdSAndroid Build Coastguard Worker-- Create compiler closure 87*387f9dfdSAndroid Build Coastguard Workerlocal function create_emitter(env, stackslots, params, param_types) 88*387f9dfdSAndroid Build Coastguard Worker 89*387f9dfdSAndroid Build Coastguard Workerlocal V = {} -- Variable tracking / register allocator 90*387f9dfdSAndroid Build Coastguard Workerlocal code = { -- Generated code 91*387f9dfdSAndroid Build Coastguard Worker pc = 0, bc_pc = 0, 92*387f9dfdSAndroid Build Coastguard Worker insn = ffi.new('struct bpf_insn[4096]'), 93*387f9dfdSAndroid Build Coastguard Worker fixup = {}, 94*387f9dfdSAndroid Build Coastguard Worker reachable = true, 95*387f9dfdSAndroid Build Coastguard Worker seen_cmp = nil, 96*387f9dfdSAndroid Build Coastguard Worker} 97*387f9dfdSAndroid Build Coastguard Workerlocal Vstate = {} -- Track variable layout at basic block exits 98*387f9dfdSAndroid Build Coastguard Worker 99*387f9dfdSAndroid Build Coastguard Worker-- Anything below this stack offset is free to use by caller 100*387f9dfdSAndroid Build Coastguard Worker-- @note: There is no tracking memory allocator, so the caller may 101*387f9dfdSAndroid Build Coastguard Worker-- lower it for persistent objects, but such memory will never 102*387f9dfdSAndroid Build Coastguard Worker-- be reclaimed and the caller is responsible for resetting stack 103*387f9dfdSAndroid Build Coastguard Worker-- top whenever the memory below is free to be reused 104*387f9dfdSAndroid Build Coastguard Workerlocal stack_top = (stackslots + 1) * ffi.sizeof('uint64_t') 105*387f9dfdSAndroid Build Coastguard Worker 106*387f9dfdSAndroid Build Coastguard Workerlocal function emit(op, dst, src, off, imm) 107*387f9dfdSAndroid Build Coastguard Worker local ins = code.insn[code.pc] 108*387f9dfdSAndroid Build Coastguard Worker ins.code = op 109*387f9dfdSAndroid Build Coastguard Worker ins.dst_reg = dst 110*387f9dfdSAndroid Build Coastguard Worker ins.src_reg = src 111*387f9dfdSAndroid Build Coastguard Worker ins.off = off 112*387f9dfdSAndroid Build Coastguard Worker ins.imm = imm 113*387f9dfdSAndroid Build Coastguard Worker code.pc = code.pc + 1 114*387f9dfdSAndroid Build Coastguard Workerend 115*387f9dfdSAndroid Build Coastguard Worker 116*387f9dfdSAndroid Build Coastguard Workerlocal function reg_spill(var) 117*387f9dfdSAndroid Build Coastguard Worker local vinfo = V[var] 118*387f9dfdSAndroid Build Coastguard Worker assert(vinfo.reg, 'attempt to spill VAR that doesn\'t have an allocated register') 119*387f9dfdSAndroid Build Coastguard Worker vinfo.spill = (var + 1) * ffi.sizeof('uint64_t') -- Index by (variable number) * (register width) 120*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + BPF.DW, 10, vinfo.reg, -vinfo.spill, 0) 121*387f9dfdSAndroid Build Coastguard Worker vinfo.reg = nil 122*387f9dfdSAndroid Build Coastguard Workerend 123*387f9dfdSAndroid Build Coastguard Worker 124*387f9dfdSAndroid Build Coastguard Workerlocal function reg_fill(var, reg) 125*387f9dfdSAndroid Build Coastguard Worker local vinfo = V[var] 126*387f9dfdSAndroid Build Coastguard Worker assert(reg, 'attempt to fill variable to register but not register is allocated') 127*387f9dfdSAndroid Build Coastguard Worker assert(vinfo.spill, 'attempt to fill register with a VAR that isn\'t spilled') 128*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.LDX + BPF.DW, reg, 10, -vinfo.spill, 0) 129*387f9dfdSAndroid Build Coastguard Worker vinfo.reg = reg 130*387f9dfdSAndroid Build Coastguard Worker vinfo.spill = nil 131*387f9dfdSAndroid Build Coastguard Workerend 132*387f9dfdSAndroid Build Coastguard Worker 133*387f9dfdSAndroid Build Coastguard Worker-- Allocate a register (lazy simple allocator) 134*387f9dfdSAndroid Build Coastguard Workerlocal function reg_alloc(var, reg) 135*387f9dfdSAndroid Build Coastguard Worker -- Specific register requested, must spill/move existing variable 136*387f9dfdSAndroid Build Coastguard Worker if reg then 137*387f9dfdSAndroid Build Coastguard Worker for k,v in pairs(V) do -- Spill any variable that has this register 138*387f9dfdSAndroid Build Coastguard Worker if v.reg == reg and not v.shadow then 139*387f9dfdSAndroid Build Coastguard Worker reg_spill(k) 140*387f9dfdSAndroid Build Coastguard Worker break 141*387f9dfdSAndroid Build Coastguard Worker end 142*387f9dfdSAndroid Build Coastguard Worker end 143*387f9dfdSAndroid Build Coastguard Worker return reg 144*387f9dfdSAndroid Build Coastguard Worker end 145*387f9dfdSAndroid Build Coastguard Worker -- Find free or least recently used slot 146*387f9dfdSAndroid Build Coastguard Worker local last, last_seen, used = nil, 0xffff, 0 147*387f9dfdSAndroid Build Coastguard Worker for k,v in pairs(V) do 148*387f9dfdSAndroid Build Coastguard Worker if v.reg then 149*387f9dfdSAndroid Build Coastguard Worker if not v.live_to or v.live_to < last_seen then 150*387f9dfdSAndroid Build Coastguard Worker last, last_seen = k, v.live_to or last_seen 151*387f9dfdSAndroid Build Coastguard Worker end 152*387f9dfdSAndroid Build Coastguard Worker used = bit.bor(used, bit.lshift(1, v.reg)) 153*387f9dfdSAndroid Build Coastguard Worker end 154*387f9dfdSAndroid Build Coastguard Worker end 155*387f9dfdSAndroid Build Coastguard Worker -- Attempt to select a free register from R7-R9 (callee saved) 156*387f9dfdSAndroid Build Coastguard Worker local free = bit.bnot(used) 157*387f9dfdSAndroid Build Coastguard Worker if bit.band(free, 0x80) ~= 0 then reg = 7 158*387f9dfdSAndroid Build Coastguard Worker elseif bit.band(free,0x100) ~= 0 then reg = 8 159*387f9dfdSAndroid Build Coastguard Worker elseif bit.band(free,0x200) ~= 0 then reg = 9 160*387f9dfdSAndroid Build Coastguard Worker end 161*387f9dfdSAndroid Build Coastguard Worker -- Select another variable to be spilled 162*387f9dfdSAndroid Build Coastguard Worker if not reg then 163*387f9dfdSAndroid Build Coastguard Worker assert(last) 164*387f9dfdSAndroid Build Coastguard Worker reg = V[last].reg 165*387f9dfdSAndroid Build Coastguard Worker reg_spill(last) 166*387f9dfdSAndroid Build Coastguard Worker end 167*387f9dfdSAndroid Build Coastguard Worker assert(reg, 'VAR '..var..'fill/spill failed') 168*387f9dfdSAndroid Build Coastguard Worker return reg 169*387f9dfdSAndroid Build Coastguard Workerend 170*387f9dfdSAndroid Build Coastguard Worker 171*387f9dfdSAndroid Build Coastguard Worker-- Set new variable 172*387f9dfdSAndroid Build Coastguard Workerlocal function vset(var, reg, const, vtype) 173*387f9dfdSAndroid Build Coastguard Worker -- Must materialise all variables shadowing this variable slot, as it will be overwritten 174*387f9dfdSAndroid Build Coastguard Worker if V[var] and V[var].reg then 175*387f9dfdSAndroid Build Coastguard Worker for _, vinfo in pairs(V) do 176*387f9dfdSAndroid Build Coastguard Worker -- Shadowing variable MUST share the same type and attributes, 177*387f9dfdSAndroid Build Coastguard Worker -- but the register assignment may have changed 178*387f9dfdSAndroid Build Coastguard Worker if vinfo.shadow == var then 179*387f9dfdSAndroid Build Coastguard Worker vinfo.reg = V[var].reg 180*387f9dfdSAndroid Build Coastguard Worker vinfo.shadow = nil 181*387f9dfdSAndroid Build Coastguard Worker end 182*387f9dfdSAndroid Build Coastguard Worker end 183*387f9dfdSAndroid Build Coastguard Worker end 184*387f9dfdSAndroid Build Coastguard Worker -- Get precise type for CDATA or attempt to narrow numeric constant 185*387f9dfdSAndroid Build Coastguard Worker if not vtype and type(const) == 'cdata' then 186*387f9dfdSAndroid Build Coastguard Worker vtype = ffi.typeof(const) 187*387f9dfdSAndroid Build Coastguard Worker end 188*387f9dfdSAndroid Build Coastguard Worker V[var] = {reg=reg, const=const, type=vtype} 189*387f9dfdSAndroid Build Coastguard Worker -- Track variable source 190*387f9dfdSAndroid Build Coastguard Worker if V[var].const and type(const) == 'table' then 191*387f9dfdSAndroid Build Coastguard Worker V[var].source = V[var].const.source 192*387f9dfdSAndroid Build Coastguard Worker end 193*387f9dfdSAndroid Build Coastguard Workerend 194*387f9dfdSAndroid Build Coastguard Worker 195*387f9dfdSAndroid Build Coastguard Worker-- Materialize (or register) a variable in a register 196*387f9dfdSAndroid Build Coastguard Worker-- If the register is nil, then the a new register is assigned (if not already assigned) 197*387f9dfdSAndroid Build Coastguard Workerlocal function vreg(var, reg, reserve, vtype) 198*387f9dfdSAndroid Build Coastguard Worker local vinfo = V[var] 199*387f9dfdSAndroid Build Coastguard Worker assert(vinfo, 'VAR '..var..' not registered') 200*387f9dfdSAndroid Build Coastguard Worker vinfo.live_to = code.pc-1 201*387f9dfdSAndroid Build Coastguard Worker if (vinfo.reg and not reg) and not vinfo.shadow then return vinfo.reg end 202*387f9dfdSAndroid Build Coastguard Worker reg = reg_alloc(var, reg) 203*387f9dfdSAndroid Build Coastguard Worker -- Materialize variable shadow copy 204*387f9dfdSAndroid Build Coastguard Worker local src = vinfo 205*387f9dfdSAndroid Build Coastguard Worker while src.shadow do src = V[src.shadow] end 206*387f9dfdSAndroid Build Coastguard Worker if reserve then -- luacheck: ignore 207*387f9dfdSAndroid Build Coastguard Worker -- No load to register occurs 208*387f9dfdSAndroid Build Coastguard Worker elseif src.reg then 209*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.X, reg, src.reg, 0, 0) 210*387f9dfdSAndroid Build Coastguard Worker elseif src.spill then 211*387f9dfdSAndroid Build Coastguard Worker vinfo.spill = src.spill 212*387f9dfdSAndroid Build Coastguard Worker reg_fill(var, reg) 213*387f9dfdSAndroid Build Coastguard Worker elseif src.const then 214*387f9dfdSAndroid Build Coastguard Worker vtype = vtype or src.type 215*387f9dfdSAndroid Build Coastguard Worker if type(src.const) == 'table' and src.const.__base then 216*387f9dfdSAndroid Build Coastguard Worker -- Load pointer type 217*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.X, reg, 10, 0, 0) 218*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.ADD + BPF.K, reg, 0, 0, -src.const.__base) 219*387f9dfdSAndroid Build Coastguard Worker elseif type(src.const) == 'table' and src.const.__dissector then 220*387f9dfdSAndroid Build Coastguard Worker -- Load dissector offset (imm32), but keep the constant part (dissector proxy) 221*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.K, reg, 0, 0, src.const.off or 0) 222*387f9dfdSAndroid Build Coastguard Worker elseif vtype and ffi.sizeof(vtype) == 8 then 223*387f9dfdSAndroid Build Coastguard Worker -- IMM64 must be done in two instructions with imm64 = (lo(imm32), hi(imm32)) 224*387f9dfdSAndroid Build Coastguard Worker emit(BPF.LD + BPF.DW, reg, 0, 0, ffi.cast('uint32_t', src.const)) 225*387f9dfdSAndroid Build Coastguard Worker emit(0, 0, 0, 0, ffi.cast('uint32_t', bit.rshift(bit.rshift(src.const, 16), 16))) 226*387f9dfdSAndroid Build Coastguard Worker vinfo.const = nil -- The variable is live 227*387f9dfdSAndroid Build Coastguard Worker else 228*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.K, reg, 0, 0, src.const) 229*387f9dfdSAndroid Build Coastguard Worker vinfo.const = nil -- The variable is live 230*387f9dfdSAndroid Build Coastguard Worker end 231*387f9dfdSAndroid Build Coastguard Worker else assert(false, 'VAR '..var..' has neither register nor constant value') end 232*387f9dfdSAndroid Build Coastguard Worker vinfo.reg = reg 233*387f9dfdSAndroid Build Coastguard Worker vinfo.shadow = nil 234*387f9dfdSAndroid Build Coastguard Worker vinfo.live_from = code.pc-1 235*387f9dfdSAndroid Build Coastguard Worker vinfo.type = vtype or vinfo.type 236*387f9dfdSAndroid Build Coastguard Worker return reg 237*387f9dfdSAndroid Build Coastguard Workerend 238*387f9dfdSAndroid Build Coastguard Worker 239*387f9dfdSAndroid Build Coastguard Worker-- Copy variable 240*387f9dfdSAndroid Build Coastguard Workerlocal function vcopy(dst, src) 241*387f9dfdSAndroid Build Coastguard Worker if dst == src then return end 242*387f9dfdSAndroid Build Coastguard Worker V[dst] = {reg=V[src].reg, const=V[src].const, shadow=src, source=V[src].source, type=V[src].type} 243*387f9dfdSAndroid Build Coastguard Workerend 244*387f9dfdSAndroid Build Coastguard Worker 245*387f9dfdSAndroid Build Coastguard Worker-- Dereference variable of pointer type 246*387f9dfdSAndroid Build Coastguard Workerlocal function vderef(dst_reg, src_reg, vinfo) 247*387f9dfdSAndroid Build Coastguard Worker -- Dereference map pointers for primitive types 248*387f9dfdSAndroid Build Coastguard Worker -- BPF doesn't allow pointer arithmetics, so use the entry value 249*387f9dfdSAndroid Build Coastguard Worker assert(type(vinfo.const) == 'table' and vinfo.const.__dissector, 'cannot dereference a non-pointer variable') 250*387f9dfdSAndroid Build Coastguard Worker local vtype = vinfo.const.__dissector 251*387f9dfdSAndroid Build Coastguard Worker local w = ffi.sizeof(vtype) 252*387f9dfdSAndroid Build Coastguard Worker assert(const_width[w], 'NYI: sizeof('..tostring(vtype)..') not 1/2/4/8 bytes') 253*387f9dfdSAndroid Build Coastguard Worker if dst_reg ~= src_reg then 254*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.X, dst_reg, src_reg, 0, 0) -- dst = src 255*387f9dfdSAndroid Build Coastguard Worker end 256*387f9dfdSAndroid Build Coastguard Worker -- Optimize the NULL check away if provably not NULL 257*387f9dfdSAndroid Build Coastguard Worker if not vinfo.source or vinfo.source:find('_or_null', 1, true) then 258*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.JEQ + BPF.K, src_reg, 0, 1, 0) -- if (src != NULL) 259*387f9dfdSAndroid Build Coastguard Worker end 260*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.LDX + const_width[w], dst_reg, src_reg, 0, 0) -- dst = *src; 261*387f9dfdSAndroid Build Coastguard Workerend 262*387f9dfdSAndroid Build Coastguard Worker 263*387f9dfdSAndroid Build Coastguard Worker-- Allocate a space for variable 264*387f9dfdSAndroid Build Coastguard Workerlocal function valloc(size, blank) 265*387f9dfdSAndroid Build Coastguard Worker local base = stack_top 266*387f9dfdSAndroid Build Coastguard Worker assert(stack_top + size < 512 * 1024, 'exceeded maximum stack size of 512kB') 267*387f9dfdSAndroid Build Coastguard Worker stack_top = stack_top + size 268*387f9dfdSAndroid Build Coastguard Worker -- Align to 8 byte boundary 269*387f9dfdSAndroid Build Coastguard Worker stack_top = math.ceil(stack_top/8)*8 270*387f9dfdSAndroid Build Coastguard Worker -- Current kernel version doesn't support ARG_PTR_TO_RAW_STACK 271*387f9dfdSAndroid Build Coastguard Worker -- so we always need to have memory initialized, remove this when supported 272*387f9dfdSAndroid Build Coastguard Worker if blank then 273*387f9dfdSAndroid Build Coastguard Worker if type(blank) == 'string' then 274*387f9dfdSAndroid Build Coastguard Worker local sp = 0 275*387f9dfdSAndroid Build Coastguard Worker while sp < size do 276*387f9dfdSAndroid Build Coastguard Worker -- TODO: no BPF_ST + BPF_DW instruction yet 277*387f9dfdSAndroid Build Coastguard Worker local as_u32 = ffi.new('uint32_t [1]') 278*387f9dfdSAndroid Build Coastguard Worker local sub = blank:sub(sp+1, sp+ffi.sizeof(as_u32)) 279*387f9dfdSAndroid Build Coastguard Worker ffi.copy(as_u32, sub, #sub) 280*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + BPF.W, 10, 0, -(stack_top-sp), as_u32[0]) 281*387f9dfdSAndroid Build Coastguard Worker sp = sp + ffi.sizeof(as_u32) 282*387f9dfdSAndroid Build Coastguard Worker end 283*387f9dfdSAndroid Build Coastguard Worker elseif type(blank) == 'boolean' then 284*387f9dfdSAndroid Build Coastguard Worker reg_alloc(stackslots, 0) 285*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.K, 0, 0, 0, 0) 286*387f9dfdSAndroid Build Coastguard Worker for sp = base+8,stack_top,8 do 287*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + BPF.DW, 10, 0, -sp, 0) 288*387f9dfdSAndroid Build Coastguard Worker end 289*387f9dfdSAndroid Build Coastguard Worker else error('NYI: will with unknown type '..type(blank)) end 290*387f9dfdSAndroid Build Coastguard Worker end 291*387f9dfdSAndroid Build Coastguard Worker return stack_top 292*387f9dfdSAndroid Build Coastguard Workerend 293*387f9dfdSAndroid Build Coastguard Worker 294*387f9dfdSAndroid Build Coastguard Worker-- Turn variable into scalar in register (or constant) 295*387f9dfdSAndroid Build Coastguard Workerlocal function vscalar(a, w) 296*387f9dfdSAndroid Build Coastguard Worker assert(const_width[w], 'sizeof(scalar variable) must be 1/2/4/8') 297*387f9dfdSAndroid Build Coastguard Worker local src_reg 298*387f9dfdSAndroid Build Coastguard Worker -- If source is a pointer, we must dereference it first 299*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(V[a].type) then 300*387f9dfdSAndroid Build Coastguard Worker src_reg = vreg(a) 301*387f9dfdSAndroid Build Coastguard Worker local tmp_reg = reg_alloc(stackslots, 1) -- Clone variable in tmp register 302*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.X, tmp_reg, src_reg, 0, 0) 303*387f9dfdSAndroid Build Coastguard Worker vderef(tmp_reg, tmp_reg, V[a]) 304*387f9dfdSAndroid Build Coastguard Worker src_reg = tmp_reg -- Materialize and dereference it 305*387f9dfdSAndroid Build Coastguard Worker -- Source is a value on stack, we must load it first 306*387f9dfdSAndroid Build Coastguard Worker elseif type(V[a].const) == 'table' and V[a].const.__base > 0 then 307*387f9dfdSAndroid Build Coastguard Worker src_reg = vreg(a) 308*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.LDX + const_width[w], src_reg, 10, -V[a].const.__base, 0) 309*387f9dfdSAndroid Build Coastguard Worker V[a].type = V[a].const.__dissector 310*387f9dfdSAndroid Build Coastguard Worker V[a].const = nil -- Value is dereferenced 311*387f9dfdSAndroid Build Coastguard Worker -- If source is an imm32 number, avoid register load 312*387f9dfdSAndroid Build Coastguard Worker elseif type(V[a].const) == 'number' and w < 8 then 313*387f9dfdSAndroid Build Coastguard Worker return nil, V[a].const 314*387f9dfdSAndroid Build Coastguard Worker -- Load variable from any other source 315*387f9dfdSAndroid Build Coastguard Worker else 316*387f9dfdSAndroid Build Coastguard Worker src_reg = vreg(a) 317*387f9dfdSAndroid Build Coastguard Worker end 318*387f9dfdSAndroid Build Coastguard Worker 319*387f9dfdSAndroid Build Coastguard Worker return src_reg, nil 320*387f9dfdSAndroid Build Coastguard Workerend 321*387f9dfdSAndroid Build Coastguard Worker 322*387f9dfdSAndroid Build Coastguard Worker-- Emit compensation code at the end of basic block to unify variable set layout on all block exits 323*387f9dfdSAndroid Build Coastguard Worker-- 1. we need to free registers by spilling 324*387f9dfdSAndroid Build Coastguard Worker-- 2. fill registers to match other exits from this BB 325*387f9dfdSAndroid Build Coastguard Workerlocal function bb_end(Vcomp) 326*387f9dfdSAndroid Build Coastguard Worker for i,v in pairs(V) do 327*387f9dfdSAndroid Build Coastguard Worker if Vcomp[i] and Vcomp[i].spill and not v.spill then 328*387f9dfdSAndroid Build Coastguard Worker -- Materialize constant or shadowing variable to be able to spill 329*387f9dfdSAndroid Build Coastguard Worker if not v.reg and (v.shadow or cdef.isimmconst(v)) then 330*387f9dfdSAndroid Build Coastguard Worker vreg(i) 331*387f9dfdSAndroid Build Coastguard Worker end 332*387f9dfdSAndroid Build Coastguard Worker reg_spill(i) 333*387f9dfdSAndroid Build Coastguard Worker end 334*387f9dfdSAndroid Build Coastguard Worker end 335*387f9dfdSAndroid Build Coastguard Worker for i,v in pairs(V) do 336*387f9dfdSAndroid Build Coastguard Worker if Vcomp[i] and Vcomp[i].reg and not v.reg then 337*387f9dfdSAndroid Build Coastguard Worker vreg(i, Vcomp[i].reg) 338*387f9dfdSAndroid Build Coastguard Worker end 339*387f9dfdSAndroid Build Coastguard Worker -- Compensate variable metadata change 340*387f9dfdSAndroid Build Coastguard Worker if Vcomp[i] and Vcomp[i].source then 341*387f9dfdSAndroid Build Coastguard Worker V[i].source = Vcomp[i].source 342*387f9dfdSAndroid Build Coastguard Worker end 343*387f9dfdSAndroid Build Coastguard Worker end 344*387f9dfdSAndroid Build Coastguard Workerend 345*387f9dfdSAndroid Build Coastguard Worker 346*387f9dfdSAndroid Build Coastguard Workerlocal function CMP_STR(a, b, op) 347*387f9dfdSAndroid Build Coastguard Worker assert(op == 'JEQ' or op == 'JNE', 'NYI: only equivallence stack/string only supports == or ~=') 348*387f9dfdSAndroid Build Coastguard Worker -- I have no better idea how to implement it than unrolled XOR loop, as we can fixup only one JMP 349*387f9dfdSAndroid Build Coastguard Worker -- So: X(a,b) = a[0] ^ b[0] | a[1] ^ b[1] | ... 350*387f9dfdSAndroid Build Coastguard Worker -- EQ(a,b) <=> X == 0 351*387f9dfdSAndroid Build Coastguard Worker -- This could be optimised by placing early exits by rewriter in second phase for long strings 352*387f9dfdSAndroid Build Coastguard Worker local base, size = V[a].const.__base, math.min(#b, ffi.sizeof(V[a].type)) 353*387f9dfdSAndroid Build Coastguard Worker local acc, tmp = reg_alloc(stackslots, 0), reg_alloc(stackslots+1, 1) 354*387f9dfdSAndroid Build Coastguard Worker local sp = 0 355*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.K, acc, 0, 0, 0) 356*387f9dfdSAndroid Build Coastguard Worker while sp < size do 357*387f9dfdSAndroid Build Coastguard Worker -- Load string chunk as imm32 358*387f9dfdSAndroid Build Coastguard Worker local as_u32 = ffi.new('uint32_t [1]') 359*387f9dfdSAndroid Build Coastguard Worker local sub = b:sub(sp+1, sp+ffi.sizeof(as_u32)) 360*387f9dfdSAndroid Build Coastguard Worker ffi.copy(as_u32, sub, #sub) 361*387f9dfdSAndroid Build Coastguard Worker -- TODO: make this faster by interleaved load/compare steps with DW length 362*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.LDX + BPF.W, tmp, 10, -(base-sp), 0) 363*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.XOR + BPF.K, tmp, 0, 0, as_u32[0]) 364*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.OR + BPF.X, acc, tmp, 0, 0) 365*387f9dfdSAndroid Build Coastguard Worker sp = sp + ffi.sizeof(as_u32) 366*387f9dfdSAndroid Build Coastguard Worker end 367*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF[op] + BPF.K, acc, 0, 0xffff, 0) 368*387f9dfdSAndroid Build Coastguard Worker code.seen_cmp = code.pc-1 369*387f9dfdSAndroid Build Coastguard Workerend 370*387f9dfdSAndroid Build Coastguard Worker 371*387f9dfdSAndroid Build Coastguard Workerlocal function CMP_REG(a, b, op) 372*387f9dfdSAndroid Build Coastguard Worker -- Fold compile-time expressions 373*387f9dfdSAndroid Build Coastguard Worker if V[a].const and V[b].const and not (is_proxy(V[a].const) or is_proxy(V[b].const)) then 374*387f9dfdSAndroid Build Coastguard Worker code.seen_cmp = const_expr[op](V[a].const, V[b].const) and ALWAYS or NEVER 375*387f9dfdSAndroid Build Coastguard Worker else 376*387f9dfdSAndroid Build Coastguard Worker -- Comparison against compile-time string or stack memory 377*387f9dfdSAndroid Build Coastguard Worker if V[b].const and type(V[b].const) == 'string' then 378*387f9dfdSAndroid Build Coastguard Worker return CMP_STR(a, V[b].const, op) 379*387f9dfdSAndroid Build Coastguard Worker end 380*387f9dfdSAndroid Build Coastguard Worker -- The 0xFFFF target here has no significance, it's just a placeholder for 381*387f9dfdSAndroid Build Coastguard Worker -- compiler to replace it's absolute offset to LJ bytecode insn with a relative 382*387f9dfdSAndroid Build Coastguard Worker -- offset in BPF program code, verifier will accept only programs with valid JMP targets 383*387f9dfdSAndroid Build Coastguard Worker local a_reg, b_reg = vreg(a), vreg(b) 384*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF[op] + BPF.X, a_reg, b_reg, 0xffff, 0) 385*387f9dfdSAndroid Build Coastguard Worker code.seen_cmp = code.pc-1 386*387f9dfdSAndroid Build Coastguard Worker end 387*387f9dfdSAndroid Build Coastguard Workerend 388*387f9dfdSAndroid Build Coastguard Worker 389*387f9dfdSAndroid Build Coastguard Workerlocal function CMP_IMM(a, b, op) 390*387f9dfdSAndroid Build Coastguard Worker local c = V[a].const 391*387f9dfdSAndroid Build Coastguard Worker if c and not is_proxy(c) then -- Fold compile-time expressions 392*387f9dfdSAndroid Build Coastguard Worker code.seen_cmp = const_expr[op](c, b) and ALWAYS or NEVER 393*387f9dfdSAndroid Build Coastguard Worker else 394*387f9dfdSAndroid Build Coastguard Worker -- Convert imm32 to number 395*387f9dfdSAndroid Build Coastguard Worker if type(b) == 'string' then 396*387f9dfdSAndroid Build Coastguard Worker if #b == 1 then b = b:byte() 397*387f9dfdSAndroid Build Coastguard Worker elseif cdef.isptr(V[a].type) then 398*387f9dfdSAndroid Build Coastguard Worker -- String comparison between stack/constant string 399*387f9dfdSAndroid Build Coastguard Worker return CMP_STR(a, b, op) 400*387f9dfdSAndroid Build Coastguard Worker elseif #b <= 4 then 401*387f9dfdSAndroid Build Coastguard Worker -- Convert to u32 with network byte order 402*387f9dfdSAndroid Build Coastguard Worker local imm = ffi.new('uint32_t[1]') 403*387f9dfdSAndroid Build Coastguard Worker ffi.copy(imm, b, #b) 404*387f9dfdSAndroid Build Coastguard Worker b = builtins.hton(imm[0]) 405*387f9dfdSAndroid Build Coastguard Worker else error('NYI: compare register with string, where #string > sizeof(u32)') end 406*387f9dfdSAndroid Build Coastguard Worker end 407*387f9dfdSAndroid Build Coastguard Worker -- The 0xFFFF target here has no significance, it's just a placeholder for 408*387f9dfdSAndroid Build Coastguard Worker -- compiler to replace it's absolute offset to LJ bytecode insn with a relative 409*387f9dfdSAndroid Build Coastguard Worker -- offset in BPF program code, verifier will accept only programs with valid JMP targets 410*387f9dfdSAndroid Build Coastguard Worker local reg = vreg(a) 411*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF[op] + BPF.K, reg, 0, 0xffff, b) 412*387f9dfdSAndroid Build Coastguard Worker code.seen_cmp = code.pc-1 413*387f9dfdSAndroid Build Coastguard Worker -- Remember NULL pointer checks as BPF prohibits pointer comparisons 414*387f9dfdSAndroid Build Coastguard Worker -- and repeated checks wouldn't pass the verifier, only comparisons 415*387f9dfdSAndroid Build Coastguard Worker -- against constants are checked. 416*387f9dfdSAndroid Build Coastguard Worker if op == 'JEQ' and tonumber(b) == 0 and V[a].source then 417*387f9dfdSAndroid Build Coastguard Worker local pos = V[a].source:find('_or_null', 1, true) 418*387f9dfdSAndroid Build Coastguard Worker if pos then 419*387f9dfdSAndroid Build Coastguard Worker code.seen_null_guard = a 420*387f9dfdSAndroid Build Coastguard Worker end 421*387f9dfdSAndroid Build Coastguard Worker -- Inverse NULL pointer check (if a ~= nil) 422*387f9dfdSAndroid Build Coastguard Worker elseif op == 'JNE' and tonumber(b) == 0 and V[a].source then 423*387f9dfdSAndroid Build Coastguard Worker local pos = V[a].source:find('_or_null', 1, true) 424*387f9dfdSAndroid Build Coastguard Worker if pos then 425*387f9dfdSAndroid Build Coastguard Worker code.seen_null_guard = a 426*387f9dfdSAndroid Build Coastguard Worker code.seen_null_guard_inverse = true 427*387f9dfdSAndroid Build Coastguard Worker end 428*387f9dfdSAndroid Build Coastguard Worker end 429*387f9dfdSAndroid Build Coastguard Worker end 430*387f9dfdSAndroid Build Coastguard Workerend 431*387f9dfdSAndroid Build Coastguard Worker 432*387f9dfdSAndroid Build Coastguard Workerlocal function ALU_IMM(dst, a, b, op) 433*387f9dfdSAndroid Build Coastguard Worker -- Fold compile-time expressions 434*387f9dfdSAndroid Build Coastguard Worker if V[a].const and not is_proxy(V[a].const) then 435*387f9dfdSAndroid Build Coastguard Worker assert(cdef.isimmconst(V[a]), 'VAR '..a..' must be numeric') 436*387f9dfdSAndroid Build Coastguard Worker vset(dst, nil, const_expr[op](V[a].const, b)) 437*387f9dfdSAndroid Build Coastguard Worker -- Now we need to materialize dissected value at DST, and add it 438*387f9dfdSAndroid Build Coastguard Worker else 439*387f9dfdSAndroid Build Coastguard Worker vcopy(dst, a) 440*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(dst) 441*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(V[a].type) then 442*387f9dfdSAndroid Build Coastguard Worker vderef(dst_reg, dst_reg, V[a]) 443*387f9dfdSAndroid Build Coastguard Worker V[dst].type = V[a].const.__dissector 444*387f9dfdSAndroid Build Coastguard Worker else 445*387f9dfdSAndroid Build Coastguard Worker V[dst].type = V[a].type 446*387f9dfdSAndroid Build Coastguard Worker end 447*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF[op] + BPF.K, dst_reg, 0, 0, b) 448*387f9dfdSAndroid Build Coastguard Worker end 449*387f9dfdSAndroid Build Coastguard Workerend 450*387f9dfdSAndroid Build Coastguard Worker 451*387f9dfdSAndroid Build Coastguard Workerlocal function ALU_REG(dst, a, b, op) 452*387f9dfdSAndroid Build Coastguard Worker -- Fold compile-time expressions 453*387f9dfdSAndroid Build Coastguard Worker if V[a].const and not (is_proxy(V[a].const) or is_proxy(V[b].const)) then 454*387f9dfdSAndroid Build Coastguard Worker assert(cdef.isimmconst(V[a]), 'VAR '..a..' must be numeric') 455*387f9dfdSAndroid Build Coastguard Worker assert(cdef.isimmconst(V[b]), 'VAR '..b..' must be numeric') 456*387f9dfdSAndroid Build Coastguard Worker if type(op) == 'string' then op = const_expr[op] end 457*387f9dfdSAndroid Build Coastguard Worker vcopy(dst, a) 458*387f9dfdSAndroid Build Coastguard Worker V[dst].const = op(V[a].const, V[b].const) 459*387f9dfdSAndroid Build Coastguard Worker else 460*387f9dfdSAndroid Build Coastguard Worker local src_reg = b and vreg(b) or 0 -- SRC is optional for unary operations 461*387f9dfdSAndroid Build Coastguard Worker if b and cdef.isptr(V[b].type) then 462*387f9dfdSAndroid Build Coastguard Worker -- We have to allocate a temporary register for dereferencing to preserve 463*387f9dfdSAndroid Build Coastguard Worker -- pointer in source variable that MUST NOT be altered 464*387f9dfdSAndroid Build Coastguard Worker reg_alloc(stackslots, 2) 465*387f9dfdSAndroid Build Coastguard Worker vderef(2, src_reg, V[b]) 466*387f9dfdSAndroid Build Coastguard Worker src_reg = 2 467*387f9dfdSAndroid Build Coastguard Worker end 468*387f9dfdSAndroid Build Coastguard Worker vcopy(dst, a) -- DST may alias B, so copy must occur after we materialize B 469*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(dst) 470*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(V[a].type) then 471*387f9dfdSAndroid Build Coastguard Worker vderef(dst_reg, dst_reg, V[a]) 472*387f9dfdSAndroid Build Coastguard Worker V[dst].type = V[a].const.__dissector 473*387f9dfdSAndroid Build Coastguard Worker end 474*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF[op] + BPF.X, dst_reg, src_reg, 0, 0) 475*387f9dfdSAndroid Build Coastguard Worker V[stackslots].reg = nil -- Free temporary registers 476*387f9dfdSAndroid Build Coastguard Worker end 477*387f9dfdSAndroid Build Coastguard Workerend 478*387f9dfdSAndroid Build Coastguard Worker 479*387f9dfdSAndroid Build Coastguard Workerlocal function ALU_IMM_NV(dst, a, b, op) 480*387f9dfdSAndroid Build Coastguard Worker -- Do DST = IMM(a) op VAR(b) where we can't invert because 481*387f9dfdSAndroid Build Coastguard Worker -- the registers are u64 but immediates are u32, so complement 482*387f9dfdSAndroid Build Coastguard Worker -- arithmetics wouldn't work 483*387f9dfdSAndroid Build Coastguard Worker vset(stackslots+1, nil, a) 484*387f9dfdSAndroid Build Coastguard Worker ALU_REG(dst, stackslots+1, b, op) 485*387f9dfdSAndroid Build Coastguard Workerend 486*387f9dfdSAndroid Build Coastguard Worker 487*387f9dfdSAndroid Build Coastguard Workerlocal function LD_ABS(dst, w, off) 488*387f9dfdSAndroid Build Coastguard Worker assert(off, 'LD_ABS called without offset') 489*387f9dfdSAndroid Build Coastguard Worker if w < 8 then 490*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(dst, 0, true, builtins.width_type(w)) -- Reserve R0 491*387f9dfdSAndroid Build Coastguard Worker emit(BPF.LD + BPF.ABS + const_width[w], dst_reg, 0, 0, off) 492*387f9dfdSAndroid Build Coastguard Worker if w > 1 and ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse 493*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU + BPF.END + BPF.TO_BE, dst_reg, 0, 0, w * 8) 494*387f9dfdSAndroid Build Coastguard Worker end 495*387f9dfdSAndroid Build Coastguard Worker elseif w == 8 then 496*387f9dfdSAndroid Build Coastguard Worker -- LD_ABS|IND prohibits DW, we need to do two W loads and combine them 497*387f9dfdSAndroid Build Coastguard Worker local tmp_reg = vreg(stackslots, 0, true, builtins.width_type(w)) -- Reserve R0 498*387f9dfdSAndroid Build Coastguard Worker emit(BPF.LD + BPF.ABS + const_width[4], tmp_reg, 0, 0, off + 4) 499*387f9dfdSAndroid Build Coastguard Worker if ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse 500*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU + BPF.END + BPF.TO_BE, tmp_reg, 0, 0, 32) 501*387f9dfdSAndroid Build Coastguard Worker end 502*387f9dfdSAndroid Build Coastguard Worker ALU_IMM(stackslots, stackslots, 32, 'LSH') 503*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(dst, 0, true, builtins.width_type(w)) -- Reserve R0, spill tmp variable 504*387f9dfdSAndroid Build Coastguard Worker emit(BPF.LD + BPF.ABS + const_width[4], dst_reg, 0, 0, off) 505*387f9dfdSAndroid Build Coastguard Worker if ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse 506*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU + BPF.END + BPF.TO_BE, dst_reg, 0, 0, 32) 507*387f9dfdSAndroid Build Coastguard Worker end 508*387f9dfdSAndroid Build Coastguard Worker ALU_REG(dst, dst, stackslots, 'OR') 509*387f9dfdSAndroid Build Coastguard Worker V[stackslots].reg = nil -- Free temporary registers 510*387f9dfdSAndroid Build Coastguard Worker else 511*387f9dfdSAndroid Build Coastguard Worker assert(w < 8, 'NYI: only LD_ABS of 1/2/4/8 is supported') 512*387f9dfdSAndroid Build Coastguard Worker end 513*387f9dfdSAndroid Build Coastguard Workerend 514*387f9dfdSAndroid Build Coastguard Worker 515*387f9dfdSAndroid Build Coastguard Workerlocal function LD_IND(dst, src, w, off) 516*387f9dfdSAndroid Build Coastguard Worker local src_reg = vreg(src) -- Must materialize first in case dst == src 517*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(dst, 0, true, builtins.width_type(w)) -- Reserve R0 518*387f9dfdSAndroid Build Coastguard Worker emit(BPF.LD + BPF.IND + const_width[w], dst_reg, src_reg, 0, off or 0) 519*387f9dfdSAndroid Build Coastguard Worker if w > 1 and ffi.abi('le') then -- LD_ABS has htonl() semantics, reverse 520*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU + BPF.END + BPF.TO_BE, dst_reg, 0, 0, w * 8) 521*387f9dfdSAndroid Build Coastguard Worker end 522*387f9dfdSAndroid Build Coastguard Workerend 523*387f9dfdSAndroid Build Coastguard Worker 524*387f9dfdSAndroid Build Coastguard Workerlocal function LD_MEM(dst, src, w, off) 525*387f9dfdSAndroid Build Coastguard Worker local src_reg = vreg(src) -- Must materialize first in case dst == src 526*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(dst, nil, true, builtins.width_type(w)) -- Reserve R0 527*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.LDX + const_width[w], dst_reg, src_reg, off or 0, 0) 528*387f9dfdSAndroid Build Coastguard Workerend 529*387f9dfdSAndroid Build Coastguard Worker 530*387f9dfdSAndroid Build Coastguard Worker-- @note: This is specific now as it expects registers reserved 531*387f9dfdSAndroid Build Coastguard Workerlocal function LD_IMM_X(dst_reg, src_type, imm, w) 532*387f9dfdSAndroid Build Coastguard Worker if w == 8 then -- IMM64 must be done in two instructions with imm64 = (lo(imm32), hi(imm32)) 533*387f9dfdSAndroid Build Coastguard Worker emit(BPF.LD + const_width[w], dst_reg, src_type, 0, ffi.cast('uint32_t', imm)) 534*387f9dfdSAndroid Build Coastguard Worker -- Must shift in two steps as bit.lshift supports [0..31] 535*387f9dfdSAndroid Build Coastguard Worker emit(0, 0, 0, 0, ffi.cast('uint32_t', bit.lshift(bit.lshift(imm, 16), 16))) 536*387f9dfdSAndroid Build Coastguard Worker else 537*387f9dfdSAndroid Build Coastguard Worker emit(BPF.LD + const_width[w], dst_reg, src_type, 0, imm) 538*387f9dfdSAndroid Build Coastguard Worker end 539*387f9dfdSAndroid Build Coastguard Workerend 540*387f9dfdSAndroid Build Coastguard Worker 541*387f9dfdSAndroid Build Coastguard Workerlocal function BUILTIN(func, ...) 542*387f9dfdSAndroid Build Coastguard Worker local builtin_export = { 543*387f9dfdSAndroid Build Coastguard Worker -- Compiler primitives (work with variable slots, emit instructions) 544*387f9dfdSAndroid Build Coastguard Worker V=V, vreg=vreg, vset=vset, vcopy=vcopy, vderef=vderef, valloc=valloc, emit=emit, 545*387f9dfdSAndroid Build Coastguard Worker reg_alloc=reg_alloc, reg_spill=reg_spill, tmpvar=stackslots, const_width=const_width, 546*387f9dfdSAndroid Build Coastguard Worker -- Extensions and helpers (use with care) 547*387f9dfdSAndroid Build Coastguard Worker LD_IMM_X = LD_IMM_X, 548*387f9dfdSAndroid Build Coastguard Worker } 549*387f9dfdSAndroid Build Coastguard Worker func(builtin_export, ...) 550*387f9dfdSAndroid Build Coastguard Workerend 551*387f9dfdSAndroid Build Coastguard Worker 552*387f9dfdSAndroid Build Coastguard Workerlocal function LOAD(dst, src, off, vtype) 553*387f9dfdSAndroid Build Coastguard Worker local base = V[src].const 554*387f9dfdSAndroid Build Coastguard Worker assert(base and base.__dissector, 'NYI: load() on variable that doesn\'t have dissector') 555*387f9dfdSAndroid Build Coastguard Worker assert(V[src].source, 'NYI: load() on variable with unknown source') 556*387f9dfdSAndroid Build Coastguard Worker -- Cast to different type if requested 557*387f9dfdSAndroid Build Coastguard Worker vtype = vtype or base.__dissector 558*387f9dfdSAndroid Build Coastguard Worker local w = ffi.sizeof(vtype) 559*387f9dfdSAndroid Build Coastguard Worker assert(const_width[w], 'NYI: load() supports 1/2/4/8 bytes at a time only, wanted ' .. tostring(w)) 560*387f9dfdSAndroid Build Coastguard Worker -- Packet access with a dissector (use BPF_LD) 561*387f9dfdSAndroid Build Coastguard Worker if V[src].source:find('ptr_to_pkt', 1, true) then 562*387f9dfdSAndroid Build Coastguard Worker if base.off then -- Absolute address to payload 563*387f9dfdSAndroid Build Coastguard Worker LD_ABS(dst, w, off + base.off) 564*387f9dfdSAndroid Build Coastguard Worker else -- Indirect address to payload 565*387f9dfdSAndroid Build Coastguard Worker LD_IND(dst, src, w, off) 566*387f9dfdSAndroid Build Coastguard Worker end 567*387f9dfdSAndroid Build Coastguard Worker -- Direct access to first argument (skb fields, pt regs, ...) 568*387f9dfdSAndroid Build Coastguard Worker elseif V[src].source:find('ptr_to_ctx', 1, true) then 569*387f9dfdSAndroid Build Coastguard Worker LD_MEM(dst, src, w, off) 570*387f9dfdSAndroid Build Coastguard Worker -- Direct skb access with a dissector (use BPF_MEM) 571*387f9dfdSAndroid Build Coastguard Worker elseif V[src].source:find('ptr_to_skb', 1, true) then 572*387f9dfdSAndroid Build Coastguard Worker LD_MEM(dst, src, w, off) 573*387f9dfdSAndroid Build Coastguard Worker -- Pointer to map-backed memory (use BPF_MEM) 574*387f9dfdSAndroid Build Coastguard Worker elseif V[src].source:find('ptr_to_map_value', 1, true) then 575*387f9dfdSAndroid Build Coastguard Worker LD_MEM(dst, src, w, off) 576*387f9dfdSAndroid Build Coastguard Worker -- Indirect read using probe (uprobe or kprobe, uses helper) 577*387f9dfdSAndroid Build Coastguard Worker elseif V[src].source:find('ptr_to_probe', 1, true) then 578*387f9dfdSAndroid Build Coastguard Worker BUILTIN(builtins[builtins.probe_read], nil, dst, src, vtype, off) 579*387f9dfdSAndroid Build Coastguard Worker V[dst].source = V[src].source -- Builtin handles everything 580*387f9dfdSAndroid Build Coastguard Worker else 581*387f9dfdSAndroid Build Coastguard Worker error('NYI: load() on variable from ' .. V[src].source) 582*387f9dfdSAndroid Build Coastguard Worker end 583*387f9dfdSAndroid Build Coastguard Worker V[dst].type = vtype 584*387f9dfdSAndroid Build Coastguard Worker V[dst].const = nil -- Dissected value is not constant anymore 585*387f9dfdSAndroid Build Coastguard Workerend 586*387f9dfdSAndroid Build Coastguard Worker 587*387f9dfdSAndroid Build Coastguard Workerlocal function CALL(a, b, d) 588*387f9dfdSAndroid Build Coastguard Worker assert(b-1 <= 1, 'NYI: CALL with >1 return values') 589*387f9dfdSAndroid Build Coastguard Worker -- Perform either compile-time, helper, or builtin 590*387f9dfdSAndroid Build Coastguard Worker local func = V[a].const 591*387f9dfdSAndroid Build Coastguard Worker -- Gather all arguments and check if they're constant 592*387f9dfdSAndroid Build Coastguard Worker local args, const, nargs = {}, true, d - 1 593*387f9dfdSAndroid Build Coastguard Worker for i = a+1, a+d-1 do 594*387f9dfdSAndroid Build Coastguard Worker table.insert(args, V[i].const) 595*387f9dfdSAndroid Build Coastguard Worker if not V[i].const or is_proxy(V[i].const) then const = false end 596*387f9dfdSAndroid Build Coastguard Worker end 597*387f9dfdSAndroid Build Coastguard Worker local builtin = builtins[func] 598*387f9dfdSAndroid Build Coastguard Worker if not const or nargs == 0 then 599*387f9dfdSAndroid Build Coastguard Worker if builtin and type(builtin) == 'function' then 600*387f9dfdSAndroid Build Coastguard Worker args = {a} 601*387f9dfdSAndroid Build Coastguard Worker for i = a+1, a+nargs do table.insert(args, i) end 602*387f9dfdSAndroid Build Coastguard Worker BUILTIN(builtin, unpack(args)) 603*387f9dfdSAndroid Build Coastguard Worker elseif V[a+2] and V[a+2].const then -- var OP imm 604*387f9dfdSAndroid Build Coastguard Worker ALU_IMM(a, a+1, V[a+2].const, builtin) 605*387f9dfdSAndroid Build Coastguard Worker elseif nargs <= 2 then -- var OP var 606*387f9dfdSAndroid Build Coastguard Worker ALU_REG(a, a+1, V[a+2] and a+2, builtin) 607*387f9dfdSAndroid Build Coastguard Worker else 608*387f9dfdSAndroid Build Coastguard Worker error('NYI: CALL non-builtin with 3 or more arguments') 609*387f9dfdSAndroid Build Coastguard Worker end 610*387f9dfdSAndroid Build Coastguard Worker -- Call on dissector implies slice retrieval 611*387f9dfdSAndroid Build Coastguard Worker elseif type(func) == 'table' and func.__dissector then 612*387f9dfdSAndroid Build Coastguard Worker assert(nargs >= 2, 'NYI: <dissector>.slice(a, b) must have at least two arguments') 613*387f9dfdSAndroid Build Coastguard Worker assert(V[a+1].const and V[a+2].const, 'NYI: slice() arguments must be constant') 614*387f9dfdSAndroid Build Coastguard Worker local off = V[a+1].const 615*387f9dfdSAndroid Build Coastguard Worker local vtype = builtins.width_type(V[a+2].const - off) 616*387f9dfdSAndroid Build Coastguard Worker -- Access to packet via packet (use BPF_LD) 617*387f9dfdSAndroid Build Coastguard Worker if V[a].source and V[a].source:find('ptr_to_', 1, true) then 618*387f9dfdSAndroid Build Coastguard Worker LOAD(a, a, off, vtype) 619*387f9dfdSAndroid Build Coastguard Worker else 620*387f9dfdSAndroid Build Coastguard Worker error('NYI: <dissector>.slice(a, b) on non-pointer memory ' .. (V[a].source or 'unknown')) 621*387f9dfdSAndroid Build Coastguard Worker end 622*387f9dfdSAndroid Build Coastguard Worker -- Strict builtins cannot be expanded on compile-time 623*387f9dfdSAndroid Build Coastguard Worker elseif builtins_strict[func] and builtin then 624*387f9dfdSAndroid Build Coastguard Worker args = {a} 625*387f9dfdSAndroid Build Coastguard Worker for i = a+1, a+nargs do table.insert(args, i) end 626*387f9dfdSAndroid Build Coastguard Worker BUILTIN(builtin, unpack(args)) 627*387f9dfdSAndroid Build Coastguard Worker -- Attempt compile-time call expansion (expects all argument compile-time known) 628*387f9dfdSAndroid Build Coastguard Worker else 629*387f9dfdSAndroid Build Coastguard Worker assert(const, 'NYI: CALL attempted on constant arguments, but at least one argument is not constant') 630*387f9dfdSAndroid Build Coastguard Worker V[a].const = func(unpack(args)) 631*387f9dfdSAndroid Build Coastguard Worker end 632*387f9dfdSAndroid Build Coastguard Workerend 633*387f9dfdSAndroid Build Coastguard Worker 634*387f9dfdSAndroid Build Coastguard Workerlocal function MAP_INIT(map_var, key, imm) 635*387f9dfdSAndroid Build Coastguard Worker local map = V[map_var].const 636*387f9dfdSAndroid Build Coastguard Worker vreg(map_var, 1, true, ffi.typeof('uint64_t')) 637*387f9dfdSAndroid Build Coastguard Worker -- Reserve R1 and load ptr for process-local map fd 638*387f9dfdSAndroid Build Coastguard Worker LD_IMM_X(1, BPF.PSEUDO_MAP_FD, map.fd, ffi.sizeof(V[map_var].type)) 639*387f9dfdSAndroid Build Coastguard Worker V[map_var].reg = nil -- R1 will be invalidated after CALL, forget register allocation 640*387f9dfdSAndroid Build Coastguard Worker -- Reserve R2 and load R2 = key pointer 641*387f9dfdSAndroid Build Coastguard Worker local key_size = ffi.sizeof(map.key_type) 642*387f9dfdSAndroid Build Coastguard Worker local w = const_width[key_size] or BPF.DW 643*387f9dfdSAndroid Build Coastguard Worker local pod_type = const_width[key_size] 644*387f9dfdSAndroid Build Coastguard Worker local sp = stack_top + key_size -- Must use stack below spill slots 645*387f9dfdSAndroid Build Coastguard Worker -- Store immediate value on stack 646*387f9dfdSAndroid Build Coastguard Worker reg_alloc(stackslots, 2) -- Spill anything in R2 (unnamed tmp variable) 647*387f9dfdSAndroid Build Coastguard Worker local key_base = key and V[key].const 648*387f9dfdSAndroid Build Coastguard Worker imm = imm or key_base 649*387f9dfdSAndroid Build Coastguard Worker if imm and (not key or not is_proxy(key_base)) then 650*387f9dfdSAndroid Build Coastguard Worker assert(pod_type, 'NYI: map[const K], K width must be 1/2/4/8') 651*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + w, 10, 0, -sp, imm) 652*387f9dfdSAndroid Build Coastguard Worker -- Key is in register, spill it 653*387f9dfdSAndroid Build Coastguard Worker elseif V[key].reg and pod_type then 654*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(V[key].type) then 655*387f9dfdSAndroid Build Coastguard Worker -- There is already pointer in register, dereference before spilling 656*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.LDX + w, 2, V[key].reg, 0, 0) 657*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + w, 10, 2, -sp, 0) 658*387f9dfdSAndroid Build Coastguard Worker else -- Variable in register is POD, spill it on the stack 659*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + w, 10, V[key].reg, -sp, 0) 660*387f9dfdSAndroid Build Coastguard Worker end 661*387f9dfdSAndroid Build Coastguard Worker -- Key is spilled from register to stack 662*387f9dfdSAndroid Build Coastguard Worker elseif V[key].spill then 663*387f9dfdSAndroid Build Coastguard Worker sp = V[key].spill 664*387f9dfdSAndroid Build Coastguard Worker -- Key is already on stack, write to base-relative address 665*387f9dfdSAndroid Build Coastguard Worker elseif key_base.__base then 666*387f9dfdSAndroid Build Coastguard Worker assert(key_size == ffi.sizeof(V[key].type), 'VAR '..key..' type incompatible with BPF map key type') 667*387f9dfdSAndroid Build Coastguard Worker sp = key_base.__base 668*387f9dfdSAndroid Build Coastguard Worker else 669*387f9dfdSAndroid Build Coastguard Worker error('VAR '..key..' is neither const-expr/register/stack/spilled') 670*387f9dfdSAndroid Build Coastguard Worker end 671*387f9dfdSAndroid Build Coastguard Worker -- If [FP+K] addressing, emit it 672*387f9dfdSAndroid Build Coastguard Worker if sp then 673*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.X, 2, 10, 0, 0) 674*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.ADD + BPF.K, 2, 0, 0, -sp) 675*387f9dfdSAndroid Build Coastguard Worker end 676*387f9dfdSAndroid Build Coastguard Workerend 677*387f9dfdSAndroid Build Coastguard Worker 678*387f9dfdSAndroid Build Coastguard Workerlocal function MAP_GET(dst, map_var, key, imm) 679*387f9dfdSAndroid Build Coastguard Worker local map = V[map_var].const 680*387f9dfdSAndroid Build Coastguard Worker MAP_INIT(map_var, key, imm) 681*387f9dfdSAndroid Build Coastguard Worker -- Flag as pointer type and associate dissector for map value type 682*387f9dfdSAndroid Build Coastguard Worker vreg(dst, 0, true, ffi.typeof('uint8_t *')) 683*387f9dfdSAndroid Build Coastguard Worker V[dst].const = {__dissector=map.val_type} 684*387f9dfdSAndroid Build Coastguard Worker V[dst].source = 'ptr_to_map_value_or_null' 685*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_lookup_elem) 686*387f9dfdSAndroid Build Coastguard Worker V[stackslots].reg = nil -- Free temporary registers 687*387f9dfdSAndroid Build Coastguard Workerend 688*387f9dfdSAndroid Build Coastguard Worker 689*387f9dfdSAndroid Build Coastguard Workerlocal function MAP_DEL(map_var, key, key_imm) 690*387f9dfdSAndroid Build Coastguard Worker -- Set R0, R1 (map fd, preempt R0) 691*387f9dfdSAndroid Build Coastguard Worker reg_alloc(stackslots, 0) -- Spill anything in R0 (unnamed tmp variable) 692*387f9dfdSAndroid Build Coastguard Worker MAP_INIT(map_var, key, key_imm) 693*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_delete_elem) 694*387f9dfdSAndroid Build Coastguard Worker V[stackslots].reg = nil -- Free temporary registers 695*387f9dfdSAndroid Build Coastguard Workerend 696*387f9dfdSAndroid Build Coastguard Worker 697*387f9dfdSAndroid Build Coastguard Workerlocal function MAP_SET(map_var, key, key_imm, src) 698*387f9dfdSAndroid Build Coastguard Worker local map = V[map_var].const 699*387f9dfdSAndroid Build Coastguard Worker -- Delete when setting nil 700*387f9dfdSAndroid Build Coastguard Worker if V[src].type == ffi.typeof('void') then 701*387f9dfdSAndroid Build Coastguard Worker return MAP_DEL(map_var, key, key_imm) 702*387f9dfdSAndroid Build Coastguard Worker end 703*387f9dfdSAndroid Build Coastguard Worker -- Set R0, R1 (map fd, preempt R0) 704*387f9dfdSAndroid Build Coastguard Worker reg_alloc(stackslots, 0) -- Spill anything in R0 (unnamed tmp variable) 705*387f9dfdSAndroid Build Coastguard Worker MAP_INIT(map_var, key, key_imm) 706*387f9dfdSAndroid Build Coastguard Worker reg_alloc(stackslots, 4) -- Spill anything in R4 (unnamed tmp variable) 707*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.K, 4, 0, 0, 0) -- BPF_ANY, create new element or update existing 708*387f9dfdSAndroid Build Coastguard Worker -- Reserve R3 for value pointer 709*387f9dfdSAndroid Build Coastguard Worker reg_alloc(stackslots, 3) -- Spill anything in R3 (unnamed tmp variable) 710*387f9dfdSAndroid Build Coastguard Worker local val_size = ffi.sizeof(map.val_type) 711*387f9dfdSAndroid Build Coastguard Worker local w = const_width[val_size] or BPF.DW 712*387f9dfdSAndroid Build Coastguard Worker local pod_type = const_width[val_size] 713*387f9dfdSAndroid Build Coastguard Worker -- Stack pointer must be aligned to both key/value size and have enough headroom for (key, value) 714*387f9dfdSAndroid Build Coastguard Worker local sp = stack_top + ffi.sizeof(map.key_type) + val_size 715*387f9dfdSAndroid Build Coastguard Worker sp = sp + (sp % val_size) 716*387f9dfdSAndroid Build Coastguard Worker local base = V[src].const 717*387f9dfdSAndroid Build Coastguard Worker if base and not is_proxy(base) then 718*387f9dfdSAndroid Build Coastguard Worker assert(pod_type, 'NYI: MAP[K] = imm V; V width must be 1/2/4/8') 719*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + w, 10, 0, -sp, base) 720*387f9dfdSAndroid Build Coastguard Worker -- Value is in register, spill it 721*387f9dfdSAndroid Build Coastguard Worker elseif V[src].reg and pod_type then 722*387f9dfdSAndroid Build Coastguard Worker -- Value is a pointer, derefernce it and spill it 723*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(V[src].type) then 724*387f9dfdSAndroid Build Coastguard Worker vderef(3, V[src].reg, V[src]) 725*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + w, 10, 3, -sp, 0) 726*387f9dfdSAndroid Build Coastguard Worker else 727*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + w, 10, V[src].reg, -sp, 0) 728*387f9dfdSAndroid Build Coastguard Worker end 729*387f9dfdSAndroid Build Coastguard Worker -- We get a pointer to spilled register on stack 730*387f9dfdSAndroid Build Coastguard Worker elseif V[src].spill then 731*387f9dfdSAndroid Build Coastguard Worker -- If variable is a pointer, we can load it to R3 directly (save "LEA") 732*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(V[src].type) then 733*387f9dfdSAndroid Build Coastguard Worker reg_fill(src, 3) 734*387f9dfdSAndroid Build Coastguard Worker -- If variable is a stack pointer, we don't have to check it 735*387f9dfdSAndroid Build Coastguard Worker if base.__base then 736*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem) 737*387f9dfdSAndroid Build Coastguard Worker return 738*387f9dfdSAndroid Build Coastguard Worker end 739*387f9dfdSAndroid Build Coastguard Worker vderef(3, V[src].reg, V[src]) 740*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + w, 10, 3, -sp, 0) 741*387f9dfdSAndroid Build Coastguard Worker else 742*387f9dfdSAndroid Build Coastguard Worker sp = V[src].spill 743*387f9dfdSAndroid Build Coastguard Worker end 744*387f9dfdSAndroid Build Coastguard Worker -- Value is already on stack, write to base-relative address 745*387f9dfdSAndroid Build Coastguard Worker elseif base.__base then 746*387f9dfdSAndroid Build Coastguard Worker if val_size ~= ffi.sizeof(V[src].type) then 747*387f9dfdSAndroid Build Coastguard Worker local err = string.format('VAR %d type (%s) incompatible with BPF map value type (%s): expected %d, got %d', 748*387f9dfdSAndroid Build Coastguard Worker src, V[src].type, map.val_type, val_size, ffi.sizeof(V[src].type)) 749*387f9dfdSAndroid Build Coastguard Worker error(err) 750*387f9dfdSAndroid Build Coastguard Worker end 751*387f9dfdSAndroid Build Coastguard Worker sp = base.__base 752*387f9dfdSAndroid Build Coastguard Worker -- Value is constant, materialize it on stack 753*387f9dfdSAndroid Build Coastguard Worker else 754*387f9dfdSAndroid Build Coastguard Worker error('VAR '.. src ..' is neither const-expr/register/stack/spilled') 755*387f9dfdSAndroid Build Coastguard Worker end 756*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, 10, 0, 0) 757*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, -sp) 758*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.map_update_elem) 759*387f9dfdSAndroid Build Coastguard Worker V[stackslots].reg = nil -- Free temporary registers 760*387f9dfdSAndroid Build Coastguard Workerend 761*387f9dfdSAndroid Build Coastguard Worker 762*387f9dfdSAndroid Build Coastguard Worker-- Finally - this table translates LuaJIT bytecode into code emitter actions. 763*387f9dfdSAndroid Build Coastguard Workerlocal BC = { 764*387f9dfdSAndroid Build Coastguard Worker -- Constants 765*387f9dfdSAndroid Build Coastguard Worker KNUM = function(a, _, c, _) -- KNUM 766*387f9dfdSAndroid Build Coastguard Worker if c < 2147483648 then 767*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, c, ffi.typeof('int32_t')) 768*387f9dfdSAndroid Build Coastguard Worker else 769*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, c, ffi.typeof('uint64_t')) 770*387f9dfdSAndroid Build Coastguard Worker end 771*387f9dfdSAndroid Build Coastguard Worker end, 772*387f9dfdSAndroid Build Coastguard Worker KSHORT = function(a, _, _, d) -- KSHORT 773*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, d, ffi.typeof('int16_t')) 774*387f9dfdSAndroid Build Coastguard Worker end, 775*387f9dfdSAndroid Build Coastguard Worker KCDATA = function(a, _, c, _) -- KCDATA 776*387f9dfdSAndroid Build Coastguard Worker -- Coerce numeric types if possible 777*387f9dfdSAndroid Build Coastguard Worker local ct = ffi.typeof(c) 778*387f9dfdSAndroid Build Coastguard Worker if ffi.istype(ct, ffi.typeof('uint64_t')) or ffi.istype(ct, ffi.typeof('int64_t')) then 779*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, c, ct) 780*387f9dfdSAndroid Build Coastguard Worker elseif tonumber(c) ~= nil then 781*387f9dfdSAndroid Build Coastguard Worker -- TODO: this should not be possible 782*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, tonumber(c), ct) 783*387f9dfdSAndroid Build Coastguard Worker else 784*387f9dfdSAndroid Build Coastguard Worker error('NYI: cannot use CDATA constant of type ' .. ct) 785*387f9dfdSAndroid Build Coastguard Worker end 786*387f9dfdSAndroid Build Coastguard Worker end, 787*387f9dfdSAndroid Build Coastguard Worker KPRI = function(a, _, _, d) -- KPRI 788*387f9dfdSAndroid Build Coastguard Worker -- KNIL is 0, must create a special type to identify it 789*387f9dfdSAndroid Build Coastguard Worker local vtype = (d < 1) and ffi.typeof('void') or ffi.typeof('uint8_t') 790*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, (d < 2) and 0 or 1, vtype) 791*387f9dfdSAndroid Build Coastguard Worker end, 792*387f9dfdSAndroid Build Coastguard Worker KSTR = function(a, _, c, _) -- KSTR 793*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, c, ffi.typeof('const char[?]')) 794*387f9dfdSAndroid Build Coastguard Worker end, 795*387f9dfdSAndroid Build Coastguard Worker MOV = function(a, _, _, d) -- MOV var, var 796*387f9dfdSAndroid Build Coastguard Worker vcopy(a, d) 797*387f9dfdSAndroid Build Coastguard Worker end, 798*387f9dfdSAndroid Build Coastguard Worker 799*387f9dfdSAndroid Build Coastguard Worker -- Comparison ops 800*387f9dfdSAndroid Build Coastguard Worker -- Note: comparisons are always followed by JMP opcode, that 801*387f9dfdSAndroid Build Coastguard Worker -- will fuse following JMP to JMP+CMP instruction in BPF 802*387f9dfdSAndroid Build Coastguard Worker -- Note: we're narrowed to integers, so operand/operator inversion is legit 803*387f9dfdSAndroid Build Coastguard Worker ISLT = function(a, _, _, d) return CMP_REG(d, a, 'JGE') end, -- (a < d) (inverted) 804*387f9dfdSAndroid Build Coastguard Worker ISGE = function(a, _, _, d) return CMP_REG(a, d, 'JGE') end, -- (a >= d) 805*387f9dfdSAndroid Build Coastguard Worker ISGT = function(a, _, _, d) return CMP_REG(a, d, 'JGT') end, -- (a > d) 806*387f9dfdSAndroid Build Coastguard Worker ISEQV = function(a, _, _, d) return CMP_REG(a, d, 'JEQ') end, -- (a == d) 807*387f9dfdSAndroid Build Coastguard Worker ISNEV = function(a, _, _, d) return CMP_REG(a, d, 'JNE') end, -- (a ~= d) 808*387f9dfdSAndroid Build Coastguard Worker ISEQS = function(a, _, c, _) return CMP_IMM(a, c, 'JEQ') end, -- (a == str(c)) 809*387f9dfdSAndroid Build Coastguard Worker ISNES = function(a, _, c, _) return CMP_IMM(a, c, 'JNE') end, -- (a ~= str(c)) 810*387f9dfdSAndroid Build Coastguard Worker ISEQN = function(a, _, c, _) return CMP_IMM(a, c, 'JEQ') end, -- (a == c) 811*387f9dfdSAndroid Build Coastguard Worker ISNEN = function(a, _, c, _) return CMP_IMM(a, c, 'JNE') end, -- (a ~= c) 812*387f9dfdSAndroid Build Coastguard Worker IST = function(_, _, _, d) return CMP_IMM(d, 0, 'JNE') end, -- (d) 813*387f9dfdSAndroid Build Coastguard Worker ISF = function(_, _, _, d) return CMP_IMM(d, 0, 'JEQ') end, -- (not d) 814*387f9dfdSAndroid Build Coastguard Worker ISEQP = function(a, _, c, _) return CMP_IMM(a, c, 'JEQ') end, -- ISEQP (a == c) 815*387f9dfdSAndroid Build Coastguard Worker -- Binary operations with RHS constants 816*387f9dfdSAndroid Build Coastguard Worker ADDVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'ADD') end, 817*387f9dfdSAndroid Build Coastguard Worker SUBVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'SUB') end, 818*387f9dfdSAndroid Build Coastguard Worker MULVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'MUL') end, 819*387f9dfdSAndroid Build Coastguard Worker DIVVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'DIV') end, 820*387f9dfdSAndroid Build Coastguard Worker MODVN = function(a, b, c, _) return ALU_IMM(a, b, c, 'MOD') end, 821*387f9dfdSAndroid Build Coastguard Worker -- Binary operations with LHS constants 822*387f9dfdSAndroid Build Coastguard Worker -- Cheat code: we're narrowed to integer arithmetic, so MUL+ADD are commutative 823*387f9dfdSAndroid Build Coastguard Worker ADDNV = function(a, b, c, _) return ALU_IMM(a, b, c, 'ADD') end, -- ADDNV 824*387f9dfdSAndroid Build Coastguard Worker MULNV = function(a, b, c, _) return ALU_IMM(a, b, c, 'MUL') end, -- MULNV 825*387f9dfdSAndroid Build Coastguard Worker SUBNV = function(a, b, c, _) return ALU_IMM_NV(a, c, b, 'SUB') end, -- SUBNV 826*387f9dfdSAndroid Build Coastguard Worker DIVNV = function(a, b, c, _) return ALU_IMM_NV(a, c, b, 'DIV') end, -- DIVNV 827*387f9dfdSAndroid Build Coastguard Worker -- Binary operations between registers 828*387f9dfdSAndroid Build Coastguard Worker ADDVV = function(a, b, _, d) return ALU_REG(a, b, d, 'ADD') end, 829*387f9dfdSAndroid Build Coastguard Worker SUBVV = function(a, b, _, d) return ALU_REG(a, b, d, 'SUB') end, 830*387f9dfdSAndroid Build Coastguard Worker MULVV = function(a, b, _, d) return ALU_REG(a, b, d, 'MUL') end, 831*387f9dfdSAndroid Build Coastguard Worker DIVVV = function(a, b, _, d) return ALU_REG(a, b, d, 'DIV') end, 832*387f9dfdSAndroid Build Coastguard Worker MODVV = function(a, b, _, d) return ALU_REG(a, b, d, 'MOD') end, 833*387f9dfdSAndroid Build Coastguard Worker -- Strings 834*387f9dfdSAndroid Build Coastguard Worker CAT = function(a, b, _, d) -- CAT A = B ~ D 835*387f9dfdSAndroid Build Coastguard Worker assert(V[b].const and V[d].const, 'NYI: CAT only works on compile-time expressions') 836*387f9dfdSAndroid Build Coastguard Worker assert(type(V[b].const) == 'string' and type(V[d].const) == 'string', 837*387f9dfdSAndroid Build Coastguard Worker 'NYI: CAT only works on compile-time strings') 838*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, V[b].const .. V[d].const) 839*387f9dfdSAndroid Build Coastguard Worker end, 840*387f9dfdSAndroid Build Coastguard Worker -- Tables 841*387f9dfdSAndroid Build Coastguard Worker GGET = function (a, _, c, _) -- GGET (A = GLOBAL[c]) 842*387f9dfdSAndroid Build Coastguard Worker if env[c] ~= nil then 843*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, env[c]) 844*387f9dfdSAndroid Build Coastguard Worker else error(string.format("undefined global '%s'", c)) end 845*387f9dfdSAndroid Build Coastguard Worker end, 846*387f9dfdSAndroid Build Coastguard Worker UGET = function (a, _, c, _) -- UGET (A = UPVALUE[c]) 847*387f9dfdSAndroid Build Coastguard Worker if env[c] ~= nil then 848*387f9dfdSAndroid Build Coastguard Worker vset(a, nil, env[c]) 849*387f9dfdSAndroid Build Coastguard Worker else error(string.format("undefined upvalue '%s'", c)) end 850*387f9dfdSAndroid Build Coastguard Worker end, 851*387f9dfdSAndroid Build Coastguard Worker TSETB = function (a, b, _, d) -- TSETB (B[D] = A) 852*387f9dfdSAndroid Build Coastguard Worker assert(V[b] and type(V[b].const) == 'table', 'NYI: B[D] where B is not Lua table, BPF map, or pointer') 853*387f9dfdSAndroid Build Coastguard Worker local vinfo = V[b].const 854*387f9dfdSAndroid Build Coastguard Worker if vinfo.__map then -- BPF map read (constant) 855*387f9dfdSAndroid Build Coastguard Worker return MAP_SET(b, nil, d, a) -- D is literal 856*387f9dfdSAndroid Build Coastguard Worker elseif vinfo.__dissector then 857*387f9dfdSAndroid Build Coastguard Worker assert(vinfo.__dissector, 'NYI: B[D] where B does not have a known element size') 858*387f9dfdSAndroid Build Coastguard Worker local w = ffi.sizeof(vinfo.__dissector) 859*387f9dfdSAndroid Build Coastguard Worker -- TODO: support vectorized moves larger than register width 860*387f9dfdSAndroid Build Coastguard Worker assert(const_width[w], 'B[C] = A, sizeof(A) must be 1/2/4/8') 861*387f9dfdSAndroid Build Coastguard Worker local src_reg, const = vscalar(a, w) 862*387f9dfdSAndroid Build Coastguard Worker -- If changing map value, write to absolute address + offset 863*387f9dfdSAndroid Build Coastguard Worker if V[b].source and V[b].source:find('ptr_to_map_value', 1, true) then 864*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(b) 865*387f9dfdSAndroid Build Coastguard Worker -- Optimization: immediate values (imm32) can be stored directly 866*387f9dfdSAndroid Build Coastguard Worker if type(const) == 'number' then 867*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, d, const) 868*387f9dfdSAndroid Build Coastguard Worker else 869*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, d, 0) 870*387f9dfdSAndroid Build Coastguard Worker end 871*387f9dfdSAndroid Build Coastguard Worker -- Table is already on stack, write to vinfo-relative address 872*387f9dfdSAndroid Build Coastguard Worker elseif vinfo.__base then 873*387f9dfdSAndroid Build Coastguard Worker -- Optimization: immediate values (imm32) can be stored directly 874*387f9dfdSAndroid Build Coastguard Worker if type(const) == 'number' then 875*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + const_width[w], 10, 0, -vinfo.__base + (d * w), const) 876*387f9dfdSAndroid Build Coastguard Worker else 877*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + const_width[w], 10, src_reg, -vinfo.__base + (d * w), 0) 878*387f9dfdSAndroid Build Coastguard Worker end 879*387f9dfdSAndroid Build Coastguard Worker else 880*387f9dfdSAndroid Build Coastguard Worker error('NYI: B[D] where B is not Lua table, BPF map, or pointer') 881*387f9dfdSAndroid Build Coastguard Worker end 882*387f9dfdSAndroid Build Coastguard Worker elseif vinfo and vinfo and V[a].const then 883*387f9dfdSAndroid Build Coastguard Worker vinfo[V[d].const] = V[a].const 884*387f9dfdSAndroid Build Coastguard Worker else 885*387f9dfdSAndroid Build Coastguard Worker error('NYI: B[D] where B is not Lua table, BPF map, or pointer') 886*387f9dfdSAndroid Build Coastguard Worker end 887*387f9dfdSAndroid Build Coastguard Worker end, 888*387f9dfdSAndroid Build Coastguard Worker TSETV = function (a, b, _, d) -- TSETV (B[D] = A) 889*387f9dfdSAndroid Build Coastguard Worker assert(V[b] and type(V[b].const) == 'table', 'NYI: B[D] where B is not Lua table, BPF map, or pointer') 890*387f9dfdSAndroid Build Coastguard Worker local vinfo = V[b].const 891*387f9dfdSAndroid Build Coastguard Worker if vinfo.__map then -- BPF map read (constant) 892*387f9dfdSAndroid Build Coastguard Worker return MAP_SET(b, d, nil, a) -- D is variable 893*387f9dfdSAndroid Build Coastguard Worker elseif vinfo.__dissector then 894*387f9dfdSAndroid Build Coastguard Worker assert(vinfo.__dissector, 'NYI: B[D] where B does not have a known element size') 895*387f9dfdSAndroid Build Coastguard Worker local w = ffi.sizeof(vinfo.__dissector) 896*387f9dfdSAndroid Build Coastguard Worker -- TODO: support vectorized moves larger than register width 897*387f9dfdSAndroid Build Coastguard Worker assert(const_width[w], 'B[C] = A, sizeof(A) must be 1/2/4/8') 898*387f9dfdSAndroid Build Coastguard Worker local src_reg, const = vscalar(a, w) 899*387f9dfdSAndroid Build Coastguard Worker -- If changing map value, write to absolute address + offset 900*387f9dfdSAndroid Build Coastguard Worker if V[b].source and V[b].source:find('ptr_to_map_value', 1, true) then 901*387f9dfdSAndroid Build Coastguard Worker -- Calculate variable address from two registers 902*387f9dfdSAndroid Build Coastguard Worker local tmp_var = stackslots + 1 903*387f9dfdSAndroid Build Coastguard Worker vset(tmp_var, nil, d) 904*387f9dfdSAndroid Build Coastguard Worker ALU_REG(tmp_var, tmp_var, b, 'ADD') 905*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(tmp_var) 906*387f9dfdSAndroid Build Coastguard Worker V[tmp_var].reg = nil -- Only temporary allocation 907*387f9dfdSAndroid Build Coastguard Worker -- Optimization: immediate values (imm32) can be stored directly 908*387f9dfdSAndroid Build Coastguard Worker if type(const) == 'number' and w < 8 then 909*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, 0, const) 910*387f9dfdSAndroid Build Coastguard Worker else 911*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, 0, 0) 912*387f9dfdSAndroid Build Coastguard Worker end 913*387f9dfdSAndroid Build Coastguard Worker -- Table is already on stack, write to vinfo-relative address 914*387f9dfdSAndroid Build Coastguard Worker elseif vinfo.__base then 915*387f9dfdSAndroid Build Coastguard Worker -- Calculate variable address from two registers 916*387f9dfdSAndroid Build Coastguard Worker local tmp_var = stackslots + 1 917*387f9dfdSAndroid Build Coastguard Worker vcopy(tmp_var, d) -- Element position 918*387f9dfdSAndroid Build Coastguard Worker if w > 1 then 919*387f9dfdSAndroid Build Coastguard Worker ALU_IMM(tmp_var, tmp_var, w, 'MUL') -- multiply by element size 920*387f9dfdSAndroid Build Coastguard Worker end 921*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(tmp_var) -- add R10 (stack pointer) 922*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.ADD + BPF.X, dst_reg, 10, 0, 0) 923*387f9dfdSAndroid Build Coastguard Worker V[tmp_var].reg = nil -- Only temporary allocation 924*387f9dfdSAndroid Build Coastguard Worker -- Optimization: immediate values (imm32) can be stored directly 925*387f9dfdSAndroid Build Coastguard Worker if type(const) == 'number' and w < 8 then 926*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, -vinfo.__base, const) 927*387f9dfdSAndroid Build Coastguard Worker else 928*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, -vinfo.__base, 0) 929*387f9dfdSAndroid Build Coastguard Worker end 930*387f9dfdSAndroid Build Coastguard Worker else 931*387f9dfdSAndroid Build Coastguard Worker error('NYI: B[D] where B is not Lua table, BPF map, or pointer') 932*387f9dfdSAndroid Build Coastguard Worker end 933*387f9dfdSAndroid Build Coastguard Worker elseif vinfo and V[d].const and V[a].const then 934*387f9dfdSAndroid Build Coastguard Worker vinfo[V[d].const] = V[a].const 935*387f9dfdSAndroid Build Coastguard Worker else 936*387f9dfdSAndroid Build Coastguard Worker error('NYI: B[D] where B is not Lua table, BPF map, or pointer') 937*387f9dfdSAndroid Build Coastguard Worker end 938*387f9dfdSAndroid Build Coastguard Worker end, 939*387f9dfdSAndroid Build Coastguard Worker TSETS = function (a, b, c, _) -- TSETS (B[C] = A) 940*387f9dfdSAndroid Build Coastguard Worker assert(V[b] and V[b].const, 'NYI: B[D] where B is not Lua table, BPF map, or pointer') 941*387f9dfdSAndroid Build Coastguard Worker local base = V[b].const 942*387f9dfdSAndroid Build Coastguard Worker if base.__dissector then 943*387f9dfdSAndroid Build Coastguard Worker local ofs,bpos = ffi.offsetof(base.__dissector, c) 944*387f9dfdSAndroid Build Coastguard Worker assert(not bpos, 'NYI: B[C] = A, where C is a bitfield') 945*387f9dfdSAndroid Build Coastguard Worker local w = builtins.sizeofattr(base.__dissector, c) 946*387f9dfdSAndroid Build Coastguard Worker -- TODO: support vectorized moves larger than register width 947*387f9dfdSAndroid Build Coastguard Worker assert(const_width[w], 'B[C] = A, sizeof(A) must be 1/2/4/8') 948*387f9dfdSAndroid Build Coastguard Worker local src_reg, const = vscalar(a, w) 949*387f9dfdSAndroid Build Coastguard Worker -- If changing map value, write to absolute address + offset 950*387f9dfdSAndroid Build Coastguard Worker if V[b].source and V[b].source:find('ptr_to_map_value', 1, true) then 951*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(b) 952*387f9dfdSAndroid Build Coastguard Worker -- Optimization: immediate values (imm32) can be stored directly 953*387f9dfdSAndroid Build Coastguard Worker if type(const) == 'number' and w < 8 then 954*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + const_width[w], dst_reg, 0, ofs, const) 955*387f9dfdSAndroid Build Coastguard Worker else 956*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + const_width[w], dst_reg, src_reg, ofs, 0) 957*387f9dfdSAndroid Build Coastguard Worker end 958*387f9dfdSAndroid Build Coastguard Worker -- Table is already on stack, write to base-relative address 959*387f9dfdSAndroid Build Coastguard Worker elseif base.__base then 960*387f9dfdSAndroid Build Coastguard Worker -- Optimization: immediate values (imm32) can be stored directly 961*387f9dfdSAndroid Build Coastguard Worker if type(const) == 'number' and w < 8 then 962*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.ST + const_width[w], 10, 0, -base.__base + ofs, const) 963*387f9dfdSAndroid Build Coastguard Worker else 964*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.STX + const_width[w], 10, src_reg, -base.__base + ofs, 0) 965*387f9dfdSAndroid Build Coastguard Worker end 966*387f9dfdSAndroid Build Coastguard Worker else 967*387f9dfdSAndroid Build Coastguard Worker error('NYI: B[C] where B is not Lua table, BPF map, or pointer') 968*387f9dfdSAndroid Build Coastguard Worker end 969*387f9dfdSAndroid Build Coastguard Worker elseif V[a].const then 970*387f9dfdSAndroid Build Coastguard Worker base[c] = V[a].const 971*387f9dfdSAndroid Build Coastguard Worker else 972*387f9dfdSAndroid Build Coastguard Worker error('NYI: B[C] where B is not Lua table, BPF map, or pointer') 973*387f9dfdSAndroid Build Coastguard Worker end 974*387f9dfdSAndroid Build Coastguard Worker end, 975*387f9dfdSAndroid Build Coastguard Worker TGETB = function (a, b, _, d) -- TGETB (A = B[D]) 976*387f9dfdSAndroid Build Coastguard Worker local base = V[b].const 977*387f9dfdSAndroid Build Coastguard Worker assert(type(base) == 'table', 'NYI: B[C] where C is string and B not Lua table or BPF map') 978*387f9dfdSAndroid Build Coastguard Worker if a ~= b then vset(a) end 979*387f9dfdSAndroid Build Coastguard Worker if base.__map then -- BPF map read (constant) 980*387f9dfdSAndroid Build Coastguard Worker MAP_GET(a, b, nil, d) 981*387f9dfdSAndroid Build Coastguard Worker -- Pointer access with a dissector (traditional uses BPF_LD, direct uses BPF_MEM) 982*387f9dfdSAndroid Build Coastguard Worker elseif V[b].source and V[b].source:find('ptr_to_') then 983*387f9dfdSAndroid Build Coastguard Worker local vtype = base.__dissector and base.__dissector or ffi.typeof('uint8_t') 984*387f9dfdSAndroid Build Coastguard Worker LOAD(a, b, d, vtype) 985*387f9dfdSAndroid Build Coastguard Worker -- Specialise PTR[0] as dereference operator 986*387f9dfdSAndroid Build Coastguard Worker elseif cdef.isptr(V[b].type) and d == 0 then 987*387f9dfdSAndroid Build Coastguard Worker vcopy(a, b) 988*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(a) 989*387f9dfdSAndroid Build Coastguard Worker vderef(dst_reg, dst_reg, V[a]) 990*387f9dfdSAndroid Build Coastguard Worker V[a].type = V[a].const.__dissector 991*387f9dfdSAndroid Build Coastguard Worker else 992*387f9dfdSAndroid Build Coastguard Worker error('NYI: A = B[D], where B is not Lua table or packet dissector or pointer dereference') 993*387f9dfdSAndroid Build Coastguard Worker end 994*387f9dfdSAndroid Build Coastguard Worker end, 995*387f9dfdSAndroid Build Coastguard Worker TGETV = function (a, b, _, d) -- TGETV (A = B[D]) 996*387f9dfdSAndroid Build Coastguard Worker local base = V[b].const 997*387f9dfdSAndroid Build Coastguard Worker assert(type(base) == 'table', 'NYI: B[C] where C is string and B not Lua table or BPF map') 998*387f9dfdSAndroid Build Coastguard Worker if a ~= b then vset(a) end 999*387f9dfdSAndroid Build Coastguard Worker if base.__map then -- BPF map read 1000*387f9dfdSAndroid Build Coastguard Worker MAP_GET(a, b, d) 1001*387f9dfdSAndroid Build Coastguard Worker -- Pointer access with a dissector (traditional uses BPF_LD, direct uses BPF_MEM) 1002*387f9dfdSAndroid Build Coastguard Worker elseif V[b].source and V[b].source:find('ptr_to_') then 1003*387f9dfdSAndroid Build Coastguard Worker local vtype = base.__dissector and base.__dissector or ffi.typeof('uint8_t') 1004*387f9dfdSAndroid Build Coastguard Worker LOAD(a, b, d, vtype) 1005*387f9dfdSAndroid Build Coastguard Worker -- Constant dereference 1006*387f9dfdSAndroid Build Coastguard Worker elseif type(V[d].const) == 'number' then 1007*387f9dfdSAndroid Build Coastguard Worker V[a].const = base[V[d].const] 1008*387f9dfdSAndroid Build Coastguard Worker else 1009*387f9dfdSAndroid Build Coastguard Worker error('NYI: A = B[D], where B is not Lua table or packet dissector or pointer dereference') 1010*387f9dfdSAndroid Build Coastguard Worker end 1011*387f9dfdSAndroid Build Coastguard Worker end, 1012*387f9dfdSAndroid Build Coastguard Worker TGETS = function (a, b, c, _) -- TGETS (A = B[C]) 1013*387f9dfdSAndroid Build Coastguard Worker local base = V[b].const 1014*387f9dfdSAndroid Build Coastguard Worker assert(type(base) == 'table', 'NYI: B[C] where C is string and B not Lua table or BPF map') 1015*387f9dfdSAndroid Build Coastguard Worker if a ~= b then vset(a) end 1016*387f9dfdSAndroid Build Coastguard Worker if base.__dissector then 1017*387f9dfdSAndroid Build Coastguard Worker local ofs,bpos,bsize = ffi.offsetof(base.__dissector, c) 1018*387f9dfdSAndroid Build Coastguard Worker -- Resolve table key using metatable 1019*387f9dfdSAndroid Build Coastguard Worker if not ofs and type(base.__dissector[c]) == 'string' then 1020*387f9dfdSAndroid Build Coastguard Worker c = base.__dissector[c] 1021*387f9dfdSAndroid Build Coastguard Worker ofs,bpos,bsize = ffi.offsetof(base.__dissector, c) 1022*387f9dfdSAndroid Build Coastguard Worker end 1023*387f9dfdSAndroid Build Coastguard Worker if not ofs and proto[c] then -- Load new dissector on given offset 1024*387f9dfdSAndroid Build Coastguard Worker BUILTIN(proto[c], a, b, c) 1025*387f9dfdSAndroid Build Coastguard Worker else 1026*387f9dfdSAndroid Build Coastguard Worker -- Loading register from offset is a little bit tricky as there are 1027*387f9dfdSAndroid Build Coastguard Worker -- several data sources and value loading modes with different restrictions 1028*387f9dfdSAndroid Build Coastguard Worker -- such as checking pointer values for NULL compared to using stack. 1029*387f9dfdSAndroid Build Coastguard Worker assert(ofs, tostring(base.__dissector)..'.'..c..' attribute not exists') 1030*387f9dfdSAndroid Build Coastguard Worker if a ~= b then vset(a) end 1031*387f9dfdSAndroid Build Coastguard Worker -- Dissected value is probably not constant anymore 1032*387f9dfdSAndroid Build Coastguard Worker local new_const = nil 1033*387f9dfdSAndroid Build Coastguard Worker local w, atype = builtins.sizeofattr(base.__dissector, c) 1034*387f9dfdSAndroid Build Coastguard Worker -- [SP+K] addressing using R10 (stack pointer) 1035*387f9dfdSAndroid Build Coastguard Worker -- Doesn't need to be checked for NULL 1036*387f9dfdSAndroid Build Coastguard Worker if base.__base and base.__base > 0 then 1037*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(atype) then -- If the member is pointer type, update base pointer with offset 1038*387f9dfdSAndroid Build Coastguard Worker new_const = {__base = base.__base-ofs} 1039*387f9dfdSAndroid Build Coastguard Worker else 1040*387f9dfdSAndroid Build Coastguard Worker local dst_reg = vreg(a, nil, true) 1041*387f9dfdSAndroid Build Coastguard Worker emit(BPF.MEM + BPF.LDX + const_width[w], dst_reg, 10, -base.__base+ofs, 0) 1042*387f9dfdSAndroid Build Coastguard Worker end 1043*387f9dfdSAndroid Build Coastguard Worker -- Pointer access with a dissector (traditional uses BPF_LD, direct uses BPF_MEM) 1044*387f9dfdSAndroid Build Coastguard Worker elseif V[b].source and V[b].source:find('ptr_to_') then 1045*387f9dfdSAndroid Build Coastguard Worker LOAD(a, b, ofs, atype) 1046*387f9dfdSAndroid Build Coastguard Worker else 1047*387f9dfdSAndroid Build Coastguard Worker error('NYI: B[C] where B is not Lua table, BPF map, or pointer') 1048*387f9dfdSAndroid Build Coastguard Worker end 1049*387f9dfdSAndroid Build Coastguard Worker -- Bitfield, must be further narrowed with a bitmask/shift 1050*387f9dfdSAndroid Build Coastguard Worker if bpos then 1051*387f9dfdSAndroid Build Coastguard Worker local mask = 0 1052*387f9dfdSAndroid Build Coastguard Worker for i=bpos+1,bpos+bsize do 1053*387f9dfdSAndroid Build Coastguard Worker mask = bit.bor(mask, bit.lshift(1, w*8-i)) 1054*387f9dfdSAndroid Build Coastguard Worker end 1055*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.AND + BPF.K, vreg(a), 0, 0, mask) 1056*387f9dfdSAndroid Build Coastguard Worker -- Free optimization: single-bit values need just boolean result 1057*387f9dfdSAndroid Build Coastguard Worker if bsize > 1 then 1058*387f9dfdSAndroid Build Coastguard Worker local shift = w*8-bsize-bpos 1059*387f9dfdSAndroid Build Coastguard Worker if shift > 0 then 1060*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.RSH + BPF.K, vreg(a), 0, 0, shift) 1061*387f9dfdSAndroid Build Coastguard Worker end 1062*387f9dfdSAndroid Build Coastguard Worker end 1063*387f9dfdSAndroid Build Coastguard Worker end 1064*387f9dfdSAndroid Build Coastguard Worker V[a].type = atype 1065*387f9dfdSAndroid Build Coastguard Worker V[a].const = new_const 1066*387f9dfdSAndroid Build Coastguard Worker V[a].source = V[b].source 1067*387f9dfdSAndroid Build Coastguard Worker -- Track direct access to skb data 1068*387f9dfdSAndroid Build Coastguard Worker -- see https://www.kernel.org/doc/Documentation/networking/filter.txt "Direct packet access" 1069*387f9dfdSAndroid Build Coastguard Worker if ffi.istype(base.__dissector, ffi.typeof('struct sk_buff')) then 1070*387f9dfdSAndroid Build Coastguard Worker -- Direct access to skb uses skb->data and skb->data_end 1071*387f9dfdSAndroid Build Coastguard Worker -- which are encoded as u32, but are actually pointers 1072*387f9dfdSAndroid Build Coastguard Worker if c == 'data' or c == 'data_end' then 1073*387f9dfdSAndroid Build Coastguard Worker V[a].const = {__dissector = ffi.typeof('uint8_t')} 1074*387f9dfdSAndroid Build Coastguard Worker V[a].source = 'ptr_to_skb' 1075*387f9dfdSAndroid Build Coastguard Worker end 1076*387f9dfdSAndroid Build Coastguard Worker end 1077*387f9dfdSAndroid Build Coastguard Worker end 1078*387f9dfdSAndroid Build Coastguard Worker else 1079*387f9dfdSAndroid Build Coastguard Worker V[a].const = base[c] 1080*387f9dfdSAndroid Build Coastguard Worker end 1081*387f9dfdSAndroid Build Coastguard Worker end, 1082*387f9dfdSAndroid Build Coastguard Worker -- Loops and branches 1083*387f9dfdSAndroid Build Coastguard Worker CALLM = function (a, b, _, d) -- A = A(A+1, ..., A+D+MULTRES) 1084*387f9dfdSAndroid Build Coastguard Worker -- NYI: Support single result only 1085*387f9dfdSAndroid Build Coastguard Worker CALL(a, b, d+2) 1086*387f9dfdSAndroid Build Coastguard Worker end, 1087*387f9dfdSAndroid Build Coastguard Worker CALL = function (a, b, _, d) -- A = A(A+1, ..., A+D-1) 1088*387f9dfdSAndroid Build Coastguard Worker CALL(a, b, d) 1089*387f9dfdSAndroid Build Coastguard Worker end, 1090*387f9dfdSAndroid Build Coastguard Worker JMP = function (a, _, c, _) -- JMP 1091*387f9dfdSAndroid Build Coastguard Worker -- Discard unused slots after jump 1092*387f9dfdSAndroid Build Coastguard Worker for i, _ in pairs(V) do 1093*387f9dfdSAndroid Build Coastguard Worker if i >= a and i < stackslots then 1094*387f9dfdSAndroid Build Coastguard Worker V[i] = nil 1095*387f9dfdSAndroid Build Coastguard Worker end 1096*387f9dfdSAndroid Build Coastguard Worker end 1097*387f9dfdSAndroid Build Coastguard Worker -- Cross basic block boundary if the jump target isn't provably unreachable 1098*387f9dfdSAndroid Build Coastguard Worker local val = code.fixup[c] or {} 1099*387f9dfdSAndroid Build Coastguard Worker if code.seen_cmp and code.seen_cmp ~= ALWAYS then 1100*387f9dfdSAndroid Build Coastguard Worker if code.seen_cmp ~= NEVER then -- Do not emit the jump or fixup 1101*387f9dfdSAndroid Build Coastguard Worker -- Store previous CMP insn for reemitting after compensation code 1102*387f9dfdSAndroid Build Coastguard Worker local jmpi = ffi.new('struct bpf_insn', code.insn[code.pc-1]) 1103*387f9dfdSAndroid Build Coastguard Worker code.pc = code.pc - 1 1104*387f9dfdSAndroid Build Coastguard Worker -- First branch point, emit compensation code 1105*387f9dfdSAndroid Build Coastguard Worker local Vcomp = Vstate[c] 1106*387f9dfdSAndroid Build Coastguard Worker if not Vcomp then 1107*387f9dfdSAndroid Build Coastguard Worker -- Select scratch register (R0-5) that isn't used as operand 1108*387f9dfdSAndroid Build Coastguard Worker -- in the CMP instruction, as the variable may not be live, after 1109*387f9dfdSAndroid Build Coastguard Worker -- the JMP, but it may be used in the JMP+CMP instruction itself 1110*387f9dfdSAndroid Build Coastguard Worker local tmp_reg = 0 1111*387f9dfdSAndroid Build Coastguard Worker for reg = 0, 5 do 1112*387f9dfdSAndroid Build Coastguard Worker if reg ~= jmpi.dst_reg and reg ~= jmpi.src_reg then 1113*387f9dfdSAndroid Build Coastguard Worker tmp_reg = reg 1114*387f9dfdSAndroid Build Coastguard Worker break 1115*387f9dfdSAndroid Build Coastguard Worker end 1116*387f9dfdSAndroid Build Coastguard Worker end 1117*387f9dfdSAndroid Build Coastguard Worker -- Force materialization of constants at the end of BB 1118*387f9dfdSAndroid Build Coastguard Worker for i, v in pairs(V) do 1119*387f9dfdSAndroid Build Coastguard Worker if not v.reg and cdef.isimmconst(v) then 1120*387f9dfdSAndroid Build Coastguard Worker vreg(i, tmp_reg) -- Load to TMP register (not saved) 1121*387f9dfdSAndroid Build Coastguard Worker reg_spill(i) -- Spill caller-saved registers 1122*387f9dfdSAndroid Build Coastguard Worker end 1123*387f9dfdSAndroid Build Coastguard Worker end 1124*387f9dfdSAndroid Build Coastguard Worker -- Record variable state 1125*387f9dfdSAndroid Build Coastguard Worker Vstate[c] = V 1126*387f9dfdSAndroid Build Coastguard Worker Vcomp = V 1127*387f9dfdSAndroid Build Coastguard Worker V = table_copy(V) 1128*387f9dfdSAndroid Build Coastguard Worker -- Variable state already set, emit specific compensation code 1129*387f9dfdSAndroid Build Coastguard Worker else 1130*387f9dfdSAndroid Build Coastguard Worker bb_end(Vcomp) 1131*387f9dfdSAndroid Build Coastguard Worker end 1132*387f9dfdSAndroid Build Coastguard Worker -- Record pointer NULL check from condition 1133*387f9dfdSAndroid Build Coastguard Worker -- If the condition checks pointer variable against NULL, 1134*387f9dfdSAndroid Build Coastguard Worker -- we can assume it will not be NULL in the fall-through block 1135*387f9dfdSAndroid Build Coastguard Worker if code.seen_null_guard then 1136*387f9dfdSAndroid Build Coastguard Worker local var = code.seen_null_guard 1137*387f9dfdSAndroid Build Coastguard Worker -- The null guard can have two forms: 1138*387f9dfdSAndroid Build Coastguard Worker -- if x == nil then goto 1139*387f9dfdSAndroid Build Coastguard Worker -- if x ~= nil then goto 1140*387f9dfdSAndroid Build Coastguard Worker -- First form guarantees that the variable will be non-nil on the following instruction 1141*387f9dfdSAndroid Build Coastguard Worker -- Second form guarantees that the variable will be non-nil at the jump target 1142*387f9dfdSAndroid Build Coastguard Worker local vinfo = code.seen_null_guard_inverse and Vcomp[var] or V[var] 1143*387f9dfdSAndroid Build Coastguard Worker if vinfo.source then 1144*387f9dfdSAndroid Build Coastguard Worker local pos = vinfo.source:find('_or_null', 1, true) 1145*387f9dfdSAndroid Build Coastguard Worker if pos then 1146*387f9dfdSAndroid Build Coastguard Worker vinfo.source = vinfo.source:sub(1, pos - 1) 1147*387f9dfdSAndroid Build Coastguard Worker end 1148*387f9dfdSAndroid Build Coastguard Worker end 1149*387f9dfdSAndroid Build Coastguard Worker end 1150*387f9dfdSAndroid Build Coastguard Worker -- Reemit CMP insn 1151*387f9dfdSAndroid Build Coastguard Worker emit(jmpi.code, jmpi.dst_reg, jmpi.src_reg, jmpi.off, jmpi.imm) 1152*387f9dfdSAndroid Build Coastguard Worker -- Fuse JMP into previous CMP opcode, mark JMP target for fixup 1153*387f9dfdSAndroid Build Coastguard Worker -- as we don't knot the relative offset in generated code yet 1154*387f9dfdSAndroid Build Coastguard Worker table.insert(val, code.pc-1) 1155*387f9dfdSAndroid Build Coastguard Worker code.fixup[c] = val 1156*387f9dfdSAndroid Build Coastguard Worker end 1157*387f9dfdSAndroid Build Coastguard Worker code.seen_cmp = nil 1158*387f9dfdSAndroid Build Coastguard Worker code.seen_null_guard = nil 1159*387f9dfdSAndroid Build Coastguard Worker code.seen_null_guard_inverse = nil 1160*387f9dfdSAndroid Build Coastguard Worker elseif c == code.bc_pc + 1 then -- luacheck: ignore 542 1161*387f9dfdSAndroid Build Coastguard Worker -- Eliminate jumps to next immediate instruction 1162*387f9dfdSAndroid Build Coastguard Worker -- e.g. 0002 JMP 1 => 0003 1163*387f9dfdSAndroid Build Coastguard Worker else 1164*387f9dfdSAndroid Build Coastguard Worker -- We need to synthesise a condition that's always true, however 1165*387f9dfdSAndroid Build Coastguard Worker -- BPF prohibits pointer arithmetic to prevent pointer leaks 1166*387f9dfdSAndroid Build Coastguard Worker -- so we have to clear out one register and use it for cmp that's always true 1167*387f9dfdSAndroid Build Coastguard Worker local dst_reg = reg_alloc(stackslots) 1168*387f9dfdSAndroid Build Coastguard Worker V[stackslots].reg = nil -- Only temporary allocation 1169*387f9dfdSAndroid Build Coastguard Worker -- First branch point, emit compensation code 1170*387f9dfdSAndroid Build Coastguard Worker local Vcomp = Vstate[c] 1171*387f9dfdSAndroid Build Coastguard Worker if not Vcomp then 1172*387f9dfdSAndroid Build Coastguard Worker -- Force materialization of constants at the end of BB 1173*387f9dfdSAndroid Build Coastguard Worker for i, v in pairs(V) do 1174*387f9dfdSAndroid Build Coastguard Worker if not v.reg and cdef.isimmconst(v) then 1175*387f9dfdSAndroid Build Coastguard Worker vreg(i, dst_reg) -- Load to TMP register (not saved) 1176*387f9dfdSAndroid Build Coastguard Worker reg_spill(i) -- Spill caller-saved registers 1177*387f9dfdSAndroid Build Coastguard Worker end 1178*387f9dfdSAndroid Build Coastguard Worker end 1179*387f9dfdSAndroid Build Coastguard Worker -- Record variable state 1180*387f9dfdSAndroid Build Coastguard Worker Vstate[c] = V 1181*387f9dfdSAndroid Build Coastguard Worker V = table_copy(V) 1182*387f9dfdSAndroid Build Coastguard Worker -- Variable state already set, emit specific compensation code 1183*387f9dfdSAndroid Build Coastguard Worker else 1184*387f9dfdSAndroid Build Coastguard Worker bb_end(Vcomp) 1185*387f9dfdSAndroid Build Coastguard Worker end 1186*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.K, dst_reg, 0, 0, 0) 1187*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.JEQ + BPF.K, dst_reg, 0, 0xffff, 0) 1188*387f9dfdSAndroid Build Coastguard Worker table.insert(val, code.pc-1) -- Fixup JMP target 1189*387f9dfdSAndroid Build Coastguard Worker code.reachable = false -- Code following the JMP is not reachable 1190*387f9dfdSAndroid Build Coastguard Worker code.fixup[c] = val 1191*387f9dfdSAndroid Build Coastguard Worker end 1192*387f9dfdSAndroid Build Coastguard Worker end, 1193*387f9dfdSAndroid Build Coastguard Worker RET1 = function (a, _, _, _) -- RET1 1194*387f9dfdSAndroid Build Coastguard Worker -- Free optimisation: spilled variable will not be filled again 1195*387f9dfdSAndroid Build Coastguard Worker for i, v in pairs(V) do 1196*387f9dfdSAndroid Build Coastguard Worker if i ~= a then v.reg = nil end 1197*387f9dfdSAndroid Build Coastguard Worker end 1198*387f9dfdSAndroid Build Coastguard Worker if V[a].reg ~= 0 then vreg(a, 0) end 1199*387f9dfdSAndroid Build Coastguard Worker -- Convenience: dereference pointer variables 1200*387f9dfdSAndroid Build Coastguard Worker -- e.g. 'return map[k]' will return actual map value, not pointer 1201*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(V[a].type) then 1202*387f9dfdSAndroid Build Coastguard Worker vderef(0, 0, V[a]) 1203*387f9dfdSAndroid Build Coastguard Worker end 1204*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.EXIT, 0, 0, 0, 0) 1205*387f9dfdSAndroid Build Coastguard Worker code.reachable = false 1206*387f9dfdSAndroid Build Coastguard Worker end, 1207*387f9dfdSAndroid Build Coastguard Worker RET0 = function (_, _, _, _) -- RET0 1208*387f9dfdSAndroid Build Coastguard Worker emit(BPF.ALU64 + BPF.MOV + BPF.K, 0, 0, 0, 0) 1209*387f9dfdSAndroid Build Coastguard Worker emit(BPF.JMP + BPF.EXIT, 0, 0, 0, 0) 1210*387f9dfdSAndroid Build Coastguard Worker code.reachable = false 1211*387f9dfdSAndroid Build Coastguard Worker end, 1212*387f9dfdSAndroid Build Coastguard Worker compile = function () 1213*387f9dfdSAndroid Build Coastguard Worker return code 1214*387f9dfdSAndroid Build Coastguard Worker end 1215*387f9dfdSAndroid Build Coastguard Worker} 1216*387f9dfdSAndroid Build Coastguard Worker 1217*387f9dfdSAndroid Build Coastguard Worker-- Composite instructions 1218*387f9dfdSAndroid Build Coastguard Workerfunction BC.CALLT(a, _, _, d) -- Tailcall: return A(A+1, ..., A+D-1) 1219*387f9dfdSAndroid Build Coastguard Worker CALL(a, 1, d) 1220*387f9dfdSAndroid Build Coastguard Worker BC.RET1(a) 1221*387f9dfdSAndroid Build Coastguard Workerend 1222*387f9dfdSAndroid Build Coastguard Worker 1223*387f9dfdSAndroid Build Coastguard Worker-- Always initialize R6 with R1 context 1224*387f9dfdSAndroid Build Coastguard Workeremit(BPF.ALU64 + BPF.MOV + BPF.X, 6, 1, 0, 0) 1225*387f9dfdSAndroid Build Coastguard Worker-- Register R6 as context variable (first argument) 1226*387f9dfdSAndroid Build Coastguard Workerif params and params > 0 then 1227*387f9dfdSAndroid Build Coastguard Worker vset(0, 6, param_types[1] or proto.skb) 1228*387f9dfdSAndroid Build Coastguard Worker assert(V[0].source == V[0].const.source) -- Propagate source annotation from typeinfo 1229*387f9dfdSAndroid Build Coastguard Workerend 1230*387f9dfdSAndroid Build Coastguard Worker-- Register tmpvars 1231*387f9dfdSAndroid Build Coastguard Workervset(stackslots) 1232*387f9dfdSAndroid Build Coastguard Workervset(stackslots+1) 1233*387f9dfdSAndroid Build Coastguard Workerreturn setmetatable(BC, { 1234*387f9dfdSAndroid Build Coastguard Worker __index = function (_, k, _) 1235*387f9dfdSAndroid Build Coastguard Worker if type(k) == 'number' then 1236*387f9dfdSAndroid Build Coastguard Worker local op_str = string.sub(require('jit.vmdef').bcnames, 6*k+1, 6*k+6) 1237*387f9dfdSAndroid Build Coastguard Worker error(string.format("NYI: opcode '0x%02x' (%-04s)", k, op_str)) 1238*387f9dfdSAndroid Build Coastguard Worker end 1239*387f9dfdSAndroid Build Coastguard Worker end, 1240*387f9dfdSAndroid Build Coastguard Worker __call = function (t, op, a, b, c, d) 1241*387f9dfdSAndroid Build Coastguard Worker code.bc_pc = code.bc_pc + 1 1242*387f9dfdSAndroid Build Coastguard Worker -- Exitting BB straight through, emit compensation code 1243*387f9dfdSAndroid Build Coastguard Worker if Vstate[code.bc_pc] then 1244*387f9dfdSAndroid Build Coastguard Worker if code.reachable then 1245*387f9dfdSAndroid Build Coastguard Worker -- Instruction is reachable from previous line 1246*387f9dfdSAndroid Build Coastguard Worker -- so we must make the variable allocation consistent 1247*387f9dfdSAndroid Build Coastguard Worker -- with the variable allocation at the jump source 1248*387f9dfdSAndroid Build Coastguard Worker -- e.g. 0001 x:R0 = 5 1249*387f9dfdSAndroid Build Coastguard Worker -- 0002 if rand() then goto 0005 1250*387f9dfdSAndroid Build Coastguard Worker -- 0003 x:R0 -> x:stack 1251*387f9dfdSAndroid Build Coastguard Worker -- 0004 y:R0 = 5 1252*387f9dfdSAndroid Build Coastguard Worker -- 0005 x:? = 10 <-- x was in R0 before jump, and stack after jump 1253*387f9dfdSAndroid Build Coastguard Worker bb_end(Vstate[code.bc_pc]) 1254*387f9dfdSAndroid Build Coastguard Worker else 1255*387f9dfdSAndroid Build Coastguard Worker -- Instruction isn't reachable from previous line, restore variable layout 1256*387f9dfdSAndroid Build Coastguard Worker -- e.g. RET or condition-less JMP on previous line 1257*387f9dfdSAndroid Build Coastguard Worker V = table_copy(Vstate[code.bc_pc]) 1258*387f9dfdSAndroid Build Coastguard Worker end 1259*387f9dfdSAndroid Build Coastguard Worker end 1260*387f9dfdSAndroid Build Coastguard Worker -- Perform fixup of jump targets 1261*387f9dfdSAndroid Build Coastguard Worker -- We need to do this because the number of consumed and emitted 1262*387f9dfdSAndroid Build Coastguard Worker -- bytecode instructions is different 1263*387f9dfdSAndroid Build Coastguard Worker local fixup = code.fixup[code.bc_pc] 1264*387f9dfdSAndroid Build Coastguard Worker if fixup ~= nil then 1265*387f9dfdSAndroid Build Coastguard Worker -- Patch JMP source insn with relative offset 1266*387f9dfdSAndroid Build Coastguard Worker for _,pc in ipairs(fixup) do 1267*387f9dfdSAndroid Build Coastguard Worker code.insn[pc].off = code.pc - 1 - pc 1268*387f9dfdSAndroid Build Coastguard Worker end 1269*387f9dfdSAndroid Build Coastguard Worker code.fixup[code.bc_pc] = nil 1270*387f9dfdSAndroid Build Coastguard Worker code.reachable = true 1271*387f9dfdSAndroid Build Coastguard Worker end 1272*387f9dfdSAndroid Build Coastguard Worker -- Execute 1273*387f9dfdSAndroid Build Coastguard Worker if code.reachable then 1274*387f9dfdSAndroid Build Coastguard Worker assert(t[op], string.format('NYI: instruction %s, parameters: %s,%s,%s,%s', op,a,b,c,d)) 1275*387f9dfdSAndroid Build Coastguard Worker return t[op](a, b, c, d) 1276*387f9dfdSAndroid Build Coastguard Worker end 1277*387f9dfdSAndroid Build Coastguard Worker end, 1278*387f9dfdSAndroid Build Coastguard Worker}) 1279*387f9dfdSAndroid Build Coastguard Workerend 1280*387f9dfdSAndroid Build Coastguard Worker 1281*387f9dfdSAndroid Build Coastguard Worker-- Emitted code dump 1282*387f9dfdSAndroid Build Coastguard Workerlocal function dump_mem(cls, ins, _, fuse) 1283*387f9dfdSAndroid Build Coastguard Worker -- This is a very dense MEM instruction decoder without much explanation 1284*387f9dfdSAndroid Build Coastguard Worker -- Refer to https://www.kernel.org/doc/Documentation/networking/filter.txt for instruction format 1285*387f9dfdSAndroid Build Coastguard Worker local mode = bit.band(ins.code, 0xe0) 1286*387f9dfdSAndroid Build Coastguard Worker if mode == BPF.XADD then cls = 5 end -- The only mode 1287*387f9dfdSAndroid Build Coastguard Worker local op_1 = {'LD', 'LDX', 'ST', 'STX', '', 'XADD'} 1288*387f9dfdSAndroid Build Coastguard Worker local op_2 = {[0]='W', [8]='H', [16]='B', [24]='DW'} 1289*387f9dfdSAndroid Build Coastguard Worker local name = op_1[cls+1] .. op_2[bit.band(ins.code, 0x18)] 1290*387f9dfdSAndroid Build Coastguard Worker local off = tonumber(ffi.cast('int16_t', ins.off)) -- Reinterpret as signed 1291*387f9dfdSAndroid Build Coastguard Worker local dst = cls < 2 and 'R'..ins.dst_reg or string.format('[R%d%+d]', ins.dst_reg, off) 1292*387f9dfdSAndroid Build Coastguard Worker local src = cls % 2 == 0 and '#'..ins.imm or 'R'..ins.src_reg 1293*387f9dfdSAndroid Build Coastguard Worker if cls == BPF.LDX then src = string.format('[R%d%+d]', ins.src_reg, off) end 1294*387f9dfdSAndroid Build Coastguard Worker if mode == BPF.ABS then src = string.format('skb[%d]', ins.imm) end 1295*387f9dfdSAndroid Build Coastguard Worker if mode == BPF.IND then src = string.format('skb[R%d%+d]', ins.src_reg, ins.imm) end 1296*387f9dfdSAndroid Build Coastguard Worker return string.format('%s\t%s\t%s', fuse and '' or name, fuse and '' or dst, src) 1297*387f9dfdSAndroid Build Coastguard Workerend 1298*387f9dfdSAndroid Build Coastguard Worker 1299*387f9dfdSAndroid Build Coastguard Workerlocal function dump_alu(cls, ins, pc) 1300*387f9dfdSAndroid Build Coastguard Worker local alu = {'ADD', 'SUB', 'MUL', 'DIV', 'OR', 'AND', 'LSH', 'RSH', 'NEG', 'MOD', 'XOR', 'MOV', 'ARSH', 'END' } 1301*387f9dfdSAndroid Build Coastguard Worker local jmp = {'JA', 'JEQ', 'JGT', 'JGE', 'JSET', 'JNE', 'JSGT', 'JSGE', 'CALL', 'EXIT'} 1302*387f9dfdSAndroid Build Coastguard Worker local helper = {'unspec', 'map_lookup_elem', 'map_update_elem', 'map_delete_elem', 'probe_read', 'ktime_get_ns', 1303*387f9dfdSAndroid Build Coastguard Worker 'trace_printk', 'get_prandom_u32', 'get_smp_processor_id', 'skb_store_bytes', 1304*387f9dfdSAndroid Build Coastguard Worker 'l3_csum_replace', 'l4_csum_replace', 'tail_call', 'clone_redirect', 'get_current_pid_tgid', 1305*387f9dfdSAndroid Build Coastguard Worker 'get_current_uid_gid', 'get_current_comm', 'get_cgroup_classid', 'skb_vlan_push', 'skb_vlan_pop', 1306*387f9dfdSAndroid Build Coastguard Worker 'skb_get_tunnel_key', 'skb_set_tunnel_key', 'perf_event_read', 'redirect', 'get_route_realm', 1307*387f9dfdSAndroid Build Coastguard Worker 'perf_event_output', 'skb_load_bytes'} 1308*387f9dfdSAndroid Build Coastguard Worker local op = 0 1309*387f9dfdSAndroid Build Coastguard Worker -- This is a very dense ALU instruction decoder without much explanation 1310*387f9dfdSAndroid Build Coastguard Worker -- Refer to https://www.kernel.org/doc/Documentation/networking/filter.txt for instruction format 1311*387f9dfdSAndroid Build Coastguard Worker for i = 0,13 do if 0x10 * i == bit.band(ins.code, 0xf0) then op = i + 1 break end end 1312*387f9dfdSAndroid Build Coastguard Worker local name = (cls == 5) and jmp[op] or alu[op] 1313*387f9dfdSAndroid Build Coastguard Worker local src = (bit.band(ins.code, 0x08) == BPF.X) and 'R'..ins.src_reg or '#'..ins.imm 1314*387f9dfdSAndroid Build Coastguard Worker local target = (cls == 5 and op < 9) and string.format('\t=> %04d', pc + ins.off + 1) or '' 1315*387f9dfdSAndroid Build Coastguard Worker if cls == 5 and op == 9 then target = string.format('\t; %s', helper[ins.imm + 1] or tostring(ins.imm)) end 1316*387f9dfdSAndroid Build Coastguard Worker return string.format('%s\t%s\t%s%s', name, 'R'..ins.dst_reg, src, target) 1317*387f9dfdSAndroid Build Coastguard Workerend 1318*387f9dfdSAndroid Build Coastguard Worker 1319*387f9dfdSAndroid Build Coastguard Workerlocal function dump_string(code, off, hide_counter) 1320*387f9dfdSAndroid Build Coastguard Worker if not code then return end 1321*387f9dfdSAndroid Build Coastguard Worker local cls_map = { 1322*387f9dfdSAndroid Build Coastguard Worker [0] = dump_mem, [1] = dump_mem, [2] = dump_mem, [3] = dump_mem, 1323*387f9dfdSAndroid Build Coastguard Worker [4] = dump_alu, [5] = dump_alu, [7] = dump_alu, 1324*387f9dfdSAndroid Build Coastguard Worker } 1325*387f9dfdSAndroid Build Coastguard Worker local result = {} 1326*387f9dfdSAndroid Build Coastguard Worker local fused = false 1327*387f9dfdSAndroid Build Coastguard Worker for i = off or 0, code.pc - 1 do 1328*387f9dfdSAndroid Build Coastguard Worker local ins = code.insn[i] 1329*387f9dfdSAndroid Build Coastguard Worker local cls = bit.band(ins.code, 0x07) 1330*387f9dfdSAndroid Build Coastguard Worker local line = cls_map[cls](cls, ins, i, fused) 1331*387f9dfdSAndroid Build Coastguard Worker if hide_counter then 1332*387f9dfdSAndroid Build Coastguard Worker table.insert(result, line) 1333*387f9dfdSAndroid Build Coastguard Worker else 1334*387f9dfdSAndroid Build Coastguard Worker table.insert(result, string.format('%04u\t%s', i, line)) 1335*387f9dfdSAndroid Build Coastguard Worker end 1336*387f9dfdSAndroid Build Coastguard Worker fused = string.find(line, 'LDDW', 1) 1337*387f9dfdSAndroid Build Coastguard Worker end 1338*387f9dfdSAndroid Build Coastguard Worker return table.concat(result, '\n') 1339*387f9dfdSAndroid Build Coastguard Workerend 1340*387f9dfdSAndroid Build Coastguard Worker 1341*387f9dfdSAndroid Build Coastguard Workerlocal function dump(code) 1342*387f9dfdSAndroid Build Coastguard Worker if not code then return end 1343*387f9dfdSAndroid Build Coastguard Worker print(string.format('-- BPF %s:0-%u', code.insn, code.pc)) 1344*387f9dfdSAndroid Build Coastguard Worker print(dump_string(code)) 1345*387f9dfdSAndroid Build Coastguard Workerend 1346*387f9dfdSAndroid Build Coastguard Worker 1347*387f9dfdSAndroid Build Coastguard Workerlocal function compile(prog, params) 1348*387f9dfdSAndroid Build Coastguard Worker -- Create code emitter sandbox, include caller locals 1349*387f9dfdSAndroid Build Coastguard Worker local env = { pkt=proto.pkt, eth=proto.pkt, BPF=BPF, ffi=ffi } 1350*387f9dfdSAndroid Build Coastguard Worker -- Include upvalues up to 4 nested scopes back 1351*387f9dfdSAndroid Build Coastguard Worker -- the narrower scope overrides broader scope 1352*387f9dfdSAndroid Build Coastguard Worker for k = 5, 2, -1 do 1353*387f9dfdSAndroid Build Coastguard Worker local i = 1 1354*387f9dfdSAndroid Build Coastguard Worker while true do 1355*387f9dfdSAndroid Build Coastguard Worker local ok, n, v = pcall(debug.getlocal, k, i) 1356*387f9dfdSAndroid Build Coastguard Worker if not ok or not n then break end 1357*387f9dfdSAndroid Build Coastguard Worker env[n] = v 1358*387f9dfdSAndroid Build Coastguard Worker i = i + 1 1359*387f9dfdSAndroid Build Coastguard Worker end 1360*387f9dfdSAndroid Build Coastguard Worker end 1361*387f9dfdSAndroid Build Coastguard Worker setmetatable(env, { 1362*387f9dfdSAndroid Build Coastguard Worker __index = function (_, k) 1363*387f9dfdSAndroid Build Coastguard Worker return proto[k] or builtins[k] or _G[k] 1364*387f9dfdSAndroid Build Coastguard Worker end 1365*387f9dfdSAndroid Build Coastguard Worker }) 1366*387f9dfdSAndroid Build Coastguard Worker -- Create code emitter and compile LuaJIT bytecode 1367*387f9dfdSAndroid Build Coastguard Worker if type(prog) == 'string' then prog = loadstring(prog) end 1368*387f9dfdSAndroid Build Coastguard Worker -- Create error handler to print traceback 1369*387f9dfdSAndroid Build Coastguard Worker local funci, pc = bytecode.funcinfo(prog), 0 1370*387f9dfdSAndroid Build Coastguard Worker local E = create_emitter(env, funci.stackslots, funci.params, params or {}) 1371*387f9dfdSAndroid Build Coastguard Worker local on_err = function (e) 1372*387f9dfdSAndroid Build Coastguard Worker funci = bytecode.funcinfo(prog, pc) 1373*387f9dfdSAndroid Build Coastguard Worker local from, to = 0, 0 1374*387f9dfdSAndroid Build Coastguard Worker for _ = 1, funci.currentline do 1375*387f9dfdSAndroid Build Coastguard Worker from = to 1376*387f9dfdSAndroid Build Coastguard Worker to = string.find(funci.source, '\n', from+1, true) or 0 1377*387f9dfdSAndroid Build Coastguard Worker end 1378*387f9dfdSAndroid Build Coastguard Worker print(funci.loc..':'..string.sub(funci.source, from+1, to-1)) 1379*387f9dfdSAndroid Build Coastguard Worker print('error: '..e) 1380*387f9dfdSAndroid Build Coastguard Worker print(debug.traceback()) 1381*387f9dfdSAndroid Build Coastguard Worker end 1382*387f9dfdSAndroid Build Coastguard Worker for _,op,a,b,c,d in bytecode.decoder(prog) do 1383*387f9dfdSAndroid Build Coastguard Worker local ok, _, err = xpcall(E,on_err,op,a,b,c,d) 1384*387f9dfdSAndroid Build Coastguard Worker if not ok then 1385*387f9dfdSAndroid Build Coastguard Worker return nil, err 1386*387f9dfdSAndroid Build Coastguard Worker end 1387*387f9dfdSAndroid Build Coastguard Worker end 1388*387f9dfdSAndroid Build Coastguard Worker return E:compile() 1389*387f9dfdSAndroid Build Coastguard Workerend 1390*387f9dfdSAndroid Build Coastguard Worker 1391*387f9dfdSAndroid Build Coastguard Worker-- BPF map interface 1392*387f9dfdSAndroid Build Coastguard Workerlocal bpf_map_mt = { 1393*387f9dfdSAndroid Build Coastguard Worker __gc = function (map) S.close(map.fd) end, 1394*387f9dfdSAndroid Build Coastguard Worker __len = function(map) return map.max_entries end, 1395*387f9dfdSAndroid Build Coastguard Worker __index = function (map, k) 1396*387f9dfdSAndroid Build Coastguard Worker if type(k) == 'string' then 1397*387f9dfdSAndroid Build Coastguard Worker -- Return iterator 1398*387f9dfdSAndroid Build Coastguard Worker if k == 'pairs' then 1399*387f9dfdSAndroid Build Coastguard Worker return function(t, key) 1400*387f9dfdSAndroid Build Coastguard Worker -- Get next key 1401*387f9dfdSAndroid Build Coastguard Worker local next_key = ffi.new(ffi.typeof(t.key)) 1402*387f9dfdSAndroid Build Coastguard Worker local cur_key 1403*387f9dfdSAndroid Build Coastguard Worker if key then 1404*387f9dfdSAndroid Build Coastguard Worker cur_key = t.key 1405*387f9dfdSAndroid Build Coastguard Worker t.key[0] = key 1406*387f9dfdSAndroid Build Coastguard Worker else 1407*387f9dfdSAndroid Build Coastguard Worker cur_key = ffi.new(ffi.typeof(t.key)) 1408*387f9dfdSAndroid Build Coastguard Worker end 1409*387f9dfdSAndroid Build Coastguard Worker local ok, err = S.bpf_map_op(S.c.BPF_CMD.MAP_GET_NEXT_KEY, map.fd, cur_key, next_key) 1410*387f9dfdSAndroid Build Coastguard Worker if not ok then return nil, err end 1411*387f9dfdSAndroid Build Coastguard Worker -- Get next value 1412*387f9dfdSAndroid Build Coastguard Worker assert(S.bpf_map_op(S.c.BPF_CMD.MAP_LOOKUP_ELEM, map.fd, next_key, map.val)) 1413*387f9dfdSAndroid Build Coastguard Worker return next_key[0], map.val[0] 1414*387f9dfdSAndroid Build Coastguard Worker end, map, nil 1415*387f9dfdSAndroid Build Coastguard Worker -- Read for perf event map 1416*387f9dfdSAndroid Build Coastguard Worker elseif k == 'reader' then 1417*387f9dfdSAndroid Build Coastguard Worker return function (pmap, pid, cpu, event_type) 1418*387f9dfdSAndroid Build Coastguard Worker -- Caller must either specify PID or CPU 1419*387f9dfdSAndroid Build Coastguard Worker if not pid or pid < 0 then 1420*387f9dfdSAndroid Build Coastguard Worker assert((cpu and cpu >= 0), 'NYI: creating composed reader for all CPUs') 1421*387f9dfdSAndroid Build Coastguard Worker pid = -1 1422*387f9dfdSAndroid Build Coastguard Worker end 1423*387f9dfdSAndroid Build Coastguard Worker -- Create BPF output reader 1424*387f9dfdSAndroid Build Coastguard Worker local pe = S.t.perf_event_attr1() 1425*387f9dfdSAndroid Build Coastguard Worker pe[0].type = 'software' 1426*387f9dfdSAndroid Build Coastguard Worker pe[0].config = 'sw_bpf_output' 1427*387f9dfdSAndroid Build Coastguard Worker pe[0].sample_type = 'raw' 1428*387f9dfdSAndroid Build Coastguard Worker pe[0].sample_period = 1 1429*387f9dfdSAndroid Build Coastguard Worker pe[0].wakeup_events = 1 1430*387f9dfdSAndroid Build Coastguard Worker local reader, err = S.t.perf_reader(S.perf_event_open(pe, pid, cpu or -1)) 1431*387f9dfdSAndroid Build Coastguard Worker if not reader then return nil, tostring(err) end 1432*387f9dfdSAndroid Build Coastguard Worker -- Register event reader fd in BPF map 1433*387f9dfdSAndroid Build Coastguard Worker assert(cpu < pmap.max_entries, string.format('BPF map smaller than read CPU %d', cpu)) 1434*387f9dfdSAndroid Build Coastguard Worker pmap[cpu] = reader.fd 1435*387f9dfdSAndroid Build Coastguard Worker -- Open memory map and start reading 1436*387f9dfdSAndroid Build Coastguard Worker local ok, err = reader:start() 1437*387f9dfdSAndroid Build Coastguard Worker assert(ok, tostring(err)) 1438*387f9dfdSAndroid Build Coastguard Worker ok, err = reader:mmap() 1439*387f9dfdSAndroid Build Coastguard Worker assert(ok, tostring(err)) 1440*387f9dfdSAndroid Build Coastguard Worker return cdef.event_reader(reader, event_type) 1441*387f9dfdSAndroid Build Coastguard Worker end 1442*387f9dfdSAndroid Build Coastguard Worker -- Signalise this is a map type 1443*387f9dfdSAndroid Build Coastguard Worker end 1444*387f9dfdSAndroid Build Coastguard Worker return k == '__map' 1445*387f9dfdSAndroid Build Coastguard Worker end 1446*387f9dfdSAndroid Build Coastguard Worker -- Retrieve key 1447*387f9dfdSAndroid Build Coastguard Worker map.key[0] = k 1448*387f9dfdSAndroid Build Coastguard Worker local ok, err = S.bpf_map_op(S.c.BPF_CMD.MAP_LOOKUP_ELEM, map.fd, map.key, map.val) 1449*387f9dfdSAndroid Build Coastguard Worker if not ok then return nil, err end 1450*387f9dfdSAndroid Build Coastguard Worker return ffi.new(map.val_type, map.val[0]) 1451*387f9dfdSAndroid Build Coastguard Worker end, 1452*387f9dfdSAndroid Build Coastguard Worker __newindex = function (map, k, v) 1453*387f9dfdSAndroid Build Coastguard Worker map.key[0] = k 1454*387f9dfdSAndroid Build Coastguard Worker if v == nil then 1455*387f9dfdSAndroid Build Coastguard Worker return S.bpf_map_op(map.fd, S.c.BPF_CMD.MAP_DELETE_ELEM, map.key, nil) 1456*387f9dfdSAndroid Build Coastguard Worker end 1457*387f9dfdSAndroid Build Coastguard Worker map.val[0] = v 1458*387f9dfdSAndroid Build Coastguard Worker return S.bpf_map_op(S.c.BPF_CMD.MAP_UPDATE_ELEM, map.fd, map.key, map.val) 1459*387f9dfdSAndroid Build Coastguard Worker end, 1460*387f9dfdSAndroid Build Coastguard Worker} 1461*387f9dfdSAndroid Build Coastguard Worker 1462*387f9dfdSAndroid Build Coastguard Worker-- Linux tracing interface 1463*387f9dfdSAndroid Build Coastguard Workerlocal function trace_check_enabled(path) 1464*387f9dfdSAndroid Build Coastguard Worker path = path or '/sys/kernel/debug/tracing' 1465*387f9dfdSAndroid Build Coastguard Worker if S.statfs(path) then return true end 1466*387f9dfdSAndroid Build Coastguard Worker return nil, 'debugfs not accessible: "mount -t debugfs nodev /sys/kernel/debug"? missing sudo?' 1467*387f9dfdSAndroid Build Coastguard Workerend 1468*387f9dfdSAndroid Build Coastguard Worker 1469*387f9dfdSAndroid Build Coastguard Worker-- Tracepoint interface 1470*387f9dfdSAndroid Build Coastguard Workerlocal tracepoint_mt = { 1471*387f9dfdSAndroid Build Coastguard Worker __index = { 1472*387f9dfdSAndroid Build Coastguard Worker bpf = function (t, prog) 1473*387f9dfdSAndroid Build Coastguard Worker if type(prog) ~= 'table' then 1474*387f9dfdSAndroid Build Coastguard Worker -- Create protocol parser with source probe 1475*387f9dfdSAndroid Build Coastguard Worker prog = compile(prog, {proto.type(t.type, {source='ptr_to_probe'})}) 1476*387f9dfdSAndroid Build Coastguard Worker end 1477*387f9dfdSAndroid Build Coastguard Worker -- Load the BPF program 1478*387f9dfdSAndroid Build Coastguard Worker local prog_fd, err, log = S.bpf_prog_load(S.c.BPF_PROG.TRACEPOINT, prog.insn, prog.pc) 1479*387f9dfdSAndroid Build Coastguard Worker assert(prog_fd, tostring(err)..': '..tostring(log)) 1480*387f9dfdSAndroid Build Coastguard Worker -- Open tracepoint and attach 1481*387f9dfdSAndroid Build Coastguard Worker t.reader:setbpf(prog_fd:getfd()) 1482*387f9dfdSAndroid Build Coastguard Worker table.insert(t.progs, prog_fd) 1483*387f9dfdSAndroid Build Coastguard Worker return prog_fd 1484*387f9dfdSAndroid Build Coastguard Worker end, 1485*387f9dfdSAndroid Build Coastguard Worker } 1486*387f9dfdSAndroid Build Coastguard Worker} 1487*387f9dfdSAndroid Build Coastguard Worker-- Open tracepoint 1488*387f9dfdSAndroid Build Coastguard Workerlocal function tracepoint_open(path, pid, cpu, group_fd) 1489*387f9dfdSAndroid Build Coastguard Worker -- Open tracepoint and compile tracepoint type 1490*387f9dfdSAndroid Build Coastguard Worker local tp = assert(S.perf_tracepoint('/sys/kernel/debug/tracing/events/'..path)) 1491*387f9dfdSAndroid Build Coastguard Worker local tp_type = assert(cdef.tracepoint_type(path)) 1492*387f9dfdSAndroid Build Coastguard Worker -- Open tracepoint reader and create interface 1493*387f9dfdSAndroid Build Coastguard Worker local reader = assert(S.perf_attach_tracepoint(tp, pid, cpu, group_fd)) 1494*387f9dfdSAndroid Build Coastguard Worker return setmetatable({tp=tp,type=tp_type,reader=reader,progs={}}, tracepoint_mt) 1495*387f9dfdSAndroid Build Coastguard Workerend 1496*387f9dfdSAndroid Build Coastguard Worker 1497*387f9dfdSAndroid Build Coastguard Workerlocal function trace_bpf(ptype, pname, pdef, retprobe, prog, pid, cpu, group_fd) 1498*387f9dfdSAndroid Build Coastguard Worker -- Load BPF program 1499*387f9dfdSAndroid Build Coastguard Worker if type(prog) ~= 'table' then 1500*387f9dfdSAndroid Build Coastguard Worker prog = compile(prog, {proto.pt_regs}) 1501*387f9dfdSAndroid Build Coastguard Worker end 1502*387f9dfdSAndroid Build Coastguard Worker local prog_fd, err, log = S.bpf_prog_load(S.c.BPF_PROG.KPROBE, prog.insn, prog.pc) 1503*387f9dfdSAndroid Build Coastguard Worker assert(prog_fd, tostring(err)..': '..tostring(log)) 1504*387f9dfdSAndroid Build Coastguard Worker -- Open tracepoint and attach 1505*387f9dfdSAndroid Build Coastguard Worker local tp, err = S.perf_probe(ptype, pname, pdef, retprobe) 1506*387f9dfdSAndroid Build Coastguard Worker if not tp then 1507*387f9dfdSAndroid Build Coastguard Worker prog_fd:close() 1508*387f9dfdSAndroid Build Coastguard Worker return nil, tostring(err) 1509*387f9dfdSAndroid Build Coastguard Worker end 1510*387f9dfdSAndroid Build Coastguard Worker local reader, err = S.perf_attach_tracepoint(tp, pid, cpu, group_fd, {sample_type='raw, callchain'}) 1511*387f9dfdSAndroid Build Coastguard Worker if not reader then 1512*387f9dfdSAndroid Build Coastguard Worker prog_fd:close() 1513*387f9dfdSAndroid Build Coastguard Worker S.perf_probe(ptype, pname, false) 1514*387f9dfdSAndroid Build Coastguard Worker return nil, tostring(err) 1515*387f9dfdSAndroid Build Coastguard Worker end 1516*387f9dfdSAndroid Build Coastguard Worker local ok, err = reader:setbpf(prog_fd:getfd()) 1517*387f9dfdSAndroid Build Coastguard Worker if not ok then 1518*387f9dfdSAndroid Build Coastguard Worker prog_fd:close() 1519*387f9dfdSAndroid Build Coastguard Worker reader:close() 1520*387f9dfdSAndroid Build Coastguard Worker S.perf_probe(ptype, pname, false) 1521*387f9dfdSAndroid Build Coastguard Worker return nil, tostring(err)..' (kernel version should be at least 4.1)' 1522*387f9dfdSAndroid Build Coastguard Worker end 1523*387f9dfdSAndroid Build Coastguard Worker -- Create GC closure for reader to close BPF program 1524*387f9dfdSAndroid Build Coastguard Worker -- and detach probe in correct order 1525*387f9dfdSAndroid Build Coastguard Worker ffi.gc(reader, function () 1526*387f9dfdSAndroid Build Coastguard Worker prog_fd:close() 1527*387f9dfdSAndroid Build Coastguard Worker reader:close() 1528*387f9dfdSAndroid Build Coastguard Worker S.perf_probe(ptype, pname, false) 1529*387f9dfdSAndroid Build Coastguard Worker end) 1530*387f9dfdSAndroid Build Coastguard Worker return {reader=reader, prog=prog_fd, probe=pname, probe_type=ptype} 1531*387f9dfdSAndroid Build Coastguard Workerend 1532*387f9dfdSAndroid Build Coastguard Worker 1533*387f9dfdSAndroid Build Coastguard Worker-- Module interface 1534*387f9dfdSAndroid Build Coastguard Workerreturn setmetatable({ 1535*387f9dfdSAndroid Build Coastguard Worker new = create_emitter, 1536*387f9dfdSAndroid Build Coastguard Worker dump = dump, 1537*387f9dfdSAndroid Build Coastguard Worker dump_string = dump_string, 1538*387f9dfdSAndroid Build Coastguard Worker maps = {}, 1539*387f9dfdSAndroid Build Coastguard Worker map = function (type, max_entries, key_ctype, val_ctype) 1540*387f9dfdSAndroid Build Coastguard Worker if not key_ctype then key_ctype = ffi.typeof('uint32_t') end 1541*387f9dfdSAndroid Build Coastguard Worker if not val_ctype then val_ctype = ffi.typeof('uint32_t') end 1542*387f9dfdSAndroid Build Coastguard Worker if not max_entries then max_entries = 4096 end 1543*387f9dfdSAndroid Build Coastguard Worker -- Special case for BPF_MAP_STACK_TRACE 1544*387f9dfdSAndroid Build Coastguard Worker if S.c.BPF_MAP[type] == S.c.BPF_MAP.STACK_TRACE then 1545*387f9dfdSAndroid Build Coastguard Worker key_ctype = ffi.typeof('int32_t') 1546*387f9dfdSAndroid Build Coastguard Worker val_ctype = ffi.typeof('struct bpf_stacktrace') 1547*387f9dfdSAndroid Build Coastguard Worker end 1548*387f9dfdSAndroid Build Coastguard Worker local fd, err = S.bpf_map_create(S.c.BPF_MAP[type], ffi.sizeof(key_ctype), ffi.sizeof(val_ctype), max_entries) 1549*387f9dfdSAndroid Build Coastguard Worker if not fd then return nil, tostring(err) end 1550*387f9dfdSAndroid Build Coastguard Worker local map = setmetatable({ 1551*387f9dfdSAndroid Build Coastguard Worker max_entries = max_entries, 1552*387f9dfdSAndroid Build Coastguard Worker key = ffi.new(ffi.typeof('$ [1]', key_ctype)), 1553*387f9dfdSAndroid Build Coastguard Worker val = ffi.new(ffi.typeof('$ [1]', val_ctype)), 1554*387f9dfdSAndroid Build Coastguard Worker map_type = S.c.BPF_MAP[type], 1555*387f9dfdSAndroid Build Coastguard Worker key_type = key_ctype, 1556*387f9dfdSAndroid Build Coastguard Worker val_type = val_ctype, 1557*387f9dfdSAndroid Build Coastguard Worker fd = fd:nogc():getfd(), 1558*387f9dfdSAndroid Build Coastguard Worker }, bpf_map_mt) 1559*387f9dfdSAndroid Build Coastguard Worker return map 1560*387f9dfdSAndroid Build Coastguard Worker end, 1561*387f9dfdSAndroid Build Coastguard Worker socket = function (sock, prog) 1562*387f9dfdSAndroid Build Coastguard Worker -- Expect socket type, if sock is string then assume it's 1563*387f9dfdSAndroid Build Coastguard Worker -- an interface name (e.g. 'lo'), if it's a number then typecast it as a socket 1564*387f9dfdSAndroid Build Coastguard Worker local ok, err 1565*387f9dfdSAndroid Build Coastguard Worker if type(sock) == 'string' then 1566*387f9dfdSAndroid Build Coastguard Worker local iface = assert(S.nl.getlink())[sock] 1567*387f9dfdSAndroid Build Coastguard Worker assert(iface, sock..' is not interface name') 1568*387f9dfdSAndroid Build Coastguard Worker sock, err = S.socket('packet', 'raw') 1569*387f9dfdSAndroid Build Coastguard Worker assert(sock, tostring(err)) 1570*387f9dfdSAndroid Build Coastguard Worker ok, err = sock:bind(S.t.sockaddr_ll({protocol='all', ifindex=iface.index})) 1571*387f9dfdSAndroid Build Coastguard Worker assert(ok, tostring(err)) 1572*387f9dfdSAndroid Build Coastguard Worker elseif type(sock) == 'number' then 1573*387f9dfdSAndroid Build Coastguard Worker sock = S.t.fd(sock):nogc() 1574*387f9dfdSAndroid Build Coastguard Worker elseif ffi.istype(S.t.fd, sock) then -- luacheck: ignore 1575*387f9dfdSAndroid Build Coastguard Worker -- No cast required 1576*387f9dfdSAndroid Build Coastguard Worker else 1577*387f9dfdSAndroid Build Coastguard Worker return nil, 'socket must either be an fd number, an interface name, or an ljsyscall socket' 1578*387f9dfdSAndroid Build Coastguard Worker end 1579*387f9dfdSAndroid Build Coastguard Worker -- Load program and attach it to socket 1580*387f9dfdSAndroid Build Coastguard Worker if type(prog) ~= 'table' then 1581*387f9dfdSAndroid Build Coastguard Worker prog = compile(prog, {proto.skb}) 1582*387f9dfdSAndroid Build Coastguard Worker end 1583*387f9dfdSAndroid Build Coastguard Worker local prog_fd, err, log = S.bpf_prog_load(S.c.BPF_PROG.SOCKET_FILTER, prog.insn, prog.pc) 1584*387f9dfdSAndroid Build Coastguard Worker assert(prog_fd, tostring(err)..': '..tostring(log)) 1585*387f9dfdSAndroid Build Coastguard Worker assert(sock:setsockopt('socket', 'attach_bpf', prog_fd:getfd())) 1586*387f9dfdSAndroid Build Coastguard Worker return prog_fd, err 1587*387f9dfdSAndroid Build Coastguard Worker end, 1588*387f9dfdSAndroid Build Coastguard Worker tracepoint = function(tp, prog, pid, cpu, group_fd) 1589*387f9dfdSAndroid Build Coastguard Worker assert(trace_check_enabled()) 1590*387f9dfdSAndroid Build Coastguard Worker -- Return tracepoint instance if no program specified 1591*387f9dfdSAndroid Build Coastguard Worker -- this allows free specialisation of arg0 to tracepoint type 1592*387f9dfdSAndroid Build Coastguard Worker local probe = tracepoint_open(tp, pid, cpu, group_fd) 1593*387f9dfdSAndroid Build Coastguard Worker -- Load the BPF program 1594*387f9dfdSAndroid Build Coastguard Worker if prog then 1595*387f9dfdSAndroid Build Coastguard Worker probe:bpf(prog) 1596*387f9dfdSAndroid Build Coastguard Worker end 1597*387f9dfdSAndroid Build Coastguard Worker return probe 1598*387f9dfdSAndroid Build Coastguard Worker end, 1599*387f9dfdSAndroid Build Coastguard Worker kprobe = function(tp, prog, retprobe, pid, cpu, group_fd) 1600*387f9dfdSAndroid Build Coastguard Worker assert(trace_check_enabled()) 1601*387f9dfdSAndroid Build Coastguard Worker -- Open tracepoint and attach 1602*387f9dfdSAndroid Build Coastguard Worker local pname, pdef = tp:match('([^:]+):(.+)') 1603*387f9dfdSAndroid Build Coastguard Worker return trace_bpf('kprobe', pname, pdef, retprobe, prog, pid, cpu, group_fd) 1604*387f9dfdSAndroid Build Coastguard Worker end, 1605*387f9dfdSAndroid Build Coastguard Worker uprobe = function(tp, prog, retprobe, pid, cpu, group_fd) 1606*387f9dfdSAndroid Build Coastguard Worker assert(trace_check_enabled()) 1607*387f9dfdSAndroid Build Coastguard Worker -- Translate symbol to address 1608*387f9dfdSAndroid Build Coastguard Worker local obj, sym_want = tp:match('([^:]+):(.+)') 1609*387f9dfdSAndroid Build Coastguard Worker if not S.statfs(obj) then return nil, S.t.error(S.c.E.NOENT) end 1610*387f9dfdSAndroid Build Coastguard Worker -- Resolve Elf object (no support for anything else) 1611*387f9dfdSAndroid Build Coastguard Worker local elf = require('bpf.elf').open(obj) 1612*387f9dfdSAndroid Build Coastguard Worker local sym = elf:resolve(sym_want) 1613*387f9dfdSAndroid Build Coastguard Worker if not sym then return nil, 'no such symbol' end 1614*387f9dfdSAndroid Build Coastguard Worker sym = sym.st_value - elf:loadaddr() 1615*387f9dfdSAndroid Build Coastguard Worker local sym_addr = string.format('%x%04x', tonumber(bit.rshift(sym, 32)), 1616*387f9dfdSAndroid Build Coastguard Worker tonumber(ffi.cast('uint32_t', sym))) 1617*387f9dfdSAndroid Build Coastguard Worker -- Convert it to expected uprobe format 1618*387f9dfdSAndroid Build Coastguard Worker local pname = string.format('%s_%s', obj:gsub('.*/', ''), sym_addr) 1619*387f9dfdSAndroid Build Coastguard Worker local pdef = obj..':0x'..sym_addr 1620*387f9dfdSAndroid Build Coastguard Worker return trace_bpf('uprobe', pname, pdef, retprobe, prog, pid, cpu, group_fd) 1621*387f9dfdSAndroid Build Coastguard Worker end, 1622*387f9dfdSAndroid Build Coastguard Worker tracelog = function(path) 1623*387f9dfdSAndroid Build Coastguard Worker assert(trace_check_enabled()) 1624*387f9dfdSAndroid Build Coastguard Worker path = path or '/sys/kernel/debug/tracing/trace_pipe' 1625*387f9dfdSAndroid Build Coastguard Worker return io.open(path, 'r') 1626*387f9dfdSAndroid Build Coastguard Worker end, 1627*387f9dfdSAndroid Build Coastguard Worker ntoh = builtins.ntoh, hton = builtins.hton, 1628*387f9dfdSAndroid Build Coastguard Worker}, { 1629*387f9dfdSAndroid Build Coastguard Worker __call = function (_, prog) return compile(prog) end, 1630*387f9dfdSAndroid Build Coastguard Worker}) 1631