From 985e026451640c880e13827454aa31b7a636d1bc Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 15 Apr 2021 00:36:42 +0930 Subject: [PATCH] PR27725, better objcopy -p times Nanosecond rather than second resolution. PR 27725 * configure.ac: Check for sys/time.h and utimensat. Use standard checks for mkstemp and mkdtemp. Whitespace. Check for nanosecond members of struct stat. * rename.c: Prefer sys/time.h for utimes over utime.h for utime. (STAT_TIMESPEC, STAT_TIMESPEC_NS): Define (get_stat_atime_ns, get_stat_mtime_ns): New inline functions. (get_stat_atime, get_stat_mtime): Likewise. (set_times): Choose first available of utimensat, utimes, utime. Use above inline functions to set timespec and timeval values. * configure: Regenerate. * config.in: Regenerate. * testsuite/binutils-all/objcopy.exp (objcopy_test): Add test of file timestamp when --preserve-dates is used. --- binutils/ChangeLog | 17 ++ binutils/config.in | 22 +++ binutils/configure | 182 +++++++++++++++++--- binutils/configure.ac | 65 +++++-- binutils/rename.c | 98 +++++++++-- binutils/testsuite/binutils-all/objcopy.exp | 12 ++ 6 files changed, 347 insertions(+), 49 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index f8a74d14620..212c35327ba 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,20 @@ +2021-04-15 Alan Modra + + PR 27725 + * configure.ac: Check for sys/time.h and utimensat. Use standard + checks for mkstemp and mkdtemp. Whitespace. Check for nanosecond + members of struct stat. + * rename.c: Prefer sys/time.h for utimes over utime.h for utime. + (STAT_TIMESPEC, STAT_TIMESPEC_NS): Define + (get_stat_atime_ns, get_stat_mtime_ns): New inline functions. + (get_stat_atime, get_stat_mtime): Likewise. + (set_times): Choose first available of utimensat, utimes, utime. + Use above inline functions to set timespec and timeval values. + * configure: Regenerate. + * config.in: Regenerate. + * testsuite/binutils-all/objcopy.exp (objcopy_test): Add test of + file timestamp when --preserve-dates is used. + 2021-04-15 Alan Modra PR 27456 diff --git a/binutils/config.in b/binutils/config.in index cc6aad16ddd..e79967c5c14 100644 --- a/binutils/config.in +++ b/binutils/config.in @@ -115,6 +115,18 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H +/* Define to 1 if `st_atimensec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMENSEC + +/* Define to 1 if `st_atimespec.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + +/* Define to 1 if `st_atim.st__tim.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC + +/* Define to 1 if `st_atim.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FILE_H @@ -124,6 +136,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H @@ -133,6 +148,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define to 1 if you have the `utimensat' function. */ +#undef HAVE_UTIMENSAT + /* Define to 1 if you have the `utimes' function. */ #undef HAVE_UTIMES @@ -182,6 +200,10 @@ /* Define to 1 if user symbol names have a leading underscore, 0 if not. */ #undef TARGET_PREPENDS_UNDERSCORE +/* Define to 1 if the type of the st_atim member of a struct stat is struct + timespec. */ +#undef TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC + /* Use b modifier when opening binary files? */ #undef USE_BINARY_FOPEN diff --git a/binutils/configure b/binutils/configure index 82720324e0d..1f855268c6f 100755 --- a/binutils/configure +++ b/binutils/configure @@ -2153,6 +2153,63 @@ rm -f conftest.val } # ac_fn_c_compute_int +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member + # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR @@ -10874,7 +10931,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10877 "configure" +#line 10934 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10980,7 +11037,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10983 "configure" +#line 11040 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12984,7 +13041,7 @@ _ACEOF # plugin-api.h tests HAVE_STDINT_H and HAVE_INTTYPES_H # Besides those, we need to check anything used in binutils/ not in C99. for ac_header in fcntl.h inttypes.h stdint.h sys/file.h \ - sys/stat.h sys/types.h unistd.h + sys/stat.h sys/time.h sys/types.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -13239,7 +13296,7 @@ $as_echo "#define HAVE_MMAP 1" >>confdefs.h fi rm -f conftest.mmap conftest.txt -for ac_func in getc_unlocked sbrk utimes +for ac_func in getc_unlocked mkdtemp mkstemp sbrk utimensat utimes do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -13251,23 +13308,10 @@ _ACEOF fi done -ac_fn_c_check_func "$LINENO" "mkstemp" "ac_cv_func_mkstemp" -if test "x$ac_cv_func_mkstemp" = xyes; then : - -$as_echo "#define HAVE_MKSTEMP 1" >>confdefs.h - -fi - -ac_fn_c_check_func "$LINENO" "mkdtemp" "ac_cv_func_mkdtemp" -if test "x$ac_cv_func_mkdtemp" = xyes; then : -$as_echo "#define HAVE_MKDTEMP 1" >>confdefs.h - -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mbstate_t" >&5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mbstate_t" >&5 $as_echo_n "checking for mbstate_t... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int @@ -13284,13 +13328,107 @@ else have_mbstate_t=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_mbstate_t" >&5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_mbstate_t" >&5 $as_echo "$have_mbstate_t" >&6; } - if test x"$have_mbstate_t" = xyes; then +if test x"$have_mbstate_t" = xyes; then $as_echo "#define HAVE_MBSTATE_T 1" >>confdefs.h - fi +fi + +# Copied from gnulib stat-time.m4. +# We should just switch over to using gnulib. +ac_fn_c_check_member "$LINENO" "struct stat" "st_atim.tv_nsec" "ac_cv_member_struct_stat_st_atim_tv_nsec" "#include + #include +" +if test "x$ac_cv_member_struct_stat_st_atim_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 1 +_ACEOF + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct stat.st_atim is of type struct timespec" >&5 +$as_echo_n "checking whether struct stat.st_atim is of type struct timespec... " >&6; } +if ${ac_cv_typeof_struct_stat_st_atim_is_struct_timespec+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #include + #if HAVE_SYS_TIME_H + # include + #endif + #include + struct timespec ts; + struct stat st; + +int +main () +{ + + st.st_atim = ts; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=yes +else + ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_typeof_struct_stat_st_atim_is_struct_timespec" >&5 +$as_echo "$ac_cv_typeof_struct_stat_st_atim_is_struct_timespec" >&6; } + if test $ac_cv_typeof_struct_stat_st_atim_is_struct_timespec = yes; then + +$as_echo "#define TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC 1" >>confdefs.h + + fi +else + ac_fn_c_check_member "$LINENO" "struct stat" "st_atimespec.tv_nsec" "ac_cv_member_struct_stat_st_atimespec_tv_nsec" "#include + #include +" +if test "x$ac_cv_member_struct_stat_st_atimespec_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC 1 +_ACEOF + + +else + ac_fn_c_check_member "$LINENO" "struct stat" "st_atimensec" "ac_cv_member_struct_stat_st_atimensec" "#include + #include +" +if test "x$ac_cv_member_struct_stat_st_atimensec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIMENSEC 1 +_ACEOF + + +else + ac_fn_c_check_member "$LINENO" "struct stat" "st_atim.st__tim.tv_nsec" "ac_cv_member_struct_stat_st_atim_st__tim_tv_nsec" "#include + #include +" +if test "x$ac_cv_member_struct_stat_st_atim_st__tim_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC 1 +_ACEOF + + +fi + +fi + +fi + +fi + # Some systems have frexp only in -lm, not in -lc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing frexp" >&5 diff --git a/binutils/configure.ac b/binutils/configure.ac index 3c5a8e13da3..b8ab642e068 100644 --- a/binutils/configure.ac +++ b/binutils/configure.ac @@ -182,24 +182,57 @@ AC_CHECK_SIZEOF([long long]) # plugin-api.h tests HAVE_STDINT_H and HAVE_INTTYPES_H # Besides those, we need to check anything used in binutils/ not in C99. AC_CHECK_HEADERS(fcntl.h inttypes.h stdint.h sys/file.h \ - sys/stat.h sys/types.h unistd.h) + sys/stat.h sys/time.h sys/types.h unistd.h) AC_HEADER_SYS_WAIT AC_FUNC_MMAP -AC_CHECK_FUNCS(getc_unlocked sbrk utimes) -AC_CHECK_FUNC([mkstemp], - AC_DEFINE([HAVE_MKSTEMP], 1, - [Define to 1 if you have the `mkstemp' function.])) -AC_CHECK_FUNC([mkdtemp], - AC_DEFINE([HAVE_MKDTEMP], 1, - [Define to 1 if you have the `mkdtemp' function.])) - AC_MSG_CHECKING([for mbstate_t]) - AC_TRY_COMPILE([#include ], - [mbstate_t teststate;], - have_mbstate_t=yes, have_mbstate_t=no) - AC_MSG_RESULT($have_mbstate_t) - if test x"$have_mbstate_t" = xyes; then - AC_DEFINE(HAVE_MBSTATE_T,1,[Define if mbstate_t exists in wchar.h.]) - fi +AC_CHECK_FUNCS(getc_unlocked mkdtemp mkstemp sbrk utimensat utimes) + +AC_MSG_CHECKING([for mbstate_t]) +AC_TRY_COMPILE([#include ], +[mbstate_t teststate;], +have_mbstate_t=yes, have_mbstate_t=no) +AC_MSG_RESULT($have_mbstate_t) +if test x"$have_mbstate_t" = xyes; then + AC_DEFINE(HAVE_MBSTATE_T,1,[Define if mbstate_t exists in wchar.h.]) +fi + +# Copied from gnulib stat-time.m4. +# We should just switch over to using gnulib. +AC_CHECK_MEMBERS([struct stat.st_atim.tv_nsec], + [AC_CACHE_CHECK([whether struct stat.st_atim is of type struct timespec], + [ac_cv_typeof_struct_stat_st_atim_is_struct_timespec], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[ + #include + #include + #if HAVE_SYS_TIME_H + # include + #endif + #include + struct timespec ts; + struct stat st; + ]], + [[ + st.st_atim = ts; + ]])], + [ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=yes], + [ac_cv_typeof_struct_stat_st_atim_is_struct_timespec=no])]) + if test $ac_cv_typeof_struct_stat_st_atim_is_struct_timespec = yes; then + AC_DEFINE([TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC], [1], + [Define to 1 if the type of the st_atim member of a struct stat is + struct timespec.]) + fi], + [AC_CHECK_MEMBERS([struct stat.st_atimespec.tv_nsec], [], + [AC_CHECK_MEMBERS([struct stat.st_atimensec], [], + [AC_CHECK_MEMBERS([struct stat.st_atim.st__tim.tv_nsec], [], [], + [#include + #include ])], + [#include + #include ])], + [#include + #include ])], + [#include + #include ]) # Some systems have frexp only in -lm, not in -lc. AC_SEARCH_LIBS(frexp, m) diff --git a/binutils/rename.c b/binutils/rename.c index 969acc12b30..2cbe46497ab 100644 --- a/binutils/rename.c +++ b/binutils/rename.c @@ -22,10 +22,10 @@ #include "bfd.h" #include "bucomm.h" -#ifdef HAVE_GOOD_UTIME_H -#include -#elif defined HAVE_UTIMES +#if defined HAVE_UTIMES #include +#elif defined HAVE_GOOD_UTIME_H +#include #endif /* The number of bytes to copy at once. */ @@ -86,6 +86,77 @@ simple_copy (int fromfd, const char *to, return 0; } +/* The following defines and inline functions are copied from gnulib. + FIXME: Use a gnulib import and stat-time.h instead. */ +#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC +# if defined TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC +# define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim) +# else +# define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.tv_nsec) +# endif +#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC +# define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim##espec) +#elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC +# define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim##ensec) +#elif defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC +# define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.st__tim.tv_nsec) +#endif + +/* Return the nanosecond component of *ST's access time. */ +inline long int +get_stat_atime_ns (struct stat const *st) +{ +# if defined STAT_TIMESPEC + return STAT_TIMESPEC (st, st_atim).tv_nsec; +# elif defined STAT_TIMESPEC_NS + return STAT_TIMESPEC_NS (st, st_atim); +# else + return 0; +# endif +} + +/* Return the nanosecond component of *ST's data modification time. */ +inline long int +get_stat_mtime_ns (struct stat const *st) +{ +# if defined STAT_TIMESPEC + return STAT_TIMESPEC (st, st_mtim).tv_nsec; +# elif defined STAT_TIMESPEC_NS + return STAT_TIMESPEC_NS (st, st_mtim); +# else + return 0; +# endif +} + +/* Return *ST's access time. */ +inline struct timespec +get_stat_atime (struct stat const *st) +{ +#ifdef STAT_TIMESPEC + return STAT_TIMESPEC (st, st_atim); +#else + struct timespec t; + t.tv_sec = st->st_atime; + t.tv_nsec = get_stat_atime_ns (st); + return t; +#endif +} + +/* Return *ST's data modification time. */ +inline struct timespec +get_stat_mtime (struct stat const *st) +{ +#ifdef STAT_TIMESPEC + return STAT_TIMESPEC (st, st_mtim); +#else + struct timespec t; + t.tv_sec = st->st_mtime; + t.tv_nsec = get_stat_mtime_ns (st); + return t; +#endif +} +/* End FIXME. */ + /* Set the times of the file DESTINATION to be the same as those in STATBUF. */ @@ -93,20 +164,25 @@ void set_times (const char *destination, const struct stat *statbuf) { int result; -#ifdef HAVE_GOOD_UTIME_H - struct utimbuf tb; - - tb.actime = statbuf->st_atime; - tb.modtime = statbuf->st_mtime; - result = utime (destination, &tb); +#if defined HAVE_UTIMENSAT + struct timespec times[2]; + times[0] = get_stat_atime (statbuf); + times[1] = get_stat_mtime (statbuf); + result = utimensat (AT_FDCWD, destination, times, 0); #elif defined HAVE_UTIMES struct timeval tv[2]; tv[0].tv_sec = statbuf->st_atime; - tv[0].tv_usec = 0; + tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000; tv[1].tv_sec = statbuf->st_mtime; - tv[1].tv_usec = 0; + tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000; result = utimes (destination, tv); +#elif defined HAVE_GOOD_UTIME_H + struct utimbuf tb; + + tb.actime = statbuf->st_atime; + tb.modtime = statbuf->st_mtime; + result = utime (destination, &tb); #else long tb[2]; diff --git a/binutils/testsuite/binutils-all/objcopy.exp b/binutils/testsuite/binutils-all/objcopy.exp index ef251876a02..e1df9ff748d 100644 --- a/binutils/testsuite/binutils-all/objcopy.exp +++ b/binutils/testsuite/binutils-all/objcopy.exp @@ -77,6 +77,7 @@ proc objcopy_test {testname srcfile type asflags ldflags} { return } set xflags "--preserve-dates" + sleep 1 } set got [binutils_run $OBJCOPY "$OBJCOPYFLAGS $xflags $t_tempfile $t_copyfile"] @@ -102,6 +103,17 @@ proc objcopy_test {testname srcfile type asflags ldflags} { if [string equal "" $exec_output] then { pass "objcopy $type ($testname)" + if { $type == "executable" } { + set dir [file dirname $t_copyfile] + set f2 [file tail $t_copyfile] + set status [remote_exec host find "$dir -name $f2 -newer $t_tempfile -print"] + set exec_output [lindex $status 1] + if [string equal "" $exec_output] then { + pass "objcopy $type ($testname) timestamp" + } else { + fail "objcopy $type ($testname) timestamp" + } + } } else { send_log "$exec_output\n" verbose "$exec_output" 1 -- 2.30.2