diff --git a/tcc.h b/tcc.h
index ce6407eb..f28b9d86 100644
--- a/tcc.h
+++ b/tcc.h
@@ -409,7 +409,7 @@ extern long double strtold (const char *__nptr, char **__endptr);
 # define ElfW_Rel ElfW(Rela)
 # define SHT_RELX SHT_RELA
 # define REL_SECTION_FMT ".rela%s"
-# define RELPLT_SECTION_FMT ".rel.plt"
+# define RELPLT_SECTION_FMT ".rela.plt"
 #else
 # define ELFCLASSW ELFCLASS32
 # define ElfW(type) Elf##32##_##type
@@ -872,7 +872,7 @@ struct TCCState {
     Section *plt;
 
     /* predefined sections */
-    Section *text_section, *data_section, *bss_section;
+    Section *text_section, *data_section, *data_ro_section, *bss_section;
     Section *common_section;
     Section *cur_text_section; /* current section where function code is generated */
 #ifdef CONFIG_TCC_BCHECK
@@ -1817,6 +1817,7 @@ ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename)
 
 #define text_section        TCC_STATE_VAR(text_section)
 #define data_section        TCC_STATE_VAR(data_section)
+#define data_ro_section     TCC_STATE_VAR(data_ro_section)
 #define bss_section         TCC_STATE_VAR(bss_section)
 #define common_section      TCC_STATE_VAR(common_section)
 #define cur_text_section    TCC_STATE_VAR(cur_text_section)
diff --git a/tccelf.c b/tccelf.c
index e31eb9f7..fcc550f3 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -58,6 +58,8 @@ ST_FUNC void tccelf_new(TCCState *s)
     /* create standard sections */
     text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
     data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
+    /* create ro data section (make ro after relocation done with GNU_RELRO) */
+    data_ro_section = new_section(s, ".data.ro", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
     bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
     common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
     common_section->sh_num = SHN_COMMON;
@@ -79,11 +81,11 @@ ST_FUNC void tccelf_new(TCCState *s)
 ST_FUNC void tccelf_bounds_new(TCCState *s)
 {
     TCCState *s1 = s;
-    /* create bounds sections */
+    /* create bounds sections (make ro after relocation done with GNU_RELRO) */
     bounds_section = new_section(s, ".bounds",
-                                 SHT_PROGBITS, SHF_ALLOC);
+                                 SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
     lbounds_section = new_section(s, ".lbounds",
-                                  SHT_PROGBITS, SHF_ALLOC);
+                                  SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
 }
 #endif
 
@@ -1043,6 +1045,16 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
         case R_386_PC32:
 #elif defined(TCC_TARGET_X86_64)
         case R_X86_64_PC32:
+	{
+	    ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+
+	    /* support __dso_handle in atexit() */
+	    if (sym->st_shndx != SHN_UNDEF &&
+		ELFW(ST_VISIBILITY)(sym->st_other) == STV_HIDDEN) {
+                rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PLT32);
+	        break;
+	    }
+	}
 #elif defined(TCC_TARGET_ARM64)
         case R_AARCH64_PREL32:
 #endif
@@ -1828,11 +1840,20 @@ struct dyn_inf {
     addr_t rel_size;
 };
 
+/* Info for GNU_RELRO */
+struct ro_inf {
+   addr_t sh_offset;
+   addr_t sh_addr;
+   addr_t sh_size;
+};
+
 /* Assign sections to segments and decide how are sections laid out when loaded
    in memory. This function also fills corresponding program headers. */
-static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
+static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr,
+			   int phnum, int phfill,
                            Section *interp, Section* strsec,
