--- /dev/null
+/* annobin - Common functions used by plugins.
+ Copyright (c) 2024 Red Hat.
+
+ This is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+
+#include "annobin-common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+extern bool parse_argument (const char *, const char *);
+
+void
+parse_env (bool (* parse_argument)(const char *, const char *))
+{
+ char arg[2048];
+ const char * env;
+
+ if ((env = getenv ("ANNOBIN")) == NULL)
+ return;
+
+ while (*env != 0)
+ {
+ const char * comma;
+
+ comma = strchr (env, ',');
+ if (comma)
+ {
+ strncpy (arg, env, comma - env);
+ arg[comma - env] = 0;
+ env = comma + 1;
+ }
+ else
+ {
+ strncpy (arg, env, sizeof arg - 1);
+ arg[sizeof arg - 1] = 0;
+ env += strlen (env);
+ }
+
+ char * value = strchr (arg, '=');
+ if (value)
+ {
+ * value = 0;
+ value ++;
+ }
+ else
+ {
+ value = (char *) "";
+ }
+
+ (void) (*parse_argument) (arg, value);
+ }
+}
--- /dev/null
+/* annobin - Common functions used by plugins.
+ Copyright (c) 2024 Red Hat.
+
+ This is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+#ifndef ANNOBIN_COMMON_H_
+#define ANNOBIN_COMMON_H_
+
+#include <stdbool.h>
+
+void parse_env (bool (* parse_argument)(const char *, const char *));
+#endif // ANNOBIN-COMMON_H_
# -flto # Not used because the object file generated is not an ELF format file.
# -fcf-protection # Not used because not supported by all architectures
-check: @srcdir@/hello.c $(PLUGIN_NAME)
+TESTS = test-global-file-syms
+
+check: @srcdir@/hello.c $(PLUGIN_NAME) $(addsuffix .log,$(TESTS))
$(CLANG) -fplugin=$(PLUGIN) $(PLUGIN_TEST_OPTIONS) -c @srcdir@/hello.c
$(READELF) --wide --notes hello.o > clang-plugin-test.readelf.out
@ grep --silent -e "annobin built by clang version" clang-plugin-test.readelf.out
@ grep --silent -e "SpecLoadHarden" clang-plugin-test.readelf.out
$(ANNOCHECK) --skip-all --test-optimization --test-fortify --test-stack-prot hello.o --verbose
@ echo "PASS Clang plugin test"
+
+test-global-file-syms.log: @srcdir@/hello.c $(PLUGIN_NAME)
+ $(CLANG) -fplugin=$(PLUGIN) -fplugin-arg-annobin-global-file-syms -c $< -o test-global-file-syms.o
+ $(READELF) --wide --syms test-global-file-syms.o > test-global-file-syms.readelf.out
+ @ grep --silent -e "_annobin.\+_hello_c_" test-global-file-syms.readelf.out
+ @ grep --silent -e '_annobin.\+_hello_c_.\+_start' test-global-file-syms.readelf.out
+ @ grep --silent -e '_annobin.\+_hello_c_.\+_end' test-global-file-syms.readelf.out
+ @ echo "PASS Clang global-file-syms test" | tee $@
#include <cstring>
#include <cctype>
#include <cstdarg>
+#include <iomanip>
#include <sstream>
+#include <sys/time.h>
namespace
{
static const unsigned int annobin_version = (unsigned int) (ANNOBIN_VERSION * 100);
bool be_verbose = false;
+ /* True if the symbols used to map addresses to file names should be global.
+ On some architectures these symbols have to be global so that they will
+ be preserved in object files. But doing so can prevent the build-id
+ mechanism from working, since the symbols contain build-date information. */
+ bool global_file_name_symbols = false;
// Helper functions used throughout this file.
template<class... Tys>
for( auto & c : name)
if (!isalnum (c))
c = '_';
+
+ if (global_file_name_symbols)
+ {
+ /* A program can have multiple source files with the same name.
+ Or indeed the same source file can be included multiple times.
+ Or a library can be built from a sources which include file names
+ that match application file names. Whatever the reason, we need
+ to be ensure that we generate unique global symbol names. So we
+ append the time to the symbol name. This will of course break
+ the functionality of build-ids. That is why this option is off
+ by default. */
+ struct timeval tv;
+
+ if (gettimeofday (& tv, NULL))
+ {
+ ice ("unable to get time of day.");
+ tv.tv_sec = tv.tv_usec = 0;
+ }
+ std::ostringstream t;
+ t << "_" << std::setfill('0') << std::setw(8) << (long) tv.tv_sec;
+ t << "_" << std::setfill('0') << std::setw(8) << (long) tv.tv_usec;
+ name += t.str();
+ }
+
}
void
{
if (args[i] == "help")
inform ("supported options:\n\
- help Display this message\n\
- disable Disable the plugin\n\
- enable Reenable the plugin if it has been disabled\n\
- version Displays the version number\n\
- verbose Produce descriptive messages whilst working");
+ help Display this message\n\
+ disable Disable the plugin\n\
+ enable Reenable the plugin if it has been disabled\n\
+ version Displays the version number\n\
+ verbose Produce descriptive messages whilst working\n\
+ \n\
+ global-file-syms Create final name symbols that are global");
else if (args[i] == "disable")
enabled = false;
else if (args[i] == "enable")
inform ("Annobin plugin version: %u", annobin_version);
else if (args[i] == "verbose")
be_verbose = true;
+ else if (args[i] == "global-file-syms")
+ global_file_name_symbols = true;
else
inform ("error: unknown option: %s", args[i].c_str());
}
AM_CPPFLAGS = -I'$(top_builddir)' -I'$(top_srcdir)'
AUTOMAKE_OPTIONS = no-dependencies
-annobin_la_SOURCES = annobin.cc
+annobin_la_SOURCES = annobin.cc ../annobin-common.cc
EXTRA_annobin_la_SOURCES = aarch64.annobin.cc arm.annobin.cc dummy.annobin.cc powerpc.annobin.cc s390.annobin.cc x86_64.annobin.cc i686.annobin.cc riscv.annobin.cc mips.annobin.cc
annobin_la_LIBADD = @target_plugin@
annobin_la_DEPENDENCIES = @target_plugin@
}
am__installdirs = "$(DESTDIR)$(plugindir)"
LTLIBRARIES = $(plugin_LTLIBRARIES)
-am_annobin_la_OBJECTS = annobin.lo
+am_annobin_la_OBJECTS = annobin.lo annobin-common.lo
annobin_la_OBJECTS = $(am_annobin_la_OBJECTS)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
plugin_LTLIBRARIES = annobin.la
AM_CPPFLAGS = -I'$(top_builddir)' -I'$(top_srcdir)'
AUTOMAKE_OPTIONS = no-dependencies
-annobin_la_SOURCES = annobin.cc
+annobin_la_SOURCES = annobin.cc ../annobin-common.cc
EXTRA_annobin_la_SOURCES = aarch64.annobin.cc arm.annobin.cc dummy.annobin.cc powerpc.annobin.cc s390.annobin.cc x86_64.annobin.cc i686.annobin.cc riscv.annobin.cc mips.annobin.cc
annobin_la_LIBADD = @target_plugin@
annobin_la_DEPENDENCIES = @target_plugin@
.cc.lo:
$(AM_V_CXX)$(LTCXXCOMPILE) -c -o $@ $<
+annobin-common.lo: ../annobin-common.cc
+ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o annobin-common.lo `test -f '../annobin-common.cc' || echo '$(srcdir)/'`../annobin-common.cc
+
mostlyclean-libtool:
-rm -f *.lo
#include <stdio.h>
#include <intl.h>
+#include "annobin-common.h"
#include "annobin-global.h"
#include "annobin.h"
return result;
}
-static void
-parse_env (const char * env)
-{
- while (*env != 0)
- {
- const char * comma;
- char * arg = annobin_note_buffer;
-
- comma = strchr (env, ',');
- if (comma)
- {
- strncpy (arg, env, comma - env);
- arg[comma - env] = 0;
- env = comma + 1;
- }
- else
- {
- strncpy (arg, env, sizeof annobin_note_buffer - 1);
- arg[sizeof annobin_note_buffer - 1] = 0;
- env += strlen (env);
- }
-
- char * value = strchr (arg, '=');
- if (value)
- {
- * value = 0;
- value ++;
- }
- else
- {
- value = (char *) "";
- }
-
- (void) parse_argument (arg, value);
- }
-}
-
int
plugin_init (struct plugin_name_args * plugin_info,
struct plugin_gcc_version * version)
return 1;
}
- const char * env;
- if ((env = getenv ("ANNOBIN")) != NULL)
- {
- parse_env (env);
- }
-
+ parse_env (&parse_argument);
if (plugins_active_p () && (annobin_extra_prefix[0] == 0))
{
/* See BZ 2162746 for an example of why this is needed. */
all: $(PLUGIN_NAME) Makefile
-$(PLUGIN_NAME): annobin.cpp
+$(PLUGIN_NAME): annobin.cpp annobin-common.o
clang++ $(CLANG_TARGET_OPTIONS) $(LLVM_CXX_OPTIONS) \
$(PLUGIN_OPTIONS) $(LLVM_LD_OPTIONS) $(LLVM_SYS_LIBS) \
- -I .. -I$(INCDIR) $< -o $@
+ -I .. -I$(INCDIR) $^ -o $@
+
+annobin-common.o: @top_srcdir@/annobin-common.cc
+ clang++ -c $(CLANG_TARGET_OPTIONS) $(LLVM_CXX_OPTIONS) \
+ $(PLUGIN_OPTIONS) $(LLVM_LD_OPTIONS) $(LLVM_SYS_LIBS) \
+ -I .. -I$(INCDIR) $^ -o $@
install: $(PLUGIN_NAME)
install -Dpm0755 -t ${PLUGIN_INSTALL_DIR} $<
# -flto # Not used because the object file generated is not an ELF format file.
# -fcf-protection # Not used because not supported by all architectures
+TESTS = test-global-file-syms
+
check: $(PLUGIN_NAME)
@ if [ `echo | clang -dM -E - | grep __clang_major__ | cut -f 3 -d ' '` -gt 14 ] ; \
then \
check-pre-clang-13:
$(MAKE) check-run LOAD_PLUGIN_ARG="-Xclang -load -Xclang $(PLUGIN)"
-check-run: @srcdir@/hello.c
+check-run: @srcdir@/hello.c $(addsuffix .log,$(TESTS))
echo Compiling with $(LOAD_PLUGIN_ARG) ...
$(CLANG) $(LOAD_PLUGIN_ARG) $(PLUGIN_TEST_OPTIONS) -c @srcdir@/hello.c
echo Checking with readelf ...
$(ANNOCHECK) --skip-all --test-optimization --test-fortify --test-stack-prot hello.o || :
$(ANNOCHECK) --skip-all --test-optimization --test-fortify --test-stack-prot hello.o
echo "PASS LLVM plugin test [$(LOAD_PLUGIN_ARG)]"
+
+test-global-file-syms.log: @srcdir@/hello.c
+ ANNOBIN=global-file-syms $(CLANG) $(LOAD_PLUGIN_ARG) -c $< -o test-global-file-syms.o
+ $(READELF) --wide --syms test-global-file-syms.o > test-global-file-syms.readelf.out
+ @ grep --silent -e "_annobin.\+_hello_c_" test-global-file-syms.readelf.out
+ @ grep --silent -e '_annobin.\+_hello_c_.\+_start' test-global-file-syms.readelf.out
+ @ grep --silent -e '_annobin.\+_hello_c_.\+_end' test-global-file-syms.readelf.out
+ @ echo "PASS LLVM global-file-syms test" | tee $@
#endif
#include "llvm/LTO/legacy/LTOCodeGenerator.h"
#include "annobin-global.h"
+#include "annobin-common.h"
#include <cstring>
#include <cctype>
#include <cstdarg>
+#include <iomanip>
#include <sstream>
+#include <sys/time.h>
using namespace llvm;
namespace
{
static bool be_verbose = false;
static unsigned int target_start_sym_bias = 0;
+ /* True if the symbols used to map addresses to file names should be global.
+ On some architectures these symbols have to be global so that they will
+ be preserved in object files. But doing so can prevent the build-id
+ mechanism from working, since the symbols contain build-date information. */
+ bool global_file_name_symbols = false;
// Helper functions used throughout this file.
template<class... Tys>
(void) std::initializer_list<int>{((oss << args), 1)...};
return strdup (oss.str().c_str());
}
-#if 0
static inline void
inform (char const fmt[], ...)
{
fputc ('\n', stderr);
va_end (args);
}
-#endif
static inline void
verbose (char const fmt[], ...)
{
free (buffer);
}
+ static bool
+ parse_argument (const char * key, const char * value)
+ {
+ if (streq (key, "verbose"))
+ be_verbose = true;
+ else if (streq (key, "global-file-syms"))
+ global_file_name_symbols = true;
+ else if (streq (key, "no-global-file-syms"))
+ global_file_name_symbols = false;
+ else
+ {
+ inform ("annobin: unrecognised option: %s\n", key);
+ return false;
+ }
+
+ return true;
+ }
+
public:
#if __clang_major__ > 12
AnnobinModule()
if (getenv ("ANNOBIN_VERBOSE") != NULL
&& ! streq (getenv ("ANNOBIN_VERBOSE"), "false"))
be_verbose = true;
+
+ parse_env (&parse_argument);
}
void
for( auto & c : name)
if (!isalnum (c))
c = '_';
+
+ if (global_file_name_symbols)
+ {
+ /* A program can have multiple source files with the same name.
+ Or indeed the same source file can be included multiple times.
+ Or a library can be built from a sources which include file names
+ that match application file names. Whatever the reason, we need
+ to be ensure that we generate unique global symbol names. So we
+ append the time to the symbol name. This will of course break
+ the functionality of build-ids. That is why this option is off
+ by default. */
+ struct timeval tv;
+
+ if (gettimeofday (& tv, NULL))
+ {
+ ice ("unable to get time of day.");
+ tv.tv_sec = tv.tv_usec = 0;
+ }
+ std::ostringstream t;
+ t << "_" << std::setfill('0') << std::setw(8) << (long) tv.tv_sec;
+ t << "_" << std::setfill('0') << std::setw(8) << (long) tv.tv_usec;
+ name += t.str();
+ }
}
static void