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 Workerlocal ffi = require('ffi') 17*387f9dfdSAndroid Build Coastguard Workerlocal bit = require('bit') 18*387f9dfdSAndroid Build Coastguard Workerlocal cdef = require('bpf.cdef') 19*387f9dfdSAndroid Build Coastguard Worker 20*387f9dfdSAndroid Build Coastguard Workerlocal BPF, HELPER = ffi.typeof('struct bpf'), ffi.typeof('struct bpf_func_id') 21*387f9dfdSAndroid Build Coastguard Workerlocal const_width = { 22*387f9dfdSAndroid Build Coastguard Worker [1] = BPF.B, [2] = BPF.H, [4] = BPF.W, [8] = BPF.DW, 23*387f9dfdSAndroid Build Coastguard Worker} 24*387f9dfdSAndroid Build Coastguard Workerlocal const_width_type = { 25*387f9dfdSAndroid Build Coastguard Worker [1] = ffi.typeof('uint8_t'), [2] = ffi.typeof('uint16_t'), [4] = ffi.typeof('uint32_t'), [8] = ffi.typeof('uint64_t'), 26*387f9dfdSAndroid Build Coastguard Worker} 27*387f9dfdSAndroid Build Coastguard Worker 28*387f9dfdSAndroid Build Coastguard Worker-- Built-ins that will be translated into BPF instructions 29*387f9dfdSAndroid Build Coastguard Worker-- i.e. bit.bor(0xf0, 0x0f) becomes {'alu64, or, k', reg(0xf0), reg(0x0f), 0, 0} 30*387f9dfdSAndroid Build Coastguard Workerlocal builtins = { 31*387f9dfdSAndroid Build Coastguard Worker [bit.lshift] = 'LSH', 32*387f9dfdSAndroid Build Coastguard Worker [bit.rshift] = 'RSH', 33*387f9dfdSAndroid Build Coastguard Worker [bit.band] = 'AND', 34*387f9dfdSAndroid Build Coastguard Worker [bit.bnot] = 'NEG', 35*387f9dfdSAndroid Build Coastguard Worker [bit.bor] = 'OR', 36*387f9dfdSAndroid Build Coastguard Worker [bit.bxor] = 'XOR', 37*387f9dfdSAndroid Build Coastguard Worker [bit.arshift] = 'ARSH', 38*387f9dfdSAndroid Build Coastguard Worker -- Extensions and intrinsics 39*387f9dfdSAndroid Build Coastguard Worker} 40*387f9dfdSAndroid Build Coastguard Worker 41*387f9dfdSAndroid Build Coastguard Workerlocal function width_type(w) 42*387f9dfdSAndroid Build Coastguard Worker -- Note: ffi.typeof doesn't accept '?' as template 43*387f9dfdSAndroid Build Coastguard Worker return const_width_type[w] or ffi.typeof(string.format('uint8_t [%d]', w)) 44*387f9dfdSAndroid Build Coastguard Workerend 45*387f9dfdSAndroid Build Coastguard Workerbuiltins.width_type = width_type 46*387f9dfdSAndroid Build Coastguard Worker 47*387f9dfdSAndroid Build Coastguard Worker-- Return struct member size/type (requires LuaJIT 2.1+) 48*387f9dfdSAndroid Build Coastguard Worker-- I am ashamed that there's no easier way around it. 49*387f9dfdSAndroid Build Coastguard Workerlocal function sizeofattr(ct, name) 50*387f9dfdSAndroid Build Coastguard Worker if not ffi.typeinfo then error('LuaJIT 2.1+ is required for ffi.typeinfo') end 51*387f9dfdSAndroid Build Coastguard Worker local cinfo = ffi.typeinfo(ct) 52*387f9dfdSAndroid Build Coastguard Worker while true do 53*387f9dfdSAndroid Build Coastguard Worker cinfo = ffi.typeinfo(cinfo.sib) 54*387f9dfdSAndroid Build Coastguard Worker if not cinfo then return end 55*387f9dfdSAndroid Build Coastguard Worker if cinfo.name == name then break end 56*387f9dfdSAndroid Build Coastguard Worker end 57*387f9dfdSAndroid Build Coastguard Worker local size = math.max(1, ffi.typeinfo(cinfo.sib or ct).size - cinfo.size) 58*387f9dfdSAndroid Build Coastguard Worker -- Guess type name 59*387f9dfdSAndroid Build Coastguard Worker return size, builtins.width_type(size) 60*387f9dfdSAndroid Build Coastguard Workerend 61*387f9dfdSAndroid Build Coastguard Workerbuiltins.sizeofattr = sizeofattr 62*387f9dfdSAndroid Build Coastguard Worker 63*387f9dfdSAndroid Build Coastguard Worker-- Byte-order conversions for little endian 64*387f9dfdSAndroid Build Coastguard Workerlocal function ntoh(x, w) 65*387f9dfdSAndroid Build Coastguard Worker if w then x = ffi.cast(const_width_type[w/8], x) end 66*387f9dfdSAndroid Build Coastguard Worker return bit.bswap(x) 67*387f9dfdSAndroid Build Coastguard Workerend 68*387f9dfdSAndroid Build Coastguard Workerlocal function hton(x, w) return ntoh(x, w) end 69*387f9dfdSAndroid Build Coastguard Workerbuiltins.ntoh = ntoh 70*387f9dfdSAndroid Build Coastguard Workerbuiltins.hton = hton 71*387f9dfdSAndroid Build Coastguard Workerbuiltins[ntoh] = function (e, dst, a, w) 72*387f9dfdSAndroid Build Coastguard Worker -- This is trickery, but TO_LE means cpu_to_le(), 73*387f9dfdSAndroid Build Coastguard Worker -- and we want exactly the opposite as network is always 'be' 74*387f9dfdSAndroid Build Coastguard Worker w = w or ffi.sizeof(e.V[a].type)*8 75*387f9dfdSAndroid Build Coastguard Worker if w == 8 then return end -- NOOP 76*387f9dfdSAndroid Build Coastguard Worker assert(w <= 64, 'NYI: hton(a[, width]) - operand larger than register width') 77*387f9dfdSAndroid Build Coastguard Worker -- Allocate registers and execute 78*387f9dfdSAndroid Build Coastguard Worker e.vcopy(dst, a) 79*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU + BPF.END + BPF.TO_BE, e.vreg(dst), 0, 0, w) 80*387f9dfdSAndroid Build Coastguard Workerend 81*387f9dfdSAndroid Build Coastguard Workerbuiltins[hton] = function (e, dst, a, w) 82*387f9dfdSAndroid Build Coastguard Worker w = w or ffi.sizeof(e.V[a].type)*8 83*387f9dfdSAndroid Build Coastguard Worker if w == 8 then return end -- NOOP 84*387f9dfdSAndroid Build Coastguard Worker assert(w <= 64, 'NYI: hton(a[, width]) - operand larger than register width') 85*387f9dfdSAndroid Build Coastguard Worker -- Allocate registers and execute 86*387f9dfdSAndroid Build Coastguard Worker e.vcopy(dst, a) 87*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU + BPF.END + BPF.TO_LE, e.vreg(dst), 0, 0, w) 88*387f9dfdSAndroid Build Coastguard Workerend 89*387f9dfdSAndroid Build Coastguard Worker-- Byte-order conversions for big endian are no-ops 90*387f9dfdSAndroid Build Coastguard Workerif ffi.abi('be') then 91*387f9dfdSAndroid Build Coastguard Worker ntoh = function (x, w) 92*387f9dfdSAndroid Build Coastguard Worker return w and ffi.cast(const_width_type[w/8], x) or x 93*387f9dfdSAndroid Build Coastguard Worker end 94*387f9dfdSAndroid Build Coastguard Worker hton = ntoh 95*387f9dfdSAndroid Build Coastguard Worker builtins[ntoh] = function(_, _, _) return end 96*387f9dfdSAndroid Build Coastguard Worker builtins[hton] = function(_, _, _) return end 97*387f9dfdSAndroid Build Coastguard Workerend 98*387f9dfdSAndroid Build Coastguard Worker-- Other built-ins 99*387f9dfdSAndroid Build Coastguard Workerlocal function xadd() error('NYI') end 100*387f9dfdSAndroid Build Coastguard Workerbuiltins.xadd = xadd 101*387f9dfdSAndroid Build Coastguard Workerbuiltins[xadd] = function (e, ret, a, b, off) 102*387f9dfdSAndroid Build Coastguard Worker local vinfo = e.V[a].const 103*387f9dfdSAndroid Build Coastguard Worker assert(vinfo and vinfo.__dissector, 'xadd(a, b[, offset]) called on non-pointer') 104*387f9dfdSAndroid Build Coastguard Worker local w = ffi.sizeof(vinfo.__dissector) 105*387f9dfdSAndroid Build Coastguard Worker -- Calculate structure attribute offsets 106*387f9dfdSAndroid Build Coastguard Worker if e.V[off] and type(e.V[off].const) == 'string' then 107*387f9dfdSAndroid Build Coastguard Worker local ct, field = vinfo.__dissector, e.V[off].const 108*387f9dfdSAndroid Build Coastguard Worker off = ffi.offsetof(ct, field) 109*387f9dfdSAndroid Build Coastguard Worker assert(off, 'xadd(a, b, offset) - offset is not valid in given structure') 110*387f9dfdSAndroid Build Coastguard Worker w = sizeofattr(ct, field) 111*387f9dfdSAndroid Build Coastguard Worker end 112*387f9dfdSAndroid Build Coastguard Worker assert(w == 4 or w == 8, 'NYI: xadd() - 1 and 2 byte atomic increments are not supported') 113*387f9dfdSAndroid Build Coastguard Worker -- Allocate registers and execute 114*387f9dfdSAndroid Build Coastguard Worker local src_reg = e.vreg(b) 115*387f9dfdSAndroid Build Coastguard Worker local dst_reg = e.vreg(a) 116*387f9dfdSAndroid Build Coastguard Worker -- Set variable for return value and call 117*387f9dfdSAndroid Build Coastguard Worker e.vset(ret) 118*387f9dfdSAndroid Build Coastguard Worker e.vreg(ret, 0, true, ffi.typeof('int32_t')) 119*387f9dfdSAndroid Build Coastguard Worker -- Optimize the NULL check away if provably not NULL 120*387f9dfdSAndroid Build Coastguard Worker if not e.V[a].source or e.V[a].source:find('_or_null', 1, true) then 121*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.JEQ + BPF.K, dst_reg, 0, 1, 0) -- if (dst != NULL) 122*387f9dfdSAndroid Build Coastguard Worker end 123*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.XADD + BPF.STX + const_width[w], dst_reg, src_reg, off or 0, 0) 124*387f9dfdSAndroid Build Coastguard Workerend 125*387f9dfdSAndroid Build Coastguard Worker 126*387f9dfdSAndroid Build Coastguard Workerlocal function probe_read() error('NYI') end 127*387f9dfdSAndroid Build Coastguard Workerbuiltins.probe_read = probe_read 128*387f9dfdSAndroid Build Coastguard Workerbuiltins[probe_read] = function (e, ret, dst, src, vtype, ofs) 129*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 1) 130*387f9dfdSAndroid Build Coastguard Worker -- Load stack pointer to dst, since only load to stack memory is supported 131*387f9dfdSAndroid Build Coastguard Worker -- we have to use allocated stack memory or create a new allocation and convert 132*387f9dfdSAndroid Build Coastguard Worker -- to pointer type 133*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) 134*387f9dfdSAndroid Build Coastguard Worker if not e.V[dst].const or not e.V[dst].const.__base > 0 then 135*387f9dfdSAndroid Build Coastguard Worker builtins[ffi.new](e, dst, vtype) -- Allocate stack memory 136*387f9dfdSAndroid Build Coastguard Worker end 137*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].const.__base) 138*387f9dfdSAndroid Build Coastguard Worker -- Set stack memory maximum size bound 139*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 2) 140*387f9dfdSAndroid Build Coastguard Worker if not vtype then 141*387f9dfdSAndroid Build Coastguard Worker vtype = cdef.typename(e.V[dst].type) 142*387f9dfdSAndroid Build Coastguard Worker -- Dereference pointer type to pointed type for size calculation 143*387f9dfdSAndroid Build Coastguard Worker if vtype:sub(-1) == '*' then vtype = vtype:sub(0, -2) end 144*387f9dfdSAndroid Build Coastguard Worker end 145*387f9dfdSAndroid Build Coastguard Worker local w = ffi.sizeof(vtype) 146*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, w) 147*387f9dfdSAndroid Build Coastguard Worker -- Set source pointer 148*387f9dfdSAndroid Build Coastguard Worker if e.V[src].reg then 149*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 3) -- Copy from original register 150*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, e.V[src].reg, 0, 0) 151*387f9dfdSAndroid Build Coastguard Worker else 152*387f9dfdSAndroid Build Coastguard Worker e.vreg(src, 3) 153*387f9dfdSAndroid Build Coastguard Worker e.reg_spill(src) -- Spill to avoid overwriting 154*387f9dfdSAndroid Build Coastguard Worker end 155*387f9dfdSAndroid Build Coastguard Worker if ofs and ofs > 0 then 156*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, ofs) 157*387f9dfdSAndroid Build Coastguard Worker end 158*387f9dfdSAndroid Build Coastguard Worker -- Call probe read helper 159*387f9dfdSAndroid Build Coastguard Worker ret = ret or e.tmpvar 160*387f9dfdSAndroid Build Coastguard Worker e.vset(ret) 161*387f9dfdSAndroid Build Coastguard Worker e.vreg(ret, 0, true, ffi.typeof('int32_t')) 162*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.probe_read) 163*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil -- Free temporary registers 164*387f9dfdSAndroid Build Coastguard Workerend 165*387f9dfdSAndroid Build Coastguard Worker 166*387f9dfdSAndroid Build Coastguard Workerbuiltins[ffi.cast] = function (e, dst, ct, x) 167*387f9dfdSAndroid Build Coastguard Worker assert(e.V[ct].const, 'ffi.cast(ctype, x) called with bad ctype') 168*387f9dfdSAndroid Build Coastguard Worker e.vcopy(dst, x) 169*387f9dfdSAndroid Build Coastguard Worker if e.V[x].const and type(e.V[x].const) == 'table' then 170*387f9dfdSAndroid Build Coastguard Worker e.V[dst].const.__dissector = ffi.typeof(e.V[ct].const) 171*387f9dfdSAndroid Build Coastguard Worker end 172*387f9dfdSAndroid Build Coastguard Worker e.V[dst].type = ffi.typeof(e.V[ct].const) 173*387f9dfdSAndroid Build Coastguard Worker -- Specific types also encode source of the data 174*387f9dfdSAndroid Build Coastguard Worker -- This is because BPF has different helpers for reading 175*387f9dfdSAndroid Build Coastguard Worker -- different data sources, so variables must track origins. 176*387f9dfdSAndroid Build Coastguard Worker -- struct pt_regs - source of the data is probe 177*387f9dfdSAndroid Build Coastguard Worker -- struct skb - source of the data is socket buffer 178*387f9dfdSAndroid Build Coastguard Worker -- struct X - source of the data is probe/tracepoint 179*387f9dfdSAndroid Build Coastguard Worker if ffi.typeof(e.V[ct].const) == ffi.typeof('struct pt_regs') then 180*387f9dfdSAndroid Build Coastguard Worker e.V[dst].source = 'ptr_to_probe' 181*387f9dfdSAndroid Build Coastguard Worker end 182*387f9dfdSAndroid Build Coastguard Workerend 183*387f9dfdSAndroid Build Coastguard Worker 184*387f9dfdSAndroid Build Coastguard Workerbuiltins[ffi.new] = function (e, dst, ct, x) 185*387f9dfdSAndroid Build Coastguard Worker if type(ct) == 'number' then 186*387f9dfdSAndroid Build Coastguard Worker ct = ffi.typeof(e.V[ct].const) -- Get ctype from variable 187*387f9dfdSAndroid Build Coastguard Worker end 188*387f9dfdSAndroid Build Coastguard Worker assert(not x, 'NYI: ffi.new(ctype, ...) - initializer is not supported') 189*387f9dfdSAndroid Build Coastguard Worker assert(not cdef.isptr(ct, true), 'NYI: ffi.new(ctype, ...) - ctype MUST NOT be a pointer') 190*387f9dfdSAndroid Build Coastguard Worker e.vset(dst, nil, ct) 191*387f9dfdSAndroid Build Coastguard Worker e.V[dst].source = 'ptr_to_stack' 192*387f9dfdSAndroid Build Coastguard Worker e.V[dst].const = {__base = e.valloc(ffi.sizeof(ct), true), __dissector = ct} 193*387f9dfdSAndroid Build Coastguard Worker -- Set array dissector if created an array 194*387f9dfdSAndroid Build Coastguard Worker -- e.g. if ct is 'char [2]', then dissector is 'char' 195*387f9dfdSAndroid Build Coastguard Worker local elem_type = tostring(ct):match('ctype<(.+)%s%[(%d+)%]>') 196*387f9dfdSAndroid Build Coastguard Worker if elem_type then 197*387f9dfdSAndroid Build Coastguard Worker e.V[dst].const.__dissector = ffi.typeof(elem_type) 198*387f9dfdSAndroid Build Coastguard Worker end 199*387f9dfdSAndroid Build Coastguard Workerend 200*387f9dfdSAndroid Build Coastguard Worker 201*387f9dfdSAndroid Build Coastguard Workerbuiltins[ffi.copy] = function (e, ret, dst, src) 202*387f9dfdSAndroid Build Coastguard Worker assert(cdef.isptr(e.V[dst].type), 'ffi.copy(dst, src) - dst MUST be a pointer type') 203*387f9dfdSAndroid Build Coastguard Worker assert(cdef.isptr(e.V[src].type), 'ffi.copy(dst, src) - src MUST be a pointer type') 204*387f9dfdSAndroid Build Coastguard Worker -- Specific types also encode source of the data 205*387f9dfdSAndroid Build Coastguard Worker -- struct pt_regs - source of the data is probe 206*387f9dfdSAndroid Build Coastguard Worker -- struct skb - source of the data is socket buffer 207*387f9dfdSAndroid Build Coastguard Worker if e.V[src].source and e.V[src].source:find('ptr_to_probe', 1, true) then 208*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 1) 209*387f9dfdSAndroid Build Coastguard Worker -- Load stack pointer to dst, since only load to stack memory is supported 210*387f9dfdSAndroid Build Coastguard Worker -- we have to either use spilled variable or allocated stack memory offset 211*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) 212*387f9dfdSAndroid Build Coastguard Worker if e.V[dst].spill then 213*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].spill) 214*387f9dfdSAndroid Build Coastguard Worker elseif e.V[dst].const.__base then 215*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].const.__base) 216*387f9dfdSAndroid Build Coastguard Worker else error('ffi.copy(dst, src) - can\'t get stack offset of dst') end 217*387f9dfdSAndroid Build Coastguard Worker -- Set stack memory maximum size bound 218*387f9dfdSAndroid Build Coastguard Worker local dst_tname = cdef.typename(e.V[dst].type) 219*387f9dfdSAndroid Build Coastguard Worker if dst_tname:sub(-1) == '*' then dst_tname = dst_tname:sub(0, -2) end 220*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 2) 221*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, ffi.sizeof(dst_tname)) 222*387f9dfdSAndroid Build Coastguard Worker -- Set source pointer 223*387f9dfdSAndroid Build Coastguard Worker if e.V[src].reg then 224*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 3) -- Copy from original register 225*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, e.V[src].reg, 0, 0) 226*387f9dfdSAndroid Build Coastguard Worker else 227*387f9dfdSAndroid Build Coastguard Worker e.vreg(src, 3) 228*387f9dfdSAndroid Build Coastguard Worker e.reg_spill(src) -- Spill to avoid overwriting 229*387f9dfdSAndroid Build Coastguard Worker end 230*387f9dfdSAndroid Build Coastguard Worker -- Call probe read helper 231*387f9dfdSAndroid Build Coastguard Worker e.vset(ret) 232*387f9dfdSAndroid Build Coastguard Worker e.vreg(ret, 0, true, ffi.typeof('int32_t')) 233*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.probe_read) 234*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil -- Free temporary registers 235*387f9dfdSAndroid Build Coastguard Worker elseif e.V[src].const and e.V[src].const.__map then 236*387f9dfdSAndroid Build Coastguard Worker error('NYI: ffi.copy(dst, src) - src is backed by BPF map') 237*387f9dfdSAndroid Build Coastguard Worker elseif e.V[src].const and e.V[src].const.__dissector then 238*387f9dfdSAndroid Build Coastguard Worker error('NYI: ffi.copy(dst, src) - src is backed by socket buffer') 239*387f9dfdSAndroid Build Coastguard Worker else 240*387f9dfdSAndroid Build Coastguard Worker -- TODO: identify cheap register move 241*387f9dfdSAndroid Build Coastguard Worker -- TODO: identify copy to/from stack 242*387f9dfdSAndroid Build Coastguard Worker error('NYI: ffi.copy(dst, src) - src is neither BPF map/socket buffer or probe') 243*387f9dfdSAndroid Build Coastguard Worker end 244*387f9dfdSAndroid Build Coastguard Workerend 245*387f9dfdSAndroid Build Coastguard Worker-- print(format, ...) builtin changes semantics from Lua print(...) 246*387f9dfdSAndroid Build Coastguard Worker-- the first parameter has to be format and only reduced set of conversion specificers 247*387f9dfdSAndroid Build Coastguard Worker-- is allowed: %d %u %x %ld %lu %lx %lld %llu %llx %p %s 248*387f9dfdSAndroid Build Coastguard Workerbuiltins[print] = function (e, ret, fmt, a1, a2, a3) 249*387f9dfdSAndroid Build Coastguard Worker -- Load format string and length 250*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.V[e.tmpvar], 1) 251*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.V[e.tmpvar+1], 1) 252*387f9dfdSAndroid Build Coastguard Worker if type(e.V[fmt].const) == 'string' then 253*387f9dfdSAndroid Build Coastguard Worker local src = e.V[fmt].const 254*387f9dfdSAndroid Build Coastguard Worker local len = #src + 1 255*387f9dfdSAndroid Build Coastguard Worker local dst = e.valloc(len, src) 256*387f9dfdSAndroid Build Coastguard Worker -- TODO: this is materialize step 257*387f9dfdSAndroid Build Coastguard Worker e.V[fmt].const = {__base=dst} 258*387f9dfdSAndroid Build Coastguard Worker e.V[fmt].type = ffi.typeof('char ['..len..']') 259*387f9dfdSAndroid Build Coastguard Worker elseif e.V[fmt].const.__base then -- luacheck: ignore 260*387f9dfdSAndroid Build Coastguard Worker -- NOP 261*387f9dfdSAndroid Build Coastguard Worker else error('NYI: print(fmt, ...) - format variable is not literal/stack memory') end 262*387f9dfdSAndroid Build Coastguard Worker -- Prepare helper call 263*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) 264*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[fmt].const.__base) 265*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, ffi.sizeof(e.V[fmt].type)) 266*387f9dfdSAndroid Build Coastguard Worker if a1 then 267*387f9dfdSAndroid Build Coastguard Worker local args = {a1, a2, a3} 268*387f9dfdSAndroid Build Coastguard Worker assert(#args <= 3, 'print(fmt, ...) - maximum of 3 arguments supported') 269*387f9dfdSAndroid Build Coastguard Worker for i, arg in ipairs(args) do 270*387f9dfdSAndroid Build Coastguard Worker e.vcopy(e.tmpvar, arg) -- Copy variable 271*387f9dfdSAndroid Build Coastguard Worker e.vreg(e.tmpvar, 3+i-1) -- Materialize it in arg register 272*387f9dfdSAndroid Build Coastguard Worker end 273*387f9dfdSAndroid Build Coastguard Worker end 274*387f9dfdSAndroid Build Coastguard Worker -- Call helper 275*387f9dfdSAndroid Build Coastguard Worker e.vset(ret) 276*387f9dfdSAndroid Build Coastguard Worker e.vreg(ret, 0, true, ffi.typeof('int32_t')) -- Return is integer 277*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.trace_printk) 278*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil -- Free temporary registers 279*387f9dfdSAndroid Build Coastguard Workerend 280*387f9dfdSAndroid Build Coastguard Worker 281*387f9dfdSAndroid Build Coastguard Worker-- Implements bpf_perf_event_output(ctx, map, flags, var, vlen) on perf event map 282*387f9dfdSAndroid Build Coastguard Workerlocal function perf_submit(e, dst, map_var, src) 283*387f9dfdSAndroid Build Coastguard Worker -- Set R2 = map fd (indirect load) 284*387f9dfdSAndroid Build Coastguard Worker local map = e.V[map_var].const 285*387f9dfdSAndroid Build Coastguard Worker e.vcopy(e.tmpvar, map_var) 286*387f9dfdSAndroid Build Coastguard Worker e.vreg(e.tmpvar, 2, true, ffi.typeof('uint64_t')) 287*387f9dfdSAndroid Build Coastguard Worker e.LD_IMM_X(2, BPF.PSEUDO_MAP_FD, map.fd, ffi.sizeof('uint64_t')) 288*387f9dfdSAndroid Build Coastguard Worker -- Set R1 = ctx 289*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 1) -- Spill anything in R1 (unnamed tmp variable) 290*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 6, 0, 0) -- CTX is always in R6, copy 291*387f9dfdSAndroid Build Coastguard Worker -- Set R3 = flags 292*387f9dfdSAndroid Build Coastguard Worker e.vset(e.tmpvar, nil, 0) -- BPF_F_CURRENT_CPU 293*387f9dfdSAndroid Build Coastguard Worker e.vreg(e.tmpvar, 3, false, ffi.typeof('uint64_t')) 294*387f9dfdSAndroid Build Coastguard Worker -- Set R4 = pointer to src on stack 295*387f9dfdSAndroid Build Coastguard Worker assert(e.V[src].const.__base, 'NYI: submit(map, var) - variable is not on stack') 296*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 4, 10, 0, 0) 297*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 4, 0, 0, -e.V[src].const.__base) 298*387f9dfdSAndroid Build Coastguard Worker -- Set R5 = src length 299*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 5, 0, 0, ffi.sizeof(e.V[src].type)) 300*387f9dfdSAndroid Build Coastguard Worker -- Set R0 = ret and call 301*387f9dfdSAndroid Build Coastguard Worker e.vset(dst) 302*387f9dfdSAndroid Build Coastguard Worker e.vreg(dst, 0, true, ffi.typeof('int32_t')) -- Return is integer 303*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.perf_event_output) 304*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil -- Free temporary registers 305*387f9dfdSAndroid Build Coastguard Workerend 306*387f9dfdSAndroid Build Coastguard Worker 307*387f9dfdSAndroid Build Coastguard Worker-- Implements bpf_skb_load_bytes(ctx, off, var, vlen) on skb->data 308*387f9dfdSAndroid Build Coastguard Workerlocal function load_bytes(e, dst, off, var) 309*387f9dfdSAndroid Build Coastguard Worker -- Set R2 = offset 310*387f9dfdSAndroid Build Coastguard Worker e.vset(e.tmpvar, nil, off) 311*387f9dfdSAndroid Build Coastguard Worker e.vreg(e.tmpvar, 2, false, ffi.typeof('uint64_t')) 312*387f9dfdSAndroid Build Coastguard Worker -- Set R1 = ctx 313*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 1) -- Spill anything in R1 (unnamed tmp variable) 314*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 6, 0, 0) -- CTX is always in R6, copy 315*387f9dfdSAndroid Build Coastguard Worker -- Set R3 = pointer to var on stack 316*387f9dfdSAndroid Build Coastguard Worker assert(e.V[var].const.__base, 'NYI: load_bytes(off, var, len) - variable is not on stack') 317*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 3, 10, 0, 0) 318*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 3, 0, 0, -e.V[var].const.__base) 319*387f9dfdSAndroid Build Coastguard Worker -- Set R4 = var length 320*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 4, 0, 0, ffi.sizeof(e.V[var].type)) 321*387f9dfdSAndroid Build Coastguard Worker -- Set R0 = ret and call 322*387f9dfdSAndroid Build Coastguard Worker e.vset(dst) 323*387f9dfdSAndroid Build Coastguard Worker e.vreg(dst, 0, true, ffi.typeof('int32_t')) -- Return is integer 324*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.skb_load_bytes) 325*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil -- Free temporary registers 326*387f9dfdSAndroid Build Coastguard Workerend 327*387f9dfdSAndroid Build Coastguard Worker 328*387f9dfdSAndroid Build Coastguard Worker-- Implements bpf_get_stack_id() 329*387f9dfdSAndroid Build Coastguard Workerlocal function stack_id(e, ret, map_var, key) 330*387f9dfdSAndroid Build Coastguard Worker -- Set R2 = map fd (indirect load) 331*387f9dfdSAndroid Build Coastguard Worker local map = e.V[map_var].const 332*387f9dfdSAndroid Build Coastguard Worker e.vcopy(e.tmpvar, map_var) 333*387f9dfdSAndroid Build Coastguard Worker e.vreg(e.tmpvar, 2, true, ffi.typeof('uint64_t')) 334*387f9dfdSAndroid Build Coastguard Worker e.LD_IMM_X(2, BPF.PSEUDO_MAP_FD, map.fd, ffi.sizeof('uint64_t')) 335*387f9dfdSAndroid Build Coastguard Worker -- Set R1 = ctx 336*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 1) -- Spill anything in R1 (unnamed tmp variable) 337*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 6, 0, 0) -- CTX is always in R6, copy 338*387f9dfdSAndroid Build Coastguard Worker -- Load flags in R2 (immediate value or key) 339*387f9dfdSAndroid Build Coastguard Worker local imm = e.V[key].const 340*387f9dfdSAndroid Build Coastguard Worker assert(tonumber(imm), 'NYI: stack_id(map, var), var must be constant number') 341*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 3) -- Spill anything in R2 (unnamed tmp variable) 342*387f9dfdSAndroid Build Coastguard Worker e.LD_IMM_X(3, 0, imm, 8) 343*387f9dfdSAndroid Build Coastguard Worker -- Return R0 as signed integer 344*387f9dfdSAndroid Build Coastguard Worker e.vset(ret) 345*387f9dfdSAndroid Build Coastguard Worker e.vreg(ret, 0, true, ffi.typeof('int32_t')) 346*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.get_stackid) 347*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil -- Free temporary registers 348*387f9dfdSAndroid Build Coastguard Workerend 349*387f9dfdSAndroid Build Coastguard Worker 350*387f9dfdSAndroid Build Coastguard Worker-- table.insert(table, value) keeps semantics with the exception of BPF maps 351*387f9dfdSAndroid Build Coastguard Worker-- map `perf_event` -> submit inserted value 352*387f9dfdSAndroid Build Coastguard Workerbuiltins[table.insert] = function (e, dst, map_var, value) 353*387f9dfdSAndroid Build Coastguard Worker assert(e.V[map_var].const.__map, 'NYI: table.insert() supported only on BPF maps') 354*387f9dfdSAndroid Build Coastguard Worker return perf_submit(e, dst, map_var, value) 355*387f9dfdSAndroid Build Coastguard Workerend 356*387f9dfdSAndroid Build Coastguard Worker 357*387f9dfdSAndroid Build Coastguard Worker-- bpf_get_current_comm(buffer) - write current process name to byte buffer 358*387f9dfdSAndroid Build Coastguard Workerlocal function comm() error('NYI') end 359*387f9dfdSAndroid Build Coastguard Workerbuiltins[comm] = function (e, ret, dst) 360*387f9dfdSAndroid Build Coastguard Worker -- Set R1 = buffer 361*387f9dfdSAndroid Build Coastguard Worker assert(e.V[dst].const.__base, 'NYI: comm(buffer) - buffer variable is not on stack') 362*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 1) -- Spill 363*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.X, 1, 10, 0, 0) 364*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, 1, 0, 0, -e.V[dst].const.__base) 365*387f9dfdSAndroid Build Coastguard Worker -- Set R2 = length 366*387f9dfdSAndroid Build Coastguard Worker e.reg_alloc(e.tmpvar, 2) -- Spill 367*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.K, 2, 0, 0, ffi.sizeof(e.V[dst].type)) 368*387f9dfdSAndroid Build Coastguard Worker -- Return is integer 369*387f9dfdSAndroid Build Coastguard Worker e.vset(ret) 370*387f9dfdSAndroid Build Coastguard Worker e.vreg(ret, 0, true, ffi.typeof('int32_t')) 371*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, HELPER.get_current_comm) 372*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil -- Free temporary registers 373*387f9dfdSAndroid Build Coastguard Workerend 374*387f9dfdSAndroid Build Coastguard Worker 375*387f9dfdSAndroid Build Coastguard Worker-- Math library built-ins 376*387f9dfdSAndroid Build Coastguard Workermath.log2 = function () error('NYI') end 377*387f9dfdSAndroid Build Coastguard Workerbuiltins[math.log2] = function (e, dst, x) 378*387f9dfdSAndroid Build Coastguard Worker -- Classic integer bits subdivison algorithm to find the position 379*387f9dfdSAndroid Build Coastguard Worker -- of the highest bit set, adapted for BPF bytecode-friendly operations. 380*387f9dfdSAndroid Build Coastguard Worker -- https://graphics.stanford.edu/~seander/bithacks.html 381*387f9dfdSAndroid Build Coastguard Worker -- r = 0 382*387f9dfdSAndroid Build Coastguard Worker local r = e.vreg(dst, nil, true) 383*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MOV + BPF.K, r, 0, 0, 0) 384*387f9dfdSAndroid Build Coastguard Worker -- v = x 385*387f9dfdSAndroid Build Coastguard Worker e.vcopy(e.tmpvar, x) 386*387f9dfdSAndroid Build Coastguard Worker local v = e.vreg(e.tmpvar, 2) 387*387f9dfdSAndroid Build Coastguard Worker if cdef.isptr(e.V[x].const) then -- No pointer arithmetics, dereference 388*387f9dfdSAndroid Build Coastguard Worker e.vderef(v, v, {const = {__dissector=ffi.typeof('uint64_t')}}) 389*387f9dfdSAndroid Build Coastguard Worker end 390*387f9dfdSAndroid Build Coastguard Worker -- Invert value to invert all tests, otherwise we would need and+jnz 391*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.NEG + BPF.K, v, 0, 0, 0) -- v = ~v 392*387f9dfdSAndroid Build Coastguard Worker -- Unrolled test cases, converted masking to arithmetic as we don't have "if !(a & b)" 393*387f9dfdSAndroid Build Coastguard Worker -- As we're testing inverted value, we have to use arithmetic shift to copy MSB 394*387f9dfdSAndroid Build Coastguard Worker for i=4,0,-1 do 395*387f9dfdSAndroid Build Coastguard Worker local k = bit.lshift(1, i) 396*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.JGT + BPF.K, v, 0, 2, bit.bnot(bit.lshift(1, k))) -- if !upper_half(x) 397*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ARSH + BPF.K, v, 0, 0, k) -- v >>= k 398*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.OR + BPF.K, r, 0, 0, k) -- r |= k 399*387f9dfdSAndroid Build Coastguard Worker end 400*387f9dfdSAndroid Build Coastguard Worker -- No longer constant, cleanup tmpvars 401*387f9dfdSAndroid Build Coastguard Worker e.V[dst].const = nil 402*387f9dfdSAndroid Build Coastguard Worker e.V[e.tmpvar].reg = nil 403*387f9dfdSAndroid Build Coastguard Workerend 404*387f9dfdSAndroid Build Coastguard Workerbuiltins[math.log10] = function (e, dst, x) 405*387f9dfdSAndroid Build Coastguard Worker -- Compute log2(x) and transform 406*387f9dfdSAndroid Build Coastguard Worker builtins[math.log2](e, dst, x) 407*387f9dfdSAndroid Build Coastguard Worker -- Relationship: log10(v) = log2(v) / log2(10) 408*387f9dfdSAndroid Build Coastguard Worker local r = e.V[dst].reg 409*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, r, 0, 0, 1) -- Compensate round-down 410*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MUL + BPF.K, r, 0, 0, 1233) -- log2(10) ~ 1233>>12 411*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.RSH + BPF.K, r, 0, 0, 12) 412*387f9dfdSAndroid Build Coastguard Workerend 413*387f9dfdSAndroid Build Coastguard Workerbuiltins[math.log] = function (e, dst, x) 414*387f9dfdSAndroid Build Coastguard Worker -- Compute log2(x) and transform 415*387f9dfdSAndroid Build Coastguard Worker builtins[math.log2](e, dst, x) 416*387f9dfdSAndroid Build Coastguard Worker -- Relationship: ln(v) = log2(v) / log2(e) 417*387f9dfdSAndroid Build Coastguard Worker local r = e.V[dst].reg 418*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.ADD + BPF.K, r, 0, 0, 1) -- Compensate round-down 419*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.MUL + BPF.K, r, 0, 0, 2839) -- log2(e) ~ 2839>>12 420*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.ALU64 + BPF.RSH + BPF.K, r, 0, 0, 12) 421*387f9dfdSAndroid Build Coastguard Workerend 422*387f9dfdSAndroid Build Coastguard Worker 423*387f9dfdSAndroid Build Coastguard Worker-- Call-type helpers 424*387f9dfdSAndroid Build Coastguard Workerlocal function call_helper(e, dst, h, vtype) 425*387f9dfdSAndroid Build Coastguard Worker e.vset(dst) 426*387f9dfdSAndroid Build Coastguard Worker e.vreg(dst, 0, true, vtype or ffi.typeof('uint64_t')) 427*387f9dfdSAndroid Build Coastguard Worker e.emit(BPF.JMP + BPF.CALL, 0, 0, 0, h) 428*387f9dfdSAndroid Build Coastguard Worker e.V[dst].const = nil -- Target is not a function anymore 429*387f9dfdSAndroid Build Coastguard Workerend 430*387f9dfdSAndroid Build Coastguard Workerlocal function cpu() error('NYI') end 431*387f9dfdSAndroid Build Coastguard Workerlocal function rand() error('NYI') end 432*387f9dfdSAndroid Build Coastguard Workerlocal function time() error('NYI') end 433*387f9dfdSAndroid Build Coastguard Workerlocal function pid_tgid() error('NYI') end 434*387f9dfdSAndroid Build Coastguard Workerlocal function uid_gid() error('NYI') end 435*387f9dfdSAndroid Build Coastguard Worker 436*387f9dfdSAndroid Build Coastguard Worker-- Export helpers and builtin variants 437*387f9dfdSAndroid Build Coastguard Workerbuiltins.cpu = cpu 438*387f9dfdSAndroid Build Coastguard Workerbuiltins.time = time 439*387f9dfdSAndroid Build Coastguard Workerbuiltins.pid_tgid = pid_tgid 440*387f9dfdSAndroid Build Coastguard Workerbuiltins.uid_gid = uid_gid 441*387f9dfdSAndroid Build Coastguard Workerbuiltins.comm = comm 442*387f9dfdSAndroid Build Coastguard Workerbuiltins.perf_submit = perf_submit 443*387f9dfdSAndroid Build Coastguard Workerbuiltins.stack_id = stack_id 444*387f9dfdSAndroid Build Coastguard Workerbuiltins.load_bytes = load_bytes 445*387f9dfdSAndroid Build Coastguard Workerbuiltins[cpu] = function (e, dst) return call_helper(e, dst, HELPER.get_smp_processor_id) end 446*387f9dfdSAndroid Build Coastguard Workerbuiltins[rand] = function (e, dst) return call_helper(e, dst, HELPER.get_prandom_u32, ffi.typeof('uint32_t')) end 447*387f9dfdSAndroid Build Coastguard Workerbuiltins[time] = function (e, dst) return call_helper(e, dst, HELPER.ktime_get_ns) end 448*387f9dfdSAndroid Build Coastguard Workerbuiltins[pid_tgid] = function (e, dst) return call_helper(e, dst, HELPER.get_current_pid_tgid) end 449*387f9dfdSAndroid Build Coastguard Workerbuiltins[uid_gid] = function (e, dst) return call_helper(e, dst, HELPER.get_current_uid_gid) end 450*387f9dfdSAndroid Build Coastguard Workerbuiltins[perf_submit] = function (e, dst, map, value) return perf_submit(e, dst, map, value) end 451*387f9dfdSAndroid Build Coastguard Workerbuiltins[stack_id] = function (e, dst, map, key) return stack_id(e, dst, map, key) end 452*387f9dfdSAndroid Build Coastguard Workerbuiltins[load_bytes] = function (e, dst, off, var, len) return load_bytes(e, dst, off, var, len) end 453*387f9dfdSAndroid Build Coastguard Worker 454*387f9dfdSAndroid Build Coastguard Workerreturn builtins 455