-                           struct dyn_inf *dyninf, int *sec_order)
+                           struct dyn_inf *dyninf, struct ro_inf *roinf,
+			   int *sec_order)
 {
     int i, sh_order_index, file_offset;
     Section *s;
@@ -1883,7 +1904,10 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
         /* dynamic relocation table information, for .dynamic section */
         dyninf->rel_addr = dyninf->rel_size = 0;
 
-        for(j = 0; j < (phnum == 6 ? 3 : 2); j++) {
+        /* read only segment mapping for GNU_RELRO */
+	roinf->sh_offset = roinf->sh_addr = roinf->sh_size = 0;
+
+        for(j = 0; j < phfill; j++) {
 	    Section *relocplt = s1->got ? s1->got->relocplt : NULL;
 
             ph->p_type = j == 2 ? PT_TLS : PT_LOAD;
@@ -1898,7 +1922,7 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
                info about the layout. We do the following ordering: interp,
                symbol tables, relocations, progbits, nobits */
             /* XXX: do faster and simpler sorting */
-            for(k = 0; k < 6; k++) {
+            for(k = 0; k < 7; k++) {
                 for(i = 1; i < s1->nb_sections; i++) {
                     s = s1->sections[i];
                     /* compute if section should be included */
@@ -1930,12 +1954,17 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
                         else if (k != 3 && s == relocplt)
                             continue;
                     } else if (s->sh_type == SHT_NOBITS) {
-                        if (k != 5)
+                        if (k != 6)
                             continue;
-                    } else {
+                    } else if (s == data_ro_section ||
+			       s == bounds_section ||
+			       s == lbounds_section) {
                         if (k != 4)
                             continue;
-                    }
+                    } else {
+                        if (k != 5)
+                            continue;
+		    }
                     sec_order[sh_order_index++] = i;
 
                     /* section matches: we align it and add its size */
@@ -1956,8 +1985,17 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
                     if (s->sh_type == SHT_RELX && s != relocplt) {
                         if (dyninf->rel_size == 0)
                             dyninf->rel_addr = addr;
-                        dyninf->rel_size += s->sh_size;
+                        dyninf->rel_size = (addr - dyninf->rel_addr) + s->sh_size;
                     }
+                    if (s == data_ro_section ||
+			s == bounds_section ||
+			s == lbounds_section) {
+                        if (roinf->sh_size == 0) {
+                            roinf->sh_offset = s->sh_offset;
+                            roinf->sh_addr = s->sh_addr;
+			}
+                        roinf->sh_size = (addr - roinf->sh_addr) + s->sh_size;
+		    }
                     addr += s->sh_size;
                     if (s->sh_type != SHT_NOBITS)
                         file_offset += s->sh_size;
@@ -2018,7 +2056,7 @@ static void put_dt(Section *dynamic, int dt, addr_t val)
 }
 
 static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
-                                 Section *dynamic, Section *note)
+                                 Section *dynamic, Section *note, struct ro_inf *roinf)
 {
     ElfW(Phdr) *ph;
 
@@ -2046,7 +2084,7 @@ static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
     }
 
     if (note) {
-        ph = &phdr[phnum - 2];
+        ph = &phdr[phnum - 2 - (roinf != NULL)];
 
         ph->p_type = PT_NOTE;
         ph->p_offset = note->sh_offset;
@@ -2060,7 +2098,7 @@ static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
 
     /* if dynamic section, then add corresponding program header */
     if (dynamic) {
-        ph = &phdr[phnum - 1];
+        ph = &phdr[phnum - 1 - (roinf != NULL)];
 
         ph->p_type = PT_DYNAMIC;
         ph->p_offset = dynamic->sh_offset;
@@ -2071,6 +2109,19 @@ static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
         ph->p_flags = PF_R | PF_W;
         ph->p_align = dynamic->sh_addralign;
     }
+
+    if (roinf) {
+        ph = &phdr[phnum - 1];
+
+        ph->p_type = PT_GNU_RELRO;
+        ph->p_offset = roinf->sh_offset;
+        ph->p_vaddr = roinf->sh_addr;
+        ph->p_paddr = ph->p_vaddr;
+        ph->p_filesz = roinf->sh_size;
+        ph->p_memsz = roinf->sh_size;
+        ph->p_flags = PF_R;
+        ph->p_align = 1;
+    }
 }
 
 /* Fill the dynamic section with tags describing the address and size of
@@ -2440,8 +2491,9 @@ static Section *create_bsd_note_section(TCCState *s1,
 /* XXX: suppress unneeded sections */
 static int elf_output_file(TCCState *s1, const char *filename)
 {
-    int ret, phnum, shnum, file_type, file_offset, *sec_order;
+    int i, ret, phnum, phfill, shnum, file_type, file_offset, *sec_order;
     struct dyn_inf dyninf = {0};
+    struct ro_inf roinf, *roinf_use = NULL;
     ElfW(Phdr) *phdr;
     Section *strsec, *interp, *dynamic, *dynstr, *note = NULL;
 
@@ -2489,6 +2541,8 @@ static int elf_output_file(TCCState *s1, const char *filename)
             s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
                                     ".dynstr",
                                     ".hash", SHF_ALLOC);
+	    /* Number of local symbols (readelf complains if not set) */
+	    s1->dynsym->sh_info = 1;
             dynstr = s1->dynsym->link;
             /* add dynamic section */
             dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC,
@@ -2556,22 +2610,31 @@ static int elf_output_file(TCCState *s1, const char *filename)
     }
 #endif
 
+    for (i = 1; i < s1->nb_sections &&
+                !(s1->sections[i]->sh_flags & SHF_TLS); i++);
+    phfill = 2 + (i < s1->nb_sections);
+
     /* compute number of program headers */
     if (file_type == TCC_OUTPUT_OBJ)
-        phnum = 0;
+        phnum = phfill = 0;
     else if (file_type == TCC_OUTPUT_DLL)
         phnum = 3;
     else if (s1->static_link)
         phnum = 2;
     else {
-        int i;
-        for (i = 1; i < s1->nb_sections &&
-                    !(s1->sections[i]->sh_flags & SHF_TLS); i++);
-        phnum = i < s1->nb_sections ? 6 : 5;
+        phnum = 5 + (i < s1->nb_sections);
     }
 
     phnum += note != NULL;
 
+#if !TARGETOS_FreeBSD && !TARGETOS_NetBSD && !defined(__APPLE__) && !defined(_WIN32)
+    /* GNU_RELRO */
+    if (file_type != TCC_OUTPUT_OBJ) {
+	phnum++;
+        roinf_use = &roinf;
+    }
+#endif
+
     /* allocate program segment headers */
     phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));
 
