I'm trying to use gaddr2line to print a prettier version of backtrace symbols. I'm just getting the addresses from backtrace() then feeding them to gaddr2line by executing it using popen. Using addr2line under linux works fine, but for some reason I'm getting strange errors when using gaddr2line on OS X. When trying this on Mac OS I keep getting BFD: ... : unknown load command ... I saw some posts metioning GDB, so I tried installing that with homebrew, but that didn't help.
Here is my code:
// set to the maximum possible path size
char exe_path[4096];
// Used to check if an error occured while setting up command
bool error = false;
// Check if we are running on Mac OS or not, and select appropriate command
char* command;
#ifdef __APPLE__
// Check if 'gaddr2line' function is available, if not exit
if( !system( "which gaddr2line > /dev/null 2>&1" ) ) {
command = "gaddr2line -Cfspe";
pid_t pid = getpid();
int path_length = proc_pidpath( pid, exe_path, sizeof( exe_path ) );
if( path_length <= 0 ) {
writeLog( SIMPLOG_LOGGER, "Unable to get execution path. Defaulting to standard backtrace." );
error = true;
}
exe_path[path_length] = 0;
} else {
writeLog( SIMPLOG_LOGGER, "Function 'gaddr2line' unavailable. Defaulting to standard backtrace. Please install package 'binutils' for better stacktrace output." );
error = true;
}
#endif
Here is the output:
gaddr2line -Cfspe /Users/nate/git/SimpleLogger/build/test 7408927
BFD: /Users/nate/git/SimpleLogger/build/test: unknown load command 0x2a
BFD: /Users/nate/git/SimpleLogger/build/test: unknown load command 0x28
BFD: /Users/nate/git/SimpleLogger/build/test: unknown load command 0x29
BFD: /Users/nate/git/SimpleLogger/build/test: unknown load command 0x2b
Related
I was taking a look at this Github project: https://github.com/LloydLabs/delete-self-poc
This project uses the SetFileInformationByHandle API (https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle) in a somewhat creative way to allow the deletion from disk of a locked file. I am attempting to implement this as part of a larger program, however I have ran into an issue when compiling for x86. I use mingw-w64 on a debian machine to compile my program and when doing compatibility checks for x86, I found a very strange issue.
#include "main.h"
static
HANDLE
ds_open_handle(
PWCHAR pwPath
)
{
return CreateFileW(pwPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
static
BOOL
ds_rename_handle(
HANDLE hHandle
)
{
FILE_RENAME_INFO fRename;
RtlSecureZeroMemory(&fRename, sizeof(fRename));
// set our FileNameLength and FileName to DS_STREAM_RENAME
LPWSTR lpwStream = DS_STREAM_RENAME;
fRename.FileNameLength = sizeof(lpwStream);
RtlCopyMemory(fRename.FileName, lpwStream, sizeof(lpwStream));
return SetFileInformationByHandle(hHandle, FileRenameInfo, &fRename, sizeof(fRename) + sizeof(lpwStream));
}
static
BOOL
ds_deposite_handle(
HANDLE hHandle
)
{
// set FILE_DISPOSITION_INFO::DeleteFile to TRUE
FILE_DISPOSITION_INFO fDelete;
RtlSecureZeroMemory(&fDelete, sizeof(fDelete));
fDelete.DeleteFile = TRUE;
return SetFileInformationByHandle(hHandle, FileDispositionInfo, &fDelete, sizeof(fDelete));
}
int
main(
int argc,
char** argv
)
{
WCHAR wcPath[MAX_PATH + 1];
RtlSecureZeroMemory(wcPath, sizeof(wcPath));
// get the path to the current running process ctx
if (GetModuleFileNameW(NULL, wcPath, MAX_PATH) == 0)
{
DS_DEBUG_LOG(L"failed to get the current module handle");
return 0;
}
HANDLE hCurrent = ds_open_handle(wcPath);
if (hCurrent == INVALID_HANDLE_VALUE)
{
DS_DEBUG_LOG(L"failed to acquire handle to current running process");
return 0;
}
// rename the associated HANDLE's file name
DS_DEBUG_LOG(L"attempting to rename file name");
if (!ds_rename_handle(hCurrent))
{
DS_DEBUG_LOG(L"failed to rename to stream");
return 0;
}
DS_DEBUG_LOG(L"successfully renamed file primary :$DATA ADS to specified stream, closing initial handle");
CloseHandle(hCurrent);
// open another handle, trigger deletion on close
hCurrent = ds_open_handle(wcPath);
if (hCurrent == INVALID_HANDLE_VALUE)
{
DS_DEBUG_LOG(L"failed to reopen current module");
return 0;
}
if (!ds_deposite_handle(hCurrent))
{
DS_DEBUG_LOG(L"failed to set delete deposition");
return 0;
}
// trigger the deletion deposition on hCurrent
DS_DEBUG_LOG(L"closing handle to trigger deletion deposition");
CloseHandle(hCurrent);
// verify we've been deleted
if (PathFileExistsW(wcPath))
{
DS_DEBUG_LOG(L"failed to delete copy, file still exists");
return 0;
}
DS_DEBUG_LOG(L"successfully deleted self from disk");
return 1;
}
When compiling the base code found in the linked repository (and shown above) as x86, attempting to run the program fails at the SetFileInformationByHandle call in the ds_rename_handle function. Calling GetLastError() returns 123:
ERROR_INVALID_NAME
123 (0x7B)
The filename, directory name, or volume label syntax is incorrect.
The very bizarre part is that the program succeeds when ran from an Administrator prompt. Even stranger, compiling the same code for x64 works both in a normal and an Administrator prompt.
As a sanity check I copied the code verbatim over to VS2019 and compiled there, and the resulting x86 program was able to run without Administrator privileges.
The only changes to the source code made on the debian system were made in the header file:
#pragma once
#pragma comment(lib, "Shlwapi.lib")
#include <Windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include <stdlib.h>
#define DS_STREAM_RENAME L":wtfbbq"
#define DS_DEBUG_LOG(msg) wprintf(L"[LOG] - %s\n", msg)
Where <Windows.h> was changed to <windows.h> and the DS_DEBUG_LOG line changed to %ls so that the entire log message would print.
The GCC command used to compile for x86 was:
i686-w64-mingw32-gcc main.c -o delete32.exe -s -DUNICODE -Os -lshlwapi
I have tried removing all switches and compiling and it still fails.
As a note, the shlwapi library is only required for the very last call in main(), PathFileExistsW. I have commented out that portion and removed shlwapi from imports and from the gcc command to no effect.
The x64 gcc command which succeeded was:
x86_64-w64-mingw32-gcc main.c -o delete32.exe -s -DUNICODE -Os -lshlwapi
In the issues tab of the github repo there are some mentions of errors in the code which I have looked at separately. However I would desperately like to know why mingw is causing an issue with the 32 bit version of this program. Unfortunately "just compile with VS" isn't an option, as I use a program to generate and compile the program that this bit of code will be part of on my linux machine.
Thanks for any insight.
RtlCopyMemory(fRename.FileName, lpwStream, sizeof(lpwStream)); writes 4 or 8 bytes into a 2 byte buffer! Who knows where the remaining bytes are going, the program behavior is probably undefined.
The concept of deleting a running exe like this might work but the code indicates that the author does not have a full understanding of Windows and C. You are better off rewriting it from scratch...
Edit:
After playing with your uploaded files I can with confidence say that it is the lack of a application manifest that causes it to fail when it is not elevated. Your vc file has a requestedExecutionLevel element which gives it Vista operating system context. If you remove the manifest resource in the vc exe it stops working. If you add a manifest to the mingw exe it starts working.
the ds_rename_handle function is wrong implemented.
the FILE_RENAME_INFO is variable size structure.
as result declaration
FILE_RENAME_INFO fRename;
almost always wrong (it will ok only if FileName containing 1 or 2 symbols)
really we need first calculate FileNameLength and then allocate PFILE_RENAME_INFO based on this
as example:
ULONG FileNameLength = (ULONG)wcslen(DS_STREAM_RENAME) * sizeof(WCHAR);
ULONG dwBufferSize = FIELD_OFFSET(FILE_RENAME_INFO, FileName) + FileNameLength;
PFILE_RENAME_INFORMATION fRename = (PFILE_RENAME_INFORMATION)alloca(dwBufferSize);
so complete code for ds_rename_handle can be next:
ULONG ds_rename_handle(HANDLE hHandle, PCWSTR DS_STREAM_RENAME)
{
ULONG FileNameLength = (ULONG)wcslen(DS_STREAM_RENAME) * sizeof(WCHAR);
ULONG dwBufferSize = FIELD_OFFSET(FILE_RENAME_INFO, FileName) + FileNameLength;
PFILE_RENAME_INFO fRename = (PFILE_RENAME_INFO)alloca(dwBufferSize);
fRename->ReplaceIfExists = TRUE;
fRename->RootDirectory = 0;
fRename->FileNameLength = FileNameLength;
memcpy(fRename->FileName, DS_STREAM_RENAME, FileNameLength);
return SetFileInformationByHandle(hHandle, FileRenameInfo,
fRename, dwBufferSize) ? NOERROR : GetLastError();
}
but documentation of FILE_RENAME_INFO is very bad. unclear - in what form - full pathname, file name or a relative pathname - must be FileName ?!
from my research - it must be full pathname only (not file name) or begin with a colon : ( The new name for the stream )
much more better use NtSetInformationFile with FileRenameInformation
compare description of FILE_RENAME_INFORMATION structure with FILE_RENAME_INFO !
here exist detailed description - in what form FileName it should be.
so i always use
NTSTATUS ds_rename_handle_nt(HANDLE hHandle, PCWSTR DS_STREAM_RENAME)
{
ULONG FileNameLength = (ULONG)wcslen(DS_STREAM_RENAME) * sizeof(WCHAR);
ULONG dwBufferSize = FIELD_OFFSET(FILE_RENAME_INFO, FileName) + FileNameLength;
PFILE_RENAME_INFORMATION fRename = (PFILE_RENAME_INFORMATION)alloca(dwBufferSize);
fRename->ReplaceIfExists = TRUE;
fRename->RootDirectory = 0;
fRename->FileNameLength = FileNameLength;
memcpy(fRename->FileName, DS_STREAM_RENAME, FileNameLength);
IO_STATUS_BLOCK iosb;
return NtSetInformationFile(
hHandle, &iosb, fRename, dwBufferSize, FileRenameInformation);
}
There is this piece of C code that is used extract info from binaries:
bincode_t *initialize_bincode(const char *file)
{
bfd *abfd;
bincode_t *bin;
//char *target = "x86_64-unknown-linux-gnu";
char *target = "i686-pc-linux-gnu";
bfd_init();
if (!bfd_set_default_target(target)) {
bs_dbgmsg(" (!) bfd_set_default_target()\n");
return NULL;
}
if ((abfd = bfd_openr(file, target)) == NULL) {
bs_dbgmsg(" (!) bfd_openr(): %s\n", file);
return NULL;
}
if (!bfd_check_format(abfd, bfd_object)) {
//isolated the error to be here (through simple print debugging)
bs_dbgmsg(" (!) bfd_check_format()\n");
printf("Error: %s", bfd_errmsg(bfd_get_error()));
bfd_close(abfd);
return NULL;
}
if((bin = malloc(sizeof(bincode_t))) == NULL) {
bs_errmsg(" (!) malloc(): bin\n");
exit(EXIT_FAILURE);
}
I ran this code on Linux against 2 Windows binary samples. However, one of sample results in an error of
Error: File format not recognized ... Section flag STYP_DSECT (0x1) ignored
The file command on both samples results in the following output:
fc671a044d48bffe519a89b06d289d83f52958cb: PE32 executable (GUI) Intel
80386, for MS Windows
and
fe0c189a5067a2dfe46bad1c2cedaa5b7bbc6a20: PE32 executable (DLL) (GUI)
Intel 80386, for MS Windows
The second binary (DLL) results into the error. My question is, why did this happen? What can I do to resolve this? I would like the code to also "see" the DLL binary.
I plugged the DLL binary into gdb and indeed gdb didn't recognize the file. GDB output:
...not in executable format: File format not recognised
Edit 1:
Added code and completed error message output. Kindly note that I am a C beginner.
Edit 2:
As suggested in the comments, I have used bfd_errmsg(bfd_get_error()) and included the output above.
For a project I am working on, I am trying to use libgit2.
For the moment, I am just trying to create a repo using git_repository_init but it fails with the following error message :
Error: -1/2: Failed to resolve path 'D:/Workspace/<project_name>/test/.git/': Invalid argument
The code is the following :
#include <iostream>
#include "git2.h"
int main(int argc, char *argv[])
{
git_libgit2_init();
git_repository *repo;
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
opts.flags |= GIT_REPOSITORY_INIT_MKDIR;
int err = git_repository_init_ext(&repo, "D:/Workspace/<project_name>/test", &opts);
if(err < 0)
{
const git_error *e = giterr_last();
std::cerr << "Error: " << err << "/" << e->klass << ": " << e->message << std::endl;
}
git_repository_free(repo);
git_libgit2_shutdown();
return 0;
}
The .git directory in the test directory is still created but it's empty.
I have tried with relative and absolute path and also Unix and windows path but the result seem to always be the same.
Also, when executing libgit2_clar, a lot of test fail always with the same error : "error -1 - Failed to resolve path 'attr': Invalid argument".
libgit2 and the previous code has been compiled and executed on a Windows XP 32bit using MinGW with gcc 4.8.1.
Windows XP is not supported by libgit2. Support ended in v0.21.0 in 2014:
Top-level Improvements
We've dropped support for Windows XP. We're evil like that.
The repo_path parameter of git_repository_init_ext isn't legal because
it contains the chars '<' '>' .
Try to make new directory with the above chars and you get error message.
I'm trying to compile example code that I found in the plotutils documentation. I added the appropriate search path for the plot.h header file and linked the binary to every object file that gets created when you make install plotutils 2.6. I'm on OS X 10.10.3 with Xcode 6.3.2 and I'm a novice when it comes to C programming or using Xcode.
The example code I'm trying to compile is:
#include <stdio.h>
#include <plot.h>
#define MAXORDER 12
void draw_c_curve (plPlotter *plotter, double dx, double dy, int order)
{
if (order >= MAXORDER)
/* continue path along (dx, dy) */
pl_fcontrel_r (plotter, dx, dy);
else
{
draw_c_curve (plotter,
0.5 * (dx - dy), 0.5 * (dx + dy), order + 1);
draw_c_curve (plotter,
0.5 * (dx + dy), 0.5 * (dy - dx), order + 1);
}
}
int main ()
{
plPlotter *plotter;
plPlotterParams *plotter_params;
/* set a Plotter parameter */
plotter_params = pl_newplparams ();
pl_setplparam (plotter_params, "PAGESIZE", "letter");
/* create a Postscript Plotter that writes to standard output */
if ((plotter = pl_newpl_r ("ps", stdin, stdout, stderr,
plotter_params)) == NULL)
{
fprintf (stderr, "Couldn’t create Plotter\n");
return 1;
}
if (pl_openpl_r (plotter) < 0) /* open Plotter */
{
fprintf (stderr, "Couldn’t open Plotter\n");
return 1;
}
pl_fspace_r (plotter, 0.0, 0.0, 1000.0, 1000.0); /* set coor system */
pl_flinewidth_r (plotter, 0.25); /* set line thickness */
pl_pencolorname_r (plotter, "red"); /* use red pen */
pl_erase_r (plotter); /* erase graphics display */
pl_fmove_r (plotter, 600.0, 300.0); /* position the graphics cursor */
draw_c_curve (plotter, 0.0, 400.0, 0);
if (pl_closepl_r (plotter) < 0) /* close Plotter */
{
fprintf (stderr, "Couldn’t close Plotter\n");
return 1;
}
if (pl_deletepl_r (plotter) < 0) /* delete Plotter */
{
fprintf (stderr, "Couldn’t delete Plotter\n");
return 1;
}
return 0;
}
The 2 issues Xcode identifies are:
Undefined symbols for architecture x86_64:
"__pl_z_maybe_output_image", referenced from:
__maybe_output_image in b_defplot.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
b_defplot.o and other object files were generated when I tried installing plotutils 2.6 by first downloading it, and doing ./configure, make, and make install.
My ultimate goal is to use the libplot package in a program I'm writing that requires a few plots to be generated, and I want my program binary file to be self-contained (i.e. if I execute my program binary on any other computer without plotutils installed, it should still work). This is why I'm linking my binary with every object file that gets created in the libplot folder when I install plotutils as described above.
Any help with the error I'm getting or enlightening me on something I'm doing terribly wrong keeping in mind what my end goal is, would be greatly appreciated.
I managed to compile the libplot example program successfully by following these steps:
Install plotutils via Homebrew using: brew install plotutils
Locate (within /usr/local/ or wherever your Homebrew installation resides) and link libplot.2.2.4.dylibto the C program you are trying to build (in this case, the C program is the one provided in the question).
Also make sure you include the plot.h header file in your header search path.
By following these steps, I was able to compile the above code (with the modifications shown below) cleanly and was able to export the png shown below. Homebrew seems to have saved me the trouble of building plotutils on my own.
I replaced if ((plotter = pl_newpl_r ("ps", stdin, stdout, stderr, plotter_params)) == NULL) with if ((plotter = pl_newpl_r ("png", stdin, fp, stderr, plotter_params)) == NULL) where fp is defined as follows:
FILE *fp;
fp = fopen("/Users/username/path/Test.png", "w");
// rest of example code shown above
fclose(fp);
I cannot mount some IMAGE on the MacOSX 10.10 via C function mount().
I use following code
...
char ImgPath[m_IMGName.size() + 1];
memcpy(ImgPath, m_IMGName.c_str(), m_IMGName.size() + 1);
struct hfs_mount_args data;
data.fspec = ImgPath;
if(mount("hfs+", m_TargetPath.c_str(), MNT_RDONLY, &data)){
throw SysCallTestExeption(errno, "mount() is failed!", GET_FILE_LINE_FUNC_ARG);
}
...
And when I start the program I get the error "mount() is failed! Error(Operation not supported by device)."
And image was mounted if I use the following command:
sudo hdiutil attach -mountpoint "${MountPointPath}" "${Image}"
Also, when I use mount() on the Linux - All is OK.
Following Linux code:
...
if(mount(m_IMGName.c_str(), m_TargetPath.c_str(), m_FSType.c_str(), 0, m_Opts.c_str())){
throw SysCallTestExeption(errno, "mount() is failed!", GET_FILE_LINE_FUNC_ARG);
}
...
There is no hfs+ type in
mount("hfs+", m_TargetPath.c_str(), MNT_RDONLY, &data)
You should use hfs to handle both HFS and HFS+. Look for example at mount_hfs command source:
#define HFS_MOUNT_TYPE "hfs"