Skip to content

Commit e688e21

Browse files
wkozaczuknyh
authored andcommitted
Enhance getopt family of functions to work with PIEs
This patch enhances implementation of getopt() and getopt_long() functions to work with PIEs. Newer GCC compiler optimizes PIEs by emitting machine code with so called copy relocations and affects how caller (PIEs) and callee (OSv kernel) see same logical global variables like optarg. New implementation of getopt() and getopt_long() detects if caller uses its (other) copies of global variables like optind and copies its value to the kernel copy. Likewise on return from the getopt() function it copies values of all "output" global variables from kernel to the equivalent caller copies of those variables. Fixes #689 Signed-off-by: Waldemar Kozaczuk <[email protected]> Message-Id: <[email protected]>
1 parent 7a4d96b commit e688e21

File tree

7 files changed

+335
-3
lines changed

7 files changed

+335
-3
lines changed

core/elf.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,19 @@ void* object::lookup(const char* symbol)
147147
return sm.relocated_addr();
148148
}
149149

150+
void* object::cached_lookup(const std::string& name)
151+
{
152+
auto cached_address_it = _cached_symbols.find(name);
153+
if (cached_address_it != _cached_symbols.end()) {
154+
return cached_address_it->second;
155+
}
156+
else {
157+
void *symbol_address = lookup(name.c_str());
158+
_cached_symbols[name] = symbol_address;
159+
return symbol_address;
160+
}
161+
}
162+
150163
std::vector<Elf64_Shdr> object::sections()
151164
{
152165
size_t bytes = size_t(_ehdr.e_shentsize) * _ehdr.e_shnum;

include/osv/elf.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <stack>
1515
#include <memory>
1616
#include <unordered_set>
17+
#include <unordered_map>
1718
#include <osv/types.h>
1819
#include <atomic>
1920

@@ -349,6 +350,7 @@ public:
349350
void run_fini_funcs();
350351
template <typename T = void>
351352
T* lookup(const char* name);
353+
void* cached_lookup(const std::string& name);
352354
dladdr_info lookup_addr(const void* addr);
353355
bool contains_addr(const void* addr);
354356
ulong module_index() const;
@@ -415,6 +417,8 @@ protected:
415417
bool _is_executable;
416418
bool is_core();
417419

420+
std::unordered_map<std::string,void*> _cached_symbols;
421+
418422
// Keep list of references to other modules, to prevent them from being
419423
// unloaded. When this object is unloaded, the reference count of all
420424
// objects listed here goes down, and they too may be unloaded.

libc/misc/getopt.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <limits.h>
55
#include <stdlib.h>
66
#include <libc/libc.hh>
7+
#include "getopt.hh"
78

89
extern "C" {
910

@@ -13,7 +14,7 @@ int optind=1, opterr=1, optopt, __optpos, __optreset=0;
1314
#define optpos __optpos
1415
weak_alias(__optreset, optreset);
1516

16-
int getopt(int argc, char * const argv[], const char *optstring)
17+
int __getopt(int argc, char * const argv[], const char *optstring)
1718
{
1819
int i;
1920
wchar_t c, d;
@@ -73,5 +74,11 @@ int getopt(int argc, char * const argv[], const char *optstring)
7374
return c;
7475
}
7576

77+
int getopt(int argc, char * const argv[], const char *optstring)
78+
{
79+
getopt_caller_vars_copier guard;
80+
return __getopt(argc, argv, optstring);
81+
}
82+
7683
weak_alias(getopt, __posix_getopt);
7784
}

libc/misc/getopt.hh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (C) 2019 Waldemar Kozaczuk
3+
*
4+
* This work is open source software, licensed under the terms of the
5+
* BSD license as described in the LICENSE file in the top-level directory.
6+
*/
7+
8+
#ifndef GETOPT_HH_
9+
#define GETOPT_HH_
10+
11+
#include <osv/app.hh>
12+
13+
// As explained in http://www.shrubbery.net/solaris9ab/SUNWdev/LLM/p22.html#CHAPTER4-84604
14+
// newer versions of gcc produce position independent executables with copies of
15+
// some global variables like those used by getopt() and getopt_long() for optimizations reason.
16+
// In those circumstances the caller of these functions uses different copies of
17+
// global variables (like optind) than the getopt() code that is part of OSv kernel.
18+
// For that reason in the beginning of these functions we need to copy values of the caller
19+
// copies of those variables to the kernel placeholders. Likewise on every return from the function
20+
// we need to copy the values of kernel copies of global variables to the caller ones.
21+
//
22+
// See http://man7.org/linux/man-pages/man3/getopt.3.html
23+
//
24+
// This is a simple RAII class for retrieving the caller's copy of the global opt* variables
25+
// on initialization, and returning them back to the caller on destruction.
26+
class getopt_caller_vars_copier {
27+
std::shared_ptr<osv::application_runtime> _runtime;
28+
int *other_optind;
29+
30+
public:
31+
getopt_caller_vars_copier() : _runtime(sched::thread::current()->app_runtime()) {
32+
if (_runtime) {
33+
auto obj = _runtime->app.lib();
34+
other_optind = reinterpret_cast<int*>(obj->cached_lookup("optind"));
35+
if (other_optind) {
36+
optind = *other_optind;
37+
}
38+
39+
auto other_opterr = reinterpret_cast<int*>(obj->cached_lookup("opterr"));
40+
if (other_opterr) {
41+
opterr = *other_opterr;
42+
}
43+
}
44+
}
45+
46+
~getopt_caller_vars_copier() {
47+
if (_runtime) {
48+
auto obj = _runtime->app.lib();
49+
if (other_optind) {
50+
*other_optind = optind;
51+
}
52+
auto other_optopt = reinterpret_cast<int*>(obj->cached_lookup("optopt"));
53+
if (other_optopt) {
54+
*other_optopt = optopt;
55+
}
56+
auto other_optarg = reinterpret_cast<char**>(obj->cached_lookup("optarg"));
57+
if (other_optarg) {
58+
*other_optarg = optarg;
59+
}
60+
}
61+
}
62+
};
63+
64+
extern "C" int __getopt(int argc, char * const argv[], const char *optstring);
65+
66+
#endif

libc/misc/getopt_long.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
#include <stddef.h>
22
#include <getopt.h>
33
#include <stdio.h>
4+
#include "getopt.hh"
45

56
extern "C" {
67

78
extern int __optpos, __optreset;
89

910
static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
1011
{
12+
getopt_caller_vars_copier guard;
13+
1114
if (!optind || __optreset) {
1215
__optreset = 0;
1316
__optpos = 0;
@@ -46,7 +49,7 @@ static int __getopt_long(int argc, char *const *argv, const char *optstring, con
4649
return '?';
4750
}
4851
}
49-
return getopt(argc, argv, optstring);
52+
return __getopt(argc, argv, optstring);
5053
}
5154

5255
int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)

modules/tests/Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ $(out)/%.so: $(out)/%.o
6262

6363
$(out)/tests/tst-non-fpic.o: CXXFLAGS:=$(subst -fPIC,-mcmodel=large,$(CXXFLAGS))
6464

65+
$(out)/tests/tst-getopt-pie.o: CXXFLAGS:=$(subst -fPIC,-fpie,$(CXXFLAGS))
66+
$(out)/tests/tst-getopt-pie.o: $(src)/tests/tst-getopt.cc
67+
$(makedir)
68+
$(call quiet, $(CXX) $(CXXFLAGS) -c -o $@ $<, CXX $*.cc)
69+
$(out)/tests/tst-getopt-pie.so: $(out)/tests/tst-getopt-pie.o
70+
$(call quiet, $(CXX) $(CXXFLAGS) -pie -o $@ $< $(LIBS), LD $*.so)
71+
6572
# The rofs test image mounts /tmp as ramfs and 4 tests that exercise file system
6673
# fail due to some unresolved bugs or other shortcomings of the ramfs implementation
6774
# and are temporarily removed from the rofs-only-tests list. The tests tst-readdir.so
@@ -117,7 +124,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so tst-bsd-evh.so \
117124
tst-ttyname.so tst-pthread-barrier.so tst-feexcept.so tst-math.so \
118125
tst-sigaltstack.so tst-fread.so tst-tcp-cork.so tst-tcp-v6.so \
119126
tst-calloc.so tst-crypt.so tst-non-fpic.so tst-small-malloc.so \
120-
tst-mmx-fpu.so
127+
tst-mmx-fpu.so tst-getopt.so tst-getopt-pie.so
121128
# libstatic-thread-variable.so tst-static-thread-variable.so \
122129
123130
tests += testrunner.so

0 commit comments

Comments
 (0)