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")