Why aren't external variables added to .dynsym table? - global-variables

From my understanding of .dynsym, it should contain the symbols that should be visible from an external program. I tried a simple program that contain this line:
extern int global = 1000;
I thought the executable would contain the global symbol in its .dynsym, but it is not the case. Is my understanding of this section wrong?

I thought the executable would contain the global symbol in its .dynsym
Why did you think that?
By default, only symbols referenced by some other shared library participating in the link are exported in the dynamic symbol table from the executable.
You can force all symbols to be exported in the dynamic symbol table using --export-dynamic linker flag:
$ cat foo.c
int global = 1000;
int main() { return 0; }
$ gcc foo.c && nm -D a.out | grep global
# no output
$ gcc foo.c -Wl,--export-dynamic && nm -D a.out | grep global
0000000000004028 D global

Related

Accessing an external variable from a C library

I am currently learning C and am trying to understand the possibilities of dynamic libraries.
My current question is, if I have a simple "Hello World" application in C called "ProgA", and this program dynamically loads a shared library with some example code called "LibB", can LibB access a global variable in ProgA, which was declared as external?
Given is the following example code for demonstration of the problem:
file header.h
#ifndef TEST_H
#define TEST_H
typedef struct test_import_s {
int some_field;
} test_import_t;
extern test_import_t newtestimport;
#endif
file prog_a.c
#include <stdio.h>
#include <windows.h>
#include "header.h"
test_import_t newtestimport = {
.some_field = 42
};
int main()
{
HINSTANCE hinstLib;
typedef void (*FunctionPointer)();
newtestimport.some_field = 42;
hinstLib = LoadLibrary("lib_b.dll");
if (hinstLib != NULL)
{
FunctionPointer initialize_lib_b;
initialize_lib_b = (FunctionPointer)GetProcAddress(hinstLib, "initialize_lib_b");
if (initialize_lib_b != NULL)
{
initialize_lib_b();
}
FreeLibrary(hinstLib);
}
return 0;
}
file lib_b.c
#include <stdio.h>
#include "header.h"
test_import_t *timp;
void initialize_lib_b() {
timp = &newtestimport;
int some_field = timp->some_field;
printf("Result from function: %d\n", some_field);
}
file CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(dynamic-library-2 C)
set(CMAKE_C_STANDARD 23)
add_library(lib_b SHARED lib_b.c)
set_target_properties(lib_b PROPERTIES PREFIX "" OUTPUT_NAME "lib_b")
add_executable(prog_a prog_a.c)
target_link_libraries(prog_a lib_b)
In the above example, the headerfile header.h defines the struct test_import_t and an external variable newtestimport using this struct. In the C file of the main program prog_a.c one property of this struct is assigned the value 42. It then dynamically loads the library lib_b.c using the Windows API and executes a function in it. The function then should access the variable newtestimport of the main program and print out the value of the variable (42).
This example does not work. The compiler throws the following error:
====================[ Build | prog_a | Debug ]==================================
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\cmake\win\x64\bin\cmake.exe --build C:\Users\user1\projects\learning-c\cmake-build-debug --target prog_a -j 9
[1/2] Linking C shared library dynamic-library-2\lib_b.dll
FAILED: dynamic-library-2/lib_b.dll dynamic-library-2/liblib_b.dll.a
cmd.exe /C "cd . && C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin\gcc.exe -fPIC -g -Wl,--export-all-symbols -shared -o dynamic-library-2\lib_b.dll -Wl,--out-implib,dynamic-library-2\liblib_b.dll.a -Wl,--major-image-version,0,--minor-image-version,0 dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin/ld.exe: dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj:lib_b.c:(.rdata$.refptr.newtestimport[.refptr.newtestimport]+0x0): undefined reference to `newtestimport'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
How can the example be fixed to accomplish the described goal?
Windows DLLs are self-contained, and can not have undefined references similar to newtestimport, unless these references are satisfied by another DLL.
How can the example be fixed to accomplish the described goal?
The best fix is to pass the address of newtestimport into the function that needs it (initialize_lib_b() here).
If for some reason you can't do that, your next best option is to define the newtestimport as a dllexport variable in another DLL, e.g. lib_c.dll.
Then both the main executable and lib_b.dll would be linked against lib_c.lib, and would both use that variable from lib_c.dll.
P.S. Global variables are a "code smell" and a significant source of bugs. You should avoid them whenever possible, and in your example there doesn't seem to be any good reason to use them.

Which shell will execute the cmd when popen is called

