Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2012-06

header translation question, 64 bit problem

11 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bernd

Hi,

I am translating parts of the libpurple headers. Now I seem to have a problem with this:

typedef enum
{
PURPLE_PLUGIN_UNKNOWN = -1, /**< Unknown type. */ PURPLE_PLUGIN_STANDARD = 0, /**< Standard plugin. */ PURPLE_PLUGIN_LOADER, /**< Loader plugin. */ PURPLE_PLUGIN_PROTOCOL /**< Protocol plugin. */

} PurplePluginType;
typedef int PurplePluginPriority;
struct _PurplePluginInfo
{
	unsigned int magic;
	unsigned int major_version;
	unsigned int minor_version;
	PurplePluginType type;               // <-- maybe this is the problem?
	char *ui_requirement;
	unsigned long flags;
	GList *dependencies;
	PurplePluginPriority priority;
	char *id;
	char *name;
	char *version;
	char *summary;
	char *description;
	char *author;
	char *homepage;
	/**
	 * If a plugin defines a 'load' function, and it returns FALSE,
	 * then the plugin will not be loaded.
	 */
	gboolean (*load)(PurplePlugin *plugin);
	gboolean (*unload)(PurplePlugin *plugin);
	void (*destroy)(PurplePlugin *plugin);

void *ui_info; /**< Used only by UI-specific plugins to build a preference screen with a custom UI */

	void *extra_info;
	PurplePluginUiInfo *prefs_info; /**< Used by any plugin to display

preferences. If #ui_info has been specified, this will be ignored. */

	/**
	 * This callback has a different use depending on whether this
	 * plugin type is PURPLE_PLUGIN_STANDARD or PURPLE_PLUGIN_PROTOCOL.
	 *
	 * If PURPLE_PLUGIN_STANDARD then the list of actions will show up
	 * in the Tools menu, under a submenu with the name of the plugin.
	 * context will be NULL.
	 *
	 * If PURPLE_PLUGIN_PROTOCOL then the list of actions will show up
	 * in the Accounts menu, under a submenu with the name of the
	 * account.  context will be set to the PurpleConnection for that
	 * account.  This callback will only be called for online accounts.
	 */
	GList *(*actions)(PurplePlugin *plugin, gpointer context);
	void (*_purple_reserved1)(void);
	void (*_purple_reserved2)(void);
	void (*_purple_reserved3)(void);
	void (*_purple_reserved4)(void);
};

I have translated it as follows:

{$calling cdecl}
type
  TPurplePluginType = (
    PURPLE_PLUGIN_UNKNOWN  := -1,  // Unknown type.
    PURPLE_PLUGIN_STANDARD := 0,   // Standard plugin.
    PURPLE_PLUGIN_LOADER,          // Loader plugin.
    PURPLE_PLUGIN_PROTOCOL         // Protocol plugin.
  );
  TPurplePluginPriority = Integer;
  TPurplePluginInfo = packed record
    magic: Integer;
    major_version: Integer;
    minor_version: Integer;
    plugintype: TPurplePluginType;
    ui_requirement: PChar;
    flags: LongInt;
    dependencies: PGList;
    priority: TPurplePluginPriority;
    id: PChar;
    name: PChar;
    version: PChar;
    summary: PChar;
    description: PChar;
    author: PChar;
    homepage: PChar;
    load: function(plugin: PPurplePlugin): GBoolean;
    unload: function(plugin: PPurplePlugin): GBoolean;
    destroy: procedure(plugin: PPurplePlugin);
    ui_info: Pointer;
    extra_info: Pointer;
    prefs_info: PPurplePluginUiInfo;
    actions: function(plugin: PPurplePlugin; context: Pointer): PGList;
    _purple_reserved1: Pointer;
    _purple_reserved2: Pointer;
    _purple_reserved3: Pointer;
    _purple_reserved4: Pointer;
  end;

This works perfectly well on windows 32 bit and Linux 32 bit but today someone helped me compile it on linux x86_64 and from the debug output of libpurple it seems that when I pass it this record initialized with my values it *can* detect that plugintype is set to PURPLE_PLUGIN_PROTOCOL because then it will try to read extra_info and it thinks it is null, probably it is off a few bytes.

I cannot test this myself because I don't have a 64 bit Linux. When an experienced programmer (should be most on this list) looks at this header what is the obvious thing that might break it on 64 bit? Pointers should be ok, but what is with int and Integer and what is with enums? And are there maybe also certain compiler switches that influence it? What might be wrong?

