diff --git a/arm-link.c b/arm-link.c
index 9227b229..c75eeb70 100644
--- a/arm-link.c
+++ b/arm-link.c
@@ -16,7 +16,7 @@
 #define ELF_PAGE_SIZE  0x1000
 
 #define PCRELATIVE_DLLPLT 1
-#define RELOCATE_DLLPLT 0
+#define RELOCATE_DLLPLT 1
 
 enum float_abi {
     ARM_SOFTFP_FLOAT,
@@ -107,8 +107,6 @@ ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_
 
     /* when building a DLL, GOT entry accesses must be done relative to
        start of GOT (see x86_64 example above)  */
-    if (s1->output_type == TCC_OUTPUT_DLL)
-        tcc_error("DLLs unimplemented!");
 
     /* empty PLT: create PLT0 entry that push address of call site and
        jump to ld.so resolution routine (GOT + 8) */
@@ -127,13 +125,9 @@ ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_
         write32le(p,   0x4778); /* bx pc */
         write32le(p+2, 0x46c0); /* nop   */
     }
-    p = section_ptr_add(plt, 16);
-    /* Jump to GOT entry where ld.so initially put address of PLT0 */
-    write32le(p,   0xe59fc004); /* ldr ip, [pc, #4] */
-    write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */
-    write32le(p+8, 0xe59cf000); /* ldr pc, [ip] */
-    /* p + 12 contains offset to GOT entry once patched by relocate_plt */
-    write32le(p+12, got_offset);
+    p = section_ptr_add(plt, 12);
+    /* save GOT offset for relocate_plt */
+    write32le(p + 4, got_offset);
     return plt_offset;
 }
 
@@ -154,10 +148,13 @@ ST_FUNC void relocate_plt(TCCState *s1)
         write32le(s1->plt->data + 16, x - 16);
         p += 20;
         while (p < p_end) {
+	    unsigned off = x  + read32le(p + 4) + (s1->plt->data - p) + 4;
             if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */
                 p += 4;
-            add32le(p + 12, x + (s1->plt->data - p));
-            p += 16;
+            write32le(p, 0xe28fc600 | ((off >> 20) & 0xff));     // add ip, pc, #0xNN00000
+            write32le(p + 4, 0xe28cca00 | ((off >> 12) & 0xff)); // add ip, ip, #0xNN000
+            write32le(p + 8, 0xe5bcf000 | (off & 0xfff));	 // ldr pc, [ip, #0xNNN]!
+            p += 12;
         }
     }
 }
@@ -166,7 +163,7 @@ ST_FUNC void relocate_plt(TCCState *s1)
 void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
 {
     ElfW(Sym) *sym;
-    int sym_index;
+    int sym_index, esym_index;
 
     sym_index = ELFW(R_SYM)(rel->r_info);
     sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
@@ -348,6 +345,18 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t
                 (*(int *)ptr) |= x & 0x7fffffff;
             }
         case R_ARM_ABS32:
+            if (s1->output_type == TCC_OUTPUT_DLL) {
+                esym_index = get_sym_attr(s1, sym_index, 0)->dyn_index;
+                qrel->r_offset = rel->r_offset;
+                if (esym_index) {
+                    qrel->r_info = ELFW(R_INFO)(esym_index, R_ARM_ABS32);
+                    qrel++;
+                    return;
+                } else {
+                    qrel->r_info = ELFW(R_INFO)(0, R_ARM_RELATIVE);
+                    qrel++;
+                }
+            }
             *(int *)ptr += val;
             return;
         case R_ARM_REL32:
