Skip to content

Commit

Permalink
pw_chrono: Introduce SystemClock backed link time wrappers
Browse files Browse the repository at this point in the history
Consolidate the existing build time backed wrappers under pw_chrono

Change-Id: I8fe10f7eacbec3b84c2e8358f3f5a7f58827883b
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/213551
Reviewed-by: Armando Montanez <[email protected]>
Lint: Lint 🤖 <[email protected]>
Commit-Queue: Austin Foxley <[email protected]>
  • Loading branch information
afoxley authored and CQ Bot Account committed Jun 4, 2024
1 parent d988f70 commit 6618a7d
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 101 deletions.
31 changes: 31 additions & 0 deletions pw_chrono/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,34 @@ pw_cc_test(
"//pw_unit_test",
],
)

py_binary(
name = "generate_build_time_header",
srcs = ["generate_build_time_header.py"],
)

genrule(
name = "build_time_header",
outs = ["build_time.h"],
cmd = "$(location :generate_build_time_header) $@",
tools = [":generate_build_time_header"],
)

cc_library(
name = "wrap_time_build_time",
srcs = [
"wrap_time_build_time.cc",
":build_time_header",
],
)

cc_library(
name = "wrap_time_system_clock",
srcs = ["wrap_time_system_clock.cc"],
linkopts = [
"-Wl,--wrap=gettimeofday",
"-Wl,--wrap=time",
],
deps = [":system_clock"],
alwayslink = 1,
)
35 changes: 35 additions & 0 deletions pw_chrono/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,41 @@ pw_test("system_timer_facade_test") {
]
}

# The ":time" target wraps the time() and gettimeofday(), which are
# commonly used by TLS libraries for expiration check.
config("time_wrap") {
# Link options that wrap C time calls.
ldflags = [
"-Wl,--wrap=time",
"-Wl,--wrap=gettimeofday",
]
visibility = [ ":*" ]
}

# The build time is obtained with a python script and put in a generated header
# file. The header file is included in build_time.cc
pw_python_action("generate_build_time_header") {
header_output = "$target_gen_dir/pw_chrono/build_time.h"
script = "generate_build_time_header.py"
outputs = [ header_output ]
args = [ rebase_path(header_output) ]
}

# The target provides a backend to :time that returns build time.
pw_source_set("wrap_time_build_time") {
all_dependent_configs = [ ":time_wrap" ]
include_dirs =
[ get_label_info(":generate_build_time_header", "target_gen_dir") ]
sources = [ "wrap_time_build_time.cc" ]
deps = [ ":generate_build_time_header" ]
}

pw_source_set("wrap_time_system_clock") {
all_dependent_configs = [ ":time_wrap" ]
sources = [ "wrap_time_system_clock.cc" ]
deps = [ ":system_clock" ]
}

pw_proto_library("protos") {
sources = [ "chrono.proto" ]
prefix = "pw_chrono_protos"
Expand Down
42 changes: 42 additions & 0 deletions pw_chrono/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,45 @@ if(NOT "${pw_chrono.system_timer_BACKEND}" STREQUAL "")
pw_chrono
)
endif()

function(generate_build_time_header NAME)
pw_parse_arguments(
NUM_POSITIONAL_ARGS
1
)

set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/${NAME}")
set(output "${out_dir}/pw_chrono/build_time.h")
get_filename_component(output_include_path "${output}" DIRECTORY)

set(gen_header "$ENV{PW_ROOT}/pw_chrono/generate_build_time_header.py")
LIST(APPEND gen_header_cmd python3 "${gen_header}" ${output})

add_custom_command(
COMMAND
${gen_header_cmd}
DEPENDS
${gen_header}
OUTPUT
${output})

add_custom_target("${NAME}._generate" DEPENDS ${output})

pw_add_library_generic("${NAME}" INTERFACE
PUBLIC_INCLUDES
${out_dir}
)
add_dependencies("${NAME}" "${NAME}._generate")
endfunction()

generate_build_time_header(pw_chrono.build_time_header)

pw_add_library(pw_chrono.wrap_time_build_time STATIC
SOURCES
wrap_time_build_time.cc
PUBLIC_DEPS
pw_chrono.build_time_header
PUBLIC_LINK_OPTIONS
-Wl,--wrap=time
-Wl,--wrap=gettimeofday
)
32 changes: 32 additions & 0 deletions pw_chrono/docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,35 @@ Example in C++
:maxdepth: 1

Backends <backends>