Bernd

Reply by email

ik
to Bernd
Bernd wrote 78 lines
Hi, I am translating parts of the libpurple headers. Now I seem to have a problem with this: typedef enum { PURPLE_PLUGIN_UNKNOWN = -1, /**< Unknown type. */ PURPLE_PLUGIN_STANDARD = 0, /**< Standard plugin. */ PURPLE_PLUGIN_LOADER, /**< Loader plugin. */ PURPLE_PLUGIN_PROTOCOL /**< Protocol plugin. */ } PurplePluginType; typedef int PurplePluginPriority; struct _PurplePluginInfo { unsigned int magic; unsigned int major_version; unsigned int minor_version; PurplePluginType type; // <-- maybe this is the problem? char *ui_requirement; unsigned long flags; GList *dependencies; PurplePluginPriority priority; char *id; char *name; char *version; char *summary; char *description; char *author; char *homepage; /** * If a plugin defines a 'load' function, and it returns FALSE, * then the plugin will not be loaded. */ gboolean (*load)(PurplePlugin *plugin); gboolean (*unload)(PurplePlugin *plugin); void (*destroy)(PurplePlugin *plugin); void *ui_info; /**< Used only by UI-specific plugins to build a preference screen with a custom UI */ void *extra_info; PurplePluginUiInfo *prefs_info; /**< Used by any plugin to display preferences. If #ui_info has been specified, this will be ignored. */ /** * This callback has a different use depending on whether this * plugin type is PURPLE_PLUGIN_STANDARD or PURPLE_PLUGIN_PROTOCOL. * * If PURPLE_PLUGIN_STANDARD then the list of actions will show up * in the Tools menu, under a submenu with the name of the plugin. * context will be NULL. * * If PURPLE_PLUGIN_PROTOCOL then the list of actions will show up * in the Accounts menu, under a submenu with the name of the * account. context will be set to the PurpleConnection for that * account. This callback will only be called for online accounts. */ GList *(*actions)(PurplePlugin *plugin, gpointer context); void (*_purple_reserved1)(void); void (*_purple_reserved2)(void); void (*_purple_reserved3)(void); void (*_purple_reserved4)(void); }; I have translated it as follows:

you have few problems here.
1. Please use ctypes instead, they are set to work properly with 64 bit etc.. and are properly sized to fit C's type sizes. For example C's int, is not equal to Pascal's Integer, but to longint of Pascal

Show quoted text 43 lines
{$calling cdecl} type TPurplePluginType = ( PURPLE_PLUGIN_UNKNOWN := -1, // Unknown type. PURPLE_PLUGIN_STANDARD := 0, // Standard plugin. PURPLE_PLUGIN_LOADER, // Loader plugin. PURPLE_PLUGIN_PROTOCOL // Protocol plugin. ); TPurplePluginPriority = Integer; TPurplePluginInfo = packed record magic: Integer; major_version: Integer; minor_version: Integer; plugintype: TPurplePluginType; ui_requirement: PChar; flags: LongInt; dependencies: PGList; priority: TPurplePluginPriority; id: PChar; name: PChar; version: PChar; summary: PChar; description: PChar; author: PChar; homepage: PChar; load: function(plugin: PPurplePlugin): GBoolean; unload: function(plugin: PPurplePlugin): GBoolean; destroy: procedure(plugin: PPurplePlugin); ui_info: Pointer; extra_info: Pointer; prefs_info: PPurplePluginUiInfo; actions: function(plugin: PPurplePlugin; context: Pointer): PGList; _purple_reserved1: Pointer; _purple_reserved2: Pointer; _purple_reserved3: Pointer; _purple_reserved4: Pointer;

This 4 types are actually callback rather then "simple" pointers. It will work, but personally I prefer it as a callback when I can avoid general "Pointer".

Show quoted text 21 lines
end; This works perfectly well on windows 32 bit and Linux 32 bit but today someone helped me compile it on linux x86_64 and from the debug output of libpurple it seems that when I pass it this record initialized with my values it *can* detect that plugintype is set to PURPLE_PLUGIN_PROTOCOL because then it will try to read extra_info and it thinks it is null, probably it is off a few bytes. I cannot test this myself because I don't have a 64 bit Linux. When an experienced programmer (should be most on this list) looks at this header what is the obvious thing that might break it on 64 bit? Pointers should be ok, but what is with int and Integer and what is with enums? And are there maybe also certain compiler switches that influence it? What might be wrong? Bernd _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Sven Barth
to Bernd: I am translating parts of the libpurple headers. Now I seem to have a problem with this:…
Bernd wrote 27 lines
Hi, I am translating parts of the libpurple headers. Now I seem to have a problem with this: typedef enum { PURPLE_PLUGIN_UNKNOWN = -1, /**< Unknown type. */ PURPLE_PLUGIN_STANDARD = 0, /**< Standard plugin. */ PURPLE_PLUGIN_LOADER, /**< Loader plugin. */ PURPLE_PLUGIN_PROTOCOL /**< Protocol plugin. */ } PurplePluginType; typedef int PurplePluginPriority; struct _PurplePluginInfo { unsigned int magic; unsigned int major_version; unsigned int minor_version; PurplePluginType type; // <-- maybe this is the problem? char *ui_requirement; unsigned long flags;

This last line is the problem. On non-Windows 64-Bit GCC systems sizeof(long) and sizeof(unsigned long) is 8 while it is 4 on 32-Bit systems and on Windows 64-Bit. See also here for this problematic: http://en.wikipedia.org/wiki/64-bit#64-bit_data_models

As ik already wrote it is preferred to use the types defined in the ctypes.pp unit to solve this problem (because they are defined correctly for the corresponding platform). The types are usually named similar to their C-type with a "c" equivalent (the prefix is "cu" for unsigned types). E.g. "unsigned int" will be "cuint" and "long" will be "clong". The unit also compiles with Delphi.

Regards,
Sven

Reply by email

Florian Klämpfl
to Bernd: I am translating parts of the libpurple headers. Now I seem to have a problem with this:…
Bernd wrote
TPurplePluginInfo = packed record

You should also avoid using packed. FPC usually uses the same alignment as C on a certain platform does if the types are correctly used. However, on platforms (e.g. arm, sparc) which require proper alignment, packed causes the compiler to load every data element byte by byte because packed records do not guarantee to be aligned properly. This slows down code a lot.

Reply by email

Bernd
to Florian Klämpfl

Thank you all for the valuable info. I have now changed everything to use only the types from ctypes, cint for int and culong for unsigned long. Once my tester on the other side of the atlantic wakes up I can test it.

2012/6/17 Florian Klämpfl :

Show quoted text 6 lines
Am 17.06.2012 01:02, schrieb Bernd: > >   TPurplePluginInfo = packed record You should also avoid using packed. FPC usually uses the same alignment as C on a certain platform does if the types are correctly used.

I did not know this. I have read in many different places that one should use packed (thats the reason I did it in the first place), but I will now remove it from all my records.

Bernd

Reply by email

Jonas Maebe
to Florian Klämpfl: You should also avoid using packed. FPC usually uses the same alignment as C on a certain…
Florian Klämpfl wrote 6 lines
Am 17.06.2012 01:02, schrieb Bernd: > > TPurplePluginInfo = packed record You should also avoid using packed. FPC usually uses the same alignment as C on a certain platform does if the types are correctly used.

And if you use {$packrecords c}, then FPC is guaranteed to use the same alignment as C compilers (aka what the platform ABI specifies).

Jonas

Reply by email

Jonas Maebe
to Bernd: Thank you all for the valuable info. I have now changed everything to use only the types…
Bernd wrote
I did not know this. I have read in many different places that one should use packed

Those places please probably said "you should use packed *if you want the layout of a record to be the same on all platforms and across different FPC versions*". In this particular case, you want everything but it to be the same on all platforms. Instead, you want it to change to adapt to the ABI of the different platforms.

And when using {$packrecords c}, the record layout will not change across FPC versions either (unless a bug is found in the alignment rules for that platform, but in that case the change will improve the compatibility with the C record declaration).

Jonas

Reply by email

Bernd
to Bernd

2012/6/17 Bernd :

Show quoted text 6 lines
2012/6/17 Jonas Maebe : > And if you use {$packrecords c}, then FPC is guaranteed to use the same alignment as C compilers (aka what the platform ABI specifies). Done :-) https://github.com/prof7bit/TorChat/blob/torchat2/src/purple/purple.pas

It works! this record with the unsigned long was the only error in 6000 lines of code, everything else immediately started working on 64 bit without changing a single line of code :-)

(Almost) The only other small discontinuity was the PNG writer from fcl-image needs to be explicitly told Indexed:=False on 64 bit while on 32 bit this was not necessary.

Bernd

Reply by email