First of all, forgive my awful english.....
This is the prototype
FILE *popen(const char* cmd_string, const char* type);
Here is my question, the book says that when popen function is called, it will call exec to get a shell to execute the cmd_string we give to popen, but I'm not sure which shell will exec get, so can anyone give me an answer?
/bin/sh : From the doc:
The command argument is a pointer to a null-terminated string
containing a shell command line. This command is passed to /bin/sh
using the -c flag; interpretation, if any, is performed by the shell.
Let's try and see:
$ cat test.c
#include <stdio.h>
int main() {
FILE *fp;
char var[5];
fp = popen("echo $0", "r");
fgets(var, 5, fp);
printf("%s", var);
}
$ gcc -Wall test.c
$ ./a.out
sh

Google Closure Compiler is importing my extern functions

I have created an extern in a javascript file and specified it as part of the Google Closure Compiler (GCC) command line option. I am compiling with advanced mode. GCC is taking the function in my extern and placing it in the compiled code. I have no idea why it would do this. GCC is suppose to recognize that the extern function is in a separate file. When I export the object, it will rename the object and leave the object's function names alone BUT it will create a copy of the entire extern function in the compiled code.
I have tried many variations (too numerous here to list) to see how to prevent GCC from doing this but nothing has worked.
My extern:
var MyCustomResizer = {
"onResize": function (a, b) {
},
"detach": function () {
}
}
I exported the object as follows:
window["MyCustomResizer"] = MyCustomResizer;
My app using the "detach" function:
MyCustomResizer.detach();
My compiler settings:
java -jar closure-compiler/compiler.jar \
--compilation_level ADVANCED_OPTIMIZATIONS \
--externs scripts/externs/resizer-extern.js \
--js_output_file scripts/release/myapp.js \
--warning_level VERBOSE \
--language_out ECMASCRIPT5 \
--language_in=ECMASCRIPT_2017 \
--js scripts/base.js
And the generated compiled output contains this:
ha.detach();
...
var ha = {
onResize: function () {
}, detach: function () {
}
};
It turns out that when you specify extern files, you MUST use the --extern option in front of every extern file. I only had it on the first one:
Incorrect:
java -jar closure-compiler/compiler.jar \
--compilation_level ADVANCED_OPTIMIZATIONS \
--externs scripts/externs/jQuery/jquery-1.9-externs.js \
scripts/externs/third-party.js \
--js_output_file scripts/release/servetus-min.js \
Correct:
java -jar closure-compiler/compiler.jar \
--compilation_level ADVANCED_OPTIMIZATIONS \
--externs scripts/externs/jQuery/jquery-1.9-externs.js \
--externs scripts/externs/third-party.js \
--js_output_file scripts/release/servetus-min.js \
I find it very strange that the compiler just ignores the one without the --externs but just goes ahead anyway and copies its functions into the compiled code. This should not be allowed and a warning should be issued. This took an entire day to track down.

How to include nmath.h?

I would like to include the header nmath.h for my C code (within an R package) to find R_FINITE and ML_ERR_return_NAN. I found that one cannot include nmath.h directly. For R_FINITE to be found, I could include R_ext/libextern.h. But I don't know what to include so that ML_ERR_return_NAN is found. Any ideas? I found here that Prof. Brian Ripley referred to Writing R Extensions, but I couldn't find nmath.h being addressed there (where exactly?)
On Debian or Ubuntu:
sudo apt-get install r-mathlib
after which you can build test programs such as this:
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4;
// compile-command: "gcc -s -Wall -O3 \
// -I/usr/share/R/include -o rmath_rnorm \
// rmath_rnorm.c -lRmath -lm" -*-
// Compare to
// $ Rscript -e "RNGkind('Marsaglia'); \
// .Random.seed[2:3] <- c(123L, 456L); rnorm(2)"
// [1] -0.2934974 -0.3343770
#include <stdio.h>
#define MATHLIB_STANDALONE 1
#include <Rmath.h>
int main(void) {
set_seed(123, 456);
printf("rnorm: %f %f\n", rnorm(0.0, 1.0), rnorm(0.0, 1.0));
return 0;
}
Note: The first four lines should be one-line in the file you safe, then M-x compile build the program for your. Ditto for the Rscript invocation: one line.
Edit: Drats. Answered the wrong question :) nmath.h appears to not be exported from src/nmath/nmath.h but this R Mathlibrary is what is exported by R Core for use by others. Where as the nmath.h file has
/* Private header file for use during compilation of Mathlib */
#ifndef MATHLIB_PRIVATE_H
#define MATHLIB_PRIVATE_H
so you are not supposed to rely on it.

Compiling and Linking KISSFFT

