Skip to content

Commit

Permalink
pw_build: Add LinkerSymbol
Browse files Browse the repository at this point in the history
Change-Id: I452f2672ad2b8ed8ea7d6cce721bc1427b83429c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/242635
Commit-Queue: Jonathon Reinhart <[email protected]>
Reviewed-by: Austin Foxley <[email protected]>
Presubmit-Verified: CQ Bot Account <[email protected]>
Reviewed-by: Wyatt Hepler <[email protected]>
Lint: Lint 🤖 <[email protected]>
  • Loading branch information
JonathonReinhart authored and CQ Bot Account committed Oct 18, 2024
1 parent f66f4e4 commit 170f745
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ _doxygen_input_files = [ # keep-sorted: start
"$dir_pw_bluetooth/public/pw_bluetooth/low_energy/peripheral.h",
"$dir_pw_bluetooth_proxy/public/pw_bluetooth_proxy/h4_packet.h",
"$dir_pw_bluetooth_proxy/public/pw_bluetooth_proxy/proxy_host.h",
"$dir_pw_build/public/pw_build/linker_symbol.h",
"$dir_pw_build/public/pw_build/must_place.ld.h",
"$dir_pw_bytes/public/pw_bytes/alignment.h",
"$dir_pw_bytes/public/pw_bytes/bit.h",
Expand Down
24 changes: 24 additions & 0 deletions pw_build/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,30 @@ cc_library(
includes = ["public"],
)

cc_library(
name = "linker_symbol",
hdrs = ["public/pw_build/linker_symbol.h"],
includes = ["public"],
deps = [
"//third_party/fuchsia:stdcompat",
],
)

pw_cc_test(
name = "linker_symbol_test",
srcs = ["linker_symbol_test.cc"],
linkopts = [
"-T$(location linker_symbol_test.ld)",
],
target_compatible_with = [
"@platforms//os:linux",
],
deps = [
":linker_symbol",
":linker_symbol_test.ld",
],
)

pw_string_list_comparison_test(
name = "simple_glob",
actual = glob_dirs(
Expand Down
18 changes: 18 additions & 0 deletions pw_build/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,12 @@ pw_source_set("must_place") {
public = [ "public/pw_build/must_place.ld.h" ]
}

pw_source_set("linker_symbol") {
public_configs = [ ":default_config" ]
public = [ "public/pw_build/linker_symbol.h" ]
public_deps = [ "$dir_pw_third_party/fuchsia:stdcompat" ]
}

pw_doc_group("docs") {
sources = [
"bazel.rst",
Expand Down Expand Up @@ -449,6 +455,17 @@ if (current_toolchain != default_toolchain) {
deps = [ ":test_blob" ]
}

pw_test("linker_symbol_test") {
sources = [ "linker_symbol_test.cc" ]
enable_if = current_os == "linux"
deps = [ ":linker_symbol" ]
inputs = [ "linker_symbol_test.ld" ]
ldflags = [
"-T",
rebase_path("linker_symbol_test.ld", root_build_dir),
]
}

pw_test("module_config_test") {
public_configs = [ ":module_config_test_config" ]
sources = [ "module_config_test.cc" ]
Expand Down Expand Up @@ -521,6 +538,7 @@ if (current_toolchain != default_toolchain) {
tests = [
":cc_blob_library_test",
":file_prefix_map_test",
":linker_symbol_test",
]
}
}
36 changes: 35 additions & 1 deletion pw_build/linker_scripts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,41 @@ Linker Script Helpers
---------------------

``PW_MUST_PLACE``
-----------------
=================
.. doxygengroup:: pw_must_place
:content-only:
:members:

``LinkerSymbol``
================
.. doxygenclass:: pw::LinkerSymbol
:members:

.. note::

``LinkerSymbol`` does not support, and is not necessary for, symbols that
communicate a pointer value (i.e. an address). For those, simply define an
extern variable of the pointed-to type, e.g.:

.. code-block:: cpp
extern "C" uint32_t PTR_SYM;
``LinkerSymbol`` is superior to the traditional ``extern uint8_t FOO;``
``(uint32_t)&FOO`` method because it catches subtle errors:

* Missing ``extern`` specifier:

.. code-block:: none
error: use of deleted function 'pw::build::LinkerSymbol::LinkerSymbol()'
| LinkerSymbol oops;
| ^~~~
* Missing ``&`` operator:

.. code-block:: none
error: invalid cast from type 'pw::build::LinkerSymbol' to type 'uint32_t' {aka 'long unsigned int'}
| uint32_t val = (uint32_t)FOO_SYM;
| ^~~~~~~~~~~~~~~~~
68 changes: 68 additions & 0 deletions pw_build/linker_symbol_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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.

#include "pw_build/linker_symbol.h"

#include "gtest/gtest.h"

namespace pw {
namespace {

// These symbols are defined in linker_symbol_test.ld
extern "C" LinkerSymbol<int> FOO_SYM;
extern "C" LinkerSymbol BAR_SYM; // Test template default (uintptr_t)
extern "C" LinkerSymbol<int> NEGATIVE_SYM;
extern "C" LinkerSymbol<char> CHAR_SYM;

enum class MyEnum {
kValue7 = 7,
};
extern "C" LinkerSymbol<MyEnum> ENUM_SYM;

TEST(LinkerSymbolTest, ValueWorks) {
// You can use value() to get the value as the specified type.
auto value = FOO_SYM.value();
static_assert(std::is_same_v<decltype(value), int>);
EXPECT_EQ(value, 42);
}

TEST(LinkerSymbolTest, NegativeValueWorks) {
// LinkerSymbol works with negative integers.
EXPECT_EQ(NEGATIVE_SYM.value(), -567);
}

TEST(LinkerSymbolTest, CharValueWorks) {
// LinkerSymbol works with characters.
EXPECT_EQ(CHAR_SYM.value(), 'a');
}

TEST(LinkerSymbolTest, EnumValueWorks) {
// LinkerSymbol works with enums.
EXPECT_EQ(ENUM_SYM.value(), MyEnum::kValue7);
}

TEST(LinkerSymbolTest, ValueWorksDefaultType) {
// You can use value() to get the value as the default type (uintptr_t).
auto value = BAR_SYM.value();
static_assert(std::is_same_v<decltype(value), uintptr_t>);
EXPECT_EQ(value, 0xDEADBEEFu);
}

TEST(LinkerSymbolTest, CStyleCastWorks) {
// You can use a C-style cast, if you insist.
EXPECT_EQ((uintptr_t)&FOO_SYM, 42u);
}

} // namespace
} // namespace pw
20 changes: 20 additions & 0 deletions pw_build/linker_symbol_test.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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.
*/
FOO_SYM = 42;
BAR_SYM = 0xDEADBEEF;
NEGATIVE_SYM = -567;
CHAR_SYM = 0x61; /* 'a' */
ENUM_SYM = 7;
73 changes: 73 additions & 0 deletions pw_build/public/pw_build/linker_symbol.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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.
#pragma once

#include <cstdint>
#include <type_traits>

#include "lib/stdcompat/bit.h"

namespace pw {

/// Represents a symbol provided by the linker, i.e. via a linker script.
///
/// LinkerSymbol objects are used with linker-provided symbols that don't have
/// storage (which is common), and only provide a value e.g.
///
/// @code
/// MY_LINKER_VARIABLE = 42
/// @endcode
///
/// LinkerSymbol objects are not actual variables (do not have storage) and thus
/// cannot be created; they can only be used with an `extern "C"` declaration.
/// The purpose is to communicate *values* from the linker script to C++ code.
///
/// Example:
///
/// @code{.cpp}
/// #include "pw_build/linker_symbol.h"
///
/// extern "C" pw::LinkerSymbol<uint32_t> MY_LINKER_VARIABLE;
///
/// uint32_t GetMyLinkerVariable() {
/// return MY_LINKER_VARIABLE.value();
/// }
/// @endcode
///
/// @tparam T The type of the value communicated by the linker, defaulting to
/// `uintptr_t`. Must be an integral or enum type, no larger than
/// `uintptr_t`.
template <class T = uintptr_t>
class LinkerSymbol {
public:
LinkerSymbol() = delete;
~LinkerSymbol() = delete;
LinkerSymbol(const LinkerSymbol&) = delete;
LinkerSymbol(const LinkerSymbol&&) = delete;
LinkerSymbol& operator=(const LinkerSymbol&) = delete;
LinkerSymbol& operator=(const LinkerSymbol&&) = delete;

/// Gets the value of this linker symbol, converted to the specified type.
T value() const {
static_assert(std::is_integral_v<T> || std::is_enum_v<T>);
static_assert(sizeof(T) <= sizeof(uintptr_t));
return static_cast<T>(raw_value());
}

private:
/// Gets the raw value of this linker symbol.
uintptr_t raw_value() const { return cpp20::bit_cast<uintptr_t>(this); }
};

} // namespace pw

0 comments on commit 170f745

Please sign in to comment.