{"id":13546102,"url":"https://github.com/japaric/cargo-call-stack","last_synced_at":"2025-05-14T12:11:34.938Z","repository":{"id":37748383,"uuid":"160117570","full_name":"japaric/cargo-call-stack","owner":"japaric","description":"Whole program static stack analysis","archived":false,"fork":false,"pushed_at":"2024-10-28T17:29:02.000Z","size":568,"stargazers_count":605,"open_issues_count":37,"forks_count":51,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-04-11T18:19:48.912Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/japaric.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-03T01:52:30.000Z","updated_at":"2025-04-10T21:33:21.000Z","dependencies_parsed_at":"2024-10-28T18:24:25.892Z","dependency_job_id":"8a22b21e-9841-4cdc-ba25-c5b55f77e0f1","html_url":"https://github.com/japaric/cargo-call-stack","commit_stats":{"total_commits":143,"total_committers":11,"mean_commits":13.0,"dds":0.5454545454545454,"last_synced_commit":"49080d56b827573f1e19ff127401e312d08f138c"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japaric%2Fcargo-call-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japaric%2Fcargo-call-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japaric%2Fcargo-call-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/japaric%2Fcargo-call-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/japaric","download_url":"https://codeload.github.com/japaric/cargo-call-stack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254140766,"owners_count":22021220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T12:00:31.619Z","updated_at":"2025-05-14T12:11:34.868Z","avatar_url":"https://github.com/japaric.png","language":"Rust","readme":"# `cargo-call-stack`\n\n\u003e Static, whole program stack usage analyzer\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://japaric.github.io/cargo-call-stack/cycle.svg\"\u003e\n    \u003cimg alt=\"Call graph with a cycle\" src=\"assets/cycle.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nOther examples:\n[Embedded CoAP / IPv4 server](https://japaric.github.io/cargo-call-stack/ipv4.svg)\n([source](https://github.com/japaric/jnet/tree/b5bd70cb998b1e01236f6b07ebc516a0359fde3d/firmware#ipv4))\n[\"Hello, world!\"](https://japaric.github.io/cargo-call-stack/hello.svg)\n\n**HEADS UP**: This tool relies on an experimental feature (`-Z stack-sizes`)\nand implementation details of `rustc` (like symbol mangling) and could stop\nworking with a nightly toolchain at any time. You have been warned! Last tested nightly: 2023-11-13.\n\n**NOTE**: This tool main use case are embedded (microcontroller) programs that lack, or have very\nlittle, indirect function calls and recursion. This tool is of very limited use -- specially its\nstack usage analysis -- on programs that link to the standard library as even the smallest program\nthat links to the standard library will contain a large number of indirect function calls (like\ntrait object dynamic dispatch specially in `core::fmt`) and potential recursion (specially in\npanicking branches).\n\n## Features\n\n- The tool produces the full call graph of a program as a [dot file].\n\n[dot file]: https://www.graphviz.org/doc/info/lang.html\n\n- A [start point](#start-point) can be specified to analyze only the call graph\n  that begins at that function.\n\n- Each node (function) in the call graph includes the local stack usage of the\n  function, *if* available (see [`-Z emit-stack-sizes`]).\n\n[`-Z emit-stack-sizes`]: https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/emit-stack-sizes.html\n\n- The *maximum stack usage* of each function is also computed, or at least a\n  lower bound is provided. Maximum stack usage of a function here refers to the\n  stack usage that includes the stack used by functions that the function may\n  invoke.\n\n- The tool has *imperfect* support for calls through function pointers (`fn()`)\n  and dynamic dispatch (`dyn Trait`). You *will* get a call graph from programs\n  that do indirect calls but it will likely be missing edges or contain\n  incorrect edges. It's best to use this tool on programs that only do direct\n  function calls.\n\n## Known limitations\n\n- Dynamically linked binaries are not supported. As a portion of the call graph is injected at\n  runtime by the dynamic linker is not possible to produce a complete call graph or compute an upper\n  bound of the program's stack usage.\n\n## Installation\n\n``` console\n$ # NOTE always use the latest stable release\n$ cargo +stable install cargo-call-stack\n\n$ rustup +nightly component add rust-src\n```\n\n## Example usage\n\n\u003e **NOTE** this tool requires that your Cargo project is configured to use *fat LTO* when Cargo uses\n\u003e the release profile. This is not the default configuration (as of Rust 1.63) so you'll need to add\n\u003e this to your `Cargo.toml`:\n\n``` toml\n[profile.release]\n# `lto = true` should also work\nlto = 'fat'\n```\n\nThe tool builds your program in release mode with LTO enabled, analyses it and\nthen prints a dot file to stdout. See `cargo call-stack -h` for a list of build\noptions (e.g. `--features`).\n\n[`cortex-m-rt`]: https://crates.io/crates/cortex-m-rt\n\n\u003e **NOTE** if you have *not* set a compilation target in e.g. `.cargo/config.toml` then you'll need\n\u003e to pass the `--target` flag to `cargo-call-stack` even if you are *not* cross compiling \n\n\u003e **NOTE** the analysis corresponds to the newly produced binary,\n\u003e which won't be the same as the binary produced by `cargo +nightly build --release`\n\n``` console\n$ cargo +nightly call-stack --example app \u003e cg.dot\nwarning: assuming that llvm_asm!(\"\") does *not* use the stack\nwarning: assuming that llvm_asm!(\"\") does *not* use the stack\n```\n\nGraphviz's `dot` can then be used to generate an image from this dot file.\n\n``` console\n$ dot -Tsvg cg.dot \u003e cg.svg\n```\n\n[![Call graph with direct function calls](assets/direct.png)](https://japaric.github.io/cargo-call-stack/direct.svg)\n\nEach node in this graph represents a function, which could be a free function,\nan inherent method or a trait method. Each directed edge indicates a \"calls\"\nrelationship. For example, in the above graph `Reset` calls both `main` and\n`DefaultPreInit`.\n\nEach node also contains its `local` stack usage in bytes and its `max`-imum\nstack usage, also in bytes. The maximum stack usage includes the stack usage of\nall the other functions that the function could invoke.\n\nThis is the `no_std` program used to generate the call graph shown above.\n\n``` rust\n#![feature(llvm_asm)]\n#![no_main]\n#![no_std]\n\nextern crate panic_halt;\n\nuse core::ptr;\n\nuse cortex_m_rt::{entry, exception};\n\n#[entry]\nfn main() -\u003e ! {\n    foo();\n\n    bar();\n\n    loop {}\n}\n\n#[inline(never)]\nfn foo() {\n    // spill variables onto the stack\n    unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5)) }\n}\n\n#[inline(never)]\nfn bar() {\n    unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5) \"r\"(6) \"r\"(7)) }\n}\n\n#[exception]\nfn SysTick() {\n    bar();\n}\n\n#[inline(never)]\nfn baz() {\n    let x = 0;\n    unsafe {\n        // force `x` to be on the stack\n        ptr::read_volatile(\u0026\u0026x);\n    }\n\n}\n```\n\n\u003e In the previous example the call graph contained disconnected subgraphs. The\n\u003e reason for that is *exceptions* (also known as interrupts). `SysTick`, for\n\u003e example, is an exception handler that can preempt any function called from\n\u003e `Reset`. This exception handler is never called from software but can be\n\u003e invoked by the hardware at any time. These exception handlers can appear as\n\u003e the roots of disconnected subgraphs.\n\n## Start point\n\nIn some cases you may be interested in the maximum stack usage of a particular\nfunction. The tool lets you specify a *start point* which will be used to filter\nthe call graph to only include nodes reachable from that function.\n\nIf we invoke the tool on the previous program but select `main` as the start\npoint we get this call graph:\n\n``` console\n$ cargo +nightly call-stack --example app main \u003e cg.dot\nwarning: assuming that llvm_asm!(\"\") does *not* use the stack\nwarning: assuming that llvm_asm!(\"\") does *not* use the stack\n```\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://japaric.github.io/cargo-call-stack/filtered.svg\"\u003e\n    \u003cimg alt=\"Filtered call graph\" src=\"assets/filtered.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nNotice that `SysTick` and `baz` don't appear in this call graph since they are\nnot reachable from `main`.\n\n## Cycles\n\nThe tool can, in some cases, compute the maximum stack usage of programs that\ninvolve recursion. Recursion appears as cycles in the call graph. Consider the\nfollowing example:\n\n``` rust\n#![feature(llvm_asm)]\n#![no_main]\n#![no_std]\n\nextern crate panic_halt;\n\nuse core::sync::atomic::{AtomicBool, Ordering};\n\nuse cortex_m_rt::{entry, exception};\n\nstatic X: AtomicBool = AtomicBool::new(true);\n\n#[inline(never)]\n#[entry]\nfn main() -\u003e ! {\n    foo();\n\n    quux();\n\n    loop {}\n}\n\n// these three functions form a cycle that breaks when `SysTick` runs\n#[inline(never)]\nfn foo() {\n    if X.load(Ordering::Relaxed) {\n        bar()\n    }\n}\n\n#[inline(never)]\nfn bar() {\n    if X.load(Ordering::Relaxed) {\n        baz()\n    }\n}\n\n#[inline(never)]\nfn baz() {\n    if X.load(Ordering::Relaxed) {\n        foo()\n    }\n}\n\n#[inline(never)]\nfn quux() {\n    // spill variables onto the stack\n    unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5)) }\n}\n\n#[exception]\nfn SysTick() {\n    X.store(false, Ordering::Relaxed);\n}\n```\n\nIt produces the following call graph:\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://japaric.github.io/cargo-call-stack/cycle.svg\"\u003e\n    \u003cimg alt=\"Call graph with a cycle\" src=\"assets/cycle.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nThe functions `foo`, `bar` and `baz` use zero stack space thus the cycle formed\nby them also uses zero stack space. In this particular case the maximum stack\nusage of `main` can be computed.\n\nFor the curious this is the disassembly of the \"cyclic\" program:\n\n``` armasm\n08000400 \u003capp::foo\u003e:\n 8000400:       f240 0000       movw    r0, #0\n 8000404:       f2c2 0000       movt    r0, #8192       ; 0x2000\n 8000408:       7800            ldrb    r0, [r0, #0]\n 800040a:       0600            lsls    r0, r0, #24\n 800040c:       bf18            it      ne\n 800040e:       f000 b801       bne.w   8000414 \u003capp::bar\u003e\n 8000412:       4770            bx      lr\n\n08000414 \u003capp::bar\u003e:\n 8000414:       f240 0000       movw    r0, #0\n 8000418:       f2c2 0000       movt    r0, #8192       ; 0x2000\n 800041c:       7800            ldrb    r0, [r0, #0]\n 800041e:       0600            lsls    r0, r0, #24\n 8000420:       bf18            it      ne\n 8000422:       f000 b801       bne.w   8000428 \u003capp::baz\u003e\n 8000426:       4770            bx      lr\n\n08000428 \u003capp::baz\u003e:\n 8000428:       f240 0000       movw    r0, #0\n 800042c:       f2c2 0000       movt    r0, #8192       ; 0x2000\n 8000430:       7800            ldrb    r0, [r0, #0]\n 8000432:       0600            lsls    r0, r0, #24\n 8000434:       bf18            it      ne\n 8000436:       f7ff bfe3       bne.w   8000400 \u003capp::foo\u003e\n 800043a:       4770            bx      lr\n\n0800043c \u003capp::quux\u003e:\n 800043c:       b580            push    {r7, lr}\n 800043e:       f04f 0c00       mov.w   ip, #0\n 8000442:       f04f 0e01       mov.w   lr, #1\n 8000446:       2202            movs    r2, #2\n 8000448:       2303            movs    r3, #3\n 800044a:       2004            movs    r0, #4\n 800044c:       2105            movs    r1, #5\n 800044e:       bd80            pop     {r7, pc}\n\n08000450 \u003cmain\u003e:\n 8000450:       f7ff ffd6       bl      8000400 \u003capp::foo\u003e\n 8000454:       f7ff fff2       bl      800043c \u003capp::quux\u003e\n 8000458:       e7fe            b.n     8000458 \u003cmain+0x8\u003e\n```\n\nAnd yes, the estimated maximum stack usage is correct as shown in this debug\nsession:\n\n``` console\n(gdb) b app::foo\n\n(gdb) b app::bar\n\n(gdb) b app::baz\n\n(gdb) c\nContinuing.\n\nBreakpoint 3, main () at src/main.rs:16\n16          foo();\n\n(gdb) p $sp\n$1 = (void *) 0x20005000\n\n(gdb) c\nContinuing.\nhalted: PC: 0x08000400\n\nBreakpoint 4, app::foo () at src/main.rs:31\n31          if X.load(Ordering::Relaxed) {\n\n(gdb) p $sp\n$2 = (void *) 0x20005000\n\n(gdb) c\nContinuing.\nhalted: PC: 0x0800040c\n\nBreakpoint 5, app::bar () at src/main.rs:38\n38          if X.load(Ordering::Relaxed) {\n\n(gdb) p $sp\n$3 = (void *) 0x20005000\n\n(gdb) c\nContinuing.\nhalted: PC: 0x08000420\n\nBreakpoint 6, app::baz () at src/main.rs:45\n45          if X.load(Ordering::Relaxed) {\n\n(gdb) p $sp\n$4 = (void *) 0x20005000\n\n(gdb) c\nContinuing.\nhalted: PC: 0x08000434\n\nBreakpoint 4, app::foo () at src/main.rs:31\n31          if X.load(Ordering::Relaxed) {\n\n(gdb) p $sp\n$5 = (void *) 0x20005000\n```\n\n## Trait object dispatch\n\n\u003e NOTE as of ~nightly-2022-09-20 there's no distinction between function pointers and trait objects\n\u003e at the llvm-ir so the analysis can insert an edge from a dynamic dispatch call site (caller) to a\n\u003e regular function (callee)\n\nIn *some* cases the tool can produce correct call graphs for programs that use\ntrait objects -- more details about where and how it fails in the [\"Known\nlimitations\"](#known-limitations) section. Here's an example:\n\n``` rust\n#![feature(llvm_asm)]\n#![no_main]\n#![no_std]\n\nextern crate panic_halt;\n\nuse cortex_m_rt::{entry, exception};\nuse spin::Mutex; // spin = \"0.5.0\"\n\nstatic TO: Mutex\u003c\u0026'static (dyn Foo + Sync)\u003e = Mutex::new(\u0026Bar);\n\n#[entry]\n#[inline(never)]\nfn main() -\u003e ! {\n    // trait object dispatch\n    (*TO.lock()).foo();\n\n    Quux.foo();\n\n    loop {}\n}\n\ntrait Foo {\n    // default implementation of this method\n    fn foo(\u0026self) -\u003e bool {\n        // spill variables onto the stack\n        unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5)) }\n\n        false\n    }\n}\n\nstruct Bar;\n\n// uses the default method implementation\nimpl Foo for Bar {}\n\nstruct Baz;\n\nimpl Foo for Baz {\n    // overrides the default method\n    fn foo(\u0026self) -\u003e bool {\n        unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5) \"r\"(6) \"r\"(7)) }\n\n        true\n    }\n}\n\nstruct Quux;\n\nimpl Quux {\n    // not a trait method!\n    #[inline(never)]\n    fn foo(\u0026self) -\u003e bool {\n        // NOTE(llvm_asm!) side effect to preserve function calls to this method\n        unsafe { llvm_asm!(\"NOP\" : : : : \"volatile\") }\n\n        false\n    }\n}\n\n// this handler can change the trait object at any time\n#[exception]\nfn SysTick() {\n    *TO.lock() = \u0026Baz;\n}\n```\n\nThe tool produces the following call graph:\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://japaric.github.io/cargo-call-stack/to.svg\"\u003e\n    \u003cimg alt=\"Dynamic dispatch\" src=\"assets/to.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nHere `i1 ({}*)` denotes dynamic dispatch of a method with (Rust) signature\n`fn(\u0026[mut] self) -\u003e bool`. The dynamic dispatch can invoke either `Bar.foo`,\nwhich boils down to the default method implementation (`app::Foo::foo` in the\ngraph), or `Baz.foo` (`\u003capp::Baz as app::Foo\u003e::foo` in the graph). In this case\nthe tool does *not* a draw an edge between `i1 ({}*)` and `Quux::foo`, whose\nsignature is also `fn(\u0026self) -\u003e bool`, so the call graph is accurate.\n\nIf you are wondering why we use LLVM notation for the function signature of the\ntrait method: that's because the tool operates on LLVM-IR where there's no\n`bool` primitive and most of Rust's type information has been erased.\n\n## Function pointers\n\n\u003e NOTE as of ~nightly-2022-09-20 llvm discards type information associated to pointers and uses\n\u003e opaque pointers in llvm-ir so functions whose signature include references (and raw pointers) will\n\u003e include more callee edges that will never occur in practice.\n\nIn *some* cases the tool can produce correct call graphs for programs that\ninvoke functions via pointers (e.g. `fn()`). Here's an example:\n\n``` rust\n#![feature(llvm_asm)]\n#![no_main]\n#![no_std]\n\nextern crate panic_halt;\n\nuse core::sync::atomic::{AtomicPtr, Ordering};\n\nuse cortex_m_rt::{entry, exception};\n\nstatic F: AtomicPtr\u003cfn() -\u003e bool\u003e = AtomicPtr::new(foo as *mut _);\n\n#[inline(never)]\n#[entry]\nfn main() -\u003e ! {\n    if let Some(f) = unsafe { F.load(Ordering::Relaxed).as_ref() } {\n        // call via function pointer\n        f();\n    }\n\n    loop {}\n}\n\nfn foo() -\u003e bool {\n    // spill variables onto the stack\n    unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5)) }\n\n    false\n}\n\nfn bar() -\u003e bool {\n    unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5) \"r\"(6) \"r\"(7)) }\n\n    true\n}\n\n// this handler can change the function pointer at any time\n#[exception]\nfn SysTick() {\n    F.store(bar as *mut _, Ordering::Relaxed);\n}\n```\n\nThe tool produces the following call graph:\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://japaric.github.io/cargo-call-stack/fn.svg\"\u003e\n    \u003cimg alt=\"Function pointers\" src=\"assets/fn.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nThe node `i1 ()*` represents a call via function pointer -- the LLVM type `i1\n()*` is equivalent to Rust's `fn() -\u003e bool`. This indirect call could invoke\n`foo` or `bar`, the only functions with signature `fn() -\u003e bool`.\n\n## Known limitations\n\n### Lossy type information\n\nTo reason about indirect function calls the tool uses the type information\navailable in the LLVM-IR of the program. This information does not exactly match\nRust's type information and leads to mislabeling of functions. For example,\nconsider this program:\n\n``` rust\n#![feature(llvm_asm)]\n#![no_main]\n#![no_std]\n\nextern crate panic_halt;\n\nuse core::{\n    ptr,\n    sync::atomic::{AtomicPtr, Ordering},\n};\n\nuse cortex_m_rt::{entry, exception};\n\nstatic F: AtomicPtr\u003cfn() -\u003e u32\u003e = AtomicPtr::new(foo as *mut _);\n\n#[inline(never)]\n#[entry]\nfn main() -\u003e ! {\n    if let Some(f) = unsafe { F.load(Ordering::Relaxed).as_ref() } {\n        // call via function pointer\n        f();\n    }\n\n    let x = baz();\n    unsafe {\n        // NOTE(volatile) used to preserve the return value of `baz`\n        ptr::read_volatile(\u0026x);\n    }\n\n    loop {}\n}\n\n// this handler can change the function pointer at any time\n#[exception]\nfn SysTick() {\n    F.store(bar as *mut _, Ordering::Relaxed);\n}\n\nfn foo() -\u003e u32 {\n    // spill variables onto the stack\n    unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5)) }\n\n    0\n}\n\nfn bar() -\u003e u32 {\n    1\n}\n\n#[inline(never)]\nfn baz() -\u003e i32 {\n    unsafe { llvm_asm!(\"\" : : \"r\"(0) \"r\"(1) \"r\"(2) \"r\"(3) \"r\"(4) \"r\"(5) \"r\"(6) \"r\"(7)) }\n\n    F.load(Ordering::Relaxed) as usize as i32\n}\n```\n\nThe tool produces the following call graph:\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://japaric.github.io/cargo-call-stack/lossy.svg\"\u003e\n    \u003cimg alt=\"Lossy types\" src=\"assets/lossy.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nNote that the node that represents the indirect function call has type `i32 ()*`\n(`fn() -\u003e i32`), not `u32 ()*`. The reason is that there's no `u32` type in\nLLVM, there are only signed integers. This leads the tool to wrongly add an edge\nbetween `i32 ()*` and `baz`. If the tool had Rust's type information then this\nedge would have not been added.\n\n### Opaque pointers in llvm-ir\n\nAs of ~nightly-2022-09-20 llvm is discarding type information associated to pointers and instead\nusing opaque pointers (`ptr`) in llvm-ir.\nThis results in even greater loss of type information, for example:\n\n- the signature of `fn f(x: \u0026i32) -\u003e bool` becomes `fn(ptr) -\u003e i1` in llvm-ir\n- the signature of `impl Foo { fn f(\u0026self) -\u003e bool }` also becomes `fn(ptr) -\u003e i1` in llvm-ir\n\nso the two are callee candidates for both a function pointer call with signature `fn(\u0026T) -\u003e bool`\nand for dynamic dispatch of a method with signature `fn(\u0026self) -\u003e bool`\n\n### Miscellaneous\n\nInline assembly breaks LLVM's stack usage analysis.\nLLVM does *not* consider inline assembly in its analysis and reports an incorrect number.\nIn this case, `cargo-call-stack` will use its own stack usage analysis based on machine code, which only supports the ARM Cortex-M architecture.\n\nHardware exceptions, like `SysTick` on Cortex-M devices, appear as disconnected nodes in the call graph.\nAt the moment, `cargo-call-stack` cannot compute the whole program maximum stack usage when exceptions are present.\n\nThe tool only supports ELF binaries because `-Z emit-stack-sizes` only supports the ELF format.\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or\n  http://www.apache.org/licenses/LICENSE-2.0)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\n### Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n","funding_links":[],"categories":["Vulnerability Assessment","Rust","Programming Languages"],"sub_categories":["Binary Analysis \u0026 Reversing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaparic%2Fcargo-call-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaparic%2Fcargo-call-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaparic%2Fcargo-call-stack/lists"}