1 /*
2 * QuickJS command line compiler
3 *
4 * Copyright (c) 2018-2020 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <inttypes.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #if !defined(_WIN32)
33 #include <sys/wait.h>
34 #endif
35
36 #include "cutils.h"
37 #include "quickjs-libc.h"
38
39 typedef struct {
40 char *name;
41 char *short_name;
42 int flags;
43 } namelist_entry_t;
44
45 typedef struct namelist_t {
46 namelist_entry_t *array;
47 int count;
48 int size;
49 } namelist_t;
50
51 typedef struct {
52 const char *option_name;
53 const char *init_name;
54 } FeatureEntry;
55
56 static namelist_t cname_list;
57 static namelist_t cmodule_list;
58 static namelist_t init_module_list;
59 static uint64_t feature_bitmap;
60 static FILE *outfile;
61 static BOOL byte_swap;
62 static BOOL dynamic_export;
63 static const char *c_ident_prefix = "qjsc_";
64
65 #define FE_ALL (-1)
66
67 static const FeatureEntry feature_list[] = {
68 { "date", "Date" },
69 { "eval", "Eval" },
70 { "string-normalize", "StringNormalize" },
71 { "regexp", "RegExp" },
72 { "json", "JSON" },
73 { "proxy", "Proxy" },
74 { "map", "MapSet" },
75 { "typedarray", "TypedArrays" },
76 { "promise", "Promise" },
77 #define FE_MODULE_LOADER 9
78 { "module-loader", NULL },
79 #ifdef CONFIG_BIGNUM
80 { "bigint", "BigInt" },
81 #endif
82 };
83
namelist_add(namelist_t * lp,const char * name,const char * short_name,int flags)84 void namelist_add(namelist_t *lp, const char *name, const char *short_name,
85 int flags)
86 {
87 namelist_entry_t *e;
88 if (lp->count == lp->size) {
89 size_t newsize = lp->size + (lp->size >> 1) + 4;
90 namelist_entry_t *a =
91 realloc(lp->array, sizeof(lp->array[0]) * newsize);
92 /* XXX: check for realloc failure */
93 lp->array = a;
94 lp->size = newsize;
95 }
96 e = &lp->array[lp->count++];
97 e->name = strdup(name);
98 if (short_name)
99 e->short_name = strdup(short_name);
100 else
101 e->short_name = NULL;
102 e->flags = flags;
103 }
104
namelist_free(namelist_t * lp)105 void namelist_free(namelist_t *lp)
106 {
107 while (lp->count > 0) {
108 namelist_entry_t *e = &lp->array[--lp->count];
109 free(e->name);
110 free(e->short_name);
111 }
112 free(lp->array);
113 lp->array = NULL;
114 lp->size = 0;
115 }
116
namelist_find(namelist_t * lp,const char * name)117 namelist_entry_t *namelist_find(namelist_t *lp, const char *name)
118 {
119 int i;
120 for(i = 0; i < lp->count; i++) {
121 namelist_entry_t *e = &lp->array[i];
122 if (!strcmp(e->name, name))
123 return e;
124 }
125 return NULL;
126 }
127
get_c_name(char * buf,size_t buf_size,const char * file)128 static void get_c_name(char *buf, size_t buf_size, const char *file)
129 {
130 const char *p, *r;
131 size_t len, i;
132 int c;
133 char *q;
134
135 p = strrchr(file, '/');
136 if (!p)
137 p = file;
138 else
139 p++;
140 r = strrchr(p, '.');
141 if (!r)
142 len = strlen(p);
143 else
144 len = r - p;
145 pstrcpy(buf, buf_size, c_ident_prefix);
146 q = buf + strlen(buf);
147 for(i = 0; i < len; i++) {
148 c = p[i];
149 if (!((c >= '0' && c <= '9') ||
150 (c >= 'A' && c <= 'Z') ||
151 (c >= 'a' && c <= 'z'))) {
152 c = '_';
153 }
154 if ((q - buf) < buf_size - 1)
155 *q++ = c;
156 }
157 *q = '\0';
158 }
159
dump_hex(FILE * f,const uint8_t * buf,size_t len)160 static void dump_hex(FILE *f, const uint8_t *buf, size_t len)
161 {
162 size_t i, col;
163 col = 0;
164 for(i = 0; i < len; i++) {
165 fprintf(f, " 0x%02x,", buf[i]);
166 if (++col == 8) {
167 fprintf(f, "\n");
168 col = 0;
169 }
170 }
171 if (col != 0)
172 fprintf(f, "\n");
173 }
174
output_object_code(JSContext * ctx,FILE * fo,JSValueConst obj,const char * c_name,BOOL load_only)175 static void output_object_code(JSContext *ctx,
176 FILE *fo, JSValueConst obj, const char *c_name,
177 BOOL load_only)
178 {
179 uint8_t *out_buf;
180 size_t out_buf_len;
181 int flags;
182 flags = JS_WRITE_OBJ_BYTECODE;
183 if (byte_swap)
184 flags |= JS_WRITE_OBJ_BSWAP;
185 out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags);
186 if (!out_buf) {
187 js_std_dump_error(ctx);
188 exit(1);
189 }
190
191 namelist_add(&cname_list, c_name, NULL, load_only);
192
193 fprintf(fo, "const uint32_t %s_size = %u;\n\n",
194 c_name, (unsigned int)out_buf_len);
195 fprintf(fo, "const uint8_t %s[%u] = {\n",
196 c_name, (unsigned int)out_buf_len);
197 dump_hex(fo, out_buf, out_buf_len);
198 fprintf(fo, "};\n\n");
199
200 js_free(ctx, out_buf);
201 }
202
js_module_dummy_init(JSContext * ctx,JSModuleDef * m)203 static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m)
204 {
205 /* should never be called when compiling JS code */
206 abort();
207 }
208
find_unique_cname(char * cname,size_t cname_size)209 static void find_unique_cname(char *cname, size_t cname_size)
210 {
211 char cname1[1024];
212 int suffix_num;
213 size_t len, max_len;
214 assert(cname_size >= 32);
215 /* find a C name not matching an existing module C name by
216 adding a numeric suffix */
217 len = strlen(cname);
218 max_len = cname_size - 16;
219 if (len > max_len)
220 cname[max_len] = '\0';
221 suffix_num = 1;
222 for(;;) {
223 snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num);
224 if (!namelist_find(&cname_list, cname1))
225 break;
226 suffix_num++;
227 }
228 pstrcpy(cname, cname_size, cname1);
229 }
230
jsc_module_loader(JSContext * ctx,const char * module_name,void * opaque)231 JSModuleDef *jsc_module_loader(JSContext *ctx,
232 const char *module_name, void *opaque)
233 {
234 JSModuleDef *m;
235 namelist_entry_t *e;
236
237 /* check if it is a declared C or system module */
238 e = namelist_find(&cmodule_list, module_name);
239 if (e) {
240 /* add in the static init module list */
241 namelist_add(&init_module_list, e->name, e->short_name, 0);
242 /* create a dummy module */
243 m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
244 } else if (has_suffix(module_name, ".so")) {
245 fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name);
246 /* create a dummy module */
247 m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
248 /* the resulting executable will export its symbols for the
249 dynamic library */
250 dynamic_export = TRUE;
251 } else {
252 size_t buf_len;
253 uint8_t *buf;
254 JSValue func_val;
255 char cname[1024];
256
257 buf = js_load_file(ctx, &buf_len, module_name);
258 if (!buf) {
259 JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
260 module_name);
261 return NULL;
262 }
263
264 /* compile the module */
265 func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
266 JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
267 js_free(ctx, buf);
268 if (JS_IsException(func_val))
269 return NULL;
270 get_c_name(cname, sizeof(cname), module_name);
271 if (namelist_find(&cname_list, cname)) {
272 find_unique_cname(cname, sizeof(cname));
273 }
274 output_object_code(ctx, outfile, func_val, cname, TRUE);
275
276 /* the module is already referenced, so we must free it */
277 m = JS_VALUE_GET_PTR(func_val);
278 JS_FreeValue(ctx, func_val);
279 }
280 return m;
281 }
282
compile_file(JSContext * ctx,FILE * fo,const char * filename,const char * c_name1,int module)283 static void compile_file(JSContext *ctx, FILE *fo,
284 const char *filename,
285 const char *c_name1,
286 int module)
287 {
288 uint8_t *buf;
289 char c_name[1024];
290 int eval_flags;
291 JSValue obj;
292 size_t buf_len;
293
294 buf = js_load_file(ctx, &buf_len, filename);
295 if (!buf) {
296 fprintf(stderr, "Could not load '%s'\n", filename);
297 exit(1);
298 }
299 eval_flags = JS_EVAL_FLAG_COMPILE_ONLY;
300 if (module < 0) {
301 module = (has_suffix(filename, ".mjs") ||
302 JS_DetectModule((const char *)buf, buf_len));
303 }
304 if (module)
305 eval_flags |= JS_EVAL_TYPE_MODULE;
306 else
307 eval_flags |= JS_EVAL_TYPE_GLOBAL;
308 obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags);
309 if (JS_IsException(obj)) {
310 js_std_dump_error(ctx);
311 exit(1);
312 }
313 js_free(ctx, buf);
314 if (c_name1) {
315 pstrcpy(c_name, sizeof(c_name), c_name1);
316 } else {
317 get_c_name(c_name, sizeof(c_name), filename);
318 }
319 output_object_code(ctx, fo, obj, c_name, FALSE);
320 JS_FreeValue(ctx, obj);
321 }
322
323 static const char main_c_template1[] =
324 "int main(int argc, char **argv)\n"
325 "{\n"
326 " JSRuntime *rt;\n"
327 " JSContext *ctx;\n"
328 " rt = JS_NewRuntime();\n"
329 " js_std_init_handlers(rt);\n"
330 ;
331
332 static const char main_c_template2[] =
333 " js_std_loop(ctx);\n"
334 " JS_FreeContext(ctx);\n"
335 " JS_FreeRuntime(rt);\n"
336 " return 0;\n"
337 "}\n";
338
339 #define PROG_NAME "qjsc"
340
help(void)341 void help(void)
342 {
343 printf("QuickJS Compiler version " CONFIG_VERSION "\n"
344 "usage: " PROG_NAME " [options] [files]\n"
345 "\n"
346 "options are:\n"
347 "-c only output bytecode in a C file\n"
348 "-e output main() and bytecode in a C file (default = executable output)\n"
349 "-o output set the output filename\n"
350 "-N cname set the C name of the generated data\n"
351 "-m compile as Javascript module (default=autodetect)\n"
352 "-M module_name[,cname] add initialization code for an external C module\n"
353 "-x byte swapped output\n"
354 "-p prefix set the prefix of the generated C names\n"
355 "-S n set the maximum stack size to 'n' bytes (default=%d)\n",
356 JS_DEFAULT_STACK_SIZE);
357 #ifdef CONFIG_LTO
358 {
359 int i;
360 printf("-flto use link time optimization\n");
361 printf("-fbignum enable bignum extensions\n");
362 printf("-fno-[");
363 for(i = 0; i < countof(feature_list); i++) {
364 if (i != 0)
365 printf("|");
366 printf("%s", feature_list[i].option_name);
367 }
368 printf("]\n"
369 " disable selected language features (smaller code size)\n");
370 }
371 #endif
372 exit(1);
373 }
374
375 #if defined(CONFIG_CC) && !defined(_WIN32)
376
exec_cmd(char ** argv)377 int exec_cmd(char **argv)
378 {
379 int pid, status, ret;
380
381 pid = fork();
382 if (pid == 0) {
383 execvp(argv[0], argv);
384 exit(1);
385 }
386
387 for(;;) {
388 ret = waitpid(pid, &status, 0);
389 if (ret == pid && WIFEXITED(status))
390 break;
391 }
392 return WEXITSTATUS(status);
393 }
394
output_executable(const char * out_filename,const char * cfilename,BOOL use_lto,BOOL verbose,const char * exename)395 static int output_executable(const char *out_filename, const char *cfilename,
396 BOOL use_lto, BOOL verbose, const char *exename)
397 {
398 const char *argv[64];
399 const char **arg, *bn_suffix, *lto_suffix;
400 char libjsname[1024];
401 char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
402 int ret;
403
404 /* get the directory of the executable */
405 pstrcpy(exe_dir, sizeof(exe_dir), exename);
406 p = strrchr(exe_dir, '/');
407 if (p) {
408 *p = '\0';
409 } else {
410 pstrcpy(exe_dir, sizeof(exe_dir), ".");
411 }
412
413 /* if 'quickjs.h' is present at the same path as the executable, we
414 use it as include and lib directory */
415 snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir);
416 if (access(buf, R_OK) == 0) {
417 pstrcpy(inc_dir, sizeof(inc_dir), exe_dir);
418 pstrcpy(lib_dir, sizeof(lib_dir), exe_dir);
419 } else {
420 snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX);
421 snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX);
422 }
423
424 lto_suffix = "";
425 bn_suffix = "";
426
427 arg = argv;
428 *arg++ = CONFIG_CC;
429 *arg++ = "-O2";
430 #ifdef CONFIG_LTO
431 if (use_lto) {
432 *arg++ = "-flto";
433 lto_suffix = ".lto";
434 }
435 #endif
436 /* XXX: use the executable path to find the includes files and
437 libraries */
438 *arg++ = "-D";
439 *arg++ = "_GNU_SOURCE";
440 *arg++ = "-I";
441 *arg++ = inc_dir;
442 *arg++ = "-o";
443 *arg++ = out_filename;
444 if (dynamic_export)
445 *arg++ = "-rdynamic";
446 *arg++ = cfilename;
447 snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a",
448 lib_dir, bn_suffix, lto_suffix);
449 *arg++ = libjsname;
450 *arg++ = "-lm";
451 *arg++ = "-ldl";
452 *arg++ = "-lpthread";
453 *arg = NULL;
454
455 if (verbose) {
456 for(arg = argv; *arg != NULL; arg++)
457 printf("%s ", *arg);
458 printf("\n");
459 }
460
461 ret = exec_cmd((char **)argv);
462 unlink(cfilename);
463 return ret;
464 }
465 #else
output_executable(const char * out_filename,const char * cfilename,BOOL use_lto,BOOL verbose,const char * exename)466 static int output_executable(const char *out_filename, const char *cfilename,
467 BOOL use_lto, BOOL verbose, const char *exename)
468 {
469 fprintf(stderr, "Executable output is not supported for this target\n");
470 exit(1);
471 return 0;
472 }
473 #endif
474
475
476 typedef enum {
477 OUTPUT_C,
478 OUTPUT_C_MAIN,
479 OUTPUT_EXECUTABLE,
480 } OutputTypeEnum;
481
main(int argc,char ** argv)482 int main(int argc, char **argv)
483 {
484 int c, i, verbose;
485 const char *out_filename, *cname;
486 char cfilename[1024];
487 FILE *fo;
488 JSRuntime *rt;
489 JSContext *ctx;
490 BOOL use_lto;
491 int module;
492 OutputTypeEnum output_type;
493 size_t stack_size;
494 #ifdef CONFIG_BIGNUM
495 BOOL bignum_ext = FALSE;
496 #endif
497
498 out_filename = NULL;
499 output_type = OUTPUT_EXECUTABLE;
500 cname = NULL;
501 feature_bitmap = FE_ALL;
502 module = -1;
503 byte_swap = FALSE;
504 verbose = 0;
505 use_lto = FALSE;
506 stack_size = 0;
507
508 /* add system modules */
509 namelist_add(&cmodule_list, "std", "std", 0);
510 namelist_add(&cmodule_list, "os", "os", 0);
511
512 for(;;) {
513 c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:");
514 if (c == -1)
515 break;
516 switch(c) {
517 case 'h':
518 help();
519 case 'o':
520 out_filename = optarg;
521 break;
522 case 'c':
523 output_type = OUTPUT_C;
524 break;
525 case 'e':
526 output_type = OUTPUT_C_MAIN;
527 break;
528 case 'N':
529 cname = optarg;
530 break;
531 case 'f':
532 {
533 const char *p;
534 p = optarg;
535 if (!strcmp(optarg, "lto")) {
536 use_lto = TRUE;
537 } else if (strstart(p, "no-", &p)) {
538 use_lto = TRUE;
539 for(i = 0; i < countof(feature_list); i++) {
540 if (!strcmp(p, feature_list[i].option_name)) {
541 feature_bitmap &= ~((uint64_t)1 << i);
542 break;
543 }
544 }
545 if (i == countof(feature_list))
546 goto bad_feature;
547 } else
548 #ifdef CONFIG_BIGNUM
549 if (!strcmp(optarg, "bignum")) {
550 bignum_ext = TRUE;
551 } else
552 #endif
553 {
554 bad_feature:
555 fprintf(stderr, "unsupported feature: %s\n", optarg);
556 exit(1);
557 }
558 }
559 break;
560 case 'm':
561 module = 1;
562 break;
563 case 'M':
564 {
565 char *p;
566 char path[1024];
567 char cname[1024];
568 pstrcpy(path, sizeof(path), optarg);
569 p = strchr(path, ',');
570 if (p) {
571 *p = '\0';
572 pstrcpy(cname, sizeof(cname), p + 1);
573 } else {
574 get_c_name(cname, sizeof(cname), path);
575 }
576 namelist_add(&cmodule_list, path, cname, 0);
577 }
578 break;
579 case 'x':
580 byte_swap = TRUE;
581 break;
582 case 'v':
583 verbose++;
584 break;
585 case 'p':
586 c_ident_prefix = optarg;
587 break;
588 case 'S':
589 stack_size = (size_t)strtod(optarg, NULL);
590 break;
591 default:
592 break;
593 }
594 }
595
596 if (optind >= argc)
597 help();
598
599 if (!out_filename) {
600 if (output_type == OUTPUT_EXECUTABLE) {
601 out_filename = "a.out";
602 } else {
603 out_filename = "out.c";
604 }
605 }
606
607 if (output_type == OUTPUT_EXECUTABLE) {
608 #if defined(_WIN32) || defined(__ANDROID__)
609 /* XXX: find a /tmp directory ? */
610 snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid());
611 #else
612 snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid());
613 #endif
614 } else {
615 pstrcpy(cfilename, sizeof(cfilename), out_filename);
616 }
617
618 fo = fopen(cfilename, "w");
619 if (!fo) {
620 perror(cfilename);
621 exit(1);
622 }
623 outfile = fo;
624
625 rt = JS_NewRuntime();
626 ctx = JS_NewContext(rt);
627 #ifdef CONFIG_BIGNUM
628 if (bignum_ext) {
629 JS_AddIntrinsicBigFloat(ctx);
630 JS_AddIntrinsicBigDecimal(ctx);
631 JS_AddIntrinsicOperators(ctx);
632 JS_EnableBignumExt(ctx, TRUE);
633 }
634 #endif
635
636 /* loader for ES6 modules */
637 JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
638
639 fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
640 "\n"
641 );
642
643 if (output_type != OUTPUT_C) {
644 fprintf(fo, "#include \"quickjs-libc.h\"\n"
645 "\n"
646 );
647 } else {
648 fprintf(fo, "#include <inttypes.h>\n"
649 "\n"
650 );
651 }
652
653 for(i = optind; i < argc; i++) {
654 const char *filename = argv[i];
655 compile_file(ctx, fo, filename, cname, module);
656 cname = NULL;
657 }
658
659 if (output_type != OUTPUT_C) {
660 fputs(main_c_template1, fo);
661 fprintf(fo, " ctx = JS_NewContextRaw(rt);\n");
662
663 if (stack_size != 0) {
664 fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n",
665 (unsigned int)stack_size);
666 }
667
668 /* add the module loader if necessary */
669 if (feature_bitmap & (1 << FE_MODULE_LOADER)) {
670 fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n");
671 }
672
673 /* add the basic objects */
674
675 fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n");
676 for(i = 0; i < countof(feature_list); i++) {
677 if ((feature_bitmap & ((uint64_t)1 << i)) &&
678 feature_list[i].init_name) {
679 fprintf(fo, " JS_AddIntrinsic%s(ctx);\n",
680 feature_list[i].init_name);
681 }
682 }
683 #ifdef CONFIG_BIGNUM
684 if (bignum_ext) {
685 fprintf(fo,
686 " JS_AddIntrinsicBigFloat(ctx);\n"
687 " JS_AddIntrinsicBigDecimal(ctx);\n"
688 " JS_AddIntrinsicOperators(ctx);\n"
689 " JS_EnableBignumExt(ctx, 1);\n");
690 }
691 #endif
692 fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n");
693
694 for(i = 0; i < init_module_list.count; i++) {
695 namelist_entry_t *e = &init_module_list.array[i];
696 /* initialize the static C modules */
697
698 fprintf(fo,
699 " {\n"
700 " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n"
701 " js_init_module_%s(ctx, \"%s\");\n"
702 " }\n",
703 e->short_name, e->short_name, e->name);
704 }
705
706 for(i = 0; i < cname_list.count; i++) {
707 namelist_entry_t *e = &cname_list.array[i];
708 fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, %s);\n",
709 e->name, e->name,
710 e->flags ? "1" : "0");
711 }
712 fputs(main_c_template2, fo);
713 }
714
715 JS_FreeContext(ctx);
716 JS_FreeRuntime(rt);
717
718 fclose(fo);
719
720 if (output_type == OUTPUT_EXECUTABLE) {
721 return output_executable(out_filename, cfilename, use_lto, verbose,
722 argv[0]);
723 }
724 namelist_free(&cname_list);
725 namelist_free(&cmodule_list);
726 namelist_free(&init_module_list);
727 return 0;
728 }
729