Skip to content

Commit

Permalink
i#3348 sym conflicts: support not hiding symbols in static DR lib (#3411
Browse files Browse the repository at this point in the history
)

Adds a new dynamorio_static_nohide library which is only built when
tests are built.  It is used for sanity checks on global symbol names
for better interoperability with toolchains where objcopy
--localize-hidden is a pain point.

Renames core/CMake_globalize_pic_thunks.cmake to
core/CMake_finalize_static_lib.cmake to better reflect the multiple
steps it is taking beyond just the thunks.

Adds a new test api.static_symbols of linking dynamorio_static_nohide.

Adds a new post-build step that runs a new script
CMake_symbol_check.cmake which looks for likely-to-conflict symbols in
dynamorio_static_nohide.

Issue: #3348
  • Loading branch information
derekbruening committed Mar 2, 2019
1 parent a1dbd0b commit 0bbf5d5
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 21 deletions.
56 changes: 42 additions & 14 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -783,31 +783,31 @@ endif (UNIX)
###########################################################################
# DynamoRIO static core library

# XXX i#1997: not fully supported on Mac yet
if (NOT APPLE)
add_library(dynamorio_static STATIC
# hide_symbols is only honored for UNIX.
function (configure_static_core_lib name hide_symbols)
add_library(${name} STATIC
${CORE_SRCS} ${ARCH_SRCS} ${OS_SRCS})

configure_core_lib(dynamorio_static)
configure_core_lib(${name})

append_property_list(TARGET dynamorio_static COMPILE_DEFINITIONS "STATIC_LIBRARY")
append_property_list(TARGET ${name} COMPILE_DEFINITIONS "STATIC_LIBRARY")

if (UNIX)
# We build static DR as PIC in case we're linked into a .so or a PIE.
append_property_string(TARGET dynamorio_static COMPILE_FLAGS "-fPIC")
target_link_libraries(dynamorio_static dl)
append_property_string(TARGET ${name} COMPILE_FLAGS "-fPIC")
target_link_libraries(${name} dl)

# We need to do extra work to hide symbols in the static library build.
# First we do a partial link with ld -r, which makes a single libdynamorio.o
# object file. Then we use objcopy --localize-hidden to hide all
# non-exported symbols.
# non-exported symbols, if hide_symbols is set.
if (NOT EXISTS ${CMAKE_OBJCOPY})
message( "dynamorio_static requires objcopy")
message( "${name} requires objcopy")
endif ()

set(locvar_name dynamorio_static_loc)
set(locvar_name ${name}_loc)
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${locvar_name}.cmake" CONTENT
"set(${locvar_name} \"$<TARGET_FILE:dynamorio_static>\")\n")
"set(${locvar_name} \"$<TARGET_FILE:${name}>\")\n")

string(REPLACE " " "\;" partial_link_flags "${CMAKE_C_FLAGS}") # Get -m32/64

Expand All @@ -820,17 +820,18 @@ if (NOT APPLE)
set(disable_pie_flag "-no-pie")
endif ()

add_custom_command(TARGET dynamorio_static
add_custom_command(TARGET ${name}
POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -D lib_fileloc=${CMAKE_CURRENT_BINARY_DIR}/${locvar_name}
-D CMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-D partial_link_flags=${partial_link_flags}
-D disable_pie_flag=${disable_pie_flag}
-D localize_hidden=${hide_symbols}
-D CMAKE_OBJCOPY=${CMAKE_OBJCOPY}
-D CMAKE_AR=${CMAKE_AR}
-D CMAKE_RANLIB=${CMAKE_RANLIB}
-P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_globalize_pic_thunks.cmake
-P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_finalize_static_lib.cmake
WORKING_DIRECTORY ${DR_LIBRARY_OUTPUT_DIRECTORY}
VERBATIM
)
Expand All @@ -842,9 +843,36 @@ if (NOT APPLE)
# here to make sure things get linked in the right order to avoid duplicate
# definitions. See the case 4125 msvcrt comment above about why we need a C
# library.
target_link_libraries(dynamorio_static ${WIN32_C_LIB} ntdll_imports)
target_link_libraries(${name} ${WIN32_C_LIB} ntdll_imports)

endif (UNIX)
endfunction()

# XXX i#1997: not fully supported on Mac yet
if (NOT APPLE)
configure_static_core_lib(dynamorio_static ON)
if (UNIX AND BUILD_TESTS)
# Create a version without hidden symbols to support that model.
# The symbol hiding step is complex and for some toolchains it is simpler
# to avoid it and count on symbol names preventing collisions (i#3348).
configure_static_core_lib(dynamorio_static_nohide OFF)

if (LINUX AND READELF_EXECUTABLE)
# We already located readelf above for the shared lib.
# We also already created a loc.cmake file in configure_static_core_lib.
set(locvar_name dynamorio_static_nohide_loc)
add_custom_command(TARGET dynamorio_static_nohide
POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -D lib_fileloc=${CMAKE_CURRENT_BINARY_DIR}/${locvar_name}
-D READELF_EXECUTABLE=${READELF_EXECUTABLE}
-D CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}
-P ${CMAKE_CURRENT_SOURCE_DIR}/CMake_symbol_check.cmake
VERBATIM
)
endif ()
endif ()

