Stragglers plus warning fixen.
[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 #ifndef __FreeBSD__
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 /* First, define the Databases */
32 #define NativeSystemDb 0
33 #define NativeGuiDb 1
34
35 /* Now the Service Tables */
36 #define NtosServiceTable 2
37 #define Win32kServiceTable 3
38
39 /* And finally, the stub files. */
40 #define NtosUserStubs 4
41 #define NtosKernelStubs 5
42 #define Win32kGdiStubs 6
43 #define Win32kUserStubs 7
44
45 /********** Stub Code ************/
46
47 /*
48 * This stubs calls into KUSER_SHARED_DATA where either a
49 * sysenter or interrupt is performed, depending on CPU support.
50 */
51 #if defined(__GNUC__)
52 #define UserModeStub_x86 " movl $0x%x, %%eax\n" \
53 " movl $KUSER_SHARED_SYSCALL, %%ecx\n" \
54 " call *%%ecx\n" \
55 " ret $0x%x\n\n"
56
57 #define UserModeStub_ppc " mflr 0\n" \
58 " addi 1,1,-16\n" \
59 " li 0,%x\n" \
60 " stw 0,1(0)\n" \
61 " sc\n" \
62 " lwz 0,1(0)\n" \
63 " mtlr 0\n" \
64 " addi 1,1,16\n" \
65 " blr\n"
66 #elif defined(_MSC_VER)
67 #define UserModeStub_x86 " asm { \n" \
68 " mov eax, %xh\n" \
69 " mov ecx, KUSER_SHARED_SYSCALL\n" \
70 " call [ecx]\n" \
71 " ret %xh\n" \
72 " }\n"
73 #else
74 #error Unknown compiler for inline assembler
75 #endif
76
77 /*
78 * This stub calls KiSystemService directly with a fake INT2E stack.
79 * Because EIP is pushed during the call, the handler will return here.
80 */
81 #if defined(__GNUC__)
82 #define KernelModeStub_x86 " movl $0x%x, %%eax\n" \
83 " leal 4(%%esp), %%edx\n" \
84 " pushfl\n" \
85 " pushl $KGDT_R0_CODE\n" \
86 " call _KiSystemService\n" \
87 " ret $0x%x\n\n"
88
89 #define KernelModeStub_ppc " bl KiSystemService\n" \
90 " rfi\n"
91 #elif defined(_MSC_VER)
92 #define KernelModeStub_x86 " asm { \n" \
93 " mov eax, %xh\n" \
94 " lea edx, [esp+4]\n" \
95 " pushf\n" \
96 " push KGDT_R0_CODE\n" \
97 " call _KiSystemService\n" \
98 " ret %xh\n" \
99 " }\n"
100 #else
101 #error Unknown compiler for inline assembler
102 #endif
103
104 /***** Arch Dependent Stuff ******/
105 struct ncitool_data_t {
106 const char *arch;
107 int args_to_bytes;
108 const char *km_stub;
109 const char *um_stub;
110 const char *global_header;
111 const char *declaration;
112 };
113
114 struct ncitool_data_t ncitool_data[] = {
115 { "x86", 4, KernelModeStub_x86, UserModeStub_x86,
116 ".global _%s@%d\n", "_%s@%d:\n" },
117 { "powerpc", 4, KernelModeStub_ppc, UserModeStub_ppc,
118 "\t.globl %s\n", "%s:\n" },
119 { 0 }
120 };
121 int arch_sel = 0;
122 #define ARGS_TO_BYTES(x) 4*(ncitool_data[arch_sel].args_to_bytes)
123 #define UserModeStub ncitool_data[arch_sel].um_stub
124 #define KernelModeStub ncitool_data[arch_sel].km_stub
125 #define GlobalHeader ncitool_data[arch_sel].global_header
126 #define Declaration ncitool_data[arch_sel].declaration
127
128 /* FUNCTIONS ****************************************************************/
129
130 /*++
131 * WriteFileHeader
132 *
133 * Prints out the File Header for a Stub File.
134 *
135 * Params:
136 * StubFile - Stub File to which to write the header.
137 *
138 * FileDescription - Description of the Stub file to which to write the header.
139 *
140 * FileLocation - Name of the Stub file to which to write the header.
141 *
142 * Returns:
143 * None.
144 *
145 * Remarks:
146 * FileLocation is only used for printing the header.
147 *
148 *--*/
149 void
150 WriteFileHeader(FILE * StubFile,
151 char* FileDescription,
152 char* FileLocation)
153 {
154 /* This prints out the file header */
155 fprintf(StubFile,
156 "/* FILE: %s\n"
157 " * COPYRIGHT: See COPYING in the top level directory\n"
158 " * PURPOSE: %s\n"
159 " * PROGRAMMER: Computer Generated File. See tools/nci/ncitool.c\n"
160 " * REMARK: DO NOT EDIT OR COMMIT MODIFICATIONS TO THIS FILE\n"
161 " */\n\n\n"
162 "#include <ndk/asm.h>\n\n",
163 FileDescription,
164 FileLocation);
165 }
166
167 /*++
168 * WriteFileHeader
169 *
170 * Prints out the File Header for a Stub File.
171 *
172 * Params:
173 * StubFile - Stub File to which to write the header.
174 *
175 * FileDescription - Description of the Stub file to which to write the header.
176 *
177 * FileLocation - Name of the Stub file to which to write the header.
178 *
179 * Returns:
180 * None.
181 *
182 * Remarks:
183 * FileLocation is only used for printing the header.
184 *
185 *--*/
186 void
187 WriteStubHeader(FILE* StubFile,
188 char* SyscallName,
189 unsigned StackBytes)
190 {
191 /* Export the function */
192 fprintf(StubFile, GlobalHeader, SyscallName, StackBytes);
193
194 /* Define it */
195 fprintf(StubFile, Declaration, SyscallName, StackBytes);
196 }
197
198
199 /*++
200 * WriteKernelModeStub
201 *
202 * Prints out the Kernel Mode Stub for a System Call.
203 *
204 * Params:
205 * StubFile - Stub File to which to write the header.
206 *
207 * SyscallName - Name of System Call for which to add the stub.
208 *
209 * StackBytes - Number of bytes on the stack to return after doing the system call.
210 *
211 * SyscallId - Service Descriptor Table ID for this System Call.
212 *
213 * Returns:
214 * None.
215 *
216 * Remarks:
217 * On i386, StackBytes is the number of arguments x 4.
218 *
219 *--*/
220 void
221 WriteKernelModeStub(FILE* StubFile,
222 char* SyscallName,
223 unsigned StackBytes,
224 unsigned int SyscallId)
225 {
226 /* Write the Stub Header and export the Function */
227 WriteStubHeader(StubFile, SyscallName, StackBytes);
228
229 /* Write the Stub Code */
230 fprintf(StubFile, KernelModeStub, SyscallId, StackBytes);
231 }
232
233 /*++
234 * WriteUserModeStub
235 *
236 * Prints out the User Mode Stub for a System Call.
237 *
238 * Params:
239 * StubFile - Stub File to which to write the header.
240 *
241 * SyscallName - Name of System Call for which to add the stub.
242 *
243 * StackBytes - Number of bytes on the stack to return after doing the system call.
244 *
245 * SyscallId - Service Descriptor Table ID for this System Call.
246 *
247 * Returns:
248 * None.
249 *
250 * Remarks:
251 * On i386, StackBytes is the number of arguments x 4.
252 *
253 *--*/
254 void
255 WriteUserModeStub(FILE* StubFile,
256 char* SyscallName,
257 unsigned StackBytes,
258 unsigned int SyscallId)
259 {
260 /* Write the Stub Header and export the Function */
261 WriteStubHeader(StubFile, SyscallName, StackBytes);
262
263 /* Write the Stub Code */
264 fprintf(StubFile, UserModeStub, SyscallId, StackBytes);
265 }
266
267 /*++
268 * GetNameAndArgumentsFromDb
269 *
270 * Parses an entry from a System Call Database, extracting
271 * the function's name and arguments that it takes.
272 *
273 * Params:
274 * Line - Entry from the Database to parse.
275 *
276 * NtSyscallName - Output string to which to save the Function Name
277 *
278 * SyscallArguments - Output string to which to save the number of
279 * arguments that the function takes.
280 *
281 * Returns:
282 * None.
283 *
284 * Remarks:
285 * On i386, StackBytes is the number of arguments x 4.
286 *
287 *--*/
288 void
289 GetNameAndArgumentsFromDb(char Line[],
290 char ** NtSyscallName,
291 char ** SyscallArguments)
292 {
293 char *s;
294 char *stmp;
295
296 /* Remove new line */
297 if ((s = (char *) strchr(Line,'\r')) != NULL) {
298 *s = '\0';
299 }
300
301 /* Skip comments (#) and empty lines */
302 s = &Line[0];
303 if ((*s) != '#' && (*s) != '\0') {
304
305 /* Extract the NtXXX name */
306 *NtSyscallName = (char *)strtok(s," \t");
307
308 /* Extract the argument count */
309 *SyscallArguments = (char *)strtok(NULL," \t");
310
311 /* Remove, if present, the trailing LF */
312 if ((stmp = strchr(*SyscallArguments, '\n')) != NULL) {
313 *stmp = '\0';
314 }
315
316 } else {
317
318 /* Skip this entry */
319 *NtSyscallName = NULL;
320 *SyscallArguments = NULL;
321 }
322 }
323
324 /*++
325 * CreateStubs
326 *
327 * Parses a System Call Database and creates stubs for all the entries.
328 *
329 * Params:
330 * SyscallDb - System Call Database to parse.
331 *
332 * UserModeFiles - Array of Usermode Stub Files to which to write the stubs.
333 *
334 * KernelModeFile - Kernelmode Stub Files to which to write the stubs.
335 *
336 * Index - Name of System Call for which to add the stub.
337 *
338 * UserFiles - Number of bytes on the stack to return after doing the system call.
339 *
340 * NeedsZw - Service Descriptor Table ID for this System Call.
341 *
342 * Returns:
343 * None.
344 *
345 * Remarks:
346 * None.
347 *
348 *--*/
349 void
350 CreateStubs(FILE * SyscallDb,
351 FILE * UserModeFiles[],
352 FILE * KernelModeFile,
353 unsigned Index,
354 unsigned UserFiles,
355 unsigned NeedsZw)
356 {
357 char Line[INPUT_BUFFER_SIZE];
358 char *NtSyscallName;
359 char *SyscallArguments;
360 int SyscallId;
361 unsigned StackBytes;
362
363 /* We loop, incrementing the System Call Index, until the end of the file */
364 for (SyscallId = 0; ((!feof(SyscallDb)) && (fgets(Line, sizeof(Line), SyscallDb) != NULL));) {
365
366 /* Extract the Name and Arguments */
367 GetNameAndArgumentsFromDb(Line, &NtSyscallName, &SyscallArguments);
368 if (SyscallArguments != NULL)
369 StackBytes = ARGS_TO_BYTES(strtoul(SyscallArguments, NULL, 0));
370 else
371 StackBytes = 0;
372
373 /* Make sure we really extracted something */
374 if (NtSyscallName) {
375
376 /* Create Usermode Stubs for Nt/Zw syscalls in each Usermode file */
377 int i;
378 for (i= 0; i < UserFiles; i++) {
379
380 /* Write the Nt Version */
381 WriteUserModeStub(UserModeFiles[i],
382 NtSyscallName,
383 StackBytes,
384 SyscallId | Index);
385
386 /* If a Zw Version is needed (was specified), write it too */
387 if (NeedsZw) {
388
389 NtSyscallName[0] = 'Z';
390 NtSyscallName[1] = 'w';
391 WriteUserModeStub(UserModeFiles[i],
392 NtSyscallName,
393 StackBytes,
394 SyscallId | Index);
395 }
396
397 }
398
399 /* Create the Kernel coutnerparts (only Zw*, Nt* are the real functions!) */
400 if (KernelModeFile) {
401
402 NtSyscallName[0] = 'Z';
403 NtSyscallName[1] = 'w';
404 WriteKernelModeStub(KernelModeFile,
405 NtSyscallName,
406 StackBytes,
407 SyscallId | Index);
408 }
409
410 /* Only increase if we actually added something */
411 SyscallId++;
412 }
413 }
414 }
415
416 /*++
417 * CreateSystemServiceTable
418 *
419 * Parses a System Call Database and creates a System Call Service Table for it.
420 *
421 * Params:
422 * SyscallDb - System Call Database to parse.
423 *
424 * SyscallTable - File in where to create System Call Service Table.
425 *
426 * Name - Name of the Service Table.
427 *
428 * FileLocation - Filename containing the Table.
429 *
430 * Returns:
431 * None.
432 *
433 * Remarks:
434 * FileLocation is only used for the header generation.
435 *
436 *--*/
437 void
438 CreateSystemServiceTable(FILE *SyscallDb,
439 FILE *SyscallTable,
440 char * Name,
441 char * FileLocation)
442 {
443 char Line[INPUT_BUFFER_SIZE];
444 char *NtSyscallName;
445 char *SyscallArguments;
446 int SyscallId;
447
448 /* Print the Header */
449 WriteFileHeader(SyscallTable, "System Call Table for Native API", FileLocation);
450
451 /* First we build the SSDT */
452 fprintf(SyscallTable,"\n\n\n");
453 fprintf(SyscallTable,"ULONG_PTR %sSSDT[] = {\n", Name);
454
455 /* We loop, incrementing the System Call Index, until the end of the file */
456 for (SyscallId = 0; ((!feof(SyscallDb)) && (fgets(Line, sizeof(Line), SyscallDb) != NULL));) {
457
458 /* Extract the Name and Arguments */
459 GetNameAndArgumentsFromDb(Line, &NtSyscallName, &SyscallArguments);
460
461 /* Make sure we really extracted something */
462 if (NtSyscallName) {
463
464 /* Add a new line */
465 if (SyscallId > 0) fprintf(SyscallTable,",\n");
466
467 /* Write the syscall name in the service table. */
468 fprintf(SyscallTable,"\t\t(ULONG_PTR)%s", NtSyscallName);
469
470 /* Only increase if we actually added something */
471 SyscallId++;
472 }
473 }
474
475 /* Close the service table (C syntax) */
476 fprintf(SyscallTable,"\n};\n");
477
478 /* Now we build the SSPT */
479 rewind(SyscallDb);
480 fprintf(SyscallTable,"\n\n\n");
481 fprintf(SyscallTable,"UCHAR %sSSPT[] = {\n", Name);
482
483 for (SyscallId = 0; ((!feof(SyscallDb)) && (fgets(Line, sizeof(Line), SyscallDb) != NULL));) {
484
485 /* Extract the Name and Arguments */
486 GetNameAndArgumentsFromDb(Line, &NtSyscallName, &SyscallArguments);
487
488 /* Make sure we really extracted something */
489 if (NtSyscallName) {
490
491 /* Add a new line */
492 if (SyscallId > 0) fprintf(SyscallTable,",\n");
493
494 /* Write the syscall arguments in the argument table. */
495 if (SyscallArguments != NULL)
496 fprintf(SyscallTable,"\t\t%lu * sizeof(void *)",strtoul(SyscallArguments, NULL, 0));
497 else
498 fprintf(SyscallTable,"\t\t0");
499
500 /* Only increase if we actually added something */
501 SyscallId++;
502 }
503 }
504
505 /* Close the service table (C syntax) */
506 fprintf(SyscallTable,"\n};\n");
507
508 /*
509 * We write some useful defines
510 */
511 fprintf(SyscallTable, "\n\n#define MIN_SYSCALL_NUMBER 0\n");
512 fprintf(SyscallTable, "#define MAX_SYSCALL_NUMBER %d\n", SyscallId - 1);
513 fprintf(SyscallTable, "#define NUMBER_OF_SYSCALLS %d\n", SyscallId);
514 fprintf(SyscallTable, "ULONG %sNumberOfSysCalls = %d;\n", Name, SyscallId);
515 }
516
517 void usage(char * argv0)
518 {
519 printf("Usage: %s [-arch <arch>] sysfuncs.lst w32ksvc.db napi.h ssdt.h napi.S zw.S win32k.S win32k.S\n"
520 " sysfuncs.lst native system functions database\n"
521 " w32ksvc.db native graphic functions database\n"
522 " napi.h NTOSKRNL service table\n"
523 " ssdt.h WIN32K service table\n"
524 " napi.S NTDLL stubs\n"
525 " zw.S NTOSKRNL Zw stubs\n"
526 " win32k.S GDI32 stubs\n"
527 " win32k.S USER32 stubs\n"
528 " -arch is optional, default is x86\n",
529 argv0
530 );
531 }
532
533 int main(int argc, char* argv[])
534 {
535 FILE * Files[Arguments] = { };
536 int FileNumber, ArgOffset = 1;
537 char * OpenType = "r";
538
539 /* Catch architecture argument */
540 if (argc > 3 && !strcmp(argv[1],"-arch")) {
541 int i;
542 for( i = 0; ncitool_data[arch_sel].arch && strcmp(argv[2],ncitool_data[i].arch); i++ );
543 if (!ncitool_data[arch_sel].arch) {
544 usage(argv[0]);
545 return 1;
546 }
547 arch_sel = i;
548 ArgOffset = 3;
549 }
550 /* Make sure all arguments all there */
551 if (argc != Arguments + ArgOffset) {
552 usage(argv[0]);
553 return(1);
554 }
555
556 /* Open all Output and bail out if any fail */
557 for (FileNumber = 0; FileNumber < Arguments; FileNumber++) {
558
559 /* Open the File */
560 if (FileNumber == 2) OpenType = "wb";
561 Files[FileNumber] = fopen(argv[FileNumber + ArgOffset], OpenType);
562
563 /* Check for failure and error out if so */
564 if (!Files[FileNumber]) {
565 perror(argv[FileNumber + ArgOffset]);
566 return (1);
567 }
568
569 }
570
571 /* Write the File Headers */
572 WriteFileHeader(Files[NtosUserStubs],
573 "System Call Stubs for Native API",
574 argv[NtosUserStubs + ArgOffset]);
575
576 WriteFileHeader(Files[NtosKernelStubs],
577 "System Call Stubs for Native API",
578 argv[NtosKernelStubs + ArgOffset]);
579 fputs("#include <ndk/asm.h>\n\n", Files[NtosKernelStubs]);
580
581 WriteFileHeader(Files[Win32kGdiStubs],
582 "System Call Stubs for Native API",
583 argv[Win32kGdiStubs + ArgOffset]);
584
585 WriteFileHeader(Files[Win32kUserStubs],
586 "System Call Stubs for Native API",
587 argv[Win32kUserStubs + ArgOffset]);
588
589
590 /* Create the System Stubs */
591 CreateStubs(Files[NativeSystemDb],
592 &Files[NtosUserStubs],
593 Files[NtosKernelStubs],
594 MAIN_INDEX,
595 1,
596 1);
597
598 /* Create the Graphics Stubs */
599 CreateStubs(Files[NativeGuiDb],
600 &Files[Win32kGdiStubs],
601 NULL,
602 WIN32K_INDEX,
603 2,
604 0);
605
606 /* Rewind the databases */
607 rewind(Files[NativeSystemDb]);
608 rewind(Files[NativeGuiDb]);
609
610 /* Create the Service Tables */
611 CreateSystemServiceTable(Files[NativeSystemDb],
612 Files[NtosServiceTable],
613 "Main",
614 argv[NtosServiceTable + ArgOffset]);
615
616 CreateSystemServiceTable(Files[NativeGuiDb],
617 Files[Win32kServiceTable],
618 "Win32k",
619 argv[Win32kServiceTable + ArgOffset]);
620
621 /* Close all files */
622 for (FileNumber = 0; FileNumber < Arguments-ArgOffset; FileNumber++) {
623
624 /* Close the File */
625 fclose(Files[FileNumber]);
626
627 }
628
629 return(0);
630 }