Problems with printf() on AVR in C with floating-point - c

I've been getting back into C, and I've been working on an 'academic' exercise to sharpen some old skills again. My project revolves around the rather simple process of generating sines. I started out just coding for x86 on the command line (Fedora Core 20) and everything worked fine. I then migrated the code to AVR, and I started learning a lot. For example, how to set up the UART as stdout. However, as sines are floating-point, I ran into problems using printf and sprintf.
The program generates 30 sines, and then prints the values to terminal. The text "Sine: " prints properly, but then I get question marks for the float. Replacing the variable with a constant had no effect.
The first thing I was suggested was if I had remembered the linker option for full floating point support - indeed I had forgotten. Again, adding this into my makefile had no effect.
I'm not sure of the policy here of copying and pasting code: should I paste it and my makefile here for inspection?
EDIT: Sorry for the long delay. I've done some more reading, including what the first answer links to. I had already read that reference before (the GNU one) and I have included the link in my Makefile, which is why I'm so confused. Here's my Makefile in all its glory:
P = sines
OBJ = sines.o
PROGRAMMER = buspirate
PORT = /dev/ttyUSB0
MCU_TARGET = atmega328p
AVRDUDE_TARGET = atmega328p
HZ = 16000000
DEFS =
LIBS = -lprintf_flt -lm
CC = avr-gcc
override CFLAGS = -g -DF_CPU=$(HZ) -Wall -O1 -mmcu=$(MCU_TARGET) $(DEFS)
override LDFLAGS= -Wl,-Map,$(P).map -u,vfprintf
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
all: $(P).elf lst text
$(P).elf: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^ $(LIBS)
clean:
rm -rf *.hex *.bin *.map *~ sine*.csv *.o $(P).elf *.lst
lst: $(P).lst
%.lst: %.elf
$(OBJDUMP) -h -S $< > $#
text: hex bin
hex: $(P).hex
bin: $(P).bin
%.hex: %.elf
$(OBJCOPY) -j .text -j .data -O ihex $< $#
%.bin: %.elf
$(OBJCOPY) -j .text -j .data -O binary $< $#
install: $(P).hex
avrdude -p $(AVRDUDE_TARGET) -c $(PROGRAMMER) -P $(PORT) -v -U flash:w:$(P).hex
What I'm concerned about is that perhaps the linker arguments aren't in the correct order? From what I can tell, they are but...
I'm fairly sure my code itself is fine. If wanted, I can post it here as well.
Also, thanks for transferring my question over here. Didn't quite understand the difference between the two!
Here's the source code. It's being run on an ATmega328P. This current version is printing a constant as a debug, instead of the result from sinescalc(), even though I know that function is working (at least, it should be, I'm pretty sure I checked using avr-gdb at one point -- it definitely works on the command line, and also on an MSP430).
#include <avr/io.h>
//#include <util/delay.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
static int uart_putchar(char c, FILE *stream);
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
static int uart_putchar(char c, FILE *stream) {
if (c == '\n')
uart_putchar('\r', stream);
while(!(UCSR0A & (1<<UDRE0)));
UDR0 = c;
return 0;
}
void sinescalc(double *sinptr, int cycles, int size) {
double pi = acos(-1);
double step = ((pi * (cycles*2))/ size);
float temp;
double z = step;
int y;
for(y = 0; y<= size; y++) {
temp = sin(z); // calculate the current sine
*sinptr = (double)temp; // pass it into the array from main()
z += step; // add the step value to prepare for next sine
sinptr++; // should move the pointer by correct size of variable
}
}
int main(void) {
unsigned long _fosc = 16000000;
unsigned int _baud = 19200;
unsigned long _myubrr = _fosc/16/_baud-1;
unsigned int array_size = 256;
UBRR0L = (unsigned char)_myubrr;
UCSR0B = (1<<RXEN0)|(1<<TXEN0); //enable receiver and transmitter
stdout = &mystdout;
double sines[array_size];
double *sinepointer = sines; // set sinepointer to first element of sines array
sinescalc(sinepointer, 2, array_size); // calculate two cycles of sine, with 255 data points
int y;
//char msg[6] = ("Sine: ");
char output[40];
for(y = 0; y <= array_size; y++) {
sprintf(output, "Sine:\t %.6f", 1.354462);
printf(output);
printf("\n");
}
return 0;
}

This page states that to get the full printf() implementation, that supports printing floats, you should use:
-Wl,-u,vfprintf -lprintf_flt -lm

So I solved it. For those wondering, it really DOES come down to the order of arguments after -Wl. Here's the final makefile that seems to be working (so far, in a simulator):
P = sines
OBJ = sines.o
PROGRAMMER = buspirate
PORT = /dev/ttyUSB0
MCU_TARGET = atmega328p
AVRDUDE_TARGET = atmega328p
HZ = 16000000
DEFS =
LIBS = -lprintf_flt -lm
CC = avr-gcc
override CFLAGS = -g -DF_CPU=$(HZ) -Wall -O2 -mmcu=$(MCU_TARGET) $(DEFS)
override LDFLAGS= -Wl,-u,vfprintf,-Map,$(P).map
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
all: $(P).elf lst text
$(P).elf: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $# $^
I thought that I had tried that order before, and it threw an error. What I think I did wrong before was omit the comma after vfprintf. Also, the suggestion from another website of putting the -u,vfprintf AFTER the LDFLAGS (and after the -o section) was also clearly incorrect.
Edit from 2018: Recent versions of GCC seem to require the libraries to come as the last argument, no matter what other arguments there are. If you are having problems compiling (I'm not sure what versions of gcc/avr-gcc are being distributed by the various distros) and you are getting a bunch of "implicit declaration of x function" errors, it's because your library arguments are in the wrong spot. This just bit me recently, and I haven't seen any information on why this change happened, or which versions are affected.
Why it has to go in exactly that spot, I don't know yet. Perhaps someone can shed some light on it? I can't believe the answer was as inane as this. I just started moving things around until I hit on this. However, after looking at the GNU documentation again, they show the -Wl, directly before the -u,vfprintf. What threw me off was the presence of the -Map,$(P).map, which I thought had to go directly after -Wl as well. It seems like the -lprintf_flt -lm can come afterwards. I know that -lm is the option to link in the GNU math libraries, which is important for floating point math, obviously. I also understand what the other two options do (link in the correct version of the stream functions with floating-point support compiled in). But as I said before, perhaps someone can point me (and others) to a resource regarding hierarchy of gcc linker options? This problem has been dogging me for a week, and nobody was able to just point out that the -Map could come afterwards, but still needed a comma in between. I might try flipping around the -Map and the -u options, still with their commas, to see if it's THAT hierarchically important...
It isn't. Just changed it to -Wl,-Map,sines.map,-u,vfprintf and t still works with no problem. So the answer had to do with commas, which I take it means that all linker options need to be attached with commas? Why doesn't -lm need to be there as well? I'm a littl baffled, but relieved that it's working. Now I just need to try it on the hardware, though I'm pretty sure it'll work just fine.
Thanks everyone, for your help! This has been a great introduction to Stack Overflow (and all the Stacks) and I really hope that I can learn a lot, and contribute to this community. I've been carefully re-reading all the articles about asking good questions, and how the reputation and voting system works, so hopefully I get it right and I don't piss anyone off :)
Cheers!!

You can convert float (can be double, but in avr-gcc till v9 is implemented the same as float) to string prior printing it, and you can go without linker alterations by ie:
float in_volt = temp/1024.0*5.0;
char in_volt_string[8];
dtostrf(in_volt, 6, 4, in_volt_string);
That solved it for me in AVR Studio 4 with Atmega32. Then was put to lcd and tera term without problems.

Related

Receiving *** caught segfault ***, 'memory not mapped' and mangled results after passing values from R to C

I'm trying to implement a simple integration of R with C. Initially it's simple: I want to pass values from R to a C function built into a .o shared library via .C or .Call function. The C function should simply print the values passed in via printf.
Here's my .Call method:
.Call("test", as.integer(5), as.character("A"), as.character("string_test"))
And my C code:
#include <stdio.h>
void test(int integer, char character, char **str) {
printf("Integer: %i\nChar: %c\nString: %s\n", integer, character, *str);
}
But when I call the C function from R via console (RStudio crashes) with gdb enabled, I receive:
Integer: 1466480376
Char: �
Float: -100407552.000000
String:
***caught segfault ***
address 0x20000090, cause 'memory not mapped'
Traceback:
1: .Call("test", as.integer(5), as.character("A"), as.character("string_test"))
As if it were not enough, as we can see the values passed in are printed very strangely.
Details of what I did, step by step:
I built the .o shared library with gcc:
gcc -shared -o func_teste.o -fPIC func_teste.c
And prepared it for dynamic loading in R environment:
$ R CMD SHLIB func_teste.o
gcc -m64 -I/usr/include/R -DNDEBUG -I/usr/local/include -fpic -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -c func_teste.c -o func_teste.o
gcc -m64 -shared -L/usr/lib64/R/lib -Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -o func_teste.so func_teste.o -L/usr/lib64/R/lib -lR
And finally, inside R console, i ran:
>dyn.load('func_teste.o')
>.Call("test", as.integer(5), as.character("A"), as.character("string_test"))
Does anyone have idea why this is happening?
R offers two main functions for interfacing from C code (and hence C++ code, or any other language able to use a C interface):
- .C() is the older interface using int*, double*, ... and alike
- .Call() is the newer, more powerful interface using SEXP objects
Now, .Call() looks more complicated but it is so much more powerful as well as safer. There is near universal consensus that .C() should no longer be used (see various discussions on the r-devel list and other places).
The main downside with .Call() is that you need to learn how to pack and unpack your values. Or ... you cheat and let Rcpp do it for you. So with that, here is one-line solution of the OP's example:
> library(Rcpp)
> cppFunction("void mytest(int i, char c, std::string str) { printf(\"Integer: %i Char: %c String: %s\\n\", i, c, str.c_str()); }")
> mytest(42L, 'Q', "a boat")
Integer: 42 Char: Q String: a boat
>
I made the char* a string. Note that cppFunction() requires escaping of strings, you may want to look into sourceCpp() and packages for real work. The Rcpp documentation has details.
Don't as.character on "string_test".
Read more here: http://mazamascience.com/WorkingWithData/?p=1067

Undefined symbols while linking OSMalloc.h using clang on OS X

Hello and thank you for your assistance.
I'm attempting to create a simple 'hello world' using only low-level OS X kernel calls to allocate memory and write out to stdout. Why? I'm finishing up chapter 8 of 2nd Edition K&R which is focused on writing standard file library from scratch. It is, of course, totally out of date but the concept of the chapter remains. Anyway, I cannot seem to figure out how to properly link to get everything to work out and are thus earning myself lots of nice undefined symbol errors.
I have parsed through many other questions causing the same error, but have not found any that address how to link in the kernel library I'm attempting to use. The crazy long path in the third #include was needed to just get the thing to compile even prior to the link errors.
The Code:
#include <fcntl.h>
#include <unistd.h> // equivalent to (K&R) #include "syscalls.h"
#include </Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/libkern/OSMalloc.h> // for low-level memory allocation
#define MINSTDIOTAG "com.apple.minstdio" // Used by OSMalloc from <libkern/OSMalloc.h>
#define PAGE_SIZE_64K (64 * 1024) // Page size to allocate
int main(void) {
char *base = NULL; // Memory buffer
char *ptr = base; // Location in buffer
// Create tag
OSMallocTag mytag = OSMalloc_Tagalloc(MINSTDIOTAG, OSMT_DEFAULT);
// Attempt to allocate PAGE_SIZE_64K of memory
if ((base = (char *)OSMalloc(PAGE_SIZE_64K, mytag)) == NULL)
return 1;
ptr = base;
// Stuff the buffer with stuff
*ptr++ = 'f';
*ptr++ = 'o';
*ptr++ = 'o';
*ptr++ = '\n';
*ptr = '\0';
// Write it out to stdout
(void)write(STDOUT_FILENO, base, (size_t)(ptr - base));
// Free allocated memory
OSFree(base, PAGE_SIZE_64K, mytag);
// Get out of Dodge City, Kansas
return 0;
}
Makefile:
BIN = ../../bin
ODIR = obj
CC = cc
CFLAGS = -std=c99 -Wall -g -I.
_OBJ = minstdio3.o
_BIN = minstdio3
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
.PHONY: all clean
all: $(_BIN)
clean:
rm -rv $(ODIR) $(_BIN)
minstdio3: $(ODIR)/minstdio3.o
$(CC) $(CFLAGS) $^ -o $#
cp -v $# $(BIN)/$#
$(ODIR)/%.o: %.c $(DEPS)
mkdir -pv $(ODIR)
$(CC) $(CFLAGS) -c -o $# $<
Errors Received:
Todds-MBP-2:cbasics todddecker$ make
mkdir -pv obj
cc -std=c99 -Wall -g -I. -c -o obj/minstdio3.o minstdio3.c
cc -std=c99 -Wall -g -I. obj/minstdio3.o -o minstdio3
Undefined symbols for architecture x86_64:
"_OSFree", referenced from:
_main in minstdio3.o
"_OSMalloc", referenced from:
_main in minstdio3.o
"_OSMalloc_Tagalloc", referenced from:
_main in minstdio3.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [minstdio3] Error 1
-- EDITS --
"Why are you not using the sbrk system call if you want low-level? This would match your use of write and shouldn't give any linking issues." (from CRD)
Using 'sbrk' (and it's cousin 'brk') was the original path I was headed down; however, the man page for 'sbrk' states, "The brk and sbrk functions are historical curiosities left over from earlier days before the advent of virtual memory management." This statement put me on a path toward trying to discover its replacement. 'malloc' is, of course, the correct and normal utility for memory allocation. However, K&R Chapter 8 is all about writing your own from base OS system calls. So, the base call I was able to find for OS X Darwin is 'OSMalloc' which I'm trying to use.
OSMalloc is only available for writing the kernel itself (kernel extensions, device drivers). User programs have to use sbrk.

Segmentation fault when using CyaSSL Keygen

I'm trying to get the Keygen function of CyaSSL to work using the example in section 7.7 from here: http://www.yassl.com/yaSSL/Docs-cyassl-manual-7-keys-and-certificates.html
I'm using CyaSSL 3.2.0 with the --enable-keygen option, but couldn't get it working with 3.1.0 either.
This is the code:
#include <stdio.h>
#include <cyassl/ctaocrypt/asn.h>
#include <cyassl/ctaocrypt/rsa.h>
int main() {
RsaKey genKey;
RNG rng;
int ret;
printf("%d\n",InitRng(&rng));
printf("%d\n",InitRsaKey(&genKey, 0));
ret = MakeRsaKey(&genKey, 1024, 65537, &rng);
printf("ret: %d\n",ret);
return 0;
}
I get a segmentation fault in the line with InitRsaKey, presumably because of an invalid write or something.
Anyone got an idea where my issue may be? Any help is appreciated
Good morning, please do not forget to include options.h header. This will ensure that you get the proper configuration settings in your project for example if you configure CyaSSL with --enable-keygen then view cyassl/options.h you will see the line #undef CYASSL_KEY_GEN followed by #define CYASSL_KEY_GEN. Also in your makefile do not forget to include the cyassl library. This can be accomplished using -lcyassl in your build line. See code below for reference:
#include <stdio.h>
#include <cyassl/options.h> //pull in the define for CYASSL_KEY_GEN
#include <cyassl/ctaocrypt/asn.h>
#include <cyassl/ctaocrypt/rsa.h>
int main() {
RsaKey genKey;
RNG rng;
int ret;
printf("%d\n",InitRng(&rng));
printf("%d\n",InitRsaKey(&genKey, 0));
ret = MakeRsaKey(&genKey, 1024, 65537, &rng);
printf("ret: %d\n",ret);
return 0;
}
Makefile:
CC=gcc #you can use clang or other instead of gcc
CFLAGS=-Wall
LIBS=-lpthread -lcyassl #must have -lcyassl in makefile
all: run
#NOTE: arrows denote a hard tab, replace them with hard tab in makefile
run: test.o
→→→→$(CC) -o $# $(LIBS) $^ $(CFLAGS) #put $(LIBS) into build command
.PHONY: clean all
clean:
→→→→rm -f *.o test.o run

undefined reference to function called 'full_adder(BIT A, BIT B, BIT Ci, BIT *Co)'

I am a CS student learning how to program in C. I have 3 files and a Makefile. I have
logic.c logic.h main.c
the top of logic.c is:
#include "logic.h"
//use this function in 4 bit adder
//BIT full_adder(BIT A, BIT B, BIT Ci, BIT *Co);
int adder(int O1, int O2, BIT carryIn, BIT *carryOut){
printf("in adder");
return 0x0;
}
BIT full_adder(BIT A, BIT B, BIT Ci, BIT *Co){
BIT S = FALSE;
//implementation of if else's based on Truth Table <br>
if((A==FALSE)&&(B==FALSE)&&(Ci==FALSE)){
S=FALSE;
*Co = FALSE;
} <br>
return S;
}
the top of main.c is:
#include "logic.h"
BIT full_adder(BIT A, BIT B, BIT Ci, BIT *Co);
in main.c I have my main function, at the bottom of main function I have :
assert((full_adder(FALSE,FALSE,FALSE,&Co)==FALSE) && (Co==FALSE));
adder(FALSE,FALSE,FALSE,&Co2);
return 0;
I am getting the error:
main.o: In function 'main': main.c:158: undefined referenceto 'full_adder'
collect2: ld returned 1 exit status <br>
make: *** [pa3] Error 1 <br>
./bscript: line 3: pa3: command not found
This is homework, but I have spent a good deal of time on this problem and am looking for some help. I have the correct return type for adder, full_adder, and I declare the function in the top of both logic.c and main.c.
The questions that did not answer my question were:
Undefined Reference to a function
"Undefined reference to function" error
Any help is appreciated.
EDIT:
logic.c is getting compiled and it does create a logic.o file.
EDIT: my prof says that I cannot edit logic.h
EDIT: makefile:
# Makefile template for CS 270 (specialized for PA3)
# List of files
C_SRCS = main.c logic.c
C_OBJS = main.o logic.o
C_HEADERS = logic.h
OBJS = ${C_OBJS}
EXE = pa3
# Compiler and loader commands and flags
GCC = gcc
GCC_FLAGS = -g -std=c99 -Wall -O0 -c
LD_FLAGS = -g -std=c99 -Wall -O0
# Target is the executable
pa3 : $(OBJS)
#echo "Linking all object modules ..."
$(GCC) $(LD_FLAGS) $(OBJS) -o $(EXE)
#echo ""
# Recompile C objects if headers change
${C_OBJS}: ${C_HEADERS}
# Compile .c files to .o files
.c.o:
#echo "Compiling each C source file separately ..."
$(GCC) $(GCC_FLAGS) $<
#echo ""
# Clean up the directory
clean:
#echo "Cleaning up project directory ..."
rm -f *.o $(EXE) core a.out
#echo ""
EDIT: I compile my code with the script:
#!/usr/bin/sh
make
pa3
First of all, separate definitions from declarations:
logic.h:
#ifndef _LOGIC_H_
#define _LOGIC_H_
/* Define BIT as before */
int adder(int O1, int O2, BIT carryIn, BIT *carryOut);
BIT full_adder(BIT A, BIT B, BIT Ci, BIT *Co);
#endif
logic.c:
#include <stdio.h>
#include "logic.h"
int adder(int O1, int O2, BIT carryIn, BIT *carryOut){
printf("in adder");
return 0x0;
}
BIT full_adder(BIT A, BIT B, BIT Ci, BIT *Co){
BIT S = FALSE;
//implementation of if else's based on Truth Table
if((A==FALSE)&&(B==FALSE)&&(Ci==FALSE)){
S=FALSE;
*Co = FALSE;
}
return S;
}
main.c:
#include "logic.h"
int main(int argc, char* argv[]) {
/* Do your stuff */
return 0;
}
Now things are where they should be and your logic.h supplies the needed information to the compiler.
Your makefile should not need any fancy stuff with only these three files.
This needs to be done via the makefile:
gcc -c -o main.o main.c
gcc -c -o logic.o logic.c
gcc -o main main.o logic.o
(The logical problems that you have in your full_adder are left for your to solve...)
I have seen linkers not-find/find functions depending on the order of the object files.
In your makefile you may want to try changing the order:
C_OBJS = logic.o main.o
I declare the function in the top of both logic.c and main.c.
The function prototype
BIT full_adder(BIT A, BIT B, BIT Ci, BIT *Co);
should only occur once, in logic.h.
The error you are getting is from the linker.
You may want to just try and compile/link it outside the Makefile:
gcc -c logic.c
gcc -o main main.c logic.o

Getting weird crashes in mixed fortran/C program

I am trying to replace some graphics code in a set of fortran programs (not my own code). I got one of the simpler ones ('psvdraw') to work just fine, replacing the fortran postscript-generating code with C calls that call the Cairo library (graphic_output.c). I have been able to successfully work out the cross-language calls without too much trouble.
However, when trying to get the second, larger program ('pssect') to work, calling the same C code, I get Segmentation Faults, or in some cases, the program flow goes back to a fortran routine 'error' (I do not call this, or any fortran routine in my C code).
In trying to diagnose this problem, I linked in a bunch of the fortran code from pssect into psvdraw ('biglib.f'), and got the same errors. Note that none of this added code is actually called! Also the errors occur right at the first call from fortan into the c code. So: psvdraw with biglib.f linked in fails, but psvdraw without it succeeds.
Here are relevant bits of the makefile:
Makefile
COMP77 = gfortran
FFLAGS = -C -v -pedantic -Wunused -fno-underscoring
CC = gcc-4
CFLAGS = -v -pedantic -Wunused
CAIRO_INCLUDE = /sw/include/cairo
CAIRO_LIB = /sw/lib
# PSVDRAW Make setup that works:
psvdraw: psvdraw.o graphic_output.o tlib.o pscom.o
$(COMP77) $(FFLAGS) $#.o graphic_output.o tlib.o pscom.o -L$(CAIRO_LIB) -lcairo -o $#
# PSVDRAW Make setup with errors:
#psvdraw: psvdraw.o graphic_output.o tlib.o pscom.o biglib.o
# $(COMP77) $(FFLAGS) $#.o graphic_output.o pscom.o tlib.o biglib.o -L$(CAIRO_LIB) -lcairo -o $#
pssect: pssect.o graphic_output.o pscom.o tlib.o biglib.o
$(COMP77) $(FFLAGS) $#.o graphic_output.o pscom.o tlib.o biglib.o -L$(CAIRO_LIB) -lcairo -o $#
pssect.o: pssect.f
$(COMP77) $(FFLAGS) -c pssect.f
psvdraw.o: psvdraw.f
$(COMP77) $(FFLAGS) -c psvdraw.f
pscom.o: pscom.f
$(COMP77) $(FFLAGS) -c pscom.f
tlib.o: tlib.f
$(COMP77) $(FFLAGS) -c tlib.f
biglib.o: biglib.f
$(COMP77) $(FFLAGS) -c biglib.f
graphic_output.o: graphic_output.c
$(CC) $(CFLAGS) $(INCL) -c -I$(CAIRO_INCLUDE) graphic_output.c
.c.o:
$(CC) $(CFLAGS) $(INCL) -c $<
.f.o:
$(FC) $(FFLAGS) $(INCL) -c $<
Here is the offending fortran code: Note that the problem occurs right at the beginning of the program:
Beginning of pssect.f:
PROGRAM PSSECT
implicit none
include 'perplex_parameters.h'
integer jop0, ier99
logical vertex, output, first
character*100 fname, yes*1
integer iop0
logical debug
common / basic /iop0, debug
integer isec,icopt,ifull,imsg,io3p
common/ cst103 /isec,icopt,ifull,imsg,io3p
c----------------------------------------------------------------------
c Look for the "debug_yes" file to turn on debugging messages
PRINT *,'Pre-PSOPEN1'
call psopen ('plot2')
PRINT *,'Post-PSOPEN1'
And here is part of the c code that gets called and produces a fault:
Part of graphic_output.c:
char dmh_debug = 0;
#define DEBUGPRINT(x) if (dmh_debug) {printf x;};
void psopen(char *fname, int fnamelen) {
printf("Debug opened\n");
char *outFileName;
char outputType[255];
char pageWidthString[255];
char pageHeightString[255];
/* Set debug status based upon presence of file named 'debug_yes' in directory */
FILE *debugFile = fopen("debug_yes", "r");
if (debugFile == NULL) {
dmh_debug = 0;
} else {
dmh_debug = 1;
}
fclose(debugFile);
dmh_debug = 1;
DEBUGPRINT(("Debug closed\n"));
fname[fnamelen]='\0';
fname = trim(fname);
outFileName = malloc((strlen(fname) + 50) * sizeof(char));
strcpy(outFileName, fname);
DEBUGPRINT(("Found file name:%s of length: %lu\n", fname, strlen(fname)));
[...]
Results of running the program
pnr-rethington:source dave$ ./pssect
Pre-PSOPEN1
Debug opened
Segmentation fault
If linking in unused code triggers the problem, that would tend to indicate that somewhere (either in the Fortran code or the C code) you're overwriting memory that you shouldn't. Try running the compiled program under Valgrind - it should help pinpoint where this is happening.
I'd still suspect that you have a problem with ones of the calls between Fortran and C, resulting in an inconsistency. How are you making the calls? I think that the most reliable way is to use the ISO C Binding to specify to Fortran how the C routines should be called.
Or, you could consider a graphics package that has a Fortran interface or binding. Then you wouldn't have to work on an interface, since Fortran calls or a Fortran interface would already exist. I've been using DISLIN. Another possibility is PLplot.
With the current approach, I suggest examining the arguments at the entry to the C routines, to make sure that they are correct.
You are calling psopen from Fortran with a single argument, and the C routine expects two arguments. Maybe your Fortran compiler adds the length of the character string as a trailing argument after each string. Or maybe you got lucky on your first attempt and it just happened to work with a "random" value that the C routine found on the stack. In the second case, in another situation, a peculiar crash is likely. At best, this is a non-portable way to interface Fortran and C. You could try adding an integer length to your Fortran call and see what happens. Or print or use the debugger to see the value of the second argument at entry to the C-routine.
The ISO C Binding works much better. It is supported by numerous compilers (e.g., gfortran >= 4.3, ifort) and provides a defined and portable way of connecting Fortran and C. You specify an "interface" in your Fortran code so that the Fortran compiler generate the correct instructions for the C-call.
Still, it might be easier to use a plotting package that already provides a Fortran interface.
I notice from your make file that you are using gfortran. The combination of gfortran (>=4.3) & gcc supports the ISO C Binding. (Part of Fortran 2003.) If you include the interface example and call psopen with two arguments, it should work. The interface goes into the declaration of the Fortran program and is the Fortran description of the C routine psopen. Not guaranteed since I haven't tested it... Strings are a special case -- you match a scaler string in the Fortran program to a string array in the interface, because the C argument is an array of chars, or a pointer to chars.
interface To_psopen
subroutine psopen ( fname, fnamelen ) bind (C, name="psopen")
use iso_c_binding
implicit none
character (kind=c_char, len=1), dimension (100), intent (inout) :: fname
integer (c_int), value, intent (in) :: fnamelen
end subroutine psopen
end interface To_psopen
Running under the debugger (gdb) should tell you where the segfault happens. Compile all code with -O0 -g to get accurate information.

Resources