├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── rustfmt.toml ├── tests ├── absolute-paths.test ├── archive-with-arbitrary.test ├── archive.test ├── columns.test ├── compress-fail.test ├── compress.test ├── cu-and-tu-info-section-v5.s ├── debug-macro-v5.s ├── duplicate.test ├── dwos-list-from-exec-simple.test ├── empty.test ├── gcc-type.test ├── handle-strx.test ├── help.test ├── incompatible-cu-index-versions.s ├── incompatible-tu-index-version.s ├── info-v5.s ├── inputs │ ├── columns-a.dwo │ ├── columns-b.dwo │ ├── compress-fail-a.dwo │ ├── compress-fail.dwo │ ├── compress.dwo │ ├── duplicate-ac.dwp │ ├── duplicate-bc.dwp │ ├── duplicate-c.dwo │ ├── duplicate-dwo-name-ac.dwp │ ├── duplicate-dwo-name-bc.dwp │ ├── duplicate-dwo-name-c.dwo │ ├── dwos-list-from-exec-a.dwo │ ├── dwos-list-from-exec-b.dwo │ ├── dwos-list-from-exec-c.dwo │ ├── dwos-list-from-exec-d.dwo │ ├── dwos-list-from-exec-e.dwo │ ├── dwos-list-from-exec-libd.so │ ├── dwos-list-from-exec-main │ ├── empty-compressed-section.dwo │ ├── empty.dwo │ ├── gcc-type.dwo │ ├── handle-strx-v5.dwo │ ├── invalid-compressed.dwo │ ├── invalid-cu-index.dwp │ ├── invalid-string-form.dwo │ ├── merge-ab.dwp │ ├── merge-c.dwo │ ├── missing-dwo-id.dwo │ ├── missing-tu-index.dwp │ ├── multiple-type-sections.dwp │ ├── non-cu-top-level.dwo │ ├── simple-notypes-a.dwo │ ├── simple-notypes-b.dwo │ ├── simple-types-a.dwo │ ├── simple-types-b.dwo │ ├── type-dedup-a.dwo │ ├── type-dedup-b.dwo │ ├── type-dedup-v5-a.s │ └── type-dedup-v5-b.s ├── invalid-cu-header-length-type.s ├── invalid-cu-header-length.s ├── invalid-cu-header-version.s ├── invalid-cu-index.test ├── invalid-string-form.test ├── invalid-tu-header-length.s ├── lit.cfg.py ├── loclists.s ├── merge.test ├── missing-dwo-id.test ├── missing-tu-index.test ├── multiple-debug-info-sections-in-dwp.s ├── multiple-type-sections.test ├── no-cu-found.s ├── non-cu-top-level.test ├── rnglists.s ├── simple.test ├── tu-units-v5.s ├── type-dedup-v5.test ├── type-dedup.test ├── unknown-section-id.s └── wrong-unit-type-info-v4.s ├── thorin-bin ├── Cargo.toml ├── README.md └── src │ └── main.rs └── thorin ├── Cargo.toml ├── README.md └── src ├── error.rs ├── ext.rs ├── index.rs ├── lib.rs ├── package.rs ├── relocate.rs └── strings.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: continuous integration 4 | 5 | jobs: 6 | test: 7 | name: test 8 | runs-on: ubuntu-22.04 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: dtolnay/rust-toolchain@stable 12 | - run: cargo build --release 13 | 14 | - name: Install LLVM 15 | run: | 16 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 17 | sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main" 18 | sudo apt-get update 19 | sudo apt-get install --no-install-recommends --yes llvm-15 llvm-15-tools 20 | - name: Install lit 21 | run: pip install lit 22 | - name: Run lit testsuite 23 | run: lit -v --path "$PWD/target/release/:/usr/lib/llvm-15/bin/" ./tests 24 | 25 | fmt: 26 | name: rustfmt 27 | runs-on: ubuntu-22.04 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: dtolnay/rust-toolchain@stable 31 | with: 32 | components: rustfmt 33 | - run: cargo fmt --all -- --check 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /tests/.lit_test_times.txt 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "adler2" 7 | version = "2.0.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "1.1.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "allocator-api2" 22 | version = "0.2.21" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 25 | 26 | [[package]] 27 | name = "ansi_term" 28 | version = "0.12.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 31 | dependencies = [ 32 | "winapi", 33 | ] 34 | 35 | [[package]] 36 | name = "anyhow" 37 | version = "1.0.97" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" 40 | 41 | [[package]] 42 | name = "atty" 43 | version = "0.2.14" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 46 | dependencies = [ 47 | "hermit-abi", 48 | "libc", 49 | "winapi", 50 | ] 51 | 52 | [[package]] 53 | name = "bitflags" 54 | version = "1.3.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 57 | 58 | [[package]] 59 | name = "cfg-if" 60 | version = "1.0.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 63 | 64 | [[package]] 65 | name = "clap" 66 | version = "2.34.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 69 | dependencies = [ 70 | "ansi_term", 71 | "atty", 72 | "bitflags", 73 | "strsim", 74 | "textwrap", 75 | "unicode-width", 76 | "vec_map", 77 | ] 78 | 79 | [[package]] 80 | name = "crc32fast" 81 | version = "1.4.2" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 84 | dependencies = [ 85 | "cfg-if", 86 | ] 87 | 88 | [[package]] 89 | name = "equivalent" 90 | version = "1.0.2" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 93 | 94 | [[package]] 95 | name = "fallible-iterator" 96 | version = "0.3.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 99 | 100 | [[package]] 101 | name = "flate2" 102 | version = "1.1.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" 105 | dependencies = [ 106 | "crc32fast", 107 | "miniz_oxide", 108 | ] 109 | 110 | [[package]] 111 | name = "foldhash" 112 | version = "0.1.5" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 115 | 116 | [[package]] 117 | name = "gimli" 118 | version = "0.31.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 121 | dependencies = [ 122 | "fallible-iterator", 123 | "indexmap", 124 | "stable_deref_trait", 125 | ] 126 | 127 | [[package]] 128 | name = "hashbrown" 129 | version = "0.15.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 132 | dependencies = [ 133 | "allocator-api2", 134 | "equivalent", 135 | "foldhash", 136 | ] 137 | 138 | [[package]] 139 | name = "heck" 140 | version = "0.3.3" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 143 | dependencies = [ 144 | "unicode-segmentation", 145 | ] 146 | 147 | [[package]] 148 | name = "hermit-abi" 149 | version = "0.1.19" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 152 | dependencies = [ 153 | "libc", 154 | ] 155 | 156 | [[package]] 157 | name = "indexmap" 158 | version = "2.8.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" 161 | dependencies = [ 162 | "equivalent", 163 | "hashbrown", 164 | ] 165 | 166 | [[package]] 167 | name = "lazy_static" 168 | version = "1.5.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 171 | 172 | [[package]] 173 | name = "libc" 174 | version = "0.2.171" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 177 | 178 | [[package]] 179 | name = "log" 180 | version = "0.4.27" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 183 | 184 | [[package]] 185 | name = "matchers" 186 | version = "0.1.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 189 | dependencies = [ 190 | "regex-automata 0.1.10", 191 | ] 192 | 193 | [[package]] 194 | name = "memchr" 195 | version = "2.7.4" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 198 | 199 | [[package]] 200 | name = "memmap2" 201 | version = "0.5.10" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" 204 | dependencies = [ 205 | "libc", 206 | ] 207 | 208 | [[package]] 209 | name = "miniz_oxide" 210 | version = "0.8.5" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 213 | dependencies = [ 214 | "adler2", 215 | ] 216 | 217 | [[package]] 218 | name = "nu-ansi-term" 219 | version = "0.46.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 222 | dependencies = [ 223 | "overload", 224 | "winapi", 225 | ] 226 | 227 | [[package]] 228 | name = "object" 229 | version = "0.36.7" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 232 | dependencies = [ 233 | "crc32fast", 234 | "flate2", 235 | "hashbrown", 236 | "indexmap", 237 | "memchr", 238 | "ruzstd", 239 | ] 240 | 241 | [[package]] 242 | name = "once_cell" 243 | version = "1.21.1" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" 246 | 247 | [[package]] 248 | name = "overload" 249 | version = "0.1.1" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 252 | 253 | [[package]] 254 | name = "pin-project-lite" 255 | version = "0.2.16" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 258 | 259 | [[package]] 260 | name = "proc-macro-error" 261 | version = "1.0.4" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 264 | dependencies = [ 265 | "proc-macro-error-attr", 266 | "proc-macro2", 267 | "quote", 268 | "syn 1.0.109", 269 | "version_check", 270 | ] 271 | 272 | [[package]] 273 | name = "proc-macro-error-attr" 274 | version = "1.0.4" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 277 | dependencies = [ 278 | "proc-macro2", 279 | "quote", 280 | "version_check", 281 | ] 282 | 283 | [[package]] 284 | name = "proc-macro2" 285 | version = "1.0.94" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 288 | dependencies = [ 289 | "unicode-ident", 290 | ] 291 | 292 | [[package]] 293 | name = "quote" 294 | version = "1.0.40" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 297 | dependencies = [ 298 | "proc-macro2", 299 | ] 300 | 301 | [[package]] 302 | name = "regex" 303 | version = "1.11.1" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 306 | dependencies = [ 307 | "aho-corasick", 308 | "memchr", 309 | "regex-automata 0.4.9", 310 | "regex-syntax 0.8.5", 311 | ] 312 | 313 | [[package]] 314 | name = "regex-automata" 315 | version = "0.1.10" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 318 | dependencies = [ 319 | "regex-syntax 0.6.29", 320 | ] 321 | 322 | [[package]] 323 | name = "regex-automata" 324 | version = "0.4.9" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 327 | dependencies = [ 328 | "aho-corasick", 329 | "memchr", 330 | "regex-syntax 0.8.5", 331 | ] 332 | 333 | [[package]] 334 | name = "regex-syntax" 335 | version = "0.6.29" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 338 | 339 | [[package]] 340 | name = "regex-syntax" 341 | version = "0.8.5" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 344 | 345 | [[package]] 346 | name = "ruzstd" 347 | version = "0.7.3" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" 350 | dependencies = [ 351 | "twox-hash", 352 | ] 353 | 354 | [[package]] 355 | name = "sharded-slab" 356 | version = "0.1.7" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 359 | dependencies = [ 360 | "lazy_static", 361 | ] 362 | 363 | [[package]] 364 | name = "smallvec" 365 | version = "1.14.0" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 368 | 369 | [[package]] 370 | name = "stable_deref_trait" 371 | version = "1.2.0" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 374 | 375 | [[package]] 376 | name = "static_assertions" 377 | version = "1.1.0" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 380 | 381 | [[package]] 382 | name = "strsim" 383 | version = "0.8.0" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 386 | 387 | [[package]] 388 | name = "structopt" 389 | version = "0.3.26" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" 392 | dependencies = [ 393 | "clap", 394 | "lazy_static", 395 | "structopt-derive", 396 | ] 397 | 398 | [[package]] 399 | name = "structopt-derive" 400 | version = "0.4.18" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 403 | dependencies = [ 404 | "heck", 405 | "proc-macro-error", 406 | "proc-macro2", 407 | "quote", 408 | "syn 1.0.109", 409 | ] 410 | 411 | [[package]] 412 | name = "syn" 413 | version = "1.0.109" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 416 | dependencies = [ 417 | "proc-macro2", 418 | "quote", 419 | "unicode-ident", 420 | ] 421 | 422 | [[package]] 423 | name = "syn" 424 | version = "2.0.100" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 427 | dependencies = [ 428 | "proc-macro2", 429 | "quote", 430 | "unicode-ident", 431 | ] 432 | 433 | [[package]] 434 | name = "textwrap" 435 | version = "0.11.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 438 | dependencies = [ 439 | "unicode-width", 440 | ] 441 | 442 | [[package]] 443 | name = "thiserror" 444 | version = "1.0.69" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 447 | dependencies = [ 448 | "thiserror-impl", 449 | ] 450 | 451 | [[package]] 452 | name = "thiserror-impl" 453 | version = "1.0.69" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 456 | dependencies = [ 457 | "proc-macro2", 458 | "quote", 459 | "syn 2.0.100", 460 | ] 461 | 462 | [[package]] 463 | name = "thorin-dwp" 464 | version = "0.9.0" 465 | dependencies = [ 466 | "gimli", 467 | "hashbrown", 468 | "object", 469 | "tracing", 470 | ] 471 | 472 | [[package]] 473 | name = "thorin-dwp-bin" 474 | version = "0.9.0" 475 | dependencies = [ 476 | "anyhow", 477 | "memmap2", 478 | "object", 479 | "structopt", 480 | "thiserror", 481 | "thorin-dwp", 482 | "tracing", 483 | "tracing-subscriber", 484 | "tracing-tree", 485 | "typed-arena", 486 | ] 487 | 488 | [[package]] 489 | name = "thread_local" 490 | version = "1.1.8" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 493 | dependencies = [ 494 | "cfg-if", 495 | "once_cell", 496 | ] 497 | 498 | [[package]] 499 | name = "tracing" 500 | version = "0.1.41" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 503 | dependencies = [ 504 | "pin-project-lite", 505 | "tracing-attributes", 506 | "tracing-core", 507 | ] 508 | 509 | [[package]] 510 | name = "tracing-attributes" 511 | version = "0.1.28" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 514 | dependencies = [ 515 | "proc-macro2", 516 | "quote", 517 | "syn 2.0.100", 518 | ] 519 | 520 | [[package]] 521 | name = "tracing-core" 522 | version = "0.1.33" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 525 | dependencies = [ 526 | "once_cell", 527 | "valuable", 528 | ] 529 | 530 | [[package]] 531 | name = "tracing-log" 532 | version = "0.1.4" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" 535 | dependencies = [ 536 | "log", 537 | "once_cell", 538 | "tracing-core", 539 | ] 540 | 541 | [[package]] 542 | name = "tracing-log" 543 | version = "0.2.0" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 546 | dependencies = [ 547 | "log", 548 | "once_cell", 549 | "tracing-core", 550 | ] 551 | 552 | [[package]] 553 | name = "tracing-subscriber" 554 | version = "0.3.19" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 557 | dependencies = [ 558 | "matchers", 559 | "nu-ansi-term", 560 | "once_cell", 561 | "regex", 562 | "sharded-slab", 563 | "smallvec", 564 | "thread_local", 565 | "tracing", 566 | "tracing-core", 567 | "tracing-log 0.2.0", 568 | ] 569 | 570 | [[package]] 571 | name = "tracing-tree" 572 | version = "0.2.5" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d" 575 | dependencies = [ 576 | "nu-ansi-term", 577 | "tracing-core", 578 | "tracing-log 0.1.4", 579 | "tracing-subscriber", 580 | ] 581 | 582 | [[package]] 583 | name = "twox-hash" 584 | version = "1.6.3" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 587 | dependencies = [ 588 | "cfg-if", 589 | "static_assertions", 590 | ] 591 | 592 | [[package]] 593 | name = "typed-arena" 594 | version = "2.0.2" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" 597 | 598 | [[package]] 599 | name = "unicode-ident" 600 | version = "1.0.18" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 603 | 604 | [[package]] 605 | name = "unicode-segmentation" 606 | version = "1.12.0" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 609 | 610 | [[package]] 611 | name = "unicode-width" 612 | version = "0.1.14" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 615 | 616 | [[package]] 617 | name = "valuable" 618 | version = "0.1.1" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 621 | 622 | [[package]] 623 | name = "vec_map" 624 | version = "0.8.2" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 627 | 628 | [[package]] 629 | name = "version_check" 630 | version = "0.9.5" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 633 | 634 | [[package]] 635 | name = "winapi" 636 | version = "0.3.9" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 639 | dependencies = [ 640 | "winapi-i686-pc-windows-gnu", 641 | "winapi-x86_64-pc-windows-gnu", 642 | ] 643 | 644 | [[package]] 645 | name = "winapi-i686-pc-windows-gnu" 646 | version = "0.4.0" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 649 | 650 | [[package]] 651 | name = "winapi-x86_64-pc-windows-gnu" 652 | version = "0.4.0" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 655 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "thorin", 5 | "thorin-bin" 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 The Rust Project Developers 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `thorin` 2 | `thorin` is an DWARF packaging utility for creating DWARF packages (`*.dwp` files) out of input 3 | DWARF objects (`*.dwo` files; or `*.o` files with `.dwo` sections), supporting both the pre-standard 4 | GNU extension format for DWARF packages and the standardized format introduced in DWARF 5. 5 | 6 | `thorin` was written as part of the implementation of Split DWARF in `rustc`. A Rust implementation 7 | of a DWARF packaging utility is easier to integrate into the compiler and can support features like 8 | loading dwarf objects from archive files (or rustc's rlibs) which are helpful in supporting 9 | cross-crate Split DWARF packaging in `rustc`. 10 | 11 | See the README documents of the [`thorin` crate](thorin/README.md) and the 12 | [`thorin-bin` crate](thorin-bin/README.md) for usage details of the library and binary interfaces 13 | respectively. 14 | 15 | ## Contributing to `thorin` 16 | If you want help or mentorship, reach out to us in a GitHub issue, or ask `davidtwco` or in 17 | `#t-compiler` on the [Rust Zulip instance](https://rust-lang.zulipchat.com/). 18 | 19 | `thorin` should always build on stable `rustc`. To build `thorin`: 20 | 21 | ```shell-session 22 | $ cargo build 23 | ``` 24 | 25 | To run the tests, first install the relevant dependencies: 26 | 27 | ```shell-session 28 | $ apt install --no-install-recommends --yes llvm-15 llvm-15-tools 29 | $ pip install lit 30 | ``` 31 | 32 | Next, run the `lit` testsuite (replacing `/path/to/llvm/bin` with the correct path to your LLVM 33 | installation, if required): 34 | 35 | ```shell-session 36 | $ cargo build # in debug mode.. 37 | $ lit -v --path "$PWD/target/debug/:/path/to/llvm/bin/" ./tests 38 | $ cargo build --release # ..or in release mode 39 | $ lit -v --path "$PWD/target/release/:/path/to/llvm/bin/" ./tests 40 | ``` 41 | 42 | We use `rustfmt` to automatically format and style all of our code. To install and use `rustfmt`: 43 | 44 | ```shell-session 45 | $ rustup component add rustfmt 46 | $ cargo fmt 47 | ``` 48 | 49 | ### Filing an issue 50 | Think you've found a bug? File an issue! To help us understand and reproduce the 51 | issue, provide us with: 52 | 53 | * The (preferably minimal) test case 54 | * Steps to reproduce the issue using the test case 55 | * The expected result of following those steps 56 | * The actual result of following those steps 57 | 58 | Definitely file an issue if you see an unexpected panic originating from within `thorin`! 59 | `thorin` should never panic unless it is explicitly documented to panic in the specific 60 | circumstances provided. 61 | 62 |
63 | 64 | #### Name 65 | 66 | thorin is named after Thorin Oakenshield from The Hobbit, as Thorin is 67 | a dwarf who leads other dwarves. thorin uses the gimli library 68 | (named after a dwarf from Lord of the Rings) to read DWARF format debug information, 69 | the name of which is a medieval fantasy complement to ELF, the file format for executables 70 | and object files. 71 | 72 | 73 |
74 | 75 | 76 | You could also call this project rust-dwp, if you'd prefer that. 77 | 78 | 79 |
80 | 81 | #### Author and acknowledgements 82 | 83 | thorin is authored by David Wood of Huawei 84 | Technologies Research & Development (UK) Ltd. thorin is maintained by the 85 | Rust Compiler Team. 86 | 87 | 88 |
89 | 90 | 91 | In addition, thanks to the authors of object and gimli, on which this 92 | utility depends heavily; and to Philip Craig for advice 93 | and reviews during initial implementation of thorin. 94 | 95 | 96 |
97 | 98 | #### License 99 | 100 | Licensed under either of Apache License, 101 | Version 2.0 or MIT license at your option. 102 | 103 | 104 |
105 | 106 | 107 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in 108 | this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without 109 | any additional terms or conditions. 110 | 111 | 112 |
113 | 114 | #### Code of conduct 115 | 116 | When contributing or interacting with this project, we ask abide the 117 | Rust Code of Conduct and ask that you do 118 | too. 119 | 120 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_small_heuristics = "Max" 2 | -------------------------------------------------------------------------------- /tests/absolute-paths.test: -------------------------------------------------------------------------------- 1 | ; RUN: rm -rf %t 2 | ; RUN: mkdir -p %t 3 | ; RUN: llc %s -mtriple=x86_64-linux --split-dwarf-file=%t/test.dwo \ 4 | ; RUN: --split-dwarf-output=%t/test.dwo --filetype=obj -o %t/test.o 5 | ; RUN: llvm-dwarfdump -v %t/test.dwo | FileCheck %s -DPATH=%t 6 | ; RUN: thorin -e %t/test.o -o %t/test.dwp 7 | ; RUN: llvm-dwarfdump -v %t/test.dwp | FileCheck %s -DPATH=%t 8 | 9 | ; CHECK-LABEL: .debug_abbrev.dwo contents: 10 | ; CHECK: DW_AT_name 11 | ; CHECK: DW_AT_GNU_dwo_name 12 | ; CHECK: DW_AT_name 13 | ; CHECK-LABEL: .debug_str.dwo contents: 14 | ; CHECK: "banana" 15 | ; CHECK: "/tmp/test.c" 16 | ; CHECK: "[[PATH]]/test.dwo" 17 | 18 | define void @banana() !dbg !8 { 19 | ret void, !dbg !12 20 | } 21 | 22 | !llvm.dbg.cu = !{!0} 23 | !llvm.module.flags = !{!3, !4, !5, !6} 24 | !llvm.ident = !{!7} 25 | 26 | !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.1", isOptimized: true, runtimeVersion: 0, splitDebugFilename: "test.dwo", emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: GNU) 27 | !1 = !DIFile(filename: "/tmp/test.c", directory: "/tmp") 28 | !2 = !{} 29 | !3 = !{i32 7, !"Dwarf Version", i32 4} 30 | !4 = !{i32 2, !"Debug Info Version", i32 3} 31 | !5 = !{i32 1, !"wchar_size", i32 4} 32 | !6 = !{i32 7, !"PIC Level", i32 2} 33 | !7 = !{!"clang version 11.0.1"} 34 | !8 = distinct !DISubprogram(name: "banana", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) 35 | !9 = !DIFile(filename: "test.c", directory: "/tmp") 36 | !10 = !DISubroutineType(types: !11) 37 | !11 = !{null} 38 | !12 = !DILocation(line: 1, column: 20, scope: !8) 39 | -------------------------------------------------------------------------------- /tests/archive-with-arbitrary.test: -------------------------------------------------------------------------------- 1 | RUN: rm -rf %t 2 | RUN: mkdir %t 3 | RUN: cd %t 4 | RUN: llvm-ar q inputs.ar \ 5 | RUN: %p/inputs/dwos-list-from-exec-a.dwo \ 6 | RUN: %p/inputs/dwos-list-from-exec-b.dwo \ 7 | RUN: %p/inputs/dwos-list-from-exec-c.dwo \ 8 | RUN: %p/inputs/dwos-list-from-exec-d.dwo \ 9 | RUN: %p/inputs/dwos-list-from-exec-e.dwo \ 10 | RUN: %p/archive-with-arbitrary.test 11 | RUN: thorin inputs.ar \ 12 | RUN: -e %p/inputs/dwos-list-from-exec-main \ 13 | RUN: -e %p/inputs/dwos-list-from-exec-libd.so -o - | llvm-dwarfdump -v - | FileCheck %s 14 | 15 | CHECK-LABEL: .debug_abbrev.dwo contents: 16 | 17 | CHECK-LABEL: Abbrev table for offset: 18 | CHECK: DW_TAG_compile_unit 19 | CHECK: DW_TAG_subprogram 20 | 21 | CHECK-LABEL: Abbrev table for offset: 22 | CHECK: DW_TAG_compile_unit 23 | CHECK: DW_TAG_subprogram 24 | 25 | CHECK-LABEL: Abbrev table for offset: 26 | CHECK: DW_TAG_compile_unit 27 | CHECK: DW_TAG_subprogram 28 | 29 | CHECK-LABEL: Abbrev table for offset: 30 | CHECK: DW_TAG_compile_unit 31 | CHECK: DW_TAG_subprogram 32 | 33 | CHECK-LABEL: Abbrev table for offset: 34 | CHECK: DW_TAG_compile_unit 35 | CHECK: DW_TAG_subprogram 36 | 37 | CHECK: .debug_info.dwo contents: 38 | CHECK: [[AOFF:0x[0-9a-f]*]]: 39 | 40 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 41 | CHECK: DW_TAG_compile_unit 42 | CHECK: DW_AT_name {{.*}} "a.cpp" 43 | CHECK: DW_TAG_subprogram 44 | CHECK: DW_AT_name {{.*}} "a" 45 | 46 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 47 | CHECK: DW_TAG_compile_unit 48 | CHECK: DW_AT_name {{.*}} "b.cpp" 49 | CHECK: DW_TAG_subprogram 50 | CHECK: DW_AT_name {{.*}} "b" 51 | CHECK: DW_TAG_subprogram 52 | CHECK: DW_AT_name {{.*}} "main" 53 | 54 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 55 | CHECK: DW_TAG_compile_unit 56 | CHECK: DW_AT_name {{.*}} "c.cpp" 57 | CHECK: DW_TAG_subprogram 58 | CHECK: DW_AT_name {{.*}} "c" 59 | 60 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 61 | CHECK: DW_TAG_compile_unit 62 | CHECK: DW_AT_name {{.*}} "d.cpp" 63 | CHECK: DW_TAG_subprogram 64 | CHECK: DW_AT_name {{.*}} "d" 65 | 66 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 67 | CHECK: DW_TAG_compile_unit 68 | CHECK: DW_AT_name {{.*}} "e.cpp" 69 | CHECK: DW_TAG_subprogram 70 | CHECK: DW_AT_name {{.*}} "e" 71 | -------------------------------------------------------------------------------- /tests/archive.test: -------------------------------------------------------------------------------- 1 | RUN: rm -rf %t 2 | RUN: mkdir %t 3 | RUN: cd %t 4 | RUN: llvm-ar q inputs.ar \ 5 | RUN: %p/inputs/dwos-list-from-exec-a.dwo \ 6 | RUN: %p/inputs/dwos-list-from-exec-b.dwo \ 7 | RUN: %p/inputs/dwos-list-from-exec-c.dwo \ 8 | RUN: %p/inputs/dwos-list-from-exec-d.dwo \ 9 | RUN: %p/inputs/dwos-list-from-exec-e.dwo 10 | RUN: thorin inputs.ar \ 11 | RUN: -e %p/inputs/dwos-list-from-exec-main \ 12 | RUN: -e %p/inputs/dwos-list-from-exec-libd.so -o - | llvm-dwarfdump -v - | FileCheck %s 13 | RUN: not thorin \ 14 | RUN: -e %p/inputs/dwos-list-from-exec-main \ 15 | RUN: -e %p/inputs/dwos-list-from-exec-libd.so -o - 2>&1 | FileCheck --check-prefix=MISSING %s 16 | 17 | CHECK-LABEL: .debug_abbrev.dwo contents: 18 | 19 | CHECK-LABEL: Abbrev table for offset: 20 | CHECK: DW_TAG_compile_unit 21 | CHECK: DW_TAG_subprogram 22 | 23 | CHECK-LABEL: Abbrev table for offset: 24 | CHECK: DW_TAG_compile_unit 25 | CHECK: DW_TAG_subprogram 26 | 27 | CHECK-LABEL: Abbrev table for offset: 28 | CHECK: DW_TAG_compile_unit 29 | CHECK: DW_TAG_subprogram 30 | 31 | CHECK-LABEL: Abbrev table for offset: 32 | CHECK: DW_TAG_compile_unit 33 | CHECK: DW_TAG_subprogram 34 | 35 | CHECK-LABEL: Abbrev table for offset: 36 | CHECK: DW_TAG_compile_unit 37 | CHECK: DW_TAG_subprogram 38 | 39 | CHECK: .debug_info.dwo contents: 40 | CHECK: [[AOFF:0x[0-9a-f]*]]: 41 | 42 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 43 | CHECK: DW_TAG_compile_unit 44 | CHECK: DW_AT_name {{.*}} "a.cpp" 45 | CHECK: DW_TAG_subprogram 46 | CHECK: DW_AT_name {{.*}} "a" 47 | 48 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 49 | CHECK: DW_TAG_compile_unit 50 | CHECK: DW_AT_name {{.*}} "b.cpp" 51 | CHECK: DW_TAG_subprogram 52 | CHECK: DW_AT_name {{.*}} "b" 53 | CHECK: DW_TAG_subprogram 54 | CHECK: DW_AT_name {{.*}} "main" 55 | 56 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 57 | CHECK: DW_TAG_compile_unit 58 | CHECK: DW_AT_name {{.*}} "c.cpp" 59 | CHECK: DW_TAG_subprogram 60 | CHECK: DW_AT_name {{.*}} "c" 61 | 62 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 63 | CHECK: DW_TAG_compile_unit 64 | CHECK: DW_AT_name {{.*}} "d.cpp" 65 | CHECK: DW_TAG_subprogram 66 | CHECK: DW_AT_name {{.*}} "d" 67 | 68 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 69 | CHECK: DW_TAG_compile_unit 70 | CHECK: DW_AT_name {{.*}} "e.cpp" 71 | CHECK: DW_TAG_subprogram 72 | CHECK: DW_AT_name {{.*}} "e" 73 | 74 | MISSING: Error: Failed verifying final DWARF package 75 | MISSING: Unit 0x{{.*}} referenced by executable was not found 76 | -------------------------------------------------------------------------------- /tests/columns.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/columns-a.dwo %p/inputs/columns-b.dwo -o - \ 2 | RUN: | llvm-dwarfdump -v - | FileCheck --check-prefixes=CHECK %s 3 | 4 | CHECK-LABEL: .debug_cu_index contents: 5 | CHECK: Index Signature INFO ABBREV LINE STR_OFFSETS 6 | CHECK: 2 0x{{.*}} [0x0000002d, 0x00000052) [0x0000002c, 0x00000056) [0x00000000, 0x00000000) [0x00000008, 0x00000018) 7 | CHECK: 3 0x{{.*}} [0x00000000, 0x0000002d) [0x00000000, 0x0000002c) [0x00000000, 0x00000025) [0x00000000, 0x00000008) 8 | -------------------------------------------------------------------------------- /tests/compress-fail.test: -------------------------------------------------------------------------------- 1 | RUN: not thorin %p/inputs/compress-fail-a.dwo -o %t 2>&1 | FileCheck %s 2 | RUN: not thorin %p/inputs/empty-compressed-section.dwo -o %t 2>&1 | FileCheck %s 3 | RUN: not thorin %p/inputs/invalid-compressed.dwo -o %t 2>&1 | FileCheck --check-prefix=INVALID %s 4 | 5 | CHECK: Error: Failed to add `{{.*}}` to DWARF package 6 | CHECK: Invalid GNU compressed section header 7 | 8 | # `llvm-dwp` fails to decompress this, but `thorin` is able to but the contents aren't meaningful. 9 | INVALID: Error: Failed to add `{{.*}}/invalid-compressed.dwo` to DWARF package 10 | INVALID: Read string at offset 0x00000000 of `.debug_str.dwo` section 11 | -------------------------------------------------------------------------------- /tests/compress.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/compress.dwo -o %t 2 | RUN: llvm-dwarfdump -v %t | FileCheck %s 3 | 4 | CHECK: .debug_info.dwo contents: 5 | CHECK: Compile Unit: 6 | CHECK: DW_TAG_compile_unit 7 | CHECK: DW_TAG_subprogram 8 | CHECK: DW_TAG_formal_parameter 9 | -------------------------------------------------------------------------------- /tests/cu-and-tu-info-section-v5.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj --split-dwarf-file=%t.dwo \ 2 | # RUN: -dwarf-version=5 %s -o %t.o 3 | # RUN: thorin %t.dwo -o %t.dwp 4 | # RUN: llvm-dwarfdump -debug-info -debug-tu-index %t.dwp | FileCheck %s 5 | 6 | # CHECK-DAG: .debug_info.dwo contents 7 | # CHECK: 0x00000000: Type Unit: length = 0x00000017, format = DWARF32, version = 0x0005, unit_type = DW_UT_split_type, abbr_offset = 0x0000, addr_size = 0x08, name = '', type_signature = {{.*}}, type_offset = 0x0019 (next unit at 0x0000001b) 8 | # CHECK: 0x0000001b: Compile Unit: length = 0x00000011, format = DWARF32, version = 0x0005, unit_type = DW_UT_split_compile, abbr_offset = 0x0000, addr_size = 0x08, DWO_id = {{.*}} (next unit at 0x00000030) 9 | 10 | .section .debug_info.dwo,"e",@progbits 11 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 12 | .Ldebug_info_dwo_start0: 13 | .short 5 # DWARF version number 14 | .byte 6 # DWARF Unit Type (DW_UT_split_type) 15 | .byte 8 # Address Size (in bytes) 16 | .long 0 # Offset Into Abbrev. Section 17 | .quad 5657452045627120676 # Type Signature 18 | .long 25 # Type DIE Offset 19 | .byte 1 # Abbrev [1] DW_TAG_type_unit 20 | .byte 2 # Abbrev [2] DW_TAG_structure_type 21 | .byte 0 # End Of Children Mark 22 | .Ldebug_info_dwo_end0: 23 | .section .debug_info.dwo,"e",@progbits 24 | .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit 25 | .Ldebug_info_dwo_start1: 26 | .short 5 # DWARF version number 27 | .byte 5 # DWARF Unit Type (DW_UT_split_compile) 28 | .byte 8 # Address Size (in bytes) 29 | .long 0 # Offset Into Abbrev. Section 30 | .quad -1506010254921578184 31 | .byte 3 # Abbrev [3] DW_TAG_compile_unit 32 | .Ldebug_info_dwo_end1: 33 | .section .debug_abbrev.dwo,"e",@progbits 34 | .byte 1 # Abbreviation Code 35 | .byte 65 # DW_TAG_type_unit 36 | .byte 1 # DW_CHILDREN_yes 37 | .byte 0 # EOM(1) 38 | .byte 0 # EOM(2) 39 | .byte 3 # Abbreviation Code 40 | .byte 17 # DW_TAG_compile_unit 41 | .byte 0 # DW_CHILDREN_no 42 | .byte 0 # EOM(1) 43 | .byte 0 # EOM(2) 44 | .byte 0 # EOM(3) 45 | -------------------------------------------------------------------------------- /tests/debug-macro-v5.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux --filetype=obj --split-dwarf-file=%t.dwo \ 2 | # RUN: -dwarf-version=5 %s -o %t.o 3 | # RUN: thorin %t.dwo -o %t.dwp 2>&1 4 | # RUN: llvm-dwarfdump -debug-macro -debug-cu-index %t.dwp | FileCheck %s 5 | 6 | # CHECK-DAG: .debug_macro.dwo contents: 7 | # CHECK: macro header: version = 0x0005, flags = 0x00, format = DWARF32 8 | # CHECK-NEXT: DW_MACRO_start_file - lineno: 0 filenum: 0 9 | # CHECK-NEXT: DW_MACRO_define_strx - lineno: 1 macro: x 5 10 | # CHECK-NEXT: DW_MACRO_end_file 11 | 12 | # CHECK-DAG: .debug_cu_index contents: 13 | # CHECK-NEXT: version = 5, units = 1, slots = 2 14 | # CHECK: Index Signature INFO ABBREV STR_OFFSETS MACRO 15 | # CHECK: 1 0x0000000000000000 [0x00000000, 0x00000019) [0x00000000, 0x00000008) [0x00000000, 0x0000000c) [0x00000000, 0x0000000b) 16 | 17 | .section .debug_info.dwo,"e",@progbits 18 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 19 | .Ldebug_info_dwo_start0: 20 | .short 5 # DWARF version number 21 | .byte 5 # DWARF Unit Type (DW_UT_split_compile) 22 | .byte 8 # Address Size (in bytes) 23 | .long 0 # Offset Into Abbrev. Section 24 | .quad 0 25 | .byte 1 # Abbrev [1] 0x14:0x5 DW_TAG_compile_unit 26 | .long 0 # DW_AT_macros 27 | .Ldebug_info_dwo_end0: 28 | .section .debug_macro.dwo,"e",@progbits 29 | .short 5 # Macro information version 30 | .byte 0 # Flags: 32 bit 31 | .byte 3 # DW_MACRO_start_file 32 | .byte 0 # Line Number 33 | .byte 0 # File Number 34 | .byte 11 # DW_MACRO_define_strx 35 | .byte 1 # Line Number 36 | .byte 0 # Macro String 37 | .byte 4 # DW_MACRO_end_file 38 | .byte 0 # End Of Macro List Mark 39 | .section .debug_abbrev.dwo,"e",@progbits 40 | .byte 1 # Abbreviation Code 41 | .byte 17 # DW_TAG_compile_unit 42 | .byte 0 # DW_CHILDREN_no 43 | .byte 121 # DW_AT_macros 44 | .byte 23 # DW_FORM_sec_offset 45 | .byte 0 # EOM(1) 46 | .byte 0 # EOM(2) 47 | .byte 0 # EOM(3) 48 | .section .debug_str.dwo,"eMS",@progbits,1 49 | .asciz "x 5" # string offset=0 50 | .section .debug_str_offsets.dwo,"e",@progbits 51 | .long 8 # Length of String Offsets Set 52 | .short 5 53 | .short 0 54 | .long 0 55 | -------------------------------------------------------------------------------- /tests/duplicate.test: -------------------------------------------------------------------------------- 1 | RUN: not thorin %p/inputs/duplicate-c.dwo %p/inputs/duplicate-c.dwo -o %t 2>&1 \ 2 | RUN: | FileCheck --check-prefix=DWOS %s 3 | 4 | RUN: not thorin %p/inputs/duplicate-ac.dwp %p/inputs/duplicate-c.dwo -o %t 2>&1 \ 5 | RUN: | FileCheck --check-prefix=1DWP %s 6 | 7 | RUN: not thorin %p/inputs/duplicate-c.dwo %p/inputs/duplicate-bc.dwp -o %t 2>&1 \ 8 | RUN: | FileCheck --check-prefix=2DWP %s 9 | 10 | RUN: not thorin %p/inputs/duplicate-dwo-name-c.dwo %p/inputs/duplicate-dwo-name-c.dwo -o %t 2>&1 \ 11 | RUN: | FileCheck --check-prefix=DWODWOS %s 12 | 13 | RUN: not thorin %p/inputs/duplicate-dwo-name-ac.dwp %p/inputs/duplicate-dwo-name-c.dwo -o %t 2>&1 \ 14 | RUN: | FileCheck --check-prefix=DWO1DWP %s 15 | 16 | RUN: not thorin %p/inputs/duplicate-dwo-name-c.dwo %p/inputs/duplicate-dwo-name-bc.dwp -o %t 2>&1 \ 17 | RUN: | FileCheck --check-prefix=DWO2DWP %s 18 | 19 | DWOS: Error: Failed to add `{{.*}}` to DWARF package 20 | DWOS: Duplicate split compilation unit ({{.*}}) 21 | 22 | 1DWP: Error: Failed to add `{{.*}}` to DWARF package 23 | 1DWP: Duplicate split compilation unit ({{.*}}) 24 | 25 | 2DWP: Error: Failed to add `{{.*}}` to DWARF package 26 | 2DWP: Duplicate split compilation unit ({{.*}}) 27 | 28 | DWODWOS: Error: Failed to add `{{.*}}` to DWARF package 29 | DWODWOS: Duplicate split compilation unit ({{.*}}) 30 | 31 | DWO1DWP: Error: Failed to add `{{.*}}` to DWARF package 32 | DWO1DWP: Duplicate split compilation unit ({{.*}}) 33 | 34 | DWO2DWP: Error: Failed to add `{{.*}}` to DWARF package 35 | DWO2DWP: Duplicate split compilation unit ({{.*}}) 36 | -------------------------------------------------------------------------------- /tests/dwos-list-from-exec-simple.test: -------------------------------------------------------------------------------- 1 | RUN: rm -rf %t 2 | RUN: mkdir %t 3 | RUN: cd %t 4 | RUN: cp %p/inputs/dwos-list-from-exec-a.dwo a.dwo 5 | RUN: cp %p/inputs/dwos-list-from-exec-b.dwo b.dwo 6 | RUN: cp %p/inputs/dwos-list-from-exec-c.dwo c.dwo 7 | RUN: cp %p/inputs/dwos-list-from-exec-d.dwo d.dwo 8 | RUN: cp %p/inputs/dwos-list-from-exec-e.dwo e.dwo 9 | RUN: cp %p/inputs/dwos-list-from-exec-main main 10 | RUN: cp %p/inputs/dwos-list-from-exec-libd.so libd.so 11 | RUN: thorin c.dwo e.dwo -e main -e libd.so -o - | llvm-dwarfdump -v - | FileCheck %s 12 | 13 | CHECK-LABEL: .debug_abbrev.dwo contents: 14 | 15 | CHECK-LABEL: Abbrev table for offset: 16 | CHECK: DW_TAG_compile_unit 17 | CHECK: DW_TAG_subprogram 18 | 19 | CHECK-LABEL: Abbrev table for offset: 20 | CHECK: DW_TAG_compile_unit 21 | CHECK: DW_TAG_subprogram 22 | 23 | CHECK-LABEL: Abbrev table for offset: 24 | CHECK: DW_TAG_compile_unit 25 | CHECK: DW_TAG_subprogram 26 | 27 | CHECK-LABEL: Abbrev table for offset: 28 | CHECK: DW_TAG_compile_unit 29 | CHECK: DW_TAG_subprogram 30 | 31 | CHECK-LABEL: Abbrev table for offset: 32 | CHECK: DW_TAG_compile_unit 33 | CHECK: DW_TAG_subprogram 34 | 35 | CHECK: .debug_info.dwo contents: 36 | CHECK: [[AOFF:0x[0-9a-f]*]]: 37 | 38 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 39 | CHECK: DW_TAG_compile_unit 40 | CHECK: DW_AT_name {{.*}} "c.cpp" 41 | CHECK: DW_TAG_subprogram 42 | CHECK: DW_AT_name {{.*}} "c" 43 | 44 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 45 | CHECK: DW_TAG_compile_unit 46 | CHECK: DW_AT_name {{.*}} "e.cpp" 47 | CHECK: DW_TAG_subprogram 48 | CHECK: DW_AT_name {{.*}} "e" 49 | 50 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 51 | CHECK: DW_TAG_compile_unit 52 | CHECK: DW_AT_name {{.*}} "a.cpp" 53 | CHECK: DW_TAG_subprogram 54 | CHECK: DW_AT_name {{.*}} "a" 55 | 56 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 57 | CHECK: DW_TAG_compile_unit 58 | CHECK: DW_AT_name {{.*}} "b.cpp" 59 | CHECK: DW_TAG_subprogram 60 | CHECK: DW_AT_name {{.*}} "b" 61 | CHECK: DW_TAG_subprogram 62 | CHECK: DW_AT_name {{.*}} "main" 63 | 64 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004 65 | CHECK: DW_TAG_compile_unit 66 | CHECK: DW_AT_name {{.*}} "d.cpp" 67 | CHECK: DW_TAG_subprogram 68 | CHECK: DW_AT_name {{.*}} "d" 69 | -------------------------------------------------------------------------------- /tests/empty.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/empty.dwo -o %t 2 | RUN: llvm-dwarfdump -v %t | FileCheck %s 3 | 4 | CHECK: file format 5 | CHECK-NOT: .debug_cu_index 6 | CHECK-NOT: version 7 | CHECK-NOT: .debug_tu_index 8 | CHECK-NOT: version 9 | -------------------------------------------------------------------------------- /tests/gcc-type.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/gcc-type.dwo -o - | llvm-dwarfdump -v - | FileCheck %s 2 | RUN: not thorin %p/inputs/gcc-type.dwo %p/inputs/gcc-type.dwo -o %t 2>&1 | FileCheck --check-prefix=DUP %s 3 | 4 | CHECK: Type Unit 5 | CHECK: Type Unit 6 | 7 | DUP: Error: Failed to add `{{.*}}/gcc-type.dwo` to DWARF package 8 | DUP: Duplicate split compilation unit ({{.*}}) 9 | -------------------------------------------------------------------------------- /tests/handle-strx.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/handle-strx-v5.dwo -o %t 2>/dev/null 2 | RUN: llvm-dwarfdump --verbose %t 2>/dev/null | FileCheck --check-prefix=READ_STRX %s 3 | 4 | RUN: not thorin %p/inputs/handle-strx-v5.dwo %p/inputs/handle-strx-v5.dwo -o %t 2>&1 \ 5 | RUN: | FileCheck --check-prefix=PARSE_STRX %s 6 | 7 | READ_STRX: DW_AT_name [DW_FORM_strx1]{{.*}}dw5.cc 8 | 9 | PARSE_STRX: Error: Failed to add `{{.*}}/handle-strx-v5.dwo` to DWARF package 10 | PARSE_STRX: Duplicate split compilation unit ({{.*}}) 11 | -------------------------------------------------------------------------------- /tests/help.test: -------------------------------------------------------------------------------- 1 | # RUN: thorin --help | FileCheck %s --check-prefix HELP 2 | 3 | # HELP: USAGE 4 | # HELP: FLAGS 5 | # HELP: OPTIONS 6 | # HELP: ARGS 7 | -------------------------------------------------------------------------------- /tests/incompatible-cu-index-versions.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.dwp 2 | # RUN: not thorin %t.dwp -o %t 2>&1 | FileCheck %s 3 | 4 | # CHECK: Error: Failed to add `{{.*}}/incompatible-cu-index-versions.s.tmp.dwp` to DWARF package 5 | # CHECK: Incompatible `.debug_cu_index` index version: found version 2, expected version 5 6 | 7 | .section .debug_info.dwo, "e", @progbits 8 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 9 | .Ldebug_info_dwo_start0: 10 | .short 5 # DWARF version number 11 | .byte 5 # DWARF Unit type (DW_UT_split_compile) 12 | .byte 8 # Address Size (in bytes) 13 | .long 0 # Offset Into Abbrev. Section 14 | .quad -346972125991005518 15 | .byte 0 # Abbrev [9] 0xb:0x37 DW_TAG_compile_unit 16 | .Ldebug_info_dwo_end0: 17 | .section .debug_cu_index, "", @progbits 18 | ## Header: 19 | .short 2 # Version 20 | .space 2 # Padding 21 | .long 2 # Section count 22 | .long 1 # Unit count 23 | .long 2 # Slot count 24 | ## Hash Table of Signatures: 25 | .quad 0x1100001122222222 26 | .quad 0 27 | ## Parallel Table of Indexes: 28 | .long 1 29 | .long 0 30 | ## Table of Section Offsets: 31 | ## Row 0: 32 | .long 1 # DW_SECT_INFO 33 | .long 3 # DW_SECT_ABBREV 34 | ## Row 1: 35 | .long 0 36 | .long 0 37 | ## Table of Section Sizes: 38 | .long 1 39 | .long 1 40 | -------------------------------------------------------------------------------- /tests/incompatible-tu-index-version.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.dwp 2 | # RUN: not thorin %t.dwp -o %t 2>&1 | FileCheck %s 3 | 4 | # CHECK: Error: Failed to add `{{.*}}/incompatible-tu-index-version.s.tmp.dwp` to DWARF package 5 | # CHECK: Incompatible `.debug_tu_index` index version: found version 5, expected version 2 6 | 7 | .section .debug_abbrev.dwo, "e", @progbits 8 | .LAbbrevBegin: 9 | .uleb128 1 # Abbreviation Code 10 | .uleb128 17 # DW_TAG_compile_unit 11 | .byte 1 # DW_CHILDREN_no 12 | .uleb128 0x2131 # DW_AT_GNU_dwo_id 13 | .uleb128 7 # DW_FORM_data8 14 | .byte 0 # EOM(1) 15 | .byte 0 # EOM(2) 16 | .byte 0 # EOM(3) 17 | .LAbbrevEnd: 18 | 19 | .section .debug_info.dwo, "e", @progbits 20 | .LCUBegin: 21 | .long .LCUEnd-.LCUVersion # Length of Unit 22 | .LCUVersion: 23 | .short 4 # Version 24 | .long 0 # Abbrev offset 25 | .byte 8 # Address size 26 | .uleb128 1 # Abbrev [1] DW_TAG_compile_unit 27 | .quad 0x1100001122222222 # DW_AT_GNU_dwo_id 28 | .LCUEnd: 29 | 30 | .section .debug_types.dwo, "e", @progbits 31 | .space 1 32 | 33 | .section .debug_cu_index, "", @progbits 34 | ## Header: 35 | .long 2 # Version 36 | .long 2 # Section count 37 | .long 1 # Unit count 38 | .long 2 # Slot count 39 | ## Hash Table of Signatures: 40 | .quad 0x1100001122222222 41 | .quad 0 42 | ## Parallel Table of Indexes: 43 | .long 1 44 | .long 0 45 | ## Table of Section Offsets: 46 | ## Row 0: 47 | .long 1 # DW_SECT_INFO 48 | .long 3 # DW_SECT_ABBREV 49 | ## Row 1: 50 | .long 0 # Offset in .debug_info.dwo 51 | .long 0 # Offset in .debug_abbrev.dwo 52 | ## Table of Section Sizes: 53 | .long .LCUEnd-.LCUBegin # Size in .debug_info.dwo 54 | .long .LAbbrevEnd-.LAbbrevBegin # Size in .debug_abbrev.dwo 55 | 56 | .section .debug_tu_index, "", @progbits 57 | ## Header: 58 | .short 5 # Version 59 | .space 2 # Padding 60 | .long 2 # Section count 61 | .long 1 # Unit count 62 | .long 2 # Slot count 63 | ## Hash Table of Signatures: 64 | .quad 0x1100003333333333 65 | .quad 0 66 | ## Parallel Table of Indexes: 67 | .long 1 68 | .long 0 69 | ## Table of Section Offsets: 70 | ## Row 0: 71 | .long 1 # DW_SECT_INFO 72 | .long 3 # DW_SECT_ABBREV 73 | ## Row 1: 74 | .long 0 75 | .long 0 76 | ## Table of Section Sizes: 77 | .long 1 78 | .long 1 79 | -------------------------------------------------------------------------------- /tests/info-v5.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj --split-dwarf-file=%t.dwo -dwarf-version=5 %s -o %t.o 2 | 3 | # RUN: thorin %t.dwo -o %t.dwp 4 | # RUN: llvm-dwarfdump -v %t.dwp | FileCheck %s 5 | 6 | # CHECK-DAG: .debug_info.dwo contents: 7 | # CHECK: 0x00000000: Compile Unit: length = 0x00000050, format = DWARF32, version = 0x0005, unit_type = DW_UT_split_compile, abbr_offset = 0x0000, addr_size = 0x08, DWO_id = [[DWOID:.*]] (next unit at 0x00000054) 8 | 9 | # CHECK-DAG: .debug_cu_index contents: 10 | # CHECK: version = 5, units = 1, slots = 2 11 | # CHECK: Index Signature INFO ABBREV 12 | # CHECK: 1 [[DWOID]] [0x00000000, 0x00000054) [0x00000000, 0x0000002a) 13 | 14 | .section .debug_info.dwo,"e",@progbits 15 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 16 | .Ldebug_info_dwo_start0: 17 | .short 5 # DWARF version number 18 | .byte 5 # DWARF Unit Type 19 | .byte 8 # Address Size (in bytes) 20 | .long 0 # Offset Into Abbrev. Section 21 | .quad -1173350285159172090 22 | .byte 1 # Abbrev [1] 0x14:0x16 DW_TAG_compile_unit 23 | .asciz "clang version 11.0.0" # DW_AT_producer 24 | .short 12 # DW_AT_language 25 | .asciz "int.c" # DW_AT_name 26 | .asciz "int.dwo" # DW_AT_dwo_name 27 | .byte 2 # Abbrev [2] 0x1a:0xb DW_TAG_variable 28 | .asciz "integer" # DW_AT_name 29 | .long 37 # DW_AT_type 30 | # DW_AT_external 31 | .byte 0 # DW_AT_decl_file 32 | .byte 1 # DW_AT_decl_line 33 | .byte 2 # DW_AT_location 34 | .byte 161 35 | .byte 0 36 | .byte 3 # Abbrev [3] 0x25:0x4 DW_TAG_base_type 37 | .asciz "int" # DW_AT_name 38 | .byte 5 # DW_AT_encoding 39 | .byte 4 # DW_AT_byte_size 40 | .byte 0 # End Of Children Mark 41 | .Ldebug_info_dwo_end0: 42 | .section .debug_abbrev.dwo,"e",@progbits 43 | .byte 1 # Abbreviation Code 44 | .byte 17 # DW_TAG_compile_unit 45 | .byte 1 # DW_CHILDREN_yes 46 | .byte 37 # DW_AT_producer 47 | .byte 8 # DW_FORM_string 48 | .byte 19 # DW_AT_language 49 | .byte 5 # DW_FORM_data2 50 | .byte 3 # DW_AT_name 51 | .byte 8 # DW_FORM_string 52 | .byte 118 # DW_AT_dwo_name 53 | .byte 8 # DW_FORM_string 54 | .byte 0 # EOM(1) 55 | .byte 0 # EOM(2) 56 | .byte 2 # Abbreviation Code 57 | .byte 52 # DW_TAG_variable 58 | .byte 0 # DW_CHILDREN_no 59 | .byte 3 # DW_AT_name 60 | .byte 8 # DW_FORM_string 61 | .byte 73 # DW_AT_type 62 | .byte 19 # DW_FORM_ref4 63 | .byte 63 # DW_AT_external 64 | .byte 25 # DW_FORM_flag_present 65 | .byte 58 # DW_AT_decl_file 66 | .byte 11 # DW_FORM_data1 67 | .byte 59 # DW_AT_decl_line 68 | .byte 11 # DW_FORM_data1 69 | .byte 2 # DW_AT_location 70 | .byte 24 # DW_FORM_exprloc 71 | .byte 0 # EOM(1) 72 | .byte 0 # EOM(2) 73 | .byte 3 # Abbreviation Code 74 | .byte 36 # DW_TAG_base_type 75 | .byte 0 # DW_CHILDREN_no 76 | .byte 3 # DW_AT_name 77 | .byte 8 # DW_FORM_string 78 | .byte 62 # DW_AT_encoding 79 | .byte 11 # DW_FORM_data1 80 | .byte 11 # DW_AT_byte_size 81 | .byte 11 # DW_FORM_data1 82 | .byte 0 # EOM(1) 83 | .byte 0 # EOM(2) 84 | .byte 0 # EOM(3) 85 | -------------------------------------------------------------------------------- /tests/inputs/columns-a.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/columns-a.dwo -------------------------------------------------------------------------------- /tests/inputs/columns-b.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/columns-b.dwo -------------------------------------------------------------------------------- /tests/inputs/compress-fail-a.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/compress-fail-a.dwo -------------------------------------------------------------------------------- /tests/inputs/compress-fail.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/compress-fail.dwo -------------------------------------------------------------------------------- /tests/inputs/compress.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/compress.dwo -------------------------------------------------------------------------------- /tests/inputs/duplicate-ac.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/duplicate-ac.dwp -------------------------------------------------------------------------------- /tests/inputs/duplicate-bc.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/duplicate-bc.dwp -------------------------------------------------------------------------------- /tests/inputs/duplicate-c.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/duplicate-c.dwo -------------------------------------------------------------------------------- /tests/inputs/duplicate-dwo-name-ac.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/duplicate-dwo-name-ac.dwp -------------------------------------------------------------------------------- /tests/inputs/duplicate-dwo-name-bc.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/duplicate-dwo-name-bc.dwp -------------------------------------------------------------------------------- /tests/inputs/duplicate-dwo-name-c.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/duplicate-dwo-name-c.dwo -------------------------------------------------------------------------------- /tests/inputs/dwos-list-from-exec-a.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/dwos-list-from-exec-a.dwo -------------------------------------------------------------------------------- /tests/inputs/dwos-list-from-exec-b.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/dwos-list-from-exec-b.dwo -------------------------------------------------------------------------------- /tests/inputs/dwos-list-from-exec-c.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/dwos-list-from-exec-c.dwo -------------------------------------------------------------------------------- /tests/inputs/dwos-list-from-exec-d.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/dwos-list-from-exec-d.dwo -------------------------------------------------------------------------------- /tests/inputs/dwos-list-from-exec-e.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/dwos-list-from-exec-e.dwo -------------------------------------------------------------------------------- /tests/inputs/dwos-list-from-exec-libd.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/dwos-list-from-exec-libd.so -------------------------------------------------------------------------------- /tests/inputs/dwos-list-from-exec-main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/dwos-list-from-exec-main -------------------------------------------------------------------------------- /tests/inputs/empty-compressed-section.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/empty-compressed-section.dwo -------------------------------------------------------------------------------- /tests/inputs/empty.dwo: -------------------------------------------------------------------------------- 1 | ELF>P@@.shstrtab@  -------------------------------------------------------------------------------- /tests/inputs/gcc-type.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/gcc-type.dwo -------------------------------------------------------------------------------- /tests/inputs/handle-strx-v5.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/handle-strx-v5.dwo -------------------------------------------------------------------------------- /tests/inputs/invalid-compressed.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/invalid-compressed.dwo -------------------------------------------------------------------------------- /tests/inputs/invalid-cu-index.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/invalid-cu-index.dwp -------------------------------------------------------------------------------- /tests/inputs/invalid-string-form.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/invalid-string-form.dwo -------------------------------------------------------------------------------- /tests/inputs/merge-ab.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/merge-ab.dwp -------------------------------------------------------------------------------- /tests/inputs/merge-c.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/merge-c.dwo -------------------------------------------------------------------------------- /tests/inputs/missing-dwo-id.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/missing-dwo-id.dwo -------------------------------------------------------------------------------- /tests/inputs/missing-tu-index.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/missing-tu-index.dwp -------------------------------------------------------------------------------- /tests/inputs/multiple-type-sections.dwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/multiple-type-sections.dwp -------------------------------------------------------------------------------- /tests/inputs/non-cu-top-level.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/non-cu-top-level.dwo -------------------------------------------------------------------------------- /tests/inputs/simple-notypes-a.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/simple-notypes-a.dwo -------------------------------------------------------------------------------- /tests/inputs/simple-notypes-b.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/simple-notypes-b.dwo -------------------------------------------------------------------------------- /tests/inputs/simple-types-a.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/simple-types-a.dwo -------------------------------------------------------------------------------- /tests/inputs/simple-types-b.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/simple-types-b.dwo -------------------------------------------------------------------------------- /tests/inputs/type-dedup-a.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/type-dedup-a.dwo -------------------------------------------------------------------------------- /tests/inputs/type-dedup-b.dwo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/thorin/256c3d1e89ba375f64a9b32965678cf905ef7745/tests/inputs/type-dedup-b.dwo -------------------------------------------------------------------------------- /tests/inputs/type-dedup-v5-a.s: -------------------------------------------------------------------------------- 1 | ## Note: For the purpose of checking the de-duplication of type units 2 | ## it is not necessary to have the DIEs for the structure type, that 3 | ## are referenced by the type unit. 4 | 5 | .section .debug_info.dwo,"e",@progbits 6 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 7 | .Ldebug_info_dwo_start0: 8 | .short 5 # DWARF version number 9 | .byte 6 # DWARF Unit Type (DW_UT_split_type) 10 | .byte 8 # Address Size (in bytes) 11 | .long 0 # Offset Into Abbrev. Section 12 | .quad 5657452045627120676 # Type Signature 13 | .long 25 # Type DIE Offset 14 | .byte 2 # Abbrev [2] DW_TAG_type_unit 15 | .byte 3 # Abbrev [3] DW_TAG_structure_type 16 | .byte 0 # End Of Children Mark 17 | .Ldebug_info_dwo_end0: 18 | .section .debug_info.dwo,"e",@progbits 19 | .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit 20 | .Ldebug_info_dwo_start2: 21 | .short 5 # DWARF version number 22 | .byte 5 # DWARF Unit Type (DW_UT_split_compile) 23 | .byte 8 # Address Size (in bytes) 24 | .long 0 # Offset Into Abbrev. Section 25 | .quad 0 26 | .byte 1 # Abbrev [1] DW_TAG_compile_unit 27 | .Ldebug_info_dwo_end2: 28 | .section .debug_abbrev.dwo,"e",@progbits 29 | .byte 1 # Abbreviation Code 30 | .byte 17 # DW_TAG_compile_unit 31 | .byte 0 # DW_CHILDREN_no 32 | .byte 0 # EOM(1) 33 | .byte 0 # EOM(2) 34 | .byte 2 # Abbreviation Code 35 | .byte 65 # DW_TAG_type_unit 36 | .byte 1 # DW_CHILDREN_yes 37 | .byte 0 # EOM 38 | .byte 0 # EOM 39 | .byte 0 # EOM 40 | -------------------------------------------------------------------------------- /tests/inputs/type-dedup-v5-b.s: -------------------------------------------------------------------------------- 1 | ## Note: For the purpose of checking the de-duplication of type units 2 | ## it is not necessary to have the DIEs for the structure type, that 3 | ## are referenced by the type unit. 4 | 5 | .section .debug_info.dwo,"e",@progbits 6 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 7 | .Ldebug_info_dwo_start0: 8 | .short 5 # DWARF version number 9 | .byte 6 # DWARF Unit Type (DW_UT_split_type) 10 | .byte 8 # Address Size (in bytes) 11 | .long 0 # Offset Into Abbrev. Section 12 | .quad 5657452045627120676 # Type Signature 13 | .long 25 # Type DIE Offset 14 | .byte 2 # Abbrev [2] DW_TAG_type_unit 15 | .byte 3 # Abbrev [3] DW_TAG_structure_type 16 | .byte 0 # End Of Children Mark 17 | .Ldebug_info_dwo_end0: 18 | .section .debug_info.dwo,"e",@progbits 19 | .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit 20 | .Ldebug_info_dwo_start2: 21 | .short 5 # DWARF version number 22 | .byte 5 # DWARF Unit Type (DW_UT_split_compile) 23 | .byte 8 # Address Size (in bytes) 24 | .long 0 # Offset Into Abbrev. Section 25 | .quad -1709724327721109161 26 | .byte 1 # Abbrev [1] DW_TAG_compile_unit 27 | .Ldebug_info_dwo_end2: 28 | .section .debug_abbrev.dwo,"e",@progbits 29 | .byte 1 # Abbreviation Code 30 | .byte 17 # DW_TAG_compile_unit 31 | .byte 0 # DW_CHILDREN_no 32 | .byte 0 # EOM(1) 33 | .byte 0 # EOM(2) 34 | .byte 2 # Abbreviation Code 35 | .byte 65 # DW_TAG_type_unit 36 | .byte 1 # DW_CHILDREN_yes 37 | .byte 0 # EOM 38 | .byte 0 # EOM 39 | .byte 0 # EOM 40 | -------------------------------------------------------------------------------- /tests/invalid-cu-header-length-type.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o -split-dwarf-file=%t.dwo \ 2 | # RUN: -dwarf-version=5 3 | # RUN: not thorin %t.dwo -o %t.dwp 2>&1 | FileCheck %s 4 | 5 | # CHECK: Error: Failed to add `{{.*}}/invalid-cu-header-length-type.s.tmp.dwo` to DWARF package 6 | # CHECK: 0: Failed to parse unit header 7 | # CHECK: 1: Hit the end of input before it was expected 8 | 9 | .section .debug_info.dwo,"e",@progbits 10 | .short 0 # Length of Unit 11 | -------------------------------------------------------------------------------- /tests/invalid-cu-header-length.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o \ 2 | # RUN: -split-dwarf-file=%t.dwo -dwarf-version=5 3 | # RUN: not thorin %t.dwo -o %t.dwp 2>&1 | FileCheck %s 4 | 5 | # CHECK: Error: Failed to add `{{.*}}/invalid-cu-header-length.s.tmp.dwo` to DWARF package 6 | # CHECK: 0: Failed to parse unit header 7 | # CHECK: 1: Hit the end of input before it was expected 8 | 9 | .section .debug_info.dwo,"e",@progbits 10 | .long 16 # Length of Unit 11 | .short 5 # Version 12 | -------------------------------------------------------------------------------- /tests/invalid-cu-header-version.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o \ 2 | # RUN: -split-dwarf-file=%t.dwo -dwarf-version=5 3 | # RUN: not thorin %t.dwo -o %t.dwp 2>&1 | FileCheck %s 4 | 5 | # CHECK: Error: Failed to add `{{.*}}/invalid-cu-header-version.s.tmp.dwo` to DWARF package 6 | # CHECK: 0: Failed to parse unit header 7 | # CHECK: 1: Hit the end of input before it was expected 8 | 9 | .section .debug_info.dwo,"e",@progbits 10 | .long 0 # Length of Unit 11 | -------------------------------------------------------------------------------- /tests/invalid-cu-index.test: -------------------------------------------------------------------------------- 1 | RUN: not thorin %p/inputs/invalid-cu-index.dwp -o %t 2>&1 | FileCheck %s 2 | 3 | CHECK: Error: Failed to add `{{.*}}/invalid-cu-index.dwp` to DWARF package 4 | CHECK: 0: Failed to parse `.debug_cu_index` index section 5 | CHECK: 1: Hit the end of input before it was expected 6 | -------------------------------------------------------------------------------- /tests/invalid-string-form.test: -------------------------------------------------------------------------------- 1 | RUN: not thorin %p/inputs/invalid-string-form.dwo -o %t 2>&1 | FileCheck %s 2 | 3 | CHECK: Error: Failed to add `{{.*}}/invalid-string-form.dwo` to DWARF package 4 | CHECK: 0: Failed to parse unit attribute 5 | CHECK: 1: Found an unknown `DW_FORM_*` type 6 | -------------------------------------------------------------------------------- /tests/invalid-tu-header-length.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o -split-dwarf-file=%t.dwo \ 2 | # RUN: -dwarf-version=5 3 | # RUN: not thorin %t.dwo -o %t.dwp 2>&1 | FileCheck %s 4 | 5 | # CHECK: Error: Failed to add `{{.*}}/invalid-tu-header-length.s.tmp.dwo` to DWARF package 6 | # CHECK: 0: Failed to parse unit header 7 | # CHECK: 1: Hit the end of input before it was expected 8 | 9 | .section .debug_info.dwo,"e",@progbits 10 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 11 | .Ldebug_info_dwo_start0: 12 | .short 5 # DWARF version number 13 | .byte 6 # DWARF Unit Type (DW_UT_split_type) 14 | .byte 8 # Address Size (in bytes) 15 | .long 0 # Offset Into Abbrev. Section 16 | .quad 5657452045627120676 # Type Signature 17 | .Ldebug_info_dwo_end0: 18 | -------------------------------------------------------------------------------- /tests/lit.cfg.py: -------------------------------------------------------------------------------- 1 | import lit 2 | import os 3 | import tempfile 4 | 5 | config.name = "thorin" 6 | config.test_format = lit.formats.ShTest(True) 7 | config.excludes = ['inputs'] 8 | config.suffixes = ['.s', '.test'] 9 | config.test_source_root = os.path.dirname(__file__) 10 | config.test_exec_root = tempfile.TemporaryDirectory().name 11 | -------------------------------------------------------------------------------- /tests/loclists.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o \ 2 | # RUN: -split-dwarf-file=%t.dwo -dwarf-version=5 3 | # RUN: thorin %t.dwo -o %t.dwp 4 | # RUN: llvm-dwarfdump -debug-loclists -debug-cu-index -debug-tu-index %t.dwp | FileCheck %s 5 | 6 | # CHECK-DAG: .debug_loclists.dwo contents: 7 | # CHECK: locations list header: length = 0x00000019, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000001 8 | # CHECK-NEXT: offsets: [ 9 | # CHECK-NEXT: 0x00000004 10 | # CHECK-NEXT: ] 11 | # CHECK: DW_LLE_base_addressx (0x0000000000000000) 12 | # CHECK-NEXT: DW_LLE_offset_pair (0x0000000000000000, 0x0000000000000004): DW_OP_reg5 RDI 13 | # CHECK-NEXT: DW_LLE_offset_pair (0x0000000000000004, 0x0000000000000008): DW_OP_reg3 RBX 14 | 15 | # CHECK-DAG: .debug_cu_index contents: 16 | # CHECK: Index Signature INFO ABBREV LOCLISTS 17 | # CHECK: 1 {{.*}} [0x00000018, 0x0000002d) [0x00000000, 0x00000004) [0x00000000, 0x0000001d) 18 | 19 | # CHECK-DAG: .debug_tu_index contents: 20 | # CHECK: Index Signature INFO ABBREV LOCLISTS 21 | # CHECK: 2 {{.*}} [0x00000000, 0x00000018) [0x00000000, 0x00000004) [0x00000000, 0x0000001d) 22 | 23 | .section .debug_info.dwo,"e",@progbits 24 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 25 | .Ldebug_info_dwo_start0: 26 | .short 5 # DWARF version number 27 | .byte 6 # DWARF Unit Type 28 | .byte 8 # Address Size (in bytes) 29 | .long 0 # Offset Into Abbrev. Section 30 | .quad -4287463584810542331 # Type Signature 31 | .long 31 # Type DIE Offset 32 | .Ldebug_info_dwo_end0: 33 | .section .debug_info.dwo,"e",@progbits 34 | .long .Ldebug_info_dwo_end3-.Ldebug_info_dwo_start3 # Length of Unit 35 | .Ldebug_info_dwo_start3: 36 | .short 5 # DWARF version number 37 | .byte 5 # DWARF Unit Type 38 | .byte 8 # Address Size (in bytes) 39 | .long 0 # Offset Into Abbrev. Section 40 | .quad 1152943841751211454 41 | .byte 1 # Abbrev [1] 0x14:0x349 DW_TAG_compile_unit 42 | .Ldebug_info_dwo_end3: 43 | .section .debug_loclists.dwo,"e",@progbits 44 | .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length 45 | .Ldebug_list_header_start0: 46 | .short 5 # Version 47 | .byte 8 # Address size 48 | .byte 0 # Segment selector size 49 | .long 1 # Offset entry count 50 | .Lloclists_table_base0: 51 | .long .Ldebug_loc0-.Lloclists_table_base0 52 | .Ldebug_loc0: 53 | .byte 1 # DW_LLE_base_addressx 54 | .byte 0 # base address index 55 | .byte 4 # DW_LLE_offset_pair 56 | .uleb128 0 # starting offset 57 | .uleb128 4 # ending offset 58 | .byte 1 # Loc expr size 59 | .byte 85 # DW_OP_reg5 60 | .byte 4 # DW_LLE_offset_pair 61 | .uleb128 4 # starting offset 62 | .uleb128 8 # ending offset 63 | .byte 1 # Loc expr size 64 | .byte 83 # DW_OP_reg3 65 | .byte 0 # DW_LLE_end_of_list 66 | .Ldebug_list_header_end0: 67 | .section .debug_abbrev.dwo,"e",@progbits 68 | .byte 1 # Abbreviation Code 69 | .byte 17 # DW_TAG_compile_unit 70 | .byte 0 # DW_CHILDREN_no 71 | .byte 0 # EOM(1) 72 | -------------------------------------------------------------------------------- /tests/merge.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/merge-c.dwo %p/inputs/merge-ab.dwp -o - | \ 2 | RUN: llvm-dwarfdump -v - | FileCheck --check-prefix=CHECK %s 3 | 4 | CHECK-LABEL: .debug_abbrev.dwo contents: 5 | CHECK-LABEL: Abbrev table for offset: 6 | CHECK: 0x0000[[CAOFF:.*]] 7 | CHECK-LABEL: Abbrev table for offset: 8 | CHECK: 0x0000[[AAOFF:.*]] 9 | CHECK-LABEL: Abbrev table for offset: 10 | CHECK: 0x0000[[BAOFF:.*]] 11 | 12 | CHECK: .debug_info.dwo contents: 13 | CHECK: [[COFF:0x[0-9a-f]*]]: 14 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004, abbr_offset = 15 | CHECK: 0x[[CAOFF]], addr_size = 0x08 (next unit at [[AOFF:.*]]) 16 | CHECK: DW_AT_GNU_dwo_id {{.*}} ([[DWOC:.*]]) 17 | CHECK: [[AOFF]]: 18 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004, abbr_offset = 19 | CHECK: 0x[[AAOFF]], addr_size = 0x08 (next unit at [[BOFF:.*]]) 20 | CHECK: DW_AT_GNU_dwo_id {{.*}} ([[DWOA:.*]]) 21 | CHECK: [[BOFF]]: 22 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004, abbr_offset = 23 | CHECK: 0x[[BAOFF]], addr_size = 0x08 (next unit at [[XOFF:.*]]) 24 | CHECK: DW_AT_GNU_dwo_id {{.*}} ([[DWOB:.*]]) 25 | 26 | CHECK-LABEL: .debug_cu_index 27 | CHECK: Index Signature INFO ABBREV LINE STR_OFFSETS 28 | CHECK-DAG: [[DWOC]] {{\[}}[[COFF]], [[AOFF]]) [0x0000[[CAOFF]], 0x0000[[AAOFF]]) [0x00000000, 0x00000011) [0x00000000, 0x00000018) 29 | CHECK-DAG: [[DWOA]] {{\[}}[[AOFF]], [[BOFF]]) [0x0000[[AAOFF]], 0x0000[[BAOFF]]) [0x00000011, 0x00000022) [0x00000018, 0x00000028) 30 | CHECK-DAG: [[DWOB]] {{\[}}[[BOFF]], [[XOFF]]) [0x0000[[BAOFF]], 0x000000c3) [0x00000022, 0x00000033) [0x00000028, 0x0000003c) 31 | -------------------------------------------------------------------------------- /tests/missing-dwo-id.test: -------------------------------------------------------------------------------- 1 | RUN: rm -f %t 2 | RUN: not thorin %p/inputs/missing-dwo-id.dwo -o %t 2>&1 | FileCheck %s 3 | RUN: not ls %t 4 | 5 | CHECK: Error: Failed to add `{{.*}}/missing-dwo-id.dwo` to DWARF package 6 | CHECK: Regular compilation unit in object (missing dwo identifier) 7 | -------------------------------------------------------------------------------- /tests/missing-tu-index.test: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.dwp 2 | # RUN: not thorin %t.dwp -o %t 2>&1 | FileCheck %s 3 | 4 | # CHECK: Error: Failed to add `{{.*}}/missing-tu-index.test.tmp.dwp` to DWARF package 5 | # CHECK: 0: Failed to parse `.debug_tu_index` index section 6 | # CHECK: 1: Hit the end of input before it was expected 7 | 8 | .section .debug_abbrev.dwo, "e", @progbits 9 | .LAbbrevBegin: 10 | .uleb128 1 # Abbreviation Code 11 | .uleb128 17 # DW_TAG_compile_unit 12 | .byte 1 # DW_CHILDREN_no 13 | .uleb128 0x2131 # DW_AT_GNU_dwo_id 14 | .uleb128 7 # DW_FORM_data8 15 | .byte 0 # EOM(1) 16 | .byte 0 # EOM(2) 17 | .byte 0 # EOM(3) 18 | .LAbbrevEnd: 19 | 20 | .section .debug_info.dwo, "e", @progbits 21 | .LCUBegin: 22 | .long .LCUEnd-.LCUVersion # Length of Unit 23 | .LCUVersion: 24 | .short 4 # Version 25 | .long 0 # Abbrev offset 26 | .byte 8 # Address size 27 | .uleb128 1 # Abbrev [1] DW_TAG_compile_unit 28 | .quad 0x1100001122222222 # DW_AT_GNU_dwo_id 29 | .LCUEnd: 30 | 31 | .section .debug_types.dwo, "e", @progbits 32 | .space 1 33 | 34 | .section .debug_cu_index, "", @progbits 35 | 36 | ## Header: 37 | .long 2 # Version 38 | .long 2 # Section count 39 | .long 1 # Unit count 40 | .long 2 # Slot count 41 | ## Hash Table of Signatures: 42 | .quad 0x1100001122222222 43 | .quad 0 44 | ## Parallel Table of Indexes: 45 | .long 1 46 | .long 0 47 | ## Table of Section Offsets: 48 | ## Row 0: 49 | .long 1 # DW_SECT_INFO 50 | .long 3 # DW_SECT_ABBREV 51 | ## Row 1: 52 | .long 0 # Offset in .debug_info.dwo 53 | .long 0 # Offset in .debug_abbrev.dwo 54 | ## Table of Section Sizes: 55 | .long .LCUEnd-.LCUBegin # Size in .debug_info.dwo 56 | .long .LAbbrevEnd-.LAbbrevBegin # Size in .debug_abbrev.dwo 57 | 58 | .section .debug_tu_index, "", @progbits 59 | ## Header: 60 | .short 2 # Version 61 | -------------------------------------------------------------------------------- /tests/multiple-debug-info-sections-in-dwp.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.dwp 2 | # RUN: not thorin %t.dwp -o /dev/null 2>&1 | FileCheck %s 3 | 4 | # CHECK: Error: Failed to add `{{.*}}/multiple-debug-info-sections-in-dwp.s.tmp.dwp` to DWARF package 5 | # CHECK: 0: Failed to parse `.debug_cu_index` index section 6 | # CHECK: 1: Hit the end of input before it was expected 7 | 8 | .section .debug_info.dwo,"G",@progbits,0xFDFDFDFD,comdat 9 | .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit 10 | .Ldebug_info_dwo_start1: 11 | .short 5 # DWARF version number 12 | .byte 5 # DWARF Unit Type (DW_UT_split_compile) 13 | .byte 8 # Address Size (in bytes) 14 | .long 0 # Offset Into Abbrev. Section 15 | .quad -1506010254921578184 16 | .byte 1 # Abbrev [1] DW_TAG_compile_unit 17 | .Ldebug_info_dwo_end1: 18 | .section .debug_info.dwo,"G",@progbits,0xDFDFDFDF,comdat 19 | .long 0 # Length of Unit 20 | .section .debug_cu_index, "", @progbits 21 | ## Incomplete Header: 22 | .long 2 # Version 23 | -------------------------------------------------------------------------------- /tests/multiple-type-sections.test: -------------------------------------------------------------------------------- 1 | RUN: not thorin %p/inputs/multiple-type-sections.dwp -o %t 2>&1 | FileCheck %s 2 | 3 | CHECK: Error: Failed to add `{{.*}}/multiple-type-sections.dwp` to DWARF package 4 | CHECK: Multiple `.debug_types.dwo` sections in a package 5 | -------------------------------------------------------------------------------- /tests/no-cu-found.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj --split-dwarf-file=%t.dwo \ 2 | # RUN: -dwarf-version=5 %s -o %t.o 3 | # RUN: not thorin %t.dwo -o /dev/null 2>&1 | FileCheck %s 4 | 5 | # CHECK: Error: Failed to add `{{.*}}/no-cu-found.s.tmp.dwo` to DWARF package 6 | # CHECK: 0: Failed to parse unit header 7 | # CHECK: 1: The `DW_UT_*` value for this unit is not supported yet 8 | 9 | .section .debug_info.dwo,"e",@progbits 10 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 11 | .Ldebug_info_dwo_start0: 12 | .short 5 # DWARF version number 13 | .byte 12 # DWARF Unit Type (DW_TAG_string_type, wrong type) 14 | .byte 8 # Address Size (in bytes) 15 | .long 0 # Offset Into Abbrev. Section 16 | .quad -1173350285159172090 17 | .byte 1 # Abbrev [1] 0x14:0x16 DW_TAG_compile_unit 18 | .asciz "clang version 11.0.0" # DW_AT_producer 19 | .short 12 # DW_AT_language 20 | .asciz "int.c" # DW_AT_name 21 | .asciz "int.dwo" # DW_AT_dwo_name 22 | .byte 0 # End Of Children Mark 23 | .Ldebug_info_dwo_end0: 24 | .section .debug_abbrev.dwo,"e",@progbits 25 | .byte 1 # Abbreviation Code 26 | .byte 17 # DW_TAG_compile_unit 27 | .byte 1 # DW_CHILDREN_yes 28 | .byte 37 # DW_AT_producer 29 | .byte 8 # DW_FORM_string 30 | .byte 19 # DW_AT_language 31 | .byte 5 # DW_FORM_data2 32 | .byte 3 # DW_AT_name 33 | .byte 8 # DW_FORM_string 34 | .byte 118 # DW_AT_dwo_name 35 | .byte 8 # DW_FORM_string 36 | .byte 0 # EOM(1) 37 | .byte 0 # EOM(2) 38 | -------------------------------------------------------------------------------- /tests/non-cu-top-level.test: -------------------------------------------------------------------------------- 1 | RUN: not thorin %p/inputs/non-cu-top-level.dwo -o %t 2>&1 | FileCheck %s 2 | 3 | CHECK: Error: Failed to add `{{.*}}/non-cu-top-level.dwo` to DWARF package 4 | CHECK: Top-level debugging information entry is not a compilation/type unit 5 | -------------------------------------------------------------------------------- /tests/rnglists.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o \ 2 | # RUN: -split-dwarf-file=%t.dwo -dwarf-version=5 3 | # RUN: thorin %t.dwo -o %t.dwp 4 | # RUN: llvm-dwarfdump -debug-rnglists -debug-cu-index -debug-tu-index %t.dwp | FileCheck %s 5 | 6 | # CHECK-DAG: .debug_cu_index contents: 7 | # CHECK: Index Signature INFO ABBREV RNGLISTS 8 | # CHECK: 1 {{.*}} [0x00000018, 0x0000002d) [0x00000000, 0x00000004) [0x00000000, 0x00000017) 9 | 10 | # CHECK-DAG: .debug_tu_index contents: 11 | # CHECK: Index Signature INFO ABBREV RNGLISTS 12 | # CHECK: 2 {{.*}} [0x00000000, 0x00000018) [0x00000000, 0x00000004) [0x00000000, 0x00000017) 13 | 14 | # CHECK-DAG: .debug_rnglists.dwo contents: 15 | # range list header: length = 0x00000013, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000001 16 | # CHECK: offsets: [ 17 | # CHECK-NEXT: 0x00000004 18 | # CHECK-NEXT: ] 19 | # CHECK-NEXT: ranges: 20 | # CHECK-NEXT: [0x0000000000000004, 0x0000000000000008) 21 | # CHECK-NEXT: [0x000000000000000c, 0x0000000000000010) 22 | 23 | .section .debug_info.dwo,"e",@progbits 24 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 25 | .Ldebug_info_dwo_start0: 26 | .short 5 # DWARF version number 27 | .byte 6 # DWARF Unit Type 28 | .byte 8 # Address Size (in bytes) 29 | .long 0 # Offset Into Abbrev. Section 30 | .quad -4287463584810542331 # Type Signature 31 | .long 31 # Type DIE Offset 32 | .Ldebug_info_dwo_end0: 33 | .section .debug_info.dwo,"e",@progbits 34 | .long .Ldebug_info_dwo_end3-.Ldebug_info_dwo_start3 # Length of Unit 35 | .Ldebug_info_dwo_start3: 36 | .short 5 # DWARF version number 37 | .byte 5 # DWARF Unit Type 38 | .byte 8 # Address Size (in bytes) 39 | .long 0 # Offset Into Abbrev. Section 40 | .quad 1152943841751211454 41 | .byte 1 # Abbrev [1] 0x14:0x349 DW_TAG_compile_unit 42 | .Ldebug_info_dwo_end3: 43 | .section .debug_abbrev.dwo,"e",@progbits 44 | .byte 1 # Abbreviation Code 45 | .byte 17 # DW_TAG_compile_unit 46 | .byte 0 # DW_CHILDREN_no 47 | .byte 0 # EOM(1) 48 | .section .debug_rnglists.dwo,"e",@progbits 49 | .long .Ldebug_list_header_end1-.Ldebug_list_header_start1 # Length 50 | .Ldebug_list_header_start1: 51 | .short 5 # Version 52 | .byte 8 # Address size 53 | .byte 0 # Segment selector size 54 | .long 1 # Offset entry count 55 | .Lrnglists_dwo_table_base0: 56 | .long .Ldebug_ranges0-.Lrnglists_dwo_table_base0 57 | .Ldebug_ranges0: 58 | .byte 4 # DW_RLE_offset_pair 59 | .uleb128 4 # starting offset 60 | .uleb128 8 # ending offset 61 | .byte 4 # DW_RLE_offset_pair 62 | .uleb128 12 # starting offset 63 | .uleb128 16 # ending offset 64 | .byte 0 # DW_RLE_end_of_list 65 | .Ldebug_list_header_end1: 66 | -------------------------------------------------------------------------------- /tests/simple.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/simple-notypes-a.dwo %p/inputs/simple-notypes-b.dwo -o %t 2 | RUN: llvm-dwarfdump -v %t | FileCheck --check-prefixes=CHECK,NOTYP %s 3 | RUN: llvm-objdump -h %t | FileCheck --check-prefix=NOTYPOBJ %s 4 | RUN: thorin %p/inputs/simple-types-a.dwo %p/inputs/simple-types-b.dwo -o - \ 5 | RUN: | llvm-dwarfdump -v - | FileCheck --check-prefixes=CHECK,TYPES %s 6 | 7 | CHECK-LABEL: .debug_abbrev.dwo contents: 8 | CHECK-LABEL: Abbrev table for offset: 9 | CHECK: 0x0000[[AAOFF:.*]] 10 | CHECK: DW_TAG_compile_unit 11 | CHECK: DW_TAG_variable 12 | CHECK: DW_TAG_structure_type 13 | CHECK-LABEL: Abbrev table for offset: 14 | CHECK: 0x0000[[BAOFF:.*]] 15 | CHECK: DW_TAG_compile_unit 16 | CHECK: DW_TAG_structure_type 17 | CHECK: DW_TAG_subprogram 18 | CHECK: DW_TAG_formal_parameter 19 | 20 | CHECK: .debug_info.dwo contents: 21 | CHECK: [[AOFF:0x[0-9a-f]*]]: 22 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004, abbr_offset = 23 | CHECK: 0x[[AAOFF]], addr_size = 0x08 (next unit at [[BOFF:.*]]) 24 | CHECK: DW_TAG_compile_unit 25 | CHECK: DW_AT_name {{.*}} "a.cpp" 26 | CHECK: DW_AT_GNU_dwo_id {{.*}} ([[DWOA:.*]]) 27 | CHECK: DW_TAG_variable 28 | CHECK: DW_AT_name {{.*}} "a" 29 | CHECK: DW_TAG_structure_type 30 | NOTYP: DW_AT_name {{.*}} "foo" 31 | TYPES: DW_AT_signature {{.*}} ([[FOOSIG:.*]]) 32 | 33 | CHECK: [[BOFF]]: 34 | CHECK-LABEL: Compile Unit: length = {{.*}}, version = 0x0004, abbr_offset = 35 | CHECK: 0x[[BAOFF]], addr_size = 0x08 (next unit at [[XOFF:.*]]) 36 | CHECK: DW_AT_name {{.*}} "b.cpp" 37 | CHECK: DW_AT_GNU_dwo_id {{.*}} ([[DWOB:.*]]) 38 | CHECK: DW_TAG_structure_type 39 | NOTYP: DW_AT_name {{.*}} "bar" 40 | TYPES: DW_AT_signature {{.*}} ([[BARSIG:.*]]) 41 | CHECK: DW_TAG_subprogram 42 | CHECK: DW_AT_name {{.*}} "b" 43 | CHECK: DW_TAG_formal_parameter 44 | 45 | NOTYP-NOT: .debug_types.dwo contents: 46 | TYPES-LABEL: .debug_types.dwo contents: 47 | TYPES: [[FOOUOFF:0x[0-9a-f]*]]: 48 | TYPES-LABEL: Type Unit: length = 0x00000020, format = DWARF32, version = 0x0004, abbr_offset = 49 | TYPES: 0x[[AAOFF]], addr_size = 0x08, name = 'foo', type_signature = [[FOOSIG]], type_offset = 0x[[FOOOFF:.*]] (next unit at [[BARUOFF:.*]]) 50 | TYPES: DW_TAG_type_unit 51 | TYPES: [[FOOOFF]]: DW_TAG_structure_type 52 | TYPES: DW_AT_name {{.*}} "foo" 53 | TYPES: [[BARUOFF]]: 54 | TYPES-LABEL: Type Unit: length = 0x00000020, format = DWARF32, version = 0x0004, abbr_offset = 55 | TYPES: 0x[[BAOFF]], addr_size = 0x08, name = 'bar', type_signature = [[BARSIG]], type_offset = 0x001e (next unit at [[XUOFF:.*]]) 56 | TYPES: DW_TAG_type_unit 57 | TYPES: 0x00000042: DW_TAG_structure_type 58 | TYPES: DW_AT_name {{.*}} "bar" 59 | 60 | CHECK-LABEL: .debug_cu_index contents: 61 | CHECK: Index Signature INFO ABBREV LINE STR_OFFSETS 62 | TYPES: 1 [[DWOA]] {{\[}}[[AOFF]], [[BOFF]]) [0x0000[[AAOFF]], 0x0000[[BAOFF]]) [0x00000000, 0x0000001a) [0x00000000, 0x00000010) 63 | TYPES: 3 [[DWOB]] {{\[}}[[BOFF]], [[XOFF]]) [0x0000[[BAOFF]], 0x00000099) [0x0000001a, 0x00000034) [0x00000010, 0x00000024) 64 | NOTYP: 3 [[DWOA]] {{\[}}[[AOFF]], [[BOFF]]) [0x0000[[AAOFF]], 0x0000[[BAOFF]]) [0x00000000, 0x00000011) [0x00000000, 0x00000010) 65 | NOTYP: 4 [[DWOB]] {{\[}}[[BOFF]], [[XOFF]]) [0x0000[[BAOFF]], 0x00000075) [0x00000011, 0x00000022) [0x00000010, 0x00000024) 66 | 67 | Ensure we do not create a debug_tu_index, even an empty or malformed one. 68 | NOTYPOBJ-NOT: .debug_tu_index 69 | 70 | TYPES: Index Signature TYPES ABBREV LINE STR_OFFSETS 71 | TYPES: 1 [[FOOSIG]] {{\[}}[[FOOUOFF]], [[BARUOFF]]) [0x0000[[AAOFF]], 0x0000[[BAOFF]]) [0x00000000, 0x0000001a) [0x00000000, 0x00000010) 72 | TYPES: 4 [[BARSIG]] {{\[}}[[BARUOFF]], [[XUOFF]]) [0x0000[[BAOFF]], 0x00000099) [0x0000001a, 0x00000034) [0x00000010, 0x00000024) 73 | 74 | CHECK-LABEL: .debug_str.dwo contents: 75 | CHECK: "clang version 76 | CHECK: 0x[[ACPP:.*]]: "a.cpp" 77 | CHECK-NOT: "clang version 78 | CHECK: 0x[[BCPP:.*]]: "b.cpp" 79 | 80 | CHECK-LABEL: .debug_str_offsets.dwo contents: 81 | CHECK: : 00000000 82 | CHECK: : [[ACPP]] 83 | CHECK: : 00000000 84 | CHECK: : [[BCPP]] 85 | -------------------------------------------------------------------------------- /tests/tu-units-v5.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o \ 2 | # RUN: -split-dwarf-file=%t.dwo -dwarf-version=5 3 | # RUN: thorin %t.dwo -o %t.dwp 4 | # RUN: llvm-dwarfdump -debug-info -debug-tu-index %t.dwp | FileCheck %s 5 | 6 | # CHECK-DAG: .debug_info.dwo contents: 7 | # CHECK: 0x00000000: Type Unit: length = 0x00000017, format = DWARF32, version = 0x0005, unit_type = DW_UT_split_type, abbr_offset = 0x0000, addr_size = 0x08, name = '', type_signature = [[TUID1:.*]], type_offset = 0x0019 (next unit at 0x0000001b) 8 | # CHECK: 0x0000001b: Type Unit: length = 0x00000017, format = DWARF32, version = 0x0005, unit_type = DW_UT_split_type, abbr_offset = 0x0000, addr_size = 0x08, name = '', type_signature = [[TUID2:.*]], type_offset = 0x0019 (next unit at 0x00000036) 9 | # CHECK-DAG: .debug_tu_index contents: 10 | # CHECK: version = 5, units = 2, slots = 4 11 | # CHECK: Index Signature INFO ABBREV 12 | # CHECK: 1 [[TUID1]] [0x00000000, 0x0000001b) [0x00000000, 0x00000010) 13 | # CHECK: 4 [[TUID2]] [0x0000001b, 0x00000036) [0x00000000, 0x00000010) 14 | 15 | .section .debug_info.dwo,"e",@progbits 16 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 17 | .Ldebug_info_dwo_start0: 18 | .short 5 # DWARF version number 19 | .byte 6 # DWARF Unit Type (DW_UT_split_type) 20 | .byte 8 # Address Size (in bytes) 21 | .long 0 # Offset Into Abbrev. Section 22 | .quad 5657452045627120676 # Type Signature 23 | .long 25 # Type DIE Offset 24 | .byte 2 # Abbrev [2] DW_TAG_type_unit 25 | .byte 3 # Abbrev [3] DW_TAG_structure_type 26 | .byte 0 # End Of Children Mark 27 | .Ldebug_info_dwo_end0: 28 | .section .debug_info.dwo,"e",@progbits 29 | .long .Ldebug_info_dwo_end1-.Ldebug_info_dwo_start1 # Length of Unit 30 | .Ldebug_info_dwo_start1: 31 | .short 5 # DWARF version number 32 | .byte 6 # DWARF Unit Type (DW_UT_split_type) 33 | .byte 8 # Address Size (in bytes) 34 | .long 0 # Offset Into Abbrev. Section 35 | .quad -8528522068957683993 # Type Signature 36 | .long 25 # Type DIE Offset 37 | .byte 4 # Abbrev [4] DW_TAG_type_unit 38 | .byte 5 # Abbrev [5] DW_TAG_structure_type 39 | .byte 0 # End Of Children Mark 40 | .Ldebug_info_dwo_end1: 41 | .section .debug_info.dwo,"e",@progbits 42 | .long .Ldebug_info_dwo_end2-.Ldebug_info_dwo_start2 # Length of Unit 43 | .Ldebug_info_dwo_start2: 44 | .short 5 # DWARF version number 45 | .byte 5 # DWARF Unit Type (DW_UT_split_compile) 46 | .byte 8 # Address Size (in bytes) 47 | .long 0 # Offset Into Abbrev. Section 48 | .quad 0 49 | .byte 1 # Abbrev [1] DW_TAG_compile_unit 50 | .Ldebug_info_dwo_end2: 51 | .section .debug_abbrev.dwo,"e",@progbits 52 | .byte 1 # Abbreviation Code 53 | .byte 17 # DW_TAG_compile_unit 54 | .byte 0 # DW_CHILDREN_no 55 | .byte 0 # EOM(1) 56 | .byte 0 # EOM(2) 57 | .byte 2 # Abbreviation Code 58 | .byte 65 # DW_TAG_type_unit 59 | .byte 1 # DW_CHILDREN_yes 60 | .byte 0 # EOM 61 | .byte 0 # EOM 62 | .byte 4 # Abbreviation Code 63 | .byte 65 # DW_TAG_type_unit 64 | .byte 1 # DW_CHILDREN_yes 65 | .byte 0 # EOM 66 | .byte 0 # EOM 67 | .byte 0 # EOM 68 | -------------------------------------------------------------------------------- /tests/type-dedup-v5.test: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %p/inputs/type-dedup-v5-a.s -filetype=obj -o %t-a.o \ 2 | # RUN: -split-dwarf-file=%t-a.dwo -dwarf-version=5 3 | # RUN: llvm-mc -triple x86_64-unknown-linux %p/inputs/type-dedup-v5-b.s -filetype=obj -o %t-b.o \ 4 | # RUN: -split-dwarf-file=%t-b.dwo -dwarf-version=5 5 | # RUN: thorin %t-a.dwo %t-b.dwo -o %t.dwp 6 | # RUN: llvm-dwarfdump -debug-tu-index %t.dwp | FileCheck %s 7 | 8 | # CHECK-DAG: .debug_tu_index contents: 9 | # CHECK: version = 5, units = 1, slots = 2 10 | -------------------------------------------------------------------------------- /tests/type-dedup.test: -------------------------------------------------------------------------------- 1 | RUN: thorin %p/inputs/type-dedup-a.dwo %p/inputs/type-dedup-b.dwo -o %t 2 | RUN: llvm-dwarfdump -v %t | FileCheck %s 3 | RUN: thorin %p/inputs/type-dedup-b.dwo -o %tb.dwp 4 | RUN: thorin %p/inputs/type-dedup-a.dwo %tb.dwp -o %t 5 | RUN: llvm-dwarfdump -v %t | FileCheck %s 6 | 7 | CHECK-LABEL: .debug_types.dwo contents: 8 | CHECK: [[COMMONUOFF:0x[0-9a-f]*]]: 9 | CHECK-LABEL: Type Unit: length = 0x00000020, format = DWARF32, version = 0x0004, abbr_offset = 10 | CHECK: 0x0000, addr_size = 0x08, name = 'common', type_signature = [[COMMONSIG:0x[0-9a-f]*]], type_offset = 0x[[COMMONOFF:.*]] (next unit at [[AUOFF:.*]]) 11 | CHECK: DW_TAG_type_unit 12 | CHECK: [[COMMONOFF]]: DW_TAG_structure_type 13 | CHECK: DW_AT_name {{.*}} "common" 14 | CHECK: [[AUOFF]]: 15 | CHECK-LABEL: Type Unit: length = 0x00000020, format = DWARF32, version = 0x0004, abbr_offset = 16 | CHECK: 0x0000, addr_size = 0x08, name = 'adistinct', type_signature = [[ASIG:0x[0-9a-f]*]], type_offset = 0x[[AOFF:.*]] (next unit at [[BUOFF:.*]]) 17 | CHECK: DW_TAG_type_unit 18 | CHECK: 0x00000042: DW_TAG_structure_type 19 | CHECK: DW_AT_name {{.*}} "adistinct" 20 | CHECK: [[BUOFF]]: 21 | CHECK-LABEL: Type Unit: length = 0x00000020, format = DWARF32, version = 0x0004, abbr_offset = 22 | CHECK: 0x{{.*}}, addr_size = 0x08, name = 'bdistinct', type_signature = [[BSIG:0x[0-9a-f]*]], type_offset = 0x[[BOFF:.*]] (next unit at [[XUOFF:.*]]) 23 | CHECK: DW_TAG_type_unit 24 | CHECK: 0x00000066: DW_TAG_structure_type 25 | CHECK: DW_AT_name {{.*}} "bdistinct" 26 | CHECK-NOT: Type Unit 27 | -------------------------------------------------------------------------------- /tests/unknown-section-id.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux-gnu %s -filetype=obj -o %t.dwp 2 | # RUN: not thorin %t.dwp -o /dev/null 3 | 4 | # CHECK: Error: Failed to add input object to DWARF package 5 | # CHECK: 0: Failed to parse index section 6 | # CHECK: 1: Unknown section type in `.dwp` index 7 | 8 | .section .debug_abbrev.dwo, "e", @progbits 9 | .LCUAbbrevBegin: 10 | .uleb128 1 # Abbreviation Code 11 | .uleb128 0x11 # DW_TAG_compile_unit 12 | .byte 0 # DW_CHILDREN_no 13 | .uleb128 0x2131 # DW_AT_GNU_dwo_id 14 | .uleb128 7 # DW_FORM_data8 15 | .byte 0 # EOM(1) 16 | .byte 0 # EOM(2) 17 | .byte 0 # EOM(3) 18 | .LCUAbbrevEnd: 19 | 20 | .LTUAbbrevBegin: 21 | .uleb128 1 # Abbreviation Code 22 | .uleb128 0x41 # DW_TAG_type_unit 23 | .byte 1 # DW_CHILDREN_yes 24 | .byte 0 # EOM(1) 25 | .byte 0 # EOM(2) 26 | .uleb128 2 # Abbreviation Code 27 | .uleb128 0x13 # DW_TAG_structure_type 28 | .byte 0 # DW_CHILDREN_no 29 | .byte 0 # EOM(1) 30 | .byte 0 # EOM(2) 31 | .byte 0 # EOM(3) 32 | .LTUAbbrevEnd: 33 | 34 | .section .debug_info.dwo, "e", @progbits 35 | .LCUBegin: 36 | .long .LCUEnd-.LCUVersion # Length of Unit 37 | .LCUVersion: 38 | .short 4 # Version 39 | .long 0 # Abbrev offset 40 | .byte 8 # Address size 41 | .uleb128 1 # Abbrev [1] DW_TAG_compile_unit 42 | .quad 0x1100002222222222 # DW_AT_GNU_dwo_id 43 | .LCUEnd: 44 | 45 | .section .debug_types.dwo, "e", @progbits 46 | .LTUBegin: 47 | .long .LTUEnd-.LTUVersion # Length of Unit 48 | .LTUVersion: 49 | .short 4 # Version 50 | .long 0 # Abbrev offset 51 | .byte 8 # Address size 52 | .quad 0x1100003333333333 # Type signature 53 | .long .LTUType-.LTUBegin # Type offset 54 | .uleb128 1 # Abbrev [1] DW_TAG_type_unit 55 | .LTUType: 56 | .uleb128 2 # Abbrev [2] DW_TAG_structure_type 57 | .LTUEnd: 58 | 59 | .section .debug_cu_index, "", @progbits 60 | ## Header: 61 | .long 2 # Version 62 | .long 4 # Section count 63 | .long 1 # Unit count 64 | .long 2 # Slot count 65 | ## Hash Table of Signatures: 66 | .quad 0x1100002222222222 67 | .quad 0 68 | ## Parallel Table of Indexes: 69 | .long 1 70 | .long 0 71 | ## Table of Section Offsets: 72 | ## Row 0: 73 | .long 1 # DW_SECT_INFO 74 | .long 3 # DW_SECT_ABBREV 75 | .long 0 # Invalid ID, less than DW_SECT_INFO 76 | .long 9 # Invalid ID, greater than DW_SECT_MACRO 77 | ## Row 1: 78 | .long 0 # Offset in .debug_info.dwo 79 | .long 0 # Offset in .debug_abbrev.dwo 80 | .long 0 81 | .long 0 82 | ## Table of Section Sizes: 83 | .long .LCUEnd-.LCUBegin # Size in .debug_info.dwo 84 | .long .LCUAbbrevEnd-.LCUAbbrevBegin # Size in .debug_abbrev.dwo 85 | .long 1 86 | .long 1 87 | 88 | .section .debug_tu_index, "", @progbits 89 | ## Header: 90 | .long 2 # Version 91 | .long 4 # Section count 92 | .long 1 # Unit count 93 | .long 2 # Slot count 94 | ## Hash Table of Signatures: 95 | .quad 0 96 | .quad 0x1100003333333333 97 | ## Parallel Table of Indexes: 98 | .long 0 99 | .long 1 100 | ## Table of Section Offsets: 101 | ## Row 0: 102 | .long 2 # DW_SECT_TYPES 103 | .long 3 # DW_SECT_ABBREV 104 | .long 0 # Invalid ID, less than DW_SECT_INFO 105 | .long 9 # Invalid ID, greater than DW_SECT_MACRO 106 | ## Row 1: 107 | .long 0 # Offset in .debug_types.dwo 108 | .long .LTUAbbrevBegin-.debug_abbrev.dwo # Offset in .debug_abbrev.dwo 109 | .long 0 110 | .long 0 111 | ## Table of Section Sizes: 112 | .long .LTUEnd-.LTUBegin # Size in .debug_types.dwo 113 | .long .LTUAbbrevEnd-.LTUAbbrevBegin # Size in .debug_abbrev.dwo 114 | .long 1 115 | .long 1 116 | -------------------------------------------------------------------------------- /tests/wrong-unit-type-info-v4.s: -------------------------------------------------------------------------------- 1 | # RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.dwp 2 | # RUN: not thorin %t.dwp -o %t 2>&1 | FileCheck %s 3 | 4 | # CHECK: Error: Failed to add `{{.*}}/wrong-unit-type-info-v4.s.tmp.dwp` to DWARF package 5 | # CHECK: 0: Failed to parse unit 6 | # CHECK: 1: Hit the end of input before it was expected 7 | 8 | .section .debug_info.dwo,"e",@progbits 9 | .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit 10 | .Ldebug_info_dwo_start0: 11 | .short 4 # DWARF version number 12 | .long 0 # Offset Into Abbrev. Section 13 | .byte 8 # Address Size (in bytes) 14 | .byte 1 # Abbrev [1] 0xb:0x1 DW_TAG_string_type 15 | .Ldebug_info_dwo_end0: 16 | .section .debug_abbrev.dwo,"e",@progbits 17 | .byte 1 # Abbreviation Code 18 | .byte 18 # DW_TAG_string_type 19 | -------------------------------------------------------------------------------- /thorin-bin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "thorin-dwp-bin" 3 | authors = [ "David Wood " ] 4 | categories = ["development-tools::debugging", "command-line-utilities"] 5 | description = "Merge DWARF objects and packages into DWARF packages" 6 | homepage = "https://github.com/rust-lang/thorin" 7 | keywords = ["dwarf", "split-dwarf", "dwarf-package", "dwarf-object", "dwp"] 8 | license = "MIT OR Apache-2.0" 9 | readme = "README.md" 10 | repository = "https://github.com/rust-lang/thorin" 11 | version = "0.9.0" 12 | edition = "2021" 13 | 14 | [dependencies] 15 | thorin-dwp = { version = "0.9.0", path = "../thorin" } 16 | 17 | anyhow = "1.0.51" 18 | memmap2 = "0.5.0" 19 | structopt = "0.3.25" 20 | thiserror = "1.0.30" 21 | tracing = "0.1.29" 22 | tracing-subscriber = { version = "0.3.18", features = [ "env-filter" ] } 23 | tracing-tree = "0.2.3" 24 | typed-arena = "2.0.1" 25 | 26 | [dependencies.object] 27 | version = "0.36.0" 28 | default-features = false 29 | features = [ "archive", "read", "write", "compression" ] 30 | 31 | [[bin]] 32 | name = "thorin" 33 | path = "src/main.rs" 34 | -------------------------------------------------------------------------------- /thorin-bin/README.md: -------------------------------------------------------------------------------- 1 | # `thorin` 2 | `thorin` is an DWARF packaging utility for creating DWARF packages (`*.dwp` files) out of input 3 | DWARF objects (`*.dwo` files; or `*.o` files with `.dwo` sections), supporting both the pre-standard 4 | GNU extension format for DWARF packages and the standardized format introduced in DWARF 5. 5 | 6 | `thorin` was written as part of the implementation of Split DWARF in `rustc`. A Rust implementation 7 | of a DWARF packaging utility is easier to integrate into the compiler and can support features like 8 | loading dwarf objects from archive files (or rustc's rlibs) which are helpful in supporting 9 | cross-crate Split DWARF packaging in `rustc`. 10 | 11 | ## Usage 12 | `thorin` can read input DWARF objects from executables or can package arbitrary input dwarf 13 | objects (including DWARF objects in archive files, such as Rust rlibs)! Install `thorin` using 14 | `cargo`: 15 | 16 | ```shell-session 17 | $ cargo install thorin-dwp-bin 18 | $ thorin --help 19 | thorin 0.9.0 20 | merge dwarf objects into dwarf packages 21 | 22 | USAGE: 23 | thorin [OPTIONS] [--] [inputs]... 24 | 25 | FLAGS: 26 | -h, --help Prints help information 27 | -V, --version Prints version information 28 | 29 | OPTIONS: 30 | -e, --exec ... Specify path to executables to read list of dwarf objects from 31 | -o, --output Specify path to write the dwarf package to [default: -] 32 | 33 | ARGS: 34 | ... Specify path to input dwarf objects and packages 35 | ``` 36 | 37 | If the input objects are of DWARF version 5 or greater, then the output package will be in DWARF 5 38 | format. For version 4 and below, the GNU Extension format will be used for the output package. 39 | 40 | ## Stability 41 | `thorin`'s command-line interface is intended to be stable and have limited breaking changes. 42 | 43 |
44 | 45 | #### Name 46 | 47 | thorin is named after Thorin Oakenshield from The Hobbit, as Thorin is 48 | a dwarf who leads other dwarves. thorin uses the gimli library 49 | (named after a dwarf from Lord of the Rings) to read DWARF format debug information, 50 | the name of which is a medieval fantasy complement to ELF, the file format for executables 51 | and object files. 52 | 53 | 54 |
55 | 56 | 57 | You could also call this project rust-dwp, if you'd prefer that. 58 | 59 | 60 |
61 | 62 | #### Author and acknowledgements 63 | 64 | thorin is authored by David Wood of Huawei 65 | Technologies Research & Development (UK) Ltd. thorin is maintained by the 66 | Rust Compiler Team. 67 | 68 | 69 |
70 | 71 | 72 | In addition, thanks to the authors of object and gimli, on which this 73 | utility depends heavily; and to Philip Craig for advice 74 | and reviews during initial implementation of thorin. 75 | 76 | 77 |
78 | 79 | #### License 80 | 81 | Licensed under either of Apache License, 82 | Version 2.0 or MIT license at your option. 83 | 84 | 85 |
86 | 87 | 88 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in 89 | this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without 90 | any additional terms or conditions. 91 | 92 | 93 |
94 | 95 | #### Code of conduct 96 | 97 | When contributing or interacting with this project, we ask abide the 98 | Rust Code of Conduct and ask that you do 99 | too. 100 | 101 | -------------------------------------------------------------------------------- /thorin-bin/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | borrow::Borrow, 3 | ffi::OsStr, 4 | fs::{File, OpenOptions}, 5 | io::{self, BufWriter, Write}, 6 | path::{Path, PathBuf}, 7 | }; 8 | 9 | use anyhow::{Context, Result}; 10 | use memmap2::Mmap; 11 | use object::write::StreamingBuffer; 12 | use structopt::StructOpt; 13 | use thiserror::Error; 14 | use tracing::trace; 15 | use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; 16 | use tracing_tree::HierarchicalLayer; 17 | use typed_arena::Arena; 18 | 19 | #[derive(Debug, Error)] 20 | enum Error { 21 | #[error("Failed to add `{0}` to DWARF package")] 22 | AddInputObject(String), 23 | #[error("Failed to add DWARF object/packages referenced by `{0}` to DWARF package")] 24 | AddExecutable(String), 25 | #[error("Failed to create output object file at `{0}`")] 26 | CreateOutputFile(String), 27 | #[error("Failed verifying final DWARF package")] 28 | Finish, 29 | #[error("Failed writing output object to output buffer")] 30 | EmitOutputObject, 31 | } 32 | 33 | #[derive(Debug, StructOpt)] 34 | #[structopt(name = "thorin", about = "merge dwarf objects into dwarf packages")] 35 | struct Opt { 36 | /// Specify path to input dwarf objects and packages 37 | #[structopt(parse(from_os_str))] 38 | inputs: Vec, 39 | /// Specify path to executables to read list of dwarf objects from 40 | #[structopt(short = "e", long = "exec", parse(from_os_str))] 41 | executables: Vec, 42 | /// Specify path to write the dwarf package to [default: -] 43 | #[structopt(short = "o", long = "output", parse(from_os_str))] 44 | output: Option, 45 | } 46 | 47 | /// Implementation of `thorin::Session` using `typed_arena` and `memmap2`. 48 | #[derive(Default)] 49 | struct Session { 50 | arena_data: Arena>, 51 | arena_mmap: Arena, 52 | arena_relocations: Arena, 53 | } 54 | 55 | impl Session { 56 | fn alloc_mmap<'arena>(&'arena self, data: Mmap) -> &'arena Mmap { 57 | (*self.arena_mmap.alloc(data)).borrow() 58 | } 59 | } 60 | 61 | impl thorin::Session for Session { 62 | fn alloc_data<'arena>(&'arena self, data: Vec) -> &'arena [u8] { 63 | (*self.arena_data.alloc(data)).borrow() 64 | } 65 | 66 | fn alloc_relocation<'arena>(&'arena self, data: Relocations) -> &'arena Relocations { 67 | (*self.arena_relocations.alloc(data)).borrow() 68 | } 69 | 70 | fn read_input<'arena>(&'arena self, path: &Path) -> std::io::Result<&'arena [u8]> { 71 | let file = File::open(&path)?; 72 | let mmap = (unsafe { Mmap::map(&file) })?; 73 | Ok(self.alloc_mmap(mmap)) 74 | } 75 | } 76 | 77 | /// Returns `true` if the file type is a fifo. 78 | #[cfg(not(target_family = "unix"))] 79 | fn is_fifo(_: std::fs::FileType) -> bool { 80 | false 81 | } 82 | 83 | /// Returns `true` if the file type is a fifo. 84 | #[cfg(target_family = "unix")] 85 | fn is_fifo(file_type: std::fs::FileType) -> bool { 86 | use std::os::unix::fs::FileTypeExt; 87 | file_type.is_fifo() 88 | } 89 | 90 | /// Wrapper around output writer which handles differences between stdout, file and pipe outputs. 91 | pub(crate) enum Output { 92 | Stdout(io::Stdout), 93 | File(File), 94 | Pipe(File), 95 | } 96 | 97 | impl Output { 98 | /// Create a `Output` from the input path (or "-" for stdout). 99 | pub(crate) fn new(path: &OsStr) -> io::Result { 100 | if path == "-" { 101 | return Ok(Output::Stdout(io::stdout())); 102 | } 103 | 104 | let file = 105 | OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path)?; 106 | if is_fifo(file.metadata()?.file_type()) { 107 | Ok(Output::File(file)) 108 | } else { 109 | Ok(Output::Pipe(file)) 110 | } 111 | } 112 | } 113 | 114 | impl io::Write for Output { 115 | fn flush(&mut self) -> io::Result<()> { 116 | match self { 117 | Output::Stdout(stdout) => stdout.flush(), 118 | Output::Pipe(pipe) => pipe.flush(), 119 | Output::File(file) => file.flush(), 120 | } 121 | } 122 | 123 | fn write(&mut self, buf: &[u8]) -> io::Result { 124 | match self { 125 | Output::Stdout(stdout) => stdout.write(buf), 126 | Output::Pipe(pipe) => pipe.write(buf), 127 | Output::File(file) => file.write(buf), 128 | } 129 | } 130 | } 131 | 132 | fn main() -> Result<()> { 133 | let subscriber = Registry::default().with(EnvFilter::from_env("THORIN_LOG")).with( 134 | HierarchicalLayer::default() 135 | .with_writer(io::stderr) 136 | .with_indent_lines(true) 137 | .with_targets(true) 138 | .with_indent_amount(2), 139 | ); 140 | tracing::subscriber::set_global_default(subscriber).expect("failed to set subscriber"); 141 | 142 | let opt = Opt::from_args(); 143 | trace!(?opt); 144 | 145 | let sess = Session::default(); 146 | let mut package = thorin::DwarfPackage::new(&sess); 147 | 148 | // Return early if there isn't any input. 149 | if opt.inputs.is_empty() && opt.executables.is_empty() { 150 | return Ok(()); 151 | } 152 | 153 | for input in &opt.inputs { 154 | package 155 | .add_input_object(input) 156 | .with_context(|| Error::AddInputObject(input.display().to_string()))?; 157 | } 158 | 159 | for executable in &opt.executables { 160 | // Failing to read the referenced object might be expected if the path referenced by 161 | // the executable isn't found but the referenced DWARF object is later found as an 162 | // input - calling `finish` will return an error in this case. 163 | package 164 | .add_executable(executable, thorin::MissingReferencedObjectBehaviour::Skip) 165 | .with_context(|| Error::AddExecutable(executable.display().to_string()))?; 166 | } 167 | 168 | let output = opt.output.unwrap_or_else(|| { 169 | if opt.executables.len() == 1 { 170 | let mut path = opt.executables.first().unwrap().clone().into_os_string(); 171 | path.push(".dwp"); 172 | PathBuf::from(path) 173 | } else { 174 | PathBuf::from("-") 175 | } 176 | }); 177 | let output_stream = Output::new(output.as_os_str()) 178 | .with_context(|| Error::CreateOutputFile(output.display().to_string()))?; 179 | let mut output_stream = StreamingBuffer::new(BufWriter::new(output_stream)); 180 | package 181 | .finish() 182 | .context(Error::Finish)? 183 | .emit(&mut output_stream) 184 | .context(Error::EmitOutputObject)?; 185 | output_stream.result().context(Error::EmitOutputObject)?; 186 | output_stream.into_inner().flush().context(Error::EmitOutputObject) 187 | } 188 | -------------------------------------------------------------------------------- /thorin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "thorin-dwp" 3 | authors = [ "David Wood " ] 4 | categories = ["development-tools::debugging"] 5 | description = "Library for building DWARF packages from input DWARF objects and packages" 6 | homepage = "https://docs.rs/thorin-dwp" 7 | keywords = ["dwarf", "split-dwarf", "dwarf-package", "dwarf-object", "dwp"] 8 | license = "MIT OR Apache-2.0" 9 | readme = "README.md" 10 | repository = "https://github.com/rust-lang/thorin" 11 | version = "0.9.0" 12 | edition = "2021" 13 | 14 | [dependencies] 15 | tracing = "0.1.29" 16 | hashbrown = "0.15.0" 17 | 18 | [dependencies.gimli] 19 | version = "0.31.0" 20 | default-features = false 21 | # `gimli/std` pulls in `fallible-iterator` which we don't use, but can't opt out of, because of 22 | # cargo#8832. 23 | features = [ "read", "write", "std" ] 24 | 25 | [dependencies.object] 26 | version = "0.36.0" 27 | default-features = false 28 | features = [ "archive", "read", "write", "compression" ] 29 | 30 | [lib] 31 | name = "thorin" 32 | bench = false 33 | -------------------------------------------------------------------------------- /thorin/README.md: -------------------------------------------------------------------------------- 1 | # `thorin` 2 | `thorin` is an DWARF packaging utility for creating DWARF packages (`*.dwp` files) out of input 3 | DWARF objects (`*.dwo` files; or `*.o` files with `.dwo` sections), supporting both the pre-standard 4 | GNU extension format for DWARF packages and the standardized format introduced in DWARF 5. 5 | 6 | `thorin` was written as part of the implementation of Split DWARF in `rustc`. A Rust implementation 7 | of a DWARF packaging utility is easier to integrate into the compiler and can support features like 8 | loading dwarf objects from archive files (or rustc's rlibs) which are helpful in supporting 9 | cross-crate Split DWARF packaging in `rustc`. 10 | 11 | ## Usage 12 | To use `thorin` in your own project, add it to your `Cargo.toml`: 13 | 14 | ```toml 15 | thorin-dwp = "0.9.0" 16 | ``` 17 | 18 | See the [`thorin-bin`](../thorin-bin/README.md) crate for an example of using `thorin`'s library 19 | interface. 20 | 21 | ## Stability 22 | `thorin`'s library interface is intended for use by `rustc` for its *Split DWARF* support, it 23 | currently comes with no stability guarantees and may change at any time. 24 | 25 |
26 | 27 | #### Name 28 | 29 | thorin is named after Thorin Oakenshield from The Hobbit, as Thorin is 30 | a dwarf who leads other dwarves. thorin uses the gimli library 31 | (named after a dwarf from Lord of the Rings) to read DWARF format debug information, 32 | the name of which is a medieval fantasy complement to ELF, the file format for executables 33 | and object files. 34 | 35 | 36 |
37 | 38 | 39 | You could also call this project rust-dwp, if you'd prefer that. 40 | 41 | 42 |
43 | 44 | #### Author and acknowledgements 45 | 46 | thorin is authored by David Wood of Huawei 47 | Technologies Research & Development (UK) Ltd. thorin is maintained by the 48 | Rust Compiler Team. 49 | 50 | 51 |
52 | 53 | 54 | In addition, thanks to the authors of object and gimli, on which this 55 | utility depends heavily; and to Philip Craig for advice 56 | and reviews during initial implementation of thorin. 57 | 58 | 59 |
60 | 61 | #### License 62 | 63 | Licensed under either of Apache License, 64 | Version 2.0 or MIT license at your option. 65 | 66 | 67 |
68 | 69 | 70 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in 71 | this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without 72 | any additional terms or conditions. 73 | 74 | 75 |
76 | 77 | #### Code of conduct 78 | 79 | When contributing or interacting with this project, we ask abide the 80 | Rust Code of Conduct and ask that you do 81 | too. 82 | 83 | -------------------------------------------------------------------------------- /thorin/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error as StdError; 2 | use std::fmt; 3 | 4 | pub(crate) type Result = std::result::Result; 5 | 6 | /// Helper trait for converting an error to a `&dyn std::error::Error`. 7 | pub trait AsDynError<'a> { 8 | fn as_dyn_error(&self) -> &(dyn StdError + 'a); 9 | } 10 | 11 | impl<'a, T: StdError + 'a> AsDynError<'a> for T { 12 | #[inline] 13 | fn as_dyn_error(&self) -> &(dyn StdError + 'a) { 14 | self 15 | } 16 | } 17 | 18 | /// Diagnostics (and contexts) emitted during DWARF packaging. 19 | #[derive(Debug)] 20 | #[non_exhaustive] 21 | pub enum Error { 22 | /// Failure to read input file. 23 | /// 24 | /// This error occurs in the `Session::read_input` function provided by the user of `thorin`. 25 | ReadInput(std::io::Error), 26 | /// Failed to parse kind of input file. 27 | /// 28 | /// Input file kind is necessary to determine how to parse the rest of the input, and to 29 | /// validate that the input file is of a type that `thorin` can process. 30 | ParseFileKind(object::Error), 31 | /// Failed to parse object file. 32 | ParseObjectFile(object::Error), 33 | /// Failed to parse archive file. 34 | ParseArchiveFile(object::Error), 35 | /// Failed to parse archive member. 36 | ParseArchiveMember(object::Error), 37 | /// Invalid kind of input. 38 | /// 39 | /// Only archive and elf files are supported input files. 40 | InvalidInputKind, 41 | /// Failed to decompress data. 42 | /// 43 | /// `thorin` uses `object` for decompression, so `object` probably didn't have support for the 44 | /// type of compression used. 45 | DecompressData(object::Error), 46 | /// Section without a name. 47 | NamelessSection(object::Error, usize), 48 | /// Relocation has invalid symbol for a section. 49 | RelocationWithInvalidSymbol(String, usize), 50 | /// Multiple relocations for a section. 51 | MultipleRelocations(String, usize), 52 | /// Unsupported relocations for a section. 53 | UnsupportedRelocation(String, usize), 54 | /// Input object that has a `DwoId` (or `DebugTypeSignature`) does not have a 55 | /// `DW_AT_GNU_dwo_name` or `DW_AT_dwo_name` attribute. 56 | MissingDwoName(u64), 57 | /// Input object has no compilation units. 58 | NoCompilationUnits, 59 | /// No top-level debugging information entry in unit. 60 | NoDie, 61 | /// Top-level debugging information entry is not a compilation/type unit. 62 | TopLevelDieNotUnit, 63 | /// Section required of input DWARF objects was missing. 64 | MissingRequiredSection(&'static str), 65 | /// Failed to parse unit abbreviations. 66 | ParseUnitAbbreviations(gimli::read::Error), 67 | /// Failed to parse unit attribute. 68 | ParseUnitAttribute(gimli::read::Error), 69 | /// Failed to parse unit header. 70 | ParseUnitHeader(gimli::read::Error), 71 | /// Failed to parse unit. 72 | ParseUnit(gimli::read::Error), 73 | /// Input DWARF package has a different index version than the version being output. 74 | IncompatibleIndexVersion(String, u16, u16), 75 | /// Failed to read string offset from `.debug_str_offsets` at index. 76 | OffsetAtIndex(gimli::read::Error, u64), 77 | /// Failed to read string from `.debug_str` at offset. 78 | StrAtOffset(gimli::read::Error, usize), 79 | /// Failed to parse index section. 80 | /// 81 | /// If an input file is a DWARF package, its index section needs to be read to ensure that the 82 | /// contributions within it are preserved. 83 | ParseIndex(gimli::read::Error, String), 84 | /// Compilation unit in DWARF package is not its index. 85 | UnitNotInIndex(u64), 86 | /// Row for a compilation unit is not in the index. 87 | RowNotInIndex(gimli::read::Error, u32), 88 | /// Section not found in unit's row in index, i.e. a DWARF package contains a section but its 89 | /// index doesn't record contributions to it. 90 | SectionNotInRow, 91 | /// Compilation unit in input DWARF object has no content. 92 | EmptyUnit(u64), 93 | /// Found multiple `.debug_info.dwo` sections. 94 | MultipleDebugInfoSection, 95 | /// Found multiple `.debug_types.dwo` sections in a DWARF package file. 96 | MultipleDebugTypesSection, 97 | /// Found a regular compilation unit in a DWARF object. 98 | NotSplitUnit, 99 | /// Found duplicate split compilation unit. 100 | DuplicateUnit(u64), 101 | /// Unit referenced by an executable was not found. 102 | MissingReferencedUnit(u64), 103 | /// No output object was created from inputs 104 | NoOutputObjectCreated, 105 | /// Input objects have different encodings. 106 | MixedInputEncodings, 107 | 108 | /// Catch-all for `std::io::Error`. 109 | Io(std::io::Error), 110 | /// Catch-all for `object::Error`. 111 | ObjectRead(object::Error), 112 | /// Catch-all for `object::write::Error`. 113 | ObjectWrite(object::write::Error), 114 | /// Catch-all for `gimli::read::Error`. 115 | GimliRead(gimli::read::Error), 116 | /// Catch-all for `gimli::write::Error`. 117 | GimliWrite(gimli::write::Error), 118 | } 119 | 120 | impl StdError for Error { 121 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 122 | match self { 123 | Error::ReadInput(source) => Some(source.as_dyn_error()), 124 | Error::ParseFileKind(source) => Some(source.as_dyn_error()), 125 | Error::ParseObjectFile(source) => Some(source.as_dyn_error()), 126 | Error::ParseArchiveFile(source) => Some(source.as_dyn_error()), 127 | Error::ParseArchiveMember(source) => Some(source.as_dyn_error()), 128 | Error::InvalidInputKind => None, 129 | Error::DecompressData(source) => Some(source.as_dyn_error()), 130 | Error::NamelessSection(source, _) => Some(source.as_dyn_error()), 131 | Error::RelocationWithInvalidSymbol(_, _) => None, 132 | Error::MultipleRelocations(_, _) => None, 133 | Error::UnsupportedRelocation(_, _) => None, 134 | Error::MissingDwoName(_) => None, 135 | Error::NoCompilationUnits => None, 136 | Error::NoDie => None, 137 | Error::TopLevelDieNotUnit => None, 138 | Error::MissingRequiredSection(_) => None, 139 | Error::ParseUnitAbbreviations(source) => Some(source.as_dyn_error()), 140 | Error::ParseUnitAttribute(source) => Some(source.as_dyn_error()), 141 | Error::ParseUnitHeader(source) => Some(source.as_dyn_error()), 142 | Error::ParseUnit(source) => Some(source.as_dyn_error()), 143 | Error::IncompatibleIndexVersion(_, _, _) => None, 144 | Error::OffsetAtIndex(source, _) => Some(source.as_dyn_error()), 145 | Error::StrAtOffset(source, _) => Some(source.as_dyn_error()), 146 | Error::ParseIndex(source, _) => Some(source.as_dyn_error()), 147 | Error::UnitNotInIndex(_) => None, 148 | Error::RowNotInIndex(source, _) => Some(source.as_dyn_error()), 149 | Error::SectionNotInRow => None, 150 | Error::EmptyUnit(_) => None, 151 | Error::MultipleDebugInfoSection => None, 152 | Error::MultipleDebugTypesSection => None, 153 | Error::NotSplitUnit => None, 154 | Error::DuplicateUnit(_) => None, 155 | Error::MissingReferencedUnit(_) => None, 156 | Error::NoOutputObjectCreated => None, 157 | Error::MixedInputEncodings => None, 158 | Error::Io(transparent) => StdError::source(transparent.as_dyn_error()), 159 | Error::ObjectRead(transparent) => StdError::source(transparent.as_dyn_error()), 160 | Error::ObjectWrite(transparent) => StdError::source(transparent.as_dyn_error()), 161 | Error::GimliRead(transparent) => StdError::source(transparent.as_dyn_error()), 162 | Error::GimliWrite(transparent) => StdError::source(transparent.as_dyn_error()), 163 | } 164 | } 165 | } 166 | 167 | impl fmt::Display for Error { 168 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 169 | match self { 170 | Error::ReadInput(_) => write!(f, "Failed to read input file"), 171 | Error::ParseFileKind(_) => write!(f, "Failed to parse input file kind"), 172 | Error::ParseObjectFile(_) => write!(f, "Failed to parse input object file"), 173 | Error::ParseArchiveFile(_) => write!(f, "Failed to parse input archive file"), 174 | Error::ParseArchiveMember(_) => write!(f, "Failed to parse archive member"), 175 | Error::InvalidInputKind => write!(f, "Input is not an archive or elf object"), 176 | Error::DecompressData(_) => write!(f, "Failed to decompress compressed section"), 177 | Error::NamelessSection(_, offset) => { 178 | write!(f, "Section without name at offset 0x{:08x}", offset) 179 | } 180 | Error::RelocationWithInvalidSymbol(section, offset) => write!( 181 | f, 182 | "Relocation with invalid symbol for section `{}` at offset 0x{:08x}", 183 | section, offset 184 | ), 185 | Error::MultipleRelocations(section, offset) => write!( 186 | f, 187 | "Multiple relocations for section `{}` at offset 0x{:08x}", 188 | section, offset 189 | ), 190 | Error::UnsupportedRelocation(section, offset) => write!( 191 | f, 192 | "Unsupported relocation for section {} at offset 0x{:08x}", 193 | section, offset 194 | ), 195 | Error::MissingDwoName(id) => { 196 | write!(f, "Missing path attribute to DWARF object (0x{:08x})", id) 197 | } 198 | Error::NoCompilationUnits => { 199 | write!(f, "Input object has no compilation units") 200 | } 201 | Error::NoDie => { 202 | write!(f, "No top-level debugging information entry in compilation/type unit") 203 | } 204 | Error::TopLevelDieNotUnit => { 205 | write!(f, "Top-level debugging information entry is not a compilation/type unit") 206 | } 207 | Error::MissingRequiredSection(section) => { 208 | write!(f, "Input object missing required section `{}`", section) 209 | } 210 | Error::ParseUnitAbbreviations(_) => write!(f, "Failed to parse unit abbreviations"), 211 | Error::ParseUnitAttribute(_) => write!(f, "Failed to parse unit attribute"), 212 | Error::ParseUnitHeader(_) => write!(f, "Failed to parse unit header"), 213 | Error::ParseUnit(_) => write!(f, "Failed to parse unit"), 214 | Error::IncompatibleIndexVersion(section, format, actual) => { 215 | write!( 216 | f, 217 | "Incompatible `{}` index version: found version {}, expected version {}", 218 | section, actual, format 219 | ) 220 | } 221 | Error::OffsetAtIndex(_, index) => { 222 | write!(f, "Read offset at index {} of `.debug_str_offsets.dwo` section", index) 223 | } 224 | Error::StrAtOffset(_, offset) => { 225 | write!(f, "Read string at offset 0x{:08x} of `.debug_str.dwo` section", offset) 226 | } 227 | Error::ParseIndex(_, section) => { 228 | write!(f, "Failed to parse `{}` index section", section) 229 | } 230 | Error::UnitNotInIndex(unit) => { 231 | write!(f, "Unit 0x{0:08x} from input package is not in its index", unit) 232 | } 233 | Error::RowNotInIndex(_, row) => { 234 | write!(f, "Row {0} found in index's hash table not present in index", row) 235 | } 236 | Error::SectionNotInRow => write!(f, "Section not found in unit's row in index"), 237 | Error::EmptyUnit(unit) => { 238 | write!(f, "Unit 0x{:08x} in input DWARF object with no data", unit) 239 | } 240 | Error::MultipleDebugInfoSection => { 241 | write!(f, "Multiple `.debug_info.dwo` sections") 242 | } 243 | Error::MultipleDebugTypesSection => { 244 | write!(f, "Multiple `.debug_types.dwo` sections in a package") 245 | } 246 | Error::NotSplitUnit => { 247 | write!(f, "Regular compilation unit in object (missing dwo identifier)") 248 | } 249 | Error::DuplicateUnit(unit) => { 250 | write!(f, "Duplicate split compilation unit (0x{:08x})", unit) 251 | } 252 | Error::MissingReferencedUnit(unit) => { 253 | write!(f, "Unit 0x{:08x} referenced by executable was not found", unit) 254 | } 255 | Error::NoOutputObjectCreated => write!(f, "No output object was created from inputs"), 256 | Error::MixedInputEncodings => write!(f, "Input objects haved mixed encodings"), 257 | Error::Io(e) => fmt::Display::fmt(e, f), 258 | Error::ObjectRead(e) => fmt::Display::fmt(e, f), 259 | Error::ObjectWrite(e) => fmt::Display::fmt(e, f), 260 | Error::GimliRead(e) => fmt::Display::fmt(e, f), 261 | Error::GimliWrite(e) => fmt::Display::fmt(e, f), 262 | } 263 | } 264 | } 265 | 266 | impl From for Error { 267 | fn from(source: std::io::Error) -> Self { 268 | Error::Io(source) 269 | } 270 | } 271 | 272 | impl From for Error { 273 | fn from(source: object::Error) -> Self { 274 | Error::ObjectRead(source) 275 | } 276 | } 277 | 278 | impl From for Error { 279 | fn from(source: object::write::Error) -> Self { 280 | Error::ObjectWrite(source) 281 | } 282 | } 283 | 284 | impl From for Error { 285 | fn from(source: gimli::read::Error) -> Self { 286 | Error::GimliRead(source) 287 | } 288 | } 289 | 290 | impl From for Error { 291 | fn from(source: gimli::write::Error) -> Self { 292 | Error::GimliWrite(source) 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /thorin/src/ext.rs: -------------------------------------------------------------------------------- 1 | use gimli::{Encoding, EndianSlice, RunTimeEndian, UnitIndex}; 2 | use object::{Endianness, ObjectSection}; 3 | 4 | use crate::{relocate::RelocationMap, Session}; 5 | 6 | /// Helper trait to translate between `object`'s `Endianness` and `gimli`'s `RunTimeEndian`. 7 | pub(crate) trait EndianityExt { 8 | fn as_runtime_endian(&self) -> RunTimeEndian; 9 | } 10 | 11 | impl EndianityExt for Endianness { 12 | fn as_runtime_endian(&self) -> RunTimeEndian { 13 | match *self { 14 | Endianness::Little => RunTimeEndian::Little, 15 | Endianness::Big => RunTimeEndian::Big, 16 | } 17 | } 18 | } 19 | 20 | /// Helper trait to add `compressed_data_range` function to `ObjectSection` types. 21 | pub(crate) trait CompressedDataRangeExt<'input, 'session: 'input>: 22 | ObjectSection<'input> 23 | { 24 | /// Return the decompressed contents of the section data in the given range. 25 | /// 26 | /// Decompression happens only if the data is compressed. 27 | fn compressed_data_range( 28 | &self, 29 | sess: &'session impl Session, 30 | address: u64, 31 | size: u64, 32 | ) -> object::Result>; 33 | } 34 | 35 | impl<'input, 'session: 'input, S> CompressedDataRangeExt<'input, 'session> for S 36 | where 37 | S: ObjectSection<'input>, 38 | { 39 | fn compressed_data_range( 40 | &self, 41 | sess: &'session impl Session, 42 | address: u64, 43 | size: u64, 44 | ) -> object::Result> { 45 | let data = self.compressed_data()?.decompress()?; 46 | 47 | /// Originally from `object::read::util`, used in `ObjectSection::data_range`, but not 48 | /// public. 49 | fn data_range( 50 | data: &[u8], 51 | data_address: u64, 52 | range_address: u64, 53 | size: u64, 54 | ) -> Option<&[u8]> { 55 | let offset = range_address.checked_sub(data_address)?; 56 | data.get(offset.try_into().ok()?..)?.get(..size.try_into().ok()?) 57 | } 58 | 59 | let data_ref = sess.alloc_owned_cow(data); 60 | Ok(data_range(data_ref, self.address(), address, size)) 61 | } 62 | } 63 | 64 | /// Helper trait that abstracts over `gimli::DebugCuIndex` and `gimli::DebugTuIndex`. 65 | pub(crate) trait IndexSectionExt<'input, Endian: gimli::Endianity, R: gimli::Reader>: 66 | gimli::Section 67 | { 68 | fn new(section: &'input [u8], endian: Endian) -> Self; 69 | 70 | fn index(self) -> gimli::read::Result>; 71 | } 72 | 73 | impl<'input, Endian: gimli::Endianity> IndexSectionExt<'input, Endian, EndianSlice<'input, Endian>> 74 | for gimli::DebugCuIndex> 75 | { 76 | fn new(section: &'input [u8], endian: Endian) -> Self { 77 | Self::new(section, endian) 78 | } 79 | 80 | fn index(self) -> gimli::read::Result>> { 81 | Self::index(self) 82 | } 83 | } 84 | 85 | impl<'input, Endian: gimli::Endianity> IndexSectionExt<'input, Endian, EndianSlice<'input, Endian>> 86 | for gimli::DebugTuIndex> 87 | { 88 | fn new(section: &'input [u8], endian: Endian) -> Self { 89 | Self::new(section, endian) 90 | } 91 | 92 | fn index(self) -> gimli::read::Result>> { 93 | Self::index(self) 94 | } 95 | } 96 | 97 | /// Helper trait to add DWARF package specific functions to the `Encoding` type. 98 | pub(crate) trait PackageFormatExt { 99 | /// Returns `true` if this `Encoding` would produce to a DWARF 5-standardized package file. 100 | /// 101 | /// See Sec 7.3.5 and Appendix F of the [DWARF specification]. 102 | /// 103 | /// [DWARF specification]: https://dwarfstd.org/doc/DWARF5.pdf 104 | fn is_std_dwarf_package_format(&self) -> bool; 105 | 106 | /// Returns `true` if this `Encoding` would produce a GNU Extension DWARF package file 107 | /// (preceded standardized version from DWARF 5). 108 | /// 109 | /// See [specification](https://gcc.gnu.org/wiki/DebugFissionDWP). 110 | fn is_gnu_extension_dwarf_package_format(&self) -> bool; 111 | 112 | /// Returns index version of DWARF package for this `Encoding`. 113 | fn dwarf_package_index_version(&self) -> u16; 114 | 115 | /// Returns `true` if the dwarf package index version provided is compatible with this 116 | /// `Encoding`. 117 | fn is_compatible_dwarf_package_index_version(&self, index_version: u16) -> bool; 118 | } 119 | 120 | impl PackageFormatExt for Encoding { 121 | fn is_gnu_extension_dwarf_package_format(&self) -> bool { 122 | !self.is_std_dwarf_package_format() 123 | } 124 | 125 | fn is_std_dwarf_package_format(&self) -> bool { 126 | self.version >= 5 127 | } 128 | 129 | fn dwarf_package_index_version(&self) -> u16 { 130 | if self.is_gnu_extension_dwarf_package_format() { 131 | 2 132 | } else { 133 | 5 134 | } 135 | } 136 | 137 | fn is_compatible_dwarf_package_index_version(&self, index_version: u16) -> bool { 138 | if self.is_gnu_extension_dwarf_package_format() { 139 | index_version == 2 140 | } else { 141 | index_version >= 5 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /thorin/src/index.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use gimli::{ 4 | write::{EndianVec, Writer}, 5 | Encoding, 6 | }; 7 | use tracing::{debug, trace}; 8 | 9 | use crate::{ 10 | error::{Error, Result}, 11 | ext::PackageFormatExt, 12 | package::DwarfObject, 13 | }; 14 | 15 | /// Helper trait for types that can be used in creating the `.debug_{cu,tu}_index` hash table. 16 | pub(crate) trait Bucketable { 17 | fn index(&self) -> u64; 18 | } 19 | 20 | /// Returns a hash table computed for `elements`. Used in the `.debug_{cu,tu}_index` sections. 21 | #[tracing::instrument(level = "trace", skip_all)] 22 | fn bucket(elements: &[B]) -> Vec { 23 | let unit_count: u32 = elements.len().try_into().expect("unit count larger than u32"); 24 | let num_buckets = if elements.len() < 2 { 2 } else { (3 * unit_count / 2).next_power_of_two() }; 25 | let mask: u64 = num_buckets as u64 - 1; 26 | trace!(?mask); 27 | 28 | let mut buckets = vec![0u32; num_buckets as usize]; 29 | trace!(?buckets); 30 | for (elem, i) in elements.iter().zip(0u32..) { 31 | trace!(?i, ?elem); 32 | let s = elem.index(); 33 | let mut h = s & mask; 34 | let hp = ((s >> 32) & mask) | 1; 35 | trace!(?s, ?h, ?hp); 36 | 37 | while buckets[h as usize] > 0 { 38 | assert!(elements[(buckets[h as usize] - 1) as usize].index() != elem.index()); 39 | h = (h + hp) & mask; 40 | trace!(?h); 41 | } 42 | 43 | buckets[h as usize] = i + 1; 44 | trace!(?buckets); 45 | } 46 | 47 | buckets 48 | } 49 | 50 | /// New-type'd offset into a section of a compilation/type unit's contribution. 51 | #[derive(Copy, Clone, Eq, Hash, PartialEq)] 52 | pub(crate) struct ContributionOffset(pub(crate) u64); 53 | 54 | impl fmt::Debug for ContributionOffset { 55 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 56 | write!(f, "ContributionOffset({:#x})", self.0) 57 | } 58 | } 59 | 60 | /// Type alias for the size of a compilation/type unit's contribution. 61 | type ContributionSize = u64; 62 | 63 | /// Contribution to a section - offset and size. 64 | #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] 65 | pub(crate) struct Contribution { 66 | /// Offset of this contribution into its containing section. 67 | pub(crate) offset: ContributionOffset, 68 | /// Size of this contribution in its containing section. 69 | pub(crate) size: ContributionSize, 70 | } 71 | 72 | /// Populated columns in the `.debug_cu_index` or `.debug_tu_index` section. 73 | #[derive(Copy, Clone, Debug, Default)] 74 | pub(crate) struct IndexColumns { 75 | pub(crate) debug_info: bool, 76 | pub(crate) debug_types: bool, 77 | pub(crate) debug_abbrev: bool, 78 | pub(crate) debug_line: bool, 79 | pub(crate) debug_loc: bool, 80 | pub(crate) debug_loclists: bool, 81 | pub(crate) debug_rnglists: bool, 82 | pub(crate) debug_str_offsets: bool, 83 | pub(crate) debug_macinfo: bool, 84 | pub(crate) debug_macro: bool, 85 | } 86 | 87 | impl IndexColumns { 88 | /// Return the number of columns required for all index entries. 89 | fn number_of_columns(&self) -> u32 { 90 | self.debug_info as u32 91 | + self.debug_types as u32 92 | + self.debug_abbrev as u32 93 | + self.debug_line as u32 94 | + self.debug_loc as u32 95 | + self.debug_loclists as u32 96 | + self.debug_rnglists as u32 97 | + self.debug_str_offsets as u32 98 | + self.debug_macinfo as u32 99 | + self.debug_macro as u32 100 | } 101 | 102 | /// Update the columns to include the columns required for an index entry. 103 | fn add_entry(&mut self, entry: &IndexEntry) { 104 | self.debug_info |= entry.debug_info.is_some(); 105 | self.debug_types |= entry.debug_types.is_some(); 106 | self.debug_abbrev |= entry.debug_abbrev.is_some(); 107 | self.debug_line |= entry.debug_line.is_some(); 108 | self.debug_loc |= entry.debug_loc.is_some(); 109 | self.debug_loclists |= entry.debug_loclists.is_some(); 110 | self.debug_rnglists |= entry.debug_rnglists.is_some(); 111 | self.debug_str_offsets |= entry.debug_str_offsets.is_some(); 112 | self.debug_macinfo |= entry.debug_macinfo.is_some(); 113 | self.debug_macro |= entry.debug_macro.is_some(); 114 | } 115 | 116 | /// Write the header row for the columns. 117 | /// 118 | /// There is only a single header row in any index section, its contents depend on the output 119 | /// format and the columns from contributions so the complete index entries are required to 120 | /// know what header to write. 121 | fn write_header( 122 | &self, 123 | out: &mut EndianVec, 124 | encoding: Encoding, 125 | ) -> Result<()> { 126 | if encoding.is_gnu_extension_dwarf_package_format() { 127 | if self.debug_info { 128 | out.write_u32(gimli::DW_SECT_V2_INFO.0)?; 129 | } 130 | if self.debug_types { 131 | out.write_u32(gimli::DW_SECT_V2_TYPES.0)?; 132 | } 133 | if self.debug_abbrev { 134 | out.write_u32(gimli::DW_SECT_V2_ABBREV.0)?; 135 | } 136 | if self.debug_line { 137 | out.write_u32(gimli::DW_SECT_V2_LINE.0)?; 138 | } 139 | if self.debug_loc { 140 | out.write_u32(gimli::DW_SECT_V2_LOC.0)?; 141 | } 142 | if self.debug_str_offsets { 143 | out.write_u32(gimli::DW_SECT_V2_STR_OFFSETS.0)?; 144 | } 145 | if self.debug_macinfo { 146 | out.write_u32(gimli::DW_SECT_V2_MACINFO.0)?; 147 | } 148 | if self.debug_macro { 149 | out.write_u32(gimli::DW_SECT_V2_MACRO.0)?; 150 | } 151 | } else { 152 | if self.debug_info { 153 | out.write_u32(gimli::DW_SECT_INFO.0)?; 154 | } 155 | if self.debug_abbrev { 156 | out.write_u32(gimli::DW_SECT_ABBREV.0)?; 157 | } 158 | if self.debug_line { 159 | out.write_u32(gimli::DW_SECT_LINE.0)?; 160 | } 161 | if self.debug_loclists { 162 | out.write_u32(gimli::DW_SECT_LOCLISTS.0)?; 163 | } 164 | if self.debug_rnglists { 165 | out.write_u32(gimli::DW_SECT_RNGLISTS.0)?; 166 | } 167 | if self.debug_str_offsets { 168 | out.write_u32(gimli::DW_SECT_STR_OFFSETS.0)?; 169 | } 170 | if self.debug_macro { 171 | out.write_u32(gimli::DW_SECT_MACRO.0)?; 172 | } 173 | } 174 | 175 | Ok(()) 176 | } 177 | } 178 | 179 | /// Entry into the `.debug_cu_index` or `.debug_tu_index` section. 180 | /// 181 | /// Contributions from `.debug_loclists.dwo` and `.debug_rnglists.dwo` from type units aren't 182 | /// defined in the DWARF 5 specification but are tested by `llvm-dwp`'s test suite. 183 | #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] 184 | pub(crate) struct IndexEntry { 185 | pub(crate) encoding: Encoding, 186 | pub(crate) id: DwarfObject, 187 | pub(crate) debug_info: Option, 188 | pub(crate) debug_types: Option, 189 | pub(crate) debug_abbrev: Option, 190 | pub(crate) debug_line: Option, 191 | pub(crate) debug_loc: Option, 192 | pub(crate) debug_loclists: Option, 193 | pub(crate) debug_rnglists: Option, 194 | pub(crate) debug_str_offsets: Option, 195 | pub(crate) debug_macinfo: Option, 196 | pub(crate) debug_macro: Option, 197 | } 198 | 199 | impl IndexEntry { 200 | /// Visit each contribution in an entry, calling `proj` to write a specific field. 201 | #[tracing::instrument(level = "trace", skip(out, proj))] 202 | fn write_contribution( 203 | &self, 204 | out: &mut EndianVec, 205 | proj: Proj, 206 | columns: &IndexColumns, 207 | ) -> Result<()> 208 | where 209 | Endian: gimli::Endianity, 210 | Proj: Copy + Fn(Contribution) -> u32, 211 | { 212 | let proj = |contrib: Option| contrib.map(proj).unwrap_or(0); 213 | if columns.debug_info { 214 | out.write_u32(proj(self.debug_info))?; 215 | } 216 | if columns.debug_types { 217 | out.write_u32(proj(self.debug_types))?; 218 | } 219 | if columns.debug_abbrev { 220 | out.write_u32(proj(self.debug_abbrev))?; 221 | } 222 | if columns.debug_line { 223 | out.write_u32(proj(self.debug_line))?; 224 | } 225 | if columns.debug_loc { 226 | out.write_u32(proj(self.debug_loc))?; 227 | } 228 | if columns.debug_loclists { 229 | out.write_u32(proj(self.debug_loclists))?; 230 | } 231 | if columns.debug_rnglists { 232 | out.write_u32(proj(self.debug_rnglists))?; 233 | } 234 | if columns.debug_str_offsets { 235 | out.write_u32(proj(self.debug_str_offsets))?; 236 | } 237 | if columns.debug_macinfo { 238 | out.write_u32(proj(self.debug_macinfo))?; 239 | } 240 | if columns.debug_macro { 241 | out.write_u32(proj(self.debug_macro))?; 242 | } 243 | 244 | Ok(()) 245 | } 246 | } 247 | 248 | impl Bucketable for IndexEntry { 249 | fn index(&self) -> u64 { 250 | self.id.index() 251 | } 252 | } 253 | 254 | /// Write a `.debug_{cu,tu}_index` section given `IndexEntry`s. 255 | #[tracing::instrument(level = "trace")] 256 | pub(crate) fn write_index<'output, Endian: gimli::Endianity>( 257 | endianness: Endian, 258 | entries: &[IndexEntry], 259 | ) -> Result> { 260 | let mut out = EndianVec::new(endianness); 261 | 262 | if entries.len() == 0 { 263 | return Ok(out); 264 | } 265 | 266 | let buckets = bucket(entries); 267 | debug!(?buckets); 268 | 269 | let encoding = entries[0].encoding; 270 | if !entries.iter().all(|e| e.encoding == encoding) { 271 | return Err(Error::MixedInputEncodings); 272 | } 273 | debug!(?encoding); 274 | 275 | let mut columns = IndexColumns::default(); 276 | for entry in entries { 277 | columns.add_entry(entry); 278 | } 279 | let num_columns = columns.number_of_columns(); 280 | debug!(?entries, ?columns, ?num_columns); 281 | 282 | // Write header.. 283 | if encoding.is_gnu_extension_dwarf_package_format() { 284 | // GNU Extension 285 | out.write_u32(2)?; 286 | } else { 287 | // DWARF 5 288 | out.write_u16(5)?; 289 | // Reserved padding 290 | out.write_u16(0)?; 291 | } 292 | 293 | // Columns (e.g. info, abbrev, loc, etc.) 294 | out.write_u32(num_columns)?; 295 | // Number of units 296 | out.write_u32(entries.len().try_into().expect("number of units larger than u32"))?; 297 | // Number of buckets 298 | out.write_u32(buckets.len().try_into().expect("number of buckets larger than u32"))?; 299 | 300 | // Write signatures.. 301 | for i in &buckets { 302 | if *i > 0 { 303 | out.write_u64(entries[(*i - 1) as usize].index())?; 304 | } else { 305 | out.write_u64(0)?; 306 | } 307 | } 308 | 309 | // Write indices.. 310 | for i in &buckets { 311 | out.write_u32(*i)?; 312 | } 313 | 314 | // Write column headers.. 315 | columns.write_header(&mut out, encoding)?; 316 | 317 | // Write offsets.. 318 | let write_offset = |contrib: Contribution| { 319 | contrib.offset.0.try_into().expect("contribution offset larger than u32") 320 | }; 321 | for entry in entries { 322 | entry.write_contribution(&mut out, write_offset, &columns)?; 323 | } 324 | 325 | // Write sizes.. 326 | let write_size = 327 | |contrib: Contribution| contrib.size.try_into().expect("contribution size larger than u32"); 328 | for entry in entries { 329 | entry.write_contribution(&mut out, write_size, &columns)?; 330 | } 331 | 332 | Ok(out) 333 | } 334 | -------------------------------------------------------------------------------- /thorin/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub extern crate object; 2 | 3 | use std::{ 4 | borrow::Cow, 5 | collections::HashSet, 6 | fmt, 7 | path::{Path, PathBuf}, 8 | }; 9 | 10 | use gimli::{EndianSlice, Reader}; 11 | use object::{write::Object as WritableObject, FileKind, Object, ObjectSection}; 12 | use tracing::{debug, trace}; 13 | 14 | use crate::{ 15 | error::Result, 16 | ext::EndianityExt, 17 | index::Bucketable, 18 | package::{dwo_identifier_of_unit, DwarfObject, InProgressDwarfPackage}, 19 | relocate::{add_relocations, Relocate, RelocationMap}, 20 | }; 21 | 22 | mod error; 23 | mod ext; 24 | mod index; 25 | mod package; 26 | mod relocate; 27 | mod strings; 28 | 29 | pub use crate::error::Error; 30 | 31 | /// `Session` is expected to be implemented by users of `thorin`, allowing users of `thorin` to 32 | /// decide how to manage data, rather than `thorin` having arenas internally. 33 | pub trait Session { 34 | /// Returns a reference to `data`'s contents with lifetime `'session`. 35 | fn alloc_data<'session>(&'session self, data: Vec) -> &'session [u8]; 36 | 37 | /// Returns a reference to `data`'s contents with lifetime `'input`. 38 | /// 39 | /// If `Cow` is borrowed, then return the contained reference (`'input`). If `Cow` is owned, 40 | /// then calls `alloc_data` to return a reference of lifetime `'session`, which is guaranteed 41 | /// to be longer than `'input`, so can be returned. 42 | fn alloc_owned_cow<'input, 'session: 'input>( 43 | &'session self, 44 | data: Cow<'input, [u8]>, 45 | ) -> &'input [u8] { 46 | match data { 47 | Cow::Borrowed(data) => data, 48 | Cow::Owned(data) => self.alloc_data(data), 49 | } 50 | } 51 | 52 | /// Returns a reference to `relocation` with lifetime `'session`. 53 | fn alloc_relocation<'session>(&'session self, data: Relocations) -> &'session Relocations; 54 | 55 | /// Returns a reference to contents of file at `path` with lifetime `'session`. 56 | fn read_input<'session>(&'session self, path: &Path) -> std::io::Result<&'session [u8]>; 57 | } 58 | 59 | /// Should missing DWARF objects referenced by executables be skipped or result in an error? 60 | /// 61 | /// Referenced objects that are still missing when the DWARF package is finished will result in 62 | /// an error. 63 | #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] 64 | pub enum MissingReferencedObjectBehaviour { 65 | /// Skip missing referenced DWARF objects - useful if this is expected, i.e. the path in the 66 | /// executable is wrong, but the referenced object will be found because it is an input. 67 | Skip, 68 | /// Error when encountering missing referenced DWARF objects. 69 | Error, 70 | } 71 | 72 | impl MissingReferencedObjectBehaviour { 73 | /// Should missing referenced objects be skipped? 74 | pub fn skip_missing(&self) -> bool { 75 | match *self { 76 | MissingReferencedObjectBehaviour::Skip => true, 77 | MissingReferencedObjectBehaviour::Error => false, 78 | } 79 | } 80 | } 81 | 82 | /// Builder for DWARF packages, add input objects/packages with `add_input_object` or input objects 83 | /// referenced by an executable with `add_executable` before accessing the completed object with 84 | /// `finish`. 85 | pub struct DwarfPackage<'output, 'session: 'output, Sess: Session> { 86 | sess: &'session Sess, 87 | maybe_in_progress: Option>, 88 | targets: HashSet, 89 | } 90 | 91 | impl<'output, 'session: 'output, Sess> fmt::Debug for DwarfPackage<'output, 'session, Sess> 92 | where 93 | Sess: Session, 94 | { 95 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 96 | f.debug_struct("DwarfPackage") 97 | .field("in_progress", &self.maybe_in_progress) 98 | .field("target_count", &self.targets.len()) 99 | .finish() 100 | } 101 | } 102 | 103 | impl<'output, 'session: 'output, Sess> DwarfPackage<'output, 'session, Sess> 104 | where 105 | Sess: Session, 106 | { 107 | /// Create a new `DwarfPackage` with the provided `Session` implementation. 108 | pub fn new(sess: &'session Sess) -> Self { 109 | Self { sess, maybe_in_progress: None, targets: HashSet::new() } 110 | } 111 | 112 | /// Add an input object to the in-progress package. 113 | #[tracing::instrument(level = "trace", skip(obj))] 114 | fn process_input_object<'input>(&mut self, obj: &'input object::File<'input>) -> Result<()> { 115 | if self.maybe_in_progress.is_none() { 116 | self.maybe_in_progress = 117 | Some(InProgressDwarfPackage::new(obj.architecture(), obj.endianness())); 118 | } 119 | 120 | let encoding = if let Some(section) = obj.section_by_name(".debug_info.dwo") { 121 | let data = section.compressed_data()?.decompress()?; 122 | let data_ref = self.sess.alloc_owned_cow(data); 123 | let debug_info = gimli::DebugInfo::new(data_ref, obj.endianness().as_runtime_endian()); 124 | debug_info 125 | .units() 126 | .next() 127 | .map_err(Error::ParseUnitHeader)? 128 | .map(|root_header| root_header.encoding()) 129 | .ok_or(Error::NoCompilationUnits)? 130 | } else { 131 | debug!("no `.debug_info.dwo` in input dwarf object"); 132 | return Ok(()); 133 | }; 134 | 135 | let sess = self.sess; 136 | self.maybe_in_progress 137 | .as_mut() 138 | .expect("`process_input_object` is broken") 139 | .add_input_object(sess, obj, encoding) 140 | } 141 | 142 | /// Add input objects referenced by executable to the DWARF package. 143 | #[tracing::instrument(level = "trace")] 144 | pub fn add_executable( 145 | &mut self, 146 | path: &Path, 147 | missing_behaviour: MissingReferencedObjectBehaviour, 148 | ) -> Result<()> { 149 | let data = self.sess.read_input(path).map_err(Error::ReadInput)?; 150 | let obj = object::File::parse(data).map_err(Error::ParseObjectFile)?; 151 | 152 | let mut load_section = |id: gimli::SectionId| -> Result<_> { 153 | let mut relocations = RelocationMap::default(); 154 | let data = match obj.section_by_name(&id.name()) { 155 | Some(ref section) => { 156 | add_relocations(&mut relocations, &obj, section)?; 157 | section.compressed_data()?.decompress()? 158 | } 159 | // Use a non-zero capacity so that `ReaderOffsetId`s are unique. 160 | None => Cow::Owned(Vec::with_capacity(1)), 161 | }; 162 | 163 | let data_ref = self.sess.alloc_owned_cow(data); 164 | let reader = EndianSlice::new(data_ref, obj.endianness().as_runtime_endian()); 165 | let section = reader; 166 | let relocations = self.sess.alloc_relocation(relocations); 167 | Ok(Relocate { relocations, section, reader }) 168 | }; 169 | 170 | let dwarf = gimli::Dwarf::load(&mut load_section)?; 171 | 172 | let mut iter = dwarf.units(); 173 | while let Some(header) = iter.next().map_err(Error::ParseUnitHeader)? { 174 | let unit = dwarf.unit(header).map_err(Error::ParseUnit)?; 175 | 176 | let target = match dwo_identifier_of_unit(&dwarf.debug_abbrev, &unit.header)? { 177 | Some(target) => target, 178 | None => { 179 | debug!("no target"); 180 | continue; 181 | } 182 | }; 183 | 184 | let dwo_name = { 185 | let mut cursor = unit.header.entries(&unit.abbreviations); 186 | cursor.next_dfs()?; 187 | let root = cursor.current().expect("unit w/out root debugging information entry"); 188 | 189 | let dwo_name = if let Some(val) = root.attr_value(gimli::DW_AT_dwo_name)? { 190 | // DWARF 5 191 | val 192 | } else if let Some(val) = root.attr_value(gimli::DW_AT_GNU_dwo_name)? { 193 | // GNU Extension 194 | val 195 | } else { 196 | return Err(Error::MissingDwoName(target.index())); 197 | }; 198 | 199 | dwarf.attr_string(&unit, dwo_name)?.to_string()?.into_owned() 200 | }; 201 | 202 | // Prepend the compilation directory if it exists. 203 | let mut path = if let Some(comp_dir) = &unit.comp_dir { 204 | PathBuf::from(comp_dir.to_string()?.into_owned()) 205 | } else { 206 | PathBuf::new() 207 | }; 208 | path.push(dwo_name); 209 | 210 | // Only add `DwoId`s to the targets, not `DebugTypeSignature`s. There doesn't 211 | // appear to be a "skeleton type unit" to find the corresponding unit of (there are 212 | // normal type units in an executable, but should we expect to find a corresponding 213 | // split type unit for those?). 214 | if matches!(target, DwarfObject::Compilation(_)) { 215 | // Input objects are processed first, if a DWARF object referenced by this 216 | // executable was already found then don't add it to the target and try to add it 217 | // again. 218 | if let Some(package) = &self.maybe_in_progress { 219 | if package.contained_units().contains(&target) { 220 | continue; 221 | } 222 | } 223 | 224 | debug!(?target, "adding target"); 225 | self.targets.insert(target); 226 | } 227 | 228 | match self.add_input_object(&path) { 229 | Ok(()) => (), 230 | Err(Error::ReadInput(..)) if missing_behaviour.skip_missing() => (), 231 | Err(e) => return Err(e), 232 | } 233 | } 234 | 235 | Ok(()) 236 | } 237 | 238 | /// Add an input object to the DWARF package. 239 | /// 240 | /// Input object must be an archive or an elf object. 241 | #[tracing::instrument(level = "trace")] 242 | pub fn add_input_object(&mut self, path: &Path) -> Result<()> { 243 | let data = self.sess.read_input(&path).map_err(Error::ReadInput)?; 244 | 245 | let kind = FileKind::parse(data).map_err(Error::ParseFileKind)?; 246 | trace!(?kind); 247 | match kind { 248 | FileKind::Archive => { 249 | let archive = object::read::archive::ArchiveFile::parse(data) 250 | .map_err(Error::ParseArchiveFile)?; 251 | 252 | for member in archive.members() { 253 | let member = member.map_err(Error::ParseArchiveMember)?; 254 | let data = member.data(data)?; 255 | 256 | let kind = if let Ok(kind) = FileKind::parse(data) { 257 | kind 258 | } else { 259 | trace!("skipping non-elf archive member"); 260 | continue; 261 | }; 262 | 263 | trace!(?kind, "archive member"); 264 | match kind { 265 | FileKind::Elf32 | FileKind::Elf64 => { 266 | let obj = object::File::parse(data).map_err(Error::ParseObjectFile)?; 267 | self.process_input_object(&obj)?; 268 | } 269 | _ => { 270 | trace!("skipping non-elf archive member"); 271 | } 272 | } 273 | } 274 | 275 | Ok(()) 276 | } 277 | FileKind::Elf32 | FileKind::Elf64 => { 278 | let obj = object::File::parse(data).map_err(Error::ParseObjectFile)?; 279 | self.process_input_object(&obj) 280 | } 281 | _ => Err(Error::InvalidInputKind), 282 | } 283 | } 284 | 285 | /// Returns the `object::write::Object` containing the created DWARF package. 286 | /// 287 | /// Returns an `Error::MissingReferencedUnit` if DWARF objects referenced by executables were 288 | /// not subsequently found. 289 | /// Returns an `Error::NoOutputObjectCreated` if no input objects or executables were provided. 290 | #[tracing::instrument(level = "trace")] 291 | pub fn finish(self) -> Result> { 292 | match self.maybe_in_progress { 293 | Some(package) => { 294 | if let Some(missing) = self.targets.difference(package.contained_units()).next() { 295 | return Err(Error::MissingReferencedUnit(missing.index())); 296 | } 297 | 298 | package.finish() 299 | } 300 | None if !self.targets.is_empty() => { 301 | let first_missing_unit = self 302 | .targets 303 | .iter() 304 | .next() 305 | .copied() 306 | .expect("non-empty map doesn't have first element"); 307 | Err(Error::MissingReferencedUnit(first_missing_unit.index())) 308 | } 309 | None => Err(Error::NoOutputObjectCreated), 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /thorin/src/package.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashSet, fmt}; 2 | 3 | use gimli::{Encoding, RunTimeEndian, UnitHeader, UnitIndex, UnitSectionOffset, UnitType}; 4 | use object::{ 5 | write::{Object as WritableObject, SectionId}, 6 | BinaryFormat, Object, ObjectSection, SectionKind, 7 | }; 8 | use tracing::debug; 9 | 10 | use crate::{ 11 | error::{Error, Result}, 12 | ext::{CompressedDataRangeExt, EndianityExt, IndexSectionExt, PackageFormatExt}, 13 | index::{write_index, Bucketable, Contribution, ContributionOffset, IndexEntry}, 14 | relocate::RelocationMap, 15 | strings::PackageStringTable, 16 | Session, 17 | }; 18 | 19 | /// New-type'd index (constructed from `gimli::DwoId`) with a custom `Debug` implementation to 20 | /// print in hexadecimal. 21 | #[derive(Copy, Clone, Eq, Hash, PartialEq)] 22 | pub(crate) struct DwoId(pub(crate) u64); 23 | 24 | impl Bucketable for DwoId { 25 | fn index(&self) -> u64 { 26 | self.0 27 | } 28 | } 29 | 30 | impl fmt::Debug for DwoId { 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | write!(f, "DwoId({:#x})", self.0) 33 | } 34 | } 35 | 36 | impl From for DwoId { 37 | fn from(dwo_id: gimli::DwoId) -> Self { 38 | Self(dwo_id.0) 39 | } 40 | } 41 | 42 | /// New-type'd index (constructed from `gimli::DebugTypeSignature`) with a custom `Debug` 43 | /// implementation to print in hexadecimal. 44 | #[derive(Copy, Clone, Eq, Hash, PartialEq)] 45 | pub(crate) struct DebugTypeSignature(pub(crate) u64); 46 | 47 | impl Bucketable for DebugTypeSignature { 48 | fn index(&self) -> u64 { 49 | self.0 50 | } 51 | } 52 | 53 | impl fmt::Debug for DebugTypeSignature { 54 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 55 | write!(f, "DebugTypeSignature({:#x})", self.0) 56 | } 57 | } 58 | 59 | impl From for DebugTypeSignature { 60 | fn from(signature: gimli::DebugTypeSignature) -> Self { 61 | Self(signature.0) 62 | } 63 | } 64 | 65 | /// Identifier for a DWARF object. 66 | #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] 67 | pub(crate) enum DwarfObject { 68 | /// `DwoId` identifying compilation units. 69 | Compilation(DwoId), 70 | /// `DebugTypeSignature` identifying type units. 71 | Type(DebugTypeSignature), 72 | } 73 | 74 | impl Bucketable for DwarfObject { 75 | fn index(&self) -> u64 { 76 | match *self { 77 | DwarfObject::Compilation(dwo_id) => dwo_id.index(), 78 | DwarfObject::Type(type_signature) => type_signature.index(), 79 | } 80 | } 81 | } 82 | 83 | /// Returns the `DwoId` or `DebugTypeSignature` of a unit. 84 | /// 85 | /// **DWARF 5:** 86 | /// 87 | /// - `DwoId` is in the unit header of a skeleton unit (identifying the split compilation unit 88 | /// that contains the debuginfo) or split compilation unit (identifying the skeleton unit that this 89 | /// debuginfo corresponds to). 90 | /// - `DebugTypeSignature` is in the unit header of a split type unit. 91 | /// 92 | /// **Earlier DWARF versions with GNU extension:** 93 | /// 94 | /// - `DW_AT_GNU_dwo_id` attribute of the DIE contains the `DwoId`. 95 | #[tracing::instrument(level = "trace", skip(debug_abbrev, header))] 96 | pub(crate) fn dwo_identifier_of_unit( 97 | debug_abbrev: &gimli::DebugAbbrev, 98 | header: &gimli::UnitHeader, 99 | ) -> Result> { 100 | match header.type_() { 101 | // Compilation units with DWARF 5 102 | UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => { 103 | Ok(Some(DwarfObject::Compilation(dwo_id.into()))) 104 | } 105 | // Compilation units with GNU Extension 106 | UnitType::Compilation => { 107 | let abbreviations = 108 | header.abbreviations(&debug_abbrev).map_err(Error::ParseUnitAbbreviations)?; 109 | let mut cursor = header.entries(&abbreviations); 110 | cursor.next_dfs()?; 111 | let root = cursor.current().ok_or(Error::NoDie)?; 112 | match root.tag() { 113 | gimli::DW_TAG_compile_unit | gimli::DW_TAG_type_unit => (), 114 | _ => return Err(Error::TopLevelDieNotUnit), 115 | } 116 | let mut attrs = root.attrs(); 117 | while let Some(attr) = attrs.next().map_err(Error::ParseUnitAttribute)? { 118 | match (attr.name(), attr.value()) { 119 | (gimli::constants::DW_AT_GNU_dwo_id, gimli::AttributeValue::DwoId(dwo_id)) => { 120 | return Ok(Some(DwarfObject::Compilation(dwo_id.into()))) 121 | } 122 | _ => (), 123 | } 124 | } 125 | 126 | Ok(None) 127 | } 128 | // Type units with DWARF 5 129 | UnitType::SplitType { type_signature, .. } => { 130 | Ok(Some(DwarfObject::Type(type_signature.into()))) 131 | } 132 | // Type units with GNU extension 133 | UnitType::Type { type_signature, .. } => Ok(Some(DwarfObject::Type(type_signature.into()))), 134 | // Wrong compilation unit type. 135 | _ => Ok(None), 136 | } 137 | } 138 | 139 | /// Wrapper around `.debug_info.dwo` and `debug_types.dwo` unit iterators for uniform handling. 140 | enum UnitHeaderIterator { 141 | DebugInfo(gimli::read::DebugInfoUnitHeadersIter), 142 | DebugTypes(gimli::read::DebugTypesUnitHeadersIter), 143 | } 144 | 145 | impl UnitHeaderIterator { 146 | fn next(&mut self) -> gimli::read::Result>> { 147 | match self { 148 | UnitHeaderIterator::DebugInfo(iter) => iter.next(), 149 | UnitHeaderIterator::DebugTypes(iter) => iter.next(), 150 | } 151 | } 152 | } 153 | 154 | /// Returns the parsed unit index from a `.debug_{cu,tu}_index` section. 155 | pub(crate) fn maybe_load_index_section<'input, 'session: 'input, Endian, Index, R, Sess>( 156 | sess: &'session Sess, 157 | encoding: Encoding, 158 | endian: Endian, 159 | input: &object::File<'input>, 160 | ) -> Result>> 161 | where 162 | Endian: gimli::Endianity, 163 | Index: IndexSectionExt<'input, Endian, R>, 164 | R: gimli::Reader, 165 | Sess: Session, 166 | { 167 | let index_name = Index::id().dwo_name().expect("index id w/out known value"); 168 | if let Some(index_section) = input.section_by_name(index_name) { 169 | let index_data = index_section 170 | .compressed_data() 171 | .and_then(|d| d.decompress()) 172 | .map_err(Error::DecompressData)?; 173 | let index_data_ref = sess.alloc_owned_cow(index_data); 174 | let unit_index = Index::new(index_data_ref, endian) 175 | .index() 176 | .map_err(|e| Error::ParseIndex(e, index_name.to_string()))?; 177 | 178 | if !encoding.is_compatible_dwarf_package_index_version(unit_index.version()) { 179 | return Err(Error::IncompatibleIndexVersion( 180 | index_name.to_string(), 181 | encoding.dwarf_package_index_version(), 182 | unit_index.version(), 183 | )); 184 | } 185 | 186 | Ok(Some(unit_index)) 187 | } else { 188 | Ok(None) 189 | } 190 | } 191 | 192 | /// Returns a closure which takes an identifier and a `Option`, and returns an 193 | /// adjusted contribution if the input file is a DWARF package (and the contribution was 194 | /// present). 195 | /// 196 | /// For example, consider the `.debug_str_offsets` section: DWARF packages have a single 197 | /// `.debug_str_offsets` section which contains the string offsets of all of its compilation/type 198 | /// units, the contributions of each unit into that section are tracked in its 199 | /// `.debug_{cu,tu}_index` section. 200 | /// 201 | /// When a DWARF package is the input, the contributions of the units which constituted that 202 | /// package should not be lost when its `.debug_str_offsets` section is merged with the new 203 | /// DWARF package currently being created. 204 | /// 205 | /// Given a parsed index section, use the size of its contribution to `.debug_str_offsets` as the 206 | /// size of its contribution in the new unit (without this, it would be the size of the entire 207 | /// `.debug_str_offsets` section from the input, rather than the part that the compilation unit 208 | /// originally contributed to that). For subsequent units from the input, the offset in the 209 | /// contribution will need to be adjusted to based on the size of the previous units. 210 | /// 211 | /// This function returns a "contribution adjustor" closure, which adjusts the contribution's 212 | /// offset and size according to its contribution in the input's index and with an offset 213 | /// accumulated over all calls to the closure. 214 | pub(crate) fn create_contribution_adjustor<'input, R: 'input>( 215 | cu_index: Option<&'input UnitIndex>, 216 | tu_index: Option<&'input UnitIndex>, 217 | target_section_id: gimli::IndexSectionId, 218 | ) -> impl FnMut(DwarfObject, Option) -> Result> + 'input 219 | where 220 | R: gimli::Reader, 221 | { 222 | let mut cu_adjustment = 0; 223 | let mut tu_adjustment = 0; 224 | 225 | move |identifier: DwarfObject, 226 | contribution: Option| 227 | -> Result> { 228 | let (adjustment, index) = match identifier { 229 | DwarfObject::Compilation(_) => (&mut cu_adjustment, &cu_index), 230 | DwarfObject::Type(_) => (&mut tu_adjustment, &tu_index), 231 | }; 232 | match (index, contribution) { 233 | // dwp input with section 234 | (Some(index), Some(contribution)) => { 235 | let idx = identifier.index(); 236 | let row_id = index.find(idx).ok_or(Error::UnitNotInIndex(idx))?; 237 | let section = index 238 | .sections(row_id) 239 | .map_err(|e| Error::RowNotInIndex(e, row_id))? 240 | .find(|index_section| index_section.section == target_section_id) 241 | .ok_or(Error::SectionNotInRow)?; 242 | let adjusted_offset: u64 = contribution.offset.0 + *adjustment; 243 | *adjustment += section.size as u64; 244 | 245 | Ok(Some(Contribution { 246 | offset: ContributionOffset(adjusted_offset), 247 | size: section.size as u64, 248 | })) 249 | } 250 | // dwp input without section 251 | (Some(_) | None, None) => Ok(contribution), 252 | // dwo input with section, but we aren't adjusting this particular index 253 | (None, Some(_)) => Ok(contribution), 254 | } 255 | } 256 | } 257 | 258 | /// Wrapper around `object::write::Object` that keeps track of the section indexes relevant to 259 | /// DWARF packaging. 260 | struct DwarfPackageObject<'file> { 261 | /// Object file being created. 262 | obj: WritableObject<'file>, 263 | 264 | /// Identifier for output `.debug_cu_index.dwo` section. 265 | debug_cu_index: Option, 266 | /// `.debug_tu_index.dwo` 267 | debug_tu_index: Option, 268 | /// `.debug_info.dwo` 269 | debug_info: Option, 270 | /// `.debug_abbrev.dwo` 271 | debug_abbrev: Option, 272 | /// `.debug_str.dwo` 273 | debug_str: Option, 274 | /// `.debug_types.dwo` 275 | debug_types: Option, 276 | /// `.debug_line.dwo` 277 | debug_line: Option, 278 | /// `.debug_loc.dwo` 279 | debug_loc: Option, 280 | /// `.debug_loclists.dwo` 281 | debug_loclists: Option, 282 | /// `.debug_rnglists.dwo` 283 | debug_rnglists: Option, 284 | /// `.debug_str_offsets.dwo` 285 | debug_str_offsets: Option, 286 | /// `.debug_macinfo.dwo` 287 | debug_macinfo: Option, 288 | /// `.debug_macro.dwo` 289 | debug_macro: Option, 290 | } 291 | 292 | /// Macro for generating helper functions which appending non-empty data to specific sections. 293 | macro_rules! generate_append_for { 294 | ( $( $fn_name:ident => ($name:ident, $section_name:expr) ),+ ) => { 295 | $( 296 | fn $fn_name(&mut self, data: &[u8]) -> Option { 297 | if data.is_empty() { 298 | return None; 299 | } 300 | 301 | let id = *self.$name.get_or_insert_with(|| self.obj.add_section( 302 | Vec::new(), 303 | Vec::from($section_name), 304 | SectionKind::Debug, 305 | )); 306 | 307 | // FIXME: correct alignment 308 | let offset = self.obj.append_section_data(id, data, 1); 309 | debug!(?offset, ?data); 310 | Some(Contribution { 311 | offset: ContributionOffset(offset), 312 | size: data.len().try_into().expect("data size larger than u64"), 313 | }) 314 | } 315 | )+ 316 | }; 317 | } 318 | 319 | impl<'file> DwarfPackageObject<'file> { 320 | /// Create a new `DwarfPackageObject` from an architecture and endianness. 321 | #[tracing::instrument(level = "trace")] 322 | pub(crate) fn new( 323 | architecture: object::Architecture, 324 | endianness: object::Endianness, 325 | ) -> DwarfPackageObject<'file> { 326 | let obj = WritableObject::new(BinaryFormat::Elf, architecture, endianness); 327 | Self { 328 | obj, 329 | debug_cu_index: Default::default(), 330 | debug_tu_index: Default::default(), 331 | debug_info: Default::default(), 332 | debug_abbrev: Default::default(), 333 | debug_str: Default::default(), 334 | debug_types: Default::default(), 335 | debug_line: Default::default(), 336 | debug_loc: Default::default(), 337 | debug_loclists: Default::default(), 338 | debug_rnglists: Default::default(), 339 | debug_str_offsets: Default::default(), 340 | debug_macinfo: Default::default(), 341 | debug_macro: Default::default(), 342 | } 343 | } 344 | 345 | generate_append_for! { 346 | append_to_debug_abbrev => (debug_abbrev, ".debug_abbrev.dwo"), 347 | append_to_debug_cu_index => (debug_cu_index, ".debug_cu_index"), 348 | append_to_debug_info => (debug_info, ".debug_info.dwo"), 349 | append_to_debug_line => (debug_line, ".debug_line.dwo"), 350 | append_to_debug_loc => (debug_loc, ".debug_loc.dwo"), 351 | append_to_debug_loclists => (debug_loclists, ".debug_loclists.dwo"), 352 | append_to_debug_macinfo => (debug_macinfo, ".debug_macinfo.dwo"), 353 | append_to_debug_macro => (debug_macro, ".debug_macro.dwo"), 354 | append_to_debug_rnglists => (debug_rnglists, ".debug_rnglists.dwo"), 355 | append_to_debug_str => (debug_str, ".debug_str.dwo"), 356 | append_to_debug_str_offsets => (debug_str_offsets, ".debug_str_offsets.dwo"), 357 | append_to_debug_tu_index => (debug_tu_index, ".debug_tu_index"), 358 | append_to_debug_types => (debug_types, ".debug_types.dwo") 359 | } 360 | 361 | /// Return the DWARF package object file. 362 | pub(crate) fn finish(self) -> WritableObject<'file> { 363 | self.obj 364 | } 365 | } 366 | 367 | /// In-progress DWARF package being produced. 368 | pub(crate) struct InProgressDwarfPackage<'file> { 369 | /// Endianness of the DWARF package being created. 370 | endian: RunTimeEndian, 371 | 372 | /// Object file being created. 373 | obj: DwarfPackageObject<'file>, 374 | /// In-progress string table being accumulated. 375 | /// 376 | /// Used to write final `.debug_str.dwo` and `.debug_str_offsets.dwo`. 377 | string_table: PackageStringTable, 378 | 379 | /// Compilation unit index entries (offsets + sizes) being accumulated. 380 | cu_index_entries: Vec, 381 | /// Type unit index entries (offsets + sizes) being accumulated. 382 | tu_index_entries: Vec, 383 | 384 | /// `DebugTypeSignature`s of type units and `DwoId`s of compilation units that have already 385 | /// been added to the output package. 386 | /// 387 | /// Used when adding new TU index entries to de-duplicate type units (as required by the 388 | /// specification). Also used to check that all dwarf objects referenced by executables 389 | /// have been found. 390 | contained_units: HashSet, 391 | } 392 | 393 | impl<'file> InProgressDwarfPackage<'file> { 394 | /// Create an object file with empty sections that will be later populated from DWARF object 395 | /// files. 396 | #[tracing::instrument(level = "trace")] 397 | pub(crate) fn new( 398 | architecture: object::Architecture, 399 | endianness: object::Endianness, 400 | ) -> InProgressDwarfPackage<'file> { 401 | let endian = endianness.as_runtime_endian(); 402 | Self { 403 | endian, 404 | obj: DwarfPackageObject::new(architecture, endianness), 405 | string_table: PackageStringTable::new(), 406 | cu_index_entries: Default::default(), 407 | tu_index_entries: Default::default(), 408 | contained_units: Default::default(), 409 | } 410 | } 411 | 412 | /// Returns the units contained within the DWARF package. 413 | pub(crate) fn contained_units(&self) -> &HashSet { 414 | &self.contained_units 415 | } 416 | 417 | /// Process an input DWARF object. 418 | /// 419 | /// Copies relevant debug sections, compilation/type units and strings from the `input` DWARF 420 | /// object into this DWARF package. 421 | #[tracing::instrument(level = "trace", skip(sess, input,))] 422 | pub(crate) fn add_input_object<'input, 'session: 'input>( 423 | &mut self, 424 | sess: &'session impl Session, 425 | input: &object::File<'input>, 426 | encoding: Encoding, 427 | ) -> Result<()> { 428 | // Load index sections (if they exist). 429 | let cu_index = maybe_load_index_section::<_, gimli::DebugCuIndex<_>, _, _>( 430 | sess, 431 | encoding, 432 | self.endian, 433 | input, 434 | )?; 435 | let tu_index = maybe_load_index_section::<_, gimli::DebugTuIndex<_>, _, _>( 436 | sess, 437 | encoding, 438 | self.endian, 439 | input, 440 | )?; 441 | 442 | let mut debug_abbrev = None; 443 | let mut debug_line = None; 444 | let mut debug_loc = None; 445 | let mut debug_loclists = None; 446 | let mut debug_macinfo = None; 447 | let mut debug_macro = None; 448 | let mut debug_rnglists = None; 449 | let mut debug_str_offsets = None; 450 | 451 | macro_rules! update { 452 | ($target:ident += $source:expr) => { 453 | if let Some(other) = $source { 454 | let contribution = $target.get_or_insert(Contribution { size: 0, ..other }); 455 | contribution.size += other.size; 456 | } 457 | debug!(?$target); 458 | }; 459 | } 460 | 461 | // Iterate over sections rather than using `section_by_name` because sections can be 462 | // repeated. 463 | for section in input.sections() { 464 | match section.name() { 465 | Ok(".debug_abbrev.dwo" | ".zdebug_abbrev.dwo") => { 466 | let data = section.compressed_data()?.decompress()?; 467 | update!(debug_abbrev += self.obj.append_to_debug_abbrev(&data)); 468 | } 469 | Ok(".debug_line.dwo" | ".zdebug_line.dwo") => { 470 | let data = section.compressed_data()?.decompress()?; 471 | update!(debug_line += self.obj.append_to_debug_line(&data)); 472 | } 473 | Ok(".debug_loc.dwo" | ".zdebug_loc.dwo") => { 474 | let data = section.compressed_data()?.decompress()?; 475 | update!(debug_loc += self.obj.append_to_debug_loc(&data)); 476 | } 477 | Ok(".debug_loclists.dwo" | ".zdebug_loclists.dwo") => { 478 | let data = section.compressed_data()?.decompress()?; 479 | update!(debug_loclists += self.obj.append_to_debug_loclists(&data)); 480 | } 481 | Ok(".debug_macinfo.dwo" | ".zdebug_macinfo.dwo") => { 482 | let data = section.compressed_data()?.decompress()?; 483 | update!(debug_macinfo += self.obj.append_to_debug_macinfo(&data)); 484 | } 485 | Ok(".debug_macro.dwo" | ".zdebug_macro.dwo") => { 486 | let data = section.compressed_data()?.decompress()?; 487 | update!(debug_macro += self.obj.append_to_debug_macro(&data)); 488 | } 489 | Ok(".debug_rnglists.dwo" | ".zdebug_rnglists.dwo") => { 490 | let data = section.compressed_data()?.decompress()?; 491 | update!(debug_rnglists += self.obj.append_to_debug_rnglists(&data)); 492 | } 493 | Ok(".debug_str_offsets.dwo" | ".zdebug_str_offsets.dwo") => { 494 | let (debug_str_offsets_section, debug_str_offsets_section_len) = { 495 | let data = section.compressed_data()?.decompress()?; 496 | let len = data.len() as u64; 497 | let data_ref = sess.alloc_owned_cow(data); 498 | ( 499 | gimli::DebugStrOffsets::from(gimli::EndianSlice::new( 500 | data_ref, 501 | self.endian, 502 | )), 503 | len, 504 | ) 505 | }; 506 | 507 | let debug_str_section = 508 | if let Some(section) = input.section_by_name(".debug_str.dwo") { 509 | let data = section.compressed_data()?.decompress()?; 510 | let data_ref = sess.alloc_owned_cow(data); 511 | gimli::DebugStr::new(data_ref, self.endian) 512 | } else { 513 | return Err(Error::MissingRequiredSection(".debug_str.dwo")); 514 | }; 515 | 516 | let data = self.string_table.remap_str_offsets_section( 517 | debug_str_section, 518 | debug_str_offsets_section, 519 | debug_str_offsets_section_len, 520 | self.endian, 521 | encoding, 522 | )?; 523 | update!( 524 | debug_str_offsets += self.obj.append_to_debug_str_offsets(data.slice()) 525 | ); 526 | } 527 | _ => (), 528 | } 529 | } 530 | 531 | // `.debug_abbrev.dwo`'s contribution will already have been processed, but getting the 532 | // `DwoId` of a GNU Extension compilation unit requires access to it. 533 | let debug_abbrev_section = if let Some(section) = input.section_by_name(".debug_abbrev.dwo") 534 | { 535 | let data = section.compressed_data()?.decompress()?; 536 | let data_ref = sess.alloc_owned_cow(data); 537 | gimli::DebugAbbrev::new(data_ref, self.endian) 538 | } else { 539 | return Err(Error::MissingRequiredSection(".debug_abbrev.dwo")); 540 | }; 541 | 542 | // Create offset adjustor functions, see comment on `create_contribution_adjustor` for 543 | // explanation. 544 | let adjustor_for_index = 545 | |id| create_contribution_adjustor(cu_index.as_ref(), tu_index.as_ref(), id); 546 | let mut abbrev_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugAbbrev); 547 | let mut line_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugLine); 548 | let mut loc_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugLoc); 549 | let mut loclists_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugLocLists); 550 | let mut rnglists_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugRngLists); 551 | let mut str_offsets_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugStrOffsets); 552 | let mut macinfo_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugMacinfo); 553 | let mut macro_adjustor = adjustor_for_index(gimli::IndexSectionId::DebugMacro); 554 | 555 | let mut seen_debug_info = false; 556 | let mut seen_debug_types = false; 557 | 558 | for section in input.sections() { 559 | let data; 560 | let mut iter = match section.name() { 561 | Ok(".debug_info.dwo" | ".zdebug_info.dwo") 562 | // Report an error if a input DWARF package has multiple `.debug_info` 563 | // sections. 564 | if seen_debug_info && cu_index.is_some() => 565 | { 566 | return Err(Error::MultipleDebugInfoSection); 567 | } 568 | Ok(".debug_info.dwo" | ".zdebug_info.dwo") => { 569 | data = section.compressed_data()?.decompress()?; 570 | seen_debug_info = true; 571 | UnitHeaderIterator::DebugInfo( 572 | gimli::DebugInfo::new(&data, self.endian).units(), 573 | ) 574 | } 575 | Ok(".debug_types.dwo" | ".zdebug_types.dwo") 576 | // Report an error if a input DWARF package has multiple `.debug_types` 577 | // sections. 578 | if seen_debug_types && tu_index.is_some() => 579 | { 580 | return Err(Error::MultipleDebugTypesSection); 581 | } 582 | Ok(".debug_types.dwo" | ".zdebug_types.dwo") => { 583 | data = section.compressed_data()?.decompress()?; 584 | seen_debug_types = true; 585 | UnitHeaderIterator::DebugTypes( 586 | gimli::DebugTypes::new(&data, self.endian).units(), 587 | ) 588 | } 589 | _ => continue, 590 | }; 591 | 592 | while let Some(header) = iter.next().map_err(Error::ParseUnitHeader)? { 593 | let id = match dwo_identifier_of_unit(&debug_abbrev_section, &header)? { 594 | // Report an error if the unit doesn't have a `DwoId` or `DebugTypeSignature`. 595 | None => { 596 | return Err(Error::NotSplitUnit); 597 | } 598 | // Report an error when a duplicate compilation unit is found. 599 | Some(id @ DwarfObject::Compilation(dwo_id)) 600 | if self.contained_units.contains(&id) => 601 | { 602 | return Err(Error::DuplicateUnit(dwo_id.0)); 603 | } 604 | // Skip duplicate type units, these happen during proper operation of `thorin`. 605 | Some(id @ DwarfObject::Type(type_sig)) 606 | if self.contained_units.contains(&id) => 607 | { 608 | debug!(?type_sig, "skipping duplicate type unit, already seen"); 609 | continue; 610 | } 611 | Some(id) => id, 612 | }; 613 | 614 | let size: u64 = header 615 | .length_including_self() 616 | .try_into() 617 | .expect("unit header length larger than u64"); 618 | let offset = match header.offset() { 619 | UnitSectionOffset::DebugInfoOffset(offset) => offset.0, 620 | UnitSectionOffset::DebugTypesOffset(offset) => offset.0, 621 | }; 622 | 623 | let data = section 624 | .compressed_data_range( 625 | sess, 626 | offset.try_into().expect("offset larger than u64"), 627 | size, 628 | ) 629 | .map_err(Error::DecompressData)? 630 | .ok_or(Error::EmptyUnit(id.index()))?; 631 | 632 | let (debug_info, debug_types) = match (&iter, id) { 633 | (UnitHeaderIterator::DebugTypes(_), DwarfObject::Type(_)) => { 634 | (None, self.obj.append_to_debug_types(data)) 635 | } 636 | (_, DwarfObject::Compilation(_) | DwarfObject::Type(_)) => { 637 | (self.obj.append_to_debug_info(data), None) 638 | } 639 | }; 640 | 641 | let debug_abbrev = abbrev_adjustor(id, debug_abbrev)?; 642 | let debug_line = line_adjustor(id, debug_line)?; 643 | let debug_loc = loc_adjustor(id, debug_loc)?; 644 | let debug_loclists = loclists_adjustor(id, debug_loclists)?; 645 | let debug_rnglists = rnglists_adjustor(id, debug_rnglists)?; 646 | let debug_str_offsets = str_offsets_adjustor(id, debug_str_offsets)?; 647 | let debug_macinfo = macinfo_adjustor(id, debug_macinfo)?; 648 | let debug_macro = macro_adjustor(id, debug_macro)?; 649 | 650 | let entry = IndexEntry { 651 | encoding, 652 | id, 653 | debug_info, 654 | debug_types, 655 | debug_abbrev, 656 | debug_line, 657 | debug_loc, 658 | debug_loclists, 659 | debug_rnglists, 660 | debug_str_offsets, 661 | debug_macinfo, 662 | debug_macro, 663 | }; 664 | debug!(?entry); 665 | 666 | match id { 667 | DwarfObject::Compilation(_) => self.cu_index_entries.push(entry), 668 | DwarfObject::Type(_) => self.tu_index_entries.push(entry), 669 | } 670 | self.contained_units.insert(id); 671 | } 672 | } 673 | 674 | if !seen_debug_info { 675 | // Report an error if no `.debug_info` section was found. 676 | return Err(Error::MissingRequiredSection(".debug_info.dwo")); 677 | } 678 | 679 | Ok(()) 680 | } 681 | 682 | /// Return the DWARF package object being created, writing any final sections. 683 | pub(crate) fn finish(self) -> Result> { 684 | let Self { mut obj, string_table, cu_index_entries, tu_index_entries, .. } = self; 685 | 686 | // Write `.debug_str` to the object. 687 | let _ = obj.append_to_debug_str(&string_table.finish()); 688 | 689 | // Write `.debug_{cu,tu}_index` sections to the object. 690 | debug!("writing cu index"); 691 | let cu_index_data = write_index(self.endian, &cu_index_entries)?; 692 | let _ = obj.append_to_debug_cu_index(cu_index_data.slice()); 693 | debug!("writing tu index"); 694 | let tu_index_data = write_index(self.endian, &tu_index_entries)?; 695 | let _ = obj.append_to_debug_tu_index(tu_index_data.slice()); 696 | 697 | Ok(obj.finish()) 698 | } 699 | } 700 | 701 | impl<'file> fmt::Debug for InProgressDwarfPackage<'file> { 702 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 703 | write!(f, "InProgressDwarfPackage") 704 | } 705 | } 706 | -------------------------------------------------------------------------------- /thorin/src/relocate.rs: -------------------------------------------------------------------------------- 1 | //! Apply relocations to addresses and offsets during parsing, instead of requiring the data 2 | //! to be fully relocated prior to parsing. Necessary to load object files that reference dwarf 3 | //! objects (not just executables). Implementation derived from Gimli's `dwarfdump` example. 4 | 5 | use std::borrow::Cow; 6 | 7 | use gimli; 8 | use hashbrown::HashMap; 9 | use object::{Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget}; 10 | 11 | use crate::{Error, Result}; 12 | 13 | pub(crate) type RelocationMap = HashMap; 14 | 15 | #[derive(Debug, Clone)] 16 | pub(crate) struct Relocate<'a, R: gimli::Reader> { 17 | pub(crate) relocations: &'a RelocationMap, 18 | pub(crate) section: R, 19 | pub(crate) reader: R, 20 | } 21 | 22 | impl<'a, R: gimli::Reader> Relocate<'a, R> { 23 | fn relocate(&self, offset: usize, value: u64) -> u64 { 24 | if let Some(relocation) = self.relocations.get(&offset) { 25 | if matches!(relocation.kind(), RelocationKind::Absolute) { 26 | if relocation.has_implicit_addend() { 27 | // Use the explicit addend too, because it may have the symbol value. 28 | return value.wrapping_add(relocation.addend() as u64); 29 | } else { 30 | return relocation.addend() as u64; 31 | } 32 | } 33 | }; 34 | 35 | value 36 | } 37 | } 38 | 39 | impl<'a, R: gimli::Reader> gimli::Reader for Relocate<'a, R> { 40 | type Endian = R::Endian; 41 | type Offset = R::Offset; 42 | 43 | fn read_address(&mut self, address_size: u8) -> gimli::Result { 44 | let offset = self.reader.offset_from(&self.section); 45 | let value = self.reader.read_address(address_size)?; 46 | Ok(self.relocate(offset, value)) 47 | } 48 | 49 | fn read_length(&mut self, format: gimli::Format) -> gimli::Result { 50 | let offset = self.reader.offset_from(&self.section); 51 | let value = self.reader.read_length(format)?; 52 | ::from_u64(self.relocate(offset, value as u64)) 53 | } 54 | 55 | fn read_offset(&mut self, format: gimli::Format) -> gimli::Result { 56 | let offset = self.reader.offset_from(&self.section); 57 | let value = self.reader.read_offset(format)?; 58 | ::from_u64(self.relocate(offset, value as u64)) 59 | } 60 | 61 | fn read_sized_offset(&mut self, size: u8) -> gimli::Result { 62 | let offset = self.reader.offset_from(&self.section); 63 | let value = self.reader.read_sized_offset(size)?; 64 | ::from_u64(self.relocate(offset, value as u64)) 65 | } 66 | 67 | #[inline] 68 | fn split(&mut self, len: Self::Offset) -> gimli::Result { 69 | let mut other = self.clone(); 70 | other.reader.truncate(len)?; 71 | self.reader.skip(len)?; 72 | Ok(other) 73 | } 74 | 75 | // All remaining methods simply delegate to `self.reader`. 76 | 77 | #[inline] 78 | fn endian(&self) -> Self::Endian { 79 | self.reader.endian() 80 | } 81 | 82 | #[inline] 83 | fn len(&self) -> Self::Offset { 84 | self.reader.len() 85 | } 86 | 87 | #[inline] 88 | fn empty(&mut self) { 89 | self.reader.empty() 90 | } 91 | 92 | #[inline] 93 | fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> { 94 | self.reader.truncate(len) 95 | } 96 | 97 | #[inline] 98 | fn offset_from(&self, base: &Self) -> Self::Offset { 99 | self.reader.offset_from(&base.reader) 100 | } 101 | 102 | #[inline] 103 | fn offset_id(&self) -> gimli::ReaderOffsetId { 104 | self.reader.offset_id() 105 | } 106 | 107 | #[inline] 108 | fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option { 109 | self.reader.lookup_offset_id(id) 110 | } 111 | 112 | #[inline] 113 | fn find(&self, byte: u8) -> gimli::Result { 114 | self.reader.find(byte) 115 | } 116 | 117 | #[inline] 118 | fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> { 119 | self.reader.skip(len) 120 | } 121 | 122 | #[inline] 123 | fn to_slice(&self) -> gimli::Result> { 124 | self.reader.to_slice() 125 | } 126 | 127 | #[inline] 128 | fn to_string(&self) -> gimli::Result> { 129 | self.reader.to_string() 130 | } 131 | 132 | #[inline] 133 | fn to_string_lossy(&self) -> gimli::Result> { 134 | self.reader.to_string_lossy() 135 | } 136 | 137 | #[inline] 138 | fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> { 139 | self.reader.read_slice(buf) 140 | } 141 | } 142 | 143 | pub(crate) fn add_relocations( 144 | relocations: &mut RelocationMap, 145 | file: &object::File<'_>, 146 | section: &object::Section<'_, '_>, 147 | ) -> Result<()> { 148 | for (offset64, mut relocation) in section.relocations() { 149 | let offset = offset64 as usize; 150 | if offset as u64 != offset64 { 151 | continue; 152 | } 153 | 154 | let offset = offset as usize; 155 | if matches!(relocation.kind(), RelocationKind::Absolute) { 156 | if let RelocationTarget::Symbol(symbol_idx) = relocation.target() { 157 | match file.symbol_by_index(symbol_idx) { 158 | Ok(symbol) => { 159 | let addend = symbol.address().wrapping_add(relocation.addend() as u64); 160 | relocation.set_addend(addend as i64); 161 | } 162 | Err(_) => { 163 | return Err(Error::RelocationWithInvalidSymbol( 164 | section 165 | .name() 166 | .map_err(|e| Error::NamelessSection(e, offset))? 167 | .to_string(), 168 | offset, 169 | )); 170 | } 171 | } 172 | } 173 | 174 | if relocations.insert(offset, relocation).is_some() { 175 | return Err(Error::MultipleRelocations( 176 | section.name().map_err(|e| Error::NamelessSection(e, offset))?.to_string(), 177 | offset, 178 | )); 179 | } 180 | } else { 181 | return Err(Error::UnsupportedRelocation( 182 | section.name().map_err(|e| Error::NamelessSection(e, offset))?.to_string(), 183 | offset, 184 | )); 185 | } 186 | } 187 | 188 | Ok(()) 189 | } 190 | -------------------------------------------------------------------------------- /thorin/src/strings.rs: -------------------------------------------------------------------------------- 1 | use gimli::{ 2 | write::{EndianVec, Writer}, 3 | DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, Encoding, EndianSlice, Format, 4 | Section, 5 | }; 6 | use hashbrown::HashMap; 7 | use tracing::debug; 8 | 9 | use crate::{ 10 | error::{Error, Result}, 11 | ext::PackageFormatExt, 12 | }; 13 | 14 | /// New-type'd offset into `.debug_str` section. 15 | #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] 16 | pub(crate) struct PackageStringOffset(usize); 17 | 18 | /// DWARF packages need to merge the `.debug_str` sections of input DWARF objects. 19 | /// `.debug_str_offsets` sections then need to be rebuilt with offsets into the new merged 20 | /// `.debug_str` section and then concatenated (indices into each dwarf object's offset list will 21 | /// therefore still refer to the same string). 22 | /// 23 | /// Gimli's `StringTable` produces a `.debug_str` section with a single `.debug_str_offsets` 24 | /// section, but `PackageStringTable` accumulates a single `.debug_str` section and can be used to 25 | /// produce multiple `.debug_str_offsets` sections (which will be concatenated) which all offset 26 | /// into the same `.debug_str`. 27 | pub(crate) struct PackageStringTable { 28 | data: Vec, 29 | strings: HashMap, PackageStringOffset>, 30 | } 31 | 32 | impl PackageStringTable { 33 | /// Create a new `PackageStringTable` with a given endianity. 34 | pub(crate) fn new() -> Self { 35 | Self { data: Vec::new(), strings: HashMap::new() } 36 | } 37 | 38 | /// Insert a string into the string table and return its offset in the table. If the string is 39 | /// already in the table, returns its offset. 40 | pub(crate) fn get_or_insert(&mut self, bytes: &[u8]) -> PackageStringOffset { 41 | debug_assert!(!bytes.contains(&0)); 42 | if let Some(offset) = self.strings.get(bytes) { 43 | return *offset; 44 | } 45 | 46 | // Keep track of the offset for this string, it might be referenced by the next compilation 47 | // unit too. 48 | let offset = PackageStringOffset(self.data.len()); 49 | self.strings.insert(bytes.into(), offset); 50 | 51 | // Insert into the string table. 52 | self.data.extend_from_slice(bytes); 53 | self.data.push(0); 54 | 55 | offset 56 | } 57 | 58 | /// Adds strings from input `.debug_str_offsets` and `.debug_str` into the string table, returns 59 | /// data for a equivalent `.debug_str_offsets` section with offsets pointing into the new 60 | /// `.debug_str` section. 61 | pub(crate) fn remap_str_offsets_section( 62 | &mut self, 63 | debug_str: gimli::DebugStr>, 64 | debug_str_offsets: gimli::DebugStrOffsets>, 65 | section_size: u64, 66 | endian: E, 67 | encoding: Encoding, 68 | ) -> Result> { 69 | let entry_size = match encoding.format { 70 | Format::Dwarf32 => 4, 71 | Format::Dwarf64 => 8, 72 | }; 73 | 74 | // Reduce the number of allocations needed. 75 | self.data.reserve(debug_str.reader().len()); 76 | 77 | let mut data = EndianVec::new(endian); 78 | 79 | // `DebugStrOffsetsBase` knows to skip past the header with DWARF 5. 80 | let base: gimli::DebugStrOffsetsBase = 81 | DebugStrOffsetsBase::default_for_encoding_and_file(encoding, DwarfFileType::Dwo); 82 | 83 | if encoding.is_std_dwarf_package_format() { 84 | match encoding.format { 85 | Format::Dwarf32 => { 86 | // Unit length (4 bytes): size of the offsets section without this 87 | // header (8 bytes total). 88 | data.write_u32( 89 | (section_size - 8) 90 | .try_into() 91 | .expect("section size w/out header larger than u32"), 92 | )?; 93 | } 94 | Format::Dwarf64 => { 95 | // Unit length (4 bytes then 8 bytes): size of the offsets section without 96 | // this header (16 bytes total). 97 | data.write_u32(u32::MAX)?; 98 | data.write_u64(section_size - 16)?; 99 | } 100 | }; 101 | // Version (2 bytes): DWARF 5 102 | data.write_u16(5)?; 103 | // Reserved padding (2 bytes) 104 | data.write_u16(0)?; 105 | } 106 | debug!(?base); 107 | 108 | let base_offset: u64 = base.0.try_into().expect("base offset larger than u64"); 109 | let num_elements = (section_size - base_offset) / entry_size; 110 | debug!(?section_size, ?base_offset, ?num_elements); 111 | 112 | for i in 0..num_elements { 113 | let dwo_index = DebugStrOffsetsIndex(i as usize); 114 | let dwo_offset = debug_str_offsets 115 | .get_str_offset(encoding.format, base, dwo_index) 116 | .map_err(|e| Error::OffsetAtIndex(e, i))?; 117 | let dwo_str = 118 | debug_str.get_str(dwo_offset).map_err(|e| Error::StrAtOffset(e, dwo_offset.0))?; 119 | 120 | let dwp_offset = self.get_or_insert(&dwo_str); 121 | 122 | match encoding.format { 123 | Format::Dwarf32 => { 124 | let dwp_offset = 125 | dwp_offset.0.try_into().expect("string offset larger than u32"); 126 | data.write_u32(dwp_offset)?; 127 | } 128 | Format::Dwarf64 => { 129 | let dwp_offset = 130 | dwp_offset.0.try_into().expect("string offset larger than u64"); 131 | data.write_u64(dwp_offset)?; 132 | } 133 | } 134 | } 135 | 136 | Ok(data) 137 | } 138 | 139 | /// Returns the accumulated `.debug_str` section data 140 | pub(crate) fn finish(self) -> Vec { 141 | self.data 142 | } 143 | } 144 | --------------------------------------------------------------------------------