in maxas/MaxAs/Cubin.pm [132:416]
sub new
{
my ($package, $file) = @_;
my $cubin = bless { fileName => $file }, $package;
open my $fh, $file or die "$file: $!";
binmode($fh);
# Read in assuming 32 bit header
my $data;
read $fh, $data, 0x34;
my $elfHdr = $cubin->{elfHdr} = {};
@{$elfHdr}{@{$elfHdrC[1]}} = unpack $elfHdrT[1], $data;
# 1: 32bit, 2: 64bit
my $class = $elfHdr->{fileClass};
# re-read in with 64 bit header if needed
if ($class == 2)
{
seek $fh, 0, 0;
read $fh, $data, 0x46;
@{$elfHdr}{@{$elfHdrC[$class]}} = unpack $elfHdrT[$class], $data;
$cubin->{Class} = 64;
}
else
{
$cubin->{Class} = 32;
}
# verify sm_50 cubin
$cubin->{Arch} = $elfHdr->{flags} & 0xFF;
die "Cubin not in sm_50 or greater format. Found: sm_$cubin->{Arch}\n" if $cubin->{Arch} < 50;
$cubin->{AddressSize} = $elfHdr->{flags} & 0x400 ? 64 : 32;
# Read in Program Headers
seek $fh, $elfHdr->{phOffset}, 0;
foreach (1 .. $elfHdr->{phNum})
{
read $fh, $data, $elfHdr->{phEntSize};
my %prgHdr = (Indx => $_ - 1);
@prgHdr{@{$prgHdrC[$class]}} = unpack $prgHdrT[$class], $data;
push @{$cubin->{prgHdrs}}, \%prgHdr;
}
# Read in Section Headers
seek $fh, $elfHdr->{shOffset}, 0;
foreach (1 .. $elfHdr->{shNum})
{
read $fh, $data, $elfHdr->{shEntSize};
my %secHdr = (Indx => $_ - 1);
@secHdr{@{$secHdrC[$class]}} = unpack $secHdrT[$class], $data;
push @{$cubin->{secHdrs}}, \%secHdr;
}
# Read in Section data
foreach my $secHdr (@{$cubin->{secHdrs}})
{
$data = '';
# Skip sections with no data (type NULL or NOBITS)
if ($secHdr->{size} && $secHdr->{type} != 8)
{
seek $fh, $secHdr->{offset}, 0;
read $fh, $data, $secHdr->{size};
}
# Convert string tables to maps
if ($secHdr->{type} == 3) # STRTAB
{
my $strTab = $secHdr->{StrTab} = {};
my $indx = 0;
foreach my $str (split "\0", $data)
{
$strTab->{$indx} = $str;
$indx += 1 + length($str);
}
}
# Read in Symbol data
if ($secHdr->{type} == 2) # SYMTAB
{
my $offset = 0;
while ($offset < $secHdr->{size})
{
my $symEnt = {};
@{$symEnt}{@{$symHdrC[$class]}} = unpack $symHdrT[$class], substr($data, $offset, $secHdr->{entSize});
$offset += $secHdr->{entSize};
push @{$secHdr->{SymTab}}, $symEnt;
}
}
# Cache raw data for further processing and writing
$secHdr->{Data} = unpack 'H*', $data;
}
close $fh;
# Update section headers with their names. Map names directly to headers.
my $shStrTab = $cubin->{secHdrs}[$elfHdr->{shStrIndx}]{StrTab};
foreach my $secHdr (@{$cubin->{secHdrs}})
{
$secHdr->{Name} = $shStrTab->{$secHdr->{name}};
$cubin->{$secHdr->{Name}} = $secHdr;
}
# Update symbols with their names
# For the Global functions, extract kernel meta data
# Populate the kernel hash
my $strTab = $cubin->{'.strtab'}{StrTab};
foreach my $symEnt (@{$cubin->{'.symtab'}{SymTab}})
{
$symEnt->{Name} = $strTab->{$symEnt->{name}};
# Attach symbol to section
my $secHdr = $cubin->{secHdrs}[$symEnt->{shIndx}];
$secHdr->{SymbolEnt} = $symEnt;
# Look for symbols tagged FUNC
if (($symEnt->{info} & 0x0f) == 0x02)
{
# Create a hash of kernels for output
my $kernelSec = $cubin->{Kernels}{$symEnt->{Name}} = $secHdr;
# Extract local/global/weak binding info
$kernelSec->{Linkage} = $symBind[($symEnt->{info} & 0xf0) >> 4];
# Extract the kernel instructions
$kernelSec->{KernelData} = [ unpack "Q*", pack "H*", $kernelSec->{Data} ];
# Extract the max barrier resource identifier used and add 1. Should be 0-16.
# If a register is used as a barrier resource id, then this value is the max of 16.
$kernelSec->{BarCnt} = ($kernelSec->{flags} & 0x01f00000) >> 20;
# Extract the number of allocated registers for this kernel.
$kernelSec->{RegCnt} = ($kernelSec->{info} & 0xff000000) >> 24;
# Extract the size of shared memory this kernel uses.
my $sharedSec = $kernelSec->{SharedSec} = $cubin->{".nv.shared.$symEnt->{Name}"};
$kernelSec->{SharedSize} = $sharedSec ? $sharedSec->{size} : 0;
# Attach constant0 section
$kernelSec->{ConstantSec} = $cubin->{".nv.constant0.$symEnt->{Name}"};
# Extract the kernel parameter data.
my $paramSec = $kernelSec->{ParamSec} = $cubin->{".nv.info.$symEnt->{Name}"};
if ($paramSec)
{
# Extract raw param data
my @data = unpack "L*", pack "H*", $paramSec->{Data};
$paramSec->{ParamData} = \@data;
$paramSec->{ParamHex} = [ map { sprintf '0x%08x', $_ } @data ];
# Find the first param delimiter
my $idx = 0;
$idx++ while $idx < @data && $data[$idx] != 0x00080a04;
my $first = $data[$idx+2] & 0xFFFF;
#my $size = $data[$idx+2] >> 16;
$idx += 4;
my @params;
while ($idx < @data && $data[$idx] == 0x000c1704)
{
# Get the ordinal, offset, size and pointer alignment for each param
my $ord = $data[$idx+2] & 0xFFFF;
my $offset = sprintf '0x%02x', $first + ($data[$idx+2] >> 16);
my $psize = $data[$idx+3] >> 18;
my $align = $data[$idx+3] & 0x400 ? 1 << ($data[$idx+3] & 0x3ff) : 0;
unshift @params, "$ord:$offset:$psize:$align";
$idx += 4;
}
my @staticParams = @data[0 .. ($idx-1)];
my ($maxregCount, @exitOffsets, @ctaidOffsets, $ctaidzUsed, @reqntid, @maxntid, @stackSize);
while ($idx < @data)
{
my $code = $data[$idx] & 0xffff;
my $size = $data[$idx] >> 16;
$idx++;
# EIATTR_MAXREG_COUNT
if ($code == 0x1b03)
{
$maxregCount = $size;
}
# EIATTR_S2RCTAID_INSTR_OFFSETS
elsif ($code == 0x1d04)
{
while ($size > 0)
{
push @ctaidOffsets, $data[$idx++];
$size -= 4;
}
}
# EIATTR_EXIT_INSTR_OFFSETS
elsif ($code == 0x1c04)
{
while ($size > 0)
{
push @exitOffsets, $data[$idx++];
$size -= 4;
}
}
# EIATTR_CTAIDZ_USED
elsif ($code == 0x0401)
{
$ctaidzUsed = 1;
}
# EIATTR_REQNTID
elsif ($code == 0x1004)
{
while ($size > 0)
{
push @reqntid, $data[$idx++];
$size -= 4;
}
}
# EIATTR_MAX_THREADS
elsif ($code == 0x0504)
{
while ($size > 0)
{
push @maxntid, $data[$idx++];
$size -= 4;
}
}
# EIATTR_CRS_STACK_SIZE
elsif ($code == 0x1e04)
{
while ($size > 0)
{
push @stackSize, $data[$idx++];
$size -= 4;
}
}
else
{
printf STDERR "Unknown Code 0x%02x (size:%d)\n", $code, $size;
}
}
$kernelSec->{Params} = \@params;
$kernelSec->{ParamCnt} = scalar @params;
$paramSec->{StaticParams} = \@staticParams;
$paramSec->{MAXREG_COUNT} = $maxregCount;
$paramSec->{ExitOffsets} = \@exitOffsets;
$paramSec->{CTAIDOffsets} = \@ctaidOffsets;
$paramSec->{CTAIDZUsed} = $ctaidzUsed;
$paramSec->{REQNTID} = \@reqntid;
$paramSec->{MAXNTID} = \@maxntid;
$paramSec->{STACKSIZE} = \@stackSize;
}
# print Dumper($paramSec);
# exit();
}
# Note GLOBALs found in this cubin
elsif (($symEnt->{info} & 0x10) == 0x10)
{
$cubin->{Symbols}{$symEnt->{Name}} = $symEnt;
}
}
# print "phOffset: $elfHdr->{phOffset}\n";
# print "shOffset: $elfHdr->{shOffset}\n";
# foreach my $secHdr (@{$cubin->{secHdrs}})
# {
# print "secHdr($secHdr->{Indx}): $secHdr->{offset}, $secHdr->{size}, $secHdr->{align} ($secHdr->{Name})\n";
# }
# my $p = 0;
# foreach my $prgHdr (@{$cubin->{prgHdrs}})
# {
# print "prgHdr($p): type: $prgHdr->{type}, offset: $prgHdr->{offset}, fileSize: $prgHdr->{fileSize}, memSize: $prgHdr->{memSize}, align: $prgHdr->{align}\n";
# $p++;
# }
# exit();
#print map { sprintf "%016x\n", $_ } @{$cubin->{Kernels}{microbench}{KernelData}};
#print Dumper($cubin->{Kernels}{test}{KernelData});
#exit();
return $cubin;
}