Edward J. Schwartz commited on
Commit
efc2358
·
1 Parent(s): abf836d

Route old-format CUDA ELF to nvdisasm 12.9 so CFGs work

Browse files

nvdisasm 13.x disables flow analysis on pre-CUDA 13 ELF (OSABI 0x33), so
control flow graphs fail. Fetch nvdisasm 12.9 from NVIDIA's redist site at
startup and use it for those files.

Files changed (2) hide show
  1. README.md +2 -0
  2. app.py +36 -5
README.md CHANGED
@@ -12,5 +12,7 @@ short_description: Disassemble CUDA cubins with cuobjdump and nvdisasm
12
 
13
  Upload a CUDA cubin, fatbin or ELF object and run NVIDIA's `cuobjdump` or `nvdisasm` on it.
14
  Both tools are installed from PyPI (`nvidia-cuda-cuobjdump`, `nvidia-cuda-nvdisasm`); no GPU required.
 
 
15
 
16
  `examples/` holds two cubins assembled with `ptxas` from the included PTX sources.
 
12
 
13
  Upload a CUDA cubin, fatbin or ELF object and run NVIDIA's `cuobjdump` or `nvdisasm` on it.
14
  Both tools are installed from PyPI (`nvidia-cuda-cuobjdump`, `nvidia-cuda-nvdisasm`); no GPU required.
15
+ nvdisasm 13.x cannot build control flow graphs for old-format CUDA ELF (pre-CUDA 13, OSABI `0x33`), so those files are
16
+ routed to nvdisasm 12.9, downloaded from NVIDIA's redist site at startup.
17
 
18
  `examples/` holds two cubins assembled with `ptxas` from the included PTX sources.
app.py CHANGED
@@ -1,5 +1,9 @@
 
1
  import shlex
2
  import subprocess
 
 
 
3
  from pathlib import Path
4
 
5
  import gradio as gr
@@ -12,7 +16,23 @@ def find_tool(name):
12
  return str(path)
13
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  TOOLS = {name: find_tool(name) for name in ("cuobjdump", "nvdisasm")}
 
 
16
  OUTPUTS = {
17
  "cuobjdump": ["--dump-sass", "--dump-ptx", "--dump-elf", "--dump-resource-usage", "--dump-elf-symbols",
18
  "--list-elf", "--list-ptx"],
@@ -37,13 +57,23 @@ d3.select(graph).graphviz({useWorker: false, zoom: true}).renderDot(props.value,
37
  EXAMPLES = sorted(str(p) for p in Path(__file__).with_name("examples").glob("*.cubin"))
38
 
39
 
40
- def version(tool):
41
- return subprocess.run([TOOLS[tool], "--version"], capture_output=True, text=True).stdout.split("release ")[-1].splitlines()[0]
 
 
 
 
 
 
 
 
 
 
42
 
43
 
44
  def run_one(tool, args, file):
45
  try:
46
- r = subprocess.run([TOOLS[tool], *args, file], capture_output=True, text=True, timeout=60)
47
  except subprocess.TimeoutExpired:
48
  return "Timed out after 60s."
49
  return (r.stdout + r.stderr or "(no output)") + (f"\n[exit code {r.returncode}]" if r.returncode else "")
@@ -65,8 +95,9 @@ def select_tool(tool):
65
 
66
  with gr.Blocks(title="cuobjdump / nvdisasm") as demo:
67
  gr.Markdown("# cuobjdump / nvdisasm\n"
68
- "Disassemble CUDA cubins, fatbins and ELF objects with NVIDIA's tools from PyPI ("
69
- + ", ".join(f"`{t}` {version(t)}" for t in TOOLS) + ").")
 
70
  with gr.Row(equal_height=True):
71
  with gr.Column(scale=1):
72
  file = gr.File(label="cubin / fatbin / CUDA ELF", type="filepath")
 
1
+ import platform
2
  import shlex
3
  import subprocess
4
+ import tarfile
5
+ import tempfile
6
+ import urllib.request
7
  from pathlib import Path
8
 
9
  import gradio as gr
 
16
  return str(path)
17
 
18
 
19
+ def fetch_legacy_nvdisasm(version):
20
+ arch = "linux-sbsa" if platform.machine() == "aarch64" else "linux-x86_64"
21
+ name = f"cuda_nvdisasm-{arch}-{version}-archive"
22
+ path = Path(tempfile.gettempdir()) / name / "nvdisasm"
23
+ if not path.exists():
24
+ url = f"https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvdisasm/{arch}/{name}.tar.xz"
25
+ with urllib.request.urlopen(url) as r, tarfile.open(fileobj=r, mode="r|xz") as tar:
26
+ member = next(m for m in tar if m.name == f"{name}/bin/nvdisasm")
27
+ path.parent.mkdir(exist_ok=True)
28
+ path.write_bytes(tar.extractfile(member).read())
29
+ path.chmod(0o755)
30
+ return str(path)
31
+
32
+
33
  TOOLS = {name: find_tool(name) for name in ("cuobjdump", "nvdisasm")}
34
+ # nvdisasm 13.x can't run flow analysis (CFGs) on old-format CUDA ELF (OSABI 0x33, pre-CUDA 13); 12.9 can.
35
+ LEGACY_NVDISASM = fetch_legacy_nvdisasm("12.9.88")
36
  OUTPUTS = {
37
  "cuobjdump": ["--dump-sass", "--dump-ptx", "--dump-elf", "--dump-resource-usage", "--dump-elf-symbols",
38
  "--list-elf", "--list-ptx"],
 
57
  EXAMPLES = sorted(str(p) for p in Path(__file__).with_name("examples").glob("*.cubin"))
58
 
59
 
60
+ def version(exe):
61
+ return subprocess.run([exe, "--version"], capture_output=True, text=True).stdout.split("release ")[-1].splitlines()[0]
62
+
63
+
64
+ def is_old_elf(file):
65
+ with open(file, "rb") as f:
66
+ head = f.read(8)
67
+ return head[:4] == b"\x7fELF" and head[7] == 0x33
68
+
69
+
70
+ def executable(tool, file):
71
+ return LEGACY_NVDISASM if tool == "nvdisasm" and is_old_elf(file) else TOOLS[tool]
72
 
73
 
74
  def run_one(tool, args, file):
75
  try:
76
+ r = subprocess.run([executable(tool, file), *args, file], capture_output=True, text=True, timeout=60)
77
  except subprocess.TimeoutExpired:
78
  return "Timed out after 60s."
79
  return (r.stdout + r.stderr or "(no output)") + (f"\n[exit code {r.returncode}]" if r.returncode else "")
 
95
 
96
  with gr.Blocks(title="cuobjdump / nvdisasm") as demo:
97
  gr.Markdown("# cuobjdump / nvdisasm\n"
98
+ "Disassemble CUDA cubins, fatbins and ELF objects with NVIDIA's tools ("
99
+ + ", ".join(f"`{t}` {version(exe)}" for t, exe in TOOLS.items())
100
+ + f"; old-format CUDA ELF goes to `nvdisasm` {version(LEGACY_NVDISASM)}).")
101
  with gr.Row(equal_height=True):
102
  with gr.Column(scale=1):
103
  file = gr.File(label="cubin / fatbin / CUDA ELF", type="filepath")