LLVM Interface Export Annotations#

Symbols that are part of LLVM’s public interface must be explicitly annotated to support shared library builds with hidden default symbol visibility. This document provides background and guidelines for annotating the codebase.

LLVM Shared Library#

LLVM builds as a static library by default, but it can also be built as a shared library with the following configuration:

LLVM_BUILD_LLVM_DYLIB=On
LLVM_LINK_LLVM_DYLIB=On

There are three shared library executable formats we’re interested in: PE Dynamic Link Library (.dll) on Windows, Mach-O Shared Object (.dylib) on Apple systems, and ELF Shared Object (.so) on Linux, BSD and other Unix-like systems.

ELF and Mach-O Shared Object files can be built with no additional setup or configuration. This is because all global symbols in the library are exported by default – the same as when building a static library. However, when building a DLL for Windows, the situation is more complex:

  • Symbols are not exported from a DLL by default. Symbols must be annotated with __declspec(dllexport) when building the library to be externally visible.

  • Symbols imported from a Windows DLL should generally be annotated with __declspec(dllimport) when compiling clients.

  • A single Windows DLL can export a maximum of 65,535 symbols.

Because of the requirements for Windows DLLs, additional work must be done to ensure the proper set of public symbols is exported and visible to clients.

Annotation Macros#

The distinct DLL import and export annotations required for Windows DLLs typically lead developers to define a preprocessor macro for annotating exported symbols in header public files. The custom macro resolves to the export annotation when building the library and the import annotation when building the client.

We have defined the LLVM_ABI macro in llvm/Support/Compiler.h for this purpose:

#if defined(LLVM_EXPORTS)
#define LLVM_ABI __declspec(dllexport)
#else
#define LLVM_ABI __declspec(dllimport)
#endif

Windows DLL symbol visibility requirements are approximated on ELF and Mach-O shared library builds by setting default symbol visibility to hidden (-fvisibility-default=hidden) when building with the following configuration:

LLVM_BUILD_LLVM_DYLIB_VIS=On

For an ELF or Mach-O platform with this setting, the LLVM_ABI macro is defined to override the default hidden symbol visibility:

#define LLVM_ABI __attribute__((visibility("default")))

In addition to LLVM_ABI, there are a few other macros for use in less common cases described below.

Export macros are used to annotate symbols only within their intended shared library. This is necessary because of the way Windows handles import/export annotations.

For example, LLVM_ABI resolves to __declspec(dllexport) only when building source that is part of the LLVM shared library (e.g. source under llvm-project/llvm). If LLVM_ABI were incorrectly used to annotate a symbol from a different LLVM project (such as Clang) it would always resolve to __declspec(dllimport) and the symbol would not be properly exported.

How to Annotate Symbols#

Functions#

Exported function declarations in header files must be annotated with LLVM_ABI.

#include "llvm/Support/Compiler.h"

LLVM_ABI void exported_function(int a, int b);

Global Variables#

Exported global variables must be annotated with LLVM_ABI at their extern declarations.

#include "llvm/Support/Compiler.h"

LLVM_ABI extern int exported_global_variable;

Classes, Structs, and Unions#

Classes, structs, and unions can be annotated with LLVM_ABI at their declaration, but this option is generally discouraged because it will export every class member, vtable, and type information. Instead, LLVM_ABI should be applied to individual class members that require export.

In the most common case, public and protected methods without a body in the class declaration must be annotated with LLVM_ABI.

#include "llvm/Support/Compiler.h"

class ExampleClass {
public:
  // Public methods defined externally must be annotated.
  LLVM_ABI int sourceDefinedPublicMethod(int a, int b);