glapi: Harden GLX request size processing (v2)
[mesa.git] / src / mapi / glapi / gen / glX_proto_size.py
1 #!/usr/bin/env python
2
3 # (C) Copyright IBM Corporation 2004, 2005
4 # All Rights Reserved.
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining a
7 # copy of this software and associated documentation files (the "Software"),
8 # to deal in the Software without restriction, including without limitation
9 # on the rights to use, copy, modify, merge, publish, distribute, sub
10 # license, and/or sell copies of the Software, and to permit persons to whom
11 # the Software is furnished to do so, subject to the following conditions:
12 #
13 # The above copyright notice and this permission notice (including the next
14 # paragraph) shall be included in all copies or substantial portions of the
15 # Software.
16 #
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20 # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23 # IN THE SOFTWARE.
24 #
25 # Authors:
26 # Ian Romanick <idr@us.ibm.com>
27
28 import argparse
29 import sys, string
30
31 import gl_XML, glX_XML
32 import license
33
34
35 class glx_enum_function(object):
36 def __init__(self, func_name, enum_dict):
37 self.name = func_name
38 self.mode = 1
39 self.sig = None
40
41 # "enums" is a set of lists. The element in the set is the
42 # value of the enum. The list is the list of names for that
43 # value. For example, [0x8126] = {"POINT_SIZE_MIN",
44 # "POINT_SIZE_MIN_ARB", "POINT_SIZE_MIN_EXT",
45 # "POINT_SIZE_MIN_SGIS"}.
46
47 self.enums = {}
48
49 # "count" is indexed by count values. Each element of count
50 # is a list of index to "enums" that have that number of
51 # associated data elements. For example, [4] =
52 # {GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION,
53 # GL_AMBIENT_AND_DIFFUSE} (the enum names are used here,
54 # but the actual hexadecimal values would be in the array).
55
56 self.count = {}
57
58
59 # Fill self.count and self.enums using the dictionary of enums
60 # that was passed in. The generic Get functions (e.g.,
61 # GetBooleanv and friends) are handled specially here. In
62 # the data the generic Get functions are referred to as "Get".
63
64 if func_name in ["GetIntegerv", "GetBooleanv", "GetFloatv", "GetDoublev"]:
65 match_name = "Get"
66 else:
67 match_name = func_name
68
69 mode_set = 0
70 for enum_name in enum_dict:
71 e = enum_dict[ enum_name ]
72
73 if e.functions.has_key( match_name ):
74 [count, mode] = e.functions[ match_name ]
75
76 if mode_set and mode != self.mode:
77 raise RuntimeError("Not all enums for %s have the same mode." % (func_name))
78
79 self.mode = mode
80
81 if self.enums.has_key( e.value ):
82 if e.name not in self.enums[ e.value ]:
83 self.enums[ e.value ].append( e )
84 else:
85 if not self.count.has_key( count ):
86 self.count[ count ] = []
87
88 self.enums[ e.value ] = [ e ]
89 self.count[ count ].append( e.value )
90
91
92 return
93
94
95 def signature( self ):
96 if self.sig == None:
97 self.sig = ""
98 for i in self.count:
99 if i == None:
100 raise RuntimeError("i is None. WTF?")
101
102 self.count[i].sort()
103 for e in self.count[i]:
104 self.sig += "%04x,%d," % (e, i)
105
106 return self.sig
107
108
109 def is_set( self ):
110 return self.mode
111
112
113 def PrintUsingTable(self):
114 """Emit the body of the __gl*_size function using a pair
115 of look-up tables and a mask. The mask is calculated such
116 that (e & mask) is unique for all the valid values of e for
117 this function. The result of (e & mask) is used as an index
118 into the first look-up table. If it matches e, then the
119 same entry of the second table is returned. Otherwise zero
120 is returned.
121
122 It seems like this should cause better code to be generated.
123 However, on x86 at least, the resulting .o file is about 20%
124 larger then the switch-statment version. I am leaving this
125 code in because the results may be different on other
126 platforms (e.g., PowerPC or x86-64)."""
127
128 return 0
129 count = 0
130 for a in self.enums:
131 count += 1
132
133 if self.count.has_key(-1):
134 return 0
135
136 # Determine if there is some mask M, such that M = (2^N) - 1,
137 # that will generate unique values for all of the enums.
138
139 mask = 0
140 for i in [1, 2, 3, 4, 5, 6, 7, 8]:
141 mask = (1 << i) - 1
142
143 fail = 0;
144 for a in self.enums:
145 for b in self.enums:
146 if a != b:
147 if (a & mask) == (b & mask):
148 fail = 1;
149
150 if not fail:
151 break;
152 else:
153 mask = 0
154
155 if (mask != 0) and (mask < (2 * count)):
156 masked_enums = {}
157 masked_count = {}
158
159 for i in range(0, mask + 1):
160 masked_enums[i] = "0";
161 masked_count[i] = 0;
162
163 for c in self.count:
164 for e in self.count[c]:
165 i = e & mask
166 enum_obj = self.enums[e][0]
167 masked_enums[i] = '0x%04x /* %s */' % (e, enum_obj.name )
168 masked_count[i] = c
169
170
171 print ' static const GLushort a[%u] = {' % (mask + 1)
172 for e in masked_enums:
173 print ' %s, ' % (masked_enums[e])
174 print ' };'
175
176 print ' static const GLubyte b[%u] = {' % (mask + 1)
177 for c in masked_count:
178 print ' %u, ' % (masked_count[c])
179 print ' };'
180
181 print ' const unsigned idx = (e & 0x%02xU);' % (mask)
182 print ''
183 print ' return (e == a[idx]) ? (GLint) b[idx] : 0;'
184 return 1;
185 else:
186 return 0;
187
188
189 def PrintUsingSwitch(self, name):
190 """Emit the body of the __gl*_size function using a
191 switch-statement."""
192
193 print ' switch( e ) {'
194
195 for c in self.count:
196 for e in self.count[c]:
197 first = 1
198
199 # There may be multiple enums with the same
200 # value. This happens has extensions are
201 # promoted from vendor-specific or EXT to
202 # ARB and to the core. Emit the first one as
203 # a case label, and emit the others as
204 # commented-out case labels.
205
206 list = {}
207 for enum_obj in self.enums[e]:
208 list[ enum_obj.priority() ] = enum_obj.name
209
210 keys = list.keys()
211 keys.sort()
212 for k in keys:
213 j = list[k]
214 if first:
215 print ' case GL_%s:' % (j)
216 first = 0
217 else:
218 print '/* case GL_%s:*/' % (j)
219
220 if c == -1:
221 print ' return __gl%s_variable_size( e );' % (name)
222 else:
223 print ' return %u;' % (c)
224
225 print ' default: return 0;'
226 print ' }'
227
228
229 def Print(self, name):
230 print '_X_INTERNAL PURE FASTCALL GLint'
231 print '__gl%s_size( GLenum e )' % (name)
232 print '{'
233
234 if not self.PrintUsingTable():
235 self.PrintUsingSwitch(name)
236
237 print '}'
238 print ''
239
240
241 class glx_server_enum_function(glx_enum_function):
242 def __init__(self, func, enum_dict):
243 glx_enum_function.__init__(self, func.name, enum_dict)
244
245 self.function = func
246 return
247
248
249 def signature( self ):
250 if self.sig == None:
251 sig = glx_enum_function.signature(self)
252
253 p = self.function.variable_length_parameter()
254 if p:
255 sig += "%u" % (p.size())
256
257 self.sig = sig
258
259 return self.sig;
260
261
262 def Print(self, name, printer):
263 f = self.function
264 printer.common_func_print_just_header( f )
265
266 fixup = []
267
268 foo = {}
269 for param_name in f.count_parameter_list:
270 o = f.offset_of( param_name )
271 foo[o] = param_name
272
273 for param_name in f.counter_list:
274 o = f.offset_of( param_name )
275 foo[o] = param_name
276
277 keys = foo.keys()
278 keys.sort()
279 for o in keys:
280 p = f.parameters_by_name[ foo[o] ]
281
282 printer.common_emit_one_arg(p, "pc", 0)
283 fixup.append( p.name )
284
285
286 print ' GLsizei compsize;'
287 print ''
288
289 printer.common_emit_fixups(fixup)
290
291 print ''
292 print ' compsize = __gl%s_size(%s);' % (f.name, string.join(f.count_parameter_list, ","))
293 p = f.variable_length_parameter()
294 print ' return safe_pad(%s);' % (p.size_string())
295
296 print '}'
297 print ''
298
299
300 class PrintGlxSizeStubs_common(gl_XML.gl_print_base):
301 do_get = (1 << 0)
302 do_set = (1 << 1)
303
304 def __init__(self, which_functions):
305 gl_XML.gl_print_base.__init__(self)
306
307 self.name = "glX_proto_size.py (from Mesa)"
308 self.license = license.bsd_license_template % ( "(C) Copyright IBM Corporation 2004", "IBM")
309
310 self.emit_set = ((which_functions & PrintGlxSizeStubs_common.do_set) != 0)
311 self.emit_get = ((which_functions & PrintGlxSizeStubs_common.do_get) != 0)
312 return
313
314
315 class PrintGlxSizeStubs_c(PrintGlxSizeStubs_common):
316 def printRealHeader(self):
317 print ''
318 print '#include <X11/Xfuncproto.h>'
319 print '#include <GL/gl.h>'
320 if self.emit_get:
321 print '#include "indirect_size_get.h"'
322 print '#include "glxserver.h"'
323 print '#include "indirect_util.h"'
324
325 print '#include "indirect_size.h"'
326
327 print ''
328 self.printPure()
329 print ''
330 self.printFastcall()
331 print ''
332 print ''
333 print '#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(GLX_USE_APPLEGL)'
334 print '# undef HAVE_ALIAS'
335 print '#endif'
336 print '#ifdef HAVE_ALIAS'
337 print '# define ALIAS2(from,to) \\'
338 print ' _X_INTERNAL PURE FASTCALL GLint __gl ## from ## _size( GLenum e ) \\'
339 print ' __attribute__ ((alias( # to )));'
340 print '# define ALIAS(from,to) ALIAS2( from, __gl ## to ## _size )'
341 print '#else'
342 print '# define ALIAS(from,to) \\'
343 print ' _X_INTERNAL PURE FASTCALL GLint __gl ## from ## _size( GLenum e ) \\'
344 print ' { return __gl ## to ## _size( e ); }'
345 print '#endif'
346 print ''
347 print ''
348
349
350 def printBody(self, api):
351 enum_sigs = {}
352 aliases = []
353
354 for func in api.functionIterateGlx():
355 ef = glx_enum_function( func.name, api.enums_by_name )
356 if len(ef.enums) == 0:
357 continue
358
359 if (ef.is_set() and self.emit_set) or (not ef.is_set() and self.emit_get):
360 sig = ef.signature()
361 if enum_sigs.has_key( sig ):
362 aliases.append( [func.name, enum_sigs[ sig ]] )
363 else:
364 enum_sigs[ sig ] = func.name
365 ef.Print( func.name )
366
367
368 for [alias_name, real_name] in aliases:
369 print 'ALIAS( %s, %s )' % (alias_name, real_name)
370
371
372
373 class PrintGlxSizeStubs_h(PrintGlxSizeStubs_common):
374 def printRealHeader(self):
375 print """/**
376 * \\file
377 * Prototypes for functions used to determine the number of data elements in
378 * various GLX protocol messages.
379 *
380 * \\author Ian Romanick <idr@us.ibm.com>
381 */
382 """
383 print '#include <X11/Xfuncproto.h>'
384 print ''
385 self.printPure();
386 print ''
387 self.printFastcall();
388 print ''
389
390
391 def printBody(self, api):
392 for func in api.functionIterateGlx():
393 ef = glx_enum_function( func.name, api.enums_by_name )
394 if len(ef.enums) == 0:
395 continue
396
397 if (ef.is_set() and self.emit_set) or (not ef.is_set() and self.emit_get):
398 print 'extern _X_INTERNAL PURE FASTCALL GLint __gl%s_size(GLenum);' % (func.name)
399
400
401 class PrintGlxReqSize_common(gl_XML.gl_print_base):
402 """Common base class for PrintGlxSizeReq_h and PrintGlxSizeReq_h.
403
404 The main purpose of this common base class is to provide the infrastructure
405 for the derrived classes to iterate over the same set of functions.
406 """
407
408 def __init__(self):
409 gl_XML.gl_print_base.__init__(self)
410
411 self.name = "glX_proto_size.py (from Mesa)"
412 self.license = license.bsd_license_template % ( "(C) Copyright IBM Corporation 2005", "IBM")
413
414
415 class PrintGlxReqSize_h(PrintGlxReqSize_common):
416 def __init__(self):
417 PrintGlxReqSize_common.__init__(self)
418 self.header_tag = "_INDIRECT_REQSIZE_H_"
419
420
421 def printRealHeader(self):
422 print '#include <X11/Xfuncproto.h>'
423 print ''
424 self.printPure()
425 print ''
426
427
428 def printBody(self, api):
429 for func in api.functionIterateGlx():
430 if not func.ignore and func.has_variable_size_request():
431 print 'extern PURE _X_HIDDEN int __glX%sReqSize(const GLbyte *pc, Bool swap, int reqlen);' % (func.name)
432
433
434 class PrintGlxReqSize_c(PrintGlxReqSize_common):
435 """Create the server-side 'request size' functions.
436
437 Create the server-side functions that are used to determine what the
438 size of a varible length command should be. The server then uses
439 this value to determine if the incoming command packed it malformed.
440 """
441
442 def __init__(self):
443 PrintGlxReqSize_common.__init__(self)
444 self.counter_sigs = {}
445
446
447 def printRealHeader(self):
448 print ''
449 print '#include <GL/gl.h>'
450 print '#include "glxserver.h"'
451 print '#include "glxbyteorder.h"'
452 print '#include "indirect_size.h"'
453 print '#include "indirect_reqsize.h"'
454 print ''
455 print '#if defined(__CYGWIN__) || defined(__MINGW32__)'
456 print '# undef HAVE_ALIAS'
457 print '#endif'
458 print '#ifdef HAVE_ALIAS'
459 print '# define ALIAS2(from,to) \\'
460 print ' GLint __glX ## from ## ReqSize( const GLbyte * pc, Bool swap, int reqlen ) \\'
461 print ' __attribute__ ((alias( # to )));'
462 print '# define ALIAS(from,to) ALIAS2( from, __glX ## to ## ReqSize )'
463 print '#else'
464 print '# define ALIAS(from,to) \\'
465 print ' GLint __glX ## from ## ReqSize( const GLbyte * pc, Bool swap, int reqlen ) \\'
466 print ' { return __glX ## to ## ReqSize( pc, swap, reqlen ); }'
467 print '#endif'
468 print ''
469 print ''
470
471
472 def printBody(self, api):
473 aliases = []
474 enum_functions = {}
475 enum_sigs = {}
476
477 for func in api.functionIterateGlx():
478 if not func.has_variable_size_request(): continue
479
480 ef = glx_server_enum_function( func, api.enums_by_name )
481 if len(ef.enums) == 0: continue
482
483 sig = ef.signature()
484
485 if not enum_functions.has_key(func.name):
486 enum_functions[ func.name ] = sig
487
488 if not enum_sigs.has_key( sig ):
489 enum_sigs[ sig ] = ef
490
491
492
493 for func in api.functionIterateGlx():
494 # Even though server-handcode fuctions are on "the
495 # list", and prototypes are generated for them, there
496 # isn't enough information to generate a size
497 # function. If there was enough information, they
498 # probably wouldn't need to be handcoded in the first
499 # place!
500
501 if func.server_handcode: continue
502 if not func.has_variable_size_request(): continue
503
504 if enum_functions.has_key(func.name):
505 sig = enum_functions[func.name]
506 ef = enum_sigs[ sig ]
507
508 if ef.name != func.name:
509 aliases.append( [func.name, ef.name] )
510 else:
511 ef.Print( func.name, self )
512
513 elif func.images:
514 self.printPixelFunction(func)
515 elif func.has_variable_size_request():
516 a = self.printCountedFunction(func)
517 if a: aliases.append(a)
518
519
520 for [alias_name, real_name] in aliases:
521 print 'ALIAS( %s, %s )' % (alias_name, real_name)
522
523 return
524
525
526 def common_emit_fixups(self, fixup):
527 """Utility function to emit conditional byte-swaps."""
528
529 if fixup:
530 print ' if (swap) {'
531 for name in fixup:
532 print ' %s = bswap_32(%s);' % (name, name)
533 print ' }'
534
535 return
536
537
538 def common_emit_one_arg(self, p, pc, adjust):
539 offset = p.offset
540 dst = p.string()
541 src = '(%s *)' % (p.type_string())
542 print '%-18s = *%11s(%s + %u);' % (dst, src, pc, offset + adjust);
543 return
544
545
546 def common_func_print_just_header(self, f):
547 print 'int'
548 print '__glX%sReqSize( const GLbyte * pc, Bool swap, int reqlen )' % (f.name)
549 print '{'
550
551
552 def printPixelFunction(self, f):
553 self.common_func_print_just_header(f)
554
555 f.offset_of( f.parameters[0].name )
556 [dim, w, h, d, junk] = f.get_images()[0].get_dimensions()
557
558 print ' GLint row_length = * (GLint *)(pc + 4);'
559
560 if dim < 3:
561 fixup = ['row_length', 'skip_rows', 'alignment']
562 print ' GLint image_height = 0;'
563 print ' GLint skip_images = 0;'
564 print ' GLint skip_rows = * (GLint *)(pc + 8);'
565 print ' GLint alignment = * (GLint *)(pc + 16);'
566 else:
567 fixup = ['row_length', 'image_height', 'skip_rows', 'skip_images', 'alignment']
568 print ' GLint image_height = * (GLint *)(pc + 8);'
569 print ' GLint skip_rows = * (GLint *)(pc + 16);'
570 print ' GLint skip_images = * (GLint *)(pc + 20);'
571 print ' GLint alignment = * (GLint *)(pc + 32);'
572
573 img = f.images[0]
574 for p in f.parameterIterateGlxSend():
575 if p.name in [w, h, d, img.img_format, img.img_type, img.img_target]:
576 self.common_emit_one_arg(p, "pc", 0)
577 fixup.append( p.name )
578
579 print ''
580
581 self.common_emit_fixups(fixup)
582
583 if img.img_null_flag:
584 print ''
585 print ' if (*(CARD32 *) (pc + %s))' % (img.offset - 4)
586 print ' return 0;'
587
588 print ''
589 print ' return __glXImageSize(%s, %s, %s, %s, %s, %s,' % (img.img_format, img.img_type, img.img_target, w, h, d )
590 print ' image_height, row_length, skip_images,'
591 print ' skip_rows, alignment);'
592 print '}'
593 print ''
594 return
595
596
597 def printCountedFunction(self, f):
598
599 sig = ""
600 offset = 0
601 fixup = []
602 params = []
603 size = ''
604 param_offsets = {}
605
606 # Calculate the offset of each counter parameter and the
607 # size string for the variable length parameter(s). While
608 # that is being done, calculate a unique signature for this
609 # function.
610
611 for p in f.parameterIterateGlxSend():
612 if p.is_counter:
613 fixup.append( p.name )
614 params.append( p )
615 elif p.counter:
616 s = p.size()
617 if s == 0: s = 1
618
619 sig += "(%u,%u)" % (f.offset_of(p.counter), s)
620 if size == '':
621 size = p.size_string()
622 else:
623 size = "safe_add(%s, %s)" % (size, p.size_string())
624
625 # If the calculated signature matches a function that has
626 # already be emitted, don't emit this function. Instead, add
627 # it to the list of function aliases.
628
629 if self.counter_sigs.has_key(sig):
630 n = self.counter_sigs[sig];
631 alias = [f.name, n]
632 else:
633 alias = None
634 self.counter_sigs[sig] = f.name
635
636 self.common_func_print_just_header(f)
637
638 for p in params:
639 self.common_emit_one_arg(p, "pc", 0)
640
641
642 print ''
643 self.common_emit_fixups(fixup)
644 print ''
645
646 print ' return safe_pad(%s);' % (size)
647 print '}'
648 print ''
649
650 return alias
651
652
653 def _parser():
654 """Parse arguments and return a namespace."""
655 parser = argparse.ArgumentParser()
656 parser.set_defaults(which_functions=(PrintGlxSizeStubs_common.do_get |
657 PrintGlxSizeStubs_common.do_set))
658 parser.add_argument('-f',
659 dest='filename',
660 default='gl_API.xml',
661 help='an XML file describing an OpenGL API.')
662 parser.add_argument('-m',
663 dest='mode',
664 choices=['size_c', 'size_h', 'reqsize_c', 'reqsize_h'],
665 help='Which file to generate')
666 getset = parser.add_mutually_exclusive_group()
667 getset.add_argument('--only-get',
668 dest='which_functions',
669 action='store_const',
670 const=PrintGlxSizeStubs_common.do_get,
671 help='only emit "get-type" functions')
672 getset.add_argument('--only-set',
673 dest='which_functions',
674 action='store_const',
675 const=PrintGlxSizeStubs_common.do_set,
676 help='only emit "set-type" functions')
677 parser.add_argument('--header-tag',
678 dest='header_tag',
679 action='store',
680 default=None,
681 help='set header tag value')
682 return parser.parse_args()
683
684
685 def main():
686 """Main function."""
687 args = _parser()
688
689 if args.mode == "size_c":
690 printer = PrintGlxSizeStubs_c(args.which_functions)
691 elif args.mode == "size_h":
692 printer = PrintGlxSizeStubs_h(args.which_functions)
693 if args.header_tag is not None:
694 printer.header_tag = args.header_tag
695 elif args.mode == "reqsize_c":
696 printer = PrintGlxReqSize_c()
697 elif args.mode == "reqsize_h":
698 printer = PrintGlxReqSize_h()
699
700 api = gl_XML.parse_GL_API(args.filename, glX_XML.glx_item_factory())
701
702 printer.Print(api)
703
704
705 if __name__ == '__main__':
706 main()