{"id":15417121,"url":"https://github.com/mabezdev/xtensa-lld-repro","last_synced_at":"2026-05-20T06:05:14.870Z","repository":{"id":234514559,"uuid":"692139819","full_name":"MabezDev/xtensa-lld-repro","owner":"MabezDev","description":null,"archived":false,"fork":false,"pushed_at":"2023-09-15T16:42:35.000Z","size":177,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-18T07:44:53.466Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Assembly","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/MabezDev.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2023-09-15T16:42:21.000Z","updated_at":"2024-03-14T17:45:27.000Z","dependencies_parsed_at":"2024-04-19T17:24:17.145Z","dependency_job_id":"9fe4718d-f44c-484f-8b22-bda7b65d94dd","html_url":"https://github.com/MabezDev/xtensa-lld-repro","commit_stats":null,"previous_names":["mabezdev/xtensa-lld-repro"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MabezDev%2Fxtensa-lld-repro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MabezDev%2Fxtensa-lld-repro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MabezDev%2Fxtensa-lld-repro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MabezDev%2Fxtensa-lld-repro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MabezDev","download_url":"https://codeload.github.com/MabezDev/xtensa-lld-repro/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244267606,"owners_count":20425862,"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-10-01T17:14:41.268Z","updated_at":"2026-05-20T06:05:14.823Z","avatar_url":"https://github.com/MabezDev.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Running the test cases\n\n## working\n\n```rust\ncargo run --bin working --release\n```\n\n## broken\n```rust\ncargo run --bin broken --release\n```\n\n# Other data points\n\nIt is possible to get the broken example to run correcly with lld, by enabling LTO.\n\n```\n# Cargo.toml\n[profile.release]\nlto = true\n```\n\n# Suspicion: relocs\n\nStepping through each instruction once the interrupt fired brought me to a suspicious line of assembly. This is a snippet of the level2 interrupt handler from xtensa-lx-rt (full assembly available in data/broken.S):\n\n```asm\n40378e24 \u003c__default_naked_level_2_interrupt\u003e:\n40378e24:\t200110        \tor\ta0, a1, a1\n40378e27:\tffd112        \taddmi\ta1, a1, 0xffffff00\n40378e2a:\t036102        \ts32i\ta0, a1, 12\n40378e2d:\t49d100        \ts32e\ta0, a1, -12\n40378e30:\t03c200        \trsr.eps2\ta0\n40378e33:\t016102        \ts32i\ta0, a1, 4\n40378e36:\t03b200        \trsr.epc2\ta0\n40378e39:\t006102        \ts32i\ta0, a1, 0\n40378e3c:\t49c100        \ts32e\ta0, a1, -16\n40378e3f:\t03d200        \trsr.excsave2\ta0\n40378e42:\t026102        \ts32i\ta0, a1, 8\n40378e45:\tffd485        \tcall0\t40378b90 \u003csave_context\u003e\n\n; this is where we load the level 2 hal handler which is stored in IRAM\n; as we can see though, its pointing somewhere else\n; infact 0x40339194 is not even in IRAM, its ROM code\n40378e48:\t00d301        \tl32r\ta0, 40339194 \u003crom_i2c_writeReg_Mask+0x333428\u003e\n\n40378e4b:\t13e600        \twsr.ps\ta0\n40378e4e:\t002010        \trsync\n40378e51:\t02a062        \tmovi\ta6, 2\n40378e54:\t207110        \tor\ta7, a1, a1\n40378e57:\tff6b95        \tcall4\t40378510 \u003c__level_1_interrupt\u003e\n40378e5a:\tffe3c5        \tcall0\t40378c98 \u003crestore_context\u003e\n40378e5d:\t012102        \tl32i\ta0, a1, 4\n40378e60:\t13c200        \twsr.eps2\ta0\n40378e63:\t002102        \tl32i\ta0, a1, 0\n40378e66:\t13b200        \twsr.epc2\ta0\n40378e69:\t022102        \tl32i\ta0, a1, 8\n40378e6c:\t032112        \tl32i\ta1, a1, 12\n40378e6f:\t002010        \trsync\n40378e72:\t003210        \trfi\t2\n40378e75:\t0041f0        \tbreak\t1, 15\n```\n\n## reloc output\n\nFull output available in data/broken-relocs.x, here is the relevant relocs for our level 2 handler.\n\n```\nRELOCATION RECORDS FOR [.rwtext]:\nOFFSET   TYPE              VALUE \n\u003c!-- snipped --\u003e\n000002b5 R_XTENSA_SLOT0_OP  save_context\n000002b8 R_XTENSA_SLOT0_OP  .rwtext.literal+0x0000000c\n000002c7 R_XTENSA_SLOT0_OP  __level_2_interrupt\n000002ca R_XTENSA_SLOT0_OP  restore_context\n\u003c!-- snipped --\u003e\n```\n\nWe can clearly see that the save_context and restore_context relocations worked, but when we tried to relocate __level_2_interrupt it failed.\n\nMy initial hunch was that at the time that LLD relocates __level_2_interrupt is when __level_2_interrupt is assigned to the default handler ([see here](https://github.com/esp-rs/xtensa-lx-rt/blob/b9e653db6eb2d72ff44f141bf453967e0b7aa273/exception-esp32.x.jinja#L8)), but later in the hal we override this handler [here](https://github.com/MabezDev/esp-hal/blob/1835b7fed2dfd24e6f421128df2a66c31a56f86c/esp-hal-common/src/interrupt/xtensa.rs#L368-L371C27). However, this doesn't seem to be the case.\n\nI added some logging to LLD `std::cout \u003c\u003c \"Relocating  \" \u003c\u003c rel.sym-\u003egetName().str() \u003c\u003c \" dest: \" \u003c\u003c dest \u003c\u003c \"val: \" \u003c\u003c val \u003c\u003c \"isDefined(): \" \u003c\u003c rel.sym-\u003eisDefined() \u003c\u003c \"\\n\";`, the output is in output in data/relocation-log-lld.txt.\n\nIf we look at the log output in data/relocation-log-lld.txt, there is only one entry for __level_2_interrupt. This on its own is quite suspect as inside __level_2_interrupt there is more than one relocation slot. If we plug the numbers from the log file for that relocation and calculate the PC like LLD does we get a pc of `0x40378510`, which means LLD is only doing the reloc for the call4 instruction inside __level_2_interrupt, which is really odd.\n\n```\n40378e57:\tff6b95        \tcall4\t40378510 \u003c__level_1_interrupt\u003e\n```\n\n# Helpful commands\n\nReloc info is discarded in the final elf, but you can find it by extracting it from the relevant rlibs. For example\n\n`xtensa-esp32s3-elf-objdump -r target/xtensa-esp32s3-none-elf/release/deps/libxtensa_lx_rt-a98b0cd449d58097.rlib \u003e relocs.txt`","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmabezdev%2Fxtensa-lld-repro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmabezdev%2Fxtensa-lld-repro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmabezdev%2Fxtensa-lld-repro/lists"}