feat: Custom attributes for extern fn blocks

chore: allow both wasm_import and raw-dylib

feat: Refactor specific attribute solution to general solution

chore: rustfmt

fix: wasm import module binding order

fix: Use different test attribute
diff --git a/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs-many.rs b/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs-many.rs
new file mode 100644
index 0000000..5aa9e45
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs-many.rs
@@ -0,0 +1,6 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[allow(dead_code)]
+#[cfg_attr(not(windows), link(wasm_import_module = "test-module"))]
+unsafe extern "C" {
+    pub fn test_function();
+}
diff --git a/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs-wasm.rs b/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs-wasm.rs
new file mode 100644
index 0000000..388594b
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs-wasm.rs
@@ -0,0 +1,6 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[link(wasm_import_module = "test-module")]
+#[allow(dead_code)]
+unsafe extern "C" {
+    pub fn test_function();
+}
diff --git a/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs.rs b/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs.rs
new file mode 100644
index 0000000..657cec1
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/extern-fn-block-attrs.rs
@@ -0,0 +1,5 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[allow(dead_code)]
+unsafe extern "C" {
+    pub fn test_function();
+}
diff --git a/bindgen-tests/tests/headers/extern-fn-block-attrs-many.h b/bindgen-tests/tests/headers/extern-fn-block-attrs-many.h
new file mode 100644
index 0000000..0de6eeb
--- /dev/null
+++ b/bindgen-tests/tests/headers/extern-fn-block-attrs-many.h
@@ -0,0 +1,3 @@
+// bindgen-flags: --extern-fn-block-attrs '#[allow(dead_code)]' --extern-fn-block-attrs '#[cfg_attr(not(windows), link(wasm_import_module = "test-module"))]'
+
+void test_function();
\ No newline at end of file
diff --git a/bindgen-tests/tests/headers/extern-fn-block-attrs-wasm.h b/bindgen-tests/tests/headers/extern-fn-block-attrs-wasm.h
new file mode 100644
index 0000000..2f475f1
--- /dev/null
+++ b/bindgen-tests/tests/headers/extern-fn-block-attrs-wasm.h
@@ -0,0 +1,3 @@
+// bindgen-flags: --extern-fn-block-attrs '#[allow(dead_code)]' --wasm-import-module-name test-module
+
+void test_function();
\ No newline at end of file
diff --git a/bindgen-tests/tests/headers/extern-fn-block-attrs.h b/bindgen-tests/tests/headers/extern-fn-block-attrs.h
new file mode 100644
index 0000000..26480e2
--- /dev/null
+++ b/bindgen-tests/tests/headers/extern-fn-block-attrs.h
@@ -0,0 +1,3 @@
+// bindgen-flags: --extern-fn-block-attrs '#[allow(dead_code)]'
+
+void test_function();
\ No newline at end of file
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 88a5416..eee4f46 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -4665,13 +4665,19 @@
             }
         }
 
-        // Unfortunately this can't piggyback on the `attributes` list because
-        // the #[link(wasm_import_module)] needs to happen before the `extern
-        // "C"` block. It doesn't get picked up properly otherwise
-        let wasm_link_attribute =
-            ctx.options().wasm_import_module_name.as_ref().map(|name| {
-                quote! { #[link(wasm_import_module = #name)] }
+        let mut block_attributes = quote! {};
+        for attr in &ctx.options().extern_fn_block_attrs {
+            let parsed_attr = proc_macro2::TokenStream::from_str(attr).unwrap_or_else(
+                |err| {
+                    panic!(
+                        "Error parsing extern fn block attribute `{attr}`: {err}"
+                    )
+                },
+            );
+            block_attributes.extend(quote! {
+                #parsed_attr
             });
+        }
 
         let should_wrap = is_internal &&
             ctx.options().wrap_static_fns &&
@@ -4725,7 +4731,7 @@
             .then(|| quote!(unsafe));
 
         let tokens = quote! {
-            #wasm_link_attribute
+            #block_attributes
             #safety extern #abi {
                 #(#attributes)*
                 pub fn #ident ( #( #args ),* ) #ret;
diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs
index 4334e9c..972b487 100644
--- a/bindgen/options/cli.rs
+++ b/bindgen/options/cli.rs
@@ -423,6 +423,9 @@
     /// The NAME to be used in a #[link(wasm_import_module = ...)] statement
     #[arg(long, value_name = "NAME")]
     wasm_import_module_name: Option<String>,
+    /// Attributes to apply to the extern function block.
+    #[arg(long, value_name = "ATTRS")]
+    extern_fn_block_attrs: Vec<String>,
     /// Use dynamic loading mode with the given library NAME.
     #[arg(long, value_name = "NAME")]
     dynamic_loading: Option<String>,
@@ -647,6 +650,7 @@
         enable_function_attribute_detection,
         use_array_pointers_in_arguments,
         wasm_import_module_name,
+        extern_fn_block_attrs,
         dynamic_loading,
         dynamic_link_require_all,
         prefix_link_name,
@@ -907,6 +911,7 @@
             time_phases,
             use_array_pointers_in_arguments => Builder::array_pointers_in_arguments,
             wasm_import_module_name,
+            extern_fn_block_attrs => Builder::extern_fn_block_attrs,
             ctypes_prefix,
             anon_fields_prefix,
             generate => Builder::with_codegen_config,
diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs
index edc979e..b9b33a8 100644
--- a/bindgen/options/mod.rs
+++ b/bindgen/options/mod.rs
@@ -1916,6 +1916,25 @@
         },
         as_args: "--use-array-pointers-in-arguments",
     },
+    /// Attributes to add to all `extern` blocks.
+    extern_fn_block_attrs: Vec<String> {
+        methods: {
+            /// Add an attribute to all the `extern` blocks generated by `bindgen`.
+            ///
+            /// This can be used to add attributes such as `#[link(...)]` to all
+            /// the `extern` blocks.
+            pub fn extern_fn_block_attrs<T: Into<String>>(mut self, attr: T) -> Self {
+                self.options.extern_fn_block_attrs.push(attr.into());
+                self
+            }
+        },
+        as_args: |attrs, args| {
+            for attr in attrs {
+                args.push("--extern-fn-block-attrs".to_owned());
+                args.push(attr.clone());
+            }
+        },
+    },
     /// The name of the `wasm_import_module`.
     wasm_import_module_name: Option<String> {
         methods: {
@@ -1927,7 +1946,9 @@
                 mut self,
                 import_name: T,
             ) -> Self {
-                self.options.wasm_import_module_name = Some(import_name.into());
+                self.options.extern_fn_block_attrs.push(format!(
+                    "#[link(wasm_import_module = \"{}\")]", import_name.into()
+                ));
                 self
             }
         },