diff --git a/tccelf.c b/tccelf.c
index a9400624..fa673a7c 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -1028,7 +1028,8 @@ static void relocate_rel(TCCState *s1, Section *sr)
 static int prepare_dynamic_rel(TCCState *s1, Section *sr)
 {
     int count = 0;
-#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
+#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) || \
+    defined(TCC_TARGET_ARM) || defined(TCC_TARGET_ARM64)
     ElfW_Rel *rel;
     for_each_elem(sr, 0, rel, ElfW_Rel) {
         int sym_index = ELFW(R_SYM)(rel->r_info);
@@ -1046,6 +1047,8 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
         case R_X86_64_32:
         case R_X86_64_32S:
         case R_X86_64_64:
+#elif defined(TCC_TARGET_ARM)
+        case R_ARM_ABS32:
 #elif defined(TCC_TARGET_ARM64)
         case R_AARCH64_ABS32:
         case R_AARCH64_ABS64:
@@ -1779,7 +1782,11 @@ static int alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
         if ((s1->do_debug && s->sh_type != SHT_RELX) ||
             file_type == TCC_OUTPUT_OBJ ||
             (s->sh_flags & SHF_ALLOC) ||
-	    i == (s1->nb_sections - 1)) {
+	    i == (s1->nb_sections - 1)
+#ifdef TCC_TARGET_ARM
+            || s->sh_type == SHT_ARM_ATTRIBUTES
+#endif
+            ) {
             /* we output all sections if debug or object file */
             s->sh_size = s->data_offset;
         }
@@ -2339,6 +2346,42 @@ static void tidy_section_headers(TCCState *s1, int *sec_order)
 }
 #endif
 
+#ifdef TCC_TARGET_ARM
+static void create_arm_attribute_section(TCCState *s1)
+{
+   // Needed for DLL support.
+    static const unsigned char arm_attr[] = {
+        0x41,                            // 'A'
+        0x2c, 0x00, 0x00, 0x00,          // size 0x2c
+        'a', 'e', 'a', 'b', 'i', 0x00,   // "aeabi"
+        0x01, 0x22, 0x00, 0x00, 0x00,    // 'File Attributes', size 0x22
+        0x05, 0x36, 0x00,                // 'CPU_name', "6"
+        0x06, 0x06,                      // 'CPU_arch', 'v6'
+        0x08, 0x01,                      // 'ARM_ISA_use', 'Yes'
+        0x09, 0x01,                      // 'THUMB_ISA_use', 'Thumb-1'
+        0x0a, 0x02,                      // 'FP_arch', 'VFPv2'
+        0x12, 0x04,                      // 'ABI_PCS_wchar_t', 4
+        0x14, 0x01,                      // 'ABI_FP_denormal', 'Needed'
+        0x15, 0x01,                      // 'ABI_FP_exceptions', 'Needed'
+        0x17, 0x03,                      // 'ABI_FP_number_model', 'IEEE 754'
+        0x18, 0x01,                      // 'ABI_align_needed', '8-byte'
+        0x19, 0x01,                      // 'ABI_align_preserved', '8-byte, except leaf SP'
+        0x1a, 0x02,                      // 'ABI_enum_size', 'int'
+        0x1c, 0x01,                      // 'ABI_VFP_args', 'VFP registers'
+        0x22, 0x01                       // 'CPU_unaligned_access', 'v6'
+    };
+    Section *attr = new_section(s1, ".ARM.attributes", SHT_ARM_ATTRIBUTES, 0);
+    unsigned char *ptr = section_ptr_add(attr, sizeof(arm_attr));
+    attr->sh_addralign = 1;
+    memcpy(ptr, arm_attr, sizeof(arm_attr));
+    if (s1->float_abi != ARM_HARD_FLOAT) {
+        ptr[26] = 0x00; // 'FP_arch', 'No'
+        ptr[41] = 0x1e; // 'ABI_optimization_goals'
+        ptr[42] = 0x06; // 'Aggressive Debug'
+    }
+}
+#endif
+
 /* Output an elf, coff or binary file */
 /* XXX: suppress unneeded sections */
 static int elf_output_file(TCCState *s1, const char *filename)
@@ -2348,6 +2391,10 @@ static int elf_output_file(TCCState *s1, const char *filename)
     ElfW(Phdr) *phdr;
     Section *strsec, *interp, *dynamic, *dynstr;
 
+#ifdef TCC_TARGET_ARM
+    create_arm_attribute_section (s1);
+#endif
+
     file_type = s1->output_type;
     s1->nb_errors = 0;
     ret = -1;