From d6bc6b1d8abc5af86ececa9d490a1a1da03dbf02 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sun, 22 Jun 2025 20:50:24 -0500 Subject: [PATCH] Add ARM64 BCJ decoder, from Lassee Collin's xz-embedded commit 89094f05f02b --- toys/pending/xzcat.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/toys/pending/xzcat.c b/toys/pending/xzcat.c index f3417bd7..e1dbfc6e 100644 --- a/toys/pending/xzcat.c +++ b/toys/pending/xzcat.c @@ -162,7 +162,8 @@ struct xz_dec_bcj { BCJ_IA64 = 6, /* Big or little endian */ BCJ_ARM = 7, /* Little endian only */ BCJ_ARMTHUMB = 8, /* Little endian only */ - BCJ_SPARC = 9 /* Big or little endian */ + BCJ_SPARC = 9, /* Big or little endian */ + BCJ_ARM64 = 10 /* AArch64 */ } type; /* Return value of the next filter in the chain. We need to preserve @@ -453,6 +454,43 @@ static size_t bcj_sparc(struct xz_dec_bcj *s, char *buf, size_t size) return i; } +static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size) +{ + size_t i; + uint32_t instr; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 4) { + instr = get_unaligned_le32(buf + i); + + if ((instr >> 26) == 0x25) { + /* BL instruction */ + addr = instr - ((s->pos + (uint32_t)i) >> 2); + instr = 0x94000000 | (addr & 0x03FFFFFF); + put_unaligned_le32(instr, buf + i); + + } else if ((instr & 0x9F000000) == 0x90000000) { + /* ADRP instruction */ + addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC); + + /* Only convert values in the range +/-512 MiB. */ + if ((addr + 0x020000) & 0x1C0000) + continue; + + addr -= (s->pos + (uint32_t)i) >> 12; + + instr &= 0x9000001F; + instr |= (addr & 3) << 29; + instr |= (addr & 0x03FFFC) << 3; + instr |= (0U - (addr & 0x020000)) & 0xE00000; + + put_unaligned_le32(instr, buf + i); + } + } + + return i; +} + /* * Apply the selected BCJ filter. Update *pos and s->pos to match the amount * of data that got filtered. @@ -488,6 +526,9 @@ static void bcj_apply(struct xz_dec_bcj *s, case BCJ_SPARC: filtered = bcj_sparc(s, buf, size); break; + case BCJ_ARM64: + filtered = bcj_arm64(s, buf, size); + break; default: /* Never reached but silence compiler warnings. */ filtered = 0; @@ -639,6 +680,7 @@ enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, struct xz_dec_lzma2 *lzma2, */ enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, char id) { + if (idBCJ_ARM64) return XZ_OPTIONS_ERROR; s->type = id; s->ret = XZ_OK; s->pos = 0; -- 2.39.5