endif (NOT APPLE)

###########################################################################
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# **********************************************************
# Copyright (c) 2015-2017 Google, Inc. All rights reserved.
# Copyright (c) 2015-2019 Google, Inc. All rights reserved.
# Copyright (c) 2009 VMware, Inc. All rights reserved.
# **********************************************************

Expand Down Expand Up @@ -35,6 +35,7 @@
# + "CMAKE_C_COMPILER" to point to CC compiler
# + "partial_link_flags" to linker flags incl. -m32/-m64
# + "disable_pie_flag" to -no-pie or ""
# + "localize_hidden" to ON or OFF
# + "CMAKE_OBJCOPY" to point to objcopy
# + "CMAKE_AR" to point to ar
# + "CMAKE_RANLIB" to point to ranlib
Expand All @@ -59,10 +60,12 @@ if (cmd_error OR cmd_result)
message(FATAL_ERROR "*** ${CMAKE_C_COMPILER} failed (${cmd_result}): ***\n${cmd_error}")
endif ()

execute_process(COMMAND
${CMAKE_OBJCOPY} --localize-hidden "${dynamorio_dot_o}"
ERROR_VARIABLE cmd_error
)
if (localize_hidden)
execute_process(COMMAND
${CMAKE_OBJCOPY} --localize-hidden "${dynamorio_dot_o}"
ERROR_VARIABLE cmd_error
)
endif ()

if (cmd_error OR cmd_result)
message(FATAL_ERROR "*** ${CMAKE_OBJCOPY} failed (${cmd_result}): ***\n${cmd_error}")
Expand Down
3 changes: 1 addition & 2 deletions core/CMake_readelf.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# **********************************************************
# Copyright (c) 2015-2017 Google, Inc. All rights reserved.
# Copyright (c) 2015-2019 Google, Inc. All rights reserved.
# Copyright (c) 2009 VMware, Inc. All rights reserved.
# **********************************************************

Expand Down Expand Up @@ -33,7 +33,6 @@
# + "lib_fileloc" to /absolute/path/<name> that represents a file <name>.cmake that
# contains a set(<name> <path>) where path is the target binary location.
# + "READELF_EXECUTABLE" so it can be a cache variable
# + "lib" to point to target library
# + "check_textrel" to ON or OFF to check to text relocations
# + "check_deps" to ON or OFF to check for zero deps
# + "check_libc" to ON or OFF to check for too-recent libc imports
Expand Down
98 changes: 98 additions & 0 deletions core/CMake_symbol_check.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# **********************************************************
# Copyright (c) 2019 Google, Inc. All rights reserved.
# **********************************************************

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.

# i#3348: Avoid symbol conflicts when linking DR statically.

# Caller must set:
# + "lib_fileloc" to /absolute/path/<name> that represents a file <name>.cmake that
# contains a set(<name> <path>) where path is the target binary location.
# + "READELF_EXECUTABLE" so it can be a cache variable
# + "CMAKE_SYSTEM_PROCESSOR"

include(${lib_fileloc}.cmake)

get_filename_component(lib_file ${lib_fileloc} NAME)

# Get the list of symbols.
execute_process(COMMAND
${READELF_EXECUTABLE} -s ${${lib_file}}
RESULT_VARIABLE readelf_result
ERROR_VARIABLE readelf_error
OUTPUT_VARIABLE output
)
if (readelf_result OR readelf_error)
message(FATAL_ERROR "*** ${READELF_EXECUTABLE} failed: ***\n${readelf_error}")
endif (readelf_result OR readelf_error)

# Limit to global defined symbols: no "UND".
string(REGEX MATCHALL "([^\n]+ GLOBAL [A-Z]+ +[^U ]+ [^\n]+)\n" globals "${output}")

