Merge pull request #10 from nnethercote/mac-support

Add Mac support
This commit is contained in:
Nicholas Nethercote 2019-12-06 11:27:24 +11:00 коммит произвёл GitHub
Родитель 5f60163542 56bf73b44e
Коммит c65a2029b9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 633 добавлений и 85 удалений

74
Cargo.lock сгенерированный
Просмотреть файл

@ -118,7 +118,7 @@ dependencies = [
[[package]]
name = "cpp_demangle"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
@ -196,11 +196,12 @@ name = "fix-stacks"
version = "0.1.0"
dependencies = [
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"goblin 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
"symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
"symbolic-debuginfo 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
"symbolic-demangle 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
"symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
"symbolic-debuginfo 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
"symbolic-demangle 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
]
[[package]]
@ -253,12 +254,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "goblin"
version = "0.0.23"
version = "0.1.1"
source = "git+https://github.com/jan-auer/goblin?rev=ee8b997792d37cfb35f8656c028543797425137e#ee8b997792d37cfb35f8656c028543797425137e"
dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "goblin"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -375,7 +386,7 @@ dependencies = [
[[package]]
name = "pdb"
version = "0.5.0"
source = "git+https://github.com/jan-auer/pdb?rev=79112ae2fd208fcb5c8e565dd148f4b55c3e58e3#79112ae2fd208fcb5c8e565dd148f4b55c3e58e3"
source = "git+https://github.com/jan-auer/pdb?rev=6518a17aff69ed26375cf3358632abbd32073931#6518a17aff69ed26375cf3358632abbd32073931"
dependencies = [
"fallible-iterator 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -620,6 +631,14 @@ dependencies = [
"scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scroll"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scroll_derive"
version = "0.9.5"
@ -630,6 +649,16 @@ dependencies = [
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scroll_derive"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.9.0"
@ -698,7 +727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "symbolic-common"
version = "7.0.0"
source = "git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf#d217a9340df3bbd373323b732880a95e6de353bf"
source = "git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63#7d4ca78958aab62104a9fc18bb17cdba616d7a63"
dependencies = [
"debugid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -710,38 +739,38 @@ dependencies = [
[[package]]
name = "symbolic-debuginfo"
version = "7.0.0"
source = "git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf#d217a9340df3bbd373323b732880a95e6de353bf"
source = "git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63#7d4ca78958aab62104a9fc18bb17cdba616d7a63"
dependencies = [
"dmsort 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
"gimli 0.19.0 (git+https://github.com/jan-auer/gimli?rev=bf8ea0f0079505681c360d3a55d0ac1e9b498df3)",
"goblin 0.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
"goblin 0.1.1 (git+https://github.com/jan-auer/goblin?rev=ee8b997792d37cfb35f8656c028543797425137e)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=79112ae2fd208fcb5c8e565dd148f4b55c3e58e3)",
"pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=6518a17aff69ed26375cf3358632abbd32073931)",
"pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
"symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
"zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "symbolic-demangle"
version = "7.0.0"
source = "git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf#d217a9340df3bbd373323b732880a95e6de353bf"
source = "git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63#7d4ca78958aab62104a9fc18bb17cdba616d7a63"
dependencies = [
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
"cpp_demangle 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"msvc-demangler 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)",
"symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)",
]
[[package]]
@ -866,7 +895,7 @@ dependencies = [
"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum cpp_demangle 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1d355451c6fb0dd4142db91e8dbd020d0d5466393957e771032bb9bf779504b4"
"checksum cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4115af6f575a7bc82c613e9e0ed7cc36a5e4fc3a8b54920dc0c820823a31a0d6"
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
"checksum debugid 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "751dad1347b163aa77262232129c7ac46e2810485c9b095ac9f7caf200e97df4"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
@ -882,7 +911,8 @@ dependencies = [
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum gimli 0.19.0 (git+https://github.com/jan-auer/gimli?rev=bf8ea0f0079505681c360d3a55d0ac1e9b498df3)" = "<none>"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum goblin 0.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "ac56b4753b6b8c2e052ca30717e5a09acf1b02a2c1681bf3d883bd660e5d22bd"
"checksum goblin 0.1.1 (git+https://github.com/jan-auer/goblin?rev=ee8b997792d37cfb35f8656c028543797425137e)" = "<none>"
"checksum goblin 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88a79ef1f0dad46fd78075b6f80f92d97710eddf87b3e18a15a66761e8942672"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
@ -899,7 +929,7 @@ dependencies = [
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7"
"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c"
"checksum pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=79112ae2fd208fcb5c8e565dd148f4b55c3e58e3)" = "<none>"
"checksum pdb 0.5.0 (git+https://github.com/jan-auer/pdb?rev=6518a17aff69ed26375cf3358632abbd32073931)" = "<none>"
"checksum pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a"
"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
"checksum pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0"
@ -928,7 +958,9 @@ dependencies = [
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
"checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1"
"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383"
"checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28"
"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
@ -938,9 +970,9 @@ dependencies = [
"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum symbolic-common 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)" = "<none>"
"checksum symbolic-debuginfo 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)" = "<none>"
"checksum symbolic-demangle 7.0.0 (git+https://github.com/getsentry/symbolic/?rev=d217a9340df3bbd373323b732880a95e6de353bf)" = "<none>"
"checksum symbolic-common 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)" = "<none>"
"checksum symbolic-debuginfo 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)" = "<none>"
"checksum symbolic-demangle 7.0.0 (git+https://github.com/nnethercote/symbolic/?rev=7d4ca78958aab62104a9fc18bb17cdba616d7a63)" = "<none>"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92"
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"

Просмотреть файл

@ -7,6 +7,7 @@ license = "MIT/Apache-2.0"
[dependencies]
fxhash = "0.2.1"
goblin = "0.1.2"
regex = "1.0"
serde_json = "1.0"
@ -14,6 +15,12 @@ serde_json = "1.0"
# writing, 6.1.4 is the latest version of `symbolic` on crates.io, but this dev
# version of 7.0.0 fixes some API botches (`ObjectDebugSession::functions()`
# being private). Once 7.0.0 is released, we can switch to it.
symbolic-common = { git = "https://github.com/getsentry/symbolic/", rev = "d217a9340df3bbd373323b732880a95e6de353bf" }
symbolic-debuginfo = { git = "https://github.com/getsentry/symbolic/", rev = "d217a9340df3bbd373323b732880a95e6de353bf" }
symbolic-demangle = { git = "https://github.com/getsentry/symbolic/", rev = "d217a9340df3bbd373323b732880a95e6de353bf" }
#
# FIXME(https://github.com/mozilla/fix-stacks/issues/11): if/when my
# symbolic-debuginfo changes land
# (https://github.com/getsentry/symbolic/pull/173), change these to point again
# to the `getsentry` repo.
#
symbolic-common = { git = "https://github.com/nnethercote/symbolic/", rev = "7d4ca78958aab62104a9fc18bb17cdba616d7a63" }
symbolic-debuginfo = { git = "https://github.com/nnethercote/symbolic/", rev = "7d4ca78958aab62104a9fc18bb17cdba616d7a63" }
symbolic-demangle = { git = "https://github.com/nnethercote/symbolic/", rev = "7d4ca78958aab62104a9fc18bb17cdba616d7a63" }

Просмотреть файл

@ -2,8 +2,8 @@
This program post-processes ("fixes") the stack frames produced by
`MozFormatCodeAddress()`, which often lack one or more of: function name, file
name, line number. It relies on the `symbolic` crate to read debug info from
files.
name, line number. It relies on the `symbolic` and `goblin` crates to read
debug info from files.
It reads from standard input and writes to standard output. Lines matching the
special stack frame format are modified appropriately. For example, a line
@ -24,10 +24,9 @@ the stack frames and the build files. Furthermore, the build files must not
have changed since the stack frames were produced. Otherwise, source locations
in the output may be missing or incorrect.
# Shortcomings
`fix-stacks` works on Linux, Windows, and Mac.
`fix-stacks` works on Linux and Windows. We aim to eventually support
[Mac](https://github.com/mozilla/fix-stacks/issues/3) and possibly Android.
# Shortcomings
On Linux, use with debuginfo sections in separate files is untested and
probably does not work.

Просмотреть файл

@ -3,14 +3,15 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use fxhash::FxHashMap;
use fxhash::{FxHashMap, FxHashSet};
use goblin::{archive, mach};
use regex::Regex;
use std::collections::hash_map::Entry;
use std::env;
use std::fs;
use std::io::{self, BufRead, Write};
use symbolic_common::Name;
use symbolic_debuginfo::{FileFormat, Function, Object, ObjectDebugSession};
use symbolic_common::{Arch, Name};
use symbolic_debuginfo::{Archive, FileFormat, Function, Object, ObjectDebugSession};
use symbolic_demangle::{Demangle, DemangleFormat, DemangleOptions};
#[cfg(test)]
@ -57,6 +58,18 @@ enum JsonEscaping {
Yes,
}
fn format_address(address: u64, offset: i64) -> String {
if offset == 0 {
format!("0x{:x}", address)
} else {
format!(
"0x{:x} -> 0x{:x}",
address,
(address as i64 + offset) as u64
)
}
}
/// Debug info for a single line.
struct LineInfo {
address: u64,
@ -67,17 +80,17 @@ struct LineInfo {
}
impl LineInfo {
fn new(interner: &mut Interner, line: symbolic_debuginfo::LineInfo) -> LineInfo {
fn new(interner: &mut Interner, line: symbolic_debuginfo::LineInfo, offset: i64) -> LineInfo {
if PRINT_FUNCS_AND_LINES {
eprintln!(
"LINE 0x{:x} line={} file={}",
line.address,
"LINE {} line={} file={}",
format_address(line.address, offset),
line.line,
line.file.path_str()
);
}
LineInfo {
address: line.address,
address: (line.address as i64 + offset) as u64,
line: line.line,
path: interner.intern(line.file.path_str()),
}
@ -98,23 +111,23 @@ struct FuncInfo {
}
impl FuncInfo {
fn new(interner: &mut Interner, function: Function) -> FuncInfo {
fn new(interner: &mut Interner, function: Function, offset: i64) -> FuncInfo {
if PRINT_FUNCS_AND_LINES {
eprintln!(
"FUNC 0x{:x} size={} func={}",
function.address,
"FUNC {} size={} func={}",
format_address(function.address, offset),
function.size,
function.name.as_str()
);
}
FuncInfo {
address: function.address,
address: (function.address as i64 + offset) as u64,
size: function.size,
mangled_name: function.name.as_str().to_string(),
line_infos: function
.lines
.into_iter()
.map(|line| LineInfo::new(interner, line))
.map(|line| LineInfo::new(interner, line, offset))
.collect(),
}
}
@ -146,10 +159,12 @@ impl FuncInfo {
}
/// Debug info for a single file.
#[derive(Default)]
struct FileInfo {
interner: Interner,
/// The `FuncInfo`s are sorted by `address`.
func_infos: Vec<FuncInfo>,
interner: Interner,
}
impl FileInfo {
@ -160,18 +175,60 @@ impl FileInfo {
.functions()
.filter_map(|function| {
let function = function.ok()?;
Some(FuncInfo::new(&mut interner, function))
Some(FuncInfo::new(&mut interner, function, 0))
})
.collect();
func_infos.sort_unstable_by_key(|func_info| func_info.address);
func_infos.dedup_by_key(|func_info| func_info.address);
FileInfo {
interner,
func_infos,
}
}
/// Add the debug info from `debug_session` for functions in `filename`
/// to that already gathered in `interner` and `func_infos`, but only for
/// functions present in `sym_func_addrs`.
fn add(
sym_func_addrs: &SymFuncAddrs,
file_name: &str,
debug_session: ObjectDebugSession,
interner: &mut Interner,
func_infos: &mut Vec<FuncInfo>,
) {
// Build the `FileInfo` from the debug session.
func_infos.extend(debug_session.functions().filter_map(|function| {
let function = function.ok()?;
// If a function appears in the debug info but was not seen in
// the parent binary's symbol table, just ignore it. This is
// common, perhaps due to inlining (i.e. inlined functions don't
// end up with their own symbols in the binary).
//
// Otherwise, we know the function's address in the parent binary's
// symbol table. Adjust all the addresses from the debug info to
// match that address.
let sym_func_key = Fixer::sym_func_key(file_name, function.name.as_str());
let sym_func_addr = sym_func_addrs.get(&sym_func_key)?;
let offset = *sym_func_addr as i64 - function.address as i64;
Some(FuncInfo::new(interner, function, offset))
}));
}
/// Finish constructing a `FileInfo` that has been built up using
/// `Fixer::add`.
fn finish(interner: Interner, mut func_infos: Vec<FuncInfo>) -> FileInfo {
func_infos.sort_unstable_by_key(|func_info| func_info.address);
func_infos.dedup_by_key(|func_info| func_info.address);
FileInfo {
func_infos,
interner,
}
}
/// Get the `FuncInfo` for an address, if there is one.
fn func_info(&self, address: u64) -> Option<&FuncInfo> {
match self
.func_infos
@ -198,6 +255,9 @@ struct Fixer {
json_escaping: JsonEscaping,
}
/// Records address of functions from a symbol table.
type SymFuncAddrs = FxHashMap<String, u64>;
impl Fixer {
fn new(json_escaping: JsonEscaping) -> Fixer {
Fixer {
@ -220,38 +280,255 @@ impl Fixer {
/// subsequently query. Return a description of the failing operation on
/// error.
fn build_file_info(file_name: &str) -> Result<FileInfo, String> {
let msg = |op: &str| format!("Unable to {} `{}`", op, file_name);
let data = fs::read(file_name).map_err(|_| "read")?;
let file_format = Archive::peek(&data);
match file_format {
FileFormat::Elf => Fixer::build_file_info_direct(&data),
FileFormat::Pe => Fixer::build_file_info_pe(&data),
FileFormat::Pdb => Fixer::build_file_info_direct(&data),
FileFormat::MachO => Fixer::build_file_info_macho(&data),
_ => Err(format!("parse {} format file", file_format)),
}
}
// Read the file.
let mut data = fs::read(file_name).map_err(|_| msg("read"))?;
// "Direct" means that the debug info is within `data`, as opposed to being
// in another file that `data` refers to.
fn build_file_info_direct(data: &[u8]) -> Result<FileInfo, String> {
let object = Object::parse(&data).map_err(|_| "parse")?;
let debug_session = object.debug_session().map_err(|_| "read debug info from")?;
Ok(FileInfo::new(debug_session))
}
// On some platforms we have to get the debug info from another file.
// Get the name of that file, if there is one.
let file_name2 = match Object::peek(&data) {
FileFormat::Pe => {
let pe_object = Object::parse(&data).map_err(|_| msg("parse"))?;
if let Object::Pe(pe) = pe_object {
// PE files should contain a pointer to a PDB file.
let pdb_file_name = pe.debug_file_name().ok_or_else(|| msg("find PDB for"))?;
Some(pdb_file_name.to_string())
fn build_file_info_pe(data: &[u8]) -> Result<FileInfo, String> {
// For PEs we get the debug info from a PDB file.
let pe_object = Object::parse(&data).map_err(|_| "parse")?;
let pe = match pe_object {
Object::Pe(pe) => pe,
_ => unreachable!(),
};
let pdb_file_name = pe.debug_file_name().ok_or("find debug info file for")?;
let data = fs::read(pdb_file_name.to_string())
.map_err(|_| format!("read debug info file `{}` for", pdb_file_name))?;
Fixer::build_file_info_direct(&data)
}
fn build_file_info_macho(data: &[u8]) -> Result<FileInfo, String> {
// On Mac, debug info is typically stored in `.dSYM` directories. But
// they aren't normally built for Firefox because doing so is slow.
// Instead, we read the symbol table of the given file, which has
// pointers to all the object files from which it was constructed. We
// then obtain the debug info from those object files (some of which
// are embedded within `.a` files), and adjust the addresses from the
// debug info appropriately. All this requires the object files to
// still be present, and matches what `atos` does.
//
// Doing all this requires a lower level of processing than what the
// `symbolic` crate provides, so instead we use the `goblin` crate.
//
// We stop if any errors are encountered. The code could be made more
// robust in the face of errors if necessary.
let macho = Fixer::macho(&data)?;
let sym_func_addrs = Fixer::sym_func_addrs(&macho)?;
// Iterate again through the symbol table, reading every object file
// that is referenced, and adjusting the addresses in those files using
// the function addresses obtained above.
let mut seen_archives = FxHashSet::default();
let mut func_infos = vec![];
let mut interner = Interner::default();
for sym in macho.symbols() {
let (oso_name, nlist) = sym.map_err(|_| "read symbol table from")?;
if nlist.is_stab() && nlist.n_type == mach::symbols::N_OSO {
if let Some(ar_file_name) = Fixer::is_within_archive(oso_name) {
// It's an archive entry, e.g. "libgkrust.a(foo.o)". Read
// every entry in archive, if we haven't already done so.
if seen_archives.insert(ar_file_name) {
let ar_data = fs::read(ar_file_name)
.map_err(|_| format!("read ar `{}` referenced by", ar_file_name))?;
let ar = archive::Archive::parse(&ar_data)
.map_err(|_| format!("parse ar `{}` referenced by", ar_file_name))?;
for (name, _, _) in ar.summarize() {
let data = ar.extract(name, &ar_data).map_err(|_| {
format!("read an entry in ar `{}` referenced by", ar_file_name)
})?;
Fixer::do_macho_oso(
&sym_func_addrs,
ar_file_name,
data,
&mut interner,
&mut func_infos,
)?;
}
}
} else {
panic!(); // Impossible: peek() said it was a PE object.
// It's a normal object file. Read it.
let data = fs::read(oso_name)
.map_err(|_| format!("read `{}` referenced by", oso_name))?;
Fixer::do_macho_oso(
&sym_func_addrs,
oso_name,
&data,
&mut interner,
&mut func_infos,
)?;
}
}
_ => None,
};
if let Some(file_name2) = file_name2 {
data = fs::read(&file_name2)
.map_err(|_| msg(&format!("read debug info file `{}` for", file_name2)))?;
}
// Get the debug session from the file data.
let object = Object::parse(&data).map_err(|_| msg("parse"))?;
Ok(FileInfo::finish(interner, func_infos))
}
fn macho(data: &[u8]) -> Result<mach::MachO, String> {
let mach = mach::Mach::parse(&data).map_err(|_| "parse (with goblin)")?;
match mach {
mach::Mach::Binary(macho) => Ok(macho),
mach::Mach::Fat(multi_arch) => {
// Get the x86-64 object from the fat binary. (On Mac, Firefox
// is only available on x86-64.)
let macho = multi_arch.into_iter().find(|macho| {
if let Ok(macho) = macho {
if macho.header.cputype() == mach::constants::cputype::CPU_TYPE_X86_64 {
return true;
}
}
false
});
// This chaining is necessary because `MachOIterator::Item` is
// not `MachO` but `Result<MachO>`. We don't distinguish
// between the "couldn't find the x86-64 code" case and the
// "found it, but it had an error" case.
let msg = "find x86-64 code in the fat binary";
macho.ok_or(msg)?.map_err(|_| msg.to_string())
}
}
}
/// Iterate through the symbol table, getting the address of every function
/// in the file.
fn sym_func_addrs(macho: &mach::MachO) -> Result<SymFuncAddrs, String> {
let object_load_address = Fixer::object_load_address(macho);
let mut sym_func_addrs = FxHashMap::default();
let mut curr_oso_name = String::new();
for sym in macho.symbols() {
let (name, nlist) = sym.map_err(|_| "read symbol table from")?;
if !nlist.is_stab() {
continue;
}
if nlist.n_type == mach::symbols::N_OSO {
// Record this reference to an object file (or archive).
curr_oso_name = if let Some(ar_file_name) = Fixer::is_within_archive(name) {
// We have to strip the archive suffix, because the suffix
// in the symbol table often disagrees with the suffix in
// the debug info. E.g.
// - symbol table: `libjs_static.a(Unified_cpp_js_src9.o)`
// - debug info: `libjs_static.a(RegExp.o)`
//
// It's unclear why this occurs, though it doesn't seem to
// matter much, though see the comment about duplicates
// below.
ar_file_name.to_string()
} else {
name.to_string()
}
} else if nlist.n_type == mach::symbols::N_FUN
&& nlist.n_sect != mach::symbols::NO_SECT as usize
{
let name = &name[1..]; // Trim the leading underscore.
let address = nlist.n_value - object_load_address;
let sym_func_key = Fixer::sym_func_key(&curr_oso_name, name);
// There can be duplicates, in which case the last one "wins".
// This seems to only occur due to the removal of archive
// suffixes above. In practice it doesn't seem to matter.
sym_func_addrs.insert(sym_func_key, address);
}
}
Ok(sym_func_addrs)
}
// This is based on `symbolic_debuginfo::macho:MachObject::load_address`.
// We need to define it because `goblin` doesn't have an equivalent, and we
// use `goblin` rather than `symbolic_debuginfo` for Mach-O binaries.
fn object_load_address(macho: &mach::MachO) -> u64 {
for seg in macho.segments.iter() {
if let Ok(name) = seg.name() {
if name == "__TEXT" {
return seg.vmaddr;
}
}
}
0
}
/// Construct a key for the `sym_func_addrs` hash map.
fn sym_func_key(file_name: &str, func_name: &str) -> String {
format!("{}:{}", file_name, func_name)
}
/// Is this filename within an archive? E.g. `libfoo.a(bar.o)` means that
/// `bar.o` is within the archive `libfoo.a`. If so, return the archive
/// name.
fn is_within_archive(file_name: &str) -> Option<&str> {
if let (Some(index), true) = (file_name.find(".a("), file_name.ends_with(")")) {
let ar_file_name = &file_name[..index + 2];
Some(ar_file_name)
} else {
None
}
}
/// Read the debug info from a file referenced by an OSO entry in a Macho-O
/// symbol table.
fn do_macho_oso(
sym_func_addrs: &SymFuncAddrs,
file_name: &str,
data: &[u8],
interner: &mut Interner,
func_infos: &mut Vec<FuncInfo>,
) -> Result<(), String> {
// Although we use `goblin` to iterate through the symbol
// table, we use `symbolic` to read the debug info from the
// object/archive, because it's easier to use.
let archive = Archive::parse(&data)
.map_err(|e| format!("({:?}) parse `{}` referenced by", e, file_name))?;
// Get the x86-64 object from the archive, which might be a fat binary.
// (On Mac, Firefox is only available on x86-64.)
let mut x86_64_object = None;
for object in archive.objects() {
let object = object
.map_err(|_| format!("parse fat binary entry in `{}` referenced by", file_name))?;
if object.arch() == Arch::Amd64 {
x86_64_object = Some(object);
break;
}
}
let object = x86_64_object.ok_or_else(|| {
format!(
"find x86-64 code in the fat binary `{}` referenced by",
file_name
)
})?;
let debug_session = object
.debug_session()
.map_err(|_| msg("read debug info from"))?;
.map_err(|_| format!("read debug info from `{}` referenced by", file_name))?;
Ok(FileInfo::new(debug_session))
FileInfo::add(
&sym_func_addrs,
file_name,
debug_session,
interner,
func_infos,
);
Ok(())
}
/// Fix stack frames within `line` as necessary. Prints any errors to stderr.
@ -276,9 +553,16 @@ impl Fixer {
Entry::Occupied(o) => o.into_mut(),
Entry::Vacant(v) => match Fixer::build_file_info(file_name) {
Ok(file_info) => v.insert(file_info),
Err(msg) => {
eprintln!("{}", msg);
return line;
Err(op) => {
// Print an error message and then set up an empty
// `FileInfo` for this file, for two reasons.
// - If an invalid file is mentioned multiple times in the
// input, an error message will be issued only on the
// first occurrence.
// - The line will still receive some transformation, using
// the "no symbols or debug info" case below.
eprintln!("fix-stacks error: failed to {} `{}`", op, file_name);
v.insert(FileInfo::default())
}
},
};

Просмотреть файл

@ -33,7 +33,7 @@ fn test_linux() {
let mut fixer = Fixer::new(JsonEscaping::No);
// Test various addresses within `main`.
// Test various addresses.
let mut func = |name, addr, linenum| {
let line = format!("#00: ???[tests/example-linux +0x{:x}]", addr);
let line = fixer.fix(line);
@ -111,8 +111,8 @@ fn test_windows() {
let mut fixer = Fixer::new(JsonEscaping::Yes);
// Test various addresses within `main` using `example-windows`, which
// redirects to `example-windows.pdb`.
// Test various addresses using `example-windows`, which redirects to
// `example-windows.pdb`.
let mut func = |name, addr, linenum| {
let line = format!("#00: ???[tests/example-windows +0x{:x}]", addr);
let line = fixer.fix(line);
@ -158,6 +158,109 @@ fn test_windows() {
outside(0xfffffff); // A very high address.
}
#[test]
fn test_mac() {
// The debug info within `mac-multi` is as follows. (See `tests/README.md`
// for details on how these lines were generated.)
//
// FUNC 0xd70 size=54 func=main
// LINE 0xd70 line=17 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
// LINE 0xd7f line=18 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
// LINE 0xd86 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
// LINE 0xd8f line=20 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
// LINE 0xd98 line=21 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
// LINE 0xd9d line=22 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
//
// FUNC 0xdb0 size=31 func=duplicate
// LINE 0xdb0 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
// LINE 0xdb8 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
// LINE 0xdc9 line=12 file=/Users/njn/moz/fix-stacks/tests/mac-normal.c
//
// FUNC 0xdd0 size=37 func=fat_B
// LINE 0xdd0 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
// LINE 0xddc line=20 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
// LINE 0xdef line=21 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
//
// FUNC 0xe00 size=34 func=fat_A
// LINE 0xe00 line=13 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
// LINE 0xe0b line=14 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
// LINE 0xe14 line=15 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
// LINE 0xe19 line=16 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
//
// FUNC 0xe30 size=31 func=duplicate
// LINE 0xe30 line=9 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
// LINE 0xe38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
// LINE 0xe49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-fat.c
//
// FUNC 0xe50 size=37 func=lib1_B
// LINE 0xe50 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
// LINE 0xe5c line=20 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
// LINE 0xe6f line=21 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
//
// FUNC 0xe80 size=34 func=lib1_A
// LINE 0xe80 line=13 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
// LINE 0xe8b line=14 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
// LINE 0xe94 line=15 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
// LINE 0xe99 line=16 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
//
// // Note that these ones are wrong, and should start at 0xeb0. This is
// // due to the unavoidable archive suffix stripping, see comments in
// // `main.rs` for details.
// FUNC 0xf30 size=31 func=duplicate
// LINE 0xf30 line=9 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
// LINE 0xf38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
// LINE 0xf49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-lib1.c
//
// FUNC 0xed0 size=37 func=lib2_B
// LINE 0xed0 line=19 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
// LINE 0xedc line=20 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
// LINE 0xeef line=21 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
//
// FUNC 0xf00 size=39 func=lib2_A
// LINE 0xf00 line=13 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
// LINE 0xf0b line=14 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
// LINE 0xf19 line=15 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
// LINE 0xf1e line=16 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
//
// FUNC 0xf30 size=31 func=duplicate
// LINE 0xf30 line=9 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
// LINE 0xf38 line=10 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
// LINE 0xf49 line=11 file=/Users/njn/moz/fix-stacks/tests/mac-lib2.c
let mut fixer = Fixer::new(JsonEscaping::No);
// Test addresses from all the object files that `mac-multi` references.
let mut func = |name, addr, full_path, locn| {
let line = format!("#00: ???[tests/mac-multi +0x{:x}]", addr);
let line = fixer.fix(line);
let path = if full_path {
"/Users/njn/moz/fix-stacks/tests/"
} else {
"tests/"
};
assert_eq!(line, format!("#00: {} ({}{})", name, path, locn));
};
func("main", 0xd70, true, "mac-normal.c:17");
func("duplicate", 0xdb3, true, "mac-normal.c:10");
func("fat_B", 0xddc, true, "mac-fat.c:20");
func("fat_A", 0xe19, true, "mac-fat.c:16");
func("duplicate", 0xe4e, true, "mac-fat.c:11");
func("lib1_B", 0xe50, true, "mac-lib1.c:19");
func("lib1_A", 0xe95, true, "mac-lib1.c:15");
// This should be `duplicate` in `mac-lib1.c`. It's wrong due to the
// archive suffix stripping mentioned above.
func("???", 0xeaa, false, "mac-multi");
func("lib2_B", 0xedc, true, "mac-lib2.c:20");
func("lib2_A", 0xf1e, true, "mac-lib2.c:16");
// This should be `mac-lib2.c:10`. It's wrong due to the archive suffix
// stripping mentioned above.
func("duplicate", 0xf38, true, "mac-lib1.c:10");
}
#[test]
fn test_regex() {
let mut fixer = Fixer::new(JsonEscaping::No);
@ -176,8 +279,8 @@ fn test_regex() {
// An error message is also printed to stderr for file errors, but we don't
// test for that.
unchanged("#00: ???[tests/no-such-file +0x43a0]"); // No such file.
unchanged("#00: ???[src/main.rs +0x43a0]"); // File exists, but has wrong format.
unchanged("#00: ??? (tests/no-such-file)"); // No such file.
unchanged("#00: ??? (src/main.rs)"); // File exists, but has wrong format.
// Test various different changed line forms that do match the regex.
let mut changed = |line1: &str, line2_expected| {

Просмотреть файл

@ -22,19 +22,56 @@ To allow this requires the following.
## Generating inputs
Debug info is very complicated and hard to write by hand. Therefore, the tests
use executables and other data files created by compilers as inputs.
use executables and other data files created by compilers as inputs. These were
all generated within the `test` directory in the following ways.
The primary source code file used for generating these files is
`tests/example.c`. The following files were generated from it.
- `example-linux`: produced on an Ubuntu 19.04 box by clang 8.0 with the
command `clang -g example.c -o example-linux`.
- `example-windows` and `example-windows.pdb`: produced on a Windows 10 laptop
by clang 9.0 with the command `clang -g example.c -o example-windows`.
`example-windows` was then hex-edited to change the PDB reference from the
absolute path `c:\Users\njn\moz\fix-stacks\tests\example-windows.pdb` to the
relative path `tests/////////////////////////////example-windows.pdb`. (The
use of many redundant forward slashes is a hack to keep the path the same
length, which avoids the need for more complex changes to that file.)
### Linux
`example-linux` was produced on an Ubuntu 19.04 box by clang 8.0 with this
command:
```
clang -g example.c -o example-linux
```
### Windows
`example-windows` and `example-windows.pdb` were produced on a Windows 10 laptop
by clang 9.0 with this command:
```
clang -g example.c -o example-windows
```
`example-windows` was then hex-edited to change the PDB reference from the
absolute path `c:\Users\njn\moz\fix-stacks\tests\example-windows.pdb` to the
relative path `tests/////////////////////////////example-windows.pdb`. (The use
of many redundant forward slashes is a hack to keep the path the same length,
which avoids the need for more complex changes to that file.)
### Mac
The Mac tests are more complex because `fix-stacks`'s code for handling Mach-O
binaries is more complex than other formats.
`mac-multi` was produced on a MacBook Pro running macOS 10.14 by Apple clang
11.0 with these commands:
```
# A normal file.
clang -c -g mac-normal.c -o mac-normal.o
# A fat binary.
clang -m32 -c -g mac-fat.c -o mac-fat-32.o
clang -m64 -c -g mac-fat.c -o mac-fat-64.o
lipo -create mac-fat-32.o mac-fat-64.o -output mac-fat.o
# A library.
clang -c -g mac-lib1.c -o mac-lib1.o
clang -c -g mac-lib2.c -o mac-lib2.o
ar -r libexample.a mac-lib1.o mac-lib2.o
# The final executable.
clang mac-normal.o mac-fat.o libexample.a -o mac-multi
```
`mac-multi` was then hex-edited to change all the file reference from the
absolute paths such as `/Users/njn/moz/fix-stacks/tests/mac-normal.c` to the
relative paths such as `tests///////////////////////////mac-normal.c`. (The use
of many redundant forward slashes is a hack to keep the path the same length,
which avoids the need for more complex changes to that file.)
## Obtaining the debug info

Двоичные данные
tests/libexample.a Normal file

Двоичный файл не отображается.

Двоичные данные
tests/mac-fat-32.o Normal file

Двоичный файл не отображается.

Двоичные данные
tests/mac-fat-64.o Normal file

Двоичный файл не отображается.

21
tests/mac-fat.c Normal file
Просмотреть файл

@ -0,0 +1,21 @@
// This file contains the source code for some tests. If you change this file
// you may need to regenerate some test files. (Even changing the number of
// lines in this comment would have an effect.)
//
// See `tests/README.md` for more details.
#include <stdio.h>
static void duplicate() {
printf("fat duplicate");
}
static int fat_A(int x) {
x = x * 2;
duplicate();
return x;
}
void fat_B(int* x) {
*x += fat_A(*x);
}

Двоичные данные
tests/mac-fat.o Normal file

Двоичный файл не отображается.

21
tests/mac-lib1.c Normal file
Просмотреть файл

@ -0,0 +1,21 @@
// This file contains the source code for some tests. If you change this file
// you may need to regenerate some test files. (Even changing the number of
// lines in this comment would have an effect.)
//
// See `tests/README.md` for more details.
#include <stdio.h>
static void duplicate() {
printf("lib1 duplicate");
}
static int lib1_A(int x) {
x = x + 2;
duplicate();
return x;
}
void lib1_B(int* x) {
*x += lib1_A(*x);
}

Двоичные данные
tests/mac-lib1.o Normal file

Двоичный файл не отображается.

21
tests/mac-lib2.c Normal file
Просмотреть файл

@ -0,0 +1,21 @@
// This file contains the source code for some tests. If you change this file
// you may need to regenerate some test files. (Even changing the number of
// lines in this comment would have an effect.)
//
// See `tests/README.md` for more details.
#include <stdio.h>
static void duplicate() {
printf("lib2 duplicate");
}
static int lib2_A(int x) {
x = x / 2;
duplicate();
return x;
}
void lib2_B(int* x) {
*x += lib2_A(*x);
}

Двоичные данные
tests/mac-lib2.o Normal file

Двоичный файл не отображается.

Двоичные данные
tests/mac-multi Executable file

Двоичный файл не отображается.

23
tests/mac-normal.c Normal file
Просмотреть файл

@ -0,0 +1,23 @@
// This file contains the source code for some tests. If you change this file
// you may need to regenerate some test files. (Even changing the number of
// lines in this comment would have an effect.)
//
// See `tests/README.md` for more details.
#include <stdio.h>
static void duplicate() {
printf("normal duplicate");
}
void lib1_B(int* x);
void lib2_B(int* x);
int main() {
int x = 0;
lib1_B(&x);
lib2_B(&x);
duplicate();
return x;
}

Двоичные данные
tests/mac-normal.o Normal file

Двоичный файл не отображается.