I have a newb problem with compiling and linking the kissfft library 'out of the box'. I've downloaded the kissfft library and extracted it to a test directory. Upon entering the directory and running 'make testall' I get the following errors, which look like the std c math library is not being linked to properly.
sharkllama#quaaludes:~/KISSFFT/kiss_fft129$ make testall
# The simd and int32_t types may or may not work on your machine
make -C test DATATYPE=simd CFLAGADD="" test
make[1]: Entering directory `/home/sharkllama/KISSFFT/kiss_fft129/test'
cd ../tools && make all
make[2]: Entering directory `/home/sharkllama/KISSFFT/kiss_fft129/tools'
cc -o fft_simd -Wall -O3 -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Waggregate-return -Wcast-align -Wcast-qual -Wnested-externs -Wshadow -Wbad-function-cast -Wwrite-strings -I.. -DUSE_SIMD=1 -msse -lm ../kiss_fft.c fftutil.c kiss_fftnd.c kiss_fftr.c kiss_fftndr.c
/tmp/ccFbS0yK.o: In function `kiss_fft_alloc':
kiss_fft.c:(.text+0xd17): undefined reference to `sincos'
kiss_fft.c:(.text+0xd6b): undefined reference to `floor'
kiss_fft.c:(.text+0xe07): undefined reference to `sincos'
kiss_fft.c:(.text+0xeba): undefined reference to `sqrt'
/tmp/ccbYqDcf.o: In function `kiss_fftr_alloc':
kiss_fftr.c:(.text+0x118): undefined reference to `sincos'
kiss_fftr.c:(.text+0x188): undefined reference to `sincos'
collect2: ld returned 1 exit status
make[2]: *** [fft_simd] Error 1
make[2]: Leaving directory `/home/sharkllama/KISSFFT/kiss_fft129/tools'
make[1]: *** [tools] Error 2
make[1]: Leaving directory `/home/sharkllama/KISSFFT/kiss_fft129/test'
make: *** [testall] Error 2
sharkllama#quaaludes:~/KISSFFT/kiss_fft129$
Clearly, the makefile is trying to link to the math library as the -lm option has been included. Can't make any sense of this. I've compiled numerous programs that properly link to the math library before. Any help would be appreciated.
Thanks,
-B
Kissfft is not really something you need to make and install like other libraries. If you need complex ffts, then all you need to do is compile the kiss_fft.c in your project. If you need something more specialized like multidimensional or real ffts, then you should also compile the apropriate file(s) from the tools dir.
The make targets are largely for development testing of kissfft. There are a lot of system requirements to do that testing. Unless you are changing the internals of kissfft, you won't need to use those testing targets.
Just wanted to share a practical example on how to build a simple application using 1D FFT/IFFT from kissfft:
g++ example.cpp -o example -I kissfft kissfft/kiss_fft.c
example.cpp:
#include "kissfft/kiss_fft.h"
int main()
{
// initialize input data for FFT
float input[] = { 11.0f, 3.0f, 4.05f, 9.0f, 10.3f, 8.0f, 4.934f, 5.11f };
int nfft = sizeof(input) / sizeof(float); // nfft = 8
// allocate input/output 1D arrays
kiss_fft_cpx* cin = new kiss_fft_cpx[nfft];
kiss_fft_cpx* cout = new kiss_fft_cpx[nfft];
// initialize data storage
memset(cin, 0, nfft * sizeof(kiss_fft_cpx));
memset(cout, 0, nfft * sizeof(kiss_fft_cpx));
// copy the input array to cin
for (int i = 0; i < nfft; ++i)
{
cin[i].r = input[i];
}
// setup the size and type of FFT: forward
bool is_inverse_fft = false;
kiss_fft_cfg cfg_f = kiss_fft_alloc(nfft, is_inverse_fft, 0, 0); // typedef: struct kiss_fft_state*
// execute transform for 1D
kiss_fft(cfg_f, cin , cout);
// transformed: DC is stored in cout[0].r and cout[0].i
printf("\nForward Transform:\n");
for (int i = 0; i < nfft; ++i)
{
printf("#%d %f %fj\n", i, cout[i].r, cout[i].i);
}
// setup the size and type of FFT: backward
is_inverse_fft = true;
kiss_fft_cfg cfg_i = kiss_fft_alloc(nfft, is_inverse_fft, 0, 0);
// execute the inverse transform for 1D
kiss_fft(cfg_i, cout, cin);
// original input data
printf("\nInverse Transform:\n");
for (int i = 0; i < nfft; ++i)
{
printf("#%d %f\n", i, cin[i].r / nfft); // div by N to scale data back to the original range
}
// release resources
kiss_fft_free(cfg_f);
kiss_fft_free(cfg_i);
delete[] cin;
delete[] cout;
return 0;
}
To use the 2D transforms, include the appropriate header "kissfft/tools/kiss_fftnd.h" and adjust the build command to:
g++ example.cpp -o example -I kissfft kissfft/kiss_fft.c kissfft/tools/kiss_fftnd.c
Simple enough!

Resources