From fbda78aefeaaa97182658bb81b5a6f215cc24b17 Mon Sep 17 00:00:00 2001
From: Michael Matz <matz@suse.de>
Date: Mon, 14 Apr 2014 02:53:11 +0200
Subject: [PATCH] Parse and emit hidden visibility

This adds parsing of (GCC compatible) visibility attribute
in order to mark selected global symbols as hidden.  The generated
.o files contain hidden symbols already, the TCC linker doesn't
yet do the right thing.
---
 tcc.h    | 12 ++++++++++--
 tccgen.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 tcctok.h |  2 ++
 3 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/tcc.h b/tcc.h
index dda2fc14..ab14b38b 100644
--- a/tcc.h
+++ b/tcc.h
@@ -375,7 +375,8 @@ struct Attribute {
         func_proto    : 1,
         mode          : 4,
         weak          : 1,
-        fill          : 10; // 10 bits left to fit well in union below
+        visibility    : 2,
+        fill          : 8; // 8 bits left to fit well in union below
 };
 
 /* GNUC attribute definition */
@@ -787,11 +788,18 @@ struct TCCState {
 #define VT_EXPORT  0x00008000  /* win32: data exported from dll */
 #define VT_WEAK    0x00010000  /* weak symbol */
 #define VT_TLS     0x00040000  /* thread-local storage */
+#define VT_VIS_SHIFT    19     /* shift for symbol visibility, overlapping
+				  bitfield values, because bitfields never
+				  have linkage and hence never have
+				  visibility.  */
+#define VT_VIS_SIZE      2     /* We have four visibilities.  */
+#define VT_VIS_MASK (((1 << VT_VIS_SIZE)-1) << VT_VIS_SHIFT)
 
 #define VT_STRUCT_SHIFT 19     /* shift for bitfield shift values (max: 32 - 2*6) */
 
+
 /* type mask (except storage) */
-#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_IMPORT | VT_EXPORT | VT_WEAK)
+#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_IMPORT | VT_EXPORT | VT_WEAK | VT_VIS_MASK)
 #define VT_TYPE (~(VT_STORAGE))
 
 /* token values */
diff --git a/tccgen.c b/tccgen.c
index 78f24aa8..9b008247 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -300,6 +300,29 @@ static void weaken_symbol(Sym *sym)
     }
 }
 
+static void apply_visibility(Sym *sym, CType *type)
+{
+    int vis = sym->type.t & VT_VIS_MASK;
+    int vis2 = type->t & VT_VIS_MASK;
+    if (vis == (STV_DEFAULT << VT_VIS_SHIFT))
+        vis = vis2;
+    else if (vis2 == (STV_DEFAULT << VT_VIS_SHIFT))
+        ;
+    else
+        vis = (vis < vis2) ? vis : vis2;
+    sym->type.t &= ~VT_VIS_MASK;
+    sym->type.t |= vis;
+
+    if (sym->c > 0) {
+        int esym_type;
+        ElfW(Sym) *esym;
+        
+        esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+	vis >>= VT_VIS_SHIFT;
+        esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) | vis;
+    }
+}
+
 /* ------------------------------------------------------------------------- */
 
 ST_FUNC void swap(int *p, int *q)
@@ -436,6 +459,13 @@ static Sym *external_sym(int v, CType *type, int r, char *asm_label)
         tcc_error("incompatible types for redefinition of '%s'", 
               get_tok_str(v, NULL));
     }
+    /* Merge some storage attributes.  */
+    if (type->t & VT_WEAK)
+        weaken_symbol(s);
+
+    if (type->t & VT_VIS_MASK)
+        apply_visibility(s, type);
+
     return s;
 }
 
@@ -2662,6 +2692,24 @@ static void parse_attribute(AttributeDef *ad)
             next();
             skip(')');
             break;
+	case TOK_VISIBILITY1:
+	case TOK_VISIBILITY2:
+            skip('(');
+            if (tok != TOK_STR)
+                expect("visibility(\"default|hidden|internal|protected\")");
+	    if (!strcmp (tokc.cstr->data, "default"))
+	        ad->a.visibility = STV_DEFAULT;
+	    else if (!strcmp (tokc.cstr->data, "hidden"))
+	        ad->a.visibility = STV_HIDDEN;
+	    else if (!strcmp (tokc.cstr->data, "internal"))
+	        ad->a.visibility = STV_INTERNAL;
+	    else if (!strcmp (tokc.cstr->data, "protected"))
+	        ad->a.visibility = STV_PROTECTED;
+	    else
+                expect("visibility(\"default|hidden|internal|protected\")");
+            next();
+            skip(')');
+            break;
         case TOK_ALIGNED1:
         case TOK_ALIGNED2:
             if (tok == '(') {
@@ -5656,6 +5704,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
         /* patch symbol weakness */
         if (type->t & VT_WEAK)
             weaken_symbol(sym);
+	apply_visibility(sym, type);
 #ifdef CONFIG_TCC_BCHECK
         /* handles bounds now because the symbol must be defined
            before for the relocation */
@@ -5801,6 +5850,7 @@ static void gen_function(Sym *sym)
     /* patch symbol weakness (this definition overrules any prototype) */
     if (sym->type.t & VT_WEAK)
         weaken_symbol(sym);
+    apply_visibility(sym, &sym->type);
     if (tcc_state->do_debug) {
         put_stabn(N_FUN, 0, 0, ind - func_ind);
     }
@@ -5934,6 +5984,8 @@ static int decl0(int l, int is_for_loop_init)
             if (ad.a.func_export)
                 type.t |= VT_EXPORT;
 #endif
+	    type.t |= ad.a.visibility << VT_VIS_SHIFT;
+
             if (tok == '{') {
                 if (l == VT_LOCAL)
                     tcc_error("cannot use local functions");
@@ -5973,6 +6025,11 @@ static int decl0(int l, int is_for_loop_init)
                     if (sym->type.t & VT_STATIC)
                         type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
 
+		    /* If the definition has no visibility use the
+		       one from prototype.  */
+		    if (! (type.t & VT_VIS_MASK))
+		        type.t |= sym->type.t & VT_VIS_MASK;
+
                     if (!is_compatible_types(&sym->type, &type)) {
                     func_error1:
                         tcc_error("incompatible types for redefinition of '%s'", 
@@ -6063,9 +6120,6 @@ static int decl0(int l, int is_for_loop_init)
                            extern */
                         sym = external_sym(v, &type, r, asm_label);
 
-                        if (type.t & VT_WEAK)
-                            weaken_symbol(sym);
-
                         if (ad.alias_target) {
                             Section tsec;
                             Elf32_Sym *esym;
diff --git a/tcctok.h b/tcctok.h
index c17711f3..d8c0344f 100644
--- a/tcctok.h
+++ b/tcctok.h
@@ -121,6 +121,8 @@
      DEF(TOK_DLLIMPORT, "dllimport")
      DEF(TOK_NORETURN1, "noreturn")
      DEF(TOK_NORETURN2, "__noreturn__")
+     DEF(TOK_VISIBILITY1, "visibility")
+     DEF(TOK_VISIBILITY2, "__visibility__")
      DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
      DEF(TOK_builtin_constant_p, "__builtin_constant_p")
      DEF(TOK_builtin_frame_address, "__builtin_frame_address")