------------------
Libc Time Wrappers
------------------
The `gettimeofday <https://pubs.opengroup.org/onlinepubs/9699919799/functions/gettimeofday.html>`
and `time <https://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html>`
POSIX functions are defined to return the current time since the Epoch.
The default ``pw_toolchain/arg_gcc:newlib_os_interface_stubs`` stub for
``gettimeofday`` will cause a linker error if any code tried to use this
function, but it's common for software not written for embedded systems to
depend on this function being defined and returning something that increments
like a clock. In addition, some software depends on having ``gettimeofday``
return something much closer to the actual time so it can compare against well
known time points inside TLS certificates for instance.

For compatibility with such software, ``pw_toolchain`` provides two different
options to wrap libc time functions. Both of these are not recommended for
general time querying and are only intended to provide compatibility.

``pw_chrono:wrap_time_build_time``
==================================
Wrap ``gettimeofday`` and ``time`` with an implementation that returns a static
time value at which the library was built. Use this option if you need these
functions to return a known value greater than some point in the past.

``pw_chrono:wrap_time_system_clock``
====================================
Wrap ``gettimeofday`` and ``time`` with an implementation that uses
``pw::chrono::SystemClock`` to return the current time. Note the epoch is
determined by the SystemClock backend epoch, which on most embedded systems will
be time since boot. Use this option if you don't care about the time returned
being close to actual time, but do care that it increments like a real clock.
File renamed without changes.
18 changes: 7 additions & 11 deletions pw_tls_client/build_time.cc → pw_chrono/wrap_time_build_time.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,20 @@
// License for the specific language governing permissions and limitations under
// the License.

#include "build_time.h"

#include <time.h>

#include "pw_chrono/build_time.h"

#if __has_include(<sys/time.h>)
#include <sys/time.h>
#endif // __has_include(<sys/time.h>)

