diff options
-rw-r--r-- | gold/ChangeLog | 40 | ||||
-rw-r--r-- | gold/Makefile.in | 2 | ||||
-rwxr-xr-x | gold/configure | 64 | ||||
-rw-r--r-- | gold/configure.ac | 8 | ||||
-rw-r--r-- | gold/layout.cc | 21 | ||||
-rw-r--r-- | gold/output.cc | 248 | ||||
-rw-r--r-- | gold/output.h | 50 | ||||
-rw-r--r-- | gold/testsuite/Makefile.am | 10 | ||||
-rw-r--r-- | gold/testsuite/Makefile.in | 71 | ||||
-rw-r--r-- | gold/testsuite/initpri1.c | 87 |
10 files changed, 576 insertions, 25 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 492e5c5..a43fa21 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,43 @@ +2008-03-28 Ian Lance Taylor <iant@google.com> + + * layout.cc (Layout::layout): If we see an input section with a + name that needs sorting, set the must_sort flag for the output + section. + (Layout::make_output_section): If the name of the output section + indicates that it might require sorting, set the may_sort flag. + * output.h (Output_section::may_sort_attached_input_sections): New + function. + (Output_section::set_may_sort_attached_input_sections): New + function. + (Output_section::must_sort_attached_input_sections): New + function. + (Output_section::set_must_sort_attached_input_sections): New + function. + (class Output_section): Declare Input_section_sort_entry. Define + Input_section_sort_compare. Declare + sort_attached_input_sections. Add new fields: + may_sort_attached_input_sections_, + must_sort_attached_input_sections_, + attached_input_sections_are_sorted_. + * output.cc (Output_section::Output_section): Initialize new + fields. + (Output_section::add_input_section): Add an entry to + input_sections_ if may_sort or must_sort are true. + (Output_section::set_final_data_size): Call + sort_attached_input_sections if necessary. + (Output_section::Input_section_sort_entry): Define new class. + (Output_section::Input_section_sort_compare::operator()): New + function. + (Output_section::sort_attached_input_sections): New function. + * configure.ac: Check whether the compiler supports constructor + priorities. Define a CONSTRUCTOR_PRIORITY automake conditional. + * testsuite/initpri1.c: New file. + * testsuite/Makefile.am (check_PROGRAMS): Add initpri1 if + CONSTRUCTOR_PRIORITY. + (initpri1_SOURCES, initpri1_DEPENDENCIES): New variables. + (initpri1_LDFLAGS): New variable. + * configure, Makefile.in, testsuite/Makefile.in: Rebuild. + 2008-03-27 Ian Lance Taylor <iant@google.com> * common.cc (Sort_commons::operator): Correct sorting algorithm. diff --git a/gold/Makefile.in b/gold/Makefile.in index fdc0b16..127888f 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -163,6 +163,8 @@ CATOBJEXT = @CATOBJEXT@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CONSTRUCTOR_PRIORITY_FALSE = @CONSTRUCTOR_PRIORITY_FALSE@ +CONSTRUCTOR_PRIORITY_TRUE = @CONSTRUCTOR_PRIORITY_TRUE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ diff --git a/gold/configure b/gold/configure index 01cdf16..8076b8e 100755 --- a/gold/configure +++ b/gold/configure @@ -309,7 +309,7 @@ ac_includes_default="\ # include <unistd.h> #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE OBJDUMP_AND_CPPFILT_TRUE OBJDUMP_AND_CPPFILT_FALSE READELF_TRUE READELF_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE OBJDUMP_AND_CPPFILT_TRUE OBJDUMP_AND_CPPFILT_FALSE READELF_TRUE READELF_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE CONSTRUCTOR_PRIORITY_TRUE CONSTRUCTOR_PRIORITY_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -4651,6 +4651,59 @@ else fi +echo "$as_me:$LINENO: checking for constructor priorities" >&5 +echo $ECHO_N "checking for constructor priorities... $ECHO_C" >&6 +if test "${gold_cv_c_conprio+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +void f() __attribute__ ((constructor (1))); +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + gold_cv_c_conprio=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +gold_cv_c_conprio=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $gold_cv_c_conprio" >&5 +echo "${ECHO_T}$gold_cv_c_conprio" >&6 + + + +if test "$gold_cv_c_conprio" = "yes"; then + CONSTRUCTOR_PRIORITY_TRUE= + CONSTRUCTOR_PRIORITY_FALSE='#' +else + CONSTRUCTOR_PRIORITY_TRUE='#' + CONSTRUCTOR_PRIORITY_FALSE= +fi + + GCC_WARN_CFLAGS="-W -Wall -Wstrict-prototypes -Wmissing-prototypes" @@ -6501,6 +6554,13 @@ echo "$as_me: error: conditional \"STATIC_TLS\" was never defined. Usually this means the macro was only invoked conditionally." >&2;} { (exit 1); exit 1; }; } fi +if test -z "${CONSTRUCTOR_PRIORITY_TRUE}" && test -z "${CONSTRUCTOR_PRIORITY_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"CONSTRUCTOR_PRIORITY\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"CONSTRUCTOR_PRIORITY\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." >&5 @@ -7142,6 +7202,8 @@ s,@TLS_TRUE@,$TLS_TRUE,;t t s,@TLS_FALSE@,$TLS_FALSE,;t t s,@STATIC_TLS_TRUE@,$STATIC_TLS_TRUE,;t t s,@STATIC_TLS_FALSE@,$STATIC_TLS_FALSE,;t t +s,@CONSTRUCTOR_PRIORITY_TRUE@,$CONSTRUCTOR_PRIORITY_TRUE,;t t +s,@CONSTRUCTOR_PRIORITY_FALSE@,$CONSTRUCTOR_PRIORITY_FALSE,;t t s,@WARN_CFLAGS@,$WARN_CFLAGS,;t t s,@NO_WERROR@,$NO_WERROR,;t t s,@WARN_CXXFLAGS@,$WARN_CXXFLAGS,;t t diff --git a/gold/configure.ac b/gold/configure.ac index d9d965e..2355e73 100644 --- a/gold/configure.ac +++ b/gold/configure.ac @@ -223,6 +223,14 @@ error AM_CONDITIONAL(STATIC_TLS, test "$gold_cv_lib_glibc24" = "yes") +dnl Check whether the compiler supports constructor priorities in +dnl attributes, which were added in gcc 4.3. +AC_CACHE_CHECK([for constructor priorities], [gold_cv_c_conprio], +[AC_COMPILE_IFELSE([void f() __attribute__ ((constructor (1)));], +[gold_cv_c_conprio=yes], [gold_cv_c_conprio=no])]) + +AM_CONDITIONAL(CONSTRUCTOR_PRIORITY, test "$gold_cv_c_conprio" = "yes") + AM_BINUTILS_WARNINGS WARN_CXXFLAGS=`echo ${WARN_CFLAGS} | sed -e 's/-Wstrict-prototypes//' -e 's/-Wmissing-prototypes//'` diff --git a/gold/layout.cc b/gold/layout.cc index 1bbfcb6..e8582ca 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -404,6 +404,17 @@ Layout::layout(Sized_relobj<size, big_endian>* object, unsigned int shndx, return NULL; } + // By default the GNU linker sorts input sections whose names match + // .ctor.*, .dtor.*, .init_array.*, or .fini_array.*. The sections + // are sorted by name. This is used to implement constructor + // priority ordering. We are compatible. + if (!this->script_options_->saw_sections_clause() + && (is_prefix_of(".ctors.", name) + || is_prefix_of(".dtors.", name) + || is_prefix_of(".init_array.", name) + || is_prefix_of(".fini_array.", name))) + os->set_must_sort_attached_input_sections(); + // FIXME: Handle SHF_LINK_ORDER somewhere. *off = os->add_input_section(object, shndx, name, shdr, reloc_shndx, @@ -671,6 +682,16 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type, else this->attach_to_segment(os, flags); + // The GNU linker by default sorts some sections by priority, so we + // do the same. We need to know that this might happen before we + // attach any input sections. + if (!this->script_options_->saw_sections_clause() + && (strcmp(name, ".ctors") == 0 + || strcmp(name, ".dtors") == 0 + || strcmp(name, ".init_array") == 0 + || strcmp(name, ".fini_array") == 0)) + os->set_may_sort_attached_input_sections(); + return os; } diff --git a/gold/output.cc b/gold/output.cc index d4fc2a7..ad822d1 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -1595,6 +1595,9 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type, found_in_sections_clause_(false), has_load_address_(false), info_uses_section_index_(false), + may_sort_attached_input_sections_(false), + must_sort_attached_input_sections_(false), + attached_input_sections_are_sorted_(false), tls_offset_(0) { // An unallocated section has no address. Forcing this means that @@ -1711,9 +1714,14 @@ Output_section::add_input_section(Sized_relobj<size, big_endian>* object, + shdr.get_sh_size()); // We need to keep track of this section if we are already keeping - // track of sections, or if we are relaxing. FIXME: Add test for + // track of sections, or if we are relaxing. Also, if this is a + // section which requires sorting, or which may require sorting in + // the future, we keep track of the sections. FIXME: Add test for // relaxing. - if (have_sections_script || !this->input_sections_.empty()) + if (have_sections_script + || !this->input_sections_.empty() + || this->may_sort_attached_input_sections() + || this->must_sort_attached_input_sections()) this->input_sections_.push_back(Input_section(object, shndx, shdr.get_sh_size(), addralign)); @@ -1943,6 +1951,9 @@ Output_section::set_final_data_size() return; } + if (this->must_sort_attached_input_sections()) + this->sort_attached_input_sections(); + uint64_t address = this->address(); off_t startoff = this->offset(); off_t off = startoff + this->first_input_offset_; @@ -1978,6 +1989,239 @@ Output_section::do_set_tls_offset(uint64_t tls_base) this->tls_offset_ = this->address() - tls_base; } +// In a few cases we need to sort the input sections attached to an +// output section. This is used to implement the type of constructor +// priority ordering implemented by the GNU linker, in which the +// priority becomes part of the section name and the sections are +// sorted by name. We only do this for an output section if we see an +// attached input section matching ".ctor.*", ".dtor.*", +// ".init_array.*" or ".fini_array.*". + +class Output_section::Input_section_sort_entry +{ + public: + Input_section_sort_entry() + : input_section_(), index_(-1U), section_has_name_(false), + section_name_() + { } + + Input_section_sort_entry(const Input_section& input_section, + unsigned int index) + : input_section_(input_section), index_(index), + section_has_name_(input_section.is_input_section()) + { + if (this->section_has_name_) + { + // This is only called single-threaded from Layout::finalize, + // so it is OK to lock. Unfortunately we have no way to pass + // in a Task token. + const Task* dummy_task = reinterpret_cast<const Task*>(-1); + Object* obj = input_section.relobj(); + Task_lock_obj<Object> tl(dummy_task, obj); + + // This is a slow operation, which should be cached in + // Layout::layout if this becomes a speed problem. + this->section_name_ = obj->section_name(input_section.shndx()); + } + } + + // Return the Input_section. + const Input_section& + input_section() const + { + gold_assert(this->index_ != -1U); + return this->input_section_; + } + + // The index of this entry in the original list. This is used to + // make the sort stable. + unsigned int + index() const + { + gold_assert(this->index_ != -1U); + return this->index_; + } + + // Whether there is a section name. + bool + section_has_name() const + { return this->section_has_name_; } + + // The section name. + const std::string& + section_name() const + { + gold_assert(this->section_has_name_); + return this->section_name_; + } + + // Return true if the section name is either SECTION_NAME1 or + // SECTION_NAME2. + bool + match_section_name(const char* section_name1, const char* section_name2) const + { + gold_assert(this->section_has_name_); + return (this->section_name_ == section_name1 + || this->section_name_ == section_name2); + } + + // Return true if PREFIX1 or PREFIX2 is a prefix of the section + // name. + bool + match_section_name_prefix(const char* prefix1, const char* prefix2) const + { + gold_assert(this->section_has_name_); + return (this->section_name_.compare(0, strlen(prefix1), prefix1) == 0 + || this->section_name_.compare(0, strlen(prefix2), prefix2) == 0); + } + + // Return true if this is for a section named SECTION_NAME1 or + // SECTION_NAME2 in an input file whose base name matches FILE_NAME. + // The base name must have an extension of ".o", and must be exactly + // FILE_NAME.o or FILE_NAME, one character, ".o". This is to match + // crtbegin.o as well as crtbeginS.o without getting confused by + // other possibilities. Overall matching the file name this way is + // a dreadful hack, but the GNU linker does it in order to better + // support gcc, and we need to be compatible. + bool + match_section_file(const char* section_name1, const char* section_name2, + const char* match_file_name) const + { + gold_assert(this->section_has_name_); + if (this->section_name_ != section_name1 + && this->section_name_ != section_name2) + return false; + const std::string& file_name(this->input_section_.relobj()->name()); + const char* base_name = lbasename(file_name.c_str()); + size_t match_len = strlen(match_file_name); + if (strncmp(base_name, match_file_name, match_len) != 0) + return false; + size_t base_len = strlen(base_name); + if (base_len != match_len + 2 && base_len != match_len + 3) + return false; + return memcmp(base_name + base_len - 2, ".o", 2) == 0; + } + + private: + // The Input_section we are sorting. + Input_section input_section_; + // The index of this Input_section in the original list. + unsigned int index_; + // Whether this Input_section has a section name--it won't if this + // is some random Output_section_data. + bool section_has_name_; + // The section name if there is one. + std::string section_name_; +}; + +// Return true if S1 should come before S2 in the output section. + +bool +Output_section::Input_section_sort_compare::operator()( + const Output_section::Input_section_sort_entry& s1, + const Output_section::Input_section_sort_entry& s2) const +{ + // We sort all the sections with no names to the end. + if (!s1.section_has_name() || !s2.section_has_name()) + { + if (s1.section_has_name()) + return true; + if (s2.section_has_name()) + return false; + return s1.index() < s2.index(); + } + + // A .ctors or .dtors section from crtbegin.o must come before any + // other .ctors* or .dtors* section. + bool s1_begin = s1.match_section_file(".ctors", ".dtors", "crtbegin"); + bool s2_begin = s2.match_section_file(".ctors", ".dtors", "crtbegin"); + if (s1_begin || s2_begin) + { + if (!s1_begin) + return false; + if (!s2_begin) + return true; + return s1.index() < s2.index(); + } + + // A .ctors or .dtors section from crtend.o must come after any + // other .ctors* or .dtors* section. + bool s1_end = s1.match_section_file(".ctors", ".dtors", "crtend"); + bool s2_end = s2.match_section_file(".ctors", ".dtors", "crtend"); + if (s1_end || s2_end) + { + if (!s1_end) + return true; + if (!s2_end) + return false; + return s1.index() < s2.index(); + } + + // A .ctors or .init_array section with a priority precedes a .ctors + // or .init_array section without a priority. + if (s1.match_section_name_prefix(".ctors.", ".init_array.") + && s2.match_section_name(".ctors", ".init_array")) + return true; + if (s2.match_section_name_prefix(".ctors.", ".init_array.") + && s1.match_section_name(".ctors", ".init_array")) + return false; + + // A .dtors or .fini_array section with a priority follows a .dtors + // or .fini_array section without a priority. + if (s1.match_section_name_prefix(".dtors.", ".fini_array.") + && s2.match_section_name(".dtors", ".fini_array")) + return false; + if (s2.match_section_name_prefix(".dtors.", ".fini_array.") + && s1.match_section_name(".dtors", ".fini_array")) + return true; + + // Otherwise we sort by name. + int compare = s1.section_name().compare(s2.section_name()); + if (compare != 0) + return compare < 0; + + // Otherwise we keep the input order. + return s1.index() < s2.index(); +} + +// Sort the input sections attached to an output section. + +void +Output_section::sort_attached_input_sections() +{ + if (this->attached_input_sections_are_sorted_) + return; + + // The only thing we know about an input section is the object and + // the section index. We need the section name. Recomputing this + // is slow but this is an unusual case. If this becomes a speed + // problem we can cache the names as required in Layout::layout. + + // We start by building a larger vector holding a copy of each + // Input_section, plus its current index in the list and its name. + std::vector<Input_section_sort_entry> sort_list; + + unsigned int i = 0; + for (Input_section_list::iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p, ++i) + sort_list.push_back(Input_section_sort_entry(*p, i)); + + // Sort the input sections. + std::sort(sort_list.begin(), sort_list.end(), Input_section_sort_compare()); + + // Copy the sorted input sections back to our list. + this->input_sections_.clear(); + for (std::vector<Input_section_sort_entry>::iterator p = sort_list.begin(); + p != sort_list.end(); + ++p) + this->input_sections_.push_back(p->input_section()); + + // Remember that we sorted the input sections, since we might get + // called again. + this->attached_input_sections_are_sorted_ = true; +} + // Write the section header to *OSHDR. template<int size, bool big_endian> diff --git a/gold/output.h b/gold/output.h index 2b864f3..5a95748 100644 --- a/gold/output.h +++ b/gold/output.h @@ -1869,6 +1869,32 @@ class Output_section : public Output_data this->dynsym_index_ = index; } + // Return whether the input sections sections attachd to this output + // section may require sorting. This is used to handle constructor + // priorities compatibly with GNU ld. + bool + may_sort_attached_input_sections() const + { return this->may_sort_attached_input_sections_; } + + // Record that the input sections attached to this output section + // may require sorting. + void + set_may_sort_attached_input_sections() + { this->may_sort_attached_input_sections_ = true; } + + // Return whether the input sections attached to this output section + // require sorting. This is used to handle constructor priorities + // compatibly with GNU ld. + bool + must_sort_attached_input_sections() const + { return this->must_sort_attached_input_sections_; } + + // Record that the input sections attached to this output section + // require sorting. + void + set_must_sort_attached_input_sections() + { this->must_sort_attached_input_sections_ = true; } + // Return whether this section should be written after all the input // sections are complete. bool @@ -2310,6 +2336,17 @@ class Output_section : public Output_data typedef std::vector<Input_section> Input_section_list; + // This class is used to sort the input sections. + class Input_section_sort_entry; + + // This is the sort comparison function. + struct Input_section_sort_compare + { + bool + operator()(const Input_section_sort_entry&, + const Input_section_sort_entry&) const; + }; + // Fill data. This is used to fill in data between input sections. // It is also used for data statements (BYTE, WORD, etc.) in linker // scripts. When we have to keep track of the input sections, we @@ -2360,6 +2397,10 @@ class Output_section : public Output_data add_output_merge_section(Output_section_data* posd, bool is_string, uint64_t entsize); + // Sort the attached input sections. + void + sort_attached_input_sections(); + // Most of these fields are only valid after layout. // The name of the section. This will point into a Stringpool. @@ -2443,6 +2484,15 @@ class Output_section : public Output_data // section, false if it means the symbol index of the corresponding // section symbol. bool info_uses_section_index_ : 1; + // True if the input sections attached to this output section may + // need sorting. + bool may_sort_attached_input_sections_ : 1; + // True if the input sections attached to this output section must + // be sorted. + bool must_sort_attached_input_sections_ : 1; + // True if the input sections attached to this output section have + // already been sorted. + bool attached_input_sections_are_sorted_ : 1; // For SHT_TLS sections, the offset of this section relative to the base // of the TLS segment. uint64_t tls_offset_; diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index 0016f71..58ed906 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -417,6 +417,16 @@ endif FN_PTRS_IN_SO_WITHOUT_PIC endif TLS +if CONSTRUCTOR_PRIORITY + +check_PROGRAMS += initpri1 +initpri1_SOURCES = initpri1.c +initpri1_DEPENDENCIES = gcctestdir/ld +initpri1_LDFLAGS = -Bgcctestdir/ + +endif + + # Test --detect-odr-violations check_SCRIPTS += debug_msg.sh diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index bc4f73a..0e68b9a 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -45,7 +45,7 @@ target_triplet = @target@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \ $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \ - $(am__EXEEXT_7) $(am__EXEEXT_8) + $(am__EXEEXT_7) $(am__EXEEXT_8) $(am__EXEEXT_9) @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_1 = basic_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_test basic_pic_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_pic_test \ @@ -150,11 +150,24 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@STATIC_TLS_TRUE@@TLS_TRUE@am__append_5 = tls_static_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@STATIC_TLS_TRUE@@TLS_TRUE@ tls_static_pic_test @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@am__append_6 = tls_shared_nonpic_test +@CONSTRUCTOR_PRIORITY_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_7 = initpri1 +@CONSTRUCTOR_PRIORITY_FALSE@initpri1_DEPENDENCIES = libgoldtest.a \ +@CONSTRUCTOR_PRIORITY_FALSE@ ../libgold.a \ +@CONSTRUCTOR_PRIORITY_FALSE@ ../../libiberty/libiberty.a \ +@CONSTRUCTOR_PRIORITY_FALSE@ $(am__DEPENDENCIES_1) \ +@CONSTRUCTOR_PRIORITY_FALSE@ $(am__DEPENDENCIES_1) +@GCC_FALSE@initpri1_DEPENDENCIES = libgoldtest.a ../libgold.a \ +@GCC_FALSE@ ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \ +@GCC_FALSE@ $(am__DEPENDENCIES_1) +@NATIVE_LINKER_FALSE@initpri1_DEPENDENCIES = libgoldtest.a \ +@NATIVE_LINKER_FALSE@ ../libgold.a ../../libiberty/libiberty.a \ +@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1) \ +@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1) # Test --detect-odr-violations # Similar to --detect-odr-violations: check for undefined symbols in .so's -@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_7 = debug_msg.sh \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_8 = debug_msg.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ undef_symbol.sh # Create the data files that debug_msg.sh analyzes. @@ -163,11 +176,11 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ # We also want to make sure we do something reasonable when there's no # debug info available. For the best test, we use .so's. -@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_8 = debug_msg.err \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_9 = debug_msg.err \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ debug_msg_so.err \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ debug_msg_ndebug.err \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ undef_symbol.err -@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_9 = debug_msg.err \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_10 = debug_msg.err \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ debug_msg_so.err \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ debug_msg_ndebug.err \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ undef_symbol.err @@ -180,16 +193,16 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ # sections, because it requires output-file resizing. # Test symbol versioning. -@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_10 = flagstest_compress_debug_sections \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_11 = flagstest_compress_debug_sections \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ flagstest_o_specialfile \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ flagstest_o_specialfile_and_compress_debug_sections \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ ver_test ver_test_2 ver_test_6 \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_1 script_test_2 \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ justsyms binary_test -@GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@am__append_11 = \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@am__append_12 = \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@ ver_test_2.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@ ver_test_5.sh -@GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@am__append_12 = \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@am__append_13 = \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@ ver_test_2.syms \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@READELF_TRUE@ ver_test_5.syms @GCC_FALSE@script_test_1_DEPENDENCIES = libgoldtest.a ../libgold.a \ @@ -220,13 +233,13 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ @NATIVE_LINKER_FALSE@ ../libgold.a ../../libiberty/libiberty.a \ @NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1) \ @NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1) -@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_13 = ver_matching_test.sh \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_14 = ver_matching_test.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ script_test_3.sh -@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_14 = ver_matching_test.stdout \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ script_test_3.stdout @GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_15 = ver_matching_test.stdout \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ script_test_3.stdout -@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_16 = script_test_3 +@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_16 = ver_matching_test.stdout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ script_test_3.stdout +@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_17 = script_test_3 subdir = testsuite DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -289,7 +302,8 @@ libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS) @GCC_TRUE@@NATIVE_LINKER_TRUE@@STATIC_TLS_TRUE@@TLS_TRUE@am__EXEEXT_5 = tls_static_test$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@STATIC_TLS_TRUE@@TLS_TRUE@ tls_static_pic_test$(EXEEXT) @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@am__EXEEXT_6 = tls_shared_nonpic_test$(EXEEXT) -@GCC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_7 = flagstest_compress_debug_sections$(EXEEXT) \ +@CONSTRUCTOR_PRIORITY_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_7 = initpri1$(EXEEXT) +@GCC_TRUE@@NATIVE_LINKER_TRUE@am__EXEEXT_8 = flagstest_compress_debug_sections$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ flagstest_o_specialfile$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ flagstest_o_specialfile_and_compress_debug_sections$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ ver_test$(EXEEXT) \ @@ -299,7 +313,7 @@ libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS) @GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_2$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ justsyms$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ binary_test$(EXEEXT) -@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__EXEEXT_8 = script_test_3$(EXEEXT) +@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__EXEEXT_9 = script_test_3$(EXEEXT) basic_pic_test_SOURCES = basic_pic_test.c basic_pic_test_OBJECTS = basic_pic_test.$(OBJEXT) basic_pic_test_LDADD = $(LDADD) @@ -425,6 +439,10 @@ flagstest_o_specialfile_and_compress_debug_sections_LDADD = $(LDADD) flagstest_o_specialfile_and_compress_debug_sections_DEPENDENCIES = \ libgoldtest.a ../libgold.a ../../libiberty/libiberty.a \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am__initpri1_SOURCES_DIST = initpri1.c +@CONSTRUCTOR_PRIORITY_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am_initpri1_OBJECTS = initpri1.$(OBJEXT) +initpri1_OBJECTS = $(am_initpri1_OBJECTS) +initpri1_LDADD = $(LDADD) am__justsyms_SOURCES_DIST = justsyms_1.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@am_justsyms_OBJECTS = \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ justsyms_1.$(OBJEXT) @@ -638,10 +656,10 @@ SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \ $(exception_static_test_SOURCES) $(exception_test_SOURCES) \ flagstest_compress_debug_sections.c flagstest_o_specialfile.c \ flagstest_o_specialfile_and_compress_debug_sections.c \ - $(justsyms_SOURCES) $(object_unittest_SOURCES) \ - $(script_test_1_SOURCES) $(script_test_2_SOURCES) \ - script_test_3.c $(tls_pic_test_SOURCES) \ - $(tls_shared_ie_test_SOURCES) \ + $(initpri1_SOURCES) $(justsyms_SOURCES) \ + $(object_unittest_SOURCES) $(script_test_1_SOURCES) \ + $(script_test_2_SOURCES) script_test_3.c \ + $(tls_pic_test_SOURCES) $(tls_shared_ie_test_SOURCES) \ $(tls_shared_nonpic_test_SOURCES) $(tls_shared_test_SOURCES) \ $(tls_static_pic_test_SOURCES) $(tls_static_test_SOURCES) \ $(tls_test_SOURCES) $(two_file_mixed_2_shared_test_SOURCES) \ @@ -678,8 +696,8 @@ DIST_SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \ $(am__exception_test_SOURCES_DIST) \ flagstest_compress_debug_sections.c flagstest_o_specialfile.c \ flagstest_o_specialfile_and_compress_debug_sections.c \ - $(am__justsyms_SOURCES_DIST) $(object_unittest_SOURCES) \ - $(am__script_test_1_SOURCES_DIST) \ + $(am__initpri1_SOURCES_DIST) $(am__justsyms_SOURCES_DIST) \ + $(object_unittest_SOURCES) $(am__script_test_1_SOURCES_DIST) \ $(am__script_test_2_SOURCES_DIST) script_test_3.c \ $(am__tls_pic_test_SOURCES_DIST) \ $(am__tls_shared_ie_test_SOURCES_DIST) \ @@ -724,6 +742,8 @@ CATOBJEXT = @CATOBJEXT@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +CONSTRUCTOR_PRIORITY_FALSE = @CONSTRUCTOR_PRIORITY_FALSE@ +CONSTRUCTOR_PRIORITY_TRUE = @CONSTRUCTOR_PRIORITY_TRUE@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ @@ -862,13 +882,13 @@ INCLUDES = \ # .o's), but not all of them (such as .so's and .err files). We # improve on that here. automake-1.9 info docs say "mostlyclean" is # the right choice for files 'make' builds that people rebuild. -MOSTLYCLEANFILES = *.so $(am__append_9) $(am__append_15) +MOSTLYCLEANFILES = *.so $(am__append_10) $(am__append_16) # We will add to these later, for each individual test. Note # that we add each test under check_SCRIPTS or check_PROGRAMS; # the TESTS variable is automatically populated from these. -check_SCRIPTS = $(am__append_7) $(am__append_11) $(am__append_13) -check_DATA = $(am__append_8) $(am__append_12) $(am__append_14) +check_SCRIPTS = $(am__append_8) $(am__append_12) $(am__append_14) +check_DATA = $(am__append_9) $(am__append_13) $(am__append_15) TESTS = $(check_SCRIPTS) $(check_PROGRAMS) # --------------------------------------------------------------------- @@ -1074,6 +1094,9 @@ binary_unittest_SOURCES = binary_unittest.cc @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_shared_nonpic_test_DEPENDENCIES = gcctestdir/ld tls_test_shared_nonpic.so @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_shared_nonpic_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,. @FN_PTRS_IN_SO_WITHOUT_PIC_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@tls_shared_nonpic_test_LDADD = tls_test_shared_nonpic.so -lpthread +@CONSTRUCTOR_PRIORITY_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@initpri1_SOURCES = initpri1.c +@CONSTRUCTOR_PRIORITY_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@initpri1_DEPENDENCIES = gcctestdir/ld +@CONSTRUCTOR_PRIORITY_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@initpri1_LDFLAGS = -Bgcctestdir/ @GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_SOURCES = ver_test_main.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_DEPENDENCIES = gcctestdir/ld ver_test_1.so ver_test_2.so ver_test_4.so @GCC_TRUE@@NATIVE_LINKER_TRUE@ver_test_LDFLAGS = -Bgcctestdir/ -Wl,-R,. @@ -1219,6 +1242,9 @@ exception_test$(EXEEXT): $(exception_test_OBJECTS) $(exception_test_DEPENDENCIES @NATIVE_LINKER_FALSE@flagstest_o_specialfile_and_compress_debug_sections$(EXEEXT): $(flagstest_o_specialfile_and_compress_debug_sections_OBJECTS) $(flagstest_o_specialfile_and_compress_debug_sections_DEPENDENCIES) @NATIVE_LINKER_FALSE@ @rm -f flagstest_o_specialfile_and_compress_debug_sections$(EXEEXT) @NATIVE_LINKER_FALSE@ $(LINK) $(flagstest_o_specialfile_and_compress_debug_sections_LDFLAGS) $(flagstest_o_specialfile_and_compress_debug_sections_OBJECTS) $(flagstest_o_specialfile_and_compress_debug_sections_LDADD) $(LIBS) +initpri1$(EXEEXT): $(initpri1_OBJECTS) $(initpri1_DEPENDENCIES) + @rm -f initpri1$(EXEEXT) + $(LINK) $(initpri1_LDFLAGS) $(initpri1_OBJECTS) $(initpri1_LDADD) $(LIBS) justsyms$(EXEEXT): $(justsyms_OBJECTS) $(justsyms_DEPENDENCIES) @rm -f justsyms$(EXEEXT) $(CXXLINK) $(justsyms_LDFLAGS) $(justsyms_OBJECTS) $(justsyms_LDADD) $(LIBS) @@ -1348,6 +1374,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flagstest_compress_debug_sections.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flagstest_o_specialfile.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flagstest_o_specialfile_and_compress_debug_sections.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initpri1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/justsyms_1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_1.Po@am__quote@ diff --git a/gold/testsuite/initpri1.c b/gold/testsuite/initpri1.c new file mode 100644 index 0000000..4826389 --- /dev/null +++ b/gold/testsuite/initpri1.c @@ -0,0 +1,87 @@ +/* initpri1.c -- test constructor priorities. + + Copyright 2007, 2008 Free Software Foundation, Inc. + Copied from the gcc testsuite, where the test was contributed by + Mark Mitchell <mark@codesourcery.com>. + + This file is part of gold. + + This program 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 of the License, or + (at your option) any later version. + + This program 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. + + This is a test of a common symbol in the main program and a + versioned symbol in a shared library. The common symbol in the + main program should override the shared library symbol. */ + +#include <stdlib.h> + +int i; +int j; + +void c1() __attribute__((constructor (500))); +void c2() __attribute__((constructor (700))); +void c3() __attribute__((constructor (600))); + +void c1() { + if (i++ != 0) + abort (); +} + +void c2() { + if (i++ != 2) + abort (); +} + +void c3() { + if (i++ != 1) + abort (); +} + +void d1() __attribute__((destructor (500))); +void d2() __attribute__((destructor (700))); +void d3() __attribute__((destructor (600))); + +void d1() { + if (--i != 0) + abort (); +} + +void d2() { + if (--i != 2) + abort (); +} + +void d3() { + if (j != 2) + abort (); + if (--i != 1) + abort (); +} + +void cd4() __attribute__((constructor (800), destructor (800))); + +void cd4() { + if (i != 3) + abort (); + ++j; +} + +int main () { + if (i != 3) + return 1; + if (j != 1) + abort (); + return 0; +} |