Line data Source code
1 : #define _GNU_SOURCE
2 :
3 : #include "../../util/fd_util_base.h"
4 : #if defined(__linux__) && FD_HAS_X86
5 : #define FD_VM_TOOL_HAS_JIT 1
6 : #else
7 : #define FD_VM_TOOL_HAS_JIT 0
8 : #endif
9 :
10 : #include "fd_vm_base.h"
11 : #include "fd_vm_private.h"
12 :
13 : #include <stdio.h>
14 : #include <stdlib.h>
15 : #include <errno.h>
16 : #include <sys/stat.h>
17 :
18 : #if FD_VM_TOOL_HAS_JIT
19 : #include "jit/fd_jit.h"
20 : #include <sys/mman.h>
21 : #endif
22 :
23 : struct fd_vm_tool_prog {
24 : void * bin_buf;
25 : fd_sbpf_program_t * prog;
26 : fd_sbpf_syscalls_t * syscalls;
27 : };
28 :
29 : typedef struct fd_vm_tool_prog fd_vm_tool_prog_t;
30 :
31 : static fd_vm_tool_prog_t *
32 : fd_vm_tool_prog_create( fd_vm_tool_prog_t * tool_prog,
33 0 : char const * bin_path ) {
34 :
35 : /* Open file */
36 :
37 0 : FILE * bin_file = fopen( bin_path, "r" );
38 0 : if( FD_UNLIKELY( !bin_file ) )
39 0 : FD_LOG_ERR(( "fopen(\"%s\") failed (%i-%s)", bin_path, errno, fd_io_strerror( errno ) ));
40 :
41 0 : struct stat bin_stat;
42 0 : if( FD_UNLIKELY( 0!=fstat( fileno( bin_file ), &bin_stat ) ) )
43 0 : FD_LOG_ERR(( "fstat() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
44 0 : if( FD_UNLIKELY( !S_ISREG( bin_stat.st_mode ) ) )
45 0 : FD_LOG_ERR(( "File \"%s\" not a regular file", bin_path ));
46 :
47 : /* Allocate file buffer */
48 :
49 0 : ulong bin_sz = (ulong)bin_stat.st_size;
50 0 : void * bin_buf = malloc( bin_sz+8UL );
51 0 : if( FD_UNLIKELY( !bin_buf ) )
52 0 : FD_LOG_ERR(( "malloc(%#lx) failed (%i-%s)", bin_sz, errno, fd_io_strerror( errno ) ));
53 :
54 : /* Read program */
55 :
56 0 : if( FD_UNLIKELY( fread( bin_buf, bin_sz, 1UL, bin_file )!=1UL ) )
57 0 : FD_LOG_ERR(( "fread() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
58 0 : FD_TEST( 0==fclose( bin_file ) );
59 :
60 : /* Extract ELF info */
61 :
62 0 : fd_sbpf_elf_info_t elf_info;
63 0 : FD_TEST( fd_sbpf_elf_peek( &elf_info, bin_buf, bin_sz, /* deploy checks */ 0, FD_SBPF_V0, FD_SBPF_V3 ) );
64 :
65 : /* Allocate rodata segment */
66 :
67 0 : void * rodata = malloc( elf_info.rodata_footprint );
68 0 : FD_TEST( rodata );
69 :
70 : /* Allocate program buffer */
71 :
72 0 : ulong prog_align = fd_sbpf_program_align();
73 0 : ulong prog_footprint = fd_sbpf_program_footprint( &elf_info );
74 0 : fd_sbpf_program_t * prog = fd_sbpf_program_new( aligned_alloc( prog_align, prog_footprint ), &elf_info, rodata );
75 0 : FD_TEST( prog );
76 :
77 : /* Allocate syscalls */
78 0 : fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new(
79 0 : aligned_alloc( fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
80 0 : FD_TEST( syscalls );
81 :
82 0 : fd_vm_syscall_register_all( syscalls, 0 );
83 :
84 : /* Load program */
85 0 : if( FD_UNLIKELY( 0!=fd_sbpf_program_load( prog, bin_buf, bin_sz, syscalls, false ) ) )
86 0 : FD_LOG_ERR(( "fd_sbpf_program_load() failed: %s", fd_sbpf_strerror() ));
87 :
88 0 : tool_prog->bin_buf = bin_buf;
89 0 : tool_prog->prog = prog;
90 0 : tool_prog->syscalls = syscalls;
91 :
92 0 : return tool_prog;
93 0 : }
94 :
95 : static void
96 0 : fd_vm_tool_prog_free( fd_vm_tool_prog_t * prog ) {
97 0 : free( prog->prog->rodata );
98 0 : free( prog->bin_buf );
99 0 : free( fd_sbpf_program_delete ( prog->prog ) );
100 0 : free( fd_sbpf_syscalls_delete( prog->syscalls ) );
101 0 : }
102 :
103 : int
104 0 : cmd_disasm( char const * bin_path ) {
105 0 : fd_vm_tool_prog_t tool_prog;
106 0 : fd_vm_tool_prog_create( &tool_prog, bin_path ); /* FIXME: RENAME INIT? */
107 :
108 : /* FIXME: DOES DISASM NEED THE TEXT_OFF TOO FOR CALLS? */
109 : /* FIXME: WOULD DISASM BENEFIT BY ANNOTATING THE ENTRY PC AND/OR THE
110 : CALLDESTS? */
111 :
112 0 : ulong out_max = 128UL*tool_prog.prog->text_cnt; /* FIXME: OVERFLOW */
113 0 : ulong out_len = 0UL;
114 0 : char * out = (char *)malloc( out_max ); /* FIXME: GROSS */
115 0 : if( FD_UNLIKELY( !out ) ) FD_LOG_ERR(( "malloc failed" ));
116 0 : out[0] = '\0';
117 :
118 0 : int err = fd_vm_disasm_program( tool_prog.prog->text, tool_prog.prog->text_cnt, tool_prog.syscalls, out, out_max, &out_len );
119 :
120 0 : puts( out );
121 :
122 0 : free( out ); /* FIXME: GROSS */
123 :
124 0 : fd_vm_tool_prog_free( &tool_prog ); /* FIXME: RENAME DESTROY (OR MAYBE FINI)*/
125 :
126 0 : return err;
127 0 : }
128 :
129 : int
130 0 : cmd_validate( char const * bin_path ) {
131 :
132 0 : fd_vm_tool_prog_t tool_prog;
133 0 : fd_vm_tool_prog_create( &tool_prog, bin_path );
134 :
135 0 : fd_vm_t vm = {
136 0 : .text = tool_prog.prog->text,
137 0 : .text_cnt = tool_prog.prog->text_cnt,
138 0 : .text_off = tool_prog.prog->text_off,
139 0 : .entry_pc = tool_prog.prog->entry_pc,
140 0 : .calldests = tool_prog.prog->calldests,
141 0 : .syscalls = tool_prog.syscalls,
142 0 : .trace = NULL
143 0 : };
144 :
145 : /* FIXME: DO WE REALLY NEED THE WHOLE VM TO VALIDATE? */
146 :
147 0 : int err = fd_vm_validate( &vm );
148 :
149 0 : fd_vm_tool_prog_free( &tool_prog );
150 :
151 0 : return err;
152 0 : }
153 :
154 : static uchar *
155 0 : read_input_file( char const * input_path, ulong * _input_sz ) {
156 0 : if( FD_UNLIKELY( !_input_sz ) ) FD_LOG_ERR(( "input_sz cannot be NULL" ));
157 :
158 : /* Open file */
159 :
160 0 : FILE * input_file = fopen( input_path, "r" );
161 0 : if( FD_UNLIKELY( !input_file ) )
162 0 : FD_LOG_ERR(( "fopen(\"%s\") failed (%i-%s)", input_path, errno, fd_io_strerror( errno ) ));
163 :
164 0 : struct stat input_stat;
165 0 : if( FD_UNLIKELY( 0!=fstat( fileno( input_file ), &input_stat ) ) )
166 0 : FD_LOG_ERR(( "fstat() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
167 0 : if( FD_UNLIKELY( !S_ISREG( input_stat.st_mode ) ) )
168 0 : FD_LOG_ERR(( "File \"%s\" not a regular file", input_path ));
169 :
170 : /* Allocate file buffer */
171 :
172 0 : ulong input_sz = (ulong)input_stat.st_size;
173 0 : void * input_buf = malloc( input_sz );
174 0 : if( FD_UNLIKELY( !input_buf ) )
175 0 : FD_LOG_ERR(( "malloc(%#lx) failed (%i-%s)", input_sz, errno, fd_io_strerror( errno ) ));
176 :
177 : /* Read input */
178 :
179 0 : if( FD_UNLIKELY( fread( input_buf, input_sz, 1UL, input_file )!=1UL ) )
180 0 : FD_LOG_ERR(( "fread() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
181 0 : FD_TEST( 0==fclose( input_file ) );
182 :
183 0 : *_input_sz = input_sz;
184 :
185 0 : return input_buf;
186 0 : }
187 :
188 : int
189 : cmd_trace( char const * bin_path,
190 0 : char const * input_path ) {
191 :
192 0 : fd_vm_tool_prog_t tool_prog;
193 0 : fd_vm_tool_prog_create( &tool_prog, bin_path );
194 :
195 0 : ulong input_sz = 0UL;
196 0 : uchar * input = read_input_file( input_path, &input_sz ); /* FIXME: WHERE IS INPUT FREED? */
197 :
198 : /* Turn input into a single memory region */
199 0 : fd_vm_input_region_t input_region = {
200 0 : .vaddr_offset = 0UL,
201 0 : .haddr = (ulong)input,
202 0 : .region_sz = (uint)input_sz,
203 0 : .is_writable = 1U
204 0 : };
205 :
206 :
207 0 : ulong event_max = 1UL<<30; /* 1 GiB default storage */
208 0 : ulong event_data_max = 2048UL; /* 2 KiB memory range captures by default */
209 0 : fd_vm_trace_t * trace = fd_vm_trace_join( fd_vm_trace_new( aligned_alloc(
210 0 : fd_vm_trace_align(), fd_vm_trace_footprint( event_max, event_data_max ) ), event_max, event_data_max ) ); /* logs details */
211 0 : if( FD_UNLIKELY( !trace ) ) {
212 0 : FD_LOG_WARNING(( "unable to create trace" ));
213 0 : return FD_VM_ERR_INVAL; /* FIXME: ERR CODE */
214 0 : }
215 :
216 0 : fd_sha256_t _sha[1];
217 0 : fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
218 :
219 : /* FIXME: Gross init */
220 0 : fd_vm_t vm = {
221 0 : .instr_ctx = NULL, /* FIXME */
222 0 : .heap_max = FD_VM_HEAP_DEFAULT, /* FIXME: CONFIGURE */
223 0 : .entry_cu = FD_VM_COMPUTE_UNIT_LIMIT, /* FIXME: CONFIGURE */
224 0 : .rodata = tool_prog.prog->rodata,
225 0 : .rodata_sz = tool_prog.prog->rodata_sz,
226 0 : .text = tool_prog.prog->text,
227 0 : .text_cnt = tool_prog.prog->text_cnt,
228 0 : .text_off = (ulong)tool_prog.prog->text - (ulong)tool_prog.prog->rodata,
229 0 : .entry_pc = tool_prog.prog->entry_pc,
230 0 : .calldests = tool_prog.prog->calldests,
231 0 : .syscalls = tool_prog.syscalls,
232 0 : .input_mem_regions = &input_region,
233 0 : .input_mem_regions_cnt = 1U,
234 0 : .trace = trace,
235 0 : .sha = sha,
236 0 : };
237 :
238 : /* FIXME: MOVE TO EXEC */
239 0 : vm.reg[ 1] = FD_VM_MEM_MAP_INPUT_REGION_START;
240 0 : vm.reg[10] = FD_VM_MEM_MAP_STACK_REGION_START + 0x1000;
241 :
242 0 : long dt = -fd_log_wallclock();
243 0 : int exec_err = fd_vm_exec( &vm );
244 0 : dt += fd_log_wallclock();
245 :
246 0 : printf( "Frame 0\n" );
247 0 : int err = fd_vm_trace_printf( vm.trace, vm.syscalls ); /* logs details */
248 0 : if( FD_UNLIKELY( err ) ) FD_LOG_WARNING(( "fd_vm_trace_printf failed (%i-%s)", err, fd_vm_strerror( err ) ));
249 :
250 0 : printf( "Interp_res: %i (%s)\n", exec_err, fd_vm_strerror( exec_err ) );
251 0 : printf( "Return value: %lu\n", vm.reg[0] );
252 0 : printf( "Instruction counter: %lu\n", vm.ic );
253 0 : printf( "Time: %ld\n", dt );
254 :
255 0 : free( fd_vm_trace_delete( fd_vm_trace_leave( trace ) ) ); /* logs details */
256 :
257 0 : return err;
258 0 : }
259 :
260 : int
261 : cmd_run( char const * bin_path,
262 : char const * input_path,
263 0 : int use_jit /* in [0,1] */ ) {
264 :
265 0 : fd_vm_tool_prog_t tool_prog;
266 0 : fd_vm_tool_prog_create( &tool_prog, bin_path );
267 :
268 0 : ulong input_sz = 0UL;
269 0 : uchar * input = read_input_file( input_path, &input_sz ); /* FIXME: WHERE IS INPUT FREED? */
270 :
271 : /* Turn input into a single memory region */
272 0 : fd_vm_input_region_t input_region = {
273 0 : .vaddr_offset = 0UL,
274 0 : .haddr = (ulong)input,
275 0 : .region_sz = (uint)input_sz,
276 0 : .is_writable = 1U
277 0 : };
278 :
279 0 : fd_sha256_t _sha[1];
280 0 : fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
281 :
282 0 : fd_vm_t vm = {
283 0 : .instr_ctx = NULL, /* FIXME */
284 0 : .heap_max = FD_VM_HEAP_DEFAULT, /* FIXME: CONFIGURE */
285 0 : .entry_cu = FD_VM_COMPUTE_UNIT_LIMIT, /* FIXME: CONFIGURE */
286 0 : .rodata = tool_prog.prog->rodata,
287 0 : .rodata_sz = tool_prog.prog->rodata_sz,
288 0 : .text = tool_prog.prog->text,
289 0 : .text_cnt = tool_prog.prog->text_cnt,
290 0 : .text_off = (ulong)tool_prog.prog->text - (ulong)tool_prog.prog->rodata,
291 0 : .entry_pc = tool_prog.prog->entry_pc,
292 0 : .calldests = tool_prog.prog->calldests,
293 0 : .syscalls = tool_prog.syscalls,
294 0 : .input_mem_regions = &input_region,
295 0 : .input_mem_regions_cnt = 1U,
296 0 : .trace = NULL,
297 0 : .sha = sha,
298 0 : };
299 :
300 : /* FIXME: MOVE TO EXEC */
301 0 : vm.reg[ 1] = FD_VM_MEM_MAP_INPUT_REGION_START;
302 0 : vm.reg[10] = FD_VM_MEM_MAP_STACK_REGION_START + 0x1000;
303 :
304 0 : int exec_err = FD_VM_ERR_UNSUP;
305 0 : long dt = 0L;
306 0 : long compile_dt = 0L;
307 :
308 0 : if( use_jit ) {
309 0 : # if FD_VM_TOOL_HAS_JIT
310 :
311 0 : ulong code_bufsz = fd_ulong_align_up( fd_jit_est_code_sz( tool_prog.prog->text_sz ), FD_SHMEM_NORMAL_PAGE_SZ );
312 0 : uchar * code_buf = mmap( 0, code_bufsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
313 0 : if( FD_UNLIKELY( code_buf==MAP_FAILED ) ) FD_LOG_ERR(( "mmap failed" ));
314 :
315 0 : ulong scratch_bufsz = fd_jit_est_scratch_sz( tool_prog.prog->text_sz );
316 0 : void * scratch_buf = malloc( scratch_bufsz );
317 :
318 0 : for( ulong j=0; j<code_bufsz; j+=2 ) {
319 0 : code_buf[ j ] = 0x0f;
320 0 : code_buf[ j+1 ] = 0x0b;
321 0 : }
322 0 : fd_memset( scratch_buf, 0, scratch_bufsz );
323 :
324 0 : compile_dt = -fd_log_wallclock();
325 0 : int compile_err = FD_VM_ERR_EBPF_JIT_NOT_COMPILED;
326 0 : fd_jit_prog_t _jit_prog[1];
327 0 : fd_jit_prog_t * jit_prog = fd_jit_prog_new(
328 0 : _jit_prog,
329 0 : tool_prog.prog,
330 0 : tool_prog.syscalls,
331 0 : code_buf, code_bufsz,
332 0 : scratch_buf, scratch_bufsz,
333 0 : &compile_err
334 0 : );
335 0 : if( FD_UNLIKELY( !jit_prog ) ) {
336 0 : FD_LOG_ERR(( "JIT compile failed (%d-%s)", compile_err, fd_vm_strerror( compile_err ) ));
337 0 : }
338 0 : compile_dt += fd_log_wallclock();
339 :
340 0 : free( scratch_buf );
341 0 : mprotect( code_buf, fd_ulong_align_up( jit_prog->code_sz, FD_SHMEM_NORMAL_PAGE_SZ ), PROT_READ | PROT_EXEC );
342 :
343 0 : dt = -fd_log_wallclock();
344 0 : exec_err = fd_jit_exec( jit_prog, &vm );
345 0 : dt += fd_log_wallclock();
346 :
347 0 : fd_jit_prog_delete( jit_prog );
348 0 : munmap( code_buf, code_bufsz );
349 :
350 0 : # endif
351 0 : } else {
352 :
353 0 : dt = -fd_log_wallclock();
354 0 : exec_err = fd_vm_exec( &vm );
355 0 : dt += fd_log_wallclock();
356 :
357 0 : }
358 :
359 0 : printf( "Interp_res: %i (%s)\n", exec_err, fd_vm_strerror( exec_err ) );
360 0 : printf( "Return value: %lu\n", vm.reg[0] );
361 0 : printf( "Instruction counter: %lu\n", vm.ic );
362 0 : printf( "Time: %ld\n", dt );
363 0 : if( compile_dt ) {
364 0 : printf( "Compile time: %ld\n", compile_dt );
365 0 : }
366 :
367 0 : return FD_VM_SUCCESS;
368 0 : }
369 :
370 : int
371 : main( int argc,
372 : char ** argv ) {
373 : fd_boot( &argc, &argv );
374 :
375 : char const * cmd = fd_env_strip_cmdline_cstr( &argc, &argv, "--cmd", NULL, NULL );
376 : if( FD_UNLIKELY( !cmd ) ) FD_LOG_ERR(( "Please specify a command" ));
377 :
378 : if( !strcmp( cmd, "disasm" ) ) {
379 :
380 : char const * program_file = fd_env_strip_cmdline_cstr( &argc, &argv, "--program-file", NULL, NULL );
381 :
382 : if( FD_UNLIKELY( program_file==NULL ) ) FD_LOG_ERR(( "Please specify a --program-file" ));
383 :
384 : FD_LOG_NOTICE(( "disasm --program-file %s", program_file ));
385 :
386 : int err = cmd_disasm( program_file );
387 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "disasm failed (%i-%s)", err, fd_vm_strerror( err ) ));
388 :
389 : FD_LOG_NOTICE(( "disasm success" ));
390 :
391 : } else if( !strcmp( cmd, "validate" ) ) {
392 :
393 : char const * program_file = fd_env_strip_cmdline_cstr( &argc, &argv, "--program-file", NULL, NULL );
394 :
395 : if( FD_UNLIKELY( !program_file ) ) FD_LOG_ERR(( "Please specify a --program-file" ));
396 :
397 : FD_LOG_NOTICE(( "validate --program-file %s", program_file ));
398 :
399 : int err = cmd_validate( program_file );
400 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "validate failed (%i-%s)", err, fd_vm_strerror( err ) ));
401 :
402 : FD_LOG_NOTICE(( "validate success" ));
403 :
404 : } else if( !strcmp( cmd, "trace" ) ) {
405 :
406 : char const * program_file = fd_env_strip_cmdline_cstr( &argc, &argv, "--program-file", NULL, NULL );
407 : char const * input_file = fd_env_strip_cmdline_cstr( &argc, &argv, "--input-file", NULL, NULL );
408 :
409 : if( FD_UNLIKELY( !program_file ) ) FD_LOG_ERR(( "Please specify a --program-file" ));
410 : if( FD_UNLIKELY( !input_file ) ) FD_LOG_ERR(( "Please specify a --input-file" ));
411 :
412 : FD_LOG_NOTICE(( "trace --program-file %s --input-file %s", program_file, input_file ));
413 :
414 : int err = cmd_trace( program_file, input_file );
415 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "trace failed (%i-%s)", err, fd_vm_strerror( err ) ));
416 :
417 : FD_LOG_NOTICE(( "trace success" ));
418 :
419 : } else if( !strcmp( cmd, "run" ) ) {
420 :
421 : char const * program_file = fd_env_strip_cmdline_cstr( &argc, &argv, "--program-file", NULL, NULL );
422 : char const * input_file = fd_env_strip_cmdline_cstr( &argc, &argv, "--input-file", NULL, NULL );
423 : char const * backend = fd_env_strip_cmdline_cstr( &argc, &argv, "--backend", NULL, "interp" );
424 : int use_jit = 0;
425 : if( 0==strcmp( backend, "interp" ) ) {
426 : use_jit = 0;
427 : } else if( FD_VM_TOOL_HAS_JIT && 0==strcmp( backend, "jit" ) ) {
428 : use_jit = 1;
429 : } else {
430 : FD_LOG_ERR(( "Unsupported --backend \"%s\"", backend ));
431 : }
432 :
433 : if( FD_UNLIKELY( !program_file ) ) FD_LOG_ERR(( "Please specify a --program-file" ));
434 : if( FD_UNLIKELY( !input_file ) ) FD_LOG_ERR(( "Please specify a --input-file" ));
435 :
436 : FD_LOG_NOTICE(( "run --program-file %s --input-file %s --backend %s", program_file, input_file, backend ));
437 :
438 : int err = cmd_run( program_file, input_file, use_jit );
439 : if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "run failed (%i-%s)", err, fd_vm_strerror( err ) ));
440 :
441 : FD_LOG_NOTICE(( "run success" ));
442 :
443 : } else {
444 :
445 : FD_LOG_ERR(( "unknown command: %s", cmd ));
446 :
447 : }
448 :
449 : fd_halt();
450 : return 0;
451 : }
|