xref: /aosp_15_r20/external/bcc/src/lua/bpf/builtins.lua (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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