{"id":297,"date":"2025-12-22T16:27:00","date_gmt":"2025-12-22T16:27:00","guid":{"rendered":"https:\/\/haco.club\/?p=297"},"modified":"2025-12-22T16:27:00","modified_gmt":"2025-12-22T16:27:00","slug":"ldr-vs-ldur-in-aarch64","status":"publish","type":"post","link":"https:\/\/haco.club\/?p=297","title":{"rendered":"LDR vs. LDUR in AArch64"},"content":{"rendered":"\n<p>In AArch64 (ARMv8-A), the main difference between <strong><code>LDR<\/code><\/strong> and <strong><code>LDUR<\/code><\/strong> is how they handle the <strong>immediate offset<\/strong> from the base address.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>LDR<\/code> (Load Register):<\/strong> Uses a <strong>scaled<\/strong> positive immediate offset. It is the standard instruction for loading data from validly aligned structures and arrays.<\/li>\n\n\n\n<li><strong><code>LDUR<\/code> (Load Register Unscaled):<\/strong> Uses an <strong>unscaled<\/strong> signed immediate offset. It is used for accessing data at negative offsets or unaligned addresses that <code>LDR<\/code> cannot reach.<\/li>\n<\/ul>\n\n\n\n<p>Here is a detailed breakdown of the differences:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Offset Scaling<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>LDR<\/code> (Scaled):<\/strong> The immediate value you provide is automatically multiplied (scaled) by the size of the data you are loading (4 for 32-bit, 8 for 64-bit).\n<ul class=\"wp-block-list\">\n<li><em>Example:<\/em> <code>LDR X0, [X1, #8]<\/code> \u2014 The encoded immediate is actually <code>1<\/code>. The CPU calculates <code>Base + (1 * 8)<\/code>.<\/li>\n\n\n\n<li><em>Constraint:<\/em> The offset <strong>must<\/strong> be a multiple of the data size (e.g., you cannot use an offset of <code>#3<\/code> for a 64-bit load).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong><code>LDUR<\/code> (Unscaled):<\/strong> The immediate value is treated exactly as written, in bytes.\n<ul class=\"wp-block-list\">\n<li><em>Example:<\/em> <code>LDUR X0, [X1, #3]<\/code> \u2014 The encoded immediate is <code>3<\/code>. The CPU calculates <code>Base + 3<\/code>.<\/li>\n\n\n\n<li><em>Benefit:<\/em> The offset does <strong>not<\/strong> need to be a multiple of the data size, allowing access to unaligned data.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Offset Range &amp; Sign<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>LDR<\/code>:<\/strong> Supports a large, <strong>unsigned (positive only)<\/strong> range.\n<ul class=\"wp-block-list\">\n<li><strong>32-bit load:<\/strong> 0 to 16,380 bytes (must be multiple of 4).<\/li>\n\n\n\n<li><strong>64-bit load:<\/strong> 0 to 32,760 bytes (must be multiple of 8).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong><code>LDUR<\/code>:<\/strong> Supports a small, <strong>signed<\/strong> range.\n<ul class=\"wp-block-list\">\n<li><strong>Range:<\/strong> -256 to +255 bytes.<\/li>\n\n\n\n<li><em>Benefit:<\/em> This allows you to load data stored <em>before<\/em> the base register address (negative offsets).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Assembler &#8220;Magic&#8221; (Aliasing)<\/h3>\n\n\n\n<p>In practice, you rarely need to type <code>LDUR<\/code> manually. Most AArch64 assemblers are smart enough to choose the correct opcode for you based on the offset you write.<\/p>\n\n\n\n<p>If you write:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>LDR X0, &#91;X1, #-8]   ; Negative offset<\/code><\/pre>\n\n\n\n<p>The assembler sees the negative sign, realizes the standard <code>LDR<\/code> encoding cannot handle it, and automatically compiles it into the machine code for <code>LDUR<\/code>.<\/p>\n\n\n\n<p>Similarly, if you write:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>LDR X0, &#91;X1, #3]    ; Unaligned offset<\/code><\/pre>\n\n\n\n<p>The assembler detects that <code>#3<\/code> is not a multiple of 8 (for a 64-bit load) and emits an <code>LDUR<\/code> instruction.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Summary Table<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Feature<\/th><th class=\"has-text-align-left\" data-align=\"left\">LDR (Immediate)<\/th><th class=\"has-text-align-left\" data-align=\"left\">LDUR<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Full Name<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">Load Register<\/td><td class=\"has-text-align-left\" data-align=\"left\">Load Register (<strong>Unscaled<\/strong>)<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Offset Type<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\"><strong>Scaled<\/strong> (Multiplied by transfer size)<\/td><td class=\"has-text-align-left\" data-align=\"left\"><strong>Unscaled<\/strong> (Raw byte count)<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Offset Sign<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\"><strong>Positive<\/strong> (Unsigned)<\/td><td class=\"has-text-align-left\" data-align=\"left\"><strong>Signed<\/strong> (Positive or Negative)<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Offset Range<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">Large (0 to ~32KB)<\/td><td class=\"has-text-align-left\" data-align=\"left\">Small (-256 to +255 bytes)<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Alignment<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">Offset must be aligned to data size<\/td><td class=\"has-text-align-left\" data-align=\"left\">Offset can be unaligned<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Primary Use<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\">Array\/Struct access<\/td><td class=\"has-text-align-left\" data-align=\"left\">Negative offsets, unaligned fields<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>Note:<\/strong> Do not confuse <code>LDUR<\/code> (Unscaled) with <strong><code>LDTR<\/code><\/strong>, which stands for &#8220;Load Register <strong>Unprivileged<\/strong>.&#8221; <code>LDTR<\/code> is used to perform a load as if the CPU were in a lower exception level (e.g., User mode), usually for permission checking in OS kernels.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><code>STUR<\/code> (Store Register Unscaled)<\/h3>\n\n\n\n<p>It is the direct counterpart to <code>LDUR<\/code>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Purpose:<\/strong> Stores a register to memory using an <strong>unscaled<\/strong>, signed immediate offset.<\/li>\n\n\n\n<li><strong>When to use:<\/strong> It is used when you need to store data at an address offset that is <strong>not a multiple of the data size<\/strong> (unaligned) or is <strong>negative<\/strong>.<\/li>\n\n\n\n<li><strong>Assembler behavior:<\/strong> Just like with <code>LDR\/LDUR<\/code>, you rarely type <code>STUR<\/code> manually. If you write <code>STR X0, [X1, #-5]<\/code>, the assembler automatically converts it to <code>STUR<\/code> because standard <code>STR<\/code> cannot handle unaligned or negative immediate offsets in that specific way.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In AArch64 (ARMv8-A), the main difference between LDR and LDUR [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42],"tags":[13,30],"class_list":["post-297","post","type-post","status-publish","format-standard","hentry","category-knowledge-base","tag-aarch64","tag-binary"],"_links":{"self":[{"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts\/297","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=297"}],"version-history":[{"count":1,"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts\/297\/revisions"}],"predecessor-version":[{"id":298,"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts\/297\/revisions\/298"}],"wp:attachment":[{"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=297"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=297"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=297"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}