Apologies in advance about the long post - I'm having a problem I think might be related to garbage collection.
I have a class that wraps DMDScript like this:
/**
* Wrapper class for the DMDScript
*/
class ScriptingHost
{
protected static CallContext *cc ; // Call Context for interaction with the script.
protected static Program prg ; // Reference to program object (this is where the script buffer gets parsed)
static this()
{
// create our program instance
prg = new Program();
// create reference to call Context
cc = prg.callcontext;
Stdout( "cc.global: " )( cc.global ).newline ;
// add some built-in functions, like trace() and trigger()
DnativeFunction dnfTrace = new DnativeFunction( &jsTrace, "trace", 0, Dfunction.getPrototype() ) ;
DnativeFunction dnfTrigger = new DnativeFunction( &jsTrigger, "trigger", 0, Dfunction.getPrototype() ) ;
// add it to the call context
cc.global.Put("trace", dnfTrace , 0);
cc.global.Put("trigger", dnfTrigger , 0);
}
/***********************************************************************
* Helper functions for D<-->JS interaction
************************************************************************/
/**
* Trace (output)
*/
protected static void* jsTrace( Dobject pthis, CallContext* cc, Dobject othis, Value* ret, Value[] arglist)
{
Stdout( "<<" )( arglist ).newline ;
return null;
}
/**
* Trigger
*/
protected static void* jsTrigger( Dobject pthis, CallContext* cc, Dobject othis, Value* ret, Value[] arglist)
{
Stdout( "<<" )( arglist ).newline ;
return null;
}
}
So far, everything is fine and I can run the code with no errors.
Output:
cc.global: dmdscript_tango.dglobal.Dglobal
I also added a method to ScriptingHost that traced the cc.global object:
public static void testGlobal()
{
Stdout( "testGlobal: " )( cc.global ).newline.flush ;
}
...which also works fine - The problem arises when I try accessing it from outside the class ala:
int main()
{
Stdout( "DMDScriptTest" ).newline ;
ScriptingHost.testGlobal() ;
Stdout( "global: " )( ScriptingHost.global() ).newline.flush ;
ScriptingHost.testGlobal() ;
}
Then I get the following error:
cc.global: dmdscript_tango.dglobal.Dglobal
DMDScriptTest
testGlobal: dmdscript_tango.dglobal.Dglobal
object.Exception: Illegal Instruction
----------------
[ 5fd264] 0+0 ??? #0+1975211 :0
[ 404e05] 0+0 tango.text.convert.Layout.Layout!(char).Layout.parse.process #0+29 c:\dmd\dmd\bin\..\import\tango\text\convert\Layout.d:595
[ 404875] 0+0 tango.text.convert.Layout.Layout!(char).Layout.parse #0+65 c:\dmd\dmd\bin\..\import\tango\text\convert\Layout.d:603
[ 40463b] 0+0 tango.text.convert.Layout.Layout!(char).Layout.convert #0+34 c:\dmd\dmd\bin\..\import\tango\text\convert\Layout.d:347
[ 40418e] 0+0 tango.io.stream.Format.FormatOutput!(char).FormatOutput.print #0+67 c:\dmd\dmd\bin\..\import\tango\io\stream\Format.d:172
[ 40206c] 0+0 __Dmain #0+45 test2.d:87
[ 4380b5] 0+0 rt.compiler.dmd.rt.dmain2.main.runMain #0+119292 :0
[ 43800b] 0+0 rt.compiler.dmd.rt.dmain2.main.tryExec #0+119122 :0
[ 4380f3] 0+0 rt.compiler.dmd.rt.dmain2.main.runAll #0+119354 :0
[ 43800b] 0+0 rt.compiler.dmd.rt.dmain2.main.tryExec #0+119122 :0
[ 437fc3] 0+0 _main #0+119050 :0
[ 44c980] 0+0 _mainCRTStartup #0+203463 :0
[75e133c8] 0+0 ??? #0+1973388559 :0
[76f49ed0] 0+0 ??? #0+1991438359 :0
[76f49ea0] 0+0 ??? #0+1991438311 :0
global: unittest start
unittest end
Is anyone able to shed some light on the issue here - and perhaps how to work around it, please? :)
edit: I am using a windows D1-Tango setup. The version I'm using is the 0.99.9 Tango/DMD 1.056 Kai bundle .
thanks,
First of all, which operating system are you using? Based on the error I'm guessing windows? Which version of dmd/tango are you using? 32 bit or 64 bit? Try running your application through a disassembler and looking what instruction is listed at 5fd264 (search through the output). We should be able to help more with some of the information above.
What version of DMD are you using? If you're still using 1.067, are compiling in 64-bit mode and are using sufficiently ancient hardware, you may have a problem. 1.067 was the first version with 64-bit support and had a bug that would use the LAHF and SAHF instructions, which weren't supported on very old 64-bit CPUs.
Related
Thanks Mark, on resumption, now I get Z_DATA_ERROR
case STORED:
strm->msg = (char *)"invalid stored block lengths"; // got here - Anton
state->mode = BAD;
Just to see I understood your suggestions yesterday:
after inflateInit2()
// go to byte offset
ZSEEK64( , , streamCurrentPos, ZLIB_FILEFUNC_SEEK_SET)
if ( streamBits > 0 )
{
// get that byte
unz64local_getByte( , , &aByte)
// and shift down by the number of bits. This API doing it?
inflatePrime ( , streamBits, 8 - streamBits)
} else { no bits to insert }
inflateSetDictionary ()
And state of uncompression is saved like this:
typedef struct state_of_uncompression
{
ZPOS64_T streamCurrentPos; // from : unzGetCurrentFileZStreamPos64()
int streamBits; // from : stream.data_type & (~0x1C0), after clearing bits 8,7, and 6
Byte dictionary_buf[32768]; // from : inflateGetDictionary()
uInt dictLength; // from : inflateGetDictionary();
uint64_t output_wrt_offset // got this already.
} uncompression_state_info;
What? No.
inflatePrime ( , streamBits, 8 - streamBits) makes no sense. And you did nothing with the aByte you got.
It should be inflatePrime(strm, streamBits, aByte >> (8 - streamBits)).
I have a structure which is passed to a function. There some values should be changed. Back in the main I should read out the new values of my structure.
In a sample project it works EASILY!
In my main code in Visual Studio Code with ARM Cortex M3 and a crazy library it DOESNT WORK
(update: something must be wrong with compiler: I checked it hard and observed that not all variables are changed!!! )
My first big goal is to change chip_count within my structure.
Here the program flow:
I create a pointer to a special type of structure.
I pass this pointers address to a function.
The function creates a new adress with malloc.
The passed pointer gets this new address.
So it points now on the malloc.
Some variable are of the malloc structure are changed.
Return to main
Access the malloc space and read out the changed stuff
Result:
YES IT WORKS
Sry I added some more functions to understand const declarations better.
Pointer stuff:
vtss_state_t is the same as struct vtss_state_s
vtss_inst_t in turn is a pointer on struct vtss_state_s or vtss_state_t
the function vtss_create() has a parameter *vtss_inst_t const inst
This is a pointer on the pointer of of vtss_sate_s or a pointer to vtss_inst_t.
In the function at the end the adress of malloc is passed to (*inst).
(*inst) is the content of the of the pointer on which inst is pointing to.
So the pointer will point to malloc().
I hope this is right...
FIRST APPROACH ONLINE COMPILER
I just wanted to see if I can change values in my passed structure.
Result: YES
FILE:state.h
#include <stdint.h>
/******************************************************************************
Structures
*******************************************************************************/
typedef struct tests
{
int x;
} tests_t;
typedef uint32_t u32;
// This is the structure with chip_count
typedef struct vtss_state_s
{
int a;
int cookie;
tests_t u;
int port_count;
u32 chip_count // doesnt change in my real program :(
} vtss_state_t;
// typedef this structure so vtss_inst_t points to it...(?)
typedef struct vtss_state_s *vtss_inst_t;
FILE: main.c
/******************************************************************************
Online C Compiler.
Code, Compile, Run and Debug C program online.
Write your code in this editor and press "Run" button to compile and execute it.
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "state.h"
#define VTSS_PORT_COUNT 1
#define VTSS_PORTS VTSS_PORT_COUNT
//
// some global structure
vtss_inst_t testspace;
vtss_inst_t *testspace_ptr;
/******************************************************************************
functions
*******************************************************************************/
// vtss_inst_get ()
// const adress another access style
void
vtss_inst_get (const vtss_inst_t inst)
{
printf ("\ninst get()");
printf ("\ninst get() adress of inst:%d", inst);
inst->a = 1111;
}
// This function just should proof that it can change the content
void
vtss_inst_default_set (vtss_state_t * vtss_state)
{
vtss_state->port_count = VTSS_PORTS; // 1
}
//** This is the function which should change "chip_count"
// Man soll einen Pointer auf einen Pointer auf die Struktur C<bergeben (wo sich chip_count befindet )
void
vtss_inst_create (vtss_inst_t * const inst)
{
printf ("\r\n**vtss_inst_create()");
vtss_state_t *vtss_state; // temporary pointer ( just points to a structure )
vtss_state_t *vtss_state_2; // just a test compare
// pass temporary pointer a new memory space:
vtss_state = malloc (sizeof (*vtss_state)); // is this correct? anyway it also doesnt work without this malloc style
printf ("\r\n\t malloc=: %d", vtss_state);
// pass test temporary pointer a new memory space:
vtss_state_2 = malloc (sizeof (*vtss_state_2));
printf ("\r\n\t (test) malloc2: %d", vtss_state_2);
//Test: pass the temporary pointer even deeper & change content of structure where it points to
// (change variable port_count to 1)
vtss_inst_default_set (vtss_state);
vtss_inst_default_set (vtss_state_2);
//Test:Change values of the structure:
vtss_state->a = 1;
vtss_state->chip_count = 12121;
vtss_state_2->chip_count = 12121;
// ** Giving back the new adress from malloc:
// Test:(from vtss_state or vtss_state2)
(*inst) = vtss_state; //passing adress1
//Test: a should have the correct value:
printf ("\r\n\ta=1? a =%d ", (*inst)->a);
(*inst) = vtss_state_2; //passing adress2
// Some extra tests:
(testspace) = vtss_state_2; // extra Test: pass the adress to a test pointer
testspace_ptr = &testspace; // extra Test: pass the adress to a test pointer
(*testspace_ptr) = vtss_state_2; // extra Test: pass the adress to a test pointer
vtss_state_2->a = 22; // extra Test: change content of test pointer
printf ("\r\n\ta=22? a=%d ", (*inst)->a); //
}
/******************************************************************************
main
*******************************************************************************/
int
main ()
{
// **The main structure:
vtss_state_t state_test;
state_test.a = 1234; // pass some test value
// **pointer to type of vtss_state_t
vtss_inst_t inst;
// **pass the adress of the structure!:
inst = &state_test;
// Test: add a value to a structure of the structure:
// (is the same like inst->)
state_test.u.x = 123;
// Test:
inst->chip_count = 888;
vtss_inst_get (inst);
// Test: I declared two global structure to test global access:
printf ("\ntestspace points to: %d", testspace);
printf ("\ntestspace ptr points to: %d", testspace_ptr);
// Test variable a:
printf ("\n1234? inst->a=%d", inst->a);
// Test: show value of structure in a strucure:
printf ("\n123? state_test.u.x=%d", state_test.u.x);
// Test: before & after address is changed:
printf ("\r\n * Before create(): Adress inst:%d", inst);
printf ("\r\n******* Before create():chip_count: %d", inst->chip_count);
vtss_inst_create (&inst);
printf ("\r\n\n * After create(): Adress of inst:%d", inst);
printf ("\r\n\t 22? a:%d", inst->a);
printf
("\r\n even the layer2 function in create() worked:\n port_count=1?: %d",
inst->port_count);
printf
("\r\n\n\n******* chip_count has changes correctly to 12121: %d\n\n\n",
inst->chip_count);
//extra tests with the global structures:
printf ("\ntestspace points to: %d", testspace);
printf ("\n testspace has the correct value a: %d", testspace->a);
printf ("\n testspace_ptr has the correct value a:: %d",
(*testspace_ptr)->a);
printf ("\n adress of pointer testspace_ptr points to?:%d", testspace_ptr);
// extra test:
vtss_inst_get (inst);
printf ("\n there is even access to a:%d", inst->a);
printf ("\nPE-Hello World");
return 0;
}
SECOND APPROACH: ARM CORTEX M3 in VSC:
I just want to change chip_count but the library function doesn't do it!
vtss_rc **vtss_inst_create**(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void))
{
vtss_state_t *vtss_state;
vtss_state = malloc(sizeof(*vtss_state));
vtss_state->chip_count = 777;
(*inst) = vtss_state;
I call it in the main: vtss_inst_create(&create, &inst);
( now I also deleted the third test parameter: I should change it here, but result is anyway same)
when I read chip_count out in the main :
sprintf(t2, "\r\n main: chipcount should be 777 %d ??", (int)(inst->chip_count));
print_uart(t2);
Then it prints:
482531848 !!!!!!
I copied this function:
void createtest2(vtss_inst_t *const inst)
{
vtss_state_t *vtss_state;
vtss_state = malloc(sizeof(*vtss_state));
vtss_state->chip_count = 888;
(*inst) = vtss_state;
}
it is called: createtest2(&inst);
sprintf(t2, "\r\n main: wie in create() chipcount 888?: %d", (int)(inst->chip_count));
print_uart(t2);
it prints 888 correctly i guess because it prints it ..
I copied it also with correct header:
vtss_rc vtss_inst_create3(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void))
{
vtss_state_t *vtss_state;
vtss_state = malloc(sizeof(*vtss_state));
vtss_state->chip_count = 222;
(*inst) = vtss_state;
(void)create;
(void)print;
return 1;
}
I call it: vtss_inst_create3(&create, &inst, spitest);
(ignore please the third parameter)
it prints out 222 correctly
How is that possible?
Or is this simply magic :)
Update: I checked it and it is shocking to me.
lol
it works in every kind of function but not with that one I want to do it xD
First I just copied the function but only one parameter. It worked.
Then I copied it with all parameters. It worked.
But the original library func. doesn't work.
In the past I changed the library function a little bit:
Ok in the past a tuned the library function by adding one little parameter as a little test.
In combination, that it is a library function in not located in the same file it may cause some trouble.
I will now change the header back to normal:
Normal:
vtss_rc vtss_inst_create(const vtss_inst_create_t *const create,
vtss_inst_t *const inst)
How i tuned it:
vtss_rc vtss_inst_create(const vtss_inst_create_t *const create, vtss_inst_t *const inst, void (*print)(void))
{
update: lol I rechanged the function it back to normal.
Hmmm....
Another file?
Then it must be because the function is in another file or what.
I checked inside the functions they point to the same addresses as outside (main).. I mean the value of inst.
Todo I print out now the addresses of chip_count
Update: OMG something is wrong with the compiler. I compared it as described with similar functions.
Same function different result of chip_count.
But another variabale e.g. "cookie" can be changed!
So one variable is change another not.
This was really confusing to me because I didn't expect it!
CMAKE:
Maybe there is an error which is suppressed by my CMAKE. So the compiler ignores the error. At runtime the error comes to light.
add_target_compile_flags(base PRIVATE "" "-Wno-unused-parameter" "-Wno-pedantic" "-Wno-format" "-Wno-sign-conversion" "-Wno-switch-default" "-Wno-conversion" "-Wno-unused-variable" "-Wno-undef" "-Wno-unused-but-set-variable" "-Wno-unused-function" "-Wno-implicit-function-declaration" "-Wno-uninitialized")
Update on 2019/11/19:
I search google and find a lib to do this(I can't remember which is it now), and it works fine.
Update on 2019/6/19:
My env is win10, the reason is this code is not work on win10?
Origin:
I use this code to just inject int foo() {return 0} to a target process. But It cause target process crash.
The entire vs solution is here: https://github.com/huhuang03/test/tree/master/win/InjectHelloWorld. Include the InjectMe and InjectByCode.
char hand_asm[100] = {0xC3}; // 0xc3 is the retn assembly
if (!WriteProcessMemory(h_target, targetFuncSpace, &hand_asm, CODE_SPACE_SIZE, NULL)) {
showError(L"Cna't write targetFuncSpace");
return EXIT_FAILURE;
}
InjectFuncParam param;
LPVOID injectFuncParamSpace = VirtualAllocEx(h_target, NULL, sizeof(param), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!injectFuncParamSpace) {
showError(L"Can't alloc injectFuncParamSpace");
return EXIT_FAILURE;
}
system("pause");
DWORD remoteThreadId = 0;
HANDLE h_remoteThread = CreateRemoteThread(h_target, NULL, 0, (LPTHREAD_START_ROUTINE)targetFuncSpace, injectFuncParamSpace, 0, &remoteThreadId);
if (!h_remoteThread) {
VirtualFreeEx(h_target, injectFuncParamSpace, 0, MEM_RELEASE);
VirtualFreeEx(h_target, targetFuncSpace, 0, MEM_RELEASE);
showError(L"Cant' create rmeote Thread");
return EXIT_FAILURE;
this cause InjectMe crash, I can't find a way to debug this.
By the way, I use ollydbg to set a breakpoint at targetFuncSpace, but the ollydbg says it's not code segment... Why, I had use the PAGE_EXECUTE_READWRITE to alloc the space.
One problem I see is you can't VirtualFree targetFuncSpace until the remote thread in the target process has finished executing.
Also WriteProcessMemory is copying CODE_SPACE_SIZE (4096) bytes from hand_asm which is only 100 bytes.
When I try to hook MGCopyAnswer, I get a crash. I'm trying this on a jailbroken iPhone 5s in iOS 8.3, arm64 binaries.
#import <substrate.h>
extern "C" CFTypeRef MGCopyAnswer(CFStringRef);
MSHook(CFTypeRef, MGCopyAnswer, CFStringRef key)
{
return _MGCopyAnswer(key);
}
%ctor
{
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
if ( appID && [appID isEqualToString:#"com.test.test"]) {
MSHookFunction(MGCopyAnswer, MSHake(MGCopyAnswer));
}
}
Makefile:
ARCHS = armv7 armv7s arm64
TARGET = iphone:latest:8.0
test2_FRAMEWORKS = UIKit
include theos/makefiles/common.mk
TWEAK_NAME = test2
test2_FILES = Tweak.xm
test2_LIBRARIES = MobileGestalt
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
install.exec "killall -9 SpringBoard"
Crash log:
Version: 1.44 (1.4)
Code Type: ARM-64 (Native)
Parent Process: launchd [1]
Date/Time: 2016-04-25 01:09:31.810 +0800
Launch Time: 2016-04-25 01:09:31.564 +0800
OS Version: iOS 8.3 (12F70)
Report Version: 105
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x000000000068fe68
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libMobileGestalt.dylib 0x0000000195af7e84 0x195af4000 + 16004
1 libMobileGestalt.dylib 0x0000000195af82bc MGGetBoolAnswer + 32
2 AppSupport 0x000000018b020594 __CPIsInternalDevice_block_invoke + 16
3 libdispatch.dylib 0x0000000196c99950 _dispatch_client_callout + 12
4 libdispatch.dylib 0x0000000196c9a828 dispatch_once_f + 92
5 AppSupport 0x000000018b02057c CPIsInternalDevice + 60
6 UIKit 0x0000000189b58750 ___UIApplicationUsesAlternateUI_block_invoke + 12
7 libdispatch.dylib 0x0000000196c99950 _dispatch_client_callout + 12
8 libdispatch.dylib 0x0000000196c9a828 dispatch_once_f + 92
9 UIKit 0x0000000189923750 UIApplicationInitialize + 1872
10 UIKit 0x0000000189922b1c UIApplicationMain + 320
MGCopyAnswer:
-> 0x193a7fe84 <+0>: .long 0x002d7c28 ; unknown opcode
0x193a7fe88 <+4>: .long 0x00000001 ; unknown opcode
0x193a7fe8c <+8>: stp x20, x19, [sp, #32]
0x193a7fe90 <+12>: stp x29, x30, [sp, #48]
0x193a7fe94 <+16>: add x29, sp, #48
0x193a7fe98 <+20>: sub sp, sp, #48
0x193a7fe9c <+24>: mov x19, x1
0x193a7fea0 <+28>: mov x22, x0
0x193a7fea4 <+32>: movz w0, #0
0x193a7fea8 <+36>: bl 0x193a7f564 ; ___lldb_unnamed_function54$$libMobileGestalt.dylib
0x193a7feac <+40>: orr w1, wzr, #0x1
0x193a7feb0 <+44>: mov x0, x22
0x193a7feb4 <+48>: bl 0x193a7f5fc ; ___lldb_unnamed_function56$$libMobileGestalt.dylib
0x193a7feb8 <+52>: mov x21, x0
0x193a7febc <+56>: movz w20, #0
0x193a7fec0 <+60>: cbz x21, 0x193a7fefc ; <+120>
0x193a7fec4 <+64>: ldr w20, [x21, #148]
0x193a7fec8 <+68>: mov x0, x21
orig_MGCopyAnswer
0x104234000: movz x1, #0
0x104234004: stp x24, x23, [sp, #-64]!
0x104234008: stp x22, x21, [sp, #16]
0x10423400c: ldr x16, #8
0x104234010: br x16
0x104234014: .long 0x93a7fe8c
0x104234018: .long 0x00000001 ; unknown opcode
What am I doing wrong?
You cannot hook MGCopyAnswer directly because it is too short.
When CydiaSubstrate hooks a C function, it sorts of overwrites an assembly version of goto your_function; at the beginning of the original function. This "goto" in ARM64 is 16 bytes in size, which means if the original function is too short (< 16 bytes), CydiaSubstrate can spill over and corrupt the neighboring functions.
This is exactly why the problem of MGCopyAnswer. The implementation of MGCopyAnswer is basically (on 9.3.2 arm64):
01 00 80 d2 movz x1, #0
01 00 00 14 b MGCopyAnswer_internal
which is just 8 bytes (< 16 bytes) in size. So CydiaSubstrate will corrupt the 8 bytes after the end of MGCopyAnswer.
Unfortunately, MGCopyAnswer_internal is right after MGCopyAnswer, and even worse this function and is called by MGGetBoolAnswer as well. Since MGCopyAnswer_internal is corrupt, you get an EXC_BAD_INSTRUCTION crash inside libMobileGestalt.
A good news for MGCopyAnswer is that, you could just hook MGCopyAnswer_internal! This has an additional benefit that many related functions like MGGetBoolAnswer, MGCopyAnswerWithError, MGCopyMultipleAnswers etc. can respond to your change as well. The bad thing is that MGCopyAnswer_internal is completely internal, and there is no symbols pointing to it. We could rely on the fact that MGCopyAnswer_internal is exactly 8 bytes after MGCopyAnswer on ARM64, and develop this ugly hack:
static CFPropertyListRef (*orig_MGCopyAnswer_internal)(CFStringRef prop, uint32_t* outTypeCode);
CFPropertyListRef new_MGCopyAnswer_internal(CFStringRef prop, uint32_t* outTypeCode) {
return orig_MGCopyAnswer_internal(prop, outTypeCode);
}
extern "C" MGCopyAnswer(CFStringRef prop);
static CFPropertyListRef (*orig_MGCopyAnswer)(CFStringRef prop);
CFPropertyListRef new_MGCopyAnswer(CFStringRef prop) {
return orig_MGCopyAnswer(prop);
}
%ctor {
uint8_t MGCopyAnswer_arm64_impl[8] = {0x01, 0x00, 0x80, 0xd2, 0x01, 0x00, 0x00, 0x14};
const uint8_t* MGCopyAnswer_ptr = (const uint8_t*) MGCopyAnswer;
if (memcmp(MGCopyAnswer_ptr, MGCopyAnswer_arm64_impl, 8) == 0) {
MSHookFunction(MGCopyAnswer_ptr + 8, (void*)new_MGCopyAnswer_internal, (void**)&orig_MGCopyAnswer_internal);
} else {
MSHookFunction(MGCopyAnswer_ptr, (void*)new_MGCopyAnswer, (void**)&orig_MGCopyAnswer);
}
}
(This only checks for arm64 on 9.3.2. Other platforms may crash in different ways, and have different assembly code, so you may need to add additional conditions into enter the hook-MGCopyAnswer_internal branch. YMMV!)
Try this code:
#import <substrate.h>
static CFTypeRef (*orig_MGCopyAnswer)(CFStringRef str);
CFTypeRef new_MGCopyAnswer(CFStringRef str)
{
return orig_MGCopyAnswer(str);
}
%ctor
{
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
if ( appID && [appID isEqualToString:#"com.test.test"]) {
void * MGCopyAnswerFn = MSFindSymbol(NULL, "_MGCopyAnswer");
MSHookFunction(MGCopyAnswerFn, (void *) new_MGCopyAnswer, (void **)& orig_MGCopyAnswer);
}
}
Fedora 19 {though I doubt that's relevant]
If I invoke the info command for a topic that doesn't actually have an info node, but does have a man page, info will apparently create a node named (*manpages*)<topic> from the man page -- on the fly.
I can neither find this feature documented anywhere, nor (obviously) a description of how it's done.
Can anyone point me to some documentation about this?
I have checked the GNU standalone info manual and GNU Info manual and found nothing but a small sign that this feature exists. In the description of option --all we can read this:
--all
-a
Find all files matching the given menu-item (a file or node name).
Three usage patterns are supported, as follows.
First, if --all is used together with --where, info prints the names
of all matching files found on standard output (including *manpages*
if relevant) and exits.
So I'm afraid the only documentation is the source code. In info.c you can find the following function. Look for the "Fall back to loading man page." comment.
/* Get the initial Info file, either by following menus from "(dir)Top",
or what the user specifed with values in filename. */
static char *
get_initial_file (char *filename, int *argc, char ***argv, char **error)
{
char *initial_file = 0; /* First file loaded by Info. */
REFERENCE *entry;
/* If there are any more arguments, the initial file is the
dir entry given by the first one. */
if (!filename && (*argv)[0])
{
/* If they say info -O info, we want to show them the invocation node
for standalone info; there's nothing useful in info.texi. */
if (goto_invocation_p && (*argv)[0]
&& mbscasecmp ((*argv)[0], "info") == 0)
(*argv)[0] = "info-stnd";
entry = lookup_dir_entry ((*argv)[0], 0);
if (entry)
{
initial_file = info_find_fullpath (entry->filename, 0);
if (initial_file)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
/* Store full path, so that we find the already loaded file in
info_find_file, and show the full path if --where is used. */
entry->filename = initial_file;
add_pointer_to_array (info_copy_reference (entry),
ref_index, ref_list, ref_slots, 2);
return initial_file;
}
}
}
/* User used "--file". */
if (filename)
{
initial_file = info_find_fullpath (filename, 0);
if (!initial_file)
{
if (filesys_error_number)
*error = filesys_error_string (filename, filesys_error_number);
}
else
return initial_file;
}
/* File name lookup. */
if (!filename && (*argv)[0])
{
/* Try finding a file with this name, in case
it exists, but wasn't listed in dir. */
initial_file = info_find_fullpath ((*argv)[0], 0);
if (initial_file)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
return initial_file;
}
else
asprintf (error, _("No menu item `%s' in node `%s'."),
(*argv)[0], "(dir)Top");
}
/* Fall back to loading man page. */
if (filename || (*argv)[0])
{
NODE *man_node;
debug (3, ("falling back to manpage node"));
man_node = get_manpage_node (filename ? filename : (*argv)[0]);
if (man_node)
{
add_pointer_to_array
(info_new_reference (MANPAGE_FILE_BUFFER_NAME,
filename ? filename : (*argv)[0]),
ref_index, ref_list, ref_slots, 2);
initial_file = MANPAGE_FILE_BUFFER_NAME;
return initial_file;
}
}
/* Inexact dir lookup. */
if (!filename && (*argv)[0])
{
entry = lookup_dir_entry ((*argv)[0], 1);
if (entry)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
/* Clear error message. */
free (*error);
*error = 0;
initial_file = info_find_fullpath (entry->filename, 0);
/* Store full path, so that we find the already loaded file in
info_find_file, and show the full path if --where is used. */
entry->filename = initial_file;
add_pointer_to_array (info_copy_reference (entry),
ref_index, ref_list, ref_slots, 2);
return initial_file;
}
}
/* Otherwise, we want the dir node. The only node to be displayed
or output will be "Top". */
return 0;
}
man.h contains the definition of MANPAGE_FILE_BUFFER_NAME:
#define MANPAGE_FILE_BUFFER_NAME "*manpages*"
info is a command like man, you can test the command : info make but info Read documentation in Info format.