Skip to main content
Redhat Developers  Logo
  • Products

    Platforms

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat AI
      Red Hat AI
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • View All Red Hat Products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat Developer Hub
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat OpenShift Local
    • Red Hat Developer Sandbox

      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Secure Development & Architectures

      • Security
      • Secure coding
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • Product Documentation
    • API Catalog
    • Legacy Documentation
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Annocheck: Examining the contents of binary files

February 4, 2019
Nick Clifton
Related topics:
Security

Share:

    The Annobin plugin for GCC stores extra information inside binary files as they are compiled.  Examining this information used to be performed by a set of shell scripts, but that has now changed and a new program—annocheck—has been written to do the job.  The advantage of the program is that it is faster and more flexible than the scripts, and it does not rely upon other utilities to actually peer inside the binaries.

    This article is about the annocheck program: how to use it, how it works, and how to extend it. The program's main purpose is to examine how a binary was built and to check that it has all of the appropriate security hardening features enabled. But that is not its only use.  It also has several other modes that perform different kinds of examination of binary files.

    Another feature of annocheck is that it was designed to be easily extensible. It provides a framework for dissecting binary files and a set of utilities to help with this examination. It also knows how to handle archives, RPMs, and directories, presenting the contents of these to each tool as a series of ordinary files. Thus, tools need only worry about the specific tasks they want to carry out.

    Using annocheck

    Annocheck is supplied as part of the Annobin RPM shipped with Fedora. It can also be built directly from the source code obtained from the git repository. The simplest way of running it is to just invoke it with the name of a file to inspect.  (Note: The file has to be an ELF format file.  Annocheck does not handle other binary file types.)

    annocheck <file>

    This command will run the default tool—the security hardening checker—on <file>. The usual command-line options of --help, --version, and --verbose are supported to provide more information. Annocheck also supports directories, archives, and RPMs, so all of the following will also work:

    annocheck <directory>
    annocheck lib<foo>.a
    annocheck <rpm>

    The first version will recursively run annocheck on all the ELF files inside <directory>. (Other types of file will be ignored.) The second version will run annocheck on all the ELF files inside lib<foo>.a. If this is a thin archive, the links inside the archive will be followed to find the real files.

    The third version will run annocheck on all the ELF files inside <rpm>. This version also supports a command-line option to provide the name of a debug info RPM associated with the binary RPM:

    annocheck <rpm> --debug-rpm <debuginfo rpm>

    Providing the debug info RPM is not essential, but it can be very helpful as it often contains information that makes the tool's work easier.

    Annocheck contains multiple tools for examining binary files, although only the security hardening checker is enabled by default. The other tools can be enabled via specific command-line options. Currently, the extra tools that are supported by annocheck are as described below:

    annocheck --enable-built-by

    This tool reports the name of the compiler that was used to build the binary file. It looks in various different places in the binary, and if the --all command-line option is present, it will report all the strings that it finds, instead of just the first one.

    annocheck --enable-notes

    This tool displays the notes that are stored inside a binary file by the Annobin GCC plugin. It is similar to the readelf program's --notes option, except that it sorts the notes by address range and then displays them sequentially.

    annocheck --section-size=<name>

    This tool displays the size of the named sections. It is similar to the readelf program's --sections option, except that the output is restricted to specific sections, and it produces a cumulative result at the end. Thus, for example, it could be used to compute the total size of all of the .text sections in all the binary files inside a particular directory.

    Each tool also has its own set of command-line options to modify its behavior. Use the --help command-line option to see a full list of these. Multiple tools can be enabled at the same time, and the security hardening checker can be disabled, if desired, via the following option:

    annocheck --disable-hardened

    The security hardening checker

    This is the largest, and arguably most important, tool in the annocheck framework. It runs a series of checks on each binary file to see if it has been built with the appropriate security hardening options. It obtains a lot of the information about these options via the notes generated by the Annobin plugin.

    The security checker runs several tests to make sure that a program was linked correctly:

    • The program must not have a stack in an executable region of memory.
    • No segment in the program should have all three of the read, write, and execute permission bits set.
    • There should be no relocations against executable code.
    • The program must not have any relocations that are held in writable memory.
    • The runpath information used to locate shared libraries at runtime must include only directory paths that start with/usr.
    • Dynamic executables must have a dynamic segment.
    • The -pie and -z now linker options must have been enabled.

    The security checker also runs tests on the Annobin data to make sure the program was compiled correctly:

    • The program must have Annobin notes, and there should be no gaps in the notes.
    • The program must have been compiled with the following options
      enabled:
      • -D_FORTIFY_SOURCE=2
      • -fpic or -fPIC or -fpie or -fPIE
      • -fstack-protector-strong or -fstack-protector-all
      • -O2 (or -O3 or -Og)
    • Programs which use exception handling must have been compiled with -fexceptions enabled and with -D_GLIBCXX_ASSERTIONS specified.
    • If supported by the compiler, and appropriate for the specific target architecture, the following options must also have been enabled:
      • -fcf-protection=full
      • -fstack-clash-protection
      • -mcet
      • -mstackrealign

    Extending annocheck

    Each tool in annocheck is provided by an independent source file (or set of source files) that is compiled and linked into the annocheck binary. Tools can be omitted by simply not including them on the final link command line. The annocheck framework does not have any built-in knowledge of any of the tools, and instead it interacts with them via a global structure that each tool creates for itself. The
    structure looks like this:

    struct {
      const char * name;
      bool (* process_arg)
      void (* usage)
      void (* version)
      void (* start_scan)
      void (* end_scan)
      bool (* start_file)
      bool (* interesting_sec)
      bool (* check_sec)
      bool (* interesting_seg)
      bool (* check_seg)
      bool (* end_file)
    }

    This structure is defined in the annocheck.h header file. The full details of the structure have been omitted here in order to keep things simple. The fields are mostly callback functions, except for the first—name—which is the name of the tool. This name is used when reporting tool-specific information to the user.

    The callback functions are the main part of the structure. The functions can be NULL if the tool does not need that particular feature. The process_arg, usage, and version functions are part of the housekeeping facilities and are there to process command-line arguments, display help information, and display version information, respectively.

    The start_scan function is called when annocheck starts up (although after the command-line arguments have been processed). Then once all the input files have been processed the end_scan function is called. A quirk of the RPM format, however, means that it is necessary for annocheck to recursively invoke itself inside an RPM. This is allowed for in the start_scan and end_scan functions, which have a depth parameter and also the name of a file that can be used to pass information between invocation levels.

    The start_file function is called after an individual binary file has been located, but before any processing has started. It gives the tool a chance to initialize any per-file data, and to decide if it is interested in processing the contents of the file. If the tool is not interested in the file, annocheck will not call any of the other per-file functions on it.

    Once processing of a file has begun, the interesting_sec callback is called for every section in the file. This allows the tool to decide if it wants to examine the section in more detail and, if so, the check_sec callback will be called. The checking is done in this way so that if no tool is interested in a particular section, its contents are never loaded into memory, speeding up execution.

    Once the sections have been processed—if there are any—the segments are scanned in a similar fashion using the interesting_seg and check_seg callbacks.

    Once a file has been fully scanned, the end_file callback is called in order to allow the tool to perform any final processing and display its results.

    In addition to the callback structure, the annocheck.h header also provides prototypes for all the utility functions exported by annocheck itself. This includes functions for exploring debug information and ELF notes, locating symbols, and printing out messages.

    The header also defines a function to register a tool with the annocheck framework. This function is special in that it is expected to be called from inside a constructor, rather than from normal code. This is how tools tell annocheck about their existence, and it also allows annocheck to be built without any specific knowledge of any of the tools. The constructor function should look something like this:

      static bool disabled = false;
    
    static __attribute__((constructor)) void
    tool_register_checker (void) 
    {
      if (! annocheck_add_checker (& tool_structure, major_version))
         disabled = true;
    }

    In this function, tool_structure is the callback structure described above. The major_version variable is provided by annocheck, and it allows a consistency check to be made to ensure that both annocheck and the tool are using the same version of callback structure. If the annocheck_add_checker function returns false, the tool should not execute any further, because something has gone wrong with the registration process.

    Future work

    Development of annocheck is an ongoing process with new features being added all the time.  Currently, the following enhancements are planned:

    • Add support for binary files inside tar files and, especially, inside compressed tar files. Possibly also add support for zip files and other, similar archive systems.
    • Improve the argument handling. Currently, each tool defines its own command-line options, and these can conflict with other tools. Instead, the annocheck framework ought to enforce a policy of supplying only specific options to specific tools.
    • Improve the notes tool to display a matrix of notes and the memory regions to which they apply. Also, add support for displaying section and function names that apply to specific regions.
    Last updated: February 3, 2019

    Recent Posts

    • Customize RHEL CoreOS at scale: On-cluster image mode in OpenShift

    • How to set up KServe autoscaling for vLLM with KEDA

    • How I used Cursor AI to migrate a Bash test suite to Python

    • Install Python 3.13 on Red Hat Enterprise Linux from EPEL

    • Zero trust automation on AWS with Ansible and Terraform

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue