{"record":{"id":"a72ebbde60e7971e","repo":"oven-sh/bun","slug":"nm-reported-no-text-symbols-is-bunprofile","errorCode":null,"errorMessage":"${nm} reported no text symbols — is ${bunProfile} stripped?","messagePattern":"(.+?) reported no text symbols — is (.+?) stripped\\?","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/orderfile/generate.ts","lineNumber":152,"sourceCode":" * stripping — lld and ld take exactly what nm gave.\n */\nfunction readSymbolTable(bunProfile: string): Map<number, string[]> {\n  // Bare `nm` with no GNU-only long options: the regex below is the\n  // defined-text-symbol filter, and nothing here depends on output order.\n  const nm = process.env.NM || \"nm\";\n  const r = runCommand([nm, bunProfile]);\n  if (r.status !== 0) throw new Error(`${nm} failed on ${bunProfile}\\n${r.stderr}`);\n\n  const symbols = new Map<number, string[]>();\n  for (const line of r.stdout.toString().split(\"\\n\")) {\n    const m = /^([0-9a-f]+) ([tT]) (\\S+)$/.exec(line);\n    if (!m) continue;\n    const address = parseInt(m[1]!, 16);\n    const names = symbols.get(address);\n    if (names) names.push(m[3]!);\n    else symbols.set(address, [m[3]!]);\n  }\n  if (symbols.size === 0) throw new Error(`${nm} reported no text symbols — is ${bunProfile} stripped?`);\n  return symbols;\n}\n\n/** Write function starts for functrace.c: u64 magic, version, count, addresses. */\nfunction writeStarts(path: string, addresses: number[]): void {\n  const buffer = new ArrayBuffer((STARTS_HEADER_WORDS + addresses.length) * 8);\n  const words = new BigUint64Array(buffer);\n  words[0] = STARTS_MAGIC;\n  words[1] = 1n;\n  words[2] = BigInt(addresses.length);\n  for (let i = 0; i < addresses.length; i++) words[STARTS_HEADER_WORDS + i] = BigInt(addresses[i]!);\n  writeFileSync(path, new Uint8Array(buffer));\n}\n\n/** Read a trace functrace.c wrote: first-entry addresses, slide already removed. */\nfunction readTrace(path: string, name: string): number[] {\n  const raw = readFileSync(path);\n  if (raw.byteLength < TRACE_HEADER_WORDS * 8) throw new Error(`workload \"${name}\" wrote a truncated trace`);","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/scripts/orderfile/generate.ts#L134-L170","documentation":"nm succeeded but the filter /^([0-9a-f]+) ([tT]) (\\S+)$/ matched zero defined text symbols in the output, so the address→name map is empty. As the message says, the canonical cause is a stripped binary — nm lists no local/global text symbols to match.","triggerScenarios":"Pointing exeName (or the default name) at the stripped release `bun` instead of the unstripped `bun-profile`; a release configuration that strips during copy; an nm variant that emits a different line format so every line fails the regex.","commonSituations":"Running generate against the wrong artifact in build output; build scripts changed to strip; switching nm implementations (some print symbol type in a different column).","solutions":["Build the unstripped profiling binary first: `bun run build:release` produces `bun-profile`","Pass exeName explicitly if your build names the unstripped binary differently (e.g. assertions builds)","Sanity-check with `nm build/bun-profile | grep -c ' [tT] '` — it must be non-zero before rerunning","If using a custom NM, verify its output still looks like `0000000000001234 t _functionName`"],"exampleFix":"// before — pointing at the stripped artifact\ngenerateOrderFile({ buildDir, exeName: \"bun\" });\n\n// after — the unstripped symbol-bearing binary\ngenerateOrderFile({ buildDir, exeName: \"bun-profile\" });","handlingStrategy":"validation","validationCode":"import { spawnSync } from \"node:child_process\";\n\nconst r = spawnSync(process.env.NM || \"nm\", [bunProfile], { maxBuffer: 1 << 29 });\nconst textSymbols = r.stdout\n  .toString()\n  .split(\"\\n\")\n  .filter((line) => /^[0-9a-f]+ [tT] \\S+$/.test(line)).length;\nif (textSymbols === 0) {\n  throw new Error(`${bunProfile} has no text symbols (stripped?) — rebuild unstripped with bun run build:release`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never strip the profiling binary; keep build:release output unstripped for orderfile work","If your build renames the artifact, always pass exeName pointing at the unstripped one"],"tags":["binary","symbols","build","nm","orderfile"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}