# Now limit to single-word symbols, which are most likely to conflict.
string(REGEX MATCHALL " [^_\\.]+\n" oneword "${globals}")

string(REGEX MATCHALL "([^\n]+)\n" lines "${oneword}")
foreach(line ${lines})
set(is_ok OFF)
# We have some legacy exceptions we allow until we've renamed them all.
if (line MATCHES "crc32\n" OR
line MATCHES "debugRegister\n" OR
line MATCHES "decode\n" OR
line MATCHES "disassemble\n" OR
line MATCHES "dispatch\n" OR
line MATCHES "emulate\n" OR
line MATCHES "extensions\n" OR
line MATCHES "initstack\n" OR
line MATCHES "loginst\n" OR
line MATCHES "logopnd\n" OR
line MATCHES "logtrace\n" OR
line MATCHES "mangle\n" OR
line MATCHES "MD5Final\n" OR
line MATCHES "MD5Init\n" OR
line MATCHES "MD5Update\n" OR
line MATCHES "notify\n" OR
line MATCHES "regparms\n" OR
line MATCHES "stackdump\n" OR
line MATCHES "stats\n")
# OK: an exception we allow for now.
set(is_ok ON)
endif ()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR
CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
if (line MATCHES "memcpy\n" OR
line MATCHES "memset\n")
# XXX i#3348: We need to remove these from the aarchxx builds too.
set(is_ok ON)
endif ()
endif ()
if (NOT is_ok)
message(FATAL_ERROR
"*** Error: ${${lib_file}} contains a likely-to-conflict symbol: ${line}")
endif ()
endforeach()
10 changes: 10 additions & 0 deletions suite/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2380,6 +2380,16 @@ endif ()
tobuild_api(api.static_sideline_FLAKY api/static_sideline.c "" "" OFF ON)
target_link_libraries(api.static_sideline_FLAKY ${libmath})
link_with_pthread(api.static_sideline_FLAKY)

# i#3348: Ensure that DR without local symbols hidden is linkable and
# avoids conflicts (though CMake_symbol_check does most of the conflict
# checking).
tobuild_api(api.static_symbols api/static_symbols.c "" "" OFF ON)
get_target_property(lib_list api.static_symbols LINK_LIBRARIES)
list(REMOVE_ITEM lib_list dynamorio_static)
set_property(TARGET api.static_symbols PROPERTY LINK_LIBRARIES ${lib_list})
target_link_libraries(api.static_symbols dynamorio_static_nohide)
append_link_flags(api.static_symbols "-Wl,--warn-common -Wl,--fatal-warnings")
endif ()
endif ()

Expand Down
98 changes: 98 additions & 0 deletions suite/tests/api/static_symbols.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* **********************************************************
* Copyright (c) 2019 Google, Inc. All rights reserved.
* **********************************************************/

/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

/* Ensures that static DR does not produce symbol conflicts with the application.
* We build with -Wl,--warn-common to get warnings about common symbols too.
*/

#include "configure.h"
#include "dr_api.h"
#include "tools.h"

/* We can't really test individual symbols very well here as a future-changes test
* because we can only test those that have conflicted before, but we at least
* test libc startup conflicts and general linking of the no-hide library.
* CMake_symbol_check.cmake does more systematic checks.
*/

#if 0 /* i#3348: Disabling until we rename these symbols. */
int *initstack = NULL;
byte *initstack_app_xsp = NULL;

bool
is_on_initstack(void)
{
print("in app's %s\n", __FUNCTION__);
}

void
add_thread(void)
{
print("in app's %s\n", __FUNCTION__);
}
#endif

int
pathcmp(void)
{
print("in app's %s\n", __FUNCTION__);
return 0;
}

void
test_symbol_conflicts(void)
{
#if 0 /* i#3348: Disabling until we rename these symbols. */
print("initstack is %p\n", initstack);
initstack = NULL;
#endif
pathcmp();
}

int
main(int argc, const char *argv[])
{
print("pre-DR init\n");
dr_app_setup();
assert(!dr_app_running_under_dynamorio());

print("pre-DR start\n");
dr_app_start();
assert(dr_app_running_under_dynamorio());

test_symbol_conflicts();

print("pre-DR stop\n");
dr_app_stop_and_cleanup();
print("all done\n");
return 0;
}
5 changes: 5 additions & 0 deletions suite/tests/api/static_symbols.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pre-DR init
pre-DR start
in app's pathcmp
pre-DR stop
all done

0 comments on commit 0bbf5d5

Please sign in to comment.