@@ -2583,14 +2646,14 @@ static int elf_output_file(TCCState *s1, const char *filename)
     sec_order[0] = 0;
 
     /* compute section to program header mapping */
-    file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf,
-                                  sec_order);
+    file_offset = layout_sections(s1, phdr, phnum, phfill, interp, strsec,
+				  &dyninf, &roinf, sec_order);
 
 #ifndef ELF_OBJ_ONLY
     /* Fill remaining program header and finalize relocation related to dynamic
        linking. */
     if (file_type != TCC_OUTPUT_OBJ) {
-        fill_unloadable_phdr(phdr, phnum, interp, dynamic, note);
+        fill_unloadable_phdr(phdr, phnum, interp, dynamic, note, roinf_use);
         if (dynamic) {
             ElfW(Sym) *sym;
             dynamic->data_offset = dyninf.data_offset;
diff --git a/tccgen.c b/tccgen.c
index b4db04e9..0c3de487 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -8120,8 +8120,10 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
         /* allocate symbol in corresponding section */
         sec = ad->section;
         if (!sec) {
-            if (has_init)
-                sec = data_section;
+            if (type->t & VT_CONSTANT)
+		sec = data_ro_section;
+            else if (has_init)
+		sec = data_section;
             else if (tcc_state->nocommon)
                 sec = bss_section;
         }
diff --git a/tests/Makefile b/tests/Makefile
index 2aac24c0..9e804271 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -64,17 +64,11 @@ endif
 ifeq ($(TARGETOS),OpenBSD)
 dlltest: CFLAGS+=-fno-stack-protector
 endif
-ifeq ($(TARGETOS),FreeBSD)
+ifneq (,$(filter FreeBSD NetBSD,$(TARGETOS)))
   # test3 has dlsym problems
   TESTS := $(filter-out test3,$(TESTS))
   TESTS += test1
 endif
-ifeq ($(TARGETOS),NetBSD)
-  # test3 has dlsym problems
-  # dlltest does not allow text relocations
-  TESTS := $(filter-out test3 dlltest,$(TESTS))
-  TESTS += test1
-endif
 
 RUN_TCC = $(NATIVE_DEFINES) -run $(TOPSRC)/tcc.c $(TCCFLAGS)
 DISAS = objdump -d
diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile
index f0744713..1eaaa756 100644
--- a/tests/tests2/Makefile
+++ b/tests/tests2/Makefile
@@ -53,9 +53,6 @@ ifneq (,$(filter OpenBSD FreeBSD NetBSD,$(TARGETOS)))
  SKIP += 114_bound_signal.test # libc problem signal/fork
  SKIP += 116_bound_setjmp2.test # No TLS_FUNC/TLS_VAR in bcheck.c
 endif
-ifeq ($(TARGETOS),NetBSD)
-  SKIP += 113_btdll.test # text relocations
-endif
 
 # Some tests might need arguments
 ARGS =
diff --git a/x86_64-gen.c b/x86_64-gen.c
index 57e24718..9f9bf2c3 100644
--- a/x86_64-gen.c
+++ b/x86_64-gen.c
@@ -674,8 +674,8 @@ static void gen_bounds_prolog(void)
     func_bound_offset = lbounds_section->data_offset;
     func_bound_ind = ind;
     func_bound_add_epilog = 0;
-    o(0xb848 + TREG_FASTCALL_1 * 0x100); /*lbound section pointer */
-    gen_le64 (0);
+    o(0x0d8d48 + ((TREG_FASTCALL_1 == TREG_RDI) * 0x300000)); /*lbound section pointer */
+    gen_le32 (0);
     oad(0xb8, 0); /* call to function */
 }
 
@@ -700,17 +700,17 @@ static void gen_bounds_epilog(void)
     if (offset_modified) {
         saved_ind = ind;
         ind = func_bound_ind;
-        greloca(cur_text_section, sym_data, ind + 2, R_X86_64_64, 0);
-        ind = ind + 10;
+        greloca(cur_text_section, sym_data, ind + 3, R_X86_64_PC32, -4);
+        ind = ind + 7;
         gen_bounds_call(TOK___bound_local_new);
         ind = saved_ind;
     }
 
     /* generate bound check local freeing */
     o(0x5250); /* save returned value, if any */
-    greloca(cur_text_section, sym_data, ind + 2, R_X86_64_64, 0);
-    o(0xb848 + TREG_FASTCALL_1 * 0x100); /* mov xxx, %rcx/di */
-    gen_le64 (0);
+    greloca(cur_text_section, sym_data, ind + 3, R_X86_64_PC32, -4);
+    o(0x0d8d48 + ((TREG_FASTCALL_1 == TREG_RDI) * 0x300000)); /* lea xxx(%rip), %rcx/rdi */
+    gen_le32 (0);
     gen_bounds_call(TOK___bound_local_delete);
     o(0x585a); /* restore returned value, if any */
 }