KJK::Hyperion is proud to present "dllimport purity", another landmark commit that...
[reactos.git] / reactos / tools / nci / ncitool.c
1 /*
2 * FILE: tools/nci/ncitool.c
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: Native Call Interface Support Tool
5 * PURPOSE: Generates NCI Tables and Stubs.
6 * PROGRAMMER; Alex Ionescu (alex@relsoft.net)
7 * CHANGE HISTORY: 14/01/05 - Created. Based on original code by
8 * KJK::Hyperion and Emanuelle Aliberti.
9 *
10 */
11
12 /* INCLUDE ******************************************************************/
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #if !defined(__FreeBSD__) && !defined(__APPLE__)
18 # include <malloc.h>
19 #endif // __FreeBSD__
20
21 /* DEFINES ****************************************************************/
22
23 #define INPUT_BUFFER_SIZE 255
24 #define Arguments 8
25
26 /******* Table Indexes ************/
27 #define MAIN_INDEX 0x0
28 #define WIN32K_INDEX 0x1000
29
30 /******* Argument List ************/
31 /* Databases */
32 #define NativeSystemDb 0
33 #define NativeGuiDb 1
34
35 /* Service Tables */
36 #define NtosServiceTable 2
37 #define Win32kServiceTable 3
38
39 /* Stub Files */
40 #define NtosUserStubs 4
41 #define NtosKernelStubs 5
42 #define Win32kStubs 6
43
44 /* Spec Files */
45 #define NtSpec 7
46
47 /********** Stub Code ************/
48
49 /*
50 * This stubs calls into KUSER_SHARED_DATA where either a
51 * sysenter or interrupt is performed, depending on CPU support.
52 */
53 #if defined(__GNUC__)
54 #define UserModeStub_x86 " movl $0x%x, %%eax\n" \
55 " movl $KUSER_SHARED_SYSCALL, %%ecx\n" \
56 " call *(%%ecx)\n" \
57 " ret $0x%x\n\n"
58
59 #define UserModeStub_ppc " stwu 1,-16(1)\n" \
60 " mflr 0\n\t" \
61 " stw 0,0(1)\n" \
62 " li 0,0x%x\n" \
63 " sc\n" \
64 " lwz 0,0(1)\n" \
65 " mtlr 0\n" \
66 " addi 1,1,16\n" \
67 " blr\n"
68
69 #define UserModeStub_mips " li $8, KUSER_SHARED_SYSCALL\n" \
70 " lw $8,0($8)\n" \
71 " j $8\n" \
72 " nop\n"
73
74 #define UserModeStub_arm " swi #0x%x\n" \
75 " bx lr\n\n"
76
77 #elif defined(_MSC_VER)
78 #define UserModeStub_x86 " asm { \n" \
79 " mov eax, %xh\n" \
80 " mov ecx, KUSER_SHARED_SYSCALL\n" \
81 " call [ecx]\n" \
82 " ret %xh\n" \
83 " }\n"
84 #else
85 #error Unknown compiler for inline assembler
86 #endif
87
88 /*
89 * This stub calls KiSystemService directly with a fake INT2E stack.
90 * Because EIP is pushed during the call, the handler will return here.
91 */
92 #if defined(__GNUC__)
93 #define KernelModeStub_x86 " movl $0x%x, %%eax\n" \
94 " leal 4(%%esp), %%edx\n" \
95 " pushfl\n" \
96 " pushl $KGDT_R0_CODE\n" \
97 " call _KiSystemService\n" \
98 " ret $0x%x\n\n"
99
100 /* For now, use the usermode stub. We'll optimize later */
101 #define KernelModeStub_ppc UserModeStub_ppc
102
103 #define KernelModeStub_mips " j KiSystemService\n" \
104 " nop\n"
105
106 #define KernelModeStub_arm " mov ip, lr\n" \
107 " swi #0x%x\n" \
108 " bx ip\n\n"
109
110 #elif defined(_MSC_VER)
111 #define KernelModeStub_x86 " asm { \n" \
112 " mov eax, %xh\n" \
113 " lea edx, [esp+4]\n" \
114 " pushf\n" \
115 " push KGDT_R0_CODE\n" \
116 " call _KiSystemService\n" \
117 " ret %xh\n" \
118 " }\n"
119 #else
120 #error Unknown compiler for inline assembler
121 #endif
122
123 /***** Arch Dependent Stuff ******/
124 struct ncitool_data_t {
125 const char *arch;
126 int args_to_bytes;
127 const char *km_stub;
128 const char *um_stub;
129 const char *global_header;
130 const char *declaration;
131 };
132
133 struct ncitool_data_t ncitool_data[] = {
134 { "i386", 4, KernelModeStub_x86, UserModeStub_x86,
135 ".global _%s@%d\n", "_%s@%d:\n" },
136 { "powerpc", 4, KernelModeStub_ppc, UserModeStub_ppc,
137 "\t.globl %s\n", "%s:\n" },
138 { "mips", 4, KernelModeStub_mips, UserModeStub_mips,
139 "\t.globl %s\n", "%s:\n" },
140 { "arm", 4, KernelModeStub_arm, UserModeStub_arm,
141 "\t.globl %s\n", "%s:\n" },
142 { 0, }
143 };
144 int arch_sel = 0;
145 #define ARGS_TO_BYTES(x) (x)*(ncitool_data[arch_sel].args_to_bytes)
146 #define UserModeStub ncitool_data[arch_sel].um_stub
147 #define KernelModeStub ncitool_data[arch_sel].km_stub
148 #define GlobalHeader ncitool_data[arch_sel].global_header
149 #define Declaration ncitool_data[arch_sel].declaration
150
151 /* FUNCTIONS ****************************************************************/
152
153 /*++
154 * WriteFileHeader
155 *
156 * Prints out the File Header for a Stub File.
157 *
158 * Params:
159 * StubFile - Stub File to which to write the header.
160 *
161 * FileDescription - Description of the Stub file to which to write the header.
162 *
163 * FileLocation - Name of the Stub file to which to write the header.
164 *
165 * Returns:
166 * None.
167 *
168 * Remarks:
169 * FileLocation is only used for printing the header.
170 *
171 *--*/
172 void
173 WriteFileHeader(FILE * StubFile,
174 char* FileDescription,
175 char* FileLocation)
176 {
177 /* This prints out the file header */
178 fprintf(StubFile,
179 "/* FILE: %s\n"
180 " * COPYRIGHT: See COPYING in the top level directory\n"
181 " * PURPOSE: %s\n"
182 " * PROGRAMMER: Computer Generated File. See tools/nci/ncitool.c\n"
183 " * REMARK: DO NOT EDIT OR COMMIT MODIFICATIONS TO THIS FILE\n"
184 " */\n\n\n"
185 "#include <ndk/asm.h>\n\n",
186 FileDescription,
187 FileLocation);
188 }
189
190 /*++
191 * WriteFileHeader
192 *
193 * Prints out the File Header for a Stub File.
194 *
195 * Params:
196 * StubFile - Stub File to which to write the header.
197 *
198 * FileDescription - Description of the Stub file to which to write the header.
199 *
200 * FileLocation - Name of the Stub file to which to write the header.
201 *
202 * Returns:
203 * None.
204 *
205 * Remarks:
206 * FileLocation is only used for printing the header.
207 *
208 *--*/
209 void
210 WriteStubHeader(FILE* StubFile,
211 char* SyscallName,
212 unsigned StackBytes)
213 {
214 /* Export the function */
215 fprintf(StubFile, GlobalHeader, SyscallName, StackBytes);
216
217 /* Define it */
218 fprintf(StubFile, Declaration, SyscallName, StackBytes);
219 }
220
221
222 /*++
223 * WriteKernelModeStub
224 *
225 * Prints out the Kernel Mode Stub for a System Call.
226 *
227 * Params:
228 * StubFile - Stub File to which to write the header.
229 *
230 * SyscallName - Name of System Call for which to add the stub.
231 *
232 * StackBytes - Number of bytes on the stack to return after doing the system call.
233 *
234 * SyscallId - Service Descriptor Table ID for this System Call.
235 *
236 * Returns:
237 * None.
238 *
239 * Remarks:
240 * On i386, StackBytes is the number of arguments x 4.
241 *
242 *--*/
243 void
244 WriteKernelModeStub(FILE* StubFile,
245 char* SyscallName,
246 unsigned StackBytes,
247 unsigned int SyscallId)
248 {
249 /* Write the Stub Header and export the Function */
250 WriteStubHeader(StubFile, SyscallName, StackBytes);
251
252 /* Write the Stub Code */
253 fprintf(StubFile, KernelModeStub, SyscallId, StackBytes);
254 }
255
256 /*++
257 * WriteUserModeStub
258 *
259 * Prints out the User Mode Stub for a System Call.
260 *
261 * Params:
262 * StubFile - Stub File to which to write the header.
263 *
264 * SyscallName - Name of System Call for which to add the stub.
265 *
266 * StackBytes - Number of bytes on the stack to return after doing the system call.
267 *
268 * SyscallId - Service Descriptor Table ID for this System Call.
269 *
270 * Returns:
271 * None.
272 *
273 * Remarks:
274 * On i386, StackBytes is the number of arguments x 4.
275 *
276 *--*/
277 void
278 WriteUserModeStub(FILE* StubFile,
279 char* SyscallName,
280 unsigned StackBytes,
281 unsigned int SyscallId)
282 {
283 /* Write the Stub Header and export the Function */
284 WriteStubHeader(StubFile, SyscallName, StackBytes);
285
286 /* Write the Stub Code */
287 fprintf(StubFile, UserModeStub, SyscallId, StackBytes);
288 }
289
290 /*++
291 * GetNameAndArgumentsFromDb
292 *
293 * Parses an entry from a System Call Database, extracting
294 * the function's name and arguments that it takes.
295 *
296 * Params:
297 * Line - Entry from the Database to parse.
298 *
299 * NtSyscallName - Output string to which to save the Function Name
300 *
301 * SyscallArguments - Output string to which to save the number of
302 * arguments that the function takes.
303 *
304 * Returns:
305 * None.
306 *
307 * Remarks:
308 * On i386, StackBytes is the number of arguments x 4.
309 *
310 *--*/
311 void
312 GetNameAndArgumentsFromDb(char Line[],
313 char ** NtSyscallName,
314 char ** SyscallArguments)
315 {
316 char *s;
317 char *stmp;
318
319 /* Remove new line */
320 if ((s = (char *) strchr(Line,'\r')) != NULL) {
321 *s = '\0';
322 }
323
324 /* Skip comments (#) and empty lines */
325 s = &Line[0];
326 if ((*s) != '#' && (*s) != '\0') {
327
328 /* Extract the NtXXX name */
329 *NtSyscallName = (char *)strtok(s," \t");
330
331 /* Extract the argument count */
332 *SyscallArguments = (char *)strtok(NULL," \t");
333
334 /* Remove, if present, the trailing LF */
335 if ((stmp = strchr(*SyscallArguments, '\n')) != NULL) {
336 *stmp = '\0';
337 }
338
339 } else {
340
341 /* Skip this entry */
342 *NtSyscallName = NULL;
343 *SyscallArguments = NULL;
344 }
345 }
346
347 /*++
348 * CreateStubs
349 *
350 * Parses a System Call Database and creates stubs for all the entries.
351 *
352 * Params:
353 * SyscallDb - System Call Database to parse.
354 *
355 * UserModeFiles - Array of Usermode Stub Files to which to write the stubs.
356 *
357 * KernelModeFile - Kernelmode Stub Files to which to write the stubs.
358 *
359 * Index - Number of first syscall
360 *
361 * UserFiles - Number of Usermode Stub Files to create
362 *
363 * NeedsZw - Write Zw prefix?
364 *
365 * Returns:
366 * None.
367 *
368 * Remarks:
369 * None.
370 *
371 *--*/
372 void
373 CreateStubs(FILE * SyscallDb,
374 FILE * UserModeFiles[],
375 FILE * KernelModeFile,
376 unsigned Index,
377 unsigned UserFiles,
378 unsigned NeedsZw)
379 {
380 char Line[INPUT_BUFFER_SIZE];
381 char *NtSyscallName;
382 char *SyscallArguments;
383 int SyscallId;
384 unsigned StackBytes;
385
386 /* We loop, incrementing the System Call Index, until the end of the file */
387 for (SyscallId = 0; ((!feof(SyscallDb)) && (fgets(Line, sizeof(Line), SyscallDb) != NULL));) {
388
389 /* Extract the Name and Arguments */
390 GetNameAndArgumentsFromDb(Line, &NtSyscallName, &SyscallArguments);
391 if (SyscallArguments != NULL)
392 StackBytes = ARGS_TO_BYTES(strtoul(SyscallArguments, NULL, 0));
393 else
394 StackBytes = 0;
395
396 /* Make sure we really extracted something */
397 if (NtSyscallName) {
398
399 /* Create Usermode Stubs for Nt/Zw syscalls in each Usermode file */
400 int i;
401 for (i= 0; i < UserFiles; i++) {
402
403 /* Write the Nt Version */
404 WriteUserModeStub(UserModeFiles[i],
405 NtSyscallName,
406 StackBytes,
407 SyscallId | Index);
408
409 /* If a Zw Version is needed (was specified), write it too */
410 if (NeedsZw) {
411
412 NtSyscallName[0] = 'Z';
413 NtSyscallName[1] = 'w';
414 WriteUserModeStub(UserModeFiles[i],
415 NtSyscallName,
416 StackBytes,
417 SyscallId | Index);
418 }
419
420 }
421
422 /* Create the Kernel coutnerparts (only Zw*, Nt* are the real functions!) */
423 if (KernelModeFile) {
424
425 NtSyscallName[0] = 'Z';
426 NtSyscallName[1] = 'w';
427 WriteKernelModeStub(KernelModeFile,
428 NtSyscallName,
429 StackBytes,
430 SyscallId | Index);
431 }
432
433 /* Only increase if we actually added something */
434 SyscallId++;
435 }
436 }
437 }
438
439 /*++
440 * CreateSystemServiceTable
441 *
442 * Parses a System Call Database and creates a System Call Service Table for it.
443 *
444 * Params:
445 * SyscallDb - System Call Database to parse.
446 *
447 * SyscallTable - File in where to create System Call Service Table.
448 *
449 * Name - Name of the Service Table.
450 *
451 * FileLocation - Filename containing the Table.
452 *
453 * Returns:
454 * None.
455 *
456 * Remarks:
457 * FileLocation is only used for the header generation.
458 *
459 *--*/
460 void
461 CreateSystemServiceTable(FILE *SyscallDb,
462 FILE *SyscallTable,
463 char * Name,
464 char * FileLocation)
465 {
466 char Line[INPUT_BUFFER_SIZE];
467 char *NtSyscallName;
468 char *SyscallArguments;
469 int SyscallId;
470
471 /* Print the Header */
472 WriteFileHeader(SyscallTable, "System Call Table for Native API", FileLocation);
473
474 /* First we build the SSDT */
475 fprintf(SyscallTable,"\n\n\n");
476 fprintf(SyscallTable,"ULONG_PTR %sSSDT[] = {\n", Name);
477
478 /* We loop, incrementing the System Call Index, until the end of the file */
479 for (SyscallId = 0; ((!feof(SyscallDb)) && (fgets(Line, sizeof(Line), SyscallDb) != NULL));) {
480
481 /* Extract the Name and Arguments */
482 GetNameAndArgumentsFromDb(Line, &NtSyscallName, &SyscallArguments);
483
484 /* Make sure we really extracted something */
485 if (NtSyscallName) {
486
487 /* Add a new line */
488 if (SyscallId > 0) fprintf(SyscallTable,",\n");
489
490 /* Write the syscall name in the service table. */
491 fprintf(SyscallTable,"\t\t(ULONG_PTR)%s", NtSyscallName);
492
493 /* Only increase if we actually added something */
494 SyscallId++;
495 }
496 }
497
498 /* Close the service table (C syntax) */
499 fprintf(SyscallTable,"\n};\n");
500
501 /* Now we build the SSPT */
502 rewind(SyscallDb);
503 fprintf(SyscallTable,"\n\n\n");
504 fprintf(SyscallTable,"UCHAR %sSSPT[] = {\n", Name);
505
506 for (SyscallId = 0; ((!feof(SyscallDb)) && (fgets(Line, sizeof(Line), SyscallDb) != NULL));) {
507
508 /* Extract the Name and Arguments */
509 GetNameAndArgumentsFromDb(Line, &NtSyscallName, &SyscallArguments);
510
511 /* Make sure we really extracted something */
512 if (NtSyscallName) {
513
514 /* Add a new line */
515 if (SyscallId > 0) fprintf(SyscallTable,",\n");
516
517 /* Write the syscall arguments in the argument table. */
518 if (SyscallArguments != NULL)
519 fprintf(SyscallTable,"\t\t%lu * sizeof(void *)",strtoul(SyscallArguments, NULL, 0));
520 else
521 fprintf(SyscallTable,"\t\t0");
522
523 /* Only increase if we actually added something */
524 SyscallId++;
525 }
526 }
527
528 /* Close the service table (C syntax) */
529 fprintf(SyscallTable,"\n};\n");
530
531 /*
532 * We write some useful defines
533 */
534 fprintf(SyscallTable, "\n\n#define MIN_SYSCALL_NUMBER 0\n");
535 fprintf(SyscallTable, "#define MAX_SYSCALL_NUMBER %d\n", SyscallId - 1);
536 fprintf(SyscallTable, "#define NUMBER_OF_SYSCALLS %d\n", SyscallId);
537 fprintf(SyscallTable, "ULONG %sNumberOfSysCalls = %d;\n", Name, SyscallId);
538 }
539
540 /*++
541 * WriteSpec
542 *
543 * Prints out the Spec Entry for a System Call.
544 *
545 * Params:
546 * SpecFile - Spec File to which to write the header.
547 *
548 * SyscallName - Name of System Call for which to add the stub.
549 *
550 * CountArguments - Number of arguments to the System Call.
551 *
552 * Returns:
553 * None.
554 *
555 * Remarks:
556 * None.
557 *
558 *--*/
559 void
560 WriteSpec(FILE* StubFile,
561 char* SyscallName,
562 unsigned CountArguments)
563 {
564 unsigned i;
565
566 fprintf(StubFile, "@ stdcall %s", SyscallName);
567
568 fputc ('(', StubFile);
569
570 for (i = 0; i < CountArguments; ++ i)
571 fputs ("ptr ", StubFile);
572
573 fputc (')', StubFile);
574 fputc ('\n', StubFile);
575 }
576
577 /*++
578 * CreateSpec
579 *
580 * Parses a System Call Database and creates a spec file for all the entries.
581 *
582 * Params:
583 * SyscallDb - System Call Database to parse.
584 *
585 * Files - Array of Spec Files to which to write.
586 *
587 * CountFiles - Number of Spec Files to create
588 *
589 * UseZw - Use Zw prefix?
590 *
591 * Returns:
592 * None.
593 *
594 * Remarks:
595 * None.
596 *
597 *--*/
598 void
599 CreateSpec(FILE * SyscallDb,
600 FILE * Files[],
601 unsigned CountFiles,
602 unsigned UseZw)
603 {
604 char Line[INPUT_BUFFER_SIZE];
605 char *NtSyscallName;
606 char *SyscallArguments;
607 unsigned CountArguments;
608
609 /* We loop until the end of the file */
610 while ((!feof(SyscallDb)) && (fgets(Line, sizeof(Line), SyscallDb) != NULL)) {
611
612 /* Extract the Name and Arguments */
613 GetNameAndArgumentsFromDb(Line, &NtSyscallName, &SyscallArguments);
614 CountArguments = strtoul(SyscallArguments, NULL, 0);
615
616 /* Make sure we really extracted something */
617 if (NtSyscallName) {
618
619 int i;
620 for (i= 0; i < CountFiles; i++) {
621
622 if (!UseZw) {
623 WriteSpec(Files[i],
624 NtSyscallName,
625 CountArguments);
626 }
627
628 if (UseZw && NtSyscallName[0] == 'N' && NtSyscallName[1] == 't') {
629
630 NtSyscallName[0] = 'Z';
631 NtSyscallName[1] = 'w';
632 WriteSpec(Files[i],
633 NtSyscallName,
634 CountArguments);
635 }
636
637 }
638 }
639 }
640 }
641
642 void usage(char * argv0)
643 {
644 printf("Usage: %s [-arch <arch>] sysfuncs.lst w32ksvc.db napi.h ssdt.h napi.S zw.S win32k.S win32k.S\n"
645 " sysfuncs.lst native system functions database\n"
646 " w32ksvc.db native graphic functions database\n"
647 " napi.h NTOSKRNL service table\n"
648 " ssdt.h WIN32K service table\n"
649 " napi.S NTDLL stubs\n"
650 " zw.S NTOSKRNL Zw stubs\n"
651 " win32k.S GDI32 stubs\n"
652 " win32k.S USER32 stubs\n"
653 " nt.pspec NTDLL exports\n"
654 " -arch is optional, default is %s\n",
655 argv0,
656 ncitool_data[0].arch
657 );
658 }
659
660 int main(int argc, char* argv[])
661 {
662 FILE * Files[Arguments] = { };
663 int FileNumber, ArgOffset = 1;
664 char * OpenType = "r";
665
666 /* Catch architecture argument */
667 if (argc > 3 && !strcmp(argv[1],"-arch")) {
668 for( arch_sel = 0; ncitool_data[arch_sel].arch; arch_sel++ )
669 if (strcmp(argv[2],ncitool_data[arch_sel].arch) == 0)
670 break;
671 if (!ncitool_data[arch_sel].arch) {
672 printf("Invalid arch '%s'\n", argv[2]);
673 usage(argv[0]);
674 return 1;
675 }
676 ArgOffset = 3;
677 }
678 /* Make sure all arguments all there */
679 if (argc != Arguments + ArgOffset) {
680 usage(argv[0]);
681 return(1);
682 }
683
684 /* Open all Output and bail out if any fail */
685 for (FileNumber = 0; FileNumber < Arguments; FileNumber++) {
686
687 /* Open the File */
688 if (FileNumber == 2) OpenType = "wb";
689 Files[FileNumber] = fopen(argv[FileNumber + ArgOffset], OpenType);
690
691 /* Check for failure and error out if so */
692 if (!Files[FileNumber]) {
693 perror(argv[FileNumber + ArgOffset]);
694 return (1);
695 }
696 }
697
698 /* Write the File Headers */
699 WriteFileHeader(Files[NtosUserStubs],
700 "System Call Stubs for Native API",
701 argv[NtosUserStubs + ArgOffset]);
702
703 WriteFileHeader(Files[NtosKernelStubs],
704 "System Call Stubs for Native API",
705 argv[NtosKernelStubs + ArgOffset]);
706 fputs("#include <ndk/asm.h>\n\n", Files[NtosKernelStubs]);
707
708 WriteFileHeader(Files[Win32kStubs],
709 "System Call Stubs for Native API",
710 argv[Win32kStubs + ArgOffset]);
711
712 /* Create the System Stubs */
713 CreateStubs(Files[NativeSystemDb],
714 &Files[NtosUserStubs],
715 Files[NtosKernelStubs],
716 MAIN_INDEX,
717 1,
718 1);
719
720 /* Create the Graphics Stubs */
721 CreateStubs(Files[NativeGuiDb],
722 &Files[Win32kStubs],
723 NULL,
724 WIN32K_INDEX,
725 1,
726 0);
727
728 /* Create the Service Tables */
729 rewind(Files[NativeSystemDb]);
730 CreateSystemServiceTable(Files[NativeSystemDb],
731 Files[NtosServiceTable],
732 "Main",
733 argv[NtosServiceTable + ArgOffset]);
734
735 rewind(Files[NativeGuiDb]);
736 CreateSystemServiceTable(Files[NativeGuiDb],
737 Files[Win32kServiceTable],
738 "Win32k",
739 argv[Win32kServiceTable + ArgOffset]);
740
741 /* Create the Spec Files */
742 rewind(Files[NativeSystemDb]);
743 CreateSpec(Files[NativeSystemDb],
744 &Files[NtSpec],
745 1,
746 0);
747
748 rewind(Files[NativeSystemDb]);
749 CreateSpec(Files[NativeSystemDb],
750 &Files[NtSpec],
751 1,
752 1);
753
754 /* Close all files */
755 for (FileNumber = 0; FileNumber < Arguments-ArgOffset; FileNumber++) {
756
757 /* Close the File */
758 fclose(Files[FileNumber]);
759
760 }
761
762 return(0);
763 }