While tracing a bug in a static library we use in our iOS application I stumbled upon the following question:
The library we use has code that is automatically executed during the launch of our application, before our application's main method is executed. The stack trace while the function is executed looks as follows:
#0 0x000000010050ce04 in _runOnLoad ()
#1 0x000000012008ceb0 in ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) ()
#2 0x000000012008d050 in ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) ()
#3 0x0000000120088808 in ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) ()
#4 0x00000001200879b0 in ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) ()
#5 0x0000000120087a64 in ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) ()
#6 0x000000012007a08c in dyld::initializeMainExecutable() ()
#7 0x000000012007e0fc in dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) ()
#8 0x0000000120079044 in _dyld_start ()
My question is: What is going on here?
How does the image loader know that this method should be executed? When looking at the assembler of the static library, _runOnLoad() looks like a regular method. How is the information that this method should be executed on launch stored in the static library? Where is this information stored when the library is linked to our main application?
How can you specify this when compiling? Can you flag a method to be executed on load? It this something you do in code or is it a compiler argument?
While this method is run, what can I do? I assume other functions and classes are not guaranteed to be loaded yet?
This whole thing is happening inside an iOS application, but this seems to me to be very low level functionality, so I assume this works on every unix based platform?
I would love to learn some more about this.
Related
Why does this result in a segmentation fault?
#include <stddef.h>
void *malloc(size_t s) {
};
int main() {
printf("lol");
}
This is how I compile:
gcc -o l lol.c
My guess is that printf() calls malloc().
Per the C language specification, providing your own definitions of standard library functions as functions with external linkage (which is the default for functions) produces undefined behavior. Those names are reserved for such use (C17 7.1.3). You have observed one of many possible manifestations of such behavior.
You have at least four alternatives:
Just use the standard library's implementation.
Define your function with a different name. For example, my_malloc(). You will then need to use that name to call it, though you could disguise that by use of a macro.
Declare your function static (giving it internal linkage). Then it can have the same name as a standard library function, but only functions defined in the same translation unit (roughly: source file) will be able to call it via that name.
Engage implementation-specific provisions of your particular C implementation (see next).
Some C implementations make implementation-specific provision for programs to provide their own versions of at least some library functions. Glibc is one of these. However, such provisions are subject to significant limits.
First and foremost, you can expect that the implementation will require your replacement functions to provide the same binary interface and to correctly implement the behavior specified by the language. (Your function does not do the latter.)
Second, where the function is part of a set of related ones, as malloc is, you may find that the implementation requires you to replace the whole set. Indeed, Glibc docs say that "replacing malloc" involves providing replacements for all these functions: malloc, free, calloc, realloc. Your program does not do this, either. The Glibc docs recommend providing replacements for several other functions as well, with the suggestion that failure to do so, while not in itself compromising any Glibc functions, is likely to break some programs: aligned_alloc, cfree,* malloc_usable_size, memalign, posix_memalign, pvalloc, valloc. These latter are not relevant to your particular example, however.
*Required only by very old programs.
Something in the standard library is calling malloc(), expecting it to return a usable memory address, and writing something to that address.
On Unixy (or at least Linuxy) platforms, when you define a library function in the main program, it overrides the one in any other library, even when a library calls it, even when the same library that defines it calls it.
To verify that your malloc() function overrides the sibling one in the C library and is called by printf(), you can log a trace on the terminal (file descriptor number 1) by calling write() system call:
#include <stdio.h>
#include <unistd.h>
void *malloc(size_t s) {
write(1, "I am called\n", sizeof("I am called\n") - 1);
};
int main() {
printf("lol");
}
After compilation, the programs displays:
$ gcc -g overm.c -o overm
$ ./overm
I am called
Segmentation fault (core dumped)
The analysis of the generated core dump file with a debugger like gdb shows the state of the call stack at the time of the crash:
$ gdb overm core
[...]
Reading symbols from overm...
[New LWP 8494]
Core was generated by `./overm'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007fd4bf7d6dc8 in _IO_new_file_overflow (f=0x7fd4bf9336a0 <_IO_2_1_stdout_>, ch=108) at fileops.c:781
781 fileops.c: No such file or directory.
(gdb) where
#0 0x00007fd4bf7d6dc8 in _IO_new_file_overflow (f=0x7fd4bf9336a0 <_IO_2_1_stdout_>, ch=108) at fileops.c:781
#1 0x00007fd4bf7d8024 in __GI__IO_default_xsputn (n=<optimized out>, data=<optimized out>, f=<optimized out>) at libioP.h:948
#2 __GI__IO_default_xsputn (f=f#entry=0x7fd4bf9336a0 <_IO_2_1_stdout_>, data=<optimized out>, n=n#entry=3) at genops.c:370
#3 0x00007fd4bf7d56fa in _IO_new_file_xsputn (n=3, data=<optimized out>, f=<optimized out>) at fileops.c:1265
#4 _IO_new_file_xsputn (f=0x7fd4bf9336a0 <_IO_2_1_stdout_>, data=<optimized out>, n=3) at fileops.c:1197
#5 0x00007fd4bf7bc972 in __vfprintf_internal (s=0x7fd4bf9336a0 <_IO_2_1_stdout_>, format=0x5556655db011 "lol", ap=ap#entry=0x7fff657394a0,
mode_flags=mode_flags#entry=0) at ../libio/libioP.h:948
#6 0x00007fd4bf7a7d3f in __printf (format=<optimized out>) at printf.c:33
#7 0x00005556655da1ab in main () at overm.c:8
The above display shows that the crash occured while calling printf() at line 33 of the source code. Let's set a breakpoint on the printf() function and rerun the program from the debugger:
(gdb) br printf
Breakpoint 3 at 0x7ffff7e1ac90: file printf.c, line 28.
(gdb) run
Breakpoint 3, __printf (format=0x555555556011 "lol") at printf.c:28
28 printf.c: No such file or directory.
(gdb)
Once we are stopped at the printf() call, add another breakpoint onto malloc() function (we didn't do that before because malloc() is called several times during program startup by internal code) and continue the execution:
(gdb) br malloc
(gdb) continue
Continuing.
Breakpoint 4, malloc (s=140737354002065) at overm.c:4
4 void *malloc(size_t s) {
(gdb) where
#0 malloc (s=140737354002065) at overm.c:4
#1 0x00007ffff7e3ad04 in __GI__IO_file_doallocate (fp=0x7ffff7fa66a0 <_IO_2_1_stdout_>) at filedoalloc.c:101
#2 0x00007ffff7e4aed0 in __GI__IO_doallocbuf (fp=fp#entry=0x7ffff7fa66a0 <_IO_2_1_stdout_>) at libioP.h:948
#3 0x00007ffff7e49f30 in _IO_new_file_overflow (f=0x7ffff7fa66a0 <_IO_2_1_stdout_>, ch=-1) at fileops.c:745
#4 0x00007ffff7e486b5 in _IO_new_file_xsputn (n=3, data=<optimized out>, f=<optimized out>) at libioP.h:948
#5 _IO_new_file_xsputn (f=0x7ffff7fa66a0 <_IO_2_1_stdout_>, data=<optimized out>, n=3) at fileops.c:1197
#6 0x00007ffff7e2f972 in __vfprintf_internal (s=0x7ffff7fa66a0 <_IO_2_1_stdout_>, format=0x555555556011 "lol", ap=ap#entry=0x7fffffffdd90,
mode_flags=mode_flags#entry=0) at ../libio/libioP.h:948
#7 0x00007ffff7e1ad3f in __printf (format=<optimized out>) at printf.c:33
#8 0x00005555555551ab in main () at overm.c:8
(gdb)
The displayed stack when we are stopped at the malloc() call, shows that it is the result of the printf() call from line 33 in the source code.
I tried to build WebKit GTK on my ARM Mac, but the linking process fails:
Undefined symbols for architecture arm64:
"_u_charDirection_67", referenced from:
WTF::StringImpl::stripWhiteSpace() in libWTFGTK.a(StringImpl.cpp.o)
WTF::StringImpl::simplifyWhiteSpace() in libWTFGTK.a(StringImpl.cpp.o)
WTF::StringImpl::defaultWritingDirection(bool*) in libWTFGTK.a(StringImpl.cpp.o)
int WTF::toIntegralType<int, char16_t>(char16_t const*, unsigned long, bool*, int) in libWTFGTK.a(WTFString.cpp.o)
unsigned int WTF::toIntegralType<unsigned int, char16_t>(char16_t const*, unsigned long, bool*, int) in libWTFGTK.a(WTFString.cpp.o)
long long WTF::toIntegralType<long long, char16_t>(char16_t const*, unsigned long, bool*, int) in libWTFGTK.a(WTFString.cpp.o)
unsigned long long WTF::toIntegralType<unsigned long long, char16_t>(char16_t const*, unsigned long, bool*, int) in libWTFGTK.a(WTFString.cpp.o)
...
"_u_foldCase_67", referenced from:
WTF::StringImpl::foldCase() in libWTFGTK.a(StringImpl.cpp.o)
"_u_strFoldCase_67", referenced from:
WTF::StringImpl::foldCase() in libWTFGTK.a(StringImpl.cpp.o)
"_u_strToLower_67", referenced from:
WTF::StringImpl::convertToLowercaseWithoutLocale() in libWTFGTK.a(StringImpl.cpp.o)
WTF::StringImpl::convertToLowercaseWithLocale(WTF::AtomString const&) in libWTFGTK.a(StringImpl.cpp.o)
"_u_strToUpper_67", referenced from:
WTF::StringImpl::convertToUppercaseWithoutLocale() in libWTFGTK.a(StringImpl.cpp.o)
...
WTF::normalizedNFC(WTF::StringView) in libWTFGTK.a(StringView.cpp.o)
"_unorm2_isNormalized_67", referenced from:
WTF::normalizedNFC(WTF::StringView) in libWTFGTK.a(StringView.cpp.o)
"_unorm2_normalize_67", referenced from:
WTF::normalizedNFC(WTF::StringView) in libWTFGTK.a(StringView.cpp.o)
"_utext_close_67", referenced from:
WTF::setTextForIterator(UBreakIterator&, WTF::StringView) in libWTFGTK.a(TextBreakIterator.cpp.o)
WTF::acquireLineBreakIterator(WTF::StringView, WTF::AtomString const&, char16_t const*, unsigned int, WTF::LineBreakIteratorMode) in libWTFGTK.a(TextBreakIterator.cpp.o)
WTF::TextBreakIteratorICU::TextBreakIteratorICU(WTF::StringView, WTF::TextBreakIteratorICU::Mode, char const*) in libWTFGTK.a(TextBreakIterator.cpp.o)
"_utext_setup_67", referenced from:
WTF::openLatin1UTextProvider(WTF::UTextWithBuffer*, unsigned char const*, unsigned int, UErrorCode*) in libWTFGTK.a(UTextProviderLatin1.cpp.o)
WTF::openLatin1ContextAwareUTextProvider(WTF::UTextWithBuffer*, unsigned char const*, unsigned int, char16_t const*, int, UErrorCode*) in libWTFGTK.a(UTextProviderLatin1.cpp.o)
WTF::uTextLatin1Clone(UText*, UText const*, signed char, UErrorCode*) in libWTFGTK.a(UTextProviderLatin1.cpp.o)
WTF::openUTF16ContextAwareUTextProvider(UText*, char16_t const*, unsigned int, char16_t const*, int, UErrorCode*) in libWTFGTK.a(UTextProviderUTF16.cpp.o)
WTF::uTextCloneImpl(UText*, UText const*, signed char, UErrorCode*) in libWTFGTK.a(UTextProvider.cpp.o)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [bin/WebKitWebDriver] Error 1
I'm not sure why this is happening. It compiled just fine on my Intel Mac. Any ideas on how to fix this?
I met the following error while compiling z3. It seems to be an error for ld. I wonder what I can do to make it compile. It is a problem from the opt branch in git. I am on iMac with OS X 10.9.2 (13C1021)
I am with xcode Version 5.1.1 (5B1008) with xcode-select tools installed to version 2333. I use port with version 2.2.1 with ld installed.
The problem seems to be a linking problem. I am using link loader as: ld64 #136_2+llvm33 (active)
My gcc is gcc (MacPorts gcc48 4.8.2_0) 4.8.2
Thank you very much!
g++ -o z3 shell/datalog_frontend.o shell/dimacs_frontend.o
shell/gparams_register_modules.o shell/install_tactic.o shell/main.o
shell/mem_initializer.o shell/smtlib_frontend.o shell/z3_log_frontend.o api/api.a opt/opt.a parsers/smt/smtparser.a tactic/portfolio/portfolio.a tactic/ufbv/ufbv_tactic.a tactic/smtlogics/smtlogic_tactics.a muz/fp/fp.a muz/duality/duality_intf.a muz/bmc/bmc.a muz/tab/tab.a muz/clp/clp.a muz/pdr/pdr.a muz/rel/rel.a muz/transforms/transforms.a muz/base/muz.a duality/duality.a qe/qe.a tactic/sls/sls_tactic.a smt/tactic/smt_tactic.a tactic/fpa/fpa.a tactic/bv/bv_tactics.a smt/user_plugin/user_plugin.a smt/smt.a smt/proto_model/proto_model.a smt/params/smt_params.a ast/rewriter/bit_blaster/bit_blaster.a ast/pattern/pattern.a ast/macros/macros.a ast/simplifier/simplifier.a ast/proof_checker/proof_checker.a parsers/smt2/smt2parser.a cmd_context/extra_cmds/extra_cmds.a cmd_context/cmd_context.a interp/interp.a solver/solver.a tactic/aig/aig_tactic.a math/subpaving/tactic/subpaving_tactic.a nlsat/tactic/nlsat_tactic.a tactic/arith/arith_tactics.a sat/tactic/sat_tactic.a tactic/core/core_tactics.a math/euclid/euclid.a math/grobner/grobner.a parsers/util/parser_util.a ast/substitution/substitution.a tactic/tactic.a model/model.a ast/normal_forms/normal_forms.a ast/rewriter/rewriter.a ast/ast.a math/subpaving/subpaving.a math/realclosure/realclosure.a math/interval/interval.a math/simplex/simplex.a math/hilbert/hilbert.a nlsat/nlsat.a sat/sat.a math/polynomial/polynomial.a util/util.a -lpthread -fopenmp
0 0x1079c1a68 __assert_rtn + 144
1 0x107a3bccd mach_o::relocatable::Parser<x86_64>::parse(mach_o::relocatable::ParserOptions const&) + 1039
2 0x107a2b899 mach_o::relocatable::Parser<x86_64>::parse(unsigned char const*, unsigned long long, char const*, long, ld::File::Ordinal, mach_o::relocatable::ParserOptions const&) + 313
3 0x107a290f0 mach_o::relocatable::parse(unsigned char const*, unsigned long long, char const*, long, ld::File::Ordinal, mach_o::relocatable::ParserOptions const&) + 208
4 0x107a18797 archive::File<x86_64>::makeObjectFileForMember(archive::File<x86_64>::Entry const*) const + 795
5 0x107a182b3 archive::File<x86_64>::justInTimeforEachAtom(char const*, ld::File::AtomHandler&) const + 139
6 0x1079c5d46 ld::tool::InputFiles::searchLibraries(char const*, bool, bool, bool, ld::File::AtomHandler&) const + 210
7 0x107a0b772 ld::tool::Resolver::resolveUndefines() + 200
8 0x107a0d6e1 ld::tool::Resolver::resolve() + 75
9 0x1079c1d44 main + 370
A linker snapshot was created at:
/tmp/z3-2014-03-25-110931.ld-snapshot
ld: Assertion failed: (cfiStartsArray[i] != cfiStartsArray[i-1]), function parse, file src/ld/parsers/macho_relocatable_file.cpp, line 1555.
collect2: error: ld returned 1 exit status
make: *** [z3] Error 1
It is because we used port and installed gcc and ld and other packages.
Another possibility is that ld was depend on llvm 3.3 rather than llvm 3.4. The problem was solved after updating ld.
I'm developing software that uses DB2 database via ODBC (unixodbc). The issue is that running test suite with valgrind produces massive amount of errors. Let alone one connect and disconnect generates 4k error messages (code provided below). My question is:
Am I doing something wrong with connect and disconnect?
Is there clean up function that frees allocated memory by libdb2?
Valgrind also has message suppression feature, is there maintained suppression file for libdb2.so library?
Code:
static void
connect_disconnect(SQLCHAR *dsn)
{
SQLRETURN ret = -1;
SQLHENV env = NULL;
SQLHDBC dbc = NULL;
SQLCHAR msg[1024];
SQLSMALLINT msglen = 0;
/* env handle */
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
/* connection */
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *)dsn,
SQL_NTS, msg, sizeof(msg), &msglen, SQL_DRIVER_COMPLETE);
if (!SQL_SUCCEEDED(ret))
{
fprintf(stderr, "Failed to connect to database '%s'.\n", dsn);
extract_error(dbc, SQL_HANDLE_DBC);
}
SQLDisconnect(dbc);
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
dbc = NULL;
SQLFreeHandle(SQL_HANDLE_ENV, env);
env = NULL;
return;
}
I'm using:
libdb2.so acquired from DSClients-linuxx64-odbc_cli-10.1.0.2-FP002 package for Linux 64bit.
libodbc.so version 2.3.1
Edit
Last valgrind message (bigest leak):
==1318== 425,880 bytes in 1 blocks are possibly lost in loss record 145 of 145
==1318== at 0x4C2C04B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1318== by 0x68B313D: _ossMemAlloc (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6A3B513: sqlnlscmsg(char const*, SQLNLS_MSG_FILE_HEADER**, char const*, bool*, char*) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6A3AC90: sqlnlsMessage (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6A3A589: sqlnlsMessage (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6C43128: sqloMessage (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6BDCDE0: sqllcGetMessage(char const*, int, char*, char*, unsigned long, bool, char const*) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6BE0F79: sqllcInitComponent(unsigned int) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6BE14E2: sqllcInitData() (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6BD813C: sqllcGetInstalledKeyType (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6C26653: sqloGetInstalledKeyType (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6B42494: sqleuInvokeActivationRoutine(db2UCconHandle*, SQLEU_UDFSP_ARGS*, sqlca*, bool, unsigned int) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6B413A3: sqleuPerformServerActivationCheck(db2UCconHandle*, sqlca*) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x6B3FF72: sqleUCappConnect (in /usr/local/lib/libdb2.so.1)
==1318== by 0x69E8F9A: CLI_sqlConnect(CLI_CONNECTINFO*, sqlca*, CLI_ERRORHEADERINFO*) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x699997D: SQLConnect2(CLI_CONNECTINFO*, unsigned char*, short, unsigned char*, short, unsigned char*, short, unsigned char*, short, unsigned char) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x69B2640: SQLDriverConnect2(CLI_CONNECTINFO*, void*, unsigned char*, short, unsigned char*, short, short*, unsigned short, unsigned char, unsigned char, CLI_ERRORHEADERINFO*) (in /usr/local/lib/libdb2.so.1)
==1318== by 0x698BD4E: SQLDriverConnect (in /usr/local/lib/libdb2.so.1)
==1318== by 0x4E45962: SQLDriverConnect (in /usr/lib/libodbc.so.2.0.0)
==1318== by 0x400BF2: connect_disconnect (in /.../db2_leak/test)
==1318== by 0x400A8F: main (in /.../db2_leak/test)
Most of leaks are static (initialization). Each connect disconnect adds 80bytes to definitely lost byte count.
A bit bigger part of valgrind output (could not paste more then 500k): http://pastebin.com/xZfjy21Q
Biggest issue is that I cant find issues caused by my actions.
Edit
Double checked binaries, all are 64bit.
When I try to compile example1.cpp that comes with Armadillo 2.4.2, I keep getting the following linking error:
/tmp/ccbnLbA0.o: In function `double arma::blas::dot<double>(unsigned int, double const*, double const*)':
main.cpp:(.text._ZN4arma4blas3dotIdEET_jPKS2_S4_[double arma::blas::dot<double>(unsigned int, double const*, double const*)]+0x3b): undefined reference to `wrapper_ddot_'
/tmp/ccbnLbA0.o: In function `void arma::blas::gemv<double>(char const*, int const*, int const*, double const*, double const*, int const*, double const*, int const*, double const*, double*, int const*)':
main.cpp:(.text._ZN4arma4blas4gemvIdEEvPKcPKiS5_PKT_S8_S5_S8_S5_S8_PS6_S5_[void arma::blas::gemv<double>(char const*, int const*, int const*, double const*, double const*, int const*, double const*, int const*, double const*, double*, int const*)]+0x68): undefined reference to `wrapper_dgemv_'
/tmp/ccbnLbA0.o: In function `void arma::blas::gemm<double>(char const*, char const*, int const*, int const*, int const*, double const*, double const*, int const*, double const*, int const*, double const*, double*, int const*)':
main.cpp:(.text._ZN4arma4blas4gemmIdEEvPKcS3_PKiS5_S5_PKT_S8_S5_S8_S5_S8_PS6_S5_[void arma::blas::gemm<double>(char const*, char const*, int const*, int const*, int const*, double const*, double const*, int const*, double const*, int const*, double const*, double*, int const*)]+0x7a): undefined reference to `wrapper_dgemm_'
collect2: ld returned 1 exit status
Can someone help? I manually installed
latest version of BLAS
lapack-3.4.0
boost-1.48.0
latest version of ATLAS
I'm using Ubuntu 11.04 on the MacBook Pro 7,1 model
Thank you so much to osgx! After reading his comment, I took a second look at the README file! It turns out I was missing '-O1 -larmadillo' in the command!
Here's the command I used to get it working:
g++ example1.cpp -o example1 -O1 -larmadillo
Stupid mistake, I know.... It just goes to remind you how important it is to read the README.
The README also mentions:
If you get linking errors, or if Armadillo was installed manually
and you specified that LAPACK and BLAS are available, you will
need to explicitly link with LAPACK and BLAS (or their equivalents),
for example:
g++ example1.cpp -o example1 -O1 -llapack -lblas
I didn't have to include '-llapack -lblas' but maybe this will help anyone else who's having similar problems.
As of 5.0.0 (might also apply to earlier versions)
You actually need -larmadillo, on Fedora 21 -llapack and -lopenblas are not excplicitly necessary anymore.
There's an oddity I just discovered by comparing previously working compilations of code with the very problem of this thread, stressing the involvement of the gnu cc (I'm no expert in this): on my machine compilation success depends on the order of parameters to the gcc/g++ where
g++ infile -o outfile -libarmadillo ... worked, but
g++ -libarmadillo infile -o outfile ... didnt with (almost) the same error as mentioned above.
(hope that helps).