namespace pw::chrono {
namespace {
constexpr uint64_t kMicrosecondsPerSecond = 1'000'000;
}

#if __cplusplus
extern "C" {
#endif

time_t __wrap_time(time_t* t) {
extern "C" time_t __wrap_time(time_t* t) {
time_t ret =
static_cast<time_t>(kBuildTimeMicrosecondsUTC / kMicrosecondsPerSecond);
if (t) {
Expand All @@ -38,7 +36,7 @@ time_t __wrap_time(time_t* t) {

#if __has_include(<sys/time.h>)

int __wrap_gettimeofday(struct timeval* tv, void* tz) {
extern "C" int __wrap_gettimeofday(struct timeval* tv, void* tz) {
// The use of the timezone structure is obsolete (see docs "man
// gettimeofday"). Thus we don't consider it.
(void)tz;
Expand All @@ -49,6 +47,4 @@ int __wrap_gettimeofday(struct timeval* tv, void* tz) {

#endif // __has_include(<sys/time.h>)

#if __cplusplus
}
#endif
} // namespace pw::chrono
64 changes: 64 additions & 0 deletions pw_chrono/wrap_time_system_clock.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2024 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

// This file provides an implementation of time and gettimeofday that can be
// used with ld's --wrap option.

#include <time.h>

#if __has_include(<sys/time.h>)
#include <sys/time.h>
#endif // __has_include(<sys/time.h>)

#include <sys/time.h>

#include <chrono>

#include "pw_chrono/system_clock.h"

namespace pw::chrono {

extern "C" time_t __wrap_time(time_t* t) {
const pw::chrono::SystemClock::time_point now =
pw::chrono::SystemClock::now();
const time_t ret =
std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch())
.count();
if (t) {
*t = ret;
}
return ret;
}

#if __has_include(<sys/time.h>)

extern "C" int __wrap_gettimeofday(struct timeval* tv, void* tz) {
using namespace std::chrono_literals;
// The use of the timezone structure is obsolete (see docs "man
// gettimeofday"). Thus we don't consider it.
(void)tz;
// Get time since epoch from system clock.
const pw::chrono::SystemClock::time_point now =
pw::chrono::SystemClock::now();
const auto usec_since_epoch =
std::chrono::duration_cast<std::chrono::microseconds>(
now.time_since_epoch());
tv->tv_sec = (usec_since_epoch / 1s);
tv->tv_usec = (usec_since_epoch % 1s).count();
return 0;
}

#endif // __has_include(<sys/time.h>)

} // namespace pw::chrono
13 changes: 0 additions & 13 deletions pw_tls_client/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,6 @@ cc_library(
],
)

# TODO(zyecheng): The target requires a build_time.h header that defines a
# 'constexpr size_t kBuildTimeMicrosecondsUTC' variable for storing the build time.
# In gn build, this is generated by a python action target. Need to figure out a
# solution in bazel build.
cc_library(
name = "build_time",
srcs = [
"build_time.cc",
],
# TODO: b/257527057 - Get this to build.
tags = ["manual"],
)

cc_library(
name = "crlset",
hdrs = ["public/pw_tls_client/crlset.h"],
Expand Down
31 changes: 0 additions & 31 deletions pw_tls_client/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,8 @@ pw_source_set("fake_entropy") {
deps = [ "$dir_pw_log" ]
}

# The ":time" target wraps the time() and gettimeofday(), which are
# commonly used by TLS libraries for expiration check.
config("time_wrap") {
# Link options that wrap C time calls.
ldflags = [
"-Wl,--wrap=time",
"-Wl,--wrap=gettimeofday",
]
}

pw_facade("time") {
backend = pw_tls_client_TIME_BACKEND
public_configs = [ ":time_wrap" ]
public = []

# The target should only be used by TLS libraries to obtain date time
Expand All @@ -84,26 +73,6 @@ pw_facade("time") {
]
}

# The build time is obtained with a python script and put in a generated header
# file. The header file is included in build_time.cc
pw_python_action("generate_build_time_header") {
header_output = "$target_gen_dir/$target_name/build_time.h"
script = "generate_build_time_header.py"
outputs = [ header_output ]
args = [ rebase_path(header_output) ]
}

# The target provides a backend to :time that returns build time.
pw_source_set("build_time") {
time_injection_outputs = get_target_outputs(":generate_build_time_header")
include_dirs = [ get_path_info(time_injection_outputs[0], "dir") ]
sources = [ "build_time.cc" ]
deps = [
":generate_build_time_header",
":time.facade",
]
}

# TODO: b/235290724 - Add a python target to generate source file from the
# specified CRLSet file in `pw_tls_client_CRLSET_FILE`

Expand Down
43 changes: 0 additions & 43 deletions pw_tls_client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,47 +36,4 @@ pw_add_facade(pw_tls_client.pw_tls_client INTERFACE
pw_add_facade(pw_tls_client.time INTERFACE
BACKEND
pw_tls_client.time_BACKEND
PUBLIC_LINK_OPTIONS
-Wl,--wrap=time
-Wl,--wrap=gettimeofday
)

function(generate_build_time_header NAME)
pw_parse_arguments(
NUM_POSITIONAL_ARGS
1
)

set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/${NAME}")
set(output "${out_dir}/build_time.h")
get_filename_component(output_include_path "${output}" DIRECTORY)

set(gen_header "$ENV{PW_ROOT}/pw_tls_client/generate_build_time_header.py")
LIST(APPEND gen_header_cmd python3 "${gen_header}" ${output})

add_custom_command(
COMMAND
${gen_header_cmd}
DEPENDS
${gen_header}
OUTPUT
${output})

add_custom_target("${NAME}._generate" DEPENDS ${output})

pw_add_library_generic("${NAME}" INTERFACE
PUBLIC_INCLUDES
${output_include_path}
)
add_dependencies("${NAME}" "${NAME}._generate")
endfunction()

generate_build_time_header(pw_tls_client.build_time_header)

pw_add_library(pw_tls_client.build_time STATIC
SOURCES
build_time.cc
PUBLIC_DEPS
pw_tls_client.build_time_header
pw_tls_client.time.facade
)
2 changes: 1 addition & 1 deletion pw_tls_client/backend.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
pw_add_backend_variable(pw_tls_client.pw_tls_client_BACKEND)
pw_add_backend_variable(pw_tls_client.time_BACKEND
DEFAULT_BACKEND
pw_tls_client.build_time
pw_chrono.wrap_time_build_time
)
2 changes: 1 addition & 1 deletion pw_tls_client/configs.gni
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ declare_args() {
pw_tls_client_ENTROPY_BACKEND = "$dir_pw_tls_client:fake_entropy"

# Backend for pw_tls_client:time
pw_tls_client_TIME_BACKEND = "$dir_pw_tls_client:build_time"
pw_tls_client_TIME_BACKEND = "$dir_pw_chrono:wrap_time_build_time"

# The path to the CRLSet file.
pw_tls_client_CRLSET_FILE = ""
Expand Down
Loading

0 comments on commit 6618a7d

Please sign in to comment.