Skip to content

bug: C-string symbol absorbs unrelated string data #678

Description

@philprime

Note

This issue was created by an agent based on a manual, interactive investigation with me. The observations and verification limits from that session are documented below.

Environment

How we discovered this

We were investigating the app-size savings from sentry-cocoa#9003, which moves a preallocated binary-image cache from file-backed data into zero-fill storage. The Preprod comparison reports a 294.9 KB installed-size reduction with essentially unchanged download size.

While inspecting the remaining Sentry footprint, I noticed _sentrycrashstring_addressToString.hex occupying 78.67 KB in the treemap. Tracing that entry back to the SDK source and Launchpad's symbol-size calculation revealed an attribution problem, rather than another large SDK allocation.

Steps to Reproduce

  1. Open artifact 799801 with access to the sentry-sdks organization. Find _sentrycrashstring_addressToString.hex under SDK-SizeOther SymbolsC Functions.

  2. Alternatively, retrieve the analysis using an authenticated Sentry CLI and extract the entry:

    sentry api organizations/sentry-sdks/files/preprodartifacts/799801/size-analysis/ --json > /tmp/799801-size-analysis.json
    jq '.. | objects | select(.name? == "_sentrycrashstring_addressToString.hex")' /tmp/799801-size-analysis.json
  3. Compare the result with Sources/SentryCrash/Recording/Tools/SentryCrashString.c. The symbol is the function-local constant:

    static const char hex[] = "0123456789abcdef";

    This array contains 17 bytes, including its null terminator.

Compiled-binary cross-check

We also inspected a related post-merge iOS framework artifact from Release run 34471930835. This is a dynamic framework build, not the exact statically linked executable uploaded as artifact 799801. It independently confirms the constant's contents and the presence of unrelated strings immediately after it.

On macOS, while the workflow artifact remains available:

gh run download 34471930835 --repo getsentry/sentry-cocoa --name xcframework-sentry-dynamic-slice-iphoneos --dir /tmp/launchpad-hex-repro
unzip -p /tmp/launchpad-hex-repro/Sentry-Dynamic.xcarchive.zip iphoneos.xcarchive/Products/Library/Frameworks/Sentry.framework/Sentry > /tmp/launchpad-hex-repro/Sentry
xcrun nm -arch arm64 -n -m /tmp/launchpad-hex-repro/Sentry | grep -A 2 -B 2 sentrycrashstring_addressToString.hex
xcrun otool -arch arm64 -v -s __TEXT __cstring /tmp/launchpad-hex-repro/Sentry | grep -A 4 00000000001cd1ec

Observed string contents:

00000000001cd1ec  0123456789abcdef
00000000001cd1fd  0x
00000000001cd200  writeToFile:atomically:
00000000001cd218  B28@?0@8@"NSString"16B24
00000000001cd231  writeToFile:options:error:

The next string starts exactly 0x11 / 17 bytes after hex. However, the next named symbol in the symbol table is in another section (__TEXT,__const), so the symbol-size heuristic has no intervening symbol boundary for these unrelated strings.

Expected Result

  • Attribute only the actual string literal's bytes to hex, or clearly present the result as an estimated range rather than the constant's size.
  • Leave unrelated string-pool content attributed to the string section or another appropriate bucket.
  • Do not label this data constant as a C function.
  • Preserve accurate total binary-size accounting without suggesting that changing this 17-byte lookup table could remove approximately 79 KB.

Actual Result

The API returns:

{
  "name": "_sentrycrashstring_addressToString.hex",
  "size": 78670,
  "type": "modules",
  "is_dir": false,
  "children": [],
  "flagged_insights": []
}

Likely attribution mechanism

MachOSymbolSizes._symbol_sizes uses a distance-to-next-symbol heuristic, with the end of the current section as the fallback:

end = next_sym.value if same_section else max_section_addr

For a named constant in __TEXT,__cstring, subsequent unrelated literals may not have their own symbol-table entries. The heuristic therefore charges those bytes to the preceding named constant instead of stopping at its null terminator.

Additionally, MachOElementBuilder._add_other_symbols labels the otherwise unclassified symbol bucket C Functions, including data symbols such as this one.

Impact and verification limits

This sends SDK-size optimization investigations toward the wrong target. The attributed bytes are real binary content, but they are not all owned by hex. This observation does not establish that the overall installed-size total is wrong.

The session verified the exact API result, the SDK source, the related framework's symbol table and string contents, and the local Launchpad implementation. We did not inspect the exact artifact 799801 Mach-O or independently reproduce the exact 78,670-byte calculation locally. Attempting to run Launchpad's symbol-size calculator was blocked by its unavailable Python 3.14.4 interpreter. No SDK or Launchpad code was changed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions