Windows vs. Linux GCC argv[0] value [duplicate] - c++

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Get path of executable
I'm programming on Windows using MinGW, gcc 4.4.3. When I use the main function like this:
int main(int argc, char* argv[]){
cout << "path is " << argv[0] << endl;
}
On Windows I get a full path like this: "C:/dev/stuff/bin/Test". When I run the same application on Linux, however, I get some sort of relative path: "bin/Test". It's breaking my application! Any idea on how to make sure the path is absolute on both systems?

No, there isn't. Under most shells on Linux, argv[0] contains exactly what the user typed to run the binary. This allows binaries to do different things depending on what the user types.
For example, a program with several different command-line commands may install the binary once, and then hard-link the various different commands to the same binary. For example, on my system:
$ ls -l /usr/bin/git*
-rwxr-xr-x 109 root wheel 2500640 16 May 18:44 /usr/bin/git
-rwxr-xr-x 2 root wheel 121453 16 May 18:43 /usr/bin/git-cvsserver
-rwxr-xr-x 109 root wheel 2500640 16 May 18:44 /usr/bin/git-receive-pack
-rwxr-xr-x 2 root wheel 1021264 16 May 18:44 /usr/bin/git-shell
-rwxr-xr-x 109 root wheel 2500640 16 May 18:44 /usr/bin/git-upload-archive
-rwxr-xr-x 2 root wheel 1042560 16 May 18:44 /usr/bin/git-upload-pack
-rwxr-xr-x 1 root wheel 323897 16 May 18:43 /usr/bin/gitk
Notice how some of these files have exactly the same size. More investigation reveals:
$ stat /usr/bin/git
234881026 459240 -rwxr-xr-x 109 root wheel 0 2500640 "Oct 29 08:51:50 2011" "May 16 18:44:05 2011" "Jul 26 20:28:29 2011" "May 16 18:44:05 2011" 4096 4888 0 /usr/bin/git
$ stat /usr/bin/git-receive-pack
234881026 459240 -rwxr-xr-x 109 root wheel 0 2500640 "Oct 29 08:51:50 2011" "May 16 18:44:05 2011" "Jul 26 20:28:29 2011" "May 16 18:44:05 2011" 4096 4888 0 /usr/bin/git-receive-pack
The inode number (459240) is identical and so these are two links to the same file on disk. When run, the binary uses the contents of argv[0] to determine which function to execute. You can see this (sort of) in the code for Git's main().

argv array
argv[0] is a parameter like any others: it can be an arbitrary NUL terminated byte string. It can be the empty string. It is whatever the launching process wants.
By default, the shell with set argv[0] to whatever is used to name the program: a name looked-up in $PATH, a relative or an absolute path. It can be a symbolic link or a regular file.
To invoke a program with some other value, with zsh (dunno with other shells) use:
ARGV0=whatever_you_want some_program arguments
If you really need the path to the executable, you cannot use the command line on Unix.
Linux only
On Linux: /proc/self/exe is a symbolic link to the executable file.
You can readlink it. You can also stat or open it directly.
Renaming and soft link
A normal soft link is a dumb string, and doesn't know what happens to its target (if it exists at all). But the /proc/self/exe soft link is magic.
In case of renaming, the soft-but-magic-link will follow renaming. In case there are several hard links, it will follow the name of the particular hard link that was used. (So different hard links to the same file are not perfectly equivalent under Linux.)
If this hard link is unlinked, I think " (deleted)" is appended to the value of the symbolic link. Note that this is a valid file name, so another unrelated file could have that name.
In any case, the symbolic link is a hard link to the file, so you can stat or open it directly.
I don't think you can count on anything on a network file system if the binary is renamed or unlinked on another system than the one where the executable is launched.
Security considerations
When your program gets to use the /proc/self/exe special file, it is possible for the file used to launch your program to be unlinked or renamed. This should be taken seriously in case the program is privileged (SUID or Set Capabilities): even if the user doesn't have write access to the original "Set Something" binary, he may be able to make a hard link to it if he has write access to a directory on the same file system, so he may be able to change the name if a running privileged binary.
By the time you readlink, the value returned may refer to another file. (Of course, there is always an unavoidable race condition with opening the result of readlink.)
As usual, NFS does not provides all the same guaranties that local file systems have.

There is no way to ensure that argv[0] is an absolute path because it is supposed to be exactly how the user invoked the program. So, if on a Linux command line you invoke your program via ./bin/Test, then argv[0] should be exactly "./bin/Test".
It seems like a bug in MinGW's runtime if when you invoke the program from a command prompt via .\bin\Test, argv[0] is "C:/dev/stuff/bin/Test". With the latest MinGW (gcc version 4.5.2), invoking a binary via .\bin\Test means argv[0] is ".\bin\Test". A Microsoft Visual C++-built binary (cl version 16.00.40219.01) invoked via .\bin\Test also has ".\bin\Test" for argv[0].

Related

Calling shell script from system c++ function making the shell script running as different user

I am using the system c++ call to execute the shell script the caller program is running as root but the shell sctipt which is called form the c++ code is running as different user.
How can I make sure the shell script should also run as root user like the c++ binary.
I don't want to rely on using sudo command as it can ask for password.
> [user#user ~]$ ll a.out temp.sh
> -rwsrwsr-x 1 root root 8952 Jun 14 13:16 a.out
> -rwxrwxr-x 1 user user 34 Jun 14 15:43 temp.sh
[user#user ~]$ cat temp.sh
#!/bin/bash read -n 1 -p "Hello"
[user#user ~]$ ps aux | grep temp
root 13247 0.0 0.0 13252 1540 pts/0 S+ 15:44 0:00 ./a.out ./temp.sh
user 13248 0.0 0.0 113152 2544 pts/0 S+ 15:44 0:00 /bin/bash ./temp.sh
c++ code
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char *argv[])
{
system(argv[1]);
return 0;
}
A few bits of documentation to start:
From man 3 system's caveats section:
Do not use system() from a privileged program (a set-user-ID or set-group-ID program, or a program with capabilities) because strange values for some environment variables might be used to subvert system integrity. For example, PATH could be manipulated so that an arbitrary program is executed with privilege. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3) (which also use the PATH environment variable to search for an executable).
system() will not, in fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash version 2: as a security measure, bash 2 drops privileges on startup. Debian uses a different shell, dash(1), which does not do this when invoked as sh.)
And from the bash manual's description of the -p command line argument (Emphasis added):
Turn on privileged mode. In this mode, the $BASH_ENV and $ENV files are not processed, shell functions are not inherited from the environment, and the SHELLOPTS, BASHOPTS, CDPATH and GLOBIGNORE variables, if they appear in the environment, are ignored. If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied, these actions are taken and the effective user id is set to the real user id. If the -p option is supplied at startup, the effective user id is not reset. Turning this option off causes the effective user and group ids to be set to the real user and group ids.
So even if your /bin/sh doesn't drop privileges when run, bash will when it's run in turn without explicitly telling it not to.
So one option is to scrap using system(), and do a lower-level fork()/exec() of bash -p your-script-name.
Some other approaches to allowing scripts to run at elevated privileges are mentioned in Allow suid on shell scripts. In particular the answer using setuid() to change the real UID looks like it's worth investigating.
Or configure sudo to not require a password for a particular script for a given user.
Also see Why should I not #include <bits/stdc++.h>?

How to use command in function system() [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I wanted to use the system("cd \") function to go to the root directory but it does not work because as I want to create a folder system("md examplecpp"); in this path, a folder is created where I have a program.
Your problem, is that "the current directory" is a per-process property (although a child process inherits the current directory of its parent as an initial setting). The system function creates a shell process, which executes the cd command (thus changing the current directory of the shell process), and then exits. The current directory of the parent process (your program) is never changed.
Look into the _chdir function (or for Posix chdir)
Edit: You are definitely on Windows - you even put it in the tags!
I wanted to use the system("cd \") function to go to the root
directory but it does not work because as I want to create a folder
system("md examplecpp");
Merge the two (or more) commands together into one command, separated by semicolon, prior to invoking system.
i.e.
std::string cmd = "cd /home/dmoen ; mkdir examplecpp ; ls -lsa ";
std::cout << "\nsystem command: " << cmd << "\n" << std::endl;
std::system (cmd.c_str());
Lesson - the cmd string should look just like you'd type at a command prompt. During testing, you might decide to include a "rmdir examplecpp", as "mkdir" complains if dir already exists.
output (with uninteresting things snipped):
system command: cd /home/dmoen ; mkdir examplecpp ; ls -lsa
total 402216
4 drwxr-xr-x 105 dmoen dmoen 4096 Dec 23 11:42 .
4 drwxr-xr-x 5 root root 4096 Jan 3 2016 ..
[snip]
4 drwxrwxr-x 2 dmoen dmoen 4096 Dec 23 11:42 examplecpp
[snip]

ERROR: ld.so: object '/opt/xyz/mylib.so' from LD_PRELOAD cannot be preloaded: ignored

After upgrading a software from 32 to 64-bit, a pre-compiled binary starts to fail with the error:
[root#localhost /root]# LD_PRELOAD=/opt/xyz/lib/mylib.so mycommand /inputfile.txt /outputfile.txt
ERROR: ld.so: object '/opt/xyz/lib/mylib.so' from LD_PRELOAD cannot be preloaded: ignored.
mycommand: error while loading shared libraries: mylib.so: cannot open shared object file: No such file or directory
mycommand is a precompiled binary that sits in /usr/bin/mycommand. It requires the library as LD_PRELOAD.
Obviously I made sure that the file /opt/xyz/lib/mylib.so does exist:
[root#localhost lib]# ls -alh
total 512K
drwxr-xr-x 2 root root 4.0K Mar 1 15:40 .
drwxr-xr-x 10 root root 4.0K Feb 24 10:54 ..
-rwxr-xr-x 1 root root 218K Oct 28 22:41 mylib64.so
lrwxrwxrwx 1 root root 14 Mar 1 15:40 mylib.so -> mylib64.so
Is there any more debugging information I can extract other that this error?
It seems like not relevant if I simply delete the mylib.so or not, the error stays the same. It's kind of hard to say if it can't preload the library or if it can't find the library.
Is there any other environment variables or libraries that are required for LD_PRELOAD to work?
It seems to me essential to acquire a version of the source code of the mycommand binary and re-compile it. However to do that I think it would be probably useful to see why it actually fails right now.
LD_PRELOAD will not work on symlinks. You will need to set the path to the actual location of the library. For example:
LD_PRELOAD=/opt/xyz/lib/mylib64.so
I agree that a more specific error message would've been useful.

Program execution steps

I have a c++ program that works fine, however it needs to run for a long time. But while it is running I could continue to develop some parts of it. If I recompile my program, this will replace the binary with a new one. Does this will modify the behavior of the running program? Or are the process and the binary file two separate things once the program is launched?
More generally, what are the steps of a program execution?
On Linux, the process uses memory mapping to map the text section of the executable file and shared libraries directly into the running process memory. So if you could overwrite the executable file, it would affect the running process. However, writing into a file that's mapped for execution is prohibited -- you get a "Text file busy" error.
However, you can still recompile the program. If the compiler (actually the linker) gets this error, it removes the old executable file and creates a new one. On Unix, if you remove a file that's in use, the file contents are not actually removed from the disk, only the reference from the directory entry is removed; the file isn't fully deleted until all references to it (directory entries, file descriptors and memory mappings) go away. So the running process continues to be mapped to the old, nameless file. You can see this with the following demonstration:
barmar#dev:~$ ls -li testsleep
229774 -rwxr-xr-x 1 barmar adm 4584 Apr 24 04:30 testsleep
barmar#dev:~$ ./testsleep &
[1] 17538
barmar#dev:~$ touch testsleep.c
barmar#dev:~$ make testsleep
cc testsleep.c -o testsleep
barmar#dev:~$ ls -li testsleep
229779 -rwxr-xr-x 1 barmar adm 4584 Apr 24 04:32 testsleep
The inode number changed from 229774 to 229779 when I recompiled the program while it was running, indicating that a new file was created.
On Windows, you couldn't even write the new executable while the old version is running. The file on disk is locked while the process exists. On Linux, you can overwrite the file on disk, but the copy in memory remains untouched.
OTOH, while running in an IDE, it may be possible to patch the running process as the IDE is aware of the relevant details. But it's complex and not all IDE's support this.

MATLAB + Mex + OpenCV: Links and compiles correctly but can't find library at run time

I have a mex function which uses OpenCV that I'm trying to use. The compilation seems to work, but when I try and call the function within MATLAB I get this error:
Invalid MEX-file '/path/to/project/mexfunction.mexa64': libopencv_legacy.so.2.4: cannot open shared object file: No such file or directory
My OpenCV 2.4.5 install is located at /nwdata/username/ (I compiled myself from scratch using the OpenCV recommended settings from their documentation). I compile with mex using this function:
function cvmex(mexfile)
OCV_INC_DIR='/nwdata/username/include/opencv';·
OCV2_INC_DIR='/nwdata/username/include';·
OCV_LIB_DIR ='/nwdata/username/lib';·
mex(mexfile,'-g','-v',['-I',OCV2_INC_DIR],['-I',OCV_INC_DIR],['-L',OCV_LIB_DIR],'DUSE_DOUBLE',...
'-lopencv_legacy','-lopencv_imgproc','-lopencv_core','-lopencv_contrib','-lopencv_ml',...
'-lopencv_objdetect','-lopencv_calib3d','-lopencv_flann','-lopencv_features2d',...
'-lopencv_video','-lopencv_gpu');
end
When I compile, I get no errors. I then checked to see what the dependencies of the library are using ldd mexfunction.mexa64. Here is the relevant line:
libopencv_legacy.so.2.4 => /nwdata/username/lib/libopencv_legacy.so.2.4 (0x00002ad6a2123000)
Ok, so that seems alright. That file definitely exists:
[username#machine:/nwdata/username/lib]
$ ls -l libopencv_legacy*
lrwxrwxrwx 1 username REDACTED 25 Jul 15 15:07 libopencv_legacy.so -> libopencv_legacy.so.2.4.5
lrwxrwxrwx 1 username REDACTED 25 Jul 15 15:07 libopencv_legacy.so.2.4 -> libopencv_legacy.so.2.4.5
-rwxr-xr-x 1 username REDACTED 1303944 Jun 17 15:37 libopencv_legacy.so.2.4.5
[username#machine:/nwdata/username/lib]
$ file libopencv_legacy.so*
libopencv_legacy.so: symbolic link to `libopencv_legacy.so.2.4.5'
libopencv_legacy.so.2.4: symbolic link to `libopencv_legacy.so.2.4.5'
libopencv_legacy.so.2.4.5: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped
Running on Linux:
Linux machine#redacted 2.6.43.8-1.fc15.x86_64 #1 SMP Mon Jun 4 20:33:44 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
MATLAB R2011b, and g++ 4.6.3.
This seems odd. Any ideas? This machine is part of a cluster, so I don't have root access or anything.
I encountered a similar problem some days ago. This problem only happens when you using the matlab remotely on a cluster. Editing the LD_LIBRARY_FILE in Matlab is useless, and I don't know why. I solved the problem by defining the LD_LIBRARY_PATH in .bashrc (or .bash_profile).
I had a very similar problem and solved it by adding a soft link to the missing library in the Matlab binaries directory, where all the other libs were located, in my case:
sudo ln -s /users/marc/lib/libName.so /opt/matlab/MATLAB_R2014b_Linux_x86-64/bin/glnxa64/libName.so
solved the problem.