2 * Copyright (c) 2018 Rob Clark <robdclark@gmail.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * Decoder for "new" GL_OES_get_program_binary format.
27 * Overall structure is:
29 * - header at top, contains, amongst other things, offsets of
30 * per shader stage sections.
31 * - per shader stage section (shader_info) starts with a header,
32 * followed by a variably length list of descriptors. Each
33 * descriptor has a type/count/size plus offset from the start
34 * of shader_info section where the data is found
43 #include <sys/types.h>
55 static int dump_full
= 0;
56 static int dump_offsets
= 0;
57 static int gpu_id
= 320;
58 static int shaderdb
= 0; /* output shaderdb style traces to stderr */
65 /* current shader_info section, some offsets calculated relative to
66 * this, rather than relative to start of buffer.
70 /* size of each entry within a shader_descriptor_blk: */
73 const char *shader_type
;
78 #define PACKED __attribute__((__packed__))
80 #define OFF(field) do { \
82 printf("%08x: ", (uint32_t)((char *)&field - state->buf));\
85 /* decode field as hex */
86 #define X(s, field) do { \
88 printf("%s%12s:\t0x%x\n", tab(state->lvl), #field, s->field); \
91 /* decode field as digit */
92 #define D(s, field) do { \
94 printf("%s%12s:\t%u\n", tab(state->lvl), #field, s->field); \
97 /* decode field as float/hex */
98 #define F(s, field) do { \
100 printf("%s%12s:\t%f (0x%0x)\n", tab(state->lvl), #field, \
101 d2f(s->field), s->field); \
104 /* decode field as register: (type is 'r' or 'c') */
105 #define R(s, field, type) do { \
107 printf("%s%12s:\t%c%u.%c\n", tab(state->lvl), #field, type, \
108 (s->field >> 2), "xyzw"[s->field & 0x3]); \
111 /* decode inline string (presumably null terminated?) */
112 #define S(s, field) do { \
114 printf("%s%12s:\t%s\n", tab(state->lvl), #field, s->field); \
117 /* decode string-table string */
118 #define T(s, field) TODO
120 /* decode field as unknown */
121 #define U(s, start, end) \
122 dump_unknown(state, s->unk_ ## start ## _ ## end, 0x ## start, (4 + 0x ## end - 0x ## start) / 4)
124 /* decode field as offset to other section */
125 #define O(s, field, type) do { \
127 assert(s->field < state->sz); \
128 void *_p = &state->buf[s->field]; \
130 decode_ ## type (state, _p); \
135 static void decode_shader_info(struct state
*state
, struct shader_info
*info
);
137 static void dump_unknown(struct state
*state
, void *buf
, unsigned start
, unsigned n
)
140 uint8_t *ascii
= buf
;
142 for (unsigned i
= 0; i
< n
; i
++) {
146 printf("%08x:", (uint32_t)((char *)&ptr
[i
] - state
->buf
));
148 printf("%s %04x:\t%08x", tab(state
->lvl
), start
+ i
* 4, d
);
151 for (unsigned j
= 0; j
< 4; j
++) {
152 uint8_t c
= *(ascii
++);
153 printf("%c", (isascii(c
) && !iscntrl(c
)) ? c
: '.');
155 printf("|\t%f", d2f(d
));
157 /* TODO maybe scan for first non-null and non-ascii char starting from
158 * end of shader binary to (roughly) establish the start of the string
159 * table.. that would be a bit better filter for deciding if something
160 * might be a pointer into the string table. Also, the previous char
161 * to what it points to should probably be null.
163 if ((d
< state
->sz
) &&
164 isascii(state
->buf
[d
]) &&
165 (strlen(&state
->buf
[d
]) > 2) &&
166 isascii(state
->buf
[d
+1]))
167 printf("\t<== %s", &state
->buf
[d
]);
173 struct PACKED header
{
174 uint32_t version
; /* I guess, always b10bcace ? */
175 uint32_t unk_0004_0014
[5];
177 uint32_t size2
; /* just to be sure? */
178 uint32_t unk_0020_0020
[1];
179 uint32_t chksum
; /* I guess? Small changes seem to result in big diffs here */
180 uint32_t unk_0028_0050
[11];
181 uint32_t fs_info
; /* offset of FS shader_info section */
182 uint32_t unk_0058_0090
[15];
183 uint32_t vs_info
; /* offset of VS shader_info section */
184 uint32_t unk_0098_00b0
[7];
185 uint32_t vs_info2
; /* offset of VS shader_info section (again?) */
186 uint32_t unk_00b8_0110
[23];
187 uint32_t bs_info
; /* offset of binning shader_info section */
190 static void decode_header(struct state
*state
, struct header
*hdr
)
199 state
->shader_type
= "FRAG";
200 O(hdr
, fs_info
, shader_info
);
202 state
->shader_type
= "VERT";
203 O(hdr
, vs_info
, shader_info
);
205 assert(hdr
->vs_info
== hdr
->vs_info2
); /* not sure what this if it is ever different */
208 state
->shader_type
= "BVERT";
209 O(hdr
, bs_info
, shader_info
);
211 /* not sure how much of the rest of contents before start of fs_info
212 * is the header, vs other things.. just dump it all as unknown for
215 dump_unknown(state
, (void *)hdr
+ sizeof(*hdr
),
216 sizeof(*hdr
), (hdr
->fs_info
- sizeof(*hdr
)) / 4);
219 struct PACKED shader_entry_point
{
220 /* entry point name, ie. "main" of TBD length, followed by unknown */
224 static void decode_shader_entry_point(struct state
*state
,
225 struct shader_entry_point
*e
)
230 struct PACKED shader_config
{
231 uint32_t unk_0000_0008
[3];
236 static void decode_shader_config(struct state
*state
, struct shader_config
*cfg
)
242 state
->full_regs
= cfg
->full_regs
;
243 state
->half_regs
= cfg
->half_regs
;
245 /* dump reset of unknown (size differs btwn versions) */
246 dump_unknown(state
, (void *)cfg
+ sizeof(*cfg
), sizeof(*cfg
),
247 (state
->desc_size
- sizeof(*cfg
))/4);
250 struct PACKED shader_io_block
{
251 /* name of TBD length followed by unknown.. 42 dwords total */
253 uint32_t unk_0014_00a4
[37];
256 static void decode_shader_io_block(struct state
*state
,
257 struct shader_io_block
*io
)
263 struct PACKED shader_constant_block
{
265 uint32_t unk_0004_000c
[3];
267 uint32_t unk_0014_0024
[5];
270 static void decode_shader_constant_block(struct state
*state
,
271 struct shader_constant_block
*c
)
280 ENTRY_POINT
= 0, /* shader_entry_point */
281 SHADER_CONFIG
= 1, /* XXX placeholder name */
282 SHADER_INPUT
= 2, /* shader_io_block */
283 SHADER_OUTPUT
= 3, /* shader_io_block */
284 CONSTANTS
= 6, /* shader_constant_block */
285 INTERNAL
= 8, /* internal input, like bary.f coord */
287 } shader_info_block_type
;
289 /* Refers to location of some type of records, with an offset relative to
290 * start of shader_info block.
292 struct PACKED shader_descriptor_block
{
293 uint32_t type
; /* block type */
294 uint32_t offset
; /* offset (relative to start of shader_info block) */
295 uint32_t size
; /* size in bytes */
296 uint32_t count
; /* number of records */
297 uint32_t unk_0010_0010
[1];
300 static void decode_shader_descriptor_block(struct state
*state
,
301 struct shader_descriptor_block
*blk
)
309 /* offset relative to current shader block: */
310 void *ptr
= state
->shader
+ blk
->offset
;
312 if (blk
->count
== 0) {
313 assert(blk
->size
== 0);
315 assert((blk
->size
% blk
->count
) == 0);
318 state
->desc_size
= blk
->size
/ blk
->count
;
320 for (unsigned i
= 0; i
< blk
->count
; i
++) {
323 printf("%sentry point %u:\n", tab(state
->lvl
-1), i
);
324 decode_shader_entry_point(state
, ptr
);
327 printf("%sconfig %u:\n", tab(state
->lvl
-1), i
);
328 decode_shader_config(state
, ptr
);
331 printf("%sinput %u:\n", tab(state
->lvl
-1), i
);
332 decode_shader_io_block(state
, ptr
);
335 printf("%soutput %u:\n", tab(state
->lvl
-1), i
);
336 decode_shader_io_block(state
, ptr
);
339 printf("%sinternal input %u:\n", tab(state
->lvl
-1), i
);
340 decode_shader_io_block(state
, ptr
);
343 printf("%sconstant %u:\n", tab(state
->lvl
-1), i
);
344 decode_shader_constant_block(state
, ptr
);
347 struct shader_stats stats
;
348 printf("%sshader %u:\n", tab(state
->lvl
-1), i
);
349 disasm_a3xx_stat(ptr
, blk
->size
/4, state
->lvl
, stdout
, gpu_id
, &stats
);
351 unsigned dwords
= 2 * stats
.instlen
;
354 dwords
= ALIGN(dwords
, 16 * 2);
356 dwords
= ALIGN(dwords
, 4 * 2);
359 unsigned half_regs
= state
->half_regs
;
360 unsigned full_regs
= state
->full_regs
;
362 /* On a6xx w/ merged/conflicting half and full regs, the
363 * full_regs footprint will be max of full_regs and half
364 * of half_regs.. we only care about which value is higher.
367 /* footprint of half_regs in units of full_regs: */
368 unsigned half_full
= (half_regs
+ 1) / 2;
369 if (half_full
> full_regs
)
370 full_regs
= half_full
;
375 "%s shader: %u inst, %u nops, %u non-nops, %u dwords, "
376 "%u half, %u full, %u constlen, "
377 "%u (ss), %u (sy), %d max_sun, %d loops\n",
378 state
->shader_type
, stats
.instructions
,
379 stats
.nops
, stats
.instructions
- stats
.nops
,
380 dwords
, half_regs
, full_regs
,
381 stats
.constlen
, stats
.ss
, stats
.sy
,
382 0, 0); /* max_sun or loops not possible */
384 /* this is a special case in a way, blk->count is # of
385 * instructions but disasm_a3xx() decodes all instructions,
392 dump_unknown(state
, ptr
, 0, state
->desc_size
/4);
395 ptr
+= state
->desc_size
;
400 /* there looks like one of these per shader, followed by "main" and
401 * some more info, and then the shader itself.
403 struct PACKED shader_info
{
404 uint32_t unk_0000_0010
[5];
405 uint32_t desc_off
; /* offset to first descriptor block */
409 static void decode_shader_info(struct state
*state
, struct shader_info
*info
)
411 assert((info
->desc_off
% 4) == 0);
417 dump_unknown(state
, &info
[1], 0, (info
->desc_off
- sizeof(*info
))/4);
419 state
->shader
= info
;
421 struct shader_descriptor_block
*blocks
= ((void *)info
) + info
->desc_off
;
422 for (unsigned i
= 0; i
< info
->num_blocks
; i
++) {
423 printf("%sdescriptor %u:\n", tab(state
->lvl
), i
);
425 decode_shader_descriptor_block(state
, &blocks
[i
]);
430 static void dump_program(struct state
*state
)
432 struct header
*hdr
= (void *)state
->buf
;
435 dump_unknown(state
, state
->buf
, 0, state
->sz
/4);
437 decode_header(state
, hdr
);
440 int main(int argc
, char **argv
)
442 enum rd_sect_type type
= RD_NONE
;
443 enum debug_t debug
= PRINT_RAW
| PRINT_STATS
;
449 /* lame argument parsing: */
452 if ((argc
> 1) && !strcmp(argv
[1], "--verbose")) {
453 debug
|= PRINT_RAW
| PRINT_VERBOSE
;
458 if ((argc
> 1) && !strcmp(argv
[1], "--expand")) {
459 debug
|= EXPAND_REPEAT
;
464 if ((argc
> 1) && !strcmp(argv
[1], "--full")) {
465 /* only short dump, original shader, symbol table, and disassembly */
471 if ((argc
> 1) && !strcmp(argv
[1], "--dump-offsets")) {
477 if ((argc
> 1) && !strcmp(argv
[1], "--raw")) {
483 if ((argc
> 1) && !strcmp(argv
[1], "--shaderdb")) {
493 fprintf(stderr
, "usage: pgmdump2 [--verbose] [--expand] [--full] [--dump-offsets] [--raw] [--shaderdb] testlog.rd\n");
497 disasm_a3xx_set_debug(debug
);
501 io
= io_open(infile
);
503 fprintf(stderr
, "could not open: %s\n", infile
);
509 io_readn(io
, &sz
, 4);
512 /* note: allow hex dumps to go a bit past the end of the buffer..
513 * might see some garbage, but better than missing the last few bytes..
515 buf
= calloc(1, sz
+ 3);
516 io_readn(io
, buf
+ 4, sz
);
519 struct state state
= {
523 printf("############################################################\n");
524 printf("program:\n");
525 dump_program(&state
);
526 printf("############################################################\n");
530 /* figure out what sort of input we are dealing with: */
531 if (!(check_extension(infile
, ".rd") || check_extension(infile
, ".rd.gz"))) {
533 buf
= calloc(1, 100 * 1024);
534 ret
= io_readn(io
, buf
, 100 * 1024);
536 fprintf(stderr
, "error: %m");
539 return disasm_a3xx(buf
, ret
/4, 0, stdout
, gpu_id
);
542 while ((io_readn(io
, &type
, sizeof(type
)) > 0) && (io_readn(io
, &sz
, 4) > 0)) {
545 /* note: allow hex dumps to go a bit past the end of the buffer..
546 * might see some garbage, but better than missing the last few bytes..
548 buf
= calloc(1, sz
+ 3);
549 io_readn(io
, buf
, sz
);
554 printf("test: %s\n", (char *)buf
);
557 printf("vertex shader:\n%s\n", (char *)buf
);
560 printf("fragment shader:\n%s\n", (char *)buf
);
563 struct state state
= {
567 printf("############################################################\n");
568 printf("program:\n");
569 dump_program(&state
);
570 printf("############################################################\n");
574 gpu_id
= *((unsigned int *)buf
);
575 printf("gpu_id: %d\